summaryrefslogtreecommitdiff
path: root/src/arbits/slbt_archive_dlsyms.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arbits/slbt_archive_dlsyms.c')
-rw-r--r--src/arbits/slbt_archive_dlsyms.c458
1 files changed, 458 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..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);
+}