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:
@@ -142,17 +142,12 @@ poke32(u8 *buf, u64 off, u32 v)
|
|||||||
int
|
int
|
||||||
l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
||||||
{
|
{
|
||||||
/* Writable globals on the dynamic-link path need their own R+W
|
/* Writable globals share the dyn-path R+W segment with .got.plt
|
||||||
* segment, and the existing R+W segment (gotplt + dynamic) has a
|
* and .dynamic. .data is placed after .dynamic; the segment's
|
||||||
* fixed layout that l_relocate's data_va guess wouldn't match.
|
* filesz/memsz are extended to cover all three. Relocation
|
||||||
* Rather than ship a silently-miscompiled binary, refuse and
|
* targeting .data uses data_va computed inside this function
|
||||||
* leave the feature for a follow-up. */
|
* (l_relocate now runs from here, not main, so the dyn layout's
|
||||||
if (l->datalen > 0) {
|
* data VA is the one that lands in patched offsets). */
|
||||||
fprintf(stderr, "w6l: top-level mutable globals (.data) are "
|
|
||||||
"not yet supported with -l/-L; link without shared "
|
|
||||||
"libraries to use them.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int N = l->dyn_n;
|
const int N = l->dyn_n;
|
||||||
|
|
||||||
@@ -375,10 +370,12 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
|||||||
const u64 rx_end = plt_off + plt_sz;
|
const u64 rx_end = plt_off + plt_sz;
|
||||||
|
|
||||||
/* Page-align the writable segment. We skip a page of file bytes;
|
/* Page-align the writable segment. We skip a page of file bytes;
|
||||||
* the data lands at file offset gotplt_off, vaddr at base+gotplt_va. */
|
* the data lands at file offset gotplt_off, vaddr at base+gotplt_va.
|
||||||
|
* .data sits after .dynamic so the whole R+W run is one segment. */
|
||||||
const u64 gotplt_off = (rx_end + page - 1) & ~(page - 1);
|
const u64 gotplt_off = (rx_end + page - 1) & ~(page - 1);
|
||||||
const u64 dynamic_off = gotplt_off + gotplt_sz;
|
const u64 dynamic_off = gotplt_off + gotplt_sz;
|
||||||
const u64 file_end = dynamic_off + dynamic_sz;
|
const u64 data_off = dynamic_off + dynamic_sz;
|
||||||
|
const u64 file_end = data_off + l->datalen;
|
||||||
|
|
||||||
/* Virtual addresses mirror file offsets within their segment.
|
/* Virtual addresses mirror file offsets within their segment.
|
||||||
* The R+W segment in particular needs vaddr = base + gotplt_off
|
* The R+W segment in particular needs vaddr = base + gotplt_off
|
||||||
@@ -394,8 +391,24 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
|||||||
const u64 plt_va = base + plt_off;
|
const u64 plt_va = base + plt_off;
|
||||||
const u64 gotplt_va = base + gotplt_off;
|
const u64 gotplt_va = base + gotplt_off;
|
||||||
const u64 dynamic_va = base + dynamic_off;
|
const u64 dynamic_va = base + dynamic_off;
|
||||||
|
const u64 data_va = base + data_off;
|
||||||
(void)dynstr_va; (void)hash_va; (void)plt_va;
|
(void)dynstr_va; (void)hash_va; (void)plt_va;
|
||||||
|
|
||||||
|
/* Now that the dyn layout pins text_va/data_va, apply
|
||||||
|
* relocations. main.c defers this; the static path runs it from
|
||||||
|
* out.c with its own VAs. */
|
||||||
|
if (l_relocate(l, text_va, data_va) != 0) return 1;
|
||||||
|
|
||||||
|
/* BSS optimisation — same trailing-zero scan as out.c. */
|
||||||
|
u64 bsslen = 0;
|
||||||
|
if (l->datalen > 0) {
|
||||||
|
while (bsslen < l->datalen
|
||||||
|
&& l->data[l->datalen - 1 - bsslen] == 0)
|
||||||
|
bsslen++;
|
||||||
|
}
|
||||||
|
const u64 data_file_len = l->datalen - bsslen;
|
||||||
|
const u64 file_data_end = data_off + data_file_len;
|
||||||
|
|
||||||
/* ---- Pass 4: build each section into a buffer ---- */
|
/* ---- Pass 4: build each section into a buffer ---- */
|
||||||
|
|
||||||
/* .dynsym */
|
/* .dynsym */
|
||||||
@@ -593,13 +606,15 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
|||||||
ph[0].p_memsz = rx_end;
|
ph[0].p_memsz = rx_end;
|
||||||
ph[0].p_align = page;
|
ph[0].p_align = page;
|
||||||
|
|
||||||
/* PT_LOAD #2 — R+W covering .got.plt and .dynamic. */
|
/* PT_LOAD #2 — R+W covering .got.plt, .dynamic, and .data.
|
||||||
|
* filesz drops the trailing-zero suffix (BSS); memsz keeps the
|
||||||
|
* full extent so the loader zero-fills the gap. */
|
||||||
ph[1].p_type = PT_LOAD;
|
ph[1].p_type = PT_LOAD;
|
||||||
ph[1].p_flags = PF_R | PF_W;
|
ph[1].p_flags = PF_R | PF_W;
|
||||||
ph[1].p_offset = gotplt_off;
|
ph[1].p_offset = gotplt_off;
|
||||||
ph[1].p_vaddr = gotplt_va;
|
ph[1].p_vaddr = gotplt_va;
|
||||||
ph[1].p_paddr = gotplt_va;
|
ph[1].p_paddr = gotplt_va;
|
||||||
ph[1].p_filesz = file_end - gotplt_off;
|
ph[1].p_filesz = file_data_end - gotplt_off;
|
||||||
ph[1].p_memsz = file_end - gotplt_off;
|
ph[1].p_memsz = file_end - gotplt_off;
|
||||||
ph[1].p_align = page;
|
ph[1].p_align = page;
|
||||||
|
|
||||||
@@ -645,6 +660,9 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
|||||||
PAD_TO(plt_off); fwrite(plt, 1, plt_sz, f);
|
PAD_TO(plt_off); fwrite(plt, 1, plt_sz, f);
|
||||||
PAD_TO(gotplt_off); fwrite(gotplt, 1, gotplt_sz, f);
|
PAD_TO(gotplt_off); fwrite(gotplt, 1, gotplt_sz, f);
|
||||||
PAD_TO(dynamic_off); fwrite(dynamic, 1, dynamic_sz, f);
|
PAD_TO(dynamic_off); fwrite(dynamic, 1, dynamic_sz, f);
|
||||||
|
if (data_file_len > 0) {
|
||||||
|
PAD_TO(data_off); fwrite(l->data, 1, data_file_len, f);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < n_vlibs; i++) free(vlibs[i].versions);
|
for (int i = 0; i < n_vlibs; i++) free(vlibs[i].versions);
|
||||||
free(vlibs); free(versym_for); free(versym);
|
free(vlibs); free(versym_for); free(versym);
|
||||||
|
|||||||
@@ -115,20 +115,9 @@ main(int argc, char **argv)
|
|||||||
if (l_load(&l, inputs[i]) != 0) return 1;
|
if (l_load(&l, inputs[i]) != 0) return 1;
|
||||||
}
|
}
|
||||||
if (l_resolve(&l) != 0) return 1;
|
if (l_resolve(&l) != 0) return 1;
|
||||||
/* Layout for relocation purposes. The static path (out.c) uses
|
/* Relocation is deferred to the emit functions — each path knows
|
||||||
* text_va = base + 0x1000 and places .data at the next page after
|
* its own layout (text_va, data_va), and the static and dynamic
|
||||||
* .text — these must agree with the layout in out.c. PC-relative
|
* paths place .data at different VAs. */
|
||||||
* displacements between .text symbols cancel the absolute VAs, so
|
|
||||||
* a dyn-path link whose data_va is "wrong" still relocates text→
|
|
||||||
* text correctly; text→data is currently rejected in dynout.c. */
|
|
||||||
u64 text_va = base + 0x1000;
|
|
||||||
u64 data_va = 0;
|
|
||||||
if (l.datalen > 0) {
|
|
||||||
u64 page = 0x1000;
|
|
||||||
u64 data_off = (0x1000 + l.textlen + page - 1) & ~(page - 1);
|
|
||||||
data_va = base + data_off;
|
|
||||||
}
|
|
||||||
if (l_relocate(&l, text_va, data_va) != 0) return 1;
|
|
||||||
|
|
||||||
Lsym *entry = l_lookup(&l, "_start");
|
Lsym *entry = l_lookup(&l, "_start");
|
||||||
if (entry == NULL || !entry->defined) entry = l_lookup(&l, "main");
|
if (entry == NULL || !entry->defined) entry = l_lookup(&l, "main");
|
||||||
|
|||||||
@@ -63,6 +63,16 @@ l_emit_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
|||||||
const u64 rx_end = text_off + l->textlen;
|
const u64 rx_end = text_off + l->textlen;
|
||||||
const int has_data = (l->datalen > 0);
|
const int has_data = (l->datalen > 0);
|
||||||
|
|
||||||
|
/* data goes at the next page boundary so the loader can grant a
|
||||||
|
* fresh page of R+W permissions without overlapping the R+X mapping. */
|
||||||
|
const u64 data_off = has_data ? ((rx_end + page - 1) & ~(page - 1)) : 0;
|
||||||
|
const u64 data_va = has_data ? (base + data_off) : 0;
|
||||||
|
|
||||||
|
/* Apply relocations now that the layout's text_va/data_va are
|
||||||
|
* known. Deferred from main.c so the dynamic-link path can use
|
||||||
|
* its own data_va. */
|
||||||
|
if (l_relocate(l, base + text_off, data_va) != 0) return 1;
|
||||||
|
|
||||||
/* BSS optimisation: trailing zero bytes in .data can be left out
|
/* BSS optimisation: trailing zero bytes in .data can be left out
|
||||||
* of the file. The loader zero-fills the gap between p_filesz and
|
* of the file. The loader zero-fills the gap between p_filesz and
|
||||||
* p_memsz, so this shrinks the binary without changing semantics.
|
* p_memsz, so this shrinks the binary without changing semantics.
|
||||||
@@ -75,11 +85,6 @@ l_emit_elf(Lnk *l, FILE *f, u64 base, u64 entry)
|
|||||||
bsslen++;
|
bsslen++;
|
||||||
}
|
}
|
||||||
const u64 data_file_len = l->datalen - bsslen;
|
const u64 data_file_len = l->datalen - bsslen;
|
||||||
|
|
||||||
/* data goes at the next page boundary so the loader can grant a
|
|
||||||
* fresh page of R+W permissions without overlapping the R+X mapping. */
|
|
||||||
const u64 data_off = has_data ? ((rx_end + page - 1) & ~(page - 1)) : 0;
|
|
||||||
const u64 data_va = has_data ? (base + data_off) : 0;
|
|
||||||
const u64 file_end = has_data ? (data_off + data_file_len) : rx_end;
|
const u64 file_end = has_data ? (data_off + data_file_len) : rx_end;
|
||||||
(void)data_va;
|
(void)data_va;
|
||||||
|
|
||||||
|
|||||||
@@ -163,14 +163,10 @@ fn streqd(a: str, b: str) bool = {
|
|||||||
// ---- main entry --------------------------------------------------------
|
// ---- main entry --------------------------------------------------------
|
||||||
|
|
||||||
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||||
// Writable globals on the dynamic-link path need their own R+W
|
// .data shares the R+W PT_LOAD with .got.plt and .dynamic.
|
||||||
// segment that the existing gotplt + .dynamic layout doesn't
|
// Placed after .dynamic so the segment is one contiguous run;
|
||||||
// account for. Refuse rather than silently miscompile; folding
|
// relocate runs from here so the dyn layout's datava lands in
|
||||||
// .data into the existing R+W block is a follow-up.
|
// patched offsets.
|
||||||
if (l.datalen > 0u64) {
|
|
||||||
os.write(2, "w6l: top-level mutable globals (.data) not yet supported with -l/-L\n".ptr, 67u64);
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
let a: *arena = l.a;
|
let a: *arena = l.a;
|
||||||
let n: i32 = l.dynn;
|
let n: i32 = l.dynn;
|
||||||
let nu: u64 = n: u64;
|
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 gotpltoff: u64 = alignup(rxend, PAGE);
|
||||||
let dynamicoff: u64 = gotpltoff + gotpltsz;
|
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 interpva: u64 = base + interpoff;
|
||||||
let dynstrva: u64 = base + dynstroff;
|
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 pltva: u64 = base + pltoff;
|
||||||
let gotpltva: u64 = base + gotpltoff;
|
let gotpltva: u64 = base + gotpltoff;
|
||||||
let dynamicva: u64 = base + dynamicoff;
|
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 ----
|
// ---- build .dynsym ----
|
||||||
let dynsymbuf: *u8 = amalloc(a, dynsymsz): *u8;
|
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 + 8u64, gotpltoff);
|
||||||
dwr64(filebuf, p1 + 16u64, gotpltva);
|
dwr64(filebuf, p1 + 16u64, gotpltva);
|
||||||
dwr64(filebuf, p1 + 24u64, 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 + 40u64, fileend - gotpltoff);
|
||||||
dwr64(filebuf, p1 + 48u64, PAGE);
|
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, pltoff, pltbuf, pltsz);
|
||||||
dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz);
|
dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz);
|
||||||
dbcopy(filebuf, dynamicoff, dynamicbuf, dynamicsz);
|
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) {
|
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;
|
case let e: os.oserror => return 1;
|
||||||
};
|
};
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1759,14 +1759,10 @@ fn streqd(a: str, b: str) bool = {
|
|||||||
// ---- main entry --------------------------------------------------------
|
// ---- main entry --------------------------------------------------------
|
||||||
|
|
||||||
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||||
// Writable globals on the dynamic-link path need their own R+W
|
// .data shares the R+W PT_LOAD with .got.plt and .dynamic.
|
||||||
// segment that the existing gotplt + .dynamic layout doesn't
|
// Placed after .dynamic so the segment is one contiguous run;
|
||||||
// account for. Refuse rather than silently miscompile; folding
|
// relocate runs from here so the dyn layout's datava lands in
|
||||||
// .data into the existing R+W block is a follow-up.
|
// patched offsets.
|
||||||
if (l.datalen > 0u64) {
|
|
||||||
os.write(2, "w6l: top-level mutable globals (.data) not yet supported with -l/-L\n".ptr, 67u64);
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
let a: *arena = l.a;
|
let a: *arena = l.a;
|
||||||
let n: i32 = l.dynn;
|
let n: i32 = l.dynn;
|
||||||
let nu: u64 = n: u64;
|
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 gotpltoff: u64 = alignup(rxend, PAGE);
|
||||||
let dynamicoff: u64 = gotpltoff + gotpltsz;
|
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 interpva: u64 = base + interpoff;
|
||||||
let dynstrva: u64 = base + dynstroff;
|
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 pltva: u64 = base + pltoff;
|
||||||
let gotpltva: u64 = base + gotpltoff;
|
let gotpltva: u64 = base + gotpltoff;
|
||||||
let dynamicva: u64 = base + dynamicoff;
|
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 ----
|
// ---- build .dynsym ----
|
||||||
let dynsymbuf: *u8 = amalloc(a, dynsymsz): *u8;
|
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 + 8u64, gotpltoff);
|
||||||
dwr64(filebuf, p1 + 16u64, gotpltva);
|
dwr64(filebuf, p1 + 16u64, gotpltva);
|
||||||
dwr64(filebuf, p1 + 24u64, 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 + 40u64, fileend - gotpltoff);
|
||||||
dwr64(filebuf, p1 + 48u64, PAGE);
|
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, pltoff, pltbuf, pltsz);
|
||||||
dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz);
|
dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz);
|
||||||
dbcopy(filebuf, dynamicoff, dynamicbuf, dynamicsz);
|
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) {
|
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;
|
case let e: os.oserror => return 1;
|
||||||
};
|
};
|
||||||
return 0;
|
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
|
// .data lands at the next page boundary so the loader can give
|
||||||
// it fresh R+W permissions without overlapping the R+X mapping.
|
// it fresh R+W permissions without overlapping the R+X mapping.
|
||||||
let dataoff: u64 = 0u64;
|
let dataoff: u64 = 0u64;
|
||||||
|
let datava: u64 = 0u64;
|
||||||
if (hasdata) {
|
if (hasdata) {
|
||||||
dataoff = (rxend + PAGE_SZ - 1u64) & ~(PAGE_SZ - 1u64);
|
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
|
// BSS optimisation: trailing zero bytes in .data can be left
|
||||||
// out of the file. The loader zero-fills the gap between
|
// out of the file. The loader zero-fills the gap between
|
||||||
// p_filesz and p_memsz. Scan after l_relocate has applied any
|
// 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; };
|
if (resolve(l) != 0) { return 1; };
|
||||||
// Static layout for relocation purposes. .data lands at the
|
// Relocation is deferred to the emit functions — each path
|
||||||
// next page after .text; the dyn path overrides if its layout
|
// knows its own layout (textva, datava); the static and dyn
|
||||||
// is different (text→text PC32 cancels the absolute VA so
|
// paths place .data at different VAs.
|
||||||
// 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; };
|
|
||||||
|
|
||||||
let entrysym: *lsym = lookup(l, "_start");
|
let entrysym: *lsym = lookup(l, "_start");
|
||||||
if (entrysym == nil) { entrysym = lookup(l, "main"); }
|
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; };
|
if (resolve(l) != 0) { return 1; };
|
||||||
// Static layout for relocation purposes. .data lands at the
|
// Relocation is deferred to the emit functions — each path
|
||||||
// next page after .text; the dyn path overrides if its layout
|
// knows its own layout (textva, datava); the static and dyn
|
||||||
// is different (text→text PC32 cancels the absolute VA so
|
// paths place .data at different VAs.
|
||||||
// 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; };
|
|
||||||
|
|
||||||
let entrysym: *lsym = lookup(l, "_start");
|
let entrysym: *lsym = lookup(l, "_start");
|
||||||
if (entrysym == nil) { entrysym = lookup(l, "main"); }
|
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
|
// .data lands at the next page boundary so the loader can give
|
||||||
// it fresh R+W permissions without overlapping the R+X mapping.
|
// it fresh R+W permissions without overlapping the R+X mapping.
|
||||||
let dataoff: u64 = 0u64;
|
let dataoff: u64 = 0u64;
|
||||||
|
let datava: u64 = 0u64;
|
||||||
if (hasdata) {
|
if (hasdata) {
|
||||||
dataoff = (rxend + PAGE_SZ - 1u64) & ~(PAGE_SZ - 1u64);
|
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
|
// BSS optimisation: trailing zero bytes in .data can be left
|
||||||
// out of the file. The loader zero-fills the gap between
|
// out of the file. The loader zero-fills the gap between
|
||||||
// p_filesz and p_memsz. Scan after l_relocate has applied any
|
// p_filesz and p_memsz. Scan after l_relocate has applied any
|
||||||
|
|||||||
@@ -76,6 +76,28 @@ static const struct row rows[] = {
|
|||||||
" return 0;\n"
|
" return 0;\n"
|
||||||
"};", 5 },
|
"};", 5 },
|
||||||
|
|
||||||
|
/* 5. Writable global on the dyn-link path. .data now lives in the
|
||||||
|
* shared R+W segment with .got.plt and .dynamic; read and write
|
||||||
|
* exercise the new layout's data_va. */
|
||||||
|
{ "@symbol(\"_exit\") fn libc_exit(c: i32) void;\n"
|
||||||
|
"let counter: i32 = 0;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" counter = 11;\n"
|
||||||
|
" libc_exit(counter);\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};", 11 },
|
||||||
|
|
||||||
|
/* 6. Zero-init slot under dyn-link: the BSS trailing-zero scan
|
||||||
|
* should still produce a valid p_filesz<p_memsz layout when the
|
||||||
|
* R+W segment is the shared gotplt+dynamic+.data one. */
|
||||||
|
{ "@symbol(\"_exit\") fn libc_exit(c: i32) void;\n"
|
||||||
|
"let z: i64;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" z = 9i64;\n"
|
||||||
|
" libc_exit(z: i32);\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};", 9 },
|
||||||
|
|
||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user