w6l+selfhost: dynamic-link + .data — shared R+W segment
.data now lives at the end of the dyn-path R+W PT_LOAD, just after .dynamic. The single segment covers .got.plt + .dynamic + .data; its filesz drops trailing zeros (BSS) while memsz spans the full extent. Relocation moves from main.c into each emit function so the static and dynamic paths use their own data_va — text→data refs land on the right VA regardless of path. Removes the early-error in dynout.c that previously refused any .data with -l/-L. Tests: 810_dyn gains two new dyn+.data fixtures (mutable read+write of an i32, plus a zero-init i64 verifying the BSS scan still produces a valid p_filesz<p_memsz under the shared segment).
This commit is contained in:
@@ -163,14 +163,10 @@ fn streqd(a: str, b: str) bool = {
|
||||
// ---- main entry --------------------------------------------------------
|
||||
|
||||
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
// Writable globals on the dynamic-link path need their own R+W
|
||||
// segment that the existing gotplt + .dynamic layout doesn't
|
||||
// account for. Refuse rather than silently miscompile; folding
|
||||
// .data into the existing R+W block is a follow-up.
|
||||
if (l.datalen > 0u64) {
|
||||
os.write(2, "w6l: top-level mutable globals (.data) not yet supported with -l/-L\n".ptr, 67u64);
|
||||
return 1;
|
||||
};
|
||||
// .data shares the R+W PT_LOAD with .got.plt and .dynamic.
|
||||
// Placed after .dynamic so the segment is one contiguous run;
|
||||
// relocate runs from here so the dyn layout's datava lands in
|
||||
// patched offsets.
|
||||
let a: *arena = l.a;
|
||||
let n: i32 = l.dynn;
|
||||
let nu: u64 = n: u64;
|
||||
@@ -466,7 +462,8 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
|
||||
let gotpltoff: u64 = alignup(rxend, PAGE);
|
||||
let dynamicoff: u64 = gotpltoff + gotpltsz;
|
||||
let fileend: u64 = dynamicoff + dynamicsz;
|
||||
let dataoff: u64 = dynamicoff + dynamicsz;
|
||||
let fileend: u64 = dataoff + l.datalen;
|
||||
|
||||
let interpva: u64 = base + interpoff;
|
||||
let dynstrva: u64 = base + dynstroff;
|
||||
@@ -479,6 +476,23 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let pltva: u64 = base + pltoff;
|
||||
let gotpltva: u64 = base + gotpltoff;
|
||||
let dynamicva: u64 = base + dynamicoff;
|
||||
let datava: u64 = base + dataoff;
|
||||
|
||||
// Apply relocations now that the dyn layout's textva/datava are
|
||||
// pinned. main.ww defers this so each path uses its own VAs.
|
||||
if (relocate(l, textva, datava) != 0) { return 1; };
|
||||
|
||||
// BSS optimisation — same trailing-zero scan as out.ww.
|
||||
let bsslen: u64 = 0u64;
|
||||
if (l.datalen > 0u64) {
|
||||
for (bsslen < l.datalen) {
|
||||
let b: u8 = l.data[l.datalen - 1u64 - bsslen];
|
||||
if (b != 0u8) { break; };
|
||||
bsslen += 1u64;
|
||||
};
|
||||
};
|
||||
let datafilelen: u64 = l.datalen - bsslen;
|
||||
let filedataend: u64 = dataoff + datafilelen;
|
||||
|
||||
// ---- build .dynsym ----
|
||||
let dynsymbuf: *u8 = amalloc(a, dynsymsz): *u8;
|
||||
@@ -686,7 +700,9 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
dwr64(filebuf, p1 + 8u64, gotpltoff);
|
||||
dwr64(filebuf, p1 + 16u64, gotpltva);
|
||||
dwr64(filebuf, p1 + 24u64, gotpltva);
|
||||
dwr64(filebuf, p1 + 32u64, fileend - gotpltoff);
|
||||
// filesz trims the .data trailing zeros (BSS); memsz covers
|
||||
// .got.plt + .dynamic + the full .data so the loader zero-fills.
|
||||
dwr64(filebuf, p1 + 32u64, filedataend - gotpltoff);
|
||||
dwr64(filebuf, p1 + 40u64, fileend - gotpltoff);
|
||||
dwr64(filebuf, p1 + 48u64, PAGE);
|
||||
|
||||
@@ -727,10 +743,13 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
dbcopy(filebuf, pltoff, pltbuf, pltsz);
|
||||
dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz);
|
||||
dbcopy(filebuf, dynamicoff, dynamicbuf, dynamicsz);
|
||||
if (datafilelen > 0u64) {
|
||||
dbcopy(filebuf, dataoff, l.data, datafilelen);
|
||||
};
|
||||
|
||||
let wr: (i64 | os.oserror) = os.writeall(fd, filebuf, fileend);
|
||||
let wr: (i64 | os.oserror) = os.writeall(fd, filebuf, filedataend);
|
||||
match (wr) {
|
||||
case let v: i64 => { if (v != fileend: i64) { return 1; }; };
|
||||
case let v: i64 => { if (v != filedataend: i64) { return 1; }; };
|
||||
case let e: os.oserror => return 1;
|
||||
};
|
||||
return 0;
|
||||
|
||||
@@ -1759,14 +1759,10 @@ fn streqd(a: str, b: str) bool = {
|
||||
// ---- main entry --------------------------------------------------------
|
||||
|
||||
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
// Writable globals on the dynamic-link path need their own R+W
|
||||
// segment that the existing gotplt + .dynamic layout doesn't
|
||||
// account for. Refuse rather than silently miscompile; folding
|
||||
// .data into the existing R+W block is a follow-up.
|
||||
if (l.datalen > 0u64) {
|
||||
os.write(2, "w6l: top-level mutable globals (.data) not yet supported with -l/-L\n".ptr, 67u64);
|
||||
return 1;
|
||||
};
|
||||
// .data shares the R+W PT_LOAD with .got.plt and .dynamic.
|
||||
// Placed after .dynamic so the segment is one contiguous run;
|
||||
// relocate runs from here so the dyn layout's datava lands in
|
||||
// patched offsets.
|
||||
let a: *arena = l.a;
|
||||
let n: i32 = l.dynn;
|
||||
let nu: u64 = n: u64;
|
||||
@@ -2062,7 +2058,8 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
|
||||
let gotpltoff: u64 = alignup(rxend, PAGE);
|
||||
let dynamicoff: u64 = gotpltoff + gotpltsz;
|
||||
let fileend: u64 = dynamicoff + dynamicsz;
|
||||
let dataoff: u64 = dynamicoff + dynamicsz;
|
||||
let fileend: u64 = dataoff + l.datalen;
|
||||
|
||||
let interpva: u64 = base + interpoff;
|
||||
let dynstrva: u64 = base + dynstroff;
|
||||
@@ -2075,6 +2072,23 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let pltva: u64 = base + pltoff;
|
||||
let gotpltva: u64 = base + gotpltoff;
|
||||
let dynamicva: u64 = base + dynamicoff;
|
||||
let datava: u64 = base + dataoff;
|
||||
|
||||
// Apply relocations now that the dyn layout's textva/datava are
|
||||
// pinned. main.ww defers this so each path uses its own VAs.
|
||||
if (relocate(l, textva, datava) != 0) { return 1; };
|
||||
|
||||
// BSS optimisation — same trailing-zero scan as out.ww.
|
||||
let bsslen: u64 = 0u64;
|
||||
if (l.datalen > 0u64) {
|
||||
for (bsslen < l.datalen) {
|
||||
let b: u8 = l.data[l.datalen - 1u64 - bsslen];
|
||||
if (b != 0u8) { break; };
|
||||
bsslen += 1u64;
|
||||
};
|
||||
};
|
||||
let datafilelen: u64 = l.datalen - bsslen;
|
||||
let filedataend: u64 = dataoff + datafilelen;
|
||||
|
||||
// ---- build .dynsym ----
|
||||
let dynsymbuf: *u8 = amalloc(a, dynsymsz): *u8;
|
||||
@@ -2282,7 +2296,9 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
dwr64(filebuf, p1 + 8u64, gotpltoff);
|
||||
dwr64(filebuf, p1 + 16u64, gotpltva);
|
||||
dwr64(filebuf, p1 + 24u64, gotpltva);
|
||||
dwr64(filebuf, p1 + 32u64, fileend - gotpltoff);
|
||||
// filesz trims the .data trailing zeros (BSS); memsz covers
|
||||
// .got.plt + .dynamic + the full .data so the loader zero-fills.
|
||||
dwr64(filebuf, p1 + 32u64, filedataend - gotpltoff);
|
||||
dwr64(filebuf, p1 + 40u64, fileend - gotpltoff);
|
||||
dwr64(filebuf, p1 + 48u64, PAGE);
|
||||
|
||||
@@ -2323,10 +2339,13 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
dbcopy(filebuf, pltoff, pltbuf, pltsz);
|
||||
dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz);
|
||||
dbcopy(filebuf, dynamicoff, dynamicbuf, dynamicsz);
|
||||
if (datafilelen > 0u64) {
|
||||
dbcopy(filebuf, dataoff, l.data, datafilelen);
|
||||
};
|
||||
|
||||
let wr: (i64 | os.oserror) = os.writeall(fd, filebuf, fileend);
|
||||
let wr: (i64 | os.oserror) = os.writeall(fd, filebuf, filedataend);
|
||||
match (wr) {
|
||||
case let v: i64 => { if (v != fileend: i64) { return 1; }; };
|
||||
case let v: i64 => { if (v != filedataend: i64) { return 1; }; };
|
||||
case let e: os.oserror => return 1;
|
||||
};
|
||||
return 0;
|
||||
@@ -2394,10 +2413,17 @@ export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
// .data lands at the next page boundary so the loader can give
|
||||
// it fresh R+W permissions without overlapping the R+X mapping.
|
||||
let dataoff: u64 = 0u64;
|
||||
let datava: u64 = 0u64;
|
||||
if (hasdata) {
|
||||
dataoff = (rxend + PAGE_SZ - 1u64) & ~(PAGE_SZ - 1u64);
|
||||
datava = base + dataoff;
|
||||
};
|
||||
|
||||
// Apply relocations now that the layout's textva/datava are
|
||||
// known. Deferred from main.ww so the dyn path uses its own
|
||||
// datava.
|
||||
if (relocate(l, base + TEXT_OFF, datava) != 0) { return -1; };
|
||||
|
||||
// BSS optimisation: trailing zero bytes in .data can be left
|
||||
// out of the file. The loader zero-fills the gap between
|
||||
// p_filesz and p_memsz. Scan after l_relocate has applied any
|
||||
@@ -2804,18 +2830,9 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
};
|
||||
|
||||
if (resolve(l) != 0) { return 1; };
|
||||
// Static layout for relocation purposes. .data lands at the
|
||||
// next page after .text; the dyn path overrides if its layout
|
||||
// is different (text→text PC32 cancels the absolute VA so
|
||||
// only the data delta matters here).
|
||||
let textva: u64 = BASE + CODE_VA_OFF;
|
||||
let datava: u64 = 0u64;
|
||||
if (l.datalen > 0u64) {
|
||||
let page: u64 = 4096u64;
|
||||
let dataoff: u64 = (CODE_VA_OFF + l.textlen + page - 1u64) & ~(page - 1u64);
|
||||
datava = BASE + dataoff;
|
||||
};
|
||||
if (relocate(l, textva, datava) != 0) { return 1; };
|
||||
// Relocation is deferred to the emit functions — each path
|
||||
// knows its own layout (textva, datava); the static and dyn
|
||||
// paths place .data at different VAs.
|
||||
|
||||
let entrysym: *lsym = lookup(l, "_start");
|
||||
if (entrysym == nil) { entrysym = lookup(l, "main"); }
|
||||
|
||||
@@ -298,18 +298,9 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
};
|
||||
|
||||
if (resolve(l) != 0) { return 1; };
|
||||
// Static layout for relocation purposes. .data lands at the
|
||||
// next page after .text; the dyn path overrides if its layout
|
||||
// is different (text→text PC32 cancels the absolute VA so
|
||||
// only the data delta matters here).
|
||||
let textva: u64 = BASE + CODE_VA_OFF;
|
||||
let datava: u64 = 0u64;
|
||||
if (l.datalen > 0u64) {
|
||||
let page: u64 = 4096u64;
|
||||
let dataoff: u64 = (CODE_VA_OFF + l.textlen + page - 1u64) & ~(page - 1u64);
|
||||
datava = BASE + dataoff;
|
||||
};
|
||||
if (relocate(l, textva, datava) != 0) { return 1; };
|
||||
// Relocation is deferred to the emit functions — each path
|
||||
// knows its own layout (textva, datava); the static and dyn
|
||||
// paths place .data at different VAs.
|
||||
|
||||
let entrysym: *lsym = lookup(l, "_start");
|
||||
if (entrysym == nil) { entrysym = lookup(l, "main"); }
|
||||
|
||||
@@ -59,10 +59,17 @@ export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
// .data lands at the next page boundary so the loader can give
|
||||
// it fresh R+W permissions without overlapping the R+X mapping.
|
||||
let dataoff: u64 = 0u64;
|
||||
let datava: u64 = 0u64;
|
||||
if (hasdata) {
|
||||
dataoff = (rxend + PAGE_SZ - 1u64) & ~(PAGE_SZ - 1u64);
|
||||
datava = base + dataoff;
|
||||
};
|
||||
|
||||
// Apply relocations now that the layout's textva/datava are
|
||||
// known. Deferred from main.ww so the dyn path uses its own
|
||||
// datava.
|
||||
if (relocate(l, base + TEXT_OFF, datava) != 0) { return -1; };
|
||||
|
||||
// BSS optimisation: trailing zero bytes in .data can be left
|
||||
// out of the file. The loader zero-fills the gap between
|
||||
// p_filesz and p_memsz. Scan after l_relocate has applied any
|
||||
|
||||
Reference in New Issue
Block a user