Home | History | Annotate | Download | only in efi
      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