diff options
Diffstat (limited to 'src/arbits')
-rw-r--r-- | src/arbits/slbt_archive_dlsyms.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/src/arbits/slbt_archive_dlsyms.c b/src/arbits/slbt_archive_dlsyms.c new file mode 100644 index 0000000..453d4b6 --- /dev/null +++ b/src/arbits/slbt_archive_dlsyms.c @@ -0,0 +1,370 @@ +/*******************************************************************/ +/* slibtool: a skinny libtool implementation, written in C */ +/* Copyright (C) 2016--2024 SysDeer Technologies, LLC */ +/* Released under the Standard MIT License; see COPYING.SLIBTOOL. */ +/*******************************************************************/ + +#include <slibtool/slibtool.h> +#include "slibtool_ar_impl.h" +#include "slibtool_driver_impl.h" +#include "slibtool_snprintf_impl.h" +#include "slibtool_errinfo_impl.h" + +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 = 0; + + 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,"/* %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 (slbt_dprintf(fdout, + (stype == 'T') + ? "extern int %s();\n" + : "extern char %s[];\n", + mctx->syminfv[idx]->ar_symbol_name) < 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; + + for (idx=0; idx<mctx->armaps.armap_nsyms; idx++) + if (mctx->syminfv[idx]->ar_symbol_type[0] == stype) + if ((len = strlen(mctx->syminfv[idx]->ar_symbol_name)) > 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; + char * symname; + + nsyms = 0; + symname = *namebuf; + + 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) { + if (slbt_snprintf(symname,sizeof(*namebuf), + "%s\",", + mctx->syminfv[idx]->ar_symbol_name) < 0) + return SLBT_SYSTEM_ERROR(mctx->dctx,0); + + if (slbt_dprintf(fdout,fmt, + symname, + (stype == 'T') ? "&" : "", + mctx->syminfv[idx]->ar_symbol_name) < 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; + 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],72, + "backward-compatible dlsym table"); + + cline[1][3+len] = ' '; + + len = snprintf(&cline[2][3],72, + "Generated by %s (slibtool %d.%d.%d)", + dctx->program, + verinfo->major,verinfo->minor,verinfo->revision); + + cline[2][3+len] = ' '; + + len = snprintf(&cline[3][3],72, + "[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); + + 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", + dsounit,dsounit) < 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); + + /* 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); + } + + /* 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_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; + + 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,0); + } else { + fdout = fdctx.fdout; + } + + 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); +} |