w6l: route writable globals into a second PT_LOAD

Second step toward top-level mutable `let`. The static path now loads
.data PROGBITS sections from input .o files, page-aligns them after
.text, and emits a second PT_LOAD (R+W) covering them. Relocations
targeting data symbols compute against the data VA; text→text
displacements still cancel the absolute VAs and stay correct.

Inputs without any .data keep the original single-PT_LOAD layout
byte-for-byte — 992 (selfhost w6l .o diff) and 995 (self-rebuild)
depend on that invariant.

Dynamic-link path (-l/-L) rejects .data for now with a clear error;
folding writable globals into the existing R+W segment alongside
.got.plt/.dynamic is a follow-up.
This commit is contained in:
2026-05-12 11:42:30 +09:00
parent 1b0955c97b
commit 38e0b6510a
8 changed files with 406 additions and 34 deletions

View File

@@ -84,6 +84,19 @@ emit_text(Lnk *l, const u8 *src, u64 n)
l->textlen += n;
}
static void
emit_data(Lnk *l, const u8 *src, u64 n)
{
if (l->datalen + n > l->datacap) {
u64 nc = l->datacap ? l->datacap * 2 : 256;
while (nc < l->datalen + n) nc *= 2;
l->data = realloc(l->data, nc);
l->datacap = nc;
}
memcpy(l->data + l->datalen, src, n);
l->datalen += n;
}
/* Internal: load a single ELF .o image already in memory. The caller
* gives us the bytes (we own them) and a path tag for diagnostics.
* If the bytes look like an archive (magic "!<arch>\n") we recurse
@@ -116,12 +129,15 @@ elf_globals(const u8 *buf, u64 len)
Ehdr *eh = (Ehdr *)buf;
if (memcmp(eh->e_ident, "\x7f""ELF", 4) != 0) return NULL;
Shdr *sh = (Shdr *)(buf + eh->e_shoff);
int idx_text = -1, idx_symtab = -1;
int idx_text = -1, idx_data = -1, idx_symtab = -1;
const char *shstr = (const char *)(buf + sh[eh->e_shstrndx].sh_offset);
for (u16 i = 0; i < eh->e_shnum; i++) {
if (sh[i].sh_type == SHT_PROGBITS &&
strcmp(shstr + sh[i].sh_name, ".text") == 0)
idx_text = i;
else if (sh[i].sh_type == SHT_PROGBITS &&
strcmp(shstr + sh[i].sh_name, ".data") == 0)
idx_data = i;
else if (sh[i].sh_type == SHT_SYMTAB)
idx_symtab = i;
}
@@ -131,12 +147,15 @@ elf_globals(const u8 *buf, u64 len)
u64 nsyms = sh[idx_symtab].sh_size / sizeof(Sym64);
const char *str = (const char *)(buf + sh[idx_strtab].sh_offset);
/* Include both .text and .data globals so archive members that
* define a data global get pulled in when something references it. */
char **out = calloc(nsyms + 1, sizeof *out);
int n = 0;
for (u64 i = 1; i < nsyms; i++) {
if (symtab[i].st_shndx == 0) continue;
if ((symtab[i].st_info >> 4) != 1) continue; /* STB_GLOBAL */
if ((int)symtab[i].st_shndx != idx_text) continue;
int sx = (int)symtab[i].st_shndx;
if (sx != idx_text && sx != idx_data) continue;
out[n++] = strdup(str + symtab[i].st_name);
}
out[n] = NULL;
@@ -259,12 +278,15 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len)
if (eh->e_shstrndx >= eh->e_shnum) { free(buf); return -1; }
const char *shstr = (const char *)(buf + sh[eh->e_shstrndx].sh_offset);
/* find .text, .symtab, .strtab, .rela.text */
int idx_text = -1, idx_symtab = -1, idx_strtab = -1, idx_rela = -1;
/* find .text, .data (optional), .symtab, .strtab, .rela.text */
int idx_text = -1, idx_data = -1, idx_symtab = -1, idx_strtab = -1;
int idx_rela = -1;
for (u16 i = 0; i < eh->e_shnum; i++) {
const char *nm = shstr + sh[i].sh_name;
if (sh[i].sh_type == SHT_PROGBITS && strcmp(nm, ".text") == 0)
idx_text = i;
else if (sh[i].sh_type == SHT_PROGBITS && strcmp(nm, ".data") == 0)
idx_data = i;
else if (sh[i].sh_type == SHT_SYMTAB)
idx_symtab = i;
else if (sh[i].sh_type == SHT_RELA && strcmp(nm, ".rela.text") == 0)
@@ -283,11 +305,15 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len)
ob->len = len;
ob->text_off = l->textlen;
ob->text_size = sh[idx_text].sh_size;
ob->data_off = l->datalen;
ob->data_size = (idx_data >= 0) ? sh[idx_data].sh_size : 0;
ob->next = l->objs;
l->objs = ob;
/* append .text */
/* append .text and (if present) .data */
emit_text(l, buf + sh[idx_text].sh_offset, sh[idx_text].sh_size);
if (idx_data >= 0 && sh[idx_data].sh_size > 0)
emit_data(l, buf + sh[idx_data].sh_offset, sh[idx_data].sh_size);
/* per-object: load symbols */
Sym64 *symtab = (Sym64 *)(buf + sh[idx_symtab].sh_offset);
@@ -300,8 +326,10 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len)
const char *nm = str + symtab[i].st_name;
if (nm[0] == '\0') continue;
Lsym *gs = l_intern(l, nm);
if (symtab[i].st_shndx != 0 /* SHN_UNDEF */
&& symtab[i].st_shndx == idx_text) {
int sx = (int)symtab[i].st_shndx;
int in_text = (symtab[i].st_shndx != 0 && sx == idx_text);
int in_data = (idx_data >= 0 && sx == idx_data);
if (in_text || in_data) {
if (gs->defined) {
fprintf(stderr, "w6l: %s: duplicate symbol %s\n",
path, nm);
@@ -310,7 +338,12 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len)
gs->defined = 1;
gs->owner = ob;
gs->idx_in_owner = (int)i;
gs->val = ob->text_off + symtab[i].st_value;
if (in_data) {
gs->in_data = 1;
gs->val = ob->data_off + symtab[i].st_value;
} else {
gs->val = ob->text_off + symtab[i].st_value;
}
}
}
map[i] = gs;