Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/elftoolchain/elfcopy/archive.c
39563 views
1
/*-
2
* Copyright (c) 2007-2009 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/stat.h>
29
#include <err.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <unistd.h>
33
34
#ifndef LIBELF_AR
35
#include <archive.h>
36
#include <archive_entry.h>
37
#endif /* ! LIBELF_AR */
38
39
#include "elfcopy.h"
40
41
ELFTC_VCSID("$Id: archive.c 3490 2016-08-31 00:12:22Z emaste $");
42
43
#define _ARMAG_LEN 8 /* length of ar magic string */
44
#define _ARHDR_LEN 60 /* length of ar header */
45
#define _INIT_AS_CAP 128 /* initial archive string table size */
46
#define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
47
#define _INIT_SYMNAME_CAP 1024 /* initial sn table size */
48
#define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */
49
50
#ifndef LIBELF_AR
51
static void ac_read_objs(struct elfcopy *ecp, int ifd);
52
static void ac_write_cleanup(struct elfcopy *ecp);
53
static void ac_write_data(struct archive *a, const void *buf, size_t s);
54
static void ac_write_objs(struct elfcopy *ecp, int ofd);
55
#endif /* ! LIBELF_AR */
56
static void add_to_ar_str_table(struct elfcopy *elfcopy, const char *name);
57
static void add_to_ar_sym_table(struct elfcopy *ecp, const char *name);
58
static void extract_arsym(struct elfcopy *ecp);
59
static void process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj);
60
static void sync_ar(struct elfcopy *ecp);
61
62
63
static void
64
process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj)
65
{
66
struct stat sb;
67
char *tempfile;
68
int fd;
69
70
/* Output to a temporary file. */
71
create_tempfile(NULL, &tempfile, &fd);
72
if ((ecp->eout = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) {
73
cleanup_tempfile(tempfile);
74
errx(EXIT_FAILURE, "elf_begin() failed: %s",
75
elf_errmsg(-1));
76
}
77
elf_flagelf(ecp->eout, ELF_C_SET, ELF_F_LAYOUT);
78
create_elf(ecp);
79
elf_end(ecp->ein);
80
elf_end(ecp->eout);
81
free(obj->buf);
82
obj->buf = NULL;
83
84
/* Extract archive symbols. */
85
if (lseek(fd, 0, SEEK_SET) < 0) {
86
cleanup_tempfile(tempfile);
87
err(EXIT_FAILURE, "lseek failed for '%s'", tempfile);
88
}
89
if ((ecp->eout = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
90
cleanup_tempfile(tempfile);
91
errx(EXIT_FAILURE, "elf_begin() failed: %s",
92
elf_errmsg(-1));
93
}
94
extract_arsym(ecp);
95
elf_end(ecp->eout);
96
97
if (fstat(fd, &sb) == -1) {
98
cleanup_tempfile(tempfile);
99
err(EXIT_FAILURE, "fstat %s failed", tempfile);
100
}
101
if (lseek(fd, 0, SEEK_SET) < 0) {
102
cleanup_tempfile(tempfile);
103
err(EXIT_FAILURE, "lseek %s failed", tempfile);
104
}
105
obj->size = sb.st_size;
106
if ((obj->maddr = malloc(obj->size)) == NULL) {
107
cleanup_tempfile(tempfile);
108
err(EXIT_FAILURE, "memory allocation failed for '%s'",
109
tempfile);
110
}
111
if ((size_t) read(fd, obj->maddr, obj->size) != obj->size) {
112
cleanup_tempfile(tempfile);
113
err(EXIT_FAILURE, "read failed for '%s'", tempfile);
114
}
115
if (cleanup_tempfile(tempfile) < 0)
116
err(EXIT_FAILURE, "unlink %s failed", tempfile);
117
free(tempfile);
118
tempfile = NULL;
119
close(fd);
120
if (strlen(obj->name) > _MAXNAMELEN_SVR4)
121
add_to_ar_str_table(ecp, obj->name);
122
ecp->rela_off += _ARHDR_LEN + obj->size + obj->size % 2;
123
STAILQ_INSERT_TAIL(&ecp->v_arobj, obj, objs);
124
}
125
126
/*
127
* Append to the archive string table buffer.
128
*/
129
static void
130
add_to_ar_str_table(struct elfcopy *ecp, const char *name)
131
{
132
133
if (ecp->as == NULL) {
134
ecp->as_cap = _INIT_AS_CAP;
135
ecp->as_sz = 0;
136
if ((ecp->as = malloc(ecp->as_cap)) == NULL)
137
err(EXIT_FAILURE, "malloc failed");
138
}
139
140
/*
141
* The space required for holding one member name in as table includes:
142
* strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding).
143
*/
144
while (ecp->as_sz + strlen(name) + 3 > ecp->as_cap) {
145
ecp->as_cap *= 2;
146
ecp->as = realloc(ecp->as, ecp->as_cap);
147
if (ecp->as == NULL)
148
err(EXIT_FAILURE, "realloc failed");
149
}
150
strncpy(&ecp->as[ecp->as_sz], name, strlen(name));
151
ecp->as_sz += strlen(name);
152
ecp->as[ecp->as_sz++] = '/';
153
ecp->as[ecp->as_sz++] = '\n';
154
}
155
156
/*
157
* Append to the archive symbol table buffer.
158
*/
159
static void
160
add_to_ar_sym_table(struct elfcopy *ecp, const char *name)
161
{
162
163
if (ecp->s_so == NULL) {
164
if ((ecp->s_so = malloc(_INIT_SYMOFF_CAP)) == NULL)
165
err(EXIT_FAILURE, "malloc failed");
166
ecp->s_so_cap = _INIT_SYMOFF_CAP;
167
ecp->s_cnt = 0;
168
}
169
170
if (ecp->s_sn == NULL) {
171
if ((ecp->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
172
err(EXIT_FAILURE, "malloc failed");
173
ecp->s_sn_cap = _INIT_SYMNAME_CAP;
174
ecp->s_sn_sz = 0;
175
}
176
177
if (ecp->s_cnt * sizeof(uint32_t) >= ecp->s_so_cap) {
178
ecp->s_so_cap *= 2;
179
ecp->s_so = realloc(ecp->s_so, ecp->s_so_cap);
180
if (ecp->s_so == NULL)
181
err(EXIT_FAILURE, "realloc failed");
182
}
183
ecp->s_so[ecp->s_cnt] = ecp->rela_off;
184
ecp->s_cnt++;
185
186
/*
187
* The space required for holding one symbol name in sn table includes:
188
* strlen(name) + (1 for '\n') + (possibly 1 for padding).
189
*/
190
while (ecp->s_sn_sz + strlen(name) + 2 > ecp->s_sn_cap) {
191
ecp->s_sn_cap *= 2;
192
ecp->s_sn = realloc(ecp->s_sn, ecp->s_sn_cap);
193
if (ecp->s_sn == NULL)
194
err(EXIT_FAILURE, "realloc failed");
195
}
196
strncpy(&ecp->s_sn[ecp->s_sn_sz], name, strlen(name));
197
ecp->s_sn_sz += strlen(name);
198
ecp->s_sn[ecp->s_sn_sz++] = '\0';
199
}
200
201
static void
202
sync_ar(struct elfcopy *ecp)
203
{
204
size_t s_sz; /* size of archive symbol table. */
205
size_t pm_sz; /* size of pseudo members */
206
int i;
207
208
/*
209
* Pad the symbol name string table. It is treated specially because
210
* symbol name table should be padded by a '\0', not the common '\n'
211
* for other members. The size of sn table includes the pad bit.
212
*/
213
if (ecp->s_cnt != 0 && ecp->s_sn_sz % 2 != 0)
214
ecp->s_sn[ecp->s_sn_sz++] = '\0';
215
216
/*
217
* Archive string table is padded by a "\n" as the normal members.
218
* The difference is that the size of archive string table counts
219
* in the pad bit, while normal members' size fileds do not.
220
*/
221
if (ecp->as != NULL && ecp->as_sz % 2 != 0)
222
ecp->as[ecp->as_sz++] = '\n';
223
224
/*
225
* If there is a symbol table, calculate the size of pseudo members,
226
* convert previously stored relative offsets to absolute ones, and
227
* then make them Big Endian.
228
*
229
* absolute_offset = htobe32(relative_offset + size_of_pseudo_members)
230
*/
231
232
if (ecp->s_cnt != 0) {
233
s_sz = (ecp->s_cnt + 1) * sizeof(uint32_t) + ecp->s_sn_sz;
234
pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
235
if (ecp->as != NULL)
236
pm_sz += _ARHDR_LEN + ecp->as_sz;
237
for (i = 0; (size_t)i < ecp->s_cnt; i++)
238
*(ecp->s_so + i) = htobe32(*(ecp->s_so + i) +
239
pm_sz);
240
}
241
}
242
243
/*
244
* Extract global symbols from archive members.
245
*/
246
static void
247
extract_arsym(struct elfcopy *ecp)
248
{
249
Elf_Scn *scn;
250
GElf_Shdr shdr;
251
GElf_Sym sym;
252
Elf_Data *data;
253
char *name;
254
size_t n, shstrndx;
255
int elferr, tabndx, len, i;
256
257
if (elf_kind(ecp->eout) != ELF_K_ELF) {
258
warnx("internal: cannot extract symbols from non-elf object");
259
return;
260
}
261
if (elf_getshstrndx(ecp->eout, &shstrndx) == 0) {
262
warnx("elf_getshstrndx failed: %s", elf_errmsg(-1));
263
return;
264
}
265
266
tabndx = -1;
267
scn = NULL;
268
while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
269
if (gelf_getshdr(scn, &shdr) != &shdr) {
270
warnx("elf_getshdr failed: %s", elf_errmsg(-1));
271
continue;
272
}
273
if ((name = elf_strptr(ecp->eout, shstrndx, shdr.sh_name)) ==
274
NULL) {
275
warnx("elf_strptr failed: %s", elf_errmsg(-1));
276
continue;
277
}
278
if (strcmp(name, ".strtab") == 0) {
279
tabndx = elf_ndxscn(scn);
280
break;
281
}
282
}
283
elferr = elf_errno();
284
if (elferr != 0)
285
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
286
287
/* Ignore members without symbol table. */
288
if (tabndx == -1)
289
return;
290
291
scn = NULL;
292
while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
293
if (gelf_getshdr(scn, &shdr) != &shdr) {
294
warnx("elf_getshdr failed: %s", elf_errmsg(-1));
295
continue;
296
}
297
if (shdr.sh_type != SHT_SYMTAB)
298
continue;
299
300
data = NULL;
301
n = 0;
302
while (n < shdr.sh_size &&
303
(data = elf_getdata(scn, data)) != NULL) {
304
len = data->d_size / shdr.sh_entsize;
305
for (i = 0; i < len; i++) {
306
if (gelf_getsym(data, i, &sym) != &sym) {
307
warnx("gelf_getsym failed: %s",
308
elf_errmsg(-1));
309
continue;
310
}
311
312
/* keep only global or weak symbols */
313
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
314
GELF_ST_BIND(sym.st_info) != STB_WEAK)
315
continue;
316
317
/* keep only defined symbols */
318
if (sym.st_shndx == SHN_UNDEF)
319
continue;
320
321
if ((name = elf_strptr(ecp->eout, tabndx,
322
sym.st_name)) == NULL) {
323
warnx("elf_strptr failed: %s",
324
elf_errmsg(-1));
325
continue;
326
}
327
328
add_to_ar_sym_table(ecp, name);
329
}
330
}
331
}
332
elferr = elf_errno();
333
if (elferr != 0)
334
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
335
}
336
337
#ifndef LIBELF_AR
338
339
/*
340
* Convenient wrapper for general libarchive error handling.
341
*/
342
#define AC(CALL) do { \
343
if ((CALL)) \
344
errx(EXIT_FAILURE, "%s", archive_error_string(a)); \
345
} while (0)
346
347
/* Earlier versions of libarchive had some functions that returned 'void'. */
348
#if ARCHIVE_VERSION_NUMBER >= 2000000
349
#define ACV(CALL) AC(CALL)
350
#else
351
#define ACV(CALL) do { \
352
(CALL); \
353
} while (0)
354
#endif
355
356
int
357
ac_detect_ar(int ifd)
358
{
359
struct archive *a;
360
struct archive_entry *entry;
361
int r;
362
363
r = -1;
364
if ((a = archive_read_new()) == NULL)
365
return (0);
366
archive_read_support_format_ar(a);
367
if (archive_read_open_fd(a, ifd, 10240) == ARCHIVE_OK)
368
r = archive_read_next_header(a, &entry);
369
archive_read_close(a);
370
archive_read_free(a);
371
372
return (r == ARCHIVE_OK);
373
}
374
375
void
376
ac_create_ar(struct elfcopy *ecp, int ifd, int ofd)
377
{
378
379
ac_read_objs(ecp, ifd);
380
sync_ar(ecp);
381
ac_write_objs(ecp, ofd);
382
ac_write_cleanup(ecp);
383
}
384
385
static void
386
ac_read_objs(struct elfcopy *ecp, int ifd)
387
{
388
struct archive *a;
389
struct archive_entry *entry;
390
struct ar_obj *obj;
391
const char *name;
392
char *buff;
393
size_t size;
394
int r;
395
396
ecp->rela_off = 0;
397
if (lseek(ifd, 0, SEEK_SET) == -1)
398
err(EXIT_FAILURE, "lseek failed");
399
if ((a = archive_read_new()) == NULL)
400
errx(EXIT_FAILURE, "archive_read_new failed");
401
archive_read_support_format_ar(a);
402
AC(archive_read_open_fd(a, ifd, 10240));
403
for(;;) {
404
r = archive_read_next_header(a, &entry);
405
if (r == ARCHIVE_FATAL)
406
errx(EXIT_FAILURE, "%s", archive_error_string(a));
407
if (r == ARCHIVE_EOF)
408
break;
409
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
410
warnx("%s", archive_error_string(a));
411
if (r == ARCHIVE_RETRY)
412
continue;
413
414
name = archive_entry_pathname(entry);
415
416
/* skip pseudo members. */
417
if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
418
continue;
419
420
size = archive_entry_size(entry);
421
422
if (size > 0) {
423
if ((buff = malloc(size)) == NULL)
424
err(EXIT_FAILURE, "malloc failed");
425
if (archive_read_data(a, buff, size) != (ssize_t)size) {
426
warnx("%s", archive_error_string(a));
427
free(buff);
428
continue;
429
}
430
if ((obj = malloc(sizeof(*obj))) == NULL)
431
err(EXIT_FAILURE, "malloc failed");
432
if ((obj->name = strdup(name)) == NULL)
433
err(EXIT_FAILURE, "strdup failed");
434
obj->buf = buff;
435
obj->uid = archive_entry_uid(entry);
436
obj->gid = archive_entry_gid(entry);
437
obj->md = archive_entry_mode(entry);
438
obj->mtime = archive_entry_mtime(entry);
439
if ((ecp->ein = elf_memory(buff, size)) == NULL)
440
errx(EXIT_FAILURE, "elf_memory() failed: %s",
441
elf_errmsg(-1));
442
if (elf_kind(ecp->ein) != ELF_K_ELF)
443
errx(EXIT_FAILURE,
444
"file format not recognized");
445
process_ar_obj(ecp, obj);
446
}
447
}
448
AC(archive_read_close(a));
449
ACV(archive_read_free(a));
450
}
451
452
static void
453
ac_write_objs(struct elfcopy *ecp, int ofd)
454
{
455
struct archive *a;
456
struct archive_entry *entry;
457
struct ar_obj *obj;
458
time_t timestamp;
459
int nr;
460
461
if ((a = archive_write_new()) == NULL)
462
errx(EXIT_FAILURE, "archive_write_new failed");
463
archive_write_set_format_ar_svr4(a);
464
AC(archive_write_open_fd(a, ofd));
465
466
/* Write the archive symbol table, even if it's empty. */
467
entry = archive_entry_new();
468
archive_entry_copy_pathname(entry, "/");
469
if (elftc_timestamp(&timestamp) != 0)
470
err(EXIT_FAILURE, "elftc_timestamp");
471
archive_entry_set_mtime(entry, timestamp, 0);
472
archive_entry_set_size(entry, (ecp->s_cnt + 1) * sizeof(uint32_t) +
473
ecp->s_sn_sz);
474
AC(archive_write_header(a, entry));
475
nr = htobe32(ecp->s_cnt);
476
ac_write_data(a, &nr, sizeof(uint32_t));
477
ac_write_data(a, ecp->s_so, sizeof(uint32_t) * ecp->s_cnt);
478
ac_write_data(a, ecp->s_sn, ecp->s_sn_sz);
479
archive_entry_free(entry);
480
481
/* Write the archive string table, if exist. */
482
if (ecp->as != NULL) {
483
entry = archive_entry_new();
484
archive_entry_copy_pathname(entry, "//");
485
archive_entry_set_size(entry, ecp->as_sz);
486
AC(archive_write_header(a, entry));
487
ac_write_data(a, ecp->as, ecp->as_sz);
488
archive_entry_free(entry);
489
}
490
491
/* Write normal members. */
492
STAILQ_FOREACH(obj, &ecp->v_arobj, objs) {
493
entry = archive_entry_new();
494
archive_entry_copy_pathname(entry, obj->name);
495
archive_entry_set_uid(entry, obj->uid);
496
archive_entry_set_gid(entry, obj->gid);
497
archive_entry_set_mode(entry, obj->md);
498
archive_entry_set_size(entry, obj->size);
499
archive_entry_set_mtime(entry, obj->mtime, 0);
500
archive_entry_set_filetype(entry, AE_IFREG);
501
AC(archive_write_header(a, entry));
502
ac_write_data(a, obj->maddr, obj->size);
503
archive_entry_free(entry);
504
}
505
506
AC(archive_write_close(a));
507
ACV(archive_write_free(a));
508
}
509
510
static void
511
ac_write_cleanup(struct elfcopy *ecp)
512
{
513
struct ar_obj *obj, *obj_temp;
514
515
STAILQ_FOREACH_SAFE(obj, &ecp->v_arobj, objs, obj_temp) {
516
STAILQ_REMOVE(&ecp->v_arobj, obj, ar_obj, objs);
517
if (obj->maddr != NULL)
518
free(obj->maddr);
519
free(obj->name);
520
free(obj);
521
}
522
523
free(ecp->as);
524
free(ecp->s_so);
525
free(ecp->s_sn);
526
ecp->as = NULL;
527
ecp->s_so = NULL;
528
ecp->s_sn = NULL;
529
}
530
531
/*
532
* Wrapper for archive_write_data().
533
*/
534
static void
535
ac_write_data(struct archive *a, const void *buf, size_t s)
536
{
537
if (archive_write_data(a, buf, s) != (ssize_t)s)
538
errx(EXIT_FAILURE, "%s", archive_error_string(a));
539
}
540
541
#endif /* ! LIBELF_AR */
542
543