/*******************************************************************/
/* 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);
}