Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/elftoolchain/libpe/libpe_section.c
39483 views
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 <assert.h>
29
#include <errno.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <unistd.h>
33
34
#include "_libpe.h"
35
36
ELFTC_VCSID("$Id: libpe_section.c 3446 2016-05-03 01:31:17Z emaste $");
37
38
PE_Scn *
39
libpe_alloc_scn(PE *pe)
40
{
41
PE_Scn *ps;
42
43
if ((ps = calloc(1, sizeof(PE_Scn))) == NULL) {
44
errno = ENOMEM;
45
return (NULL);
46
}
47
STAILQ_INIT(&ps->ps_b);
48
ps->ps_pe = pe;
49
50
return (ps);
51
}
52
53
void
54
libpe_release_scn(PE_Scn *ps)
55
{
56
PE *pe;
57
PE_SecBuf *sb, *_sb;
58
59
assert(ps != NULL);
60
61
pe = ps->ps_pe;
62
63
STAILQ_REMOVE(&pe->pe_scn, ps, _PE_Scn, ps_next);
64
65
STAILQ_FOREACH_SAFE(sb, &ps->ps_b, sb_next, _sb)
66
libpe_release_buffer(sb);
67
68
free(ps);
69
}
70
71
static int
72
cmp_scn(PE_Scn *a, PE_Scn *b)
73
{
74
75
if (a->ps_sh.sh_addr < b->ps_sh.sh_addr)
76
return (-1);
77
else if (a->ps_sh.sh_addr == b->ps_sh.sh_addr)
78
return (0);
79
else
80
return (1);
81
}
82
83
static void
84
sort_sections(PE *pe)
85
{
86
87
if (STAILQ_EMPTY(&pe->pe_scn))
88
return;
89
90
/* Sort the list of Scn by RVA in ascending order. */
91
STAILQ_SORT(&pe->pe_scn, _PE_Scn, ps_next, cmp_scn);
92
}
93
94
int
95
libpe_parse_section_headers(PE *pe)
96
{
97
char tmp[sizeof(PE_SecHdr)], *hdr;
98
PE_Scn *ps;
99
PE_SecHdr *sh;
100
PE_CoffHdr *ch;
101
PE_DataDir *dd;
102
int found, i;
103
104
assert(pe->pe_ch != NULL);
105
106
for (i = 0; (uint16_t) i < pe->pe_ch->ch_nsec; i++) {
107
if (read(pe->pe_fd, tmp, sizeof(PE_SecHdr)) !=
108
(ssize_t) sizeof(PE_SecHdr)) {
109
pe->pe_flags |= LIBPE_F_BAD_SEC_HEADER;
110
return (0);
111
}
112
113
if ((ps = libpe_alloc_scn(pe)) == NULL)
114
return (-1);
115
STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next);
116
ps->ps_ndx = ++pe->pe_nscn; /* Setion index is 1-based */
117
sh = &ps->ps_sh;
118
119
/*
120
* Note that the section name won't be NUL-terminated if
121
* its length happens to be 8.
122
*/
123
memcpy(sh->sh_name, tmp, sizeof(sh->sh_name));
124
hdr = tmp + 8;
125
PE_READ32(hdr, sh->sh_virtsize);
126
PE_READ32(hdr, sh->sh_addr);
127
PE_READ32(hdr, sh->sh_rawsize);
128
PE_READ32(hdr, sh->sh_rawptr);
129
PE_READ32(hdr, sh->sh_relocptr);
130
PE_READ32(hdr, sh->sh_lineptr);
131
PE_READ16(hdr, sh->sh_nreloc);
132
PE_READ16(hdr, sh->sh_nline);
133
PE_READ32(hdr, sh->sh_char);
134
}
135
136
/*
137
* For all the data directories that don't belong to any section,
138
* we create pseudo sections for them to make layout easier.
139
*/
140
dd = pe->pe_dd;
141
if (dd != NULL && dd->dd_total > 0) {
142
for (i = 0; (uint32_t) i < pe->pe_dd->dd_total; i++) {
143
if (dd->dd_e[i].de_size == 0)
144
continue;
145
found = 0;
146
STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) {
147
sh = &ps->ps_sh;
148
if (dd->dd_e[i].de_addr >= sh->sh_addr &&
149
dd->dd_e[i].de_addr + dd->dd_e[i].de_size <=
150
sh->sh_addr + sh->sh_virtsize) {
151
found = 1;
152
break;
153
}
154
}
155
if (found)
156
continue;
157
158
if ((ps = libpe_alloc_scn(pe)) == NULL)
159
return (-1);
160
STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next);
161
ps->ps_ndx = 0xFFFF0000U | i;
162
sh = &ps->ps_sh;
163
sh->sh_rawptr = dd->dd_e[i].de_addr; /* FIXME */
164
sh->sh_rawsize = dd->dd_e[i].de_size;
165
}
166
}
167
168
/*
169
* Also consider the COFF symbol table as a pseudo section.
170
*/
171
ch = pe->pe_ch;
172
if (ch->ch_nsym > 0) {
173
if ((ps = libpe_alloc_scn(pe)) == NULL)
174
return (-1);
175
STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next);
176
ps->ps_ndx = 0xFFFFFFFFU;
177
sh = &ps->ps_sh;
178
sh->sh_rawptr = ch->ch_symptr;
179
sh->sh_rawsize = ch->ch_nsym * PE_SYM_ENTRY_SIZE;
180
pe->pe_nsym = ch->ch_nsym;
181
}
182
183
/* PE file headers initialization is complete if we reach here. */
184
return (0);
185
}
186
187
int
188
libpe_load_section(PE *pe, PE_Scn *ps)
189
{
190
PE_SecHdr *sh;
191
PE_SecBuf *sb;
192
size_t sz;
193
char tmp[4];
194
195
assert(pe != NULL && ps != NULL);
196
assert((ps->ps_flags & LIBPE_F_LOAD_SECTION) == 0);
197
198
sh = &ps->ps_sh;
199
200
/* Allocate a PE_SecBuf struct without buffer for empty sections. */
201
if (sh->sh_rawsize == 0) {
202
(void) libpe_alloc_buffer(ps, 0);
203
ps->ps_flags |= LIBPE_F_LOAD_SECTION;
204
return (0);
205
}
206
207
if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) {
208
if (lseek(pe->pe_fd, (off_t) sh->sh_rawptr, SEEK_SET) < 0) {
209
errno = EIO;
210
return (-1);
211
}
212
}
213
214
if ((sb = libpe_alloc_buffer(ps, sh->sh_rawsize)) == NULL)
215
return (-1);
216
217
if (read(pe->pe_fd, sb->sb_pb.pb_buf, sh->sh_rawsize) !=
218
(ssize_t) sh->sh_rawsize) {
219
errno = EIO;
220
return (-1);
221
}
222
223
if (ps->ps_ndx == 0xFFFFFFFFU) {
224
/*
225
* Index 0xFFFFFFFF indicates this section is a pseudo
226
* section that contains the COFF symbol table. We should
227
* read in the string table right after it.
228
*/
229
if (read(pe->pe_fd, tmp, sizeof(tmp)) !=
230
(ssize_t) sizeof(tmp)) {
231
errno = EIO;
232
return (-1);
233
}
234
sz = le32dec(tmp);
235
236
/*
237
* The minimum value for the size field is 4, which indicates
238
* there is no string table.
239
*/
240
if (sz > 4) {
241
sz -= 4;
242
if ((sb = libpe_alloc_buffer(ps, sz)) == NULL)
243
return (-1);
244
if (read(pe->pe_fd, sb->sb_pb.pb_buf, sz) !=
245
(ssize_t) sz) {
246
errno = EIO;
247
return (-1);
248
}
249
}
250
}
251
252
ps->ps_flags |= LIBPE_F_LOAD_SECTION;
253
254
return (0);
255
}
256
257
int
258
libpe_load_all_sections(PE *pe)
259
{
260
PE_Scn *ps;
261
PE_SecHdr *sh;
262
unsigned r, s;
263
off_t off;
264
char tmp[256];
265
266
/* Calculate the current offset into the file. */
267
off = 0;
268
if (pe->pe_dh != NULL)
269
off += pe->pe_dh->dh_lfanew + 4;
270
if (pe->pe_ch != NULL)
271
off += sizeof(PE_CoffHdr) + pe->pe_ch->ch_optsize;
272
273
STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) {
274
if (ps->ps_flags & LIBPE_F_LOAD_SECTION)
275
continue;
276
sh = &ps->ps_sh;
277
278
/*
279
* For special files, we consume the padding in between
280
* and advance to the section offset.
281
*/
282
if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) {
283
/* Can't go backwards. */
284
if (off > sh->sh_rawptr) {
285
errno = EIO;
286
return (-1);
287
}
288
if (off < sh->sh_rawptr) {
289
r = sh->sh_rawptr - off;
290
for (; r > 0; r -= s) {
291
s = r > sizeof(tmp) ? sizeof(tmp) : r;
292
if (read(pe->pe_fd, tmp, s) !=
293
(ssize_t) s) {
294
errno = EIO;
295
return (-1);
296
}
297
}
298
}
299
}
300
301
/* Load the section content. */
302
if (libpe_load_section(pe, ps) < 0)
303
return (-1);
304
}
305
306
return (0);
307
}
308
309
int
310
libpe_resync_sections(PE *pe, off_t off)
311
{
312
PE_Scn *ps;
313
PE_SecHdr *sh;
314
size_t falign, nsec;
315
316
/* Firstly, sort all sections by their file offsets. */
317
sort_sections(pe);
318
319
/* Count the number of sections. */
320
nsec = 0;
321
STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) {
322
if (ps->ps_flags & LIBPE_F_STRIP_SECTION)
323
continue;
324
if (ps->ps_ndx & 0xFFFF0000U)
325
continue;
326
nsec++;
327
}
328
pe->pe_nscn = nsec;
329
330
/*
331
* Calculate the file offset for the first section. (`off' is
332
* currently pointing to the COFF header.)
333
*/
334
off += sizeof(PE_CoffHdr);
335
if (pe->pe_ch != NULL && pe->pe_ch->ch_optsize > 0)
336
off += pe->pe_ch->ch_optsize;
337
else {
338
switch (pe->pe_obj) {
339
case PE_O_PE32:
340
off += PE_COFF_OPT_SIZE_32;
341
break;
342
case PE_O_PE32P:
343
off += PE_COFF_OPT_SIZE_32P;
344
break;
345
case PE_O_COFF:
346
default:
347
break;
348
}
349
}
350
off += nsec * sizeof(PE_SecHdr);
351
352
/*
353
* Determine the file alignment for sections.
354
*/
355
if (pe->pe_oh != NULL && pe->pe_oh->oh_filealign > 0)
356
falign = pe->pe_oh->oh_filealign;
357
else {
358
/*
359
* Use the default file alignment defined by the
360
* PE/COFF specification.
361
*/
362
if (pe->pe_obj == PE_O_COFF)
363
falign = 4;
364
else
365
falign = 512;
366
}
367
368
/*
369
* Step through each section (and pseduo section) and verify
370
* alignment constraint and overlapping, make adjustment if need.
371
*/
372
pe->pe_rvamax = 0;
373
STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) {
374
if (ps->ps_flags & LIBPE_F_STRIP_SECTION)
375
continue;
376
377
sh = &ps->ps_sh;
378
379
if (sh->sh_addr + sh->sh_virtsize > pe->pe_rvamax)
380
pe->pe_rvamax = sh->sh_addr + sh->sh_virtsize;
381
382
if (ps->ps_ndx & 0xFFFF0000U)
383
ps->ps_falign = 4;
384
else
385
ps->ps_falign = falign;
386
387
off = roundup(off, ps->ps_falign);
388
389
if (off != sh->sh_rawptr)
390
ps->ps_flags |= PE_F_DIRTY;
391
392
if (ps->ps_flags & PE_F_DIRTY) {
393
if ((ps->ps_flags & LIBPE_F_LOAD_SECTION) == 0) {
394
if (libpe_load_section(pe, ps) < 0)
395
return (-1);
396
}
397
sh->sh_rawsize = libpe_resync_buffers(ps);
398
}
399
400
/*
401
* Sections only contains uninitialized data should set
402
* PointerToRawData to zero according to the PE/COFF
403
* specification.
404
*/
405
if (sh->sh_rawsize == 0)
406
sh->sh_rawptr = 0;
407
else
408
sh->sh_rawptr = off;
409
410
off += sh->sh_rawsize;
411
}
412
413
return (0);
414
}
415
416
off_t
417
libpe_write_section_headers(PE *pe, off_t off)
418
{
419
char tmp[sizeof(PE_SecHdr)], *hdr;
420
PE_Scn *ps;
421
PE_SecHdr *sh;
422
423
if (pe->pe_flags & LIBPE_F_BAD_SEC_HEADER || pe->pe_nscn == 0)
424
return (off);
425
426
if ((pe->pe_flags & LIBPE_F_DIRTY_SEC_HEADER) == 0) {
427
off += sizeof(PE_SecHdr) * pe->pe_ch->ch_nsec;
428
return (off);
429
}
430
431
STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) {
432
if (ps->ps_flags & LIBPE_F_STRIP_SECTION)
433
continue;
434
if (ps->ps_ndx & 0xFFFF0000U)
435
continue;
436
if ((pe->pe_flags & LIBPE_F_DIRTY_SEC_HEADER) == 0 &&
437
(ps->ps_flags & PE_F_DIRTY) == 0)
438
goto next_header;
439
440
sh = &ps->ps_sh;
441
442
memcpy(tmp, sh->sh_name, sizeof(sh->sh_name));
443
hdr = tmp + 8;
444
PE_WRITE32(hdr, sh->sh_virtsize);
445
PE_WRITE32(hdr, sh->sh_addr);
446
PE_WRITE32(hdr, sh->sh_rawsize);
447
PE_WRITE32(hdr, sh->sh_rawptr);
448
PE_WRITE32(hdr, sh->sh_relocptr);
449
PE_WRITE32(hdr, sh->sh_lineptr);
450
PE_WRITE16(hdr, sh->sh_nreloc);
451
PE_WRITE16(hdr, sh->sh_nline);
452
PE_WRITE32(hdr, sh->sh_char);
453
454
if (write(pe->pe_fd, tmp, sizeof(PE_SecHdr)) !=
455
(ssize_t) sizeof(PE_SecHdr)) {
456
errno = EIO;
457
return (-1);
458
}
459
460
next_header:
461
off += sizeof(PE_SecHdr);
462
}
463
464
return (off);
465
}
466
467
off_t
468
libpe_write_sections(PE *pe, off_t off)
469
{
470
PE_Scn *ps;
471
PE_SecHdr *sh;
472
473
if (pe->pe_flags & LIBPE_F_BAD_SEC_HEADER)
474
return (off);
475
476
STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) {
477
sh = &ps->ps_sh;
478
479
if (ps->ps_flags & LIBPE_F_STRIP_SECTION)
480
continue;
481
482
/* Skip empty sections. */
483
if (sh->sh_rawptr == 0 || sh->sh_rawsize == 0)
484
continue;
485
486
/*
487
* Padding between sections. (padding always written
488
* in case the the section headers or sections are
489
* moved or shrunk.)
490
*/
491
assert(off <= sh->sh_rawptr);
492
if (off < sh->sh_rawptr)
493
libpe_pad(pe, sh->sh_rawptr - off);
494
495
if ((ps->ps_flags & PE_F_DIRTY) == 0) {
496
assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);
497
if (lseek(pe->pe_fd,
498
(off_t) (sh->sh_rawptr + sh->sh_rawsize),
499
SEEK_SET) < 0) {
500
errno = EIO;
501
return (-1);
502
}
503
off = sh->sh_rawptr + sh->sh_rawsize;
504
continue;
505
}
506
507
off = sh->sh_rawptr;
508
509
if (libpe_write_buffers(ps) < 0)
510
return (-1);
511
512
off += sh->sh_rawsize;
513
514
ps->ps_flags &= ~PE_F_DIRTY;
515
}
516
517
return (off);
518
}
519
520