diff options
Diffstat (limited to 'src/arbits')
-rw-r--r-- | src/arbits/output/slbt_au_output_arname.c | 86 | ||||
-rw-r--r-- | src/arbits/output/slbt_au_output_dlsyms.c | 12 | ||||
-rw-r--r-- | src/arbits/output/slbt_au_output_mapfile.c | 12 | ||||
-rw-r--r-- | src/arbits/output/slbt_au_output_members.c | 278 | ||||
-rw-r--r-- | src/arbits/output/slbt_au_output_symbols.c | 201 | ||||
-rw-r--r-- | src/arbits/slbt_archive_ctx.c | 117 | ||||
-rw-r--r-- | src/arbits/slbt_archive_dlsyms.c | 458 | ||||
-rw-r--r-- | src/arbits/slbt_archive_mapfile.c | 162 | ||||
-rw-r--r-- | src/arbits/slbt_archive_mapstrv.c | 47 | ||||
-rw-r--r-- | src/arbits/slbt_archive_merge.c | 734 | ||||
-rw-r--r-- | src/arbits/slbt_archive_meta.c | 888 | ||||
-rw-r--r-- | src/arbits/slbt_archive_store.c | 145 | ||||
-rw-r--r-- | src/arbits/slbt_archive_symfile.c | 138 | ||||
-rw-r--r-- | src/arbits/slbt_archive_syminfo.c | 345 | ||||
-rw-r--r-- | src/arbits/slbt_armap_bsd_32.c | 157 | ||||
-rw-r--r-- | src/arbits/slbt_armap_bsd_64.c | 174 | ||||
-rw-r--r-- | src/arbits/slbt_armap_sysv_32.c | 120 | ||||
-rw-r--r-- | src/arbits/slbt_armap_sysv_64.c | 128 |
18 files changed, 4202 insertions, 0 deletions
diff --git a/src/arbits/output/slbt_au_output_arname.c b/src/arbits/output/slbt_au_output_arname.c new file mode 100644 index 0000000..28e081d --- /dev/null +++ b/src/arbits/output/slbt_au_output_arname.c @@ -0,0 +1,86 @@ +/*******************************************************************/ +/* 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 <slibtool/slibtool.h> +#include <slibtool/slibtool_output.h> +#include "slibtool_driver_impl.h" +#include "slibtool_dprintf_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_ar_impl.h" + +#define SLBT_PRETTY_FLAGS (SLBT_PRETTY_YAML \ + | SLBT_PRETTY_POSIX \ + | SLBT_PRETTY_HEXDATA) + +static int slbt_au_output_arname_impl( + const struct slbt_driver_ctx * dctx, + const struct slbt_archive_ctx * actx, + const struct slbt_fd_ctx * fdctx, + const char * fmt) +{ + const char * path; + const char mema[] = "<memory_object>"; + + path = actx->path && *actx->path ? *actx->path : mema; + + if (slbt_dprintf(fdctx->fdout,fmt,path) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + return 0; +} + +static int slbt_au_output_arname_posix( + const struct slbt_driver_ctx * dctx, + const struct slbt_archive_ctx * actx, + const struct slbt_fd_ctx * fdctx) +{ + if (slbt_au_output_arname_impl( + dctx,actx,fdctx, + "%s:\n") < 0) + return SLBT_NESTED_ERROR(dctx); + + return 0; +} + +static int slbt_au_output_arname_yaml( + const struct slbt_driver_ctx * dctx, + const struct slbt_archive_ctx * actx, + const struct slbt_fd_ctx * fdctx) +{ + if (slbt_au_output_arname_impl( + dctx,actx,fdctx, + "Archive:\n" + " - Meta:\n" + " - [ name: %s ]\n\n") < 0) + return SLBT_NESTED_ERROR(dctx); + + return 0; +} + +int slbt_au_output_arname(const struct slbt_archive_ctx * actx) +{ + const struct slbt_driver_ctx * dctx; + struct slbt_fd_ctx fdctx; + + dctx = (slbt_get_archive_ictx(actx))->dctx; + + if (slbt_lib_get_driver_fdctx(dctx,&fdctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + switch (dctx->cctx->fmtflags & SLBT_PRETTY_FLAGS) { + case SLBT_PRETTY_YAML: + return slbt_au_output_arname_yaml( + dctx,actx,&fdctx); + + case SLBT_PRETTY_POSIX: + return slbt_au_output_arname_posix( + dctx,actx,&fdctx); + + default: + return slbt_au_output_arname_yaml( + dctx,actx,&fdctx); + } +} diff --git a/src/arbits/output/slbt_au_output_dlsyms.c b/src/arbits/output/slbt_au_output_dlsyms.c new file mode 100644 index 0000000..512f60f --- /dev/null +++ b/src/arbits/output/slbt_au_output_dlsyms.c @@ -0,0 +1,12 @@ +/*******************************************************************/ +/* 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 <slibtool/slibtool.h> + +int slbt_au_output_dlsyms(struct slbt_archive_ctx ** arctxv, const char * dlunit) +{ + return slbt_ar_create_dlsyms(arctxv,dlunit,0,0); +} diff --git a/src/arbits/output/slbt_au_output_mapfile.c b/src/arbits/output/slbt_au_output_mapfile.c new file mode 100644 index 0000000..fb470df --- /dev/null +++ b/src/arbits/output/slbt_au_output_mapfile.c @@ -0,0 +1,12 @@ +/*******************************************************************/ +/* 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 <slibtool/slibtool.h> + +int slbt_au_output_mapfile(const struct slbt_archive_meta * meta) +{ + return slbt_ar_create_mapfile(meta,0,0); +} diff --git a/src/arbits/output/slbt_au_output_members.c b/src/arbits/output/slbt_au_output_members.c new file mode 100644 index 0000000..88937b0 --- /dev/null +++ b/src/arbits/output/slbt_au_output_members.c @@ -0,0 +1,278 @@ +/*******************************************************************/ +/* 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 <time.h> +#include <locale.h> +#include <inttypes.h> +#include <slibtool/slibtool.h> +#include <slibtool/slibtool_output.h> +#include "slibtool_driver_impl.h" +#include "slibtool_dprintf_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_ar_impl.h" + +#define SLBT_PRETTY_FLAGS (SLBT_PRETTY_YAML \ + | SLBT_PRETTY_POSIX \ + | SLBT_PRETTY_HEXDATA) + +#define PPRIU64 "%"PRIu64 + +const char slbt_ar_perm_strs[8][4] = { + {'-','-','-','\0'}, + {'-','-','x','\0'}, + {'-','w','-','\0'}, + {'-','w','x','\0'}, + {'r','-','-','\0'}, + {'r','-','x','\0'}, + {'r','w','-','\0'}, + {'r','w','x','\0'} +}; + +static unsigned slbt_au_output_decimal_len_from_val(size_t val, unsigned min) +{ + unsigned ret; + + for (ret=0; val; ret++) + val /= 10; + + return (ret > min) ? ret : min; +} + +static int slbt_au_output_one_member_posix( + int fdout, + struct ar_meta_member_info * memberp) +{ + return slbt_dprintf( + fdout,"%s\n", + memberp->ar_file_header.ar_member_name); +} + +static int slbt_au_output_one_member_posix_verbose( + int fdout, + struct ar_meta_member_info * memberp, + const char * fmtstr, + locale_t arlocale) +{ + unsigned ownerbits; + unsigned groupbits; + unsigned worldbits; + time_t artimeval; + struct tm artimeloc; + char artimestr[64] = {0}; + + ownerbits = (memberp->ar_file_header.ar_file_mode & 0700) >> 6; + groupbits = (memberp->ar_file_header.ar_file_mode & 0070) >> 3; + worldbits = (memberp->ar_file_header.ar_file_mode & 0007); + artimeval = memberp->ar_file_header.ar_time_date_stamp; + + if (localtime_r(&artimeval,&artimeloc)) + strftime_l( + artimestr,sizeof(artimestr), + "%b %e %H:%M %Y",&artimeloc, + arlocale); + + return slbt_dprintf( + fdout,fmtstr, + slbt_ar_perm_strs[ownerbits], + slbt_ar_perm_strs[groupbits], + slbt_ar_perm_strs[worldbits], + memberp->ar_file_header.ar_uid, + memberp->ar_file_header.ar_gid, + memberp->ar_object_size, + artimestr, + memberp->ar_file_header.ar_member_name); +} + +static int slbt_au_output_members_posix( + const struct slbt_driver_ctx * dctx, + const struct slbt_archive_meta * meta, + const struct slbt_fd_ctx * fdctx) +{ + struct ar_meta_member_info ** memberp; + int fdout; + size_t testval; + size_t sizelen; + size_t uidlen; + size_t gidlen; + locale_t arloc; + char fmtstr[64]; + + fdout = fdctx->fdout; + arloc = 0; + + if (dctx->cctx->fmtflags & SLBT_PRETTY_VERBOSE) { + for (sizelen=0,memberp=meta->a_memberv; *memberp; memberp++) + if ((testval = memberp[0]->ar_object_size) > sizelen) + sizelen = testval; + + for (uidlen=0,memberp=meta->a_memberv; *memberp; memberp++) + if ((testval = memberp[0]->ar_file_header.ar_uid) > uidlen) + uidlen = testval; + + for (gidlen=0,memberp=meta->a_memberv; *memberp; memberp++) + if ((testval = memberp[0]->ar_file_header.ar_gid) > gidlen) + gidlen = testval; + + sizelen = slbt_au_output_decimal_len_from_val(sizelen,6); + uidlen = slbt_au_output_decimal_len_from_val(uidlen,1); + gidlen = slbt_au_output_decimal_len_from_val(gidlen,1); + arloc = newlocale(LC_ALL,setlocale(LC_ALL,0),0); + + sprintf( + fmtstr, + "%%s%%s%%s " + "%%" PPRIU64 "u" + "/%%-" PPRIU64 "u " + "%%" PPRIU64 "u " + "%%s " + "%%s\n", + uidlen, + gidlen, + sizelen); + } + + for (memberp=meta->a_memberv; *memberp; memberp++) { + switch ((*memberp)->ar_member_attr) { + case AR_MEMBER_ATTR_ARMAP: + case AR_MEMBER_ATTR_LINKINFO: + case AR_MEMBER_ATTR_NAMESTRS: + break; + + default: + if (arloc) { + if (slbt_au_output_one_member_posix_verbose( + fdout,*memberp,fmtstr,arloc) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } else { + if (slbt_au_output_one_member_posix( + fdout,*memberp) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + } + + if (arloc) + freelocale(arloc); + + return 0; +} + +static int slbt_au_output_one_member_yaml( + int fdout, + struct ar_meta_member_info * memberp) +{ + return slbt_dprintf( + fdout, + " - [ member: %s ]\n", + memberp->ar_file_header.ar_member_name); +} + +static int slbt_au_output_one_member_yaml_verbose( + int fdout, + struct ar_meta_member_info * memberp, + locale_t arlocale) +{ + time_t artimeval; + struct tm artimeloc; + char artimestr[64] = {0}; + + artimeval = memberp->ar_file_header.ar_time_date_stamp; + + if (localtime_r(&artimeval,&artimeloc)) + strftime_l( + artimestr,sizeof(artimestr), + "%Y/%m/%d @ %H:%M",&artimeloc, + arlocale); + + return slbt_dprintf( + fdout, + " - Member:\n" + " - [ name: " "%s" " ]\n" + " - [ timestamp: " "%s" " ]\n" + " - [ filesize: " PPRIU64 " ]\n" + " - [ uid: " "%d" " ]\n" + " - [ gid: " "%d" " ]\n" + " - [ mode: " "%d" " ]\n\n", + memberp->ar_file_header.ar_member_name, + artimestr, + memberp->ar_object_size, + memberp->ar_file_header.ar_uid, + memberp->ar_file_header.ar_gid, + memberp->ar_file_header.ar_file_mode); +} + +static int slbt_au_output_members_yaml( + const struct slbt_driver_ctx * dctx, + const struct slbt_archive_meta * meta, + const struct slbt_fd_ctx * fdctx) +{ + struct ar_meta_member_info ** memberp; + int fdout; + locale_t arloc; + + fdout = fdctx->fdout; + arloc = 0; + + if (dctx->cctx->fmtflags & SLBT_PRETTY_VERBOSE) { + arloc = newlocale(LC_ALL,setlocale(LC_ALL,0),0); + } + + if (slbt_dprintf(fdctx->fdout," - Members:\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + for (memberp=meta->a_memberv; *memberp; memberp++) { + switch ((*memberp)->ar_member_attr) { + case AR_MEMBER_ATTR_ARMAP: + case AR_MEMBER_ATTR_LINKINFO: + case AR_MEMBER_ATTR_NAMESTRS: + break; + + default: + if (arloc) { + if (slbt_au_output_one_member_yaml_verbose( + fdout,*memberp,arloc) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } else { + if (slbt_au_output_one_member_yaml( + fdout,*memberp) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + } + + if (arloc) + freelocale(arloc); + + return 0; +} + +int slbt_au_output_members(const struct slbt_archive_meta * meta) +{ + const struct slbt_driver_ctx * dctx; + struct slbt_fd_ctx fdctx; + + dctx = (slbt_archive_meta_ictx(meta))->dctx; + + if (slbt_lib_get_driver_fdctx(dctx,&fdctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if (!meta->a_memberv) + return 0; + + switch (dctx->cctx->fmtflags & SLBT_PRETTY_FLAGS) { + case SLBT_PRETTY_YAML: + return slbt_au_output_members_yaml( + dctx,meta,&fdctx); + + case SLBT_PRETTY_POSIX: + return slbt_au_output_members_posix( + dctx,meta,&fdctx); + + default: + return slbt_au_output_members_yaml( + dctx,meta,&fdctx); + } +} diff --git a/src/arbits/output/slbt_au_output_symbols.c b/src/arbits/output/slbt_au_output_symbols.c new file mode 100644 index 0000000..950cde8 --- /dev/null +++ b/src/arbits/output/slbt_au_output_symbols.c @@ -0,0 +1,201 @@ +/*******************************************************************/ +/* 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 <time.h> +#include <locale.h> +#include <regex.h> +#include <inttypes.h> +#include <slibtool/slibtool.h> +#include <slibtool/slibtool_output.h> +#include "slibtool_driver_impl.h" +#include "slibtool_dprintf_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_pecoff_impl.h" +#include "slibtool_tmpfile_impl.h" +#include "slibtool_ar_impl.h" + +#define SLBT_PRETTY_FLAGS (SLBT_PRETTY_YAML \ + | SLBT_PRETTY_POSIX \ + | SLBT_PRETTY_HEXDATA) + +static int slbt_au_output_symbols_posix( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * mctx, + int fdout) +{ + bool fsort; + bool fcoff; + const char * dot; + const char * mark; + const char * regex; + const char ** symv; + const char ** symstrv; + regex_t regctx; + regmatch_t pmatch[2] = {{0,0},{0,0}}; + char strbuf[4096]; + + fsort = !(dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_NOSORT); + fcoff = (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + if (fsort && !mctx->mapstrv) + if (slbt_update_mapstrv(dctx,mctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if ((regex = dctx->cctx->regex)) + if (regcomp(®ctx,regex,REG_EXTENDED|REG_NEWLINE)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + symstrv = fsort ? mctx->mapstrv : mctx->symstrv; + + for (symv=symstrv; *symv; symv++) { + if (!fcoff || slbt_is_strong_coff_symbol(*symv)) { + if (!regex || !regexec(®ctx,*symv,1,pmatch,0)) { + if (slbt_dprintf(fdout,"%s\n",*symv) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + + /* coff weak symbols: expsym = .weak.alias.strong */ + } else if (fcoff && !strncmp(*symv,".weak.",6)) { + mark = &(*symv)[6]; + dot = strchr(mark,'.'); + + strncpy(strbuf,mark,dot-mark); + strbuf[dot-mark] = '\0'; + + if (!regex || !regexec(®ctx,strbuf,1,pmatch,0)) + if (slbt_dprintf(fdout,"%s\n",strbuf) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if (regex) + regfree(®ctx); + + return 0; +} + +static int slbt_au_output_one_symbol_yaml( + int fdout, + struct slbt_archive_meta_impl * mctx, + const char * symname) +{ + struct ar_meta_symbol_info ** syminfv; + + for (syminfv=mctx->syminfv; *syminfv; syminfv++) + if (!strcmp(syminfv[0]->ar_symbol_name,symname)) + return slbt_dprintf( + fdout, + " - Symbol:\n" + " - [ object_name: " "%s" " ]\n" + " - [ symbol_name: " "%s" " ]\n" + " - [ symbol_type: " "%s" " ]\n\n", + syminfv[0]->ar_object_name, + symname, + syminfv[0]->ar_symbol_type); + + return 0; +} + +static int slbt_au_output_symbols_yaml( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * mctx, + int fdout) +{ + int fdtmp; + bool fsort; + bool fcoff; + const char * dot; + const char * mark; + const char * regex; + const char ** symv; + const char ** symstrv; + regex_t regctx; + regmatch_t pmatch[2] = {{0,0},{0,0}}; + char strbuf[4096]; + + fsort = !(dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_NOSORT); + fcoff = (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + if ((fdtmp = slbt_tmpfile()) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (fsort && !mctx->mapstrv) + if (slbt_update_mapstrv(dctx,mctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if (slbt_ar_update_syminfo_ex(mctx->actx,fdtmp) < 0) + return SLBT_NESTED_ERROR(dctx); + + if ((regex = dctx->cctx->regex)) + if (regcomp(®ctx,regex,REG_EXTENDED|REG_NEWLINE)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + symstrv = fsort ? mctx->mapstrv : mctx->symstrv; + + if (slbt_dprintf(fdout," - Symbols:\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + for (symv=symstrv; *symv; symv++) { + if (!fcoff || slbt_is_strong_coff_symbol(*symv)) { + if (!regex || !regexec(®ctx,*symv,1,pmatch,0)) { + if (slbt_au_output_one_symbol_yaml( + fdout,mctx,*symv) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + + /* coff weak symbols: expsym = .weak.alias.strong */ + } else if (fcoff && !strncmp(*symv,".weak.",6)) { + mark = &(*symv)[6]; + dot = strchr(mark,'.'); + + strncpy(strbuf,mark,dot-mark); + strbuf[dot-mark] = '\0'; + + if (!regex || !regexec(®ctx,strbuf,1,pmatch,0)) + if (slbt_au_output_one_symbol_yaml( + fdout,mctx,strbuf) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if (regex) + regfree(®ctx); + + return 0; +} + +int slbt_au_output_symbols(const struct slbt_archive_meta * meta) +{ + struct slbt_archive_meta_impl * mctx; + const struct slbt_driver_ctx * dctx; + int fdout; + + mctx = slbt_archive_meta_ictx(meta); + dctx = (slbt_archive_meta_ictx(meta))->dctx; + + fdout = slbt_driver_fdout(dctx); + + if (!meta->a_memberv) + return 0; + + switch (dctx->cctx->fmtflags & SLBT_PRETTY_FLAGS) { + case SLBT_PRETTY_YAML: + return slbt_au_output_symbols_yaml( + dctx,mctx,fdout); + + case SLBT_PRETTY_POSIX: + return slbt_au_output_symbols_posix( + dctx,mctx,fdout); + + default: + return slbt_au_output_symbols_yaml( + dctx,mctx,fdout); + } +} diff --git a/src/arbits/slbt_archive_ctx.c b/src/arbits/slbt_archive_ctx.c new file mode 100644 index 0000000..575374c --- /dev/null +++ b/src/arbits/slbt_archive_ctx.c @@ -0,0 +1,117 @@ +/*******************************************************************/ +/* 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_driver_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_ar_impl.h" + +static int slbt_map_raw_archive( + const struct slbt_driver_ctx * dctx, + int fd, + const char * path, + int prot, + struct slbt_raw_archive * map) +{ + struct slbt_input mapinfo = {0,0}; + + if (slbt_fs_map_input(dctx,fd,path,prot,&mapinfo) < 0) + return SLBT_NESTED_ERROR(dctx); + + if (mapinfo.size == 0) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_EMPTY_FILE); + + map->map_addr = mapinfo.addr; + map->map_size = mapinfo.size; + + return 0; +} + +static int slbt_unmap_raw_archive(struct slbt_raw_archive * map) +{ + struct slbt_input mapinfo; + + mapinfo.addr = map->map_addr; + mapinfo.size = map->map_size; + + return slbt_fs_unmap_input(&mapinfo); +} + +static int slbt_ar_free_archive_ctx_impl(struct slbt_archive_ctx_impl * ctx, int ret) +{ + if (ctx) { + slbt_ar_free_archive_meta(ctx->meta); + slbt_unmap_raw_archive(&ctx->map); + free(ctx->pathbuf); + free(ctx); + } + + return ret; +} + +int slbt_ar_get_archive_ctx( + const struct slbt_driver_ctx * dctx, + const char * path, + struct slbt_archive_ctx ** pctx) +{ + struct slbt_archive_ctx_impl * ctx; + struct slbt_archive_meta_impl * mctx; + int prot; + + if (!(ctx = calloc(1,sizeof(*ctx)))) + return SLBT_BUFFER_ERROR(dctx); + + slbt_driver_set_arctx( + dctx,0,path); + + prot = (dctx->cctx->actflags & SLBT_ACTION_MAP_READWRITE) + ? PROT_READ | PROT_WRITE + : PROT_READ; + + if (slbt_map_raw_archive(dctx,-1,path,prot,&ctx->map)) + return slbt_ar_free_archive_ctx_impl(ctx, + SLBT_NESTED_ERROR(dctx)); + + if (slbt_ar_get_archive_meta(dctx,&ctx->map,&ctx->meta)) + return slbt_ar_free_archive_ctx_impl(ctx, + SLBT_NESTED_ERROR(dctx)); + + if (!(ctx->pathbuf = strdup(path))) + return slbt_ar_free_archive_ctx_impl(ctx, + SLBT_NESTED_ERROR(dctx)); + + mctx = slbt_archive_meta_ictx(ctx->meta); + + ctx->dctx = dctx; + ctx->path = ctx->pathbuf; + ctx->actx.path = &ctx->path; + ctx->actx.map = &ctx->map; + ctx->actx.meta = ctx->meta; + mctx->actx = &ctx->actx; + + *pctx = &ctx->actx; + return 0; +} + +void slbt_ar_free_archive_ctx(struct slbt_archive_ctx * ctx) +{ + struct slbt_archive_ctx_impl * ictx; + uintptr_t addr; + + if (ctx) { + addr = (uintptr_t)ctx - offsetof(struct slbt_archive_ctx_impl,actx); + ictx = (struct slbt_archive_ctx_impl *)addr; + slbt_ar_free_archive_ctx_impl(ictx,0); + } +} diff --git a/src/arbits/slbt_archive_dlsyms.c b/src/arbits/slbt_archive_dlsyms.c new file mode 100644 index 0000000..ed07602 --- /dev/null +++ b/src/arbits/slbt_archive_dlsyms.c @@ -0,0 +1,458 @@ +/*******************************************************************/ +/* 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 <slibtool/slibtool.h> +#include "slibtool_ar_impl.h" +#include "slibtool_driver_impl.h" +#include "slibtool_snprintf_impl.h" +#include "slibtool_errinfo_impl.h" + +static const char * slbt_strong_symname( + const char * symname, + bool fcoff, + char (*strbuf)[4096]) +{ + const char * dot; + const char * mark; + char * sym; + + if (fcoff) { + if (!strncmp(symname,"__imp_",6)) + return 0; + + if (strncmp(symname,".weak.",6)) + return symname; + + sym = *strbuf; + mark = &symname[6]; + dot = strchr(mark,'.'); + + strncpy(sym,mark,dot-mark); + sym[dot-mark] = '\0'; + + return sym; + } + + return symname; +} + + +static int slbt_ar_dlsyms_define_by_type( + int fdout, + const char * arname, + struct slbt_archive_meta_impl * mctx, + const char * desc, + const char stype) +{ + uint64_t idx; + uint64_t nsyms; + bool fcoff; + const char * symname; + char strbuf[4096]; + + for (idx=0,nsyms=0; idx<mctx->armaps.armap_nsyms; idx++) + if (mctx->syminfv[idx]->ar_symbol_type[0] == stype) + nsyms++; + + if (nsyms == 0) + return 0; + + fcoff = slbt_host_objfmt_is_coff(mctx->dctx); + fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + if (slbt_dprintf(fdout,"/* %s (%s) */\n",desc,arname) < 0) + return SLBT_SYSTEM_ERROR(mctx->dctx,0); + + for (idx=0; idx<mctx->armaps.armap_nsyms; idx++) + if (mctx->syminfv[idx]->ar_symbol_type[0] == stype) + if ((symname = slbt_strong_symname( + mctx->syminfv[idx]->ar_symbol_name, + fcoff,&strbuf))) + if (slbt_dprintf(fdout, + (stype == 'T') + ? "extern int %s();\n" + : "extern char %s[];\n", + symname) < 0) + return SLBT_SYSTEM_ERROR(mctx->dctx,0); + + if (slbt_dprintf(fdout,"\n") < 0) + return SLBT_SYSTEM_ERROR(mctx->dctx,0); + + return 0; +} + +static int slbt_ar_dlsyms_get_max_len_by_type( + int mlen, + struct slbt_archive_meta_impl * mctx, + const char stype) +{ + int len; + uint64_t idx; + bool fcoff; + const char * symname; + char strbuf[4096]; + + fcoff = slbt_host_objfmt_is_coff(mctx->dctx); + fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + for (idx=0; idx<mctx->armaps.armap_nsyms; idx++) + if (mctx->syminfv[idx]->ar_symbol_type[0] == stype) + if ((symname = slbt_strong_symname( + mctx->syminfv[idx]->ar_symbol_name, + fcoff,&strbuf))) + if ((len = strlen(symname)) > mlen) + mlen = len; + + return mlen; +} + +static int slbt_ar_dlsyms_add_by_type( + int fdout, + struct slbt_archive_meta_impl * mctx, + const char * fmt, + const char stype, + char (*namebuf)[4096]) +{ + uint64_t idx; + uint64_t nsyms; + bool fcoff; + const char * symname; + char strbuf[4096]; + + nsyms = 0; + symname = *namebuf; + + fcoff = slbt_host_objfmt_is_coff(mctx->dctx); + fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + for (idx=0; idx<mctx->armaps.armap_nsyms; idx++) + if (mctx->syminfv[idx]->ar_symbol_type[0] == stype) + nsyms++; + + if (nsyms == 0) + return 0; + + if (slbt_dprintf(fdout,"\n") < 0) + return SLBT_SYSTEM_ERROR(mctx->dctx,0); + + for (idx=0; idx<mctx->armaps.armap_nsyms; idx++) { + if (mctx->syminfv[idx]->ar_symbol_type[0] == stype) { + symname = slbt_strong_symname( + mctx->syminfv[idx]->ar_symbol_name, + fcoff,&strbuf); + + if (symname) { + if (slbt_snprintf(*namebuf,sizeof(*namebuf), + "%s\",",symname) < 0) + return SLBT_SYSTEM_ERROR(mctx->dctx,0); + + if (slbt_dprintf(fdout,fmt, + *namebuf, + (stype == 'T') ? "&" : "", + symname) < 0) + return SLBT_NESTED_ERROR(mctx->dctx); + } + } + } + + return 0; +} + + +static int slbt_ar_output_dlsyms_impl( + int fdout, + const struct slbt_driver_ctx * dctx, + struct slbt_archive_ctx ** arctxv, + const char * dsounit) +{ + int ret; + int idx; + unsigned len; + unsigned cmp; + const char * arname; + const char * soname; + struct slbt_archive_ctx * actx; + struct slbt_archive_ctx ** parctx; + struct slbt_archive_ctx_impl * ictx; + struct slbt_archive_meta_impl * mctx; + const struct slbt_source_version * verinfo; + char dlsymfmt[32]; + char cline[6][73]; + char symname[4096]; + + /* init */ + actx = arctxv[0]; + verinfo = slbt_api_source_version(); + + /* preamble */ + memset(cline[0],'*',72); + memset(cline[1],' ',72); + memset(cline[2],' ',72); + memset(cline[3],' ',72); + memset(cline[4],'*',72); + + memset(cline[5],0,72); + cline[5][0] = '\n'; + + len = snprintf(&cline[1][3],69, + "backward-compatible dlsym table"); + + cline[1][3+len] = ' '; + + len = snprintf(&cline[2][3],69, + "Generated by %s (slibtool %d.%d.%d)", + dctx->program, + verinfo->major,verinfo->minor,verinfo->revision); + + cline[2][3+len] = ' '; + + len = snprintf(&cline[3][3],69, + "[commit reference: %s]", + verinfo->commit); + + cline[3][3+len] = ' '; + + for (idx=0; idx<5; idx++) { + cline[idx][0] = '/'; + cline[idx][1] = '*'; + + cline[idx][70] = '*'; + cline[idx][71] = '/'; + + cline[idx][72] = '\n'; + } + + if (slbt_dprintf(fdout,"%s",&cline[0]) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (slbt_dprintf(fdout, + "#ifdef __cplusplus\n" + "extern \"C\" {\n" + "#endif\n\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + /* declarations */ + for (parctx=arctxv; *parctx; parctx++) { + actx = *parctx; + ictx = slbt_get_archive_ictx(actx); + mctx = slbt_archive_meta_ictx(ictx->meta); + + if ((arname = strrchr(*actx->path,'/'))) + arname++; + + if (!arname) + arname = *actx->path; + + ret = slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Absolute Values", 'A'); + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: BSS Section", 'B'); + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Common Section", 'C'); + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Initialized Data", 'D'); + + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Small Globals", 'G'); + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Indirect References", 'I'); + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Read-Only Section", 'R'); + + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Small Objects", 'S'); + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Weak Symbols", 'W'); + + ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Text Section: Public Interfaces", 'T'); + + if (ret < 0) + return SLBT_NESTED_ERROR(dctx); + + } + + /* vtable struct definition */ + if (slbt_dprintf(fdout, + "/* name-address Public ABI struct definition */\n" + "struct lt_dlsym_symdef {\n" + "\tconst char * dlsym_name;\n" + "\tvoid * dlsym_addr;\n" + "};\n\n") < 0) + return SLBT_NESTED_ERROR(dctx); + + soname = (strcmp(dsounit,"@PROGRAM@")) ? dsounit : "_PROGRAM_"; + + if (slbt_dprintf(fdout, + "/* dlsym vtable */\n" + "extern const struct lt_dlsym_symdef " + "lt_%s_LTX_preloaded_symbols[];\n\n" + "const struct lt_dlsym_symdef " + "lt_%s_LTX_preloaded_symbols[] = {\n", + soname,soname) < 0) + return SLBT_NESTED_ERROR(dctx); + + /* align dlsym_name and dlsym_addr columsn (because we can) */ + for (parctx=arctxv,len=0; *parctx; parctx++) { + actx = *parctx; + ictx = slbt_get_archive_ictx(actx); + mctx = slbt_archive_meta_ictx(ictx->meta); + + if ((arname = strrchr(*actx->path,'/'))) + arname++; + + if (!arname) + arname = *actx->path; + + if (len < (cmp = strlen(arname))) + len = cmp; + + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'A'); + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'B'); + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'C'); + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'D'); + + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'G'); + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'I'); + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'R'); + + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'S'); + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'T'); + len = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'W'); + } + + /* quote, comma */ + len += 2; + + if (len >= sizeof(symname)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + /* aligned print format */ + snprintf(dlsymfmt,sizeof(dlsymfmt),"\t{\"%%-%ds %%s%%s},\n",len); + + /* dso unit */ + if (slbt_snprintf(symname,sizeof(symname),"%s\",",dsounit) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (slbt_dprintf(fdout,dlsymfmt,symname,"","0") < 0) + return SLBT_NESTED_ERROR(dctx); + + /* (-dlopen force) */ + if (!arctxv[0]->meta->a_memberv) + if (!strcmp(*arctxv[0]->path,"@PROGRAM@")) + arctxv++; + + /* at long last */ + for (parctx=arctxv; *parctx; parctx++) { + actx = *parctx; + ictx = slbt_get_archive_ictx(actx); + mctx = slbt_archive_meta_ictx(ictx->meta); + + if ((arname = strrchr(*actx->path,'/'))) + arname++; + + if (!arname) + arname = *actx->path; + + if (slbt_dprintf(fdout,"\n") < 0) + return SLBT_NESTED_ERROR(mctx->dctx); + + if (slbt_snprintf(symname,sizeof(symname),"%s\",",arname) < 0) + return SLBT_SYSTEM_ERROR(mctx->dctx,0); + + if (slbt_dprintf(fdout,dlsymfmt,symname,"","0") < 0) + return SLBT_NESTED_ERROR(mctx->dctx); + + ret = slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'A',&symname); + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'B',&symname); + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'C',&symname); + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'D',&symname); + + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'G',&symname); + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'I',&symname); + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'R',&symname); + + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'S',&symname); + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'S',&symname); + ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'T',&symname); + + if (ret < 0) + return SLBT_NESTED_ERROR(dctx); + } + + /* null-terminate the vtable */ + if (slbt_dprintf(fdout,"\n\t{%d,%*c%d}\n",0,len,' ',0) < 0) + return SLBT_NESTED_ERROR(mctx->dctx); + + /* close vtable, wrap translation unit */ + if (slbt_dprintf(fdout, + "};\n\n" + "#ifdef __cplusplus\n" + "}\n" + "#endif\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + return 0; +} + + +static int slbt_ar_create_dlsyms_impl( + struct slbt_archive_ctx ** arctxv, + const char * dlunit, + const char * path, + mode_t mode) +{ + int ret; + struct slbt_archive_ctx ** actx; + struct slbt_exec_ctx * ectx; + struct slbt_archive_meta_impl * mctx; + const struct slbt_driver_ctx * dctx; + struct slbt_fd_ctx fdctx; + int fdout; + + mctx = slbt_archive_meta_ictx(arctxv[0]->meta); + dctx = mctx->dctx; + ectx = 0; + + if (slbt_lib_get_driver_fdctx(dctx,&fdctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if (path) { + if ((fdout = openat( + fdctx.fdcwd,path, + O_WRONLY|O_CREAT|O_TRUNC, + mode)) < 0) + return SLBT_SYSTEM_ERROR(dctx,path); + } else { + fdout = fdctx.fdout; + } + + for (actx=arctxv; *actx; actx++) { + mctx = slbt_archive_meta_ictx((*actx)->meta); + + if (!mctx->syminfo && !ectx) + if (slbt_ectx_get_exec_ctx(dctx,&ectx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if (!mctx->syminfo) + if (slbt_ar_update_syminfo(*actx) < 0) + return SLBT_NESTED_ERROR(dctx); + } + + if (ectx) + slbt_ectx_free_exec_ctx(ectx); + + ret = slbt_ar_output_dlsyms_impl( + fdout,dctx,arctxv,dlunit); + + if (path) { + close(fdout); + } + + return ret; +} + + +int slbt_ar_create_dlsyms( + struct slbt_archive_ctx ** arctxv, + const char * dlunit, + const char * path, + mode_t mode) +{ + return slbt_ar_create_dlsyms_impl(arctxv,dlunit,path,mode); +} diff --git a/src/arbits/slbt_archive_mapfile.c b/src/arbits/slbt_archive_mapfile.c new file mode 100644 index 0000000..18f94dc --- /dev/null +++ b/src/arbits/slbt_archive_mapfile.c @@ -0,0 +1,162 @@ +/*******************************************************************/ +/* 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 <time.h> +#include <locale.h> +#include <regex.h> +#include <inttypes.h> +#include <slibtool/slibtool.h> +#include <slibtool/slibtool_output.h> +#include "slibtool_driver_impl.h" +#include "slibtool_dprintf_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_pecoff_impl.h" +#include "slibtool_ar_impl.h" + +/********************************************************/ +/* Generate a symbol mapfile (aka version script) that */ +/* could be passed to the host linker. */ +/* */ +/* Since the symbol list is directly derived from the */ +/* archive's armap member, prepending symbol names with */ +/* an underscore (where relevant) is not necessary. */ +/********************************************************/ + +static int slbt_ar_output_mapfile_impl( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * mctx, + int fdout) +{ + bool fsort; + bool fcoff; + bool fmach; + const char * dot; + const char * mark; + const char * regex; + const char ** symv; + const char ** symstrv; + regex_t regctx; + regmatch_t pmatch[2] = {{0,0},{0,0}}; + char strbuf[4096]; + + fsort = !(dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_NOSORT); + + fmach = slbt_host_objfmt_is_macho(dctx); + fmach |= (mctx->ofmtattr & AR_OBJECT_ATTR_MACHO); + + fcoff = slbt_host_objfmt_is_coff(dctx); + fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + if (fcoff) { + if (slbt_dprintf(fdout,"EXPORTS\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } else if (fmach) { + if (slbt_dprintf(fdout,"# export_list, armap underscores\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } else { + if (slbt_dprintf(fdout,"{\n" "\t" "global:\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + + if (fsort && !mctx->mapstrv) + if (slbt_update_mapstrv(dctx,mctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if ((regex = dctx->cctx->regex)) + if (regcomp(®ctx,regex,REG_EXTENDED|REG_NEWLINE)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + symstrv = fsort ? mctx->mapstrv : mctx->symstrv; + + for (symv=symstrv; *symv; symv++) { + if (!fcoff || slbt_is_strong_coff_symbol(*symv)) { + if (!regex || !regexec(®ctx,*symv,1,pmatch,0)) { + if (fcoff) { + if (slbt_dprintf(fdout," %s\n",*symv) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } else if (fmach) { + if (slbt_dprintf(fdout,"%s\n",*symv) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } else { + if (slbt_dprintf(fdout,"\t\t%s;\n",*symv) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + /* coff weak symbols: expsym = .weak.alias.strong */ + } else if (fcoff && !strncmp(*symv,".weak.",6)) { + mark = &(*symv)[6]; + dot = strchr(mark,'.'); + + strncpy(strbuf,mark,dot-mark); + strbuf[dot-mark] = '\0'; + + if (!regex || !regexec(®ctx,strbuf,1,pmatch,0)) + if (slbt_dprintf(fdout," %s = %s\n",strbuf,++dot) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if (regex) + regfree(®ctx); + + if (!fcoff && !fmach) + if (slbt_dprintf(fdout,"\n\t" "local:\n" "\t\t*;\n" "};\n") < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + return 0; +} + + +static int slbt_ar_create_mapfile_impl( + const struct slbt_archive_meta * meta, + const char * path, + mode_t mode) +{ + int ret; + struct slbt_archive_meta_impl * mctx; + const struct slbt_driver_ctx * dctx; + struct slbt_fd_ctx fdctx; + int fdout; + + mctx = slbt_archive_meta_ictx(meta); + dctx = (slbt_archive_meta_ictx(meta))->dctx; + + if (slbt_lib_get_driver_fdctx(dctx,&fdctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if (!meta->a_memberv) + return 0; + + if (path) { + if ((fdout = openat( + fdctx.fdcwd,path, + O_WRONLY|O_CREAT|O_TRUNC, + mode)) < 0) + return SLBT_SYSTEM_ERROR(dctx,path); + } else { + fdout = fdctx.fdout; + } + + ret = slbt_ar_output_mapfile_impl( + dctx,mctx,fdout); + + if (path) { + close(fdout); + } + + return ret; +} + + +int slbt_ar_create_mapfile( + const struct slbt_archive_meta * meta, + const char * path, + mode_t mode) +{ + return slbt_ar_create_mapfile_impl(meta,path,mode); +} diff --git a/src/arbits/slbt_archive_mapstrv.c b/src/arbits/slbt_archive_mapstrv.c new file mode 100644 index 0000000..e97db76 --- /dev/null +++ b/src/arbits/slbt_archive_mapstrv.c @@ -0,0 +1,47 @@ +/*******************************************************************/ +/* 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 <stdlib.h> +#include <inttypes.h> +#include <slibtool/slibtool.h> +#include "slibtool_driver_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_visibility_impl.h" +#include "slibtool_ar_impl.h" +#include "slibtool_coff_impl.h" + +static int slbt_qsort_strcmp(const void * a, const void * b) +{ + return strcmp(*(const char **)a,*(const char **)b); +} + +slbt_hidden int slbt_update_mapstrv( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * mctx) +{ + bool fcoff; + size_t nsyms; + const char ** symv; + const char ** mapstrv; + + fcoff = slbt_host_objfmt_is_coff(dctx); + fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + for (nsyms=0,symv=mctx->symstrv; *symv; symv++) + nsyms++; + + if (!(mapstrv = calloc(nsyms+1,sizeof(const char *)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + for (nsyms=0,symv=mctx->symstrv; *symv; symv++) + mapstrv[nsyms++] = *symv; + + qsort(mapstrv,nsyms,sizeof(const char *),fcoff ? slbt_coff_qsort_strcmp : slbt_qsort_strcmp); + + mctx->mapstrv = mapstrv; + + return 0; +} diff --git a/src/arbits/slbt_archive_merge.c b/src/arbits/slbt_archive_merge.c new file mode 100644 index 0000000..5f29235 --- /dev/null +++ b/src/arbits/slbt_archive_merge.c @@ -0,0 +1,734 @@ +/*******************************************************************/ +/* 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 <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 + +/* 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; 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_ar_get_archive_meta(dctx,arctx->map,&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; +} 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); + } +} diff --git a/src/arbits/slbt_archive_store.c b/src/arbits/slbt_archive_store.c new file mode 100644 index 0000000..d09a3f0 --- /dev/null +++ b/src/arbits/slbt_archive_store.c @@ -0,0 +1,145 @@ +/*******************************************************************/ +/* 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 <time.h> +#include <fcntl.h> +#include <stdio.h> +#include <stddef.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <sys/stat.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" + +/****************************************************/ +/* As elsewhere in slibtool, file-system operations */ +/* utilizie the _at variants of the relevant posix */ +/* interfaces. In the case of archives, that means */ +/* passing dctx->fdctx->fdcwd as the _fdat_ param, */ +/* where dctx is the driver context which was used */ +/* with slbt_ar_get_archive_ctx(). */ +/************************************************** */ + +#define PPRIX64 "%"PRIx64 + +int slbt_ar_store_archive( + struct slbt_archive_ctx * arctx, + const char * path, + mode_t mode) +{ + const struct slbt_driver_ctx * dctx; + struct stat st; + int fdat; + int fdtmp; + void * addr; + char * mark; + char * slash; + size_t buflen; + size_t nbytes; + ssize_t written; + char buf[PATH_MAX]; + + /* init dctx */ + if (!(dctx = slbt_get_archive_ictx(arctx)->dctx)) + return -1; + + /* validation */ + if (strlen(path) >= PATH_MAX) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + /**************************************************/ + /* create temporary file in the target directory */ + /* */ + /* the tmpfile name pattern involes the inode */ + /* of the target directory, a local stack address */ + /* in the calling thread, the current time, and */ + /* finally the pid of the current process. */ + /**************************************************/ + + memset(buf,0,sizeof(buf)); + strcpy(buf,path); + + fdat = slbt_driver_fdcwd(dctx); + + addr = buf; + mark = (slash = strrchr(buf,'/')) + ? slash : buf; + + if (slash) { + *++mark = '\0'; + + if (fstatat(fdat,buf,&st,0) < 0) + return SLBT_SYSTEM_ERROR( + dctx,buf); + } else { + if (fstatat(fdat,".",&st,0) < 0) + return SLBT_SYSTEM_ERROR( + dctx,0); + } + + buflen = sizeof(buf) - (mark - buf); + nbytes = snprintf( + mark, + buflen, + ".slibtool.tmpfile" + ".inode."PPRIX64 + ".time."PPRIX64 + ".salt.%p" + ".pid.%d" + ".tmp", + st.st_ino, + time(0),addr, + getpid()); + + if (nbytes >= buflen) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + if ((fdtmp = openat(fdat,buf,O_WRONLY|O_CREAT|O_EXCL,mode)) < 0) + return SLBT_SYSTEM_ERROR(dctx,buf); + + /* set archive size */ + if (ftruncate(fdtmp,arctx->map->map_size) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + + /* write archive */ + mark = arctx->map->map_addr; + nbytes = arctx->map->map_size; + + for (; nbytes; ) { + written = write(fdtmp,mark,nbytes); + + while ((written < 0) && (errno == EINTR)) + written = write(fdtmp,mark,nbytes); + + if (written < 0) { + unlinkat(fdat,buf,0); + return SLBT_SYSTEM_ERROR(dctx,0); + }; + + nbytes -= written; + mark += written; + } + + /* finalize (atomically) */ + if (renameat(fdat,buf,fdat,path) < 0) { + unlinkat(fdat,buf,0); + return SLBT_SYSTEM_ERROR(dctx,buf); + } + + /* yay */ + return 0; +} diff --git a/src/arbits/slbt_archive_symfile.c b/src/arbits/slbt_archive_symfile.c new file mode 100644 index 0000000..49d2ff0 --- /dev/null +++ b/src/arbits/slbt_archive_symfile.c @@ -0,0 +1,138 @@ +/*******************************************************************/ +/* 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 <time.h> +#include <locale.h> +#include <regex.h> +#include <inttypes.h> +#include <slibtool/slibtool.h> +#include <slibtool/slibtool_output.h> +#include "slibtool_driver_impl.h" +#include "slibtool_dprintf_impl.h" +#include "slibtool_errinfo_impl.h" +#include "slibtool_pecoff_impl.h" +#include "slibtool_ar_impl.h" + +/********************************************************/ +/* Generate a symbol list that could be used by a build */ +/* system for code generation or any other purpose. */ +/* */ +/* The output matches the -Wposix variant of symbol */ +/* printing, but the interface should be kept separate */ +/* since the _au_output_ functions implement several */ +/* output format, whereas here only a single, plain */ +/* output is expected. */ +/********************************************************/ + +static int slbt_ar_output_symfile_impl( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * mctx, + int fdout) +{ + bool fsort; + bool fcoff; + const char * dot; + const char * mark; + const char * regex; + const char ** symv; + const char ** symstrv; + regex_t regctx; + regmatch_t pmatch[2] = {{0,0},{0,0}}; + char strbuf[4096]; + + fsort = !(dctx->cctx->fmtflags & SLBT_OUTPUT_ARCHIVE_NOSORT); + + fcoff = slbt_host_objfmt_is_coff(dctx); + fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + if (fsort && !mctx->mapstrv) + if (slbt_update_mapstrv(dctx,mctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if ((regex = dctx->cctx->regex)) + if (regcomp(®ctx,regex,REG_EXTENDED|REG_NEWLINE)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + symstrv = fsort ? mctx->mapstrv : mctx->symstrv; + + for (symv=symstrv; *symv; symv++) { + if (!fcoff || slbt_is_strong_coff_symbol(*symv)) { + if (!regex || !regexec(®ctx,*symv,1,pmatch,0)) { + if (slbt_dprintf(fdout,"%s\n",*symv) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + + /* coff weak symbols: expsym = .weak.alias.strong */ + } else if (fcoff && !strncmp(*symv,".weak.",6)) { + mark = &(*symv)[6]; + dot = strchr(mark,'.'); + + strncpy(strbuf,mark,dot-mark); + strbuf[dot-mark] = '\0'; + + if (!regex || !regexec(®ctx,strbuf,1,pmatch,0)) + if (slbt_dprintf(fdout," %s = %s\n",strbuf,++dot) < 0) + return SLBT_SYSTEM_ERROR(dctx,0); + } + } + + if (regex) + regfree(®ctx); + + return 0; +} + + +static int slbt_ar_create_symfile_impl( + const struct slbt_archive_meta * meta, + const char * path, + mode_t mode) +{ + int ret; + struct slbt_archive_meta_impl * mctx; + const struct slbt_driver_ctx * dctx; + struct slbt_fd_ctx fdctx; + int fdout; + + mctx = slbt_archive_meta_ictx(meta); + dctx = (slbt_archive_meta_ictx(meta))->dctx; + + if (slbt_lib_get_driver_fdctx(dctx,&fdctx) < 0) + return SLBT_NESTED_ERROR(dctx); + + if (!meta->a_memberv) + return 0; + + if (path) { + if ((fdout = openat( + fdctx.fdcwd,path, + O_WRONLY|O_CREAT|O_TRUNC, + mode)) < 0) + return SLBT_SYSTEM_ERROR(dctx,path); + } else { + fdout = fdctx.fdout; + } + + ret = slbt_ar_output_symfile_impl( + dctx,mctx,fdout); + + if (path) { + close(fdout); + } + + return ret; +} + + +int slbt_ar_create_symfile( + const struct slbt_archive_meta * meta, + const char * path, + mode_t mode) +{ + return slbt_ar_create_symfile_impl(meta,path,mode); +} diff --git a/src/arbits/slbt_archive_syminfo.c b/src/arbits/slbt_archive_syminfo.c new file mode 100644 index 0000000..09c90a8 --- /dev/null +++ b/src/arbits/slbt_archive_syminfo.c @@ -0,0 +1,345 @@ +/*******************************************************************/ +/* 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 <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <limits.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <slibtool/slibtool.h> +#include "slibtool_ar_impl.h" +#include "slibtool_coff_impl.h" +#include "slibtool_driver_impl.h" +#include "slibtool_dprintf_impl.h" +#include "slibtool_spawn_impl.h" +#include "slibtool_snprintf_impl.h" +#include "slibtool_errinfo_impl.h" + +static const char ar_symbol_type_A[] = "A"; +static const char ar_symbol_type_B[] = "B"; +static const char ar_symbol_type_C[] = "C"; +static const char ar_symbol_type_D[] = "D"; +static const char ar_symbol_type_G[] = "G"; +static const char ar_symbol_type_I[] = "I"; +static const char ar_symbol_type_R[] = "R"; +static const char ar_symbol_type_S[] = "S"; +static const char ar_symbol_type_T[] = "T"; +static const char ar_symbol_type_W[] = "W"; + +static const char * const ar_symbol_type['Z'-'A'] = { + ['A'-'A'] = ar_symbol_type_A, + ['B'-'A'] = ar_symbol_type_B, + ['C'-'A'] = ar_symbol_type_C, + ['D'-'A'] = ar_symbol_type_D, + ['G'-'A'] = ar_symbol_type_G, + ['I'-'A'] = ar_symbol_type_I, + ['R'-'A'] = ar_symbol_type_R, + ['S'-'A'] = ar_symbol_type_S, + ['T'-'A'] = ar_symbol_type_T, + ['W'-'A'] = ar_symbol_type_W, +}; + +static void slbt_ar_update_syminfo_child( + char * program, + char * arname, + int fdout) +{ + char * argv[6]; + + argv[0] = program; + argv[1] = "-P"; + argv[2] = "-A"; + argv[3] = "-g"; + argv[4] = arname; + argv[5] = 0; + + if (dup2(fdout,1) == 1) + execvp(program,argv); + + _exit(EXIT_FAILURE); +} + +static int slbt_obtain_nminfo( + struct slbt_archive_ctx_impl * ictx, + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * mctx, + int fdout) +{ + int ret; + int pos; + int fdcwd; + int fdarg; + pid_t pid; + pid_t rpid; + int ecode; + char ** argv; + char arname [PATH_MAX]; + char output [PATH_MAX]; + char program[PATH_MAX]; + + /* fdcwd */ + fdcwd = slbt_driver_fdcwd(dctx); + + /* tool-specific argument vector */ + argv = (slbt_get_driver_ictx(dctx))->host.nm_argv; + + /* ar alternate argument vector */ + if (argv) { + if (slbt_snprintf(program,sizeof(program), + "%s",argv[0]) < 0) + return SLBT_BUFFER_ERROR(dctx); + } else { + if (slbt_snprintf(program,sizeof(program), + "%s",dctx->cctx->host.nm) < 0) + return SLBT_BUFFER_ERROR(dctx); + } + + /* arname (.nm suffix, buf treat as .syminfo) */ + pos = slbt_snprintf( + arname,sizeof(arname)-8,"%s", + ictx->path); + + /* output */ + if ((fdarg = fdout) < 0) { + strcpy(output,arname); + strcpy(&output[pos],".nm"); + + if ((fdout = openat(fdcwd,output,O_CREAT|O_TRUNC|O_RDWR,0644)) < 0) + return SLBT_SYSTEM_ERROR(dctx,output); + } else { + strcpy(output,"@nminfo@"); + } + + /* fork */ + if ((pid = slbt_fork()) < 0) { + close(fdout); + return SLBT_SYSTEM_ERROR(dctx,0); + } + + /* child */ + if (pid == 0) + slbt_ar_update_syminfo_child( + program,arname,fdout); + + /* parent */ + rpid = waitpid( + pid, + &ecode, + 0); + + /* nm output */ + if ((rpid > 0) && (ecode == 0)) + ret = slbt_impl_get_txtfile_ctx( + dctx,output,fdout, + &mctx->nminfo); + + if (fdarg < 0) + close(fdout); + + if (rpid < 0) { + return SLBT_SYSTEM_ERROR(dctx,0); + + } else if (ecode) { + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + } + + return (ret < 0) ? SLBT_NESTED_ERROR(dctx) : 0; +} + +static int slbt_get_symbol_nm_info( + struct slbt_archive_ctx * actx, + struct slbt_archive_meta_impl * mctx, + uint64_t idx) +{ + int cint; + const char ** pline; + const char * mark; + const char * cap; + const char * objname; + const char * symname; + struct ar_meta_symbol_info * syminfo; + struct ar_meta_member_info ** pmember; + + symname = mctx->symstrv[idx]; + syminfo = &mctx->syminfo[idx]; + + for (pline=mctx->nminfo->txtlinev; *pline; pline++) { + mark = *pline; + + if (!(mark = strchr(mark,']'))) + return -1; + + if ((*++mark != ':') || (*++mark != ' ')) + return -1; + + cap = ++mark; + + for (; *cap && !isspace((cint = *cap)); ) + cap++; + + if (*cap != ' ') + return -1; + + if (!(strncmp(symname,mark,cap-mark))) { + mark = ++cap; + + /* space only according to posix, but ... */ + if (mark[1] && (mark[1] != ' ')) + return -1; + + switch (mark[0]) { + case 'A': + case 'B': + case 'C': + case 'D': + case 'G': + case 'I': + case 'R': + case 'S': + case 'T': + case 'W': + syminfo->ar_symbol_type = ar_symbol_type[mark[0]-'A']; + break; + + default: + break; + } + + if (syminfo->ar_symbol_type) { + syminfo->ar_archive_name = *actx->path; + syminfo->ar_symbol_name = symname; + + if (!(mark = strchr(*pline,'['))) + return -1; + + if (!(cap = strchr(++mark,']'))) + return -1; + } + + pmember = mctx->memberv; + + for (; *pmember && !syminfo->ar_object_name; ) { + objname = (*pmember)->ar_file_header.ar_member_name; + + if (!(strncmp(objname,mark,cap-mark))) + syminfo->ar_object_name = objname; + + pmember++; + } + + mctx->syminfv[idx] = syminfo; + } + } + + return (mctx->syminfv[idx] ? 0 : (-1)); +} + +static int slbt_qsort_syminfo_cmp(const void * a, const void * b) +{ + struct ar_meta_symbol_info ** syminfoa; + struct ar_meta_symbol_info ** syminfob; + + syminfoa = (struct ar_meta_symbol_info **)a; + syminfob = (struct ar_meta_symbol_info **)b; + + return strcmp( + (*syminfoa)->ar_symbol_name, + (*syminfob)->ar_symbol_name); +} + +static int slbt_coff_qsort_syminfo_cmp(const void * a, const void * b) +{ + struct ar_meta_symbol_info ** syminfoa; + struct ar_meta_symbol_info ** syminfob; + + syminfoa = (struct ar_meta_symbol_info **)a; + syminfob = (struct ar_meta_symbol_info **)b; + + return slbt_coff_qsort_strcmp( + &(*syminfoa)->ar_symbol_name, + &(*syminfob)->ar_symbol_name); +} + +static int slbt_ar_update_syminfo_impl( + struct slbt_archive_ctx * actx, + int fdout) +{ + const struct slbt_driver_ctx * dctx; + struct slbt_archive_ctx_impl * ictx; + struct slbt_archive_meta_impl * mctx; + uint64_t idx; + bool fcoff; + + /* driver context, etc. */ + ictx = slbt_get_archive_ictx(actx); + mctx = slbt_archive_meta_ictx(ictx->meta); + dctx = ictx->dctx; + + /* nm -P -A -g */ + if (mctx->armaps.armap_nsyms) { + if (slbt_obtain_nminfo(ictx,dctx,mctx,fdout) < 0) + return SLBT_NESTED_ERROR(dctx); + } else { + if (slbt_lib_get_txtfile_ctx( + dctx,"/dev/null", + &mctx->nminfo) < 0) + return SLBT_NESTED_ERROR(dctx); + } + + /* free old syminfo vector */ + if (mctx->syminfv) + free(mctx->syminfv); + + /* syminfo vector: armap symbols only */ + if (!(mctx->syminfo = calloc( + mctx->armaps.armap_nsyms + 1, + sizeof(*mctx->syminfo)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (!(mctx->syminfv = calloc( + mctx->armaps.armap_nsyms + 1, + sizeof(*mctx->syminfv)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + /* do the thing */ + for (idx=0; idx<mctx->armaps.armap_nsyms; idx++) + if (slbt_get_symbol_nm_info(actx,mctx,idx) < 0) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_FLOW_ERROR); + + /* coff-aware sorting */ + fcoff = slbt_host_objfmt_is_coff(dctx); + fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF); + + qsort(mctx->syminfv,mctx->armaps.armap_nsyms,sizeof(*mctx->syminfv), + fcoff ? slbt_coff_qsort_syminfo_cmp : slbt_qsort_syminfo_cmp); + + /* yay */ + return 0; +} + +slbt_hidden int slbt_ar_update_syminfo( + struct slbt_archive_ctx * actx) +{ + return slbt_ar_update_syminfo_impl(actx,(-1)); +} + +slbt_hidden int slbt_ar_update_syminfo_ex( + struct slbt_archive_ctx * actx, + int fdout) +{ + return slbt_ar_update_syminfo_impl(actx,fdout); +} diff --git a/src/arbits/slbt_armap_bsd_32.c b/src/arbits/slbt_armap_bsd_32.c new file mode 100644 index 0000000..ee94b70 --- /dev/null +++ b/src/arbits/slbt_armap_bsd_32.c @@ -0,0 +1,157 @@ +/*******************************************************************/ +/* 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 <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" + +slbt_hidden int slbt_ar_parse_primary_armap_bsd_32( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * m) + +{ + struct ar_raw_armap_bsd_32 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_32 *armapref; + struct ar_meta_armap_ref_32 * symrefs; + uint32_t idx; + uint32_t uref; + uint32_t attr; + uint32_t nsyms; + uint32_t nstrs; + uint32_t sizeofrefs_le; + uint32_t sizeofrefs_be; + uint32_t sizeofrefs; + uint32_t sizeofstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x04]; + + armap = &m->armaps.armap_bsd_32; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_size_of_refs = mark; + uch = *mark++; + + armap->ar_first_name_offset = mark; + + sizeofrefs_le = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + sizeofrefs_be = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + + if (sizeofrefs_le < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_le; + attr = AR_ARMAP_ATTR_LE_32; + + } else if (sizeofrefs_be < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_be; + attr = AR_ARMAP_ATTR_BE_32; + } else { + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_SIZE_OF_REFS); + } + + nsyms = sizeofrefs / sizeof(struct ar_raw_armap_ref_32); + mark += (sizeofrefs / sizeof(*mark)); + + armap->ar_size_of_strs = mark; + uch = *mark++; + + sizeofstrs = (attr == AR_ARMAP_ATTR_LE_32) + ? (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0] + : (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + + if (sizeofstrs > memberp->ar_object_size - 2*sizeof(*mark) - sizeofrefs) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_SIZE_OF_STRS); + + m->symstrs = (const char *)mark; + + cap = m->symstrs; + cap += sizeofstrs; + + if ((cap == m->symstrs) && nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1] && (nstrs < nsyms)) + nstrs++; + } + + if (nstrs != nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (!(m->armaps.armap_symrefs_32 = calloc(nsyms + 1,sizeof(*symrefs)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + mark = armap->ar_first_name_offset; + symrefs = m->armaps.armap_symrefs_32; + + for (idx=0; idx<nsyms; idx++) { + uch = *mark++; + + uref = (attr == AR_ARMAP_ATTR_BE_32) + ? (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3] + : (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + + symrefs[idx].ar_name_offset = uref; + + uch = *mark++; + + uref = (attr == AR_ARMAP_ATTR_BE_32) + ? (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3] + : (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + + symrefs[idx].ar_member_offset = uref; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_32; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_bsd = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_BSD | attr; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = sizeofrefs; + armapref->ar_size_of_strs = sizeofstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_32 = armapref; + + return 0; +} diff --git a/src/arbits/slbt_armap_bsd_64.c b/src/arbits/slbt_armap_bsd_64.c new file mode 100644 index 0000000..8f214b9 --- /dev/null +++ b/src/arbits/slbt_armap_bsd_64.c @@ -0,0 +1,174 @@ +/*******************************************************************/ +/* 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 <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" + +slbt_hidden int slbt_ar_parse_primary_armap_bsd_64( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * m) +{ + struct ar_raw_armap_bsd_64 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_64 *armapref; + struct ar_meta_armap_ref_64 * symrefs; + uint64_t idx; + uint64_t uref_hi; + uint64_t uref_lo; + uint32_t attr; + uint64_t u64_lo; + uint64_t u64_hi; + uint64_t nsyms; + uint64_t nstrs; + uint64_t sizeofrefs_le; + uint64_t sizeofrefs_be; + uint64_t sizeofrefs; + uint64_t sizeofstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x08]; + + armap = &m->armaps.armap_bsd_64; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_size_of_refs = mark; + uch = *mark++; + + armap->ar_first_name_offset = mark; + + u64_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + u64_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + + sizeofrefs_le = u64_lo + (u64_hi << 32); + + u64_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + u64_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + + sizeofrefs_be = (u64_hi << 32) + u64_lo; + + if (sizeofrefs_le < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_le; + attr = AR_ARMAP_ATTR_LE_64; + + } else if (sizeofrefs_be < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_be; + attr = AR_ARMAP_ATTR_BE_64; + } else { + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_SIZE_OF_REFS); + } + + nsyms = sizeofrefs / sizeof(struct ar_raw_armap_ref_64); + mark += (sizeofrefs / sizeof(*mark)); + + armap->ar_size_of_strs = mark; + uch = *mark++; + + if (attr == AR_ARMAP_ATTR_LE_64) { + u64_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + u64_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + } else { + u64_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + u64_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + } + + sizeofstrs = u64_lo + (u64_hi << 32); + m->symstrs = (const char *)mark; + + cap = m->symstrs; + cap += sizeofstrs; + + if ((cap == m->symstrs) && nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1] && (nstrs < nsyms)) + nstrs++; + } + + if (nstrs != nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (!(m->armaps.armap_symrefs_64 = calloc(nsyms + 1,sizeof(*symrefs)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + mark = armap->ar_first_name_offset; + symrefs = m->armaps.armap_symrefs_64; + + for (idx=0; idx<nsyms; idx++) { + uch = *mark++; + + if (attr == AR_ARMAP_ATTR_BE_64) { + uref_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + uref_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + } else { + uref_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + uref_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + } + + symrefs[idx].ar_name_offset = (uref_hi << 32) + uref_lo; + + uch = *mark++; + + if (attr == AR_ARMAP_ATTR_BE_64) { + uref_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + uref_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + } else { + uref_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + uref_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + } + + symrefs[idx].ar_member_offset = (uref_hi << 32) + uref_lo; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_64; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_bsd = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_BSD | attr; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = sizeofrefs; + armapref->ar_size_of_strs = sizeofstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_64 = armapref; + + return 0; +} diff --git a/src/arbits/slbt_armap_sysv_32.c b/src/arbits/slbt_armap_sysv_32.c new file mode 100644 index 0000000..bc5a0d5 --- /dev/null +++ b/src/arbits/slbt_armap_sysv_32.c @@ -0,0 +1,120 @@ +/*******************************************************************/ +/* 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 <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" + +slbt_hidden int slbt_ar_parse_primary_armap_sysv_32( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * m) +{ + struct ar_raw_armap_sysv_32 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_32 *armapref; + struct ar_meta_armap_ref_32 * symrefs; + uint32_t idx; + uint32_t uref; + uint32_t nsyms; + uint32_t nstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x04]; + + armap = &m->armaps.armap_sysv_32; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_num_of_syms = mark; + uch = *mark++; + + armap->ar_first_ref_offset = mark; + + nsyms = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + mark += nsyms; + + if (memberp->ar_object_size < (sizeof(*mark) + (nsyms * sizeof(*mark)))) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_NUMBER_OF_SYMS); + + m->symstrs = (const char *)mark; + + cap = memberp->ar_object_data; + cap += memberp->ar_object_size; + + if ((cap == m->symstrs) && nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1]) + nstrs++; + } + + if (nstrs != nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (cap[-1]) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (!(m->armaps.armap_symrefs_32 = calloc(nsyms + 1,sizeof(*symrefs)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + mark = armap->ar_first_ref_offset; + symrefs = m->armaps.armap_symrefs_32; + + for (idx=0,uch=*mark; idx<nsyms; idx++,uch=*++mark) { + uref = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + symrefs[idx].ar_member_offset = uref; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_32; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_sysv = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_SYSV | AR_ARMAP_ATTR_BE_32; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = nsyms * sizeof(*mark); + armapref->ar_size_of_strs = cap - m->symstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_32 = armapref; + + return 0; +} diff --git a/src/arbits/slbt_armap_sysv_64.c b/src/arbits/slbt_armap_sysv_64.c new file mode 100644 index 0000000..961ee20 --- /dev/null +++ b/src/arbits/slbt_armap_sysv_64.c @@ -0,0 +1,128 @@ +/*******************************************************************/ +/* 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 <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" + +slbt_hidden int slbt_ar_parse_primary_armap_sysv_64( + const struct slbt_driver_ctx * dctx, + struct slbt_archive_meta_impl * m) +{ + struct ar_raw_armap_sysv_64 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_64 *armapref; + struct ar_meta_armap_ref_64 * symrefs; + uint64_t idx; + uint64_t uref_hi; + uint64_t uref_lo; + uint64_t nsyms_hi; + uint64_t nsyms_lo; + uint64_t nsyms; + uint64_t nstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x08]; + + armap = &m->armaps.armap_sysv_64; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_num_of_syms = mark; + uch = *mark++; + + armap->ar_first_ref_offset = mark; + + nsyms_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + nsyms_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + + nsyms = (nsyms_hi << 32) + nsyms_lo; + mark += nsyms; + + if (memberp->ar_object_size < (sizeof(*mark) + (nsyms * sizeof(*mark)))) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_NUMBER_OF_SYMS); + + m->symstrs = (const char *)mark; + + cap = memberp->ar_object_data; + cap += memberp->ar_object_size; + + if ((cap == m->symstrs) && nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1]) + nstrs++; + } + + if (nstrs != nsyms) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (cap[-1]) + return SLBT_CUSTOM_ERROR( + dctx, + SLBT_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + if (!(m->armaps.armap_symrefs_64 = calloc(nsyms + 1,sizeof(*symrefs)))) + return SLBT_SYSTEM_ERROR(dctx,0); + + mark = armap->ar_first_ref_offset; + symrefs = m->armaps.armap_symrefs_64; + + for (idx=0,uch=*mark; idx<nsyms; idx++,uch=*++mark) { + uref_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + uref_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + + symrefs[idx].ar_member_offset = (uref_hi << 32) + uref_lo; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_64; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_sysv = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_SYSV | AR_ARMAP_ATTR_BE_64; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = nsyms * sizeof(*mark); + armapref->ar_size_of_strs = cap - m->symstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_64 = armapref; + + return 0; +} |