Blame src/arbits/slbt_archive_dlsyms.c

2ec747
/*******************************************************************/
eac61a
/*  slibtool: a strong libtool implementation, written in C        */
2ec747
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
2ec747
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
2ec747
/*******************************************************************/
2ec747
2ec747
#include <slibtool/slibtool.h>
2ec747
#include "slibtool_ar_impl.h"
2ec747
#include "slibtool_driver_impl.h"
2ec747
#include "slibtool_snprintf_impl.h"
2ec747
#include "slibtool_errinfo_impl.h"
2ec747
9f06b2
static const char * slbt_strong_symname(
9f06b2
	const char *    symname,
9f06b2
	bool            fcoff,
9f06b2
	char            (*strbuf)[4096])
9f06b2
{
9f06b2
	const char *    dot;
9f06b2
	const char *    mark;
9f06b2
	char *          sym;
9f06b2
9f06b2
	if (fcoff) {
9f06b2
		if (!strncmp(symname,"__imp_",6))
9f06b2
			return 0;
9f06b2
9f06b2
		if (strncmp(symname,".weak.",6))
9f06b2
			return symname;
9f06b2
9f06b2
		sym  = *strbuf;
9f06b2
		mark = &symname[6];
9f06b2
		dot  = strchr(mark,'.');
9f06b2
9f06b2
		strncpy(sym,mark,dot-mark);
9f06b2
		sym[dot-mark] = '\0';
9f06b2
9f06b2
		return sym;
9f06b2
	}
9f06b2
9f06b2
	return symname;
9f06b2
}
9f06b2
9f06b2
2ec747
static int slbt_ar_dlsyms_define_by_type(
2ec747
	int                                 fdout,
2ec747
	const char *                        arname,
2ec747
	struct slbt_archive_meta_impl *     mctx,
2ec747
	const char *                        desc,
2ec747
	const char                          stype)
2ec747
{
9f06b2
	uint64_t      idx;
9f06b2
	uint64_t      nsyms;
9f06b2
	bool          fcoff;
9f06b2
	const char *  symname;
9f06b2
	char          strbuf[4096];
2ec747
9f06b2
	for (idx=0,nsyms=0; idx<mctx->armaps.armap_nsyms; idx++)
2ec747
		if (mctx->syminfv[idx]->ar_symbol_type[0] == stype)
2ec747
			nsyms++;
2ec747
2ec747
	if (nsyms == 0)
2ec747
		return 0;
2ec747
9f06b2
	fcoff  = slbt_host_objfmt_is_coff(mctx->dctx);
9f06b2
	fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF);
9f06b2
2ec747
	if (slbt_dprintf(fdout,"/* %s (%s) */\n",desc,arname) < 0)
2ec747
		return SLBT_SYSTEM_ERROR(mctx->dctx,0);
2ec747
2ec747
	for (idx=0; idx<mctx->armaps.armap_nsyms; idx++)
2ec747
		if (mctx->syminfv[idx]->ar_symbol_type[0] == stype)
9f06b2
			if ((symname = slbt_strong_symname(
9f06b2
					mctx->syminfv[idx]->ar_symbol_name,
9f06b2
					fcoff,&strbuf)))
9f06b2
				if (slbt_dprintf(fdout,
9f06b2
						(stype == 'T')
9f06b2
							? "extern int %s();\n"
9f06b2
							: "extern char %s[];\n",
9f06b2
						symname) < 0)
9f06b2
					return SLBT_SYSTEM_ERROR(mctx->dctx,0);
2ec747
2ec747
	if (slbt_dprintf(fdout,"\n") < 0)
2ec747
		return SLBT_SYSTEM_ERROR(mctx->dctx,0);
2ec747
2ec747
	return 0;
2ec747
}
2ec747
2ec747
static int slbt_ar_dlsyms_get_max_len_by_type(
2ec747
	int                                 mlen,
2ec747
	struct slbt_archive_meta_impl *     mctx,
2ec747
	const char                          stype)
