2 * Copyright (C) 2011 Intel Corporation; author Matt Fleming
4 * Wrap the ELF shared library in a PE32 (32bit) or PE32+ (64bit) suit.
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.
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
17 #include <linux/elf.h>
18 #include <sys/types.h>
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
;
34 #error "unsupported architecture"
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
46 static void write_header(FILE *f
, __uint32_t entry
, size_t data_size
,
47 __uint32_t so_memsz
, __uint8_t
class)
49 struct optional_hdr o_hdr
;
50 struct optional_hdr_pe32p o_hdr_pe32p
;
52 struct extra_hdr e_hdr
;
53 struct extra_hdr_pe32p e_hdr_pe32p
;
54 struct coff_hdr c_hdr
;
56 __uint32_t total_sz
= data_size
;
58 __uint32_t reloc_start
, reloc_end
;
61 * The header size have to be a multiple of file_align, which currently
68 memset(&hdr
, 0, sizeof(hdr
));
69 hdr
.msdos_signature
= MSDOS_SIGNATURE
;
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
76 hdr
.relocs_ptr
= 0x40;
78 hdr
.pe_hdr
= OFFSETOF(struct header
, pe_signature
);
79 hdr
.pe_signature
= PE_SIGNATURE
;
80 fwrite(&hdr
, sizeof(hdr
), 1, f
);
82 memset(&c_hdr
, 0, sizeof(c_hdr
));
83 c_hdr
.nr_sections
= 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
);
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
);
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
|
142 fwrite(&t_sec
, sizeof(t_sec
), 1, f
);
145 * Add some padding to align the ELF as needed
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");
153 fseek(f
, t_sec
.virtual_address
, SEEK_SET
);
156 static void usage(char *progname
)
158 fprintf(stderr
, "usage: %s <ELF shared object> <output file>\n",
162 int main(int argc
, char **argv
)
168 __uint64_t phoff
= 0;
169 __uint16_t phnum
= 0, phentsize
= 0;
173 size_t datasz
, memsz
, rv
;
180 f_in
= fopen(argv
[1], "r");
186 f_out
= fopen(argv
[2], "w");
193 * Parse the ELF header and find the entry point.
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
;
199 entry
= e32_hdr
.e_entry
;
200 phoff
= e32_hdr
.e_phoff
;
201 phnum
= e32_hdr
.e_phnum
;
202 phentsize
= e32_hdr
.e_phentsize
;
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!*/
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
;
217 fprintf(stderr
, "Unsupported architecture\n");
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");
229 if (!phoff
|| !phnum
) {
230 fprintf(stderr
, "Cannot find segment table\n");
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.
240 if (e32_hdr
.e_ident
[EI_CLASS
] == ELFCLASS32
) {
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
);
249 if (phdr
.p_type
== PT_LOAD
)
253 datasz
= phdr
.p_filesz
;
254 memsz
= phdr
.p_memsz
;
255 } else if (e32_hdr
.e_ident
[EI_CLASS
] == ELFCLASS64
) {
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
);
264 if (phdr
.p_type
== PT_LOAD
)
268 datasz
= phdr
.p_filesz
;
269 memsz
= phdr
.p_memsz
;
272 buf
= malloc(datasz
);
278 write_header(f_out
, entry
, datasz
, memsz
, class);
280 /* Write out the entire ELF shared object */
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");
288 fwrite(buf
, datasz
, rv
, f_out
);