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