| |
| |
| |
| |
| |
| |
| #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" |
| |
| |
| #ifndef MAP_ANONYMOUS |
| #define MAP_ANONYMOUS MAP_ANON |
| #endif |
| |
| |
| #define PPRII64 "%"PRIi64 |
| |
| |
| #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); |
| |
| |
| 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; |
| |
| |
| 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; |
| } |
| } |
| } |
| |
| |
| 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; |
| |
| |
| snamestrs += 1; |
| snamestrs |= 1; |
| snamestrs ^= 1; |
| |
| |
| 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; |
| |
| |
| omembers = sarchive; |
| omembers -= smembers; |
| |
| |
| |
| if (slbt_create_anonymous_archive_ctx(dctx,sarchive,&arctx) < 0) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| |
| base = arctx->map->map_addr; |
| ubase = arctx->map->map_addr; |
| |
| |
| |
| ch = base; |
| memcpy(ch,ar_signature,sizeof(struct ar_raw_signature)); |
| ch += sizeof(struct ar_raw_signature); |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| 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_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]; |
| } |
| } |
| |
| |
| 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; |
| } |
| } |
| } |
| |
| |
| 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++; |
| |
| |
| |
| 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++; |
| |
| |
| |
| 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) |
| return slbt_merge_archives_fail( |
| arctx,0,0, |
| SLBT_NESTED_ERROR(dctx)); |
| |
| ictx->actx.meta = ictx->meta; |
| |
| *arctxm = arctx; |
| |
| return 0; |
| } |