1 /*- 2 * Copyright (c) 2016 Kai Wang 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <err.h> 29 #include <gelf.h> 30 #include <libpe.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 35 #include "elfcopy.h" 36 37 ELFTC_VCSID("$Id: pe.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); 38 39 /* Convert ELF object to Portable Executable (PE). */ 40 void 41 create_pe(struct elfcopy *ecp, int ifd, int ofd) 42 { 43 Elf *e; 44 Elf_Scn *scn; 45 Elf_Data *d; 46 GElf_Ehdr eh; 47 GElf_Shdr sh; 48 PE *pe; 49 PE_Scn *ps; 50 PE_SecHdr psh; 51 PE_CoffHdr pch; 52 PE_OptHdr poh; 53 PE_Object po; 54 PE_Buffer *pb; 55 const char *name; 56 size_t indx; 57 int elferr, i; 58 59 if (ecp->otf == ETF_EFI || ecp->oem == EM_X86_64) 60 po = PE_O_PE32P; 61 else 62 po = PE_O_PE32; 63 64 if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) 65 errx(EXIT_FAILURE, "elf_begin() failed: %s", 66 elf_errmsg(-1)); 67 68 if (gelf_getehdr(e, &eh) == NULL) 69 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", 70 elf_errmsg(-1)); 71 72 if (elf_getshstrndx(ecp->ein, &indx) == 0) 73 errx(EXIT_FAILURE, "elf_getshstrndx() failed: %s", 74 elf_errmsg(-1)); 75 76 if ((pe = pe_init(ofd, PE_C_WRITE, po)) == NULL) 77 err(EXIT_FAILURE, "pe_init() failed"); 78 79 /* Setup PE COFF header. */ 80 memset(&pch, 0, sizeof(pch)); 81 switch (ecp->oem) { 82 case EM_386: 83 pch.ch_machine = IMAGE_FILE_MACHINE_I386; 84 break; 85 case EM_X86_64: 86 pch.ch_machine = IMAGE_FILE_MACHINE_AMD64; 87 break; 88 default: 89 pch.ch_machine = IMAGE_FILE_MACHINE_UNKNOWN; 90 break; 91 } 92 pch.ch_timestamp = (uint32_t) time(NULL); 93 if (pe_update_coff_header(pe, &pch) < 0) 94 err(EXIT_FAILURE, "pe_update_coff_header() failed"); 95 96 /* Setup PE optional header. */ 97 memset(&poh, 0, sizeof(poh)); 98 if (ecp->otf == ETF_EFI) 99 poh.oh_subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION; 100 poh.oh_entry = (uint32_t) eh.e_entry; 101 102 /* 103 * Default section alignment and file alignment. (Here the 104 * section alignment is set to the default page size of the 105 * archs supported. We should use different section alignment 106 * for some arch. (e.g. IA64) 107 */ 108 poh.oh_secalign = 0x1000; 109 poh.oh_filealign = 0x200; 110 111 /* Copy sections. */ 112 scn = NULL; 113 while ((scn = elf_nextscn(e, scn)) != NULL) { 114 115 /* 116 * Read in ELF section. 117 */ 118 119 if (gelf_getshdr(scn, &sh) == NULL) { 120 warnx("gelf_getshdr() failed: %s", elf_errmsg(-1)); 121 (void) elf_errno(); 122 continue; 123 } 124 if ((name = elf_strptr(ecp->ein, indx, sh.sh_name)) == 125 NULL) { 126 warnx("elf_strptr() failed: %s", elf_errmsg(-1)); 127 (void) elf_errno(); 128 continue; 129 } 130 131 /* Skip sections unneeded. */ 132 if (strcmp(name, ".shstrtab") == 0 || 133 strcmp(name, ".symtab") == 0 || 134 strcmp(name, ".strtab") == 0) 135 continue; 136 137 if ((d = elf_getdata(scn, NULL)) == NULL) { 138 warnx("elf_getdata() failed: %s", elf_errmsg(-1)); 139 (void) elf_errno(); 140 continue; 141 } 142 143 if (strcmp(name, ".text") == 0) { 144 poh.oh_textbase = (uint32_t) sh.sh_addr; 145 poh.oh_textsize = (uint32_t) roundup(sh.sh_size, 146 poh.oh_filealign); 147 } else { 148 if (po == PE_O_PE32 && strcmp(name, ".data") == 0) 149 poh.oh_database = sh.sh_addr; 150 if (sh.sh_type == SHT_NOBITS) 151 poh.oh_bsssize += (uint32_t) 152 roundup(sh.sh_size, poh.oh_filealign); 153 else if (sh.sh_flags & SHF_ALLOC) 154 poh.oh_datasize += (uint32_t) 155 roundup(sh.sh_size, poh.oh_filealign); 156 } 157 158 /* 159 * Create PE/COFF section. 160 */ 161 162 if ((ps = pe_newscn(pe)) == NULL) { 163 warn("pe_newscn() failed"); 164 continue; 165 } 166 167 /* 168 * Setup PE/COFF section header. The section name is not 169 * NUL-terminated if its length happens to be 8. Long 170 * section name should be truncated for PE image according 171 * to the PE/COFF specification. 172 */ 173 memset(&psh, 0, sizeof(psh)); 174 strncpy(psh.sh_name, name, sizeof(psh.sh_name)); 175 psh.sh_addr = sh.sh_addr; 176 psh.sh_virtsize = sh.sh_size; 177 if (sh.sh_type != SHT_NOBITS) 178 psh.sh_rawsize = sh.sh_size; 179 else 180 psh.sh_char |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; 181 182 /* 183 * Translate ELF section flags to PE/COFF section flags. 184 */ 185 psh.sh_char |= IMAGE_SCN_MEM_READ; 186 if (sh.sh_flags & SHF_WRITE) 187 psh.sh_char |= IMAGE_SCN_MEM_WRITE; 188 if (sh.sh_flags & SHF_EXECINSTR) 189 psh.sh_char |= IMAGE_SCN_MEM_EXECUTE | 190 IMAGE_SCN_CNT_CODE; 191 if ((sh.sh_flags & SHF_ALLOC) && (psh.sh_char & 0xF0) == 0) 192 psh.sh_char |= IMAGE_SCN_CNT_INITIALIZED_DATA; 193 for (i = 0xE; i > 0; i--) { 194 if (sh.sh_addralign & (1U << (i - 1))) { 195 psh.sh_char |= i << 20; 196 break; 197 } 198 } 199 200 /* Mark relocation section "discardable". */ 201 if (strcmp(name, ".reloc") == 0) 202 psh.sh_char |= IMAGE_SCN_MEM_DISCARDABLE; 203 204 if (pe_update_section_header(ps, &psh) < 0) { 205 warn("pe_update_section_header() failed"); 206 continue; 207 } 208 209 /* Copy section content. */ 210 if ((pb = pe_newbuffer(ps)) == NULL) { 211 warn("pe_newbuffer() failed"); 212 continue; 213 } 214 pb->pb_align = 1; 215 pb->pb_off = 0; 216 pb->pb_size = sh.sh_size; 217 pb->pb_buf = d->d_buf; 218 } 219 elferr = elf_errno(); 220 if (elferr != 0) 221 warnx("elf_nextscn() failed: %s", elf_errmsg(elferr)); 222 223 /* Update PE optional header. */ 224 if (pe_update_opt_header(pe, &poh) < 0) 225 err(EXIT_FAILURE, "pe_update_opt_header() failed"); 226 227 /* Write out PE/COFF object. */ 228 if (pe_update(pe) < 0) 229 err(EXIT_FAILURE, "pe_update() failed"); 230 231 pe_finish(pe); 232 elf_end(e); 233 } 234