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
37ELFTC_VCSID("$Id: pe.c 3477 2016-05-25 20:00:42Z kaiwang27 $");
38
39/* Convert ELF object to Portable Executable (PE). */
40void
41create_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;
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 = roundup(sh.sh_size, poh.oh_filealign);
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
194		/* Mark relocation section "discardable". */
195		if (strcmp(name, ".reloc") == 0)
196			psh.sh_char |= IMAGE_SCN_MEM_DISCARDABLE;
197
198		if (pe_update_section_header(ps, &psh) < 0) {
199			warn("pe_update_section_header() failed");
200			continue;
201		}
202
203		/* Copy section content. */
204		if ((pb = pe_newbuffer(ps)) == NULL) {
205			warn("pe_newbuffer() failed");
206			continue;
207		}
208		pb->pb_align = 1;
209		pb->pb_off = 0;
210		pb->pb_size = roundup(sh.sh_size, poh.oh_filealign);
211		if ((pb->pb_buf = calloc(1, pb->pb_size)) == NULL) {
212			warn("calloc failed");
213			continue;
214		}
215		memcpy(pb->pb_buf, d->d_buf, sh.sh_size);
216	}
217	elferr = elf_errno();
218	if (elferr != 0)
219		warnx("elf_nextscn() failed: %s", elf_errmsg(elferr));
220
221	/* Update PE optional header. */
222	if (pe_update_opt_header(pe, &poh) < 0)
223		err(EXIT_FAILURE, "pe_update_opt_header() failed");
224
225	/* Write out PE/COFF object. */
226	if (pe_update(pe) < 0)
227		err(EXIT_FAILURE, "pe_update() failed");
228
229	pe_finish(pe);
230	elf_end(e);
231}
232