ao-elftohex: Add conditions for skipping ELF sections
[fw/altos] / ao-tools / lib / ao-elf.c
1 /*
2  * Copyright © 2013 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <err.h>
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include "ao-elf.h"
30 #include "ao-hex.h"
31 #include "ao-verbose.h"
32
33 /*
34  * Look through the Elf file for symbols that can be adjusted before
35  * the image is written to the device
36  */
37 static struct ao_sym *
38 load_symbols (Elf *e, int *num_symbolsp)
39 {
40         Elf_Scn         *scn;
41         Elf_Data        *symbol_data = NULL;
42         GElf_Shdr       shdr;
43         GElf_Sym        sym;
44         int             i, symbol_count;
45         char            *symbol_name;
46         size_t          shstrndx;
47         struct ao_sym   *symbols = NULL;
48         struct ao_sym   *symbol;
49         int             num_symbols = 0;
50         int             size_symbols = 0;
51
52         if (elf_getshdrstrndx(e, &shstrndx) < 0)
53                 return false;
54
55         /*
56          * Find the symbols
57          */
58
59         scn = NULL;
60         while ((scn = elf_nextscn(e, scn)) != NULL) {
61
62                 if (gelf_getshdr(scn, &shdr) != &shdr)
63                         return false;
64
65                 if (shdr.sh_type == SHT_SYMTAB) {
66                         symbol_data = elf_getdata(scn, NULL);
67                         symbol_count = shdr.sh_size / shdr.sh_entsize;
68                         break;
69                 }
70         }
71
72         if (!symbol_data)
73                 return NULL;
74
75         for (i = 0; i < symbol_count; i++) {
76                 gelf_getsym(symbol_data, i, &sym);
77
78                 symbol_name = elf_strptr(e, shdr.sh_link, sym.st_name);
79                 if (!symbol_name[0])
80                         continue;
81
82                 if (num_symbols == size_symbols) {
83                         struct ao_sym   *new_symbols;
84                         int             new_size;
85
86                         if (!size_symbols)
87                                 new_size = 16;
88                         else
89                                 new_size = size_symbols * 2;
90                         new_symbols = realloc(symbols, new_size * sizeof (struct ao_sym));
91                         if (!new_symbols)
92                                 goto bail;
93
94                         symbols = new_symbols;
95                         size_symbols = new_size;
96                 }
97                 symbol = &symbols[num_symbols];
98                 memset(symbol, 0, sizeof (struct ao_sym));
99                 symbol->name = strdup(symbol_name);
100                 if (!symbol->name)
101                         goto bail;
102                 symbol->addr = sym.st_value;
103                 ao_printf(AO_VERBOSE_EXE, "Add symbol %s: %08x\n", symbol->name, symbol->addr);
104                 num_symbols++;
105         }
106         *num_symbolsp = num_symbols;
107         return symbols;
108 bail:
109         for (i = 0; i < num_symbols; i++)
110                 free(symbols[i].name);
111         free(symbols);
112         return NULL;
113 }
114
115 static uint32_t
116 round4(uint32_t a) {
117         return (a + 3) & ~3;
118 }
119
120 static struct ao_hex_image *
121 new_load (uint32_t addr, uint32_t len)
122 {
123         struct ao_hex_image *new;
124
125         len = round4(len);
126         new = calloc (1, sizeof (struct ao_hex_image) + len);
127         if (!new)
128                 abort();
129
130         new->address = addr;
131         new->length = len;
132         return new;
133 }
134
135 static void
136 load_paste(struct ao_hex_image *into, struct ao_hex_image *from)
137 {
138         if (from->address < into->address || into->address + into->length < from->address + from->length)
139                 abort();
140
141         memcpy(into->data + from->address - into->address, from->data, from->length);
142 }
143
144 /*
145  * Make a new load structure large enough to hold the old one and
146  * the new data
147  */
148 static struct ao_hex_image *
149 expand_load(struct ao_hex_image *from, uint32_t address, uint32_t length)
150 {
151         struct ao_hex_image     *new;
152
153         if (from) {
154                 uint32_t        from_last = from->address + from->length;
155                 uint32_t        last = address + length;
156
157                 if (address > from->address)
158                         address = from->address;
159                 if (last < from_last)
160                         last = from_last;
161
162                 length = last - address;
163
164                 if (address == from->address && length == from->length)
165                         return from;
166         }
167         new = new_load(address, length);
168         if (from) {
169                 load_paste(new, from);
170                 free (from);
171         }
172         return new;
173 }
174
175 /*
176  * Create a new load structure with data from the existing one
177  * and the new data
178  */
179 static struct ao_hex_image *
180 load_write(struct ao_hex_image *from, uint32_t address, uint32_t length, void *data)
181 {
182         struct ao_hex_image     *new;
183
184         new = expand_load(from, address, length);
185         memcpy(new->data + address - new->address, data, length);
186         return new;
187 }
188
189 #define DBG 0
190 /*
191  * Construct a large in-memory block for all
192  * of the loaded sections of the program
193  */
194 static struct ao_hex_image *
195 get_load(Elf *e)
196 {
197         Elf_Scn         *scn;
198         size_t          shstrndx;
199         GElf_Shdr       shdr;
200         Elf_Data        *data;
201         size_t          nphdr;
202         size_t          p;
203         GElf_Phdr       phdr;
204         GElf_Addr       sh_paddr;
205         struct ao_hex_image     *load = NULL;
206 #if DBG
207         char            *section_name;
208 #endif
209         size_t          nshdr;
210         size_t          s;
211
212         if (elf_getshdrstrndx(e, &shstrndx) < 0)
213                 return 0;
214
215         if (elf_getphdrnum(e, &nphdr) < 0)
216                 return 0;
217
218         if (elf_getshdrnum(e, &nshdr) < 0)
219                 return 0;
220
221         /*
222          * As far as I can tell, all of the phdr sections should
223          * be flashed to memory
224          */
225         for (p = 0; p < nphdr; p++) {
226
227                 /* Find this phdr */
228                 gelf_getphdr(e, p, &phdr);
229
230                 if (phdr.p_type != PT_LOAD)
231                         continue;
232
233                 /* Get the associated file section */
234
235 #if DBG
236                 fprintf (stderr, "offset %08x vaddr %08x paddr %08x filesz %08x memsz %08x\n",
237                          (uint32_t) phdr.p_offset,
238                          (uint32_t) phdr.p_vaddr,
239                          (uint32_t) phdr.p_paddr,
240                          (uint32_t) phdr.p_filesz,
241                          (uint32_t) phdr.p_memsz);
242 #endif
243
244                 for (s = 0; s < nshdr; s++) {
245                         scn = elf_getscn(e, s);
246
247                         if (!scn) {
248                                 fprintf (stderr, "getscn failed\n");
249                                 abort();
250                         }
251                         if (gelf_getshdr(scn, &shdr) != &shdr) {
252                                 fprintf (stderr, "gelf_getshdr failed\n");
253                                 abort();
254                         }
255
256 #if DBG
257                         section_name = elf_strptr(e, shstrndx, shdr.sh_name);
258 #endif
259
260                         if (shdr.sh_size != 0 && shdr.sh_type != SHT_NOBITS && (shdr.sh_flags & SHF_ALLOC) &&
261                             phdr.p_offset <= shdr.sh_offset && shdr.sh_offset < phdr.p_offset + phdr.p_filesz)
262                         {
263                                 sh_paddr = phdr.p_paddr + shdr.sh_offset - phdr.p_offset;
264
265 #if DBG
266                                 fprintf (stderr, "\tsize %08x rom %08x exec %08x %s\n",
267                                          (uint32_t) shdr.sh_size,
268                                          (uint32_t) sh_paddr,
269                                          (uint32_t) shdr.sh_addr,
270                                          section_name);
271 #endif
272
273                                 data = elf_getdata(scn, NULL);
274
275                                 /* Write the section data into the memory block */
276                                 load = load_write(load, sh_paddr, shdr.sh_size, data->d_buf);
277                         }
278                 }
279         }
280         return load;
281 }
282
283 /*
284  * Open the specified ELF file and
285  * check for the symbols we need
286  */
287
288 struct ao_hex_image *
289 ao_load_elf(char *name, struct ao_sym **symbols, int *num_symbols)
290 {
291         int             fd;
292         Elf             *e;
293         size_t          shstrndx;
294         struct ao_hex_image     *image;
295
296         if (elf_version(EV_CURRENT) == EV_NONE)
297                 return NULL;
298
299         fd = open(name, O_RDONLY, 0);
300
301         if (fd < 0)
302                 return NULL;
303
304         e = elf_begin(fd, ELF_C_READ, NULL);
305
306         if (!e)
307                 return NULL;
308
309         if (elf_kind(e) != ELF_K_ELF)
310                 return NULL;
311
312         if (elf_getshdrstrndx(e, &shstrndx) != 0)
313                 return NULL;
314
315         if (symbols)
316                 *symbols = load_symbols(e, num_symbols);
317
318         image = get_load(e);
319         if (!image) {
320                 fprintf (stderr, "Cannot create memory image from file\n");
321                 return NULL;
322         }
323
324         return image;
325 }