]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/elfcopy/pe.c
Change bsdiff to use divsufsort suffix sort library instead of qsufsort,
[FreeBSD/FreeBSD.git] / contrib / elftoolchain / elfcopy / pe.c
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 3477 2016-05-25 20:00:42Z 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;
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 }