diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/arbits/slbt_archive_merge.c | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/src/arbits/slbt_archive_merge.c b/src/arbits/slbt_archive_merge.c new file mode 100644 index 0000000..acbb318 --- /dev/null +++ b/src/arbits/slbt_archive_merge.c @@ -0,0 +1,698 @@ +/*******************************************************************/ +/* slibtool: a skinny libtool implementation, written in C */ +/* Copyright (C) 2016--2024 SysDeer Technologies, LLC */ +/* Released under the Standard MIT License; see COPYING.SLIBTOOL. */ +/*******************************************************************/ + +#include <time.h> +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <sys/mman.h> +#include <sys/types.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" + +/* 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 + +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_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_free_archive_ctx(arctx); + + return ret; +} + + +int slbt_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); + } + + /* 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); + + + /* signature only? */ + if (!armap && !arnames && !nmembers) { + slbt_free_archive_ctx(arctx); + *arctxm = 0; + return 0; + } + + + /* 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_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_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_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_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_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_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; idx<armap32->ar_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; idx<armap64->ar_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; idx<armap32->ar_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; idx<armap64->ar_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; mapidx<nsymrefs; mapidx++) + for (cmpidx=0; cmpidx<nsymrefs; cmpidx++) + if (strcmp(bsdmap32[cmpidx].symname,bsdmap32[mapidx].symname) < 0) + bsdmap32[mapidx].baseidx++; + + /* a symbol might be present in more than one member; */ + /* see whether ar_member_offset has already been set) */ + for (mapidx=0,ch=strtbl; mapidx<nsymrefs; mapidx++) { + idx = bsdmap32[mapidx].baseidx; + + for (; bsdsort32[idx].moffset; ) + idx++; + + bsdsort32[idx].moffset = bsdmap32[mapidx].moffset; + bsdsort32[idx].symname = bsdmap32[mapidx].symname; + } + + uch += armap_write_uint32(uch,2*sizeof(uint32_t)*nsymrefs); + + for (mapidx=0,ch=strtbl; mapidx<nsymrefs; mapidx++) { + uch += armap_write_uint32(uch,ch-strtbl); + uch += armap_write_uint32(uch,bsdsort32[mapidx].moffset); + + strcpy(ch,bsdsort32[mapidx].symname); + ch += strlen(ch); + ch++; + } + + uch +=armap_write_uint32(uch,ssymstrs); + + free(bsdmap32); + + } else if (bsdmap64) { + for (mapidx=0; mapidx<nsymrefs; mapidx++) + for (cmpidx=0; cmpidx<nsymrefs; cmpidx++) + if (strcmp(bsdmap64[cmpidx].symname,bsdmap64[mapidx].symname) < 0) + bsdmap64[mapidx].baseidx++; + + /* a symbol might be present in more than one member; */ + /* see whether ar_member_offset has already been set) */ + for (mapidx=0,ch=strtbl; mapidx<nsymrefs; mapidx++) { + idx = bsdmap64[mapidx].baseidx; + + for (; bsdsort64[idx].moffset; ) + idx++; + + bsdsort64[idx].moffset = bsdmap64[mapidx].moffset; + bsdsort64[idx].symname = bsdmap64[mapidx].symname; + } + + uch += armap_write_uint64(uch,2*sizeof(uint64_t)*nsymrefs); + + for (mapidx=0,ch=strtbl; mapidx<nsymrefs; mapidx++) { + uch += armap_write_uint64(uch,ch-strtbl); + uch += armap_write_uint64(uch,bsdsort64[mapidx].moffset); + + strcpy(ch,bsdsort64[mapidx].symname); + ch += strlen(ch); + ch++; + } + + uch +=armap_write_uint64(uch,ssymstrs); + + free(bsdmap64); + } + + struct slbt_archive_ctx_impl * ictx; + ictx = slbt_get_archive_ictx(arctx); + + if (slbt_get_archive_meta(dctx,arctx->map,&ictx->meta) < 0) + slbt_merge_archives_fail( + arctx,0,0, + SLBT_NESTED_ERROR(dctx)); + + ictx->actx.meta = ictx->meta; + + *arctxm = arctx; + + return 0; +} |