xref: /freebsd/contrib/elftoolchain/elfcopy/pe.c (revision faf139cc5dd3396181c11922bc6685c0c59b7b24)
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