2ec747
{
9f06b2
	int           len;
9f06b2
	uint64_t      idx;
9f06b2
	bool          fcoff;
9f06b2
	const char *  symname;
9f06b2
	char          strbuf[4096];
9f06b2
9f06b2
	fcoff  = slbt_host_objfmt_is_coff(mctx->dctx);
9f06b2
	fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF);
2ec747
2ec747
	for (idx=0; idx<mctx->armaps.armap_nsyms; idx++)
2ec747
		if (mctx->syminfv[idx]->ar_symbol_type[0] == stype)
9f06b2
			if ((symname = slbt_strong_symname(
9f06b2
					mctx->syminfv[idx]->ar_symbol_name,
9f06b2
					fcoff,&strbuf)))
9f06b2
				if ((len = strlen(symname)) > mlen)
9f06b2
					mlen = len;
2ec747
2ec747
	return mlen;
2ec747
}
2ec747
2ec747
static int slbt_ar_dlsyms_add_by_type(
2ec747
	int                                 fdout,
2ec747
	struct slbt_archive_meta_impl *     mctx,
2ec747
	const char *                        fmt,
2ec747
	const char                          stype,
2ec747
	char                                (*namebuf)[4096])
2ec747
{
9f06b2
	uint64_t      idx;
9f06b2
	uint64_t      nsyms;
9f06b2
	bool          fcoff;
9f06b2
	const char *  symname;
9f06b2
	char          strbuf[4096];
2ec747
2ec747
	nsyms   = 0;
2ec747
	symname = *namebuf;
2ec747
9f06b2
	fcoff  = slbt_host_objfmt_is_coff(mctx->dctx);
9f06b2
	fcoff |= (mctx->ofmtattr & AR_OBJECT_ATTR_COFF);
9f06b2
2ec747
	for (idx=0; idx<mctx->armaps.armap_nsyms; idx++)
2ec747
		if (mctx->syminfv[idx]->ar_symbol_type[0] == stype)
2ec747
			nsyms++;
2ec747
2ec747
	if (nsyms == 0)
2ec747
		return 0;
2ec747
2ec747
	if (slbt_dprintf(fdout,"\n") < 0)
2ec747
		return SLBT_SYSTEM_ERROR(mctx->dctx,0);
2ec747
2ec747
	for (idx=0; idx<mctx->armaps.armap_nsyms; idx++) {
2ec747
		if (mctx->syminfv[idx]->ar_symbol_type[0] == stype) {
9f06b2
			symname = slbt_strong_symname(
9f06b2
				mctx->syminfv[idx]->ar_symbol_name,
9f06b2
				fcoff,&strbuf);
9f06b2
9f06b2
			if (symname) {
9f06b2
				if (slbt_snprintf(*namebuf,sizeof(*namebuf),
9f06b2
						"%s\",",symname) < 0)
9f06b2
					return SLBT_SYSTEM_ERROR(mctx->dctx,0);
9f06b2
9f06b2
				if (slbt_dprintf(fdout,fmt,
9f06b2
						*namebuf,
9f06b2
						(stype == 'T') ? "&" : "",
9f06b2
						symname) < 0)
9f06b2
					return SLBT_NESTED_ERROR(mctx->dctx);
9f06b2
			}
2ec747
		}
2ec747
	}
2ec747
2ec747
	return 0;
2ec747
}
2ec747
2ec747
2ec747
static int slbt_ar_output_dlsyms_impl(
2ec747
	int                                 fdout,
2ec747
	const struct slbt_driver_ctx *      dctx,
2ec747
	struct slbt_archive_ctx **          arctxv,
2ec747
	const char *                        dsounit)
