Autoconstipate the build setup, and generate dependencies.
[wraplinux.git] / elf.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2008 H. Peter Anvin - All Rights Reserved
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12
13 /*
14  * elf.c
15  *
16  * Take a linked list of segments and output it as an ELF32 image
17  */
18
19 #include "wraplinux.h"
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <inttypes.h>
26 #include "elf32.h"
27 #include "segment.h"
28 #include "le.h"
29
30 static size_t c_fwrite(const void *ptr, size_t bytes, FILE *stream)
31 {
32         if (stream)
33                 return fwrite(ptr, 1, bytes, stream);
34         else
35                 return bytes;
36 }
37
38 static size_t c_writezero(size_t n, FILE *stream)
39 {
40         static const char zerobuf[BUFSIZ]; /* All zero */
41         size_t r, o = 0;
42
43         while (n) {
44                 size_t w = (n < BUFSIZ) ? n : BUFSIZ;
45                 r = c_fwrite(zerobuf, w, stream);
46                 o += r;
47                 n -= r;
48                 if (r < w)
49                         break;
50         }
51         return o;
52 }
53
54 static uint32_t align_up(uint32_t addr, int align)
55 {
56         uint32_t align_mask = (UINT32_C(1) << align)-1;
57
58         return (addr + align_mask) & ~align_mask;
59 }
60
61 static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
62 {
63         Elf32_Shdr shdr;
64         Elf32_Phdr phdr;
65         int nsections = 0, npheader = 0;
66         int namebytes = 11;     /* Null entry plus .shstrtab */
67         struct segment *s, *sp;
68         uint32_t data_start, data_offset;
69         uint32_t name_offset;
70
71         sp = NULL;
72         for (s = segs; s; s = s->next) {
73                 uint32_t align = UINT32_C(1) << s->align;
74
75                 nsections++;
76                 npheader++;
77                 if (s->name)
78                         namebytes += strlen(s->name)+1;
79
80                 if ((s->sh_type == SHT_PROGBITS ||
81                      s->sh_type == SHT_NOBITS) &&
82                     (s->sh_flags & SHF_ALLOC) &&
83                     sp &&
84                     sp->sh_type  == s->sh_type &&
85                     sp->sh_flags == s->sh_flags &&
86                     sp->align    >= s->align) {
87                         data_offset = sp->address + sp->length;
88                         data_offset += -data_offset & (align-1);
89                         if (data_offset == s->address)
90                                 npheader--; /* Can be combined into a PHDR */
91                 }
92
93                 sp = s;
94         }
95
96         wrle16(sizeof phdr, &ehdr->e_phentsize);
97         wrle16(npheader, &ehdr->e_phnum);
98         wrle16(sizeof shdr, &ehdr->e_shentsize);
99         wrle16(nsections+2, &ehdr->e_shnum);
100         wrle16(nsections+1, &ehdr->e_shstrndx);
101         c_fwrite(ehdr, sizeof *ehdr, out);
102
103         /* First actual data byte */
104         data_start = (sizeof *ehdr) + npheader*(sizeof phdr);
105
106         /* Program header table */
107         data_offset = data_start;
108         for (s = segs; s; s = s->next) {
109                 uint32_t filesize, memsize;
110                 uint32_t p_type, p_flags;
111                 uint32_t align = UINT32_C(1) << s->align;
112                 uint32_t address, offset, pad;
113
114                 filesize = (s->sh_type == SHT_NOBITS) ? 0 : s->length;
115                 memsize = (s->sh_flags & SHF_ALLOC) ? s->length : 0;
116
117                 if (s->sh_type != SHT_NOBITS)
118                         data_offset += (s->address - data_offset) & (align-1);
119
120                 switch (s->sh_type) {
121                 case SHT_PROGBITS:
122                 case SHT_NOBITS:
123                         p_type = PT_LOAD;
124                         break;
125                 case SHT_NOTE:
126                         p_type = PT_NOTE;
127                         break;
128                 default:
129                         p_type = PT_NULL; /* No other types supported */
130                         break;
131                 }
132
133                 p_flags = 0;
134                 if (s->sh_flags & SHF_ALLOC) /* Somewhat cheesy... */
135                         p_flags |= PF_R;
136                 if (s->sh_flags & SHF_EXECINSTR)
137                         p_flags |= PF_X;
138                 if (s->sh_flags & SHF_WRITE)
139                         p_flags |= PF_W;
140
141                 memset(&phdr, 0, sizeof phdr);
142                 wrle32(p_type, &phdr.p_type);
143                 wrle32(data_offset, &phdr.p_offset);
144                 wrle32(s->address, &phdr.p_vaddr);
145                 wrle32(s->address, &phdr.p_paddr);
146                 wrle32(p_flags, &phdr.p_flags);
147                 wrle32(align, &phdr.p_align);
148
149                 address = s->address;
150                 offset  = data_offset + filesize;
151
152                 if (p_type == PT_LOAD &&
153                     (s->sh_flags & SHF_ALLOC) &&
154                     s->next &&
155                     s->next->sh_type == s->sh_type &&
156                     s->next->sh_flags == s->sh_flags &&
157                     s->next->align <= s->align &&
158                     (s->next->address ==
159                      align_up(address+memsize, s->next->align))) {
160                         s = s->next;
161                         align = UINT32_C(1) << s->align;
162                         pad = (s->address - address) & (align-1);
163                         if (s->sh_type != SHT_NOBITS) {
164                                 assert(pad ==
165                                        ((s->address - offset) & (align-1)));
166                                 filesize += s->length + pad;
167                         }
168                         memsize += s->length + pad;
169                 }
170
171                 wrle32(filesize, &phdr.p_filesz);
172                 wrle32(memsize, &phdr.p_memsz);
173
174                 c_fwrite(&phdr, sizeof phdr, out);
175
176                 data_offset += filesize;
177         }
178
179         /* Actual file data */
180         data_offset = data_start;
181         for (s = segs; s; s = s->next) {
182                 if (s->sh_type != SHT_NOBITS) {
183                         uint32_t align = UINT32_C(1) << s->align;
184                         uint32_t pad = (s->address - data_offset) & (align-1);
185                         data_offset += c_writezero(pad, out);
186                         data_offset += c_fwrite(s->data, s->length, out);
187                 }
188         }
189
190         /* String table */
191         data_offset += c_fwrite("", 1, out);    /* Null string table entry */
192         for (s = segs; s; s = s->next) {
193                 uint32_t len = strlen(s->name)+1;
194                 data_offset += c_fwrite(s->name, len, out);
195         }
196         data_offset += c_fwrite(".shstrtab", 10, out);
197
198         /* Align the section header table */
199         data_offset += c_writezero(-data_offset & 3, out);
200
201         /* Section header table */
202         wrle32(data_offset, &ehdr->e_shoff); /* Valid on the next interation */
203
204         data_offset = data_start;
205
206         memset(&shdr, 0, sizeof shdr);
207         c_fwrite(&shdr, sizeof shdr, out); /* Null section header */
208
209         name_offset = 1;
210         for (s = segs; s; s = s->next) {
211                 uint32_t align = UINT32_C(1) << s->align;
212
213                 if (s->sh_type != SHT_NOBITS)
214                         data_offset += (s->address - data_offset) & (align-1);
215
216                 memset(&shdr, 0, sizeof shdr);
217                 wrle32(name_offset, &shdr.sh_name);
218                 wrle32(s->sh_type, &shdr.sh_type);
219                 wrle32(s->sh_flags, &shdr.sh_flags);
220                 wrle32(s->address, &shdr.sh_addr);
221                 wrle32(data_offset, &shdr.sh_offset); /* Even for SHT_NOBITS */
222                 wrle32(s->length, &shdr.sh_size);
223                 wrle32(align, &shdr.sh_addralign);
224
225                 c_fwrite(&shdr, sizeof shdr, out);
226
227                 name_offset += strlen(s->name)+1;
228
229                 if (s->sh_type != SHT_NOBITS)
230                         data_offset += s->length;
231         }
232
233         /* String table section header */
234         memset(&shdr, 0, sizeof shdr);
235         wrle32(name_offset, &shdr.sh_name);
236         wrle32(SHT_STRTAB, &shdr.sh_type);
237         wrle32(data_offset, &shdr.sh_offset);
238         wrle32(namebytes, &shdr.sh_size);
239         wrle32(1, &shdr.sh_addralign);
240
241         c_fwrite(&shdr, sizeof shdr, out);
242
243         return 0;
244 }
245
246 int output_elf(struct segment *segs, addr_t entry, FILE *out)
247 {
248         Elf32_Ehdr ehdr;
249         int rv;
250
251         memset(&ehdr, 0, sizeof ehdr);
252         segs = sort_segments(segs);
253
254         ehdr.e_ident[EI_MAG0]    = ELFMAG0;
255         ehdr.e_ident[EI_MAG1]    = ELFMAG1;
256         ehdr.e_ident[EI_MAG2]    = ELFMAG2;
257         ehdr.e_ident[EI_MAG3]    = ELFMAG3;
258         ehdr.e_ident[EI_CLASS]   = ELFCLASS32;
259         ehdr.e_ident[EI_DATA]    = ELFDATA2LSB;
260         ehdr.e_ident[EI_VERSION] = EV_CURRENT;
261         ehdr.e_ident[EI_OSABI]   = ELFOSABI_STANDALONE;
262
263         wrle16(ET_EXEC, &ehdr.e_type);
264         wrle16(EM_386, &ehdr.e_machine);
265         wrle32(EV_CURRENT, &ehdr.e_version);
266         wrle32(sizeof ehdr, &ehdr.e_phoff);
267         wrle32(entry, &ehdr.e_entry);
268         wrle16(sizeof ehdr, &ehdr.e_ehsize);
269
270         /* Dummy run which produces some additional data */
271         rv = gen_elf(segs, &ehdr, NULL);
272         if (rv)
273                 return rv;
274
275         /* The real run */
276         return gen_elf(segs, &ehdr, out);
277 }