6l: port ET_DYN dynamic linking to the ww side
Ports cmd/6l/{dyn,dynout}.c into selfhost/cmd/6l/{dyn,dynout}.ww:
ET_DYN .so loading + PT_INTERP/PT_DYNAMIC ELF emission with .rela.plt,
.gnu.version_r, BIND_NOW. lsym grows dyn fields; pass.ww promotes
undefs to dyn; out.ww dispatches; main.ww takes -L/-l. The ww driver
forwards -L/-l to 6l_ww so 'ww_ww build snake.ww -L /usr/lib -l ncurses
-l c' runs without cc.
Test 996 pins byte-identical output to C-6l on snake.
'make bootstrap' gains a fourth stage with cmp ww3 == ww4, proving
ww3 is byte-stable when used as a compiler — not just a coincidental
two-stage equilibrium.
Four wwstage 6c cgen quirks surfaced and are documented in dynout.ww's
header (two-level field-write through a pointer field, (scalar, str)
tuple returns, def : str, ≤6 arg calling convention).
This commit is contained in:
@@ -695,18 +695,28 @@ fn append_lit(stem: *u8, suffix: str) *u8 = {
|
||||
return buf;
|
||||
};
|
||||
|
||||
// linker flags bundled as a struct so build_one stays at the wwstage
|
||||
// 6c's 6-argument calling-convention limit.
|
||||
type lflags = struct {
|
||||
libdirs: **u8,
|
||||
n_libdirs: i32,
|
||||
libs: **u8,
|
||||
n_libs: i32,
|
||||
};
|
||||
|
||||
// build_one — compile `src` into the executable named `out`.
|
||||
// self_dir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (6c_ww/6a_ww/6l_ww)
|
||||
// src: NUL-terminated path to the .ww file
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// self_dir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (6c_ww/6a_ww/6l_ww)
|
||||
// src: NUL-terminated path to the .ww file
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||
//
|
||||
// The ww-side driver shells to the ww-side tools so a `ww_ww build`
|
||||
// touches no C-built code at runtime. The C `ww` driver in cmd/ww/
|
||||
// still drives the C-built 6c/6a/6l. Test 993 pins the two
|
||||
// pipelines to byte-identical output on a corpus.
|
||||
fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8) i32 = {
|
||||
fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
let a: *arena = newarena();
|
||||
|
||||
let c6: *u8 = join_path_lit(self_dir, "6c_ww");
|
||||
@@ -792,15 +802,45 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8) i32 = {
|
||||
};
|
||||
};
|
||||
|
||||
// Step 4: 6l -o <out> <stem>.o libwwrt.a
|
||||
// Step 4: 6l -o <out> <stem>.o libwwrt.a [-L<dir>...] [-l<name>...]
|
||||
{
|
||||
let argv: **u8 = os.alloc(48u64): **u8;
|
||||
let nl_dirs: i32 = 0;
|
||||
let nl_libs: i32 = 0;
|
||||
let l_dirs: **u8 = nil;
|
||||
let l_libs: **u8 = nil;
|
||||
if (lf != nil) {
|
||||
nl_dirs = lf.n_libdirs;
|
||||
nl_libs = lf.n_libs;
|
||||
l_dirs = lf.libdirs;
|
||||
l_libs = lf.libs;
|
||||
};
|
||||
// argv slots: 5 fixed (6l, -o, out, objf, libwwrt)
|
||||
// + 2 * n_libdirs (-L, dir)
|
||||
// + 2 * n_libs (-l, name)
|
||||
// + 1 nil terminator.
|
||||
let total: i32 = 5 + 2 * nl_dirs + 2 * nl_libs + 1;
|
||||
let argv: **u8 = os.alloc((total: u64) * 8u64): **u8;
|
||||
argv[0] = "6l\0".ptr;
|
||||
argv[1] = "-o\0".ptr;
|
||||
argv[2] = out;
|
||||
argv[3] = objf;
|
||||
argv[4] = libwwrt;
|
||||
argv[5] = nil;
|
||||
let pos: i32 = 5;
|
||||
let k: i32 = 0;
|
||||
for (k < nl_dirs) {
|
||||
argv[pos] = "-L\0".ptr;
|
||||
argv[pos + 1] = l_dirs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
k = 0;
|
||||
for (k < nl_libs) {
|
||||
argv[pos] = "-l\0".ptr;
|
||||
argv[pos + 1] = l_libs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
argv[pos] = nil;
|
||||
if (proc_run(l6, argv) != 0) {
|
||||
os.write(2, "ww: 6l failed\n".ptr, 14u64);
|
||||
return 1;
|
||||
@@ -859,11 +899,16 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let inc_off: u64 = 0u64;
|
||||
cstr_seal(incs, 0u64);
|
||||
|
||||
let max_lflags: i32 = 32;
|
||||
let libdirs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8;
|
||||
let n_libdirs: i32 = 0;
|
||||
let libs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8;
|
||||
let n_libs: i32 = 0;
|
||||
|
||||
let i: i32 = start;
|
||||
for (i < argc) {
|
||||
let p: *u8 = argv[i];
|
||||
// -I <dir>
|
||||
if (p[0u64] == 45u8) {
|
||||
if (p[0u64] == 45u8) { // '-'
|
||||
if (p[1u64] == 73u8) { // '-I'
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
@@ -882,14 +927,46 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
};
|
||||
inc_off = cstr_into(incs, inc_off, dir);
|
||||
cstr_seal(incs, inc_off);
|
||||
} else {
|
||||
// -lLIB silently ignored for now (driver doesn't yet
|
||||
// pass extra archives to 6l).
|
||||
if (p[1u64] != 108u8) {
|
||||
os.write(2, "ww build: unknown flag\n".ptr, 23u64);
|
||||
} else { if (p[1u64] == 76u8) { // '-L'
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
dir = p + 2u64;
|
||||
} else {
|
||||
if (i + 1 >= argc) {
|
||||
os.write(2, "ww build: -L needs an argument\n".ptr, 31u64);
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
dir = argv[i];
|
||||
};
|
||||
if (n_libdirs >= max_lflags) {
|
||||
os.write(2, "ww build: too many -L\n".ptr, 22u64);
|
||||
return 2;
|
||||
};
|
||||
};
|
||||
libdirs[n_libdirs] = dir;
|
||||
n_libdirs += 1;
|
||||
} else { if (p[1u64] == 108u8) { // '-l'
|
||||
let nm: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
nm = p + 2u64;
|
||||
} else {
|
||||
if (i + 1 >= argc) {
|
||||
os.write(2, "ww build: -l needs an argument\n".ptr, 31u64);
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
nm = argv[i];
|
||||
};
|
||||
if (n_libs >= max_lflags) {
|
||||
os.write(2, "ww build: too many -l\n".ptr, 22u64);
|
||||
return 2;
|
||||
};
|
||||
libs[n_libs] = nm;
|
||||
n_libs += 1;
|
||||
} else {
|
||||
os.write(2, "ww build: unknown flag\n".ptr, 23u64);
|
||||
return 2;
|
||||
}; }; };
|
||||
} else {
|
||||
if (src == nil) { src = p; };
|
||||
};
|
||||
@@ -901,7 +978,12 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
return 2;
|
||||
};
|
||||
let out: *u8 = default_out_path(src);
|
||||
return build_one(self_dir, src, out, incs);
|
||||
let lf: lflags;
|
||||
lf.libdirs = libdirs;
|
||||
lf.n_libdirs = n_libdirs;
|
||||
lf.libs = libs;
|
||||
lf.n_libs = n_libs;
|
||||
return build_one(self_dir, src, out, incs, &lf);
|
||||
};
|
||||
|
||||
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
||||
@@ -941,7 +1023,7 @@ fn do_run(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
};
|
||||
let tmp: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
make_run_tmp(tmp);
|
||||
if (build_one(self_dir, argv[start], tmp, "\0".ptr) != 0) { return 1; };
|
||||
if (build_one(self_dir, argv[start], tmp, "\0".ptr, nil) != 0) { return 1; };
|
||||
let exec_argv: **u8 = os.alloc(16u64): **u8;
|
||||
exec_argv[0] = tmp;
|
||||
exec_argv[1] = nil;
|
||||
|
||||
@@ -407,18 +407,28 @@ fn append_lit(stem: *u8, suffix: str) *u8 = {
|
||||
return buf;
|
||||
};
|
||||
|
||||
// linker flags bundled as a struct so build_one stays at the wwstage
|
||||
// 6c's 6-argument calling-convention limit.
|
||||
type lflags = struct {
|
||||
libdirs: **u8,
|
||||
n_libdirs: i32,
|
||||
libs: **u8,
|
||||
n_libs: i32,
|
||||
};
|
||||
|
||||
// build_one — compile `src` into the executable named `out`.
|
||||
// self_dir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (6c_ww/6a_ww/6l_ww)
|
||||
// src: NUL-terminated path to the .ww file
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// self_dir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (6c_ww/6a_ww/6l_ww)
|
||||
// src: NUL-terminated path to the .ww file
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||
//
|
||||
// The ww-side driver shells to the ww-side tools so a `ww_ww build`
|
||||
// touches no C-built code at runtime. The C `ww` driver in cmd/ww/
|
||||
// still drives the C-built 6c/6a/6l. Test 993 pins the two
|
||||
// pipelines to byte-identical output on a corpus.
|
||||
fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8) i32 = {
|
||||
fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
let a: *arena = newarena();
|
||||
|
||||
let c6: *u8 = join_path_lit(self_dir, "6c_ww");
|
||||
@@ -504,15 +514,45 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8) i32 = {
|
||||
};
|
||||
};
|
||||
|
||||
// Step 4: 6l -o <out> <stem>.o libwwrt.a
|
||||
// Step 4: 6l -o <out> <stem>.o libwwrt.a [-L<dir>...] [-l<name>...]
|
||||
{
|
||||
let argv: **u8 = os.alloc(48u64): **u8;
|
||||
let nl_dirs: i32 = 0;
|
||||
let nl_libs: i32 = 0;
|
||||
let l_dirs: **u8 = nil;
|
||||
let l_libs: **u8 = nil;
|
||||
if (lf != nil) {
|
||||
nl_dirs = lf.n_libdirs;
|
||||
nl_libs = lf.n_libs;
|
||||
l_dirs = lf.libdirs;
|
||||
l_libs = lf.libs;
|
||||
};
|
||||
// argv slots: 5 fixed (6l, -o, out, objf, libwwrt)
|
||||
// + 2 * n_libdirs (-L, dir)
|
||||
// + 2 * n_libs (-l, name)
|
||||
// + 1 nil terminator.
|
||||
let total: i32 = 5 + 2 * nl_dirs + 2 * nl_libs + 1;
|
||||
let argv: **u8 = os.alloc((total: u64) * 8u64): **u8;
|
||||
argv[0] = "6l\0".ptr;
|
||||
argv[1] = "-o\0".ptr;
|
||||
argv[2] = out;
|
||||
argv[3] = objf;
|
||||
argv[4] = libwwrt;
|
||||
argv[5] = nil;
|
||||
let pos: i32 = 5;
|
||||
let k: i32 = 0;
|
||||
for (k < nl_dirs) {
|
||||
argv[pos] = "-L\0".ptr;
|
||||
argv[pos + 1] = l_dirs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
k = 0;
|
||||
for (k < nl_libs) {
|
||||
argv[pos] = "-l\0".ptr;
|
||||
argv[pos + 1] = l_libs[k];
|
||||
pos += 2;
|
||||
k += 1;
|
||||
};
|
||||
argv[pos] = nil;
|
||||
if (proc_run(l6, argv) != 0) {
|
||||
os.write(2, "ww: 6l failed\n".ptr, 14u64);
|
||||
return 1;
|
||||
@@ -571,11 +611,16 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let inc_off: u64 = 0u64;
|
||||
cstr_seal(incs, 0u64);
|
||||
|
||||
let max_lflags: i32 = 32;
|
||||
let libdirs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8;
|
||||
let n_libdirs: i32 = 0;
|
||||
let libs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8;
|
||||
let n_libs: i32 = 0;
|
||||
|
||||
let i: i32 = start;
|
||||
for (i < argc) {
|
||||
let p: *u8 = argv[i];
|
||||
// -I <dir>
|
||||
if (p[0u64] == 45u8) {
|
||||
if (p[0u64] == 45u8) { // '-'
|
||||
if (p[1u64] == 73u8) { // '-I'
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
@@ -594,14 +639,46 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
};
|
||||
inc_off = cstr_into(incs, inc_off, dir);
|
||||
cstr_seal(incs, inc_off);
|
||||
} else {
|
||||
// -lLIB silently ignored for now (driver doesn't yet
|
||||
// pass extra archives to 6l).
|
||||
if (p[1u64] != 108u8) {
|
||||
os.write(2, "ww build: unknown flag\n".ptr, 23u64);
|
||||
} else { if (p[1u64] == 76u8) { // '-L'
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
dir = p + 2u64;
|
||||
} else {
|
||||
if (i + 1 >= argc) {
|
||||
os.write(2, "ww build: -L needs an argument\n".ptr, 31u64);
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
dir = argv[i];
|
||||
};
|
||||
if (n_libdirs >= max_lflags) {
|
||||
os.write(2, "ww build: too many -L\n".ptr, 22u64);
|
||||
return 2;
|
||||
};
|
||||
};
|
||||
libdirs[n_libdirs] = dir;
|
||||
n_libdirs += 1;
|
||||
} else { if (p[1u64] == 108u8) { // '-l'
|
||||
let nm: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
nm = p + 2u64;
|
||||
} else {
|
||||
if (i + 1 >= argc) {
|
||||
os.write(2, "ww build: -l needs an argument\n".ptr, 31u64);
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
nm = argv[i];
|
||||
};
|
||||
if (n_libs >= max_lflags) {
|
||||
os.write(2, "ww build: too many -l\n".ptr, 22u64);
|
||||
return 2;
|
||||
};
|
||||
libs[n_libs] = nm;
|
||||
n_libs += 1;
|
||||
} else {
|
||||
os.write(2, "ww build: unknown flag\n".ptr, 23u64);
|
||||
return 2;
|
||||
}; }; };
|
||||
} else {
|
||||
if (src == nil) { src = p; };
|
||||
};
|
||||
@@ -613,7 +690,12 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
return 2;
|
||||
};
|
||||
let out: *u8 = default_out_path(src);
|
||||
return build_one(self_dir, src, out, incs);
|
||||
let lf: lflags;
|
||||
lf.libdirs = libdirs;
|
||||
lf.n_libdirs = n_libdirs;
|
||||
lf.libs = libs;
|
||||
lf.n_libs = n_libs;
|
||||
return build_one(self_dir, src, out, incs, &lf);
|
||||
};
|
||||
|
||||
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
||||
@@ -653,7 +735,7 @@ fn do_run(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
};
|
||||
let tmp: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
make_run_tmp(tmp);
|
||||
if (build_one(self_dir, argv[start], tmp, "\0".ptr) != 0) { return 1; };
|
||||
if (build_one(self_dir, argv[start], tmp, "\0".ptr, nil) != 0) { return 1; };
|
||||
let exec_argv: **u8 = os.alloc(16u64): **u8;
|
||||
exec_argv[0] = tmp;
|
||||
exec_argv[1] = nil;
|
||||
|
||||
Reference in New Issue
Block a user