2ec747
{
2ec747
	int                                 ret;
2ec747
	int                                 idx;
2ec747
	unsigned                            len;
2ec747
	unsigned                            cmp;
2ec747
	const char *                        arname;
b7e6e8
	const char *                        soname;
2ec747
	struct slbt_archive_ctx *           actx;
2ec747
	struct slbt_archive_ctx **          parctx;
2ec747
	struct slbt_archive_ctx_impl *      ictx;
2ec747
	struct slbt_archive_meta_impl *     mctx;
2ec747
	const struct slbt_source_version *  verinfo;
2ec747
	char                                dlsymfmt[32];
2ec747
	char                                cline[6][73];
2ec747
	char                                symname[4096];
2ec747
2ec747
	/* init */
2ec747
	actx    = arctxv[0];
2ec747
	verinfo = slbt_api_source_version();
2ec747
2ec747
	/* preamble */
2ec747
	memset(cline[0],'*',72);
2ec747
	memset(cline[1],' ',72);
2ec747
	memset(cline[2],' ',72);
2ec747
	memset(cline[3],' ',72);
2ec747
	memset(cline[4],'*',72);
2ec747
2ec747
	memset(cline[5],0,72);
2ec747
	cline[5][0] = '\n';
2ec747
da73d2
	len = snprintf(&cline[1][3],69,
2ec747
		"backward-compatible dlsym table");
2ec747
2ec747
	cline[1][3+len] = ' ';
2ec747
da73d2
	len = snprintf(&cline[2][3],69,
2ec747
		"Generated by %s (slibtool %d.%d.%d)",
2ec747
		dctx->program,
2ec747
		verinfo->major,verinfo->minor,verinfo->revision);
2ec747
2ec747
	cline[2][3+len] = ' ';
2ec747
da73d2
	len = snprintf(&cline[3][3],69,
2ec747
		"[commit reference: %s]",
2ec747
		verinfo->commit);
2ec747
2ec747
	cline[3][3+len] = ' ';
2ec747
2ec747
	for (idx=0; idx<5; idx++) {
2ec747
		cline[idx][0] = '/';
2ec747
		cline[idx][1] = '*';
2ec747
2ec747
		cline[idx][70] = '*';
2ec747
		cline[idx][71] = '/';
2ec747
2ec747
		cline[idx][72] = '\n';
2ec747
	}
2ec747
2ec747
	if (slbt_dprintf(fdout,"%s",&cline[0]) < 0)
2ec747
		return SLBT_SYSTEM_ERROR(dctx,0);
2ec747
2ec747
	if (slbt_dprintf(fdout,
2ec747
			"#ifdef __cplusplus\n"
2ec747
			"extern \"C\" {\n"
2ec747
			"#endif\n\n") < 0)
2ec747
		return SLBT_SYSTEM_ERROR(dctx,0);
2ec747
2ec747
	/* declarations */
2ec747
	for (parctx=arctxv; *parctx; parctx++) {
2ec747
		actx   = *parctx;
2ec747
		ictx   = slbt_get_archive_ictx(actx);
2ec747
		mctx   = slbt_archive_meta_ictx(ictx->meta);
2ec747
2ec747
		if ((arname = strrchr(*actx->path,'/')))
2ec747
			arname++;
2ec747
2ec747
		if (!arname)
2ec747
			arname = *actx->path;
2ec747
2ec747
		ret  = slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Absolute Values",     'A');
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: BSS Section",         'B');
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Common Section",      'C');
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Initialized Data",    'D');
2ec747
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Small Globals",       'G');
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Indirect References", 'I');
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Read-Only Section",   'R');
2ec747
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Small Objects",       'S');
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Data Symbols: Weak Symbols",        'W');
2ec747
2ec747
		ret |= slbt_ar_dlsyms_define_by_type(fdout,arname,mctx,"Text Section: Public Interfaces",   'T');
2ec747
2ec747
		if (ret < 0)
2ec747
			return SLBT_NESTED_ERROR(dctx);
2ec747
2ec747
	}
2ec747
2ec747
	/* vtable struct definition */
2ec747
	if (slbt_dprintf(fdout,
2ec747
			"/* name-address Public ABI struct definition */\n"
2ec747
			"struct lt_dlsym_symdef {\n"
2ec747
			"\tconst char *   dlsym_name;\n"
2ec747
			"\tvoid *         dlsym_addr;\n"
2ec747
			"};\n\n") < 0)
2ec747
		return SLBT_NESTED_ERROR(dctx);
2ec747
b7e6e8
	soname = (strcmp(dsounit,"@PROGRAM@")) ? dsounit : "_PROGRAM_";
b7e6e8
2ec747
	if (slbt_dprintf(fdout,
2ec747
			"/* dlsym vtable */\n"
2ec747
			"extern const struct lt_dlsym_symdef "
2ec747
			"lt_%s_LTX_preloaded_symbols[];\n\n"
2ec747
			"const struct lt_dlsym_symdef "
2ec747
			"lt_%s_LTX_preloaded_symbols[] = {\n",
b7e6e8
			soname,soname) < 0)
2ec747
		return SLBT_NESTED_ERROR(dctx);
2ec747
2ec747
	/* align dlsym_name and dlsym_addr columsn (because we can) */
2ec747
	for (parctx=arctxv,len=0; *parctx; parctx++) {
2ec747
		actx = *parctx;
2ec747
		ictx = slbt_get_archive_ictx(actx);
2ec747
		mctx = slbt_archive_meta_ictx(ictx->meta);
2ec747
2ec747
		if ((arname = strrchr(*actx->path,'/')))
2ec747
			arname++;
2ec747
2ec747
		if (!arname)
2ec747
			arname = *actx->path;
2ec747
2ec747
		if (len < (cmp = strlen(arname)))
2ec747
			len = cmp;
2ec747
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'A');
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'B');
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'C');
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'D');
2ec747
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'G');
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'I');
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'R');
2ec747
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'S');
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'T');
2ec747
		len  = slbt_ar_dlsyms_get_max_len_by_type(len,mctx,'W');
