Handle alignment constraints if the backends support them
[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 <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <inttypes.h>
23 #include "elf32.h"
24 #include "segment.h"
25 #include "le.h"
26
27 static size_t c_fwrite(const void *ptr, size_t bytes, FILE *stream)
28 {
29         if (stream)
30                 return fwrite(ptr, 1, bytes, stream);
31         else
32                 return bytes;
33 }
34
35 static size_t c_writezero(size_t n, FILE *stream)
36 {
37         static const char zerobuf[BUFSIZ]; /* All zero */
38         size_t r, o = 0;
39
40         while (n) {
41                 size_t w = (n < BUFSIZ) ? n : BUFSIZ;
42                 r = c_fwrite(zerobuf, w, stream);
43                 o += r;
44                 n -= r;
45                 if (r < w)
46                         break;
47         }
48         return o;
49 }
50
51 static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
52 {
53         Elf32_Shdr shdr;
54         Elf32_Phdr phdr;
55         int nsections = 0;
56         int namebytes = 11;     /* Null entry plus .shstrtab */
57         struct segment *s;
58         uint32_t data_start, data_offset;
59         uint32_t data_size = 0;
60         uint32_t name_offset;
61
62         for (s = segs; s; s = s->next) {
63                 nsections++;
64                 if (s->name)
65                         namebytes += strlen(s->name)+1;
66                 if (s->sh_type != SHT_NOBITS)
67                         data_size += s->length;
68         }
69
70         wrle16(sizeof phdr, &ehdr->e_phentsize);
71         wrle16(nsections, &ehdr->e_phnum);
72         wrle16(sizeof shdr, &ehdr->e_shentsize);
73         wrle16(nsections+2, &ehdr->e_shnum);
74         wrle16(nsections+1, &ehdr->e_shstrndx);
75         c_fwrite(ehdr, sizeof *ehdr, out);
76
77         /* First actual data byte */
78         data_start = (sizeof *ehdr) + nsections*(sizeof phdr);
79
80         /* Program header table */
81         data_offset = data_start;
82         for (s = segs; s; s = s->next) {
83                 uint32_t filesize, memsize;
84                 uint32_t p_type, p_flags;
85                 uint32_t align = UINT32_C(1) << s->align;
86
87                 filesize = (s->sh_type == SHT_NOBITS) ? 0 : s->length;
88                 memsize = (s->sh_flags & SHF_ALLOC) ? s->length : 0;
89
90                 if (s->sh_type != SHT_NOBITS)
91                         data_offset += (s->address - data_offset) & (align-1);
92
93                 switch (s->sh_type) {
94                 case SHT_PROGBITS:
95                 case SHT_NOBITS:
96                         p_type = PT_LOAD;
97                         break;
98                 case SHT_NOTE:
99                         p_type = PT_NOTE;
100                         break;
101                 default:
102                         p_type = PT_NULL; /* No other types supported */
103                         break;
104                 }
105
106                 p_flags = 0;
107                 if (s->sh_flags & SHF_ALLOC) /* Somewhat cheesy... */
108                         p_flags |= PF_R;
109                 if (s->sh_flags & SHF_EXECINSTR)
110                         p_flags |= PF_X;
111                 if (s->sh_flags & SHF_WRITE)
112                         p_flags |= PF_W;
113
114                 memset(&phdr, 0, sizeof phdr);
115                 wrle32(p_type, &phdr.p_type);
116                 wrle32(data_offset, &phdr.p_offset);
117                 wrle32(s->address, &phdr.p_vaddr);
118                 wrle32(s->address, &phdr.p_paddr);
119                 wrle32(filesize, &phdr.p_filesz);
120                 wrle32(memsize, &phdr.p_memsz);
121                 wrle32(p_flags, &phdr.p_flags);
122                 wrle32(align, &phdr.p_align);
123
124                 c_fwrite(&phdr, sizeof phdr, out);
125                 
126                 data_offset += filesize;
127         }
128
129         /* Actual file data */
130         data_offset = data_start;
131         for (s = segs; s; s = s->next) {
132                 if (s->sh_type != SHT_NOBITS) {
133                         uint32_t align = UINT32_C(1) << s->align;
134                         uint32_t pad = (s->address - data_offset) & (align-1);
135                         data_offset += c_writezero(pad, out);
136                         data_offset += c_fwrite(s->data, s->length, out);
137                 }
138         }
139
140         /* String table */
141         data_offset += c_fwrite("", 1, out);    /* Null string table entry */
142         for (s = segs; s; s = s->next) {
143                 uint32_t len = strlen(s->name)+1;
144                 data_offset += c_fwrite(s->name, len, out);
145         }
146         data_offset += c_fwrite(".shstrtab", 10, out);
147         
148         /* Align the section header table */
149         data_offset += c_writezero(-data_offset & 3, out);
150
151         /* Section header table */
152         wrle32(data_offset, &ehdr->e_shoff); /* Valid on the next interation */
153
154         data_offset = data_start;
155
156         memset(&shdr, 0, sizeof shdr);
157         c_fwrite(&shdr, sizeof shdr, out); /* Null section header */
158
159         name_offset = 1;
160         for (s = segs; s; s = s->next) {
161                 uint32_t align = UINT32_C(1) << s->align;
162
163                 if (s->sh_type != SHT_NOBITS)
164                         data_offset += (s->address - data_offset) & (align-1);
165
166                 memset(&shdr, 0, sizeof shdr);
167                 wrle32(name_offset, &shdr.sh_name);
168                 wrle32(s->sh_type, &shdr.sh_type);
169                 wrle32(s->sh_flags, &shdr.sh_flags);
170                 wrle32(s->address, &shdr.sh_addr);
171                 wrle32(data_offset, &shdr.sh_offset); /* Even for SHT_NOBITS */
172                 wrle32(s->length, &shdr.sh_size);
173                 wrle32(align, &shdr.sh_addralign);
174
175                 c_fwrite(&shdr, sizeof shdr, out);
176
177                 name_offset += strlen(s->name)+1;
178
179                 if (s->sh_type != SHT_NOBITS)
180                         data_offset += s->length;
181         }
182
183         /* String table section header */
184         memset(&shdr, 0, sizeof shdr);
185         wrle32(name_offset, &shdr.sh_name);
186         wrle32(SHT_STRTAB, &shdr.sh_type);
187         wrle32(data_offset, &shdr.sh_offset);
188         wrle32(namebytes, &shdr.sh_size);
189         wrle32(1, &shdr.sh_addralign);
190
191         c_fwrite(&shdr, sizeof shdr, out);
192
193         return 0;
194 }
195
196 int output_elf(struct segment *segs, addr_t entry, FILE *out)
197 {
198         Elf32_Ehdr ehdr;
199         int rv;
200
201         memset(&ehdr, 0, sizeof ehdr);
202         segs = sort_segments(segs);
203
204         ehdr.e_ident[EI_MAG0]    = ELFMAG0;
205         ehdr.e_ident[EI_MAG1]    = ELFMAG1;
206         ehdr.e_ident[EI_MAG2]    = ELFMAG2;
207         ehdr.e_ident[EI_MAG3]    = ELFMAG3;
208         ehdr.e_ident[EI_CLASS]   = ELFCLASS32;
209         ehdr.e_ident[EI_DATA]    = ELFDATA2LSB;
210         ehdr.e_ident[EI_VERSION] = EV_CURRENT;
211         ehdr.e_ident[EI_OSABI]   = ELFOSABI_STANDALONE;
212
213         wrle16(ET_EXEC, &ehdr.e_type);
214         wrle16(EM_386, &ehdr.e_machine);
215         wrle32(EV_CURRENT, &ehdr.e_version);
216         wrle32(sizeof ehdr, &ehdr.e_phoff);
217         wrle32(entry, &ehdr.e_entry);
218         wrle16(sizeof ehdr, &ehdr.e_ehsize);
219
220         /* Dummy run which produces some additional data */
221         rv = gen_elf(segs, &ehdr, NULL);
222         if (rv)
223                 return rv;
224
225         /* The real run */
226         return gen_elf(segs, &ehdr, out);
227 }