xref: /freebsd/contrib/elftoolchain/elfcopy/pe.c (revision d0aa5645)
1839529caSEd Maste /*-
2839529caSEd Maste  * Copyright (c) 2016 Kai Wang
3839529caSEd Maste  * All rights reserved.
4839529caSEd Maste  *
5839529caSEd Maste  * Redistribution and use in source and binary forms, with or without
6839529caSEd Maste  * modification, are permitted provided that the following conditions
7839529caSEd Maste  * are met:
8839529caSEd Maste  * 1. Redistributions of source code must retain the above copyright
9839529caSEd Maste  *    notice, this list of conditions and the following disclaimer.
10839529caSEd Maste  * 2. Redistributions in binary form must reproduce the above copyright
11839529caSEd Maste  *    notice, this list of conditions and the following disclaimer in the
12839529caSEd Maste  *    documentation and/or other materials provided with the distribution.
13839529caSEd Maste  *
14839529caSEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15839529caSEd Maste  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16839529caSEd Maste  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17839529caSEd Maste  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18839529caSEd Maste  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19839529caSEd Maste  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20839529caSEd Maste  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21839529caSEd Maste  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22839529caSEd Maste  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23839529caSEd Maste  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24839529caSEd Maste  * SUCH DAMAGE.
25839529caSEd Maste  */
26839529caSEd Maste 
27839529caSEd Maste #include <sys/param.h>
28839529caSEd Maste #include <err.h>
29839529caSEd Maste #include <gelf.h>
30839529caSEd Maste #include <libpe.h>
31839529caSEd Maste #include <stdlib.h>
32839529caSEd Maste #include <string.h>
33839529caSEd Maste #include <time.h>
34839529caSEd Maste 
35839529caSEd Maste #include "elfcopy.h"
36839529caSEd Maste 
37b6d812d2SEd Maste ELFTC_VCSID("$Id: pe.c 3508 2016-12-27 06:19:39Z kaiwang27 $");
38839529caSEd Maste 
39839529caSEd Maste /* Convert ELF object to Portable Executable (PE). */
40839529caSEd Maste void
create_pe(struct elfcopy * ecp,int ifd,int ofd)41839529caSEd Maste create_pe(struct elfcopy *ecp, int ifd, int ofd)
42839529caSEd Maste {
43839529caSEd Maste 	Elf *e;
44839529caSEd Maste 	Elf_Scn *scn;
45839529caSEd Maste 	Elf_Data *d;
46839529caSEd Maste 	GElf_Ehdr eh;
47839529caSEd Maste 	GElf_Shdr sh;
48839529caSEd Maste 	PE *pe;
49839529caSEd Maste 	PE_Scn *ps;
50839529caSEd Maste 	PE_SecHdr psh;
51839529caSEd Maste 	PE_CoffHdr pch;
52839529caSEd Maste 	PE_OptHdr poh;
53839529caSEd Maste 	PE_Object po;
54839529caSEd Maste 	PE_Buffer *pb;
55839529caSEd Maste 	const char *name;
56839529caSEd Maste 	size_t indx;
57b6d812d2SEd Maste 	time_t timestamp;
58d38447b5SEd Maste 	int elferr;
59839529caSEd Maste 
60839529caSEd Maste 	if (ecp->otf == ETF_EFI || ecp->oem == EM_X86_64)
61839529caSEd Maste 		po = PE_O_PE32P;
62839529caSEd Maste 	else
63839529caSEd Maste 		po = PE_O_PE32;
64839529caSEd Maste 
65839529caSEd Maste 	if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
66839529caSEd Maste 		errx(EXIT_FAILURE, "elf_begin() failed: %s",
67839529caSEd Maste 		    elf_errmsg(-1));
68839529caSEd Maste 
69839529caSEd Maste 	if (gelf_getehdr(e, &eh) == NULL)
70839529caSEd Maste 		errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
71839529caSEd Maste 		    elf_errmsg(-1));
72839529caSEd Maste 
73d0aa5645SEd Maste 	if (elf_getshstrndx(e, &indx) == 0)
74839529caSEd Maste 		errx(EXIT_FAILURE, "elf_getshstrndx() failed: %s",
75839529caSEd Maste 		    elf_errmsg(-1));
76839529caSEd Maste 
77839529caSEd Maste 	if ((pe = pe_init(ofd, PE_C_WRITE, po)) == NULL)
78839529caSEd Maste 		err(EXIT_FAILURE, "pe_init() failed");
79839529caSEd Maste 
80839529caSEd Maste 	/* Setup PE COFF header. */
81839529caSEd Maste 	memset(&pch, 0, sizeof(pch));
82839529caSEd Maste 	switch (ecp->oem) {
83839529caSEd Maste 	case EM_386:
84839529caSEd Maste 		pch.ch_machine = IMAGE_FILE_MACHINE_I386;
85839529caSEd Maste 		break;
86839529caSEd Maste 	case EM_X86_64:
87839529caSEd Maste 		pch.ch_machine = IMAGE_FILE_MACHINE_AMD64;
88839529caSEd Maste 		break;
89839529caSEd Maste 	default:
90839529caSEd Maste 		pch.ch_machine = IMAGE_FILE_MACHINE_UNKNOWN;
91839529caSEd Maste 		break;
92839529caSEd Maste 	}
93b6d812d2SEd Maste 	if (elftc_timestamp(&timestamp) != 0)
94b6d812d2SEd Maste 		err(EXIT_FAILURE, "elftc_timestamp");
95b6d812d2SEd Maste 	pch.ch_timestamp = (uint32_t) timestamp;
96839529caSEd Maste 	if (pe_update_coff_header(pe, &pch) < 0)
97839529caSEd Maste 		err(EXIT_FAILURE, "pe_update_coff_header() failed");
98839529caSEd Maste 
99839529caSEd Maste 	/* Setup PE optional header. */
100839529caSEd Maste 	memset(&poh, 0, sizeof(poh));
101839529caSEd Maste 	if (ecp->otf == ETF_EFI)
102839529caSEd Maste 		poh.oh_subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION;
103839529caSEd Maste 	poh.oh_entry = (uint32_t) eh.e_entry;
104839529caSEd Maste 
105839529caSEd Maste 	/*
106839529caSEd Maste 	 * Default section alignment and file alignment. (Here the
107839529caSEd Maste 	 * section alignment is set to the default page size of the
108839529caSEd Maste 	 * archs supported. We should use different section alignment
109839529caSEd Maste 	 * for some arch. (e.g. IA64)
110839529caSEd Maste 	 */
111839529caSEd Maste 	poh.oh_secalign = 0x1000;
112839529caSEd Maste 	poh.oh_filealign = 0x200;
113839529caSEd Maste 
114839529caSEd Maste 	/* Copy sections. */
115839529caSEd Maste 	scn = NULL;
116839529caSEd Maste 	while ((scn = elf_nextscn(e, scn)) != NULL) {
117839529caSEd Maste 
118839529caSEd Maste 		/*
119839529caSEd Maste 		 * Read in ELF section.
120839529caSEd Maste 		 */
121839529caSEd Maste 
122839529caSEd Maste 		if (gelf_getshdr(scn, &sh) == NULL) {
123839529caSEd Maste 			warnx("gelf_getshdr() failed: %s", elf_errmsg(-1));
124839529caSEd Maste 			(void) elf_errno();
125839529caSEd Maste 			continue;
126839529caSEd Maste 		}
127d0aa5645SEd Maste 		if ((name = elf_strptr(e, indx, sh.sh_name)) ==
128839529caSEd Maste 		    NULL) {
129839529caSEd Maste 			warnx("elf_strptr() failed: %s", elf_errmsg(-1));
130839529caSEd Maste 			(void) elf_errno();
131839529caSEd Maste 			continue;
132839529caSEd Maste 		}
133839529caSEd Maste 
134839529caSEd Maste 		/* Skip sections unneeded. */
135839529caSEd Maste 		if (strcmp(name, ".shstrtab") == 0 ||
136839529caSEd Maste 		    strcmp(name, ".symtab") == 0 ||
137839529caSEd Maste 		    strcmp(name, ".strtab") == 0)
138839529caSEd Maste 			continue;
139839529caSEd Maste 
140839529caSEd Maste 		if ((d = elf_getdata(scn, NULL)) == NULL) {
141839529caSEd Maste 			warnx("elf_getdata() failed: %s", elf_errmsg(-1));
142839529caSEd Maste 			(void) elf_errno();
143839529caSEd Maste 			continue;
144839529caSEd Maste 		}
145839529caSEd Maste 
146839529caSEd Maste 		if (strcmp(name, ".text") == 0) {
147839529caSEd Maste 			poh.oh_textbase = (uint32_t) sh.sh_addr;
148839529caSEd Maste 			poh.oh_textsize = (uint32_t) roundup(sh.sh_size,
149839529caSEd Maste 			    poh.oh_filealign);
150839529caSEd Maste 		} else {
151839529caSEd Maste 			if (po == PE_O_PE32 && strcmp(name, ".data") == 0)
152839529caSEd Maste 				poh.oh_database = sh.sh_addr;
153839529caSEd Maste 			if (sh.sh_type == SHT_NOBITS)
154839529caSEd Maste 				poh.oh_bsssize += (uint32_t)
155839529caSEd Maste 				    roundup(sh.sh_size, poh.oh_filealign);
156839529caSEd Maste 			else if (sh.sh_flags & SHF_ALLOC)
157839529caSEd Maste 				poh.oh_datasize += (uint32_t)
158839529caSEd Maste 				    roundup(sh.sh_size, poh.oh_filealign);
159839529caSEd Maste 		}
160839529caSEd Maste 
161839529caSEd Maste 		/*
162839529caSEd Maste 		 * Create PE/COFF section.
163839529caSEd Maste 		 */
164839529caSEd Maste 
165839529caSEd Maste 		if ((ps = pe_newscn(pe)) == NULL) {
166839529caSEd Maste 			warn("pe_newscn() failed");
167839529caSEd Maste 			continue;
168839529caSEd Maste 		}
169839529caSEd Maste 
170839529caSEd Maste 		/*
171839529caSEd Maste 		 * Setup PE/COFF section header. The section name is not
172839529caSEd Maste 		 * NUL-terminated if its length happens to be 8. Long
173839529caSEd Maste 		 * section name should be truncated for PE image according
174839529caSEd Maste 		 * to the PE/COFF specification.
175839529caSEd Maste 		 */
176839529caSEd Maste 		memset(&psh, 0, sizeof(psh));
177839529caSEd Maste 		strncpy(psh.sh_name, name, sizeof(psh.sh_name));
178839529caSEd Maste 		psh.sh_addr = sh.sh_addr;
179839529caSEd Maste 		psh.sh_virtsize = sh.sh_size;
180839529caSEd Maste 		if (sh.sh_type != SHT_NOBITS)
181d38447b5SEd Maste 			psh.sh_rawsize = roundup(sh.sh_size, poh.oh_filealign);
182839529caSEd Maste 		else
183839529caSEd Maste 			psh.sh_char |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
184839529caSEd Maste 
185839529caSEd Maste 		/*
186839529caSEd Maste 		 * Translate ELF section flags to PE/COFF section flags.
187839529caSEd Maste 		 */
188839529caSEd Maste 		psh.sh_char |= IMAGE_SCN_MEM_READ;
189839529caSEd Maste 		if (sh.sh_flags & SHF_WRITE)
190839529caSEd Maste 			psh.sh_char |= IMAGE_SCN_MEM_WRITE;
191839529caSEd Maste 		if (sh.sh_flags & SHF_EXECINSTR)
192839529caSEd Maste 			psh.sh_char |= IMAGE_SCN_MEM_EXECUTE |
193839529caSEd Maste 			    IMAGE_SCN_CNT_CODE;
194839529caSEd Maste 		if ((sh.sh_flags & SHF_ALLOC) && (psh.sh_char & 0xF0) == 0)
195839529caSEd Maste 			psh.sh_char |= IMAGE_SCN_CNT_INITIALIZED_DATA;
196839529caSEd Maste 
197839529caSEd Maste 		/* Mark relocation section "discardable". */
198839529caSEd Maste 		if (strcmp(name, ".reloc") == 0)
199839529caSEd Maste 			psh.sh_char |= IMAGE_SCN_MEM_DISCARDABLE;
200839529caSEd Maste 
201839529caSEd Maste 		if (pe_update_section_header(ps, &psh) < 0) {
202839529caSEd Maste 			warn("pe_update_section_header() failed");
203839529caSEd Maste 			continue;
204839529caSEd Maste 		}
205839529caSEd Maste 
206839529caSEd Maste 		/* Copy section content. */
207839529caSEd Maste 		if ((pb = pe_newbuffer(ps)) == NULL) {
208839529caSEd Maste 			warn("pe_newbuffer() failed");
209839529caSEd Maste 			continue;
210839529caSEd Maste 		}
211839529caSEd Maste 		pb->pb_align = 1;
212839529caSEd Maste 		pb->pb_off = 0;
213d0aa5645SEd Maste 		if (sh.sh_type != SHT_NOBITS) {
214d38447b5SEd Maste 			pb->pb_size = roundup(sh.sh_size, poh.oh_filealign);
215d38447b5SEd Maste 			if ((pb->pb_buf = calloc(1, pb->pb_size)) == NULL) {
216d38447b5SEd Maste 				warn("calloc failed");
217d38447b5SEd Maste 				continue;
218d38447b5SEd Maste 			}
219d38447b5SEd Maste 			memcpy(pb->pb_buf, d->d_buf, sh.sh_size);
220839529caSEd Maste 		}
221d0aa5645SEd Maste 	}
222839529caSEd Maste 	elferr = elf_errno();
223839529caSEd Maste 	if (elferr != 0)
224839529caSEd Maste 		warnx("elf_nextscn() failed: %s", elf_errmsg(elferr));
225839529caSEd Maste 
226839529caSEd Maste 	/* Update PE optional header. */
227839529caSEd Maste 	if (pe_update_opt_header(pe, &poh) < 0)
228839529caSEd Maste 		err(EXIT_FAILURE, "pe_update_opt_header() failed");
229839529caSEd Maste 
230839529caSEd Maste 	/* Write out PE/COFF object. */
231839529caSEd Maste 	if (pe_update(pe) < 0)
232839529caSEd Maste 		err(EXIT_FAILURE, "pe_update() failed");
233839529caSEd Maste 
234839529caSEd Maste 	pe_finish(pe);
235839529caSEd Maste 	elf_end(e);
236839529caSEd Maste }
237