/*******************************************************************/ /* 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 #include #include #include #include #include #include #include #include #include #include #include "slibtool_ar_impl.h" #include "slibtool_driver_impl.h" #include "slibtool_errinfo_impl.h" /* anonymous fun */ #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif /* time stamp format specifier */ #define PPRII64 "%"PRIi64 /* file size format specifier */ #define PPRIU64 "%"PRIu64 /* dlopen self/force */ static const char slbt_ar_self_dlunit[] = "@PROGRAM@"; struct armap_buffer_32 { uint32_t moffset; const char * symname; uint32_t baseidx; }; struct armap_buffer_64 { uint64_t moffset; const char * symname; uint64_t baseidx; }; static const char ar_signature[] = AR_SIGNATURE; static int slbt_create_anonymous_archive_ctx( const struct slbt_driver_ctx * dctx, size_t size, struct slbt_archive_ctx ** pctx) { struct slbt_archive_ctx_impl * ctx; if (!(ctx = calloc(1,sizeof(*ctx)))) return SLBT_BUFFER_ERROR(dctx); ctx->map.map_size = size; ctx->map.map_addr = mmap( 0,size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); if (ctx->map.map_addr == MAP_FAILED) { free(ctx); return SLBT_SYSTEM_ERROR(dctx,0); } ctx->dctx = dctx; ctx->actx.map = &ctx->map; *pctx = &ctx->actx; return 0; } static off_t slbt_armap_write_be_32(unsigned char * mark, uint32_t val) { mark[0] = val >> 24; mark[1] = val >> 16; mark[2] = val >> 8; mark[3] = val; return sizeof(uint32_t); } static off_t slbt_armap_write_le_32(unsigned char * mark, uint32_t val) { mark[0] = val; mark[1] = val >> 8; mark[2] = val >> 16; mark[3] = val >> 24; return sizeof(uint32_t); } static off_t slbt_armap_write_be_64(unsigned char * mark, uint64_t val) { slbt_armap_write_be_32(&mark[0],val >> 32); slbt_armap_write_be_32(&mark[4],val); return sizeof(uint64_t); } static off_t slbt_armap_write_le_64(unsigned char * mark, uint64_t val) { slbt_armap_write_be_32(&mark[0],val); slbt_armap_write_be_32(&mark[4],val >> 32); return sizeof(uint64_t); } static int slbt_ar_merge_archives_fail( struct slbt_archive_ctx * arctx, struct armap_buffer_32 * bsdmap32, struct armap_buffer_64 * bsdmap64, int ret) { if (bsdmap32) free(bsdmap32); if (bsdmap64) free(bsdmap64); if (arctx) slbt_ar_free_archive_ctx(arctx); return ret; } int slbt_ar_merge_archives( struct slbt_archive_ctx * const arctxv[], struct slbt_archive_ctx ** arctxm) { struct slbt_archive_ctx * const * arctxp; struct slbt_archive_ctx * arctx; const struct slbt_driver_ctx * dctx; const struct slbt_archive_meta * meta; struct ar_raw_file_header * arhdr; struct ar_meta_member_info ** memberp; struct ar_meta_member_info * meminfo; struct ar_meta_member_info * armap; struct ar_meta_member_info * arnames; const struct ar_meta_armap_common_32 * armap32; const struct ar_meta_armap_common_64 * armap64; const struct ar_meta_armap_ref_32 * symref32; const struct ar_meta_armap_ref_64 * symref64; struct armap_buffer_32 * bsdmap32; struct armap_buffer_64 * bsdmap64; struct armap_buffer_32 * bsdsort32; struct armap_buffer_64 * bsdsort64; size_t nbytes; ssize_t nwritten; uint32_t mapattr; uint64_t nmembers; uint64_t nsymrefs; uint64_t sarmap; uint64_t sarname; uint64_t sarchive; uint64_t smembers; uint64_t ssymrefs; uint64_t ssymstrs; uint64_t snamestrs; int64_t omembers; int64_t osymrefs; int64_t onamestrs; int64_t omemfixup; char * base; unsigned char * ubase; char * ch; unsigned char * uch; char * namebase; char * namestr; char * strtbl; uint64_t idx; uint64_t mapidx; uint64_t cmpidx; off_t (*armap_write_uint32)( unsigned char *, uint32_t); off_t (*armap_write_uint64)( unsigned char *, uint64_t); /* init */ nmembers = nsymrefs = ssymstrs = mapattr = 0; sarchive = smembers = ssymrefs = snamestrs = 0; omembers = osymrefs = onamestrs = 0; if (!arctxv || !arctxv[0]) return -1; if (!(dctx = slbt_get_archive_ictx(arctxv[0])->dctx)) return -1; /* determine armap type and size of archive elements */ for (armap=0,arnames=0,arctxp=arctxv; *arctxp; arctxp++) { if (slbt_get_archive_ictx(*arctxp)->dctx != dctx) return SLBT_CUSTOM_ERROR( dctx, SLBT_ERR_AR_DRIVER_MISMATCH); meta = (*arctxp)->meta; armap32 = meta->a_armap_primary.ar_armap_common_32; armap64 = meta->a_armap_primary.ar_armap_common_64; for (memberp=meta->a_memberv; memberp && *memberp; memberp++) { meminfo = *memberp; switch (meminfo->ar_member_attr) { case AR_MEMBER_ATTR_ARMAP: if (armap32) { if (mapattr == 0) { armap = meminfo; mapattr = armap32->ar_armap_attr; } else if (mapattr != armap32->ar_armap_attr) { return SLBT_CUSTOM_ERROR( dctx, SLBT_ERR_AR_ARMAP_MISMATCH); } nsymrefs += armap32->ar_num_of_symbols; ssymstrs += armap32->ar_size_of_strs; } else if (armap64) { if (mapattr == 0) { armap = meminfo; mapattr = armap64->ar_armap_attr; } else if (mapattr != armap64->ar_armap_attr) { return SLBT_CUSTOM_ERROR( dctx, SLBT_ERR_AR_ARMAP_MISMATCH); } nsymrefs += armap64->ar_num_of_symbols; } break; case AR_MEMBER_ATTR_LINKINFO: break; case AR_MEMBER_ATTR_NAMESTRS: snamestrs += meminfo->ar_object_size; if (!arnames) arnames = meminfo; break; default: smembers += sizeof(struct ar_raw_file_header); smembers += meminfo->ar_file_header.ar_file_size; smembers += 1; smembers |= 1; smembers ^= 1; nmembers++; break; } } } /* armap size */ if (sarmap = 0, sarname = 0, (mapattr == 0)) { (void)0; } else if (mapattr & AR_ARMAP_ATTR_SYSV) { if (mapattr & (AR_ARMAP_ATTR_LE_32|AR_ARMAP_ATTR_BE_32)) { sarmap += sizeof(uint32_t); sarmap += sizeof(uint32_t) * nsymrefs; } else { sarmap += sizeof(uint64_t); sarmap += sizeof(uint64_t) * nsymrefs; } } else if (mapattr & AR_ARMAP_ATTR_BSD) { if (mapattr & (AR_ARMAP_ATTR_LE_32|AR_ARMAP_ATTR_BE_32)) { sarmap += 2 * sizeof(uint32_t); sarmap += 2 * sizeof(uint32_t) * nsymrefs; } else { sarmap += 2 * sizeof(uint64_t); sarmap += 2 * sizeof(uint64_t) * nsymrefs; } sarname += armap->ar_file_header.ar_file_size; sarname -= armap->ar_object_size; } else { return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_FLOW_ERROR); } ssymstrs += 1; ssymstrs |= 1; ssymstrs ^= 1; /* (debugging) */ (void)nmembers; /* long-names member alignment */ snamestrs += 1; snamestrs |= 1; snamestrs ^= 1; /* archive size */ sarchive = sizeof(struct ar_raw_signature); sarchive += armap ? sizeof(struct ar_raw_file_header) : 0; sarchive += arnames ? sizeof(struct ar_raw_file_header) : 0; sarchive += sarname; sarchive += sarmap; sarchive += ssymstrs; sarchive += snamestrs; sarchive += smembers; /* offset from archive base to first public member */ omembers = sarchive; omembers -= smembers; /* create in-memory archive */ if (slbt_create_anonymous_archive_ctx(dctx,sarchive,&arctx) < 0) return SLBT_NESTED_ERROR(dctx); /* get ready for writing */ base = arctx->map->map_addr; ubase = arctx->map->map_addr; /* archive header */ ch = base; memcpy(ch,ar_signature,sizeof(struct ar_raw_signature)); ch += sizeof(struct ar_raw_signature); /* armap header */ if (armap) { arhdr = (struct ar_raw_file_header *)ch; memcpy(arhdr,armap->ar_member_data,sizeof(*arhdr)+sarname); nwritten = armap->ar_file_header.ar_time_date_stamp ? sprintf(arhdr->ar_time_date_stamp,PPRII64,time(0)) : 0; if (nwritten < 0) return slbt_ar_merge_archives_fail( arctx,0,0, SLBT_SYSTEM_ERROR(dctx,0)); for (nbytes=nwritten; nbytes < sizeof(arhdr->ar_time_date_stamp); nbytes++) arhdr->ar_time_date_stamp[nbytes] = AR_DEC_PADDING; nwritten = sprintf( arhdr->ar_file_size,PPRIU64, sarname + sarmap + ssymstrs); if (nwritten < 0) return slbt_ar_merge_archives_fail( arctx,0,0, SLBT_SYSTEM_ERROR(dctx,0)); for (nbytes=nwritten; nbytes < sizeof(arhdr->ar_file_size); nbytes++) arhdr->ar_file_size[nbytes] = AR_DEC_PADDING; } /* arnames header (sysv only) */ if (arnames) { ch = base; ch += omembers; ch -= snamestrs; ch -= sizeof(struct ar_raw_file_header); namebase = ch; namebase += sizeof(struct ar_raw_file_header); memset(namebase,0,snamestrs); namestr = namebase; arhdr = (struct ar_raw_file_header *)ch; memcpy(arhdr,arnames->ar_member_data,sizeof(*arhdr)); nwritten = arnames->ar_file_header.ar_time_date_stamp ? sprintf(arhdr->ar_time_date_stamp,PPRII64,time(0)) : 0; if (nwritten < 0) return slbt_ar_merge_archives_fail( arctx,0,0, SLBT_SYSTEM_ERROR(dctx,0)); for (nbytes=nwritten; nbytes < sizeof(arhdr->ar_time_date_stamp); nbytes++) arhdr->ar_time_date_stamp[nbytes] = AR_DEC_PADDING; nwritten = sprintf( arhdr->ar_file_size,PPRIU64, snamestrs); if (nwritten < 0) return slbt_ar_merge_archives_fail( arctx,0,0, SLBT_SYSTEM_ERROR(dctx,0)); for (nbytes=nwritten; nbytes < sizeof(arhdr->ar_file_size); nbytes++) arhdr->ar_file_size[nbytes] = AR_DEC_PADDING; } /* armap data (preparation) */ armap_write_uint32 = 0; armap_write_uint64 = 0; bsdmap32 = 0; bsdmap64 = 0; if (mapattr & AR_ARMAP_ATTR_BE_32) armap_write_uint32 = slbt_armap_write_be_32; else if (mapattr & AR_ARMAP_ATTR_LE_32) armap_write_uint32 = slbt_armap_write_le_32; else if (mapattr & AR_ARMAP_ATTR_BE_64) armap_write_uint64 = slbt_armap_write_be_64; else if (mapattr & AR_ARMAP_ATTR_LE_64) armap_write_uint64 = slbt_armap_write_le_64; uch = ubase; uch += sizeof(struct ar_raw_signature); uch += sizeof(struct ar_raw_file_header); uch += sarname; if (mapattr & AR_ARMAP_ATTR_SYSV) { if (armap_write_uint32) { uch += armap_write_uint32(uch,nsymrefs); ch = base; ch += uch - ubase; ch += sizeof(uint32_t) * nsymrefs; } else { uch += armap_write_uint64(uch,nsymrefs); ch = base; ch += uch - ubase; ch += sizeof(uint64_t) * nsymrefs; } } else if (mapattr & AR_ARMAP_ATTR_BSD) { strtbl = base; strtbl += omembers; strtbl -= ssymstrs; memset(strtbl,0,ssymstrs); if (armap_write_uint32) { if (!(bsdmap32 = calloc(2*nsymrefs,sizeof(struct armap_buffer_32)))) return slbt_ar_merge_archives_fail( arctx,0,0, SLBT_SYSTEM_ERROR(dctx,0)); bsdsort32 = &bsdmap32[nsymrefs]; } else { if (!(bsdmap64 = calloc(2*nsymrefs,sizeof(struct armap_buffer_64)))) return slbt_ar_merge_archives_fail( arctx,0,0, SLBT_SYSTEM_ERROR(dctx,0)); bsdsort64 = &bsdmap64[nsymrefs]; } } /* main iteration (armap data, long-names, public members) */ for (mapidx=0,arctxp=arctxv; *arctxp; arctxp++) { meta = (*arctxp)->meta; armap32 = meta->a_armap_primary.ar_armap_common_32; armap64 = meta->a_armap_primary.ar_armap_common_64; if ((memberp = meta->a_memberv)) { for (omemfixup=0; *memberp && !omemfixup; memberp++) { meminfo = *memberp; switch (meminfo->ar_member_attr) { case AR_MEMBER_ATTR_ARMAP: case AR_MEMBER_ATTR_LINKINFO: case AR_MEMBER_ATTR_NAMESTRS: break; default: omemfixup = (int64_t)meminfo->ar_member_data; omemfixup -= (int64_t)meta->r_archive.map_addr; break; } } } for (memberp=meta->a_memberv; memberp && *memberp; memberp++) { meminfo = *memberp; switch (meminfo->ar_member_attr) { case AR_MEMBER_ATTR_ARMAP: if (armap32 && (mapattr & AR_ARMAP_ATTR_SYSV)) { symref32 = armap32->ar_symrefs; for (idx=0; idxar_num_of_symbols; idx++) { uch += armap_write_uint32( uch, symref32[idx].ar_member_offset + omembers - omemfixup); strcpy(ch,&armap32->ar_string_table[symref32[idx].ar_name_offset]); ch += strlen(ch); ch++; } } else if (armap64 && (mapattr & AR_ARMAP_ATTR_SYSV)) { symref64 = armap64->ar_symrefs; for (idx=0; idxar_num_of_symbols; idx++) { uch += armap_write_uint64( uch, symref64[idx].ar_member_offset + omembers - omemfixup); strcpy(ch,&armap64->ar_string_table[symref64[idx].ar_name_offset]); ch += strlen(ch); ch++; } } else if (armap32 && (mapattr & AR_ARMAP_ATTR_BSD)) { symref32 = armap32->ar_symrefs; for (idx=0; idxar_num_of_symbols; idx++) { bsdmap32[mapidx].moffset = symref32[idx].ar_member_offset; bsdmap32[mapidx].moffset += omembers - omemfixup; bsdmap32[mapidx].symname = armap32->ar_string_table; bsdmap32[mapidx].symname += symref32[idx].ar_name_offset; mapidx++; } } else if (armap64 && (mapattr & AR_ARMAP_ATTR_BSD)) { symref64 = armap64->ar_symrefs; for (idx=0; idxar_num_of_symbols; idx++) { bsdmap64[mapidx].moffset = symref64[idx].ar_member_offset; bsdmap64[mapidx].moffset += omembers - omemfixup; bsdmap64[mapidx].symname = armap64->ar_string_table; bsdmap64[mapidx].symname += symref64[idx].ar_name_offset; mapidx++; } } break; case AR_MEMBER_ATTR_LINKINFO: break; case AR_MEMBER_ATTR_NAMESTRS: break; default: arhdr = meminfo->ar_member_data; memcpy( &base[omembers],arhdr, sizeof(*arhdr) + meminfo->ar_file_header.ar_file_size); if (meminfo->ar_file_header.ar_header_attr & AR_HEADER_ATTR_SYSV) { if (meminfo->ar_file_header.ar_header_attr & AR_HEADER_ATTR_NAME_REF) { nwritten = sprintf( &base[omembers],"/"PPRII64, namestr - namebase); if (nwritten < 0) SLBT_SYSTEM_ERROR(dctx,0); for (nbytes=nwritten; nbytes < sizeof(arhdr->ar_file_id); nbytes++) base[omembers + nbytes] = AR_DEC_PADDING; strcpy(namestr,meminfo->ar_file_header.ar_member_name); namestr += strlen(namestr); *namestr++ = '/'; *namestr++ = AR_OBJ_PADDING; } } omembers += sizeof(*arhdr); omembers += meminfo->ar_file_header.ar_file_size; omembers += 1; omembers |= 1; omembers ^= 1; break; } } } /* bsd variant: also sort the string table (because we can:=)) */ if (bsdmap32) { for (mapidx=0; mapidxmap,&ictx->meta) < 0) return slbt_ar_merge_archives_fail( arctx,0,0, SLBT_NESTED_ERROR(dctx)); ictx->actx.meta = ictx->meta; *arctxm = arctx; return 0; } int slbt_ar_get_varchive_ctx( const struct slbt_driver_ctx * dctx, struct slbt_archive_ctx ** pctx) { struct slbt_archive_ctx * ctx; struct slbt_archive_ctx_impl * ictx; void * base; size_t size; size = sizeof(struct ar_raw_signature); if (slbt_create_anonymous_archive_ctx(dctx,size,&ctx) < 0) return SLBT_NESTED_ERROR(dctx); ictx = slbt_get_archive_ictx(ctx); base = ctx->map->map_addr; memcpy(base,ar_signature,size); if (slbt_ar_get_archive_meta(dctx,ctx->map,&ictx->meta) < 0) { slbt_ar_free_archive_ctx(ctx); return SLBT_NESTED_ERROR(dctx); } ictx->path = slbt_ar_self_dlunit; ictx->actx.meta = ictx->meta; ictx->actx.path = &ictx->path; *pctx = ctx; return 0; }