6l: dynamic linking with symbol versioning
Teach the linker to consume ET_DYN shared objects and emit a dynamically-linked ELF executable. Snake et al. can now link against libncurses + libc through the system dynamic loader. Pipeline additions: - dyn.c: read ET_DYN, parse .dynsym + DT_SONAME, walk .gnu.version_d / .gnu.version to learn each export's default version (skip hidden entries). - pass.c: when an undefined sym is provided by some Lso, promote it to dynamic, assign a PLT slot, record the matched version on the Lsym. - dynout.c: emit PT_INTERP + PT_DYNAMIC, .dynsym/.dynstr/.hash, .plt + .got.plt + .rela.plt, .gnu.version + .gnu.version_r, and the full DT_* set with DT_BIND_NOW. Patch PC32/PLT32 references against dyn syms to point at their PLT stubs. - main.c: -L<dir> and -l<name> flag parsing; resolve <name> via .so / .so.<N> / .a in libdir order, skipping GNU ld linker scripts (libc.so on most distros). - ww driver: collect -l/-L (joined and split forms) and pass through to 6l. Design choices: - DT_BIND_NOW so the loader resolves all PLT slots at startup; no PLT0 lazy resolver stub. - SysV .hash, not .gnu.hash. One bucket; loader scans the chain. Slow at scale, fine for snake-class binaries. - Non-PIE at fixed 0x400000. - No section headers — loader uses program headers, but readelf -V/-S won't display anything. Symbol versioning is the only correctness item beyond the basic PLT/GOT machinery: glibc symbols default to versions later than GLIBC_2.2.5 (e.g. clock_gettime → GLIBC_2.17 for the vDSO impl), and the loader rejects unversioned references to those without a matching Vernaux entry. test/wwc/810_dyn covers four cases: bare libc dyn call, multi-PLT, clock_gettime versioning, and fn-pointer to FFI binding (which exercises the codegen fixes from the parent commit alongside the new linker path).
This commit is contained in:
@@ -156,7 +156,8 @@ expand(FILE *out, const char *path, struct ImportSet *visited,
|
||||
}
|
||||
|
||||
static int
|
||||
build_one(const char *src, const char *out, const char *extra_includes)
|
||||
build_one(const char *src, const char *out, const char *extra_includes,
|
||||
const char *extra_libs, const char *extra_libdirs)
|
||||
{
|
||||
const char *c6 = toolpath("WW_6C", "6c");
|
||||
const char *a6 = toolpath("WW_6A", "6a");
|
||||
@@ -237,7 +238,13 @@ build_one(const char *src, const char *out, const char *extra_includes)
|
||||
snprintf(a2, sizeof a2, "%s/../obj/rt/syscall.o", self_dir);
|
||||
snprintf(rtargs, sizeof rtargs, "%s %s", a1, a2);
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s %s", l6, out, obj, rtargs);
|
||||
/* -L<dir> goes before -l<name> so 6l can resolve the latter. */
|
||||
const char *libargs = (extra_libs && extra_libs[0]) ? extra_libs : "";
|
||||
const char *libdirset = (extra_libdirs && extra_libdirs[0]) ? extra_libdirs : "";
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s %s%s%s%s%s",
|
||||
l6, out, obj, rtargs,
|
||||
libdirset[0] ? " " : "", libdirset,
|
||||
libargs[0] ? " " : "", libargs);
|
||||
if (run(cmd) != 0) {
|
||||
fprintf(stderr, "ww: 6l failed\n");
|
||||
return 1;
|
||||
@@ -256,21 +263,26 @@ static int
|
||||
do_build(int argc, char **argv)
|
||||
{
|
||||
const char *src = NULL;
|
||||
char libs[2048] = {0};
|
||||
char incs[2048] = {0};
|
||||
char libs[2048] = {0}; /* -l<name> entries, space-separated */
|
||||
char libdirs[2048] = {0}; /* -L<dir> entries, space-separated */
|
||||
char incs[2048] = {0};
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strncmp(argv[i], "-l", 2) == 0 && argv[i][2]) {
|
||||
char libpath[512];
|
||||
const char *libdir = getenv("WW_LIB");
|
||||
if (libdir == NULL) {
|
||||
static char def[1024];
|
||||
snprintf(def, sizeof def, "%s/../lib", self_dir);
|
||||
libdir = def;
|
||||
}
|
||||
snprintf(libpath, sizeof libpath, "%s/lib%s.a",
|
||||
libdir, argv[i] + 2);
|
||||
size_t n = strlen(libs);
|
||||
snprintf(libs + n, sizeof libs - n, " %s", libpath);
|
||||
snprintf(libs + n, sizeof libs - n,
|
||||
"%s%s", n ? " " : "", argv[i]);
|
||||
} else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
|
||||
size_t n = strlen(libs);
|
||||
snprintf(libs + n, sizeof libs - n,
|
||||
"%s-l%s", n ? " " : "", argv[++i]);
|
||||
} else if (strcmp(argv[i], "-L") == 0 && i + 1 < argc) {
|
||||
size_t n = strlen(libdirs);
|
||||
snprintf(libdirs + n, sizeof libdirs - n,
|
||||
"%s-L%s", n ? " " : "", argv[++i]);
|
||||
} else if (strncmp(argv[i], "-L", 2) == 0 && argv[i][2]) {
|
||||
size_t n = strlen(libdirs);
|
||||
snprintf(libdirs + n, sizeof libdirs - n,
|
||||
"%s%s", n ? " " : "", argv[i]);
|
||||
} else if (strcmp(argv[i], "-I") == 0 && i + 1 < argc) {
|
||||
size_t n = strlen(incs);
|
||||
snprintf(incs + n, sizeof incs - n,
|
||||
@@ -290,10 +302,7 @@ do_build(int argc, char **argv)
|
||||
snprintf(out, sizeof out, "%s", base);
|
||||
char *dot = strrchr(out, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
(void)libs; /* libs string is gathered; build_one currently
|
||||
* always links libwwrt.a; -l support pending more
|
||||
* glue between driver and 6l invocation. */
|
||||
return build_one(src, out, incs);
|
||||
return build_one(src, out, incs, libs, libdirs);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -302,7 +311,7 @@ do_run(int argc, char **argv)
|
||||
if (argc < 1) { fputs("ww run: missing source\n", stderr); return 2; }
|
||||
char tmp[1024];
|
||||
snprintf(tmp, sizeof tmp, "/tmp/ww_run_%d", getpid());
|
||||
if (build_one(argv[0], tmp, "") != 0) return 1;
|
||||
if (build_one(argv[0], tmp, "", "", "") != 0) return 1;
|
||||
int rc = run(tmp);
|
||||
unlink(tmp);
|
||||
return rc;
|
||||
|
||||
Reference in New Issue
Block a user