From: Reimar Döffinger <reimar.doeffin...@gmx.de> --- libtcc.c | 2 +- tcc.h | 2 +- tccelf.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 101 insertions(+), 13 deletions(-)
diff --git a/libtcc.c b/libtcc.c index 164f1402..52410396 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1114,7 +1114,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) break; case AFF_BINTYPE_AR: - ret = tcc_load_archive(s1, fd, !(flags & AFF_WHOLE_ARCHIVE)); + ret = tcc_load_archive(s1, fd, filename, !(flags & AFF_WHOLE_ARCHIVE)); break; #ifdef TCC_TARGET_PE diff --git a/tcc.h b/tcc.h index e4173863..d27c69d9 100644 --- a/tcc.h +++ b/tcc.h @@ -1557,7 +1557,7 @@ ST_FUNC ssize_t full_read(int fd, void *buf, size_t count); ST_FUNC void *load_data(int fd, unsigned long file_offset, unsigned long size); ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h); ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset); -ST_FUNC int tcc_load_archive(TCCState *s1, int fd, int alacarte); +ST_FUNC int tcc_load_archive(TCCState *s1, int fd, const char *filename, int alacarte); ST_FUNC void add_array(TCCState *s1, const char *sec, int c); ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc); diff --git a/tccelf.c b/tccelf.c index 0110152d..b571c6fb 100644 --- a/tccelf.c +++ b/tccelf.c @@ -3000,6 +3000,8 @@ ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h) } else if (size >= 8) { if (0 == memcmp(h, ARMAG, 8)) return AFF_BINTYPE_AR; + if (0 == memcmp(h, "!<thin>\n", 8)) + return AFF_BINTYPE_AR; #ifdef TCC_TARGET_COFF if (((struct filehdr*)h)->f_magic == COFF_C67_MAGIC) return AFF_BINTYPE_C67; @@ -3335,8 +3337,59 @@ static int read_ar_header(int fd, int offset, ArchiveHeader *hdr) return len; } +/* loads the "//" entry containing the long file names of the archive */ +static int tcc_load_ar_names(TCCState *s1, int fd, const char *size_str, char **ret, int *ret_size) +{ + char *names = NULL; + int size = 0; + *ret = NULL; + *ret_size = 0; + + size = strtol(size_str, NULL, 0); + names = tcc_malloc(size); + if (size < 1 || full_read(fd, names, size) != size) { + tcc_free(names); + return 1; + } + for (int i = 0; i < size - 1; i++) { + if (names[i] == '/' && names[i + 1] == '\n') + names[i] = 0; + } + names[size-1] = 0; + *ret = names; + *ret_size = size; + return 0; +} + +static int tcc_add_ar_thin_file(TCCState *s1, const char *arname, const char *n, const char *fnames, int fnames_size) +{ + int pathlen = tcc_basename(arname) - arname; + int fname_ofs = strtol(n+1, NULL, 0); + int ret; + const char *basename; + int l; + char *combined_name; + if (n[0] != '/' || !fnames || fname_ofs < 0 || fname_ofs >= fnames_size) { + tcc_error_noabort("invalid thin archive"); + return 1; + } + basename = fnames + fname_ofs; + l = strlen(basename) + 1; + // arbitrary path length limit... + if (pathlen > 4096 || l > 4096) + return 1; + if (s1->verbose == 2) + printf(" -> %s\n", basename); + combined_name = tcc_malloc(pathlen + l); + memcpy(combined_name, arname, pathlen); + memcpy(combined_name + pathlen, basename, l); + ret = tcc_add_file_internal(s1, combined_name, AFF_TYPE_BIN | AFF_PRINT_ERROR); + tcc_free(combined_name); + return ret; +} + /* load only the objects which resolve undefined symbols */ -static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize) +static int tcc_load_alacarte(TCCState *s1, int fd, const char *filename, int size, int entrysize, unsigned long file_offset, int is_thin) { int i, bound, nsyms, sym_index, len, ret = -1; unsigned long long off; @@ -3345,6 +3398,9 @@ static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize) const uint8_t *ar_index; ElfW(Sym) *sym; ArchiveHeader hdr; + ArchiveHeader fnamehdr; + char *fnames = NULL; + int fnames_size = 0; data = tcc_malloc(size); if (full_read(fd, data, size) != size) @@ -3353,6 +3409,21 @@ static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize) ar_index = data + entrysize; ar_names = (char *) ar_index + nsyms * entrysize; + if (is_thin) { + file_offset += size; + len = read_ar_header(fd, file_offset, &fnamehdr); + if (len <= 0 || memcmp(fnamehdr.ar_fmag, ARFMAG, 2)) { + tcc_error_noabort("invalid archive"); + goto the_end; + } + if (strcmp(fnamehdr.ar_name, "//")) { + tcc_error_noabort("file name directory not found"); + goto the_end; + } + if (tcc_load_ar_names(s1, fd, fnamehdr.ar_size, &fnames, &fnames_size) != 0) + goto the_end; + } + do { bound = 0; for (p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) { @@ -3372,7 +3443,10 @@ static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize) off += len; if (s1->verbose == 2) printf(" -> %s\n", hdr.ar_name); - if (tcc_load_object_file(s1, fd, off) < 0) + if (is_thin) { + if (tcc_add_ar_thin_file(s1, filename, hdr.ar_name, fnames, fnames_size) != 0) + goto the_end; + } else if (tcc_load_object_file(s1, fd, off) < 0) goto the_end; ++bound; } @@ -3380,20 +3454,23 @@ static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize) ret = 0; the_end: tcc_free(data); + tcc_free(fnames); return ret; } /* load a '.a' file */ -ST_FUNC int tcc_load_archive(TCCState *s1, int fd, int alacarte) +ST_FUNC int tcc_load_archive(TCCState *s1, int fd, const char *filename, int alacarte) { ArchiveHeader hdr; - /* char magic[8]; */ - int size, len; + char *fnames = NULL; + int fnames_size; + char magic[8]; + int size, len, is_thin; unsigned long file_offset; ElfW(Ehdr) ehdr; - /* skip magic which was already checked */ - /* full_read(fd, magic, sizeof(magic)); */ + full_read(fd, magic, sizeof(magic)); + is_thin = memcmp(magic, "!<thin>\n", 8) == 0; file_offset = sizeof ARMAG - 1; for(;;) { @@ -3409,17 +3486,28 @@ ST_FUNC int tcc_load_archive(TCCState *s1, int fd, int alacarte) if (alacarte) { /* coff symbol table : we handle it */ if (!strcmp(hdr.ar_name, "/")) - return tcc_load_alacarte(s1, fd, size, 4); + return tcc_load_alacarte(s1, fd, filename, size, 4, file_offset, is_thin); if (!strcmp(hdr.ar_name, "/SYM64/")) - return tcc_load_alacarte(s1, fd, size, 8); + return tcc_load_alacarte(s1, fd, filename, size, 8, file_offset, is_thin); + } else if (is_thin && !fnames && !strcmp(hdr.ar_name, "//")) { + if (tcc_load_ar_names(s1, fd, hdr.ar_size, &fnames, &fnames_size) != 0) + return -1; } else if (tcc_object_type(fd, &ehdr) == AFF_BINTYPE_REL) { if (s1->verbose == 2) printf(" -> %s\n", hdr.ar_name); - if (tcc_load_object_file(s1, fd, file_offset) < 0) + if (is_thin) size = 0; + if (is_thin && tcc_add_ar_thin_file(s1, filename, hdr.ar_name, fnames, fnames_size) != 0) { + tcc_free(fnames); return -1; - } + } + if (!is_thin && tcc_load_object_file(s1, fd, file_offset) < 0) + return -1; + } else if (is_thin && fnames) + // the files after the file name directory have no actual size anymore + size = 0; file_offset += size; } + tcc_free(fnames); } #ifndef ELF_OBJ_ONLY -- 2.39.3 (Apple Git-145) _______________________________________________ Tinycc-devel mailing list Tinycc-devel@nongnu.org https://lists.nongnu.org/mailman/listinfo/tinycc-devel