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 3508 2016-12-27 06:19:39Z 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	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(&timestamp) != 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