/* * w6l -o out [-L...] [-l...] file1.o file2.o ... * * The first symbol named "_start" defined among the inputs becomes * the entry point. If none is found, fall back to "main". */ #include "l.h" #include #include #include #include #include /* `path` is acceptable iff it's either an archive ("!\n") or * an ELF file ("\x7fELF"). Distros often ship lib.so as a GNU * ld linker script (plain text); we skip those rather than parse the * GROUP/INPUT directives. */ static int is_linkable(const char *path) { FILE *f = fopen(path, "rb"); if (f == NULL) return 0; u8 magic[8] = {0}; size_t n = fread(magic, 1, 8, f); fclose(f); if (n >= 8 && memcmp(magic, "!\n", 8) == 0) return 1; if (n >= 4 && memcmp(magic, "\x7f""ELF", 4) == 0) return 1; return 0; } /* Resolve -l to a filesystem path by walking the libdirs we * collected. Order: lib.so → lib.so. globs → lib.a. * Skip anything that isn't a real archive or ELF (e.g. ld scripts). */ static const char * resolve_lib(const char *name, char **libdirs, int n_libdirs) { static char buf[1024]; for (int i = 0; i < n_libdirs; i++) { snprintf(buf, sizeof buf, "%s/lib%s.so", libdirs[i], name); if (access(buf, 0) == 0 && is_linkable(buf)) return strdup(buf); for (int v = 0; v <= 8; v++) { snprintf(buf, sizeof buf, "%s/lib%s.so.%d", libdirs[i], name, v); if (access(buf, 0) == 0 && is_linkable(buf)) return strdup(buf); } snprintf(buf, sizeof buf, "%s/lib%s.a", libdirs[i], name); if (access(buf, 0) == 0 && is_linkable(buf)) return strdup(buf); } return NULL; } int main(int argc, char **argv) { const char *out = NULL; const char **inputs = calloc(argc, sizeof *inputs); int ninputs = 0; char **libdirs = calloc(argc, sizeof *libdirs); int n_libdirs = 0; const char **lflags = calloc(argc, sizeof *lflags); int n_lflags = 0; u64 base = 0x400000; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { out = argv[++i]; } else if (strncmp(argv[i], "-L", 2) == 0 && argv[i][2]) { libdirs[n_libdirs++] = strdup(argv[i] + 2); } else if (strcmp(argv[i], "-L") == 0 && i + 1 < argc) { libdirs[n_libdirs++] = strdup(argv[++i]); } else if (strncmp(argv[i], "-l", 2) == 0 && argv[i][2]) { lflags[n_lflags++] = argv[i] + 2; } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { lflags[n_lflags++] = argv[++i]; } else if (argv[i][0] == '-') { fprintf(stderr, "w6l: unknown flag %s\n", argv[i]); return 2; } else { inputs[ninputs++] = argv[i]; } } if (out == NULL || ninputs == 0) { fputs("usage: w6l -o exe [-L...] [-l...] " "file1.o [file2.o...]\n", stderr); return 2; } /* Append -l-resolved files to the input list, after the .o/.a the * caller passed positionally. They obey the same archive-pull * semantics as a positional .a. */ for (int i = 0; i < n_lflags; i++) { const char *p = resolve_lib(lflags[i], libdirs, n_libdirs); if (p == NULL) { fprintf(stderr, "w6l: cannot find -l%s\n", lflags[i]); return 1; } inputs[ninputs++] = p; } Lnk l = {0}; /* Seed both accepted entry symbols before archive loading. The root * package archive then participates in the same selective member-pull * protocol as every dependency; libwwrt.a still supplies _start. */ (void)l_intern(&l, "_start"); (void)l_intern(&l, "main"); for (int i = 0; i < ninputs; i++) { if (l_load(&l, inputs[i]) != 0) return 1; } if (l_resolve(&l) != 0) return 1; /* Relocation is deferred to the emit functions — each path knows * its own layout (text_va, data_va), and the static and dynamic * paths place .data at different VAs. */ Lsym *entry = l_lookup(&l, "_start"); if (entry == NULL || !entry->defined) entry = l_lookup(&l, "main"); if (entry == NULL || !entry->defined) { fprintf(stderr, "w6l: no _start or main symbol defined\n"); return 1; } /* BuildInstallFunc creates ordinary linked commands with 0777 and lets the * caller's umask select the installed mode. Public ww builds link into a * fresh transaction stage, whose inode is later renamed to the output. */ int fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0777); if (fd < 0) { fprintf(stderr, "w6l: cannot open %s\n", out); return 1; } FILE *f = fdopen(fd, "wb"); if (f == NULL) { close(fd); (void)unlink(out); fprintf(stderr, "w6l: cannot open %s\n", out); return 1; } int rc = l_emit_elf(&l, f, base, base + 0x1000 + entry->val); fclose(f); free(inputs); return rc; }