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