c44102f869a7fe2d6964b601ebea23f9e529a7f5
[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 /*
190  * Construct a large in-memory block for all
191  * of the loaded sections of the program
192  */
193 static struct ao_hex_image *
194 get_load(Elf *e)
195 {
196         Elf_Scn         *scn;
197         size_t          shstrndx;
198         GElf_Shdr       shdr;
199         Elf_Data        *data;
200         size_t          nphdr;
201         size_t          p;
202         GElf_Phdr       phdr;
203         GElf_Addr       sh_paddr;
204         struct ao_hex_image     *load = NULL;
205 #if 0
206         char            *section_name;
207 #endif
208         size_t          nshdr;
209         size_t          s;
210
211         if (elf_getshdrstrndx(e, &shstrndx) < 0)
212                 return 0;
213
214         if (elf_getphdrnum(e, &nphdr) < 0)
215                 return 0;
216
217         if (elf_getshdrnum(e, &nshdr) < 0)
218                 return 0;
219
220         /*
221          * As far as I can tell, all of the phdr sections should
222          * be flashed to memory
223          */
224         for (p = 0; p < nphdr; p++) {
225
226                 /* Find this phdr */
227                 gelf_getphdr(e, p, &phdr);
228
229                 if (phdr.p_type != PT_LOAD)
230                         continue;
231
232                 /* Get the associated file section */
233
234 #if 0
235                 fprintf (stderr, "offset %08x vaddr %08x paddr %08x filesz %08x memsz %08x\n",
236                          (uint32_t) phdr.p_offset,
237                          (uint32_t) phdr.p_vaddr,
238                          (uint32_t) phdr.p_paddr,
239                          (uint32_t) phdr.p_filesz,
240                          (uint32_t) phdr.p_memsz);
241 #endif
242
243                 for (s = 0; s < nshdr; s++) {
244                         scn = elf_getscn(e, s);
245
246                         if (!scn) {
247                                 fprintf (stderr, "getscn failed\n");
248                                 abort();
249                         }
250                         if (gelf_getshdr(scn, &shdr) != &shdr) {
251                                 fprintf (stderr, "gelf_getshdr failed\n");
252                                 abort();
253                         }
254
255 #if 0
256                         section_name = elf_strptr(e, shstrndx, shdr.sh_name);
257 #endif
258
259                         if (phdr.p_offset <= shdr.sh_offset && shdr.sh_offset < phdr.p_offset + phdr.p_filesz) {
260
261                                 if (shdr.sh_size == 0)
262                                         continue;
263
264                                 sh_paddr = phdr.p_paddr + shdr.sh_offset - phdr.p_offset;
265
266 #if 0
267                                 fprintf (stderr, "\tsize %08x rom %08x exec %08x %s\n",
268                                          (uint32_t) shdr.sh_size,
269                                          (uint32_t) sh_paddr,
270                                          (uint32_t) shdr.sh_addr,
271                                          section_name);
272 #endif
273
274                                 data = elf_getdata(scn, NULL);
275
276                                 /* Write the section data into the memory block */
277                                 load = load_write(load, sh_paddr, shdr.sh_size, data->d_buf);
278                         }
279                 }
280         }
281         return load;
282 }
283
284 /*
285  * Open the specified ELF file and
286  * check for the symbols we need
287  */
288
289 struct ao_hex_image *
290 ao_load_elf(char *name, struct ao_sym **symbols, int *num_symbols)
291 {
292         int             fd;
293         Elf             *e;
294         size_t          shstrndx;
295         struct ao_hex_image     *image;
296
297         if (elf_version(EV_CURRENT) == EV_NONE)
298                 return NULL;
299
300         fd = open(name, O_RDONLY, 0);
301
302         if (fd < 0)
303                 return NULL;
304
305         e = elf_begin(fd, ELF_C_READ, NULL);
306
307         if (!e)
308                 return NULL;
309
310         if (elf_kind(e) != ELF_K_ELF)
311                 return NULL;
312
313         if (elf_getshdrstrndx(e, &shstrndx) != 0)
314                 return NULL;
315
316         if (symbols)
317                 *symbols = load_symbols(e, num_symbols);
318
319         image = get_load(e);
320         if (!image) {
321                 fprintf (stderr, "Cannot create memory image from file\n");
322                 return NULL;
323         }
324
325         return image;
326 }