diff options
Diffstat (limited to 'src/arbits/slbt_archive_meta.c')
-rw-r--r-- | src/arbits/slbt_archive_meta.c | 888 |
1 files changed, 888 insertions, 0 deletions
diff --git a/src/arbits/slbt_archive_meta.c b/src/arbits/slbt_archive_meta.c new file mode 100644 index 0000000..8ef816c --- /dev/null +++ b/src/arbits/slbt_archive_meta.c @@ -0,0 +1,888 @@ +/*******************************************************************/ +/* slibtool: a strong libtool implementation, written in C */ +/* Copyright (C) 2016--2024 SysDeer Technologies, LLC */ +/* Released under the Standard MIT License; see COPYING.SLIBTOOL. */ +/*******************************************************************/ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <slibtool/slibtool.h> +#include <slibtool/slibtool_arbits.h> +#include "slibtool_ar_impl.h" +#include "slibtool_driver_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_visibility_impl.h" + +/* transient header info vector */ +struct ar_header_info { + struct ar_raw_file_header * phdr; + uint32_t attr; +}; + +static const char ar_signature[] = AR_SIGNATURE; + +static int slbt_ar_free_archive_meta_impl(struct slbt_archive_meta_impl * meta, int ret) +{ + if (meta) { + if (meta->armaps.armap_symrefs_32) + free(meta->armaps.armap_symrefs_32); + + if (meta->armaps.armap_symrefs_64) + free(meta->armaps.armap_symrefs_64); + + if (meta->hdrinfov) + free(meta->hdrinfov); + + if (meta->namestrs) + free(meta->namestrs); + + if (meta->syminfo) + free(meta->syminfo); + + if (meta->syminfv) + free(meta->syminfv); + + if (meta->memberv) + free(meta->memberv); + + if (meta->offsetv) + free(meta->offsetv); + + if (meta->members) + free(meta->members); + + if (meta->symstrv) + free(meta->symstrv); + + if (meta->mapstrv) + free(meta->mapstrv); + + if (meta->nminfo) + slbt_lib_free_txtfile_ctx(meta->nminfo); + + free(meta); + } + + return ret; +} + + +static int slbt_ar_read_octal(const char * mark, int len, uint32_t * dec) +{ + int i; + uint64_t res; + + for (; len && (mark[len-1]==AR_DEC_PADDING); ) + len--; + + for (i=0,res=0; i<len; i++) { + if ((mark[i] >= '0') && (mark[i] <= '7')) { + res *= 8; + res += (mark[i] - '0'); + } else { + return -1; + } + } + + *dec = res; + + return 0; +} + +static int slbt_ar_read_decimal_64(const char * mark, int len, uint64_t * dec) +{ + int i; + uint64_t res; + + for (; len && (mark[len-1]==AR_DEC_PADDING); ) + len--; + + for (i=0,res=0; i<len; i++) { + if ((mark[i] >= '0') && (mark[i] <= '9')) { + res *= 10; + res += (mark[i] - '0'); + } else { + return -1; + } + } + + *dec = res; + + return 0; +} + +static int slbt_ar_read_decimal_32(const char * mark, int len, uint32_t * dec) +{ + uint64_t res; + + if (slbt_ar_read_decimal_64(mark,len,&res) < 0) + return -1; + + *dec = res; + + return 0; +} + +static uint32_t slbt_ar_get_member_attr(struct ar_meta_member_info * m) +{ + const char * hdrname; + uint32_t hdrattr; + const char * data; + const char * data_cap; + const unsigned char * udata; + unsigned char uch; + const size_t siglen = sizeof(struct ar_raw_signature); + + hdrname = m->ar_file_header.ar_member_name; + hdrattr = m->ar_file_header.ar_header_attr; + + data = m->ar_object_data; + data_cap = &data[m->ar_file_header.ar_file_size]; + + if (hdrattr & AR_HEADER_ATTR_SYSV) { + /* long names member? */ + if ((hdrname[0] == '/') && (hdrname[1] == '/')) + return AR_MEMBER_ATTR_NAMESTRS; + + /* mips 64-bit armap member? */ + else if (!strncmp(hdrname,"/SYM64/",7)) + return AR_MEMBER_ATTR_ARMAP; + + /* armap member? */ + else if (hdrname[0] == '/' && (hdrname[1] == '\0')) + return AR_MEMBER_ATTR_ARMAP; + + /* nested archive? */ + else if (m->ar_file_header.ar_file_size >= siglen) + if (!strncmp(data,ar_signature,siglen)) + return AR_MEMBER_ATTR_ARCHIVE; + + } else if (hdrattr & AR_HEADER_ATTR_BSD) { + if (!strcmp(hdrname,"__.SYMDEF")) + return AR_MEMBER_ATTR_ARMAP; + + else if (!strcmp(hdrname,"__.SYMDEF SORTED")) + return AR_MEMBER_ATTR_ARMAP; + + else if (!strcmp(hdrname,"__.SYMDEF_64")) + return AR_MEMBER_ATTR_ARMAP; + + else if (!strcmp(hdrname,"__.SYMDEF_64 SORTED")) + return AR_MEMBER_ATTR_ARMAP; + } + + /* ascii only data? */ + for (; data<data_cap; ) { + if ((uch = *data) >= 0x80) + break; + + data++; + } + + if (data == data_cap) + return AR_MEMBER_ATTR_ASCII; + + data = m->ar_object_data; + udata = (unsigned char *)data; + + /* elf object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 5) + if ((udata[0] == 0x7f) + && (udata[1] == 'E') + && (udata[2] == 'L') + && (udata[3] == 'F')) + if ((m->ar_object_attr = AR_OBJECT_ATTR_ELF)) + return AR_MEMBER_ATTR_OBJECT; + + /* coff i386 object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 2) + if ((udata[0] == 0x4c) && (udata[1] == 0x01)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_COFF)) + return AR_MEMBER_ATTR_OBJECT; + + /* coff x86_64 object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 2) + if ((udata[0] == 0x64) && (udata[1] == 0x86)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_COFF)) + return AR_MEMBER_ATTR_OBJECT; + + /* big endian 32-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[0] == 0xfe) && (udata[1] == 0xed)) + if ((udata[2] == 0xfa) && (udata[3] == 0xce)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* big endian 64-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[0] == 0xfe) && (udata[1] == 0xed)) + if ((udata[2] == 0xfa) && (udata[3] == 0xcf)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* little endian 32-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[3] == 0xfe) && (udata[2] == 0xed)) + if ((udata[1] == 0xfa) && (udata[0] == 0xce)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* little endian 64-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[3] == 0xfe) && (udata[2] == 0xed)) + if ((udata[1] == 0xfa) && (udata[0] == 0xcf)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* all other */ + return AR_MEMBER_ATTR_DEFAULT; +} + +static int slbt_ar_parse_primary_armap( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * m) + +{ + struct ar_meta_member_info * memberp; + const char * hdrname; + uint32_t hdrattr; + + memberp = m->memberv[0]; + hdrname = memberp->ar_file_header.ar_member_name; + hdrattr = memberp->ar_file_header.ar_header_attr; + + if (!(memberp->ar_member_attr & AR_MEMBER_ATTR_ARMAP)) + return 0; + + if (hdrattr & AR_HEADER_ATTR_SYSV) { + /* mips 64-bit armap member? */ + if (!strncmp(hdrname,"/SYM64/",7)) + return slbt_ar_parse_primary_armap_sysv_64( + dctx,m); + + /* sysv 32-bit armap member */ + return slbt_ar_parse_primary_armap_sysv_32( + dctx,m); + + } else if (hdrattr & AR_HEADER_ATTR_BSD) { + if (!strcmp(hdrname,"__.SYMDEF")) + return slbt_ar_parse_primary_armap_bsd_32( + dctx,m); + + else if (!strcmp(hdrname,"__.SYMDEF SORTED")) + return slbt_ar_parse_primary_armap_bsd_32( + dctx,m); + + else if (!strcmp(hdrname,"__.SYMDEF_64")) + return slbt_ar_parse_primary_armap_bsd_64( + dctx,m); + + else if (!strcmp(hdrname,"__.SYMDEF_64 SORTED")) + return slbt_ar_parse_primary_armap_bsd_64( + dctx,m); + } + + return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_FLOW_ERROR); +} + +slbt_hidden struct ar_meta_member_info * slbt_archive_member_from_offset( + struct slbt_archive_meta_impl * meta, + off_t offset) +{ + intptr_t l,r,m; + off_t * offsetv; + + l = 0; + r = meta->nentries - 1; + + offsetv = meta->offsetv; + + while (l != r) { + m = (l + r) / 2; + m += (l + r) % 2; + + if (offsetv[m] > offset) { + r = --m; + } else { + l = m; + } + } + + return (offsetv[l] == offset) ? meta->memberv[l] : 0; +} + +int slbt_ar_get_archive_meta( + const struct slbt_driver_ctx * dctx, + const struct slbt_raw_archive * archive, + struct slbt_archive_meta ** meta) +{ + const char * mark; + const char * cap; + struct slbt_archive_meta_impl * m; + const char * slash; + const char * ch; + const char * fldcap; + size_t nelements; + uint64_t nentries; + uint64_t nmembers; + uint64_t stblsize; + uint64_t filesize; + uint64_t namelen; + uint64_t nameoff; + uint32_t attr; + void * s_addr; + void * m_addr; + const char * s_ptr; + const char * m_ptr; + struct ar_raw_file_header * arhdr; + struct ar_raw_file_header * arlongnames; + struct ar_meta_member_info * memberp; + char * longnamep; + size_t idx; + struct ar_meta_armap_ref_32 * symrefs_32; + struct ar_meta_armap_ref_64 * symrefs_64; + struct ar_header_info * hdrinfov; + struct ar_header_info * hdrinfov_cap; + struct ar_header_info * hdrinfov_next; + struct ar_header_info hdrinfobuf[AR_STACK_VECTOR_ELEMENTS]; + + /* init */ + hdrinfov = hdrinfobuf; + hdrinfov_cap = &hdrinfobuf[AR_STACK_VECTOR_ELEMENTS]; + nelements = AR_STACK_VECTOR_ELEMENTS; + + memset(hdrinfobuf,0,sizeof(hdrinfobuf)); + + mark = archive->map_addr; + cap = &mark[archive->map_size]; + + /* preliminary validation */ + if (archive->map_size < sizeof(struct ar_raw_signature)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_SIGNATURE); + + else if (strncmp(mark,ar_signature,sizeof(struct ar_raw_signature))) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_SIGNATURE); + + /* alloc */ + if (!(m = calloc(1,sizeof(*m)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + /* associated driver context */ + m->dctx = dctx; + + /* archive map info */ + m->armeta.r_archive.map_addr = archive->map_addr; + m->armeta.r_archive.map_size = archive->map_size; + + /* archive signature */ + m->armeta.r_signature = (struct ar_raw_signature *)mark; + m->armeta.m_signature = (struct ar_meta_signature *)ar_signature; + + /* signature only? */ + if (archive->map_size == sizeof(struct ar_raw_signature)) { + *meta = &m->armeta; + return 0; + } + + mark += sizeof(struct ar_raw_signature); + + /* only trailing null characters past the signature? */ + if (cap < &mark[sizeof(*arhdr)]) + for (ch=mark; ch<cap; ch++) + if (*ch) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + /* count entries, calculate string table size */ + for (nentries=0,stblsize=0,arlongnames=0; mark<cap; nentries++) { + arhdr = (struct ar_raw_file_header *)mark; + + /* file size */ + if ((slbt_ar_read_decimal_64( + arhdr->ar_file_size, + sizeof(arhdr->ar_file_size), + &filesize)) < 0) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + mark += sizeof(struct ar_raw_file_header); + + /* stblsize, member name type */ + fldcap = &arhdr->ar_file_id[sizeof(arhdr->ar_file_id)]; + + /* sysv long names table? */ + if ((arhdr->ar_file_id[0] == '/') && (arhdr->ar_file_id[1] == '/')) { + for (ch=&arhdr->ar_file_id[2]; ch<fldcap; ch++) + if (*ch != AR_DEC_PADDING) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + if (slbt_ar_read_decimal_64( + arhdr->ar_file_size, + sizeof(arhdr->ar_file_size), + &namelen) < 0) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + + /* duplicate long names member? */ + if (arlongnames) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_DUPLICATE_LONG_NAMES)); + + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + + stblsize++; + stblsize++; + stblsize++; + + stblsize += namelen; + + arlongnames = arhdr; + + /* the /SYM64/ string must be special cased, also below when it gets copied */ + } else if (!strncmp(arhdr->ar_file_id,"/SYM64/",7)) { + for (ch=&arhdr->ar_file_id[7]; ch<fldcap; ch++) + if (*ch != AR_DEC_PADDING) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + stblsize += 8; + + /* sysv armap member or sysv long name reference? */ + } else if (arhdr->ar_file_id[0] == '/') { + if (slbt_ar_read_decimal_64( + &arhdr->ar_file_id[1], + sizeof(arhdr->ar_file_id)-1, + &nameoff) < 0) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + if (arhdr->ar_file_id[1] == AR_DEC_PADDING) { + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + stblsize++; + stblsize++; + } else { + attr = AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_SYSV; + } + + /* bsd long name reference? */ + } else if ((arhdr->ar_file_id[0] == '#') + && (arhdr->ar_file_id[1] == '1') + && (arhdr->ar_file_id[2] == '/')) { + if (slbt_ar_read_decimal_64( + &arhdr->ar_file_id[3], + sizeof(arhdr->ar_file_id)-3, + &namelen) < 0) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + attr = AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_BSD; + + stblsize += namelen + 1; + + /* must be either a sysv short member name, or a (legacy) bsd short name */ + } else { + for (ch=arhdr->ar_file_id,slash=0; (ch<fldcap) && !slash; ch++) + if (*ch == '/') + slash = ch; + + if (slash) { + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + stblsize += (slash - arhdr->ar_file_id) + 1; + } else { + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_BSD; + stblsize += sizeof(arhdr->ar_file_id) + 1; + } + + for (; ch<fldcap; ) + if (*ch++ != AR_DEC_PADDING) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + } + + /* truncated data? */ + if (cap < &mark[filesize]) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_TRUNCATED_DATA)); + + /* ar member alignment */ + filesize += 1; + filesize |= 1; + filesize ^= 1; + + mark += filesize; + + /* only trailing null characters past the signature? */ + if (cap < &mark[sizeof(*arhdr)]) + for (; mark<cap; ) + if (*mark++) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_HEADER)); + + /* transient header info vector */ + if (&hdrinfov[nentries] == hdrinfov_cap) { + nelements = (nelements == AR_STACK_VECTOR_ELEMENTS) + ? (nelements << 4) : (nelements << 1); + + if (!(hdrinfov_next = calloc(nelements,sizeof(*hdrinfov)))) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_TRUNCATED_DATA)); + + for (idx=0; idx<nentries; idx++) { + hdrinfov_next[idx].phdr = hdrinfov[idx].phdr; + hdrinfov_next[idx].attr = hdrinfov[idx].attr; + }; + + if (hdrinfov != hdrinfobuf) + free(hdrinfov); + + hdrinfov = hdrinfov_next; + hdrinfov_cap = &hdrinfov_next[nelements]; + m->hdrinfov = hdrinfov; + } + + hdrinfov[nentries].phdr = arhdr; + hdrinfov[nentries].attr = attr; + } + + /* allocate name strings, member vector */ + if (!(m->namestrs = calloc(1,stblsize))) + return slbt_ar_free_archive_meta_impl( + m,SLBT_SYSTEM_ERROR(dctx,0)); + + if (!(m->offsetv = calloc(nentries+1,sizeof(*m->offsetv)))) + return slbt_ar_free_archive_meta_impl( + m,SLBT_SYSTEM_ERROR(dctx,0)); + + if (!(m->memberv = calloc(nentries+1,sizeof(*m->memberv)))) + return slbt_ar_free_archive_meta_impl( + m,SLBT_SYSTEM_ERROR(dctx,0)); + + if (!(m->members = calloc(nentries,sizeof(*m->members)))) + return slbt_ar_free_archive_meta_impl( + m,SLBT_SYSTEM_ERROR(dctx,0)); + + /* archive signature reference */ + s_addr = archive->map_addr; + s_ptr = s_addr; + + /* iterate, store meta data in library-friendly form */ + for (idx=0,longnamep=m->namestrs; idx<nentries; idx++) { + arhdr = hdrinfov[idx].phdr; + attr = hdrinfov[idx].attr; + + m_addr = arhdr; + m_ptr = m_addr; + + memberp = &m->members[idx]; + m->offsetv[idx] = m_ptr - s_ptr; + m->memberv[idx] = memberp; + + memberp->ar_file_header.ar_header_attr = attr; + + slbt_ar_read_decimal_64( + arhdr->ar_time_date_stamp, + sizeof(arhdr->ar_time_date_stamp), + &memberp->ar_file_header.ar_time_date_stamp); + + slbt_ar_read_decimal_32( + arhdr->ar_uid, + sizeof(arhdr->ar_uid), + &memberp->ar_file_header.ar_uid); + + slbt_ar_read_decimal_32( + arhdr->ar_gid, + sizeof(arhdr->ar_gid), + &memberp->ar_file_header.ar_gid); + + slbt_ar_read_octal( + arhdr->ar_file_mode, + sizeof(arhdr->ar_file_mode), + &memberp->ar_file_header.ar_file_mode); + + slbt_ar_read_decimal_64( + arhdr->ar_file_size, + sizeof(arhdr->ar_file_size), + &memberp->ar_file_header.ar_file_size); + + memberp->ar_file_header.ar_member_name = longnamep; + + if (attr == (AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV)) { + if ((arhdr->ar_file_id[0] == '/') && (arhdr->ar_file_id[1] == '/')) { + *longnamep++ = '/'; + *longnamep++ = '/'; + longnamep++; + + } else if ((arhdr->ar_file_id[0] == '/') && (arhdr->ar_file_id[1] == 'S')) { + *longnamep++ = '/'; + *longnamep++ = 'S'; + *longnamep++ = 'Y'; + *longnamep++ = 'M'; + *longnamep++ = '6'; + *longnamep++ = '4'; + *longnamep++ = '/'; + longnamep++; + + } else if (arhdr->ar_file_id[0] == '/') { + *longnamep++ = '/'; + longnamep++; + + } else { + ch = arhdr->ar_file_id; + + for (; (*ch != '/'); ) + *longnamep++ = *ch++; + + longnamep++; + } + + } else if (attr == (AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_BSD)) { + ch = arhdr->ar_file_id; + fldcap = &ch[sizeof(arhdr->ar_file_id)]; + + for (; (ch<fldcap) && (*ch != AR_DEC_PADDING); ) + *longnamep++ = *ch++; + + longnamep++; + + } else if (attr == (AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_SYSV)) { + slbt_ar_read_decimal_64( + &arhdr->ar_file_id[1], + sizeof(arhdr->ar_file_id) - 1, + &nameoff); + + ch = arlongnames->ar_file_id; + ch += sizeof(*arlongnames); + ch += nameoff; + + for (; *ch && (*ch != '/') && (*ch != AR_OBJ_PADDING); ) + *longnamep++ = *ch++; + + longnamep++; + + } else if (attr == (AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_BSD)) { + slbt_ar_read_decimal_64( + &arhdr->ar_file_id[3], + sizeof(arhdr->ar_file_id) - 3, + &namelen); + + mark = arhdr->ar_file_id; + mark += sizeof(*arhdr); + + memcpy(longnamep,mark,namelen); + + longnamep += namelen; + longnamep++; + } + + /* member raw header, object size, object data */ + mark = arhdr->ar_file_id; + mark += sizeof(*arhdr); + namelen = 0; + + if (attr == (AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_BSD)) { + slbt_ar_read_decimal_64( + &arhdr->ar_file_id[3], + sizeof(arhdr->ar_file_id)-3, + &namelen); + + namelen += 1; + namelen |= 1; + namelen ^= 1; + + mark += namelen; + }; + + memberp->ar_member_data = arhdr; + memberp->ar_object_data = (void *)mark; + memberp->ar_object_size = memberp->ar_file_header.ar_file_size - namelen; + + /* member attribute */ + memberp->ar_member_attr = slbt_ar_get_member_attr(memberp); + + /* pe/coff second linker member? */ + if ((idx == 1) && (memberp->ar_member_attr == AR_MEMBER_ATTR_ARMAP)) + if (hdrinfov[0].attr & AR_HEADER_ATTR_SYSV) + if (m->members[0].ar_member_attr == AR_MEMBER_ATTR_ARMAP) + if (attr & AR_HEADER_ATTR_SYSV) + memberp->ar_member_attr = AR_MEMBER_ATTR_LINKINFO; + + /* armap member must be the first */ + if ((memberp->ar_member_attr == AR_MEMBER_ATTR_ARMAP) && (idx > 0)) { + if (m->members[0].ar_member_attr == AR_MEMBER_ATTR_ARMAP) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_DUPLICATE_ARMAP_MEMBER)); + + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_MISPLACED_ARMAP_MEMBER)); + } + } + + /* number of archive members, including internal ones */ + m->nentries = nentries; + + /* primary armap (first linker member) */ + if (slbt_ar_parse_primary_armap(dctx,m) < 0) + return slbt_ar_free_archive_meta_impl( + m,SLBT_NESTED_ERROR(dctx)); + + for (idx=0,ch=m->symstrs; idx<m->armaps.armap_nsyms; idx++) { + m->symstrv[idx] = ch; + ch += strlen(ch); + ch++; + } + + if (m->armaps.armap_common_32.ar_member) { + symrefs_32 = m->armaps.armap_symrefs_32; + + for (idx=0; idx<m->armaps.armap_nsyms; idx++) { + if (m->armaps.armap_common_32.ar_armap_attr & AR_ARMAP_ATTR_SYSV) + symrefs_32[idx].ar_name_offset = m->symstrv[idx] - m->symstrv[0]; + + if (!slbt_archive_member_from_offset(m,symrefs_32[idx].ar_member_offset)) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_MEMBER_OFFSET)); + + if (symrefs_32[idx].ar_name_offset) { + ch = &m->symstrs[symrefs_32[idx].ar_name_offset]; + + if ((ch > m->symstrv[m->armaps.armap_nsyms - 1]) || *--ch) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_NAME_OFFSET)); + } + + } + } + + if (m->armaps.armap_common_64.ar_member) { + symrefs_64 = m->armaps.armap_symrefs_64; + + for (idx=0; idx<m->armaps.armap_nsyms; idx++) { + if (m->armaps.armap_common_64.ar_armap_attr & AR_ARMAP_ATTR_SYSV) + symrefs_64[idx].ar_name_offset = m->symstrv[idx] - m->symstrv[0]; + + if (!slbt_archive_member_from_offset(m,symrefs_64[idx].ar_member_offset)) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_MEMBER_OFFSET)); + + if (symrefs_64[idx].ar_name_offset) { + ch = &m->symstrs[symrefs_64[idx].ar_name_offset]; + + if ((ch > m->symstrv[m->armaps.armap_nsyms - 1]) || *--ch) + return slbt_ar_free_archive_meta_impl( + m,SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_NAME_OFFSET)); + } + + } + } + + /* number of public archive members */ + for (idx=0,nmembers=0; idx<nentries; idx++) { + switch (m->memberv[idx]->ar_member_attr) { + case AR_MEMBER_ATTR_ARMAP: + case AR_MEMBER_ATTR_LINKINFO: + case AR_MEMBER_ATTR_NAMESTRS: + break; + + default: + nmembers++; + } + } + + if (m->armaps.armap_common_32.ar_member) + m->armaps.armap_common_32.ar_num_of_members = nmembers; + + if (m->armaps.armap_common_64.ar_member) + m->armaps.armap_common_64.ar_num_of_members = nmembers; + + /* pe/coff armap attributes (second linker member) */ + (void)m->armeta.a_armap_pecoff; + + /* reference to the long names member */ + if (arlongnames) + for (idx=0; idx<nentries && !m->armeta.a_arref_longnames; idx++) + if (m->memberv[idx]->ar_member_data == arlongnames) + m->armeta.a_arref_longnames = m->memberv[idx]; + + /* common binary format (information only) */ + for (idx=0,nmembers=0; idx<nentries; idx++) { + if (m->memberv[idx]->ar_member_attr == AR_MEMBER_ATTR_OBJECT) { + if (m->ofmtattr && (m->ofmtattr != m->memberv[idx]->ar_object_attr)) { + m->ofmtattr = 0; + idx = nentries; + } else if (!m->ofmtattr) { + m->ofmtattr = m->memberv[idx]->ar_object_attr; + } + } + } + + /* member vector */ + m->armeta.a_memberv = m->memberv; + + /* all done */ + if (m->hdrinfov) { + free(m->hdrinfov); + m->hdrinfov = 0; + } + + *meta = &m->armeta; + + return 0; +} + +void slbt_ar_free_archive_meta(struct slbt_archive_meta * meta) +{ + struct slbt_archive_meta_impl * m; + + if (meta) { + m = slbt_archive_meta_ictx(meta); + slbt_ar_free_archive_meta_impl(m,0); + } +} |