man: --nbi, not --NBI
[wraplinux.git] / elf.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2008 rPath, Inc. - 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 <stdbool.h>
27 #include "elf32.h"
28 #include "segment.h"
29 #include "multiboot.h"
30 #include "le.h"
31
32 static inline uint32_t p_type(const struct segment *s)
33 {
34         switch (s->sh_type) {
35         case SHT_PROGBITS:
36         case SHT_NOBITS:
37                 return (s->sh_flags & SHF_ALLOC) ? PT_LOAD : PT_NULL;
38         case SHT_NOTE:
39                 return PT_NOTE;
40         default:
41                 return PT_NULL; /* No other types supported */
42         }
43 }
44
45 /*
46  * Return true if we should use a single PHDR for s and s->next
47  */
48 static bool merge_phdrs(const struct segment *s)
49 {
50         const struct segment *n = s->next;
51
52         if (!n)
53                 return false;
54
55         if (p_type(s) != PT_LOAD || p_type(n) != PT_LOAD)
56                 return false;
57
58         if (s->sh_type != n->sh_type)
59                 return false;
60
61         if (s->sh_flags != n->sh_flags)
62                 return false;
63
64         if (s->align < n->align)
65                 return false;
66
67         if (n->address != align_up(s->address+s->length, n->align))
68                 return false;
69
70         return true;
71 }
72
73 static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
74 {
75         Elf32_Shdr shdr;
76         Elf32_Phdr phdr;
77         int nsections = 0, npheader = 0;
78         int namebytes = 11;     /* Null entry plus .shstrtab */
79         struct segment *s;
80         uint32_t data_start, data_offset, file_offset;
81         uint32_t name_offset;
82
83         for (s = segs; s; s = s->next) {
84                 nsections++;
85                 if (s->name)
86                         namebytes += strlen(s->name)+1;
87         }
88
89         for (s = segs; s; s = s->next) {
90                 uint32_t memsize;
91                 addr_t align = (addr_t)1 << s->align;
92                 addr_t address, pad;
93
94                 if (p_type(s) == PT_NULL)
95                         continue;
96
97                 npheader++;
98
99                 address = s->address;
100                 memsize = s->length;
101
102                 while (merge_phdrs(s)) {
103                         s = s->next;
104                         align = UINT32_C(1) << s->align;
105                         pad = (s->address - (address+memsize)) & (align-1);
106                         memsize += pad + s->length;
107                 }
108         }
109
110         wrle16(sizeof phdr, &ehdr->e_phentsize);
111         wrle16(npheader, &ehdr->e_phnum);
112         wrle16(sizeof shdr, &ehdr->e_shentsize);
113         wrle16(nsections+2, &ehdr->e_shnum);
114         wrle16(nsections+1, &ehdr->e_shstrndx);
115         file_offset = c_fwrite(ehdr, sizeof *ehdr, out);
116
117         /* First actual data byte */
118         data_start = (sizeof *ehdr) + npheader*(sizeof phdr);
119
120         /* Program header table */
121         data_offset = data_start;
122         for (s = segs; s; s = s->next) {
123                 uint32_t filesize, memsize;
124                 uint32_t ptype = p_type(s);
125                 uint32_t p_flags;
126                 uint32_t align = UINT32_C(1) << s->align;
127                 addr_t address, offset, pad;
128
129                 filesize = (s->sh_type == SHT_NOBITS) ? 0 : s->length;
130                 memsize = (s->sh_flags & SHF_ALLOC) ? s->length : 0;
131
132                 if (s->sh_type == SHT_NOBITS)
133                         pad = 0;
134                 else
135                         pad = (s->address - data_offset) & (align-1);
136
137                 data_offset += pad;
138
139                 if (ptype != PT_NULL) {
140                         p_flags = 0;
141                         if (s->sh_flags & SHF_ALLOC) /* Somewhat cheesy... */
142                                 p_flags |= PF_R;
143                         if (s->sh_flags & SHF_EXECINSTR)
144                                 p_flags |= PF_X;
145                         if (s->sh_flags & SHF_WRITE)
146                                 p_flags |= PF_W;
147
148                         memset(&phdr, 0, sizeof phdr);
149                         wrle32(ptype, &phdr.p_type);
150                         wrle32(data_offset, &phdr.p_offset);
151                         wrle32(s->address, &phdr.p_vaddr);
152                         wrle32(s->address, &phdr.p_paddr);
153                         wrle32(p_flags, &phdr.p_flags);
154                         wrle32(align, &phdr.p_align);
155
156                         address = s->address;
157                         offset  = data_offset + filesize;
158
159                         while (merge_phdrs(s)) {
160                                 s = s->next;
161                                 align = UINT32_C(1) << s->align;
162                                 pad = (s->address - (address+memsize))
163                                         & (align-1);
164                                 if (s->sh_type != SHT_NOBITS) {
165                                         assert(pad == ((s->address - offset)
166                                                        & (align-1)));
167                                         filesize += s->length + pad;
168                                         offset   += s->length + pad;
169                                 }
170                                 memsize += s->length + pad;
171                         }
172
173                         wrle32(filesize, &phdr.p_filesz);
174                         wrle32(memsize, &phdr.p_memsz);
175
176                         file_offset += c_fwrite(&phdr, sizeof phdr, out);
177                 }
178                 data_offset += filesize;
179         }
180
181         /* Actual file data */
182         assert(file_offset == data_start);
183         for (s = segs; s; s = s->next) {
184                 if (s->sh_type != SHT_NOBITS) {
185                         uint32_t align = UINT32_C(1) << s->align;
186                         uint32_t pad = (s->address - file_offset) & (align-1);
187                         file_offset += c_writezero(pad, out);
188                         file_offset += c_fwrite(s->data, s->length, out);
189                 }
190         }
191
192         /* String table */
193         file_offset += c_fwrite("", 1, out);    /* Null string table entry */
194         for (s = segs; s; s = s->next) {
195                 uint32_t len = strlen(s->name)+1;
196                 file_offset += c_fwrite(s->name, len, out);
197         }
198         file_offset += c_fwrite(".shstrtab", 10, out);
199
200         /* Align the section header table */
201         file_offset += c_writezero(-file_offset & 3, out);
202
203         /* Section header table */
204         wrle32(file_offset, &ehdr->e_shoff); /* Valid on the next interation */
205
206         data_offset = data_start;
207
208         memset(&shdr, 0, sizeof shdr);
209         c_fwrite(&shdr, sizeof shdr, out); /* Null section header */
210
211         name_offset = 1;
212         for (s = segs; s; s = s->next) {
213                 uint32_t align = UINT32_C(1) << s->align;
214
215                 if (s->sh_type != SHT_NOBITS)
216                         data_offset += (s->address - data_offset) & (align-1);
217
218                 memset(&shdr, 0, sizeof shdr);
219                 wrle32(s->name ? name_offset : 0, &shdr.sh_name);
220                 wrle32(s->sh_type, &shdr.sh_type);
221                 wrle32(s->sh_flags, &shdr.sh_flags);
222                 wrle32(s->address, &shdr.sh_addr);
223                 wrle32(data_offset, &shdr.sh_offset); /* Even for SHT_NOBITS */
224                 wrle32(s->length, &shdr.sh_size);
225                 wrle32(align, &shdr.sh_addralign);
226
227                 file_offset += c_fwrite(&shdr, sizeof shdr, out);
228
229                 if (s->name)
230                         name_offset += strlen(s->name)+1;
231
232                 if (s->sh_type != SHT_NOBITS)
233                         data_offset += s->length;
234         }
235
236         /* String table section header */
237         memset(&shdr, 0, sizeof shdr);
238         wrle32(name_offset, &shdr.sh_name);
239         wrle32(SHT_STRTAB, &shdr.sh_type);
240         wrle32(data_offset, &shdr.sh_offset);
241         wrle32(namebytes, &shdr.sh_size);
242         wrle32(1, &shdr.sh_addralign);
243
244         c_fwrite(&shdr, sizeof shdr, out);
245
246         return 0;
247 }
248
249
250 static int do_elf(struct segment *segs, addr_t entry, FILE *out)
251 {
252         Elf32_Ehdr ehdr;
253         int rv;
254
255         memset(&ehdr, 0, sizeof ehdr);
256
257         ehdr.e_ident[EI_MAG0]    = ELFMAG0;
258         ehdr.e_ident[EI_MAG1]    = ELFMAG1;
259         ehdr.e_ident[EI_MAG2]    = ELFMAG2;
260         ehdr.e_ident[EI_MAG3]    = ELFMAG3;
261         ehdr.e_ident[EI_CLASS]   = ELFCLASS32;
262         ehdr.e_ident[EI_DATA]    = ELFDATA2LSB;
263         ehdr.e_ident[EI_VERSION] = EV_CURRENT;
264         ehdr.e_ident[EI_OSABI]   = ELFOSABI_STANDALONE;
265
266         wrle16(ET_EXEC, &ehdr.e_type);
267         wrle16(EM_386, &ehdr.e_machine);
268         wrle32(EV_CURRENT, &ehdr.e_version);
269         wrle32(sizeof ehdr, &ehdr.e_phoff);
270         wrle32(entry, &ehdr.e_entry);
271         wrle16(sizeof ehdr, &ehdr.e_ehsize);
272
273         /* Dummy run which produces some additional data */
274         rv = gen_elf(segs, &ehdr, NULL);
275         if (rv)
276                 return rv;
277
278         /* The real run */
279         return gen_elf(segs, &ehdr, out);
280 }
281
282 int output_elf(struct segment *segs, addr_t entry, FILE *out)
283 {
284         segs = sort_segments(segs);
285         return do_elf(segs, entry, out);
286 }
287
288 int output_multiboot(struct segment *segs, addr_t entry, FILE *out)
289 {
290         struct segment mb_seg;
291         struct multiboot_header mb_hdr;
292         uint32_t magic, flags, csum;
293
294         segs = sort_segments(segs);
295
296         /* Stick a Multiboot header in a segment at
297            the beginning of the file. */
298         mb_seg.next     = segs;
299         mb_seg.length   = sizeof mb_hdr;
300         mb_seg.align    = 2;
301         mb_seg.address  = 0;
302         mb_seg.sh_type  = SHT_PROGBITS;
303         mb_seg.sh_flags = 0;    /* No ALLOC, EXECINSTR, or WRITE */
304         mb_seg.name     = ".multiboot";
305         mb_seg.data     = &mb_hdr;
306         segs = &mb_seg;
307
308         /* Multiboot header */
309         magic   = MULTIBOOT_MAGIC;
310         flags   = MB_FLAG_PAGE;
311
312         memset(&mb_hdr, 0, sizeof mb_hdr);
313         wrle32(magic, &mb_hdr.magic);
314         wrle32(flags, &mb_hdr.flags);
315
316         csum    = magic + flags;
317         wrle32(-csum, &mb_hdr.checksum);
318
319         /* Generate the ELF image */
320         return do_elf(segs, entry, out);
321 }