]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/libpe/libpe_dos.c
Add two missing eventhandler.h headers
[FreeBSD/FreeBSD.git] / contrib / elftoolchain / libpe / libpe_dos.c
1 /*-
2  * Copyright (c) 2015 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 <sys/types.h>
29 #include <assert.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include "_libpe.h"
36
37 ELFTC_VCSID("$Id: libpe_dos.c 3312 2016-01-10 09:23:51Z kaiwang27 $");
38
39 int
40 libpe_parse_msdos_header(PE *pe, char *hdr)
41 {
42         PE_DosHdr *dh;
43         char coff[sizeof(PE_CoffHdr)];
44         uint32_t pe_magic;
45         int i;
46
47         if ((pe->pe_stub = malloc(sizeof(PE_DosHdr))) == NULL) {
48                 errno = ENOMEM;
49                 return (-1);
50         }
51         memcpy(pe->pe_stub, hdr, sizeof(PE_DosHdr));
52
53         if ((dh = malloc(sizeof(*dh))) == NULL) {
54                 errno = ENOMEM;
55                 return (-1);
56         }
57         pe->pe_dh = dh;
58
59         /* Read the conventional MS-DOS EXE header. */
60         memcpy(dh->dh_magic, hdr, 2);
61         hdr += 2;
62         PE_READ16(hdr, dh->dh_lastsize);
63         PE_READ16(hdr, dh->dh_nblock);
64         PE_READ16(hdr, dh->dh_nreloc);
65         PE_READ16(hdr, dh->dh_hdrsize);
66         PE_READ16(hdr, dh->dh_minalloc);
67         PE_READ16(hdr, dh->dh_maxalloc);
68         PE_READ16(hdr, dh->dh_ss);
69         PE_READ16(hdr, dh->dh_sp);
70         PE_READ16(hdr, dh->dh_checksum);
71         PE_READ16(hdr, dh->dh_ip);
72         PE_READ16(hdr, dh->dh_cs);
73         PE_READ16(hdr, dh->dh_relocpos);
74         PE_READ16(hdr, dh->dh_noverlay);
75
76         /* Do not continue if the EXE is not a PE/NE/... (new executable) */
77         if (dh->dh_relocpos != 0x40) {
78                 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;
79                 return (0);
80         }
81
82         for (i = 0; i < 4; i++)
83                 PE_READ16(hdr, dh->dh_reserved1[i]);
84         PE_READ16(hdr, dh->dh_oemid);
85         PE_READ16(hdr, dh->dh_oeminfo);
86         for (i = 0; i < 10; i++)
87                 PE_READ16(hdr, dh->dh_reserved2[i]);
88         PE_READ32(hdr, dh->dh_lfanew);
89
90         /* Check if the e_lfanew pointer is valid. */
91         if (dh->dh_lfanew > pe->pe_fsize - 4) {
92                 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;
93                 return (0);
94         }
95
96         if (dh->dh_lfanew < sizeof(PE_DosHdr) &&
97             (pe->pe_flags & LIBPE_F_SPECIAL_FILE)) {
98                 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;
99                 return (0);
100         }
101
102         if (dh->dh_lfanew > sizeof(PE_DosHdr)) {
103                 pe->pe_stub_ex = dh->dh_lfanew - sizeof(PE_DosHdr);
104                 if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) {
105                         /* Read in DOS stub now. */
106                         if (libpe_read_msdos_stub(pe) < 0) {
107                                 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;
108                                 return (0);
109                         }
110                 }
111         }
112
113         if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) {
114                 /* Jump to the PE header. */
115                 if (lseek(pe->pe_fd, (off_t) dh->dh_lfanew, SEEK_SET) < 0) {
116                         pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;
117                         return (0);
118                 }
119         }
120
121         if (read(pe->pe_fd, &pe_magic, 4) != 4 ||
122             htole32(pe_magic) != PE_SIGNATURE) {
123                 pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;
124                 return (0);
125         }
126
127         if (read(pe->pe_fd, coff, sizeof(coff)) != (ssize_t) sizeof(coff)) {
128                 pe->pe_flags |= LIBPE_F_BAD_COFF_HEADER;
129                 return (0);
130         }
131
132         return (libpe_parse_coff_header(pe, coff));
133 }
134
135 int
136 libpe_read_msdos_stub(PE *pe)
137 {
138         void *m;
139
140         assert(pe->pe_stub_ex > 0 &&
141             (pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0);
142
143         if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) {
144                 if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_SET) <
145                     0) {
146                         errno = EIO;
147                         goto fail;
148                 }
149         }
150
151         if ((m = realloc(pe->pe_stub, sizeof(PE_DosHdr) + pe->pe_stub_ex)) ==
152             NULL) {
153                 errno = ENOMEM;
154                 goto fail;
155         }
156         pe->pe_stub = m;
157
158         if (read(pe->pe_fd, pe->pe_stub + sizeof(PE_DosHdr), pe->pe_stub_ex) !=
159             (ssize_t) pe->pe_stub_ex) {
160                 errno = EIO;
161                 goto fail;
162         }
163
164         pe->pe_flags |= LIBPE_F_LOAD_DOS_STUB;
165
166         /* Search for the Rich header embedded just before the PE header. */
167         (void) libpe_parse_rich_header(pe);
168
169         return (0);
170
171 fail:
172         pe->pe_stub_ex = 0;
173
174         return (-1);
175 }
176
177 /*
178  * The "standard" MS-DOS stub displaying "This program cannot be run in
179  * DOS mode".
180  */
181 static const char msdos_stub[] = {
182     '\x0e','\x1f','\xba','\x0e','\x00','\xb4','\x09','\xcd',
183     '\x21','\xb8','\x01','\x4c','\xcd','\x21','\x54','\x68',
184     '\x69','\x73','\x20','\x70','\x72','\x6f','\x67','\x72',
185     '\x61','\x6d','\x20','\x63','\x61','\x6e','\x6e','\x6f',
186     '\x74','\x20','\x62','\x65','\x20','\x72','\x75','\x6e',
187     '\x20','\x69','\x6e','\x20','\x44','\x4f','\x53','\x20',
188     '\x6d','\x6f','\x64','\x65','\x2e','\x0d','\x0d','\x0a',
189     '\x24','\x00','\x00','\x00','\x00','\x00','\x00','\x00',
190 };
191
192 static void
193 init_dos_header(PE_DosHdr *dh)
194 {
195
196         dh->dh_magic[0] = 'M';
197         dh->dh_magic[1] = 'Z';
198         dh->dh_lastsize = 144;
199         dh->dh_nblock = 3;
200         dh->dh_hdrsize = 4;
201         dh->dh_maxalloc = 65535;
202         dh->dh_sp = 184;
203         dh->dh_relocpos = 0x40;
204         dh->dh_lfanew = 0x80;
205 }
206
207 off_t
208 libpe_write_msdos_stub(PE *pe, off_t off)
209 {
210         PE_DosHdr *dh;
211         char tmp[sizeof(PE_DosHdr)], *hdr;
212         off_t d;
213         int i, strip_rich;
214
215         strip_rich = 0;
216
217         if (pe->pe_cmd == PE_C_RDWR) {
218                 assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);
219
220                 if (pe->pe_dh != NULL &&
221                     (pe->pe_flags & PE_F_STRIP_DOS_STUB)) {
222                         /*
223                          * If we strip MS-DOS stub, everything after it
224                          * needs rewritten.
225                          */
226                         pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;
227                         goto done;
228                 }
229
230                 /*
231                  * lseek(2) to the PE signature if MS-DOS stub is not
232                  * modified.
233                  */
234                 if (pe->pe_dh != NULL &&
235                     (pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) == 0 &&
236                     (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 &&
237                     (pe->pe_flags & PE_F_STRIP_RICH_HEADER) == 0) {
238                         if (lseek(pe->pe_fd,
239                             (off_t) (sizeof(PE_DosHdr) + pe->pe_stub_ex),
240                             SEEK_CUR) < 0) {
241                                 errno = EIO;
242                                 return (-1);
243                         }
244                         off = sizeof(PE_DosHdr) + pe->pe_stub_ex;
245                         goto done;
246                 }
247
248                 /* Check if we should strip the Rich header. */
249                 if (pe->pe_dh != NULL && pe->pe_stub_app == NULL &&
250                     (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 &&
251                     (pe->pe_flags & PE_F_STRIP_RICH_HEADER)) {
252                         if ((pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0) {
253                                 (void) libpe_read_msdos_stub(pe);
254                                 if (lseek(pe->pe_fd, off, SEEK_SET) < 0) {
255                                         errno = EIO;
256                                         return (-1);
257                                 }
258                         }
259                         if (pe->pe_rh != NULL) {
260                                 strip_rich = 1;
261                                 pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER;
262                         }
263                 }
264
265                 /*
266                  * If length of MS-DOS stub will change, Mark the PE
267                  * signature is broken so that the PE signature and the
268                  * headers follow it will be rewritten.
269                  *
270                  * The sections should be loaded now since the stub might
271                  * overwrite the section data.
272                  */
273                 if ((pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) ||
274                     (pe->pe_stub_app != NULL && pe->pe_stub_app_sz !=
275                         sizeof(PE_DosHdr) + pe->pe_stub_ex) || strip_rich) {
276                         if (libpe_load_all_sections(pe) < 0)
277                                 return (-1);
278                         if (lseek(pe->pe_fd, off, SEEK_SET) < 0) {
279                                 errno = EIO;
280                                 return (-1);
281                         }
282                         pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;
283                 }
284         }
285
286         if (pe->pe_flags & PE_F_STRIP_DOS_STUB)
287                 goto done;
288
289         /* Always use application supplied MS-DOS stub, if exists. */
290         if (pe->pe_stub_app != NULL && pe->pe_stub_app_sz > 0) {
291                 if (write(pe->pe_fd, pe->pe_stub_app, pe->pe_stub_app_sz) !=
292                     (ssize_t) pe->pe_stub_app_sz) {
293                         errno = EIO;
294                         return (-1);
295                 }
296                 off = pe->pe_stub_app_sz;
297                 goto done;
298         }
299
300         /*
301          * Write MS-DOS header.
302          */
303
304         if (pe->pe_dh == NULL) {
305                 if ((dh = calloc(1, sizeof(PE_DosHdr))) == NULL) {
306                         errno = ENOMEM;
307                         return (-1);
308                 }
309                 pe->pe_dh = dh;
310
311                 init_dos_header(dh);
312
313                 pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER;
314         } else
315                 dh = pe->pe_dh;
316
317         if (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER)
318                 init_dos_header(dh);
319
320         if (strip_rich) {
321                 d = pe->pe_rh_start - pe->pe_stub;
322                 dh->dh_lfanew = roundup(d, 8);
323         }
324
325         if ((pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) ||
326             (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER)) {
327                 memcpy(tmp, dh->dh_magic, 2);
328                 hdr = tmp + 2;
329                 PE_WRITE16(hdr, dh->dh_lastsize);
330                 PE_WRITE16(hdr, dh->dh_nblock);
331                 PE_WRITE16(hdr, dh->dh_nreloc);
332                 PE_WRITE16(hdr, dh->dh_hdrsize);
333                 PE_WRITE16(hdr, dh->dh_minalloc);
334                 PE_WRITE16(hdr, dh->dh_maxalloc);
335                 PE_WRITE16(hdr, dh->dh_ss);
336                 PE_WRITE16(hdr, dh->dh_sp);
337                 PE_WRITE16(hdr, dh->dh_checksum);
338                 PE_WRITE16(hdr, dh->dh_ip);
339                 PE_WRITE16(hdr, dh->dh_cs);
340                 PE_WRITE16(hdr, dh->dh_relocpos);
341                 PE_WRITE16(hdr, dh->dh_noverlay);
342                 for (i = 0; i < 4; i++)
343                         PE_WRITE16(hdr, dh->dh_reserved1[i]);
344                 PE_WRITE16(hdr, dh->dh_oemid);
345                 PE_WRITE16(hdr, dh->dh_oeminfo);
346                 for (i = 0; i < 10; i++)
347                         PE_WRITE16(hdr, dh->dh_reserved2[i]);
348                 PE_WRITE32(hdr, dh->dh_lfanew);
349
350                 if (write(pe->pe_fd, tmp, sizeof(tmp)) !=
351                     (ssize_t) sizeof(tmp)) {
352                         errno = EIO;
353                         return (-1);
354                 }
355         } else {
356                 assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);
357                 if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_CUR) <
358                     0) {
359                         errno = EIO;
360                         return (-1);
361                 }
362         }
363
364         off = sizeof(PE_DosHdr);
365
366         /*
367          * Write the MS-DOS stub.
368          */
369
370         if (strip_rich) {
371                 assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);
372                 assert(pe->pe_stub != NULL && pe->pe_rh_start != NULL);
373                 d = pe->pe_rh_start - pe->pe_stub;
374                 if (lseek(pe->pe_fd, d, SEEK_SET) < 0) {
375                         errno = EIO;
376                         return (-1);
377                 }
378                 off = d;
379                 goto done;
380         }
381
382         if (pe->pe_cmd == PE_C_RDWR) {
383                 if (lseek(pe->pe_fd, (off_t) pe->pe_stub_ex, SEEK_CUR) < 0) {
384                         errno = EIO;
385                         return (-1);
386                 }
387                 off += pe->pe_stub_ex;
388                 goto done;
389         }
390
391         if (write(pe->pe_fd, msdos_stub, sizeof(msdos_stub)) !=
392             (ssize_t) sizeof(msdos_stub)) {
393                 errno = EIO;
394                 return (-1);
395         }
396         off += sizeof(msdos_stub);
397
398 done:
399         pe->pe_flags &= ~LIBPE_F_DIRTY_DOS_HEADER;
400         pe->pe_flags &= ~LIBPE_F_BAD_DOS_HEADER;
401
402         return (off);
403 }