2ec747
	}
2ec747
2ec747
	/* quote, comma */
2ec747
	len += 2;
2ec747
2ec747
	if (len >= sizeof(symname))
2ec747
		return SLBT_CUSTOM_ERROR(
2ec747
			dctx,
2ec747
			SLBT_ERR_FLOW_ERROR);
2ec747
2ec747
	/* aligned print format */
2ec747
	snprintf(dlsymfmt,sizeof(dlsymfmt),"\t{\"%%-%ds %%s%%s},\n",len);
2ec747
2ec747
	/* dso unit */
2ec747
	if (slbt_snprintf(symname,sizeof(symname),"%s\",",dsounit) < 0)
2ec747
		return SLBT_SYSTEM_ERROR(dctx,0);
2ec747
2ec747
	if (slbt_dprintf(fdout,dlsymfmt,symname,"","0") < 0)
2ec747
		return SLBT_NESTED_ERROR(dctx);
2ec747
deae20
	/* (-dlopen force) */
deae20
	if (!arctxv[0]->meta->a_memberv)
deae20
		if (!strcmp(*arctxv[0]->path,"@PROGRAM@"))
deae20
			arctxv++;
deae20
2ec747
	/* at long last */
2ec747
	for (parctx=arctxv; *parctx; parctx++) {
2ec747
		actx = *parctx;
2ec747
		ictx = slbt_get_archive_ictx(actx);
2ec747
		mctx = slbt_archive_meta_ictx(ictx->meta);
2ec747
2ec747
		if ((arname = strrchr(*actx->path,'/')))
2ec747
			arname++;
2ec747
2ec747
		if (!arname)
2ec747
			arname = *actx->path;
2ec747
2ec747
		if (slbt_dprintf(fdout,"\n") < 0)
2ec747
			return SLBT_NESTED_ERROR(mctx->dctx);
2ec747
2ec747
		if (slbt_snprintf(symname,sizeof(symname),"%s\",",arname) < 0)
2ec747
			return SLBT_SYSTEM_ERROR(mctx->dctx,0);
2ec747
2ec747
		if (slbt_dprintf(fdout,dlsymfmt,symname,"","0") < 0)
2ec747
			return SLBT_NESTED_ERROR(mctx->dctx);
2ec747
2ec747
		ret  = slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'A',&symname);
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'B',&symname);
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'C',&symname);
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'D',&symname);
2ec747
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'G',&symname);
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'I',&symname);
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'R',&symname);
2ec747
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'S',&symname);
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'S',&symname);
2ec747
		ret |= slbt_ar_dlsyms_add_by_type(fdout,mctx,dlsymfmt,'T',&symname);
