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 */ 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 156 static void usage(char *progname) 157 { 158 fprintf(stderr, "usage: %s <ELF shared object> <output file>\n", 159 progname); 160 } 161 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