1 /*
2 * Copyright (C) 2011 Intel Corporation; author Matt Fleming
3 *
4 * Wrap the ELF shared library in a PE32 (32bit) or PE32+ (64bit) suit.
5 *
6 * Syslinux plays some games with the ELF sections that are not easily
7 * converted to a PE32 executable. For instance, Syslinux requires
8 * that a symbol hash table be present (GNU hash or SysV) so that
9 * symbols in ELF modules can be resolved at runtime but the EFI
10 * firmware loader doesn't like that and refuses to load the file.
11 *
12 * We pretend that we have an EFI executable with a single .text
13 * section so that the EFI loader will load it and jump to the entry
14 * point. Once the Syslinux ELF shared object has control we can do
15 * whatever we want.
16 */
17 #include <linux/elf.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include "wrapper.h"
26
27 #if __SIZEOF_POINTER__ == 4
28 typedef Elf32_Ehdr Elf_Ehdr;
29 typedef Elf32_Addr Elf_Addr;
30 #elif __SIZEOF_POINTER__ == 8
31 typedef Elf64_Ehdr Elf_Ehdr;
32 typedef Elf64_Addr Elf_Addr;
33 #else
34 #error "unsupported architecture"
35 #endif
36
37 /*
38 * 'so_memsz' is the size of the ELF shared object once loaded.
39 * 'data_size' is the size of initialised data in the shared object.
40 * 'class' dictates how the header is written
41 * For 32bit machines (class == ELFCLASS32), the optional
42 * header includes PE32 header fields
43 * For 64bit machines (class == ELFCLASS64), the optional
44 * header includes PE32+header fields
45 */
write_header(FILE * f,__uint32_t entry,size_t data_size,__uint32_t so_memsz,__uint8_t class)46 static void write_header(FILE *f, __uint32_t entry, size_t data_size,
47 __uint32_t so_memsz, __uint8_t class)
48 {
49 struct optional_hdr o_hdr;
50 struct optional_hdr_pe32p o_hdr_pe32p;
51 struct section t_sec;
52 struct extra_hdr e_hdr;
53 struct extra_hdr_pe32p e_hdr_pe32p;
54 struct coff_hdr c_hdr;
55 struct header hdr;
56 __uint32_t total_sz = data_size;
57 __uint32_t hdr_sz;
58 __uint32_t reloc_start, reloc_end;
59
60 /*
61 * The header size have to be a multiple of file_align, which currently
62 * is 512
63 */
64 hdr_sz = 512;
65 total_sz += hdr_sz;
66 entry += hdr_sz;
67
68 memset(&hdr, 0, sizeof(hdr));
69 hdr.msdos_signature = MSDOS_SIGNATURE;
70
71 /*
72 * The relocs table pointer needs to be >= 0x40 for PE files. It
73 * informs things like file(1) that we are not an MS-DOS
74 * executable.
75 */
76 hdr.relocs_ptr = 0x40;
77
78 hdr.pe_hdr = OFFSETOF(struct header, pe_signature);
79 hdr.pe_signature = PE_SIGNATURE;
80 fwrite(&hdr, sizeof(hdr), 1, f);
81
82 memset(&c_hdr, 0, sizeof(c_hdr));
83 c_hdr.nr_sections = 1;
84 c_hdr.nr_syms = 1;
85 if (class == ELFCLASS32) {
86 c_hdr.arch = IMAGE_FILE_MACHINE_I386;
87 c_hdr.characteristics = IMAGE_FILE_32BIT_MACHINE |
88 IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE |
89 IMAGE_FILE_LINE_NUMBERS_STRIPPED;
90 c_hdr.optional_hdr_sz = sizeof(o_hdr) + sizeof(e_hdr);
91 fwrite(&c_hdr, sizeof(c_hdr), 1, f);
92 memset(&o_hdr, 0, sizeof(o_hdr));
93 o_hdr.format = PE32_FORMAT;
94 o_hdr.major_linker_version = 0x02;
95 o_hdr.minor_linker_version = 0x14;
96 o_hdr.code_sz = data_size;
97 o_hdr.entry_point = entry;
98 o_hdr.initialized_data_sz = data_size;
99 fwrite(&o_hdr, sizeof(o_hdr), 1, f);
100 memset(&e_hdr, 0, sizeof(e_hdr));
101 e_hdr.section_align = 4096;
102 e_hdr.file_align = 512;
103 e_hdr.image_sz = hdr_sz + so_memsz;
104 e_hdr.headers_sz = hdr_sz;
105 e_hdr.subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION;
106 e_hdr.rva_and_sizes_nr = sizeof(e_hdr.data_directory) / sizeof(__uint64_t);
107 fwrite(&e_hdr, sizeof(e_hdr), 1, f);
108 }
109 else if (class == ELFCLASS64) {
110 c_hdr.arch = IMAGE_FILE_MACHINE_X86_64;
111 c_hdr.characteristics = IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE |
112 IMAGE_FILE_LINE_NUMBERS_STRIPPED;
113 c_hdr.optional_hdr_sz = sizeof(o_hdr_pe32p) + sizeof(e_hdr_pe32p);
114 fwrite(&c_hdr, sizeof(c_hdr), 1, f);
115 memset(&o_hdr_pe32p, 0, sizeof(o_hdr_pe32p));
116 o_hdr_pe32p.format = PE32P_FORMAT;
117 o_hdr_pe32p.major_linker_version = 0x02;
118 o_hdr_pe32p.minor_linker_version = 0x14;
119 o_hdr_pe32p.code_sz = data_size;
120 o_hdr_pe32p.entry_point = entry;
121 o_hdr.initialized_data_sz = data_size;
122 fwrite(&o_hdr_pe32p, sizeof(o_hdr_pe32p), 1, f);
123 memset(&e_hdr_pe32p, 0, sizeof(e_hdr_pe32p));
124 e_hdr_pe32p.section_align = 4096;
125 e_hdr_pe32p.file_align = 512;
126 e_hdr_pe32p.image_sz = hdr_sz + so_memsz;
127 e_hdr_pe32p.headers_sz = hdr_sz;
128 e_hdr_pe32p.subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION;
129 e_hdr_pe32p.rva_and_sizes_nr = sizeof(e_hdr_pe32p.data_directory) / sizeof(__uint64_t);
130 fwrite(&e_hdr_pe32p, sizeof(e_hdr_pe32p), 1, f);
131 }
132
133 memset(&t_sec, 0, sizeof(t_sec));
134 strcpy((char *)t_sec.name, ".text");
135 t_sec.virtual_sz = data_size;
136 t_sec.virtual_address = hdr_sz;
137 t_sec.raw_data_sz = t_sec.virtual_sz;
138 t_sec.raw_data = t_sec.virtual_address;
139 t_sec.characteristics = IMAGE_SCN_CNT_CODE |
140 IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_MEM_EXECUTE |
141 IMAGE_SCN_MEM_READ;
142 fwrite(&t_sec, sizeof(t_sec), 1, f);
143
144 /*
145 * Add some padding to align the ELF as needed
146 */
147 if (ftell(f) > t_sec.virtual_address) {
148 /* Don't rewind! hdr_sz need to be increased. */
149 fprintf(stderr, "PE32+ headers are too large.\n");
150 exit(EXIT_FAILURE);
151 }
152
153 fseek(f, t_sec.virtual_address, SEEK_SET);
154 }
155
usage(char * progname)156 static void usage(char *progname)
157 {
158 fprintf(stderr, "usage: %s <ELF shared object> <output file>\n",
159 progname);
160 }
161
main(int argc,char ** argv)162 int main(int argc, char **argv)
163 {
164 Elf32_Ehdr e32_hdr;
165 Elf64_Ehdr e64_hdr;
166 __uint32_t entry;
167 __uint8_t class;
168 __uint64_t phoff = 0;
169 __uint16_t phnum = 0, phentsize = 0;
170 unsigned char *id;
171 FILE *f_in, *f_out;
172 void *buf;
173 size_t datasz, memsz, rv;
174
175 if (argc < 3) {
176 usage(argv[0]);
177 exit(0);
178 }
179
180 f_in = fopen(argv[1], "r");
181 if (!f_in) {
182 perror("fopen");
183 exit(EXIT_FAILURE);
184 }
185
186 f_out = fopen(argv[2], "w");
187 if (!f_out) {
188 perror("fopen");
189 exit(EXIT_FAILURE);
190 }
191
192 /*
193 * Parse the ELF header and find the entry point.
194 */
195 fread((void *)&e32_hdr, sizeof(e32_hdr), 1, f_in);
196 if (e32_hdr.e_ident[EI_CLASS] == ELFCLASS32) {
197 id = e32_hdr.e_ident;
198 class = ELFCLASS32;
199 entry = e32_hdr.e_entry;
200 phoff = e32_hdr.e_phoff;
201 phnum = e32_hdr.e_phnum;
202 phentsize = e32_hdr.e_phentsize;
203 }
204 else if (e32_hdr.e_ident[EI_CLASS] == ELFCLASS64) {
205 /* read the header again for x86_64
206 * note that the elf header entry point is 64bit whereas
207 * the entry point in PE/COFF format is 32bit!*/
208 class = ELFCLASS64;
209 rewind(f_in);
210 fread((void *)&e64_hdr, sizeof(e64_hdr), 1, f_in);
211 id = e64_hdr.e_ident;
212 entry = e64_hdr.e_entry;
213 phoff = e64_hdr.e_phoff;
214 phnum = e64_hdr.e_phnum;
215 phentsize = e64_hdr.e_phentsize;
216 } else {
217 fprintf(stderr, "Unsupported architecture\n");
218 exit(EXIT_FAILURE);
219 }
220
221 if (id[EI_MAG0] != ELFMAG0 ||
222 id[EI_MAG1] != ELFMAG1 ||
223 id[EI_MAG2] != ELFMAG2 ||
224 id[EI_MAG3] != ELFMAG3) {
225 fprintf(stderr, "Input file not ELF shared object\n");
226 exit(EXIT_FAILURE);
227 }
228
229 if (!phoff || !phnum) {
230 fprintf(stderr, "Cannot find segment table\n");
231 exit(EXIT_FAILURE);
232 }
233
234 /*
235 * Find the LOAD program header. Everything in this segment
236 * is copied verbatim to the output file.
237 * Although there may be several LOAD program headers, only
238 * one is currently copied.
239 */
240 if (e32_hdr.e_ident[EI_CLASS] == ELFCLASS32) {
241 Elf32_Phdr phdr;
242 int i;
243
244 /* Find the first LOAD program header */
245 for (i = 0; i < phnum; i++) {
246 fseek(f_in, phoff + i * phentsize, SEEK_SET);
247 fread(&phdr, sizeof(phdr), 1, f_in);
248
249 if (phdr.p_type == PT_LOAD)
250 break;
251 }
252
253 datasz = phdr.p_filesz;
254 memsz = phdr.p_memsz;
255 } else if (e32_hdr.e_ident[EI_CLASS] == ELFCLASS64) {
256 Elf64_Phdr phdr;
257 int i;
258
259 /* Find the first LOAD program header */
260 for (i = 0; i < phnum; i++) {
261 fseek(f_in, phoff + i * phentsize, SEEK_SET);
262 fread(&phdr, sizeof(phdr), 1, f_in);
263
264 if (phdr.p_type == PT_LOAD)
265 break;
266 }
267
268 datasz = phdr.p_filesz;
269 memsz = phdr.p_memsz;
270 }
271
272 buf = malloc(datasz);
273 if (!buf) {
274 perror("malloc");
275 exit(EXIT_FAILURE);
276 }
277
278 write_header(f_out, entry, datasz, memsz, class);
279
280 /* Write out the entire ELF shared object */
281 rewind(f_in);
282 rv = fread(buf, datasz, 1, f_in);
283 if (!rv && ferror(f_in)) {
284 fprintf(stderr, "Failed to read all bytes from input\n");
285 exit(EXIT_FAILURE);
286 }
287
288 fwrite(buf, datasz, rv, f_out);
289 free(buf);
290 fclose(f_out);
291 fclose(f_in);
292 return 0;
293 }
294