w6a: DATAW directive for writable .data section

First step toward top-level mutable `let`. Adds a sibling directive to
DATA whose bytes land in a separate writable .data PROGBITS section
(SHF_ALLOC|SHF_WRITE, STT_OBJECT) instead of .text. The section is
emitted only when DATAW was used, so inputs without it produce a
byte-identical .o — tests 991 (selfhost .o diff) and 995 (self-rebuild)
keep passing unchanged.

w6l still treats data-resident syms as undefined; that's the next step.
This commit is contained in:
2026-05-12 11:35:50 +09:00
parent 922877309b
commit 1b0955c97b
7 changed files with 381 additions and 18 deletions

View File

@@ -54,6 +54,17 @@ a_emit_u32(Asm *a, u32 v)
a_emit_byte(a, (u8)((v >> 24) & 0xff));
}
void
a_emit_data_byte(Asm *a, u8 b)
{
if (a->datalen + 1 > a->datacap) {
u64 nc = a->datacap ? a->datacap * 2 : 256;
a->data = realloc(a->data, nc);
a->datacap = nc;
}
a->data[a->datalen++] = b;
}
void
a_addreloc(Asm *a, u64 off, int kind, Asym *s, i64 add)
{
@@ -310,6 +321,20 @@ a_encode(Asm *a)
a_emit_byte(a, p->bytes[i]);
break;
}
case A_DATAW: {
/* Writable variant: bytes go into .data (RW) instead
* of .text. obj.c emits the extra section conditionally
* on datalen > 0 so .o output stays byte-identical for
* inputs that don't use DATAW. */
Asym *s = a_intern(a, p->to.sym);
s->defined = 1;
s->is_data = 1;
s->is_global = 1;
s->addr = a->datalen;
for (u64 i = 0; i < p->nbytes; i++)
a_emit_data_byte(a, p->bytes[i]);
break;
}
case A_RET:
a_emit_byte(a, 0xC3);
break;