Initial commit: functional for newer bzImage kernels
[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 int output_elf(struct segment *segs, addr_t entry, FILE *out)
28 {
29         Elf32_Ehdr ehdr;
30         Elf32_Shdr shdr;
31         Elf32_Phdr phdr;
32         int nsections = 0;
33         int namebytes = 11;     /* Null entry plus .shstrtab */
34         struct segment *s;
35         uint32_t data_start, data_offset;
36         uint32_t data_size = 0;
37         uint32_t name_offset;
38         uint32_t shoff;
39
40         for (s = segs; s; s = s->next) {
41                 nsections++;
42                 if (s->name)
43                         namebytes += strlen(s->name)+1;
44                 if (s->sh_type != SHT_NOBITS)
45                         data_size += s->length;
46         }
47
48         memset(&ehdr, 0, sizeof ehdr);
49         ehdr.e_ident[EI_MAG0]    = ELFMAG0;
50         ehdr.e_ident[EI_MAG1]    = ELFMAG1;
51         ehdr.e_ident[EI_MAG2]    = ELFMAG2;
52         ehdr.e_ident[EI_MAG3]    = ELFMAG3;
53         ehdr.e_ident[EI_CLASS]   = ELFCLASS32;
54         ehdr.e_ident[EI_DATA]    = ELFDATA2LSB;
55         ehdr.e_ident[EI_VERSION] = EV_CURRENT;
56         ehdr.e_ident[EI_OSABI]   = ELFOSABI_STANDALONE;
57         
58         wrle16(ET_EXEC, &ehdr.e_type);
59         wrle16(EM_386, &ehdr.e_machine);
60         wrle32(EV_CURRENT, &ehdr.e_version);
61         wrle32(entry, &ehdr.e_entry);
62         wrle32(sizeof ehdr, &ehdr.e_phoff);
63         shoff = sizeof ehdr + nsections*(sizeof phdr) + data_size + namebytes;
64         shoff = (shoff+3) & ~3;
65         wrle32(shoff, &ehdr.e_shoff);
66         wrle16(sizeof ehdr, &ehdr.e_ehsize);
67         wrle16(sizeof phdr, &ehdr.e_phentsize);
68         wrle16(nsections, &ehdr.e_phnum);
69         wrle16(sizeof shdr, &ehdr.e_shentsize);
70         wrle16(nsections+2, &ehdr.e_shnum);
71         wrle16(nsections+1, &ehdr.e_shstrndx);
72         fwrite(&ehdr, 1, sizeof ehdr, out);
73
74         /* First actual data byte */
75         data_start = (sizeof ehdr) + nsections*(sizeof phdr);
76
77         /* Program header table */
78         data_offset = data_start;
79         for (s = segs; s; s = s->next) {
80                 uint32_t filesize, memsize;
81                 uint32_t p_type, p_flags;
82
83                 filesize = (s->sh_type == SHT_NOBITS) ? 0 : s->length;
84                 memsize = (s->sh_flags & SHF_ALLOC) ? s->length : 0;
85                 
86                 switch (s->sh_type) {
87                 case SHT_PROGBITS:
88                 case SHT_NOBITS:
89                         p_type = PT_LOAD;
90                         break;
91                 case SHT_NOTE:
92                         p_type = PT_NOTE;
93                         break;
94                 default:
95                         p_type = PT_NULL; /* No other types supported */
96                         break;
97                 }
98
99                 p_flags = 0;
100                 if (s->sh_flags & SHF_ALLOC) /* Somewhat cheesy... */
101                         p_flags |= PF_R;
102                 if (s->sh_flags & SHF_EXECINSTR)
103                         p_flags |= PF_X;
104                 if (s->sh_flags & SHF_WRITE)
105                         p_flags |= PF_W;
106
107                 memset(&phdr, 0, sizeof phdr);
108                 wrle32(p_type, &phdr.p_type);
109                 wrle32(data_offset, &phdr.p_offset);
110                 wrle32(s->address, &phdr.p_vaddr);
111                 wrle32(s->address, &phdr.p_paddr);
112                 wrle32(filesize, &phdr.p_filesz);
113                 wrle32(memsize, &phdr.p_memsz);
114                 wrle32(p_flags, &phdr.p_flags);
115                 wrle32(1, &phdr.p_align);
116
117                 fwrite(&phdr, 1, sizeof phdr, out);
118
119                 data_offset += filesize;
120         }
121
122         /* Actual file data */
123         for (s = segs; s; s = s->next) {
124                 if (s->sh_type != SHT_NOBITS)
125                         fwrite(s->data, 1, s->length, out);
126         }
127
128         /* String table */
129         putc(0, out);           /* Null string table entry */
130         data_offset++;
131         for (s = segs; s; s = s->next) {
132                 uint32_t len = strlen(s->name)+1;
133                 fwrite(s->name, 1, len, out);
134                 data_offset += len;
135         }
136         fwrite(".shstrtab", 1, 10, out);
137         data_offset += 10;
138
139         while (data_offset & 3) {
140                 putc(0, out);   /* Align the section header table */
141                 data_offset++;
142         }
143
144         /* Section header table */
145         memset(&shdr, 0, sizeof shdr);
146         fwrite(&shdr, 1, sizeof shdr, out); /* Null section header */
147
148         data_offset = data_start;
149         name_offset = 1;
150         for (s = segs; s; s = s->next) {
151                 memset(&shdr, 0, sizeof shdr);
152                 wrle32(name_offset, &shdr.sh_name);
153                 wrle32(s->sh_type, &shdr.sh_type);
154                 wrle32(s->sh_flags, &shdr.sh_flags);
155                 wrle32(s->address, &shdr.sh_addr);
156                 wrle32(data_offset, &shdr.sh_offset); /* Even for SHT_NOBITS */
157                 wrle32(s->length, &shdr.sh_size);
158                 wrle32(1, &shdr.sh_addralign);
159
160                 fwrite(&shdr, 1, sizeof shdr, out);
161
162                 name_offset += strlen(s->name)+1;
163
164                 if (s->sh_type != SHT_NOBITS)
165                         data_offset += s->length;
166         }
167
168         /* String table section header */
169         memset(&shdr, 0, sizeof shdr);
170         wrle32(name_offset, &shdr.sh_name);
171         wrle32(SHT_STRTAB, &shdr.sh_type);
172         wrle32(data_offset, &shdr.sh_offset);
173         wrle32(namebytes, &shdr.sh_size);
174         
175         fwrite(&shdr, 1, sizeof shdr, out);
176
177         return 0;
178 }