2ec747
2ec747
		if (ret < 0)
2ec747
			return SLBT_NESTED_ERROR(dctx);
2ec747
	}
2ec747
25a7d0
	/* null-terminate the vtable */
25a7d0
	if (slbt_dprintf(fdout,"\n\t{%d,%*c%d}\n",0,len,' ',0) < 0)
25a7d0
		return SLBT_NESTED_ERROR(mctx->dctx);
25a7d0
2ec747
	/* close vtable, wrap translation unit */
2ec747
	if (slbt_dprintf(fdout,
2ec747
			"};\n\n"
2ec747
			"#ifdef __cplusplus\n"
2ec747
			"}\n"
2ec747
			"#endif\n") < 0)
2ec747
		return SLBT_SYSTEM_ERROR(dctx,0);
2ec747
2ec747
	return 0;
2ec747
}
2ec747
2ec747
2ec747
static int slbt_ar_create_dlsyms_impl(
2ec747
	struct slbt_archive_ctx **        arctxv,
2ec747
	const char *                      dlunit,
2ec747
	const char *                      path,
2ec747
	mode_t                            mode)
2ec747
{
2ec747
	int                               ret;
6f477a
	struct slbt_archive_ctx **        actx;
6f477a
	struct slbt_exec_ctx *            ectx;
2ec747
	struct slbt_archive_meta_impl *   mctx;
2ec747
	const struct slbt_driver_ctx *    dctx;
2ec747
	struct slbt_fd_ctx                fdctx;
2ec747
	int                               fdout;
2ec747
2ec747
	mctx = slbt_archive_meta_ictx(arctxv[0]->meta);
2ec747
	dctx = mctx->dctx;
6f477a
	ectx = 0;
2ec747
2ec747
	if (slbt_lib_get_driver_fdctx(dctx,&fdctx) < 0)
2ec747
		return SLBT_NESTED_ERROR(dctx);
2ec747
2ec747
	if (path) {
2ec747
		if ((fdout = openat(
2ec747
				fdctx.fdcwd,path,
2ec747
				O_WRONLY|O_CREAT|O_TRUNC,
2ec747
				mode)) < 0)
926d54
			return SLBT_SYSTEM_ERROR(dctx,path);
2ec747
	} else {
2ec747
		fdout = fdctx.fdout;
2ec747
	}
2ec747
6f477a
	for (actx=arctxv; *actx; actx++) {
6f477a
		mctx = slbt_archive_meta_ictx((*actx)->meta);
6f477a
6f477a
		if (!mctx->syminfo && !ectx)
6f477a
			if (slbt_ectx_get_exec_ctx(dctx,&ectx) < 0)
6f477a
				return SLBT_NESTED_ERROR(dctx);
6f477a
6f477a
		if (!mctx->syminfo)
6f477a
			if (slbt_ar_update_syminfo(*actx,ectx) < 0)
6f477a
				return SLBT_NESTED_ERROR(dctx);
6f477a
	}
6f477a
6f477a
	if (ectx)
6f477a
		slbt_ectx_free_exec_ctx(ectx);
6f477a
2ec747
	ret = slbt_ar_output_dlsyms_impl(
2ec747
		fdout,dctx,arctxv,dlunit);
2ec747
2ec747
	if (path) {
2ec747
		close(fdout);
2ec747
	}
2ec747
2ec747
	return ret;
2ec747
}
2ec747
2ec747
2ec747
int slbt_ar_create_dlsyms(
2ec747
	struct slbt_archive_ctx **  arctxv,
2ec747
	const char *                dlunit,
2ec747
	const char *                path,
2ec747
	mode_t                      mode)
2ec747
{
2ec747
	return slbt_ar_create_dlsyms_impl(arctxv,dlunit,path,mode);
2ec747
}