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 3508 2016-12-27 06:19:39Z 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 time_t timestamp; 58 int elferr; 59 60 if (ecp->otf == ETF_EFI || ecp->oem == EM_X86_64) 61 po = PE_O_PE32P; 62 else 63 po = PE_O_PE32; 64 65 if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) 66 errx(EXIT_FAILURE, "elf_begin() failed: %s", 67 elf_errmsg(-1)); 68 69 if (gelf_getehdr(e, &eh) == NULL) 70 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", 71 elf_errmsg(-1)); 72 73 if (elf_getshstrndx(e, &indx) == 0) 74 errx(EXIT_FAILURE, "elf_getshstrndx() failed: %s", 75 elf_errmsg(-1)); 76 77 if ((pe = pe_init(ofd, PE_C_WRITE, po)) == NULL) 78 err(EXIT_FAILURE, "pe_init() failed"); 79 80 /* Setup PE COFF header. */ 81 memset(&pch, 0, sizeof(pch)); 82 switch (ecp->oem) { 83 case EM_386: 84 pch.ch_machine = IMAGE_FILE_MACHINE_I386; 85 break; 86 case EM_X86_64: 87 pch.ch_machine = IMAGE_FILE_MACHINE_AMD64; 88 break; 89 default: 90 pch.ch_machine = IMAGE_FILE_MACHINE_UNKNOWN; 91 break; 92 } 93 if (elftc_timestamp(×tamp) != 0) 94 err(EXIT_FAILURE, "elftc_timestamp"); 95 pch.ch_timestamp = (uint32_t) timestamp; 96 if (pe_update_coff_header(pe, &pch) < 0) 97 err(EXIT_FAILURE, "pe_update_coff_header() failed"); 98 99 /* Setup PE optional header. */ 100 memset(&poh, 0, sizeof(poh)); 101 if (ecp->otf == ETF_EFI) 102 poh.oh_subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION; 103 poh.oh_entry = (uint32_t) eh.e_entry; 104 105 /* 106 * Default section alignment and file alignment. (Here the 107 * section alignment is set to the default page size of the 108 * archs supported. We should use different section alignment 109 * for some arch. (e.g. IA64) 110 */ 111 poh.oh_secalign = 0x1000; 112 poh.oh_filealign = 0x200; 113 114 /* Copy sections. */ 115 scn = NULL; 116 while ((scn = elf_nextscn(e, scn)) != NULL) { 117 118 /* 119 * Read in ELF section. 120 */ 121 122 if (gelf_getshdr(scn, &sh) == NULL) { 123 warnx("gelf_getshdr() failed: %s", elf_errmsg(-1)); 124 (void) elf_errno(); 125 continue; 126 } 127 if ((name = elf_strptr(e, indx, sh.sh_name)) == 128 NULL) { 129 warnx("elf_strptr() failed: %s", elf_errmsg(-1)); 130 (void) elf_errno(); 131 continue; 132 } 133 134 /* Skip sections unneeded. */ 135 if (strcmp(name, ".shstrtab") == 0 || 136 strcmp(name, ".symtab") == 0 || 137 strcmp(name, ".strtab") == 0) 138 continue; 139 140 if ((d = elf_getdata(scn, NULL)) == NULL) { 141 warnx("elf_getdata() failed: %s", elf_errmsg(-1)); 142 (void) elf_errno(); 143 continue; 144 } 145 146 if (strcmp(name, ".text") == 0) { 147 poh.oh_textbase = (uint32_t) sh.sh_addr; 148 poh.oh_textsize = (uint32_t) roundup(sh.sh_size, 149 poh.oh_filealign); 150 } else { 151 if (po == PE_O_PE32 && strcmp(name, ".data") == 0) 152 poh.oh_database = sh.sh_addr; 153 if (sh.sh_type == SHT_NOBITS) 154 poh.oh_bsssize += (uint32_t) 155 roundup(sh.sh_size, poh.oh_filealign); 156 else if (sh.sh_flags & SHF_ALLOC) 157 poh.oh_datasize += (uint32_t) 158 roundup(sh.sh_size, poh.oh_filealign); 159 } 160 161 /* 162 * Create PE/COFF section. 163 */ 164 165 if ((ps = pe_newscn(pe)) == NULL) { 166 warn("pe_newscn() failed"); 167 continue; 168 } 169 170 /* 171 * Setup PE/COFF section header. The section name is not 172 * NUL-terminated if its length happens to be 8. Long 173 * section name should be truncated for PE image according 174 * to the PE/COFF specification. 175 */ 176 memset(&psh, 0, sizeof(psh)); 177 strncpy(psh.sh_name, name, sizeof(psh.sh_name)); 178 psh.sh_addr = sh.sh_addr; 179 psh.sh_virtsize = sh.sh_size; 180 if (sh.sh_type != SHT_NOBITS) 181 psh.sh_rawsize = roundup(sh.sh_size, poh.oh_filealign); 182 else 183 psh.sh_char |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; 184 185 /* 186 * Translate ELF section flags to PE/COFF section flags. 187 */ 188 psh.sh_char |= IMAGE_SCN_MEM_READ; 189 if (sh.sh_flags & SHF_WRITE) 190 psh.sh_char |= IMAGE_SCN_MEM_WRITE; 191 if (sh.sh_flags & SHF_EXECINSTR) 192 psh.sh_char |= IMAGE_SCN_MEM_EXECUTE | 193 IMAGE_SCN_CNT_CODE; 194 if ((sh.sh_flags & SHF_ALLOC) && (psh.sh_char & 0xF0) == 0) 195 psh.sh_char |= IMAGE_SCN_CNT_INITIALIZED_DATA; 196 197 /* Mark relocation section "discardable". */ 198 if (strcmp(name, ".reloc") == 0) 199 psh.sh_char |= IMAGE_SCN_MEM_DISCARDABLE; 200 201 if (pe_update_section_header(ps, &psh) < 0) { 202 warn("pe_update_section_header() failed"); 203 continue; 204 } 205 206 /* Copy section content. */ 207 if ((pb = pe_newbuffer(ps)) == NULL) { 208 warn("pe_newbuffer() failed"); 209 continue; 210 } 211 pb->pb_align = 1; 212 pb->pb_off = 0; 213 if (sh.sh_type != SHT_NOBITS) { 214 pb->pb_size = roundup(sh.sh_size, poh.oh_filealign); 215 if ((pb->pb_buf = calloc(1, pb->pb_size)) == NULL) { 216 warn("calloc failed"); 217 continue; 218 } 219 memcpy(pb->pb_buf, d->d_buf, sh.sh_size); 220 } 221 } 222 elferr = elf_errno(); 223 if (elferr != 0) 224 warnx("elf_nextscn() failed: %s", elf_errmsg(elferr)); 225 226 /* Update PE optional header. */ 227 if (pe_update_opt_header(pe, &poh) < 0) 228 err(EXIT_FAILURE, "pe_update_opt_header() failed"); 229 230 /* Write out PE/COFF object. */ 231 if (pe_update(pe) < 0) 232 err(EXIT_FAILURE, "pe_update() failed"); 233 234 pe_finish(pe); 235 elf_end(e); 236 } 237