Unblock literal initialisers for str/slice/struct globals by wiring
an R_X86_64_64 relocation kind through both assembler and static
linker.
w6a:
- new A_DATAR directive, syntax `DATAR slot+off(SB),target(SB)`,
records an R_X86_64_64 reloc at slot+off in .data pointing at
target. The slot must be pre-defined by a prior DATAW;
- parse_operand learned the `name+disp(SB)` shape so the slot's
byte offset can be addressed explicitly;
- Areloc carries a `section` flag (0=.text / 1=.data) and obj.c
splits the reloc list into .rela.text and .rela.data, emitting
the latter conditionally with sh_info pointing at .data.
w6l:
- Lrel grows the same `section` flag; obj.c loads `.rela.data`
sections into the global reloc list with offsets shifted by
each input's data_off;
- pass.c handles R_X86_64_64: target VA is data_va+sym.val for
in_data symbols (else text_va+sym.val), addend is added, and
the 8-byte slot is patched in l->data (or l->text).
Inputs without DATAR are unaffected — bootstrap, 991 (selfhost .o
diff) and 992 (selfhost exe diff) keep their byte-identical
output. 520_datar covers the new path: asm a DATAW+DATAR pair,
verify .rela.data has exactly one R_X86_64_64 entry, link, run,
confirm the relocated pointer feeds a 5-byte write that prints
"hello".
Selfhost mirror + w6c emission for str/slice/struct literal init
land in follow-ups.
120 lines
3.2 KiB
C
120 lines
3.2 KiB
C
/*
|
|
* pass.c — resolution + relocation. After all objects are loaded:
|
|
*
|
|
* l_resolve : check that every symbol referenced by a relocation
|
|
* is defined somewhere. Errors get logged.
|
|
* l_relocate: with the final virtual base address known, walk the
|
|
* relocation list and patch the .text bytes in place.
|
|
*
|
|
* Supported relocation kinds: PC32 (2), PLT32 (4). Both are PC-relative
|
|
* 32-bit displacements; for static linking PLT32 collapses to PC32.
|
|
*/
|
|
#include "l.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define R_X86_64_64 1
|
|
#define R_X86_64_PC32 2
|
|
#define R_X86_64_PLT32 4
|
|
|
|
int
|
|
l_resolve(Lnk *l)
|
|
{
|
|
/* Initialise dynamic-linking fields. plt_idx and dynsym_idx
|
|
* default to -1; l_intern sets is_dyn/dyn_lib to 0/NULL via
|
|
* calloc, but we make the invariants explicit here for clarity. */
|
|
for (Lsym *s = l->syms; s; s = s->next) {
|
|
s->plt_idx = -1;
|
|
s->dynsym_idx = -1;
|
|
}
|
|
|
|
/* Promote each undefined sym that some Lso exports to "dynamic"
|
|
* and hand it a PLT slot. Order is the iteration order over the
|
|
* relocation list; that determines slot numbering and is stable
|
|
* across runs (rels are pushed onto the head as objects load). */
|
|
for (Lrel *r = l->rels; r; r = r->next) {
|
|
if (r->sym == NULL || r->sym->defined) continue;
|
|
if (r->sym->is_dyn) continue; /* already promoted */
|
|
for (Lso *so = l->sos; so; so = so->next) {
|
|
const char *ver = NULL;
|
|
if (l_so_provides_v(so, r->sym->name, &ver)) {
|
|
r->sym->is_dyn = 1;
|
|
r->sym->dyn_lib = so;
|
|
r->sym->dyn_version = ver; /* may be NULL */
|
|
r->sym->plt_idx = l->dyn_n++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* What remains undefined truly is undefined. */
|
|
for (Lrel *r = l->rels; r; r = r->next) {
|
|
if (r->sym == NULL) continue;
|
|
if (!r->sym->defined && !r->sym->is_dyn) {
|
|
fprintf(stderr, "w6l: undefined reference to '%s'\n",
|
|
r->sym->name);
|
|
l->errs++;
|
|
}
|
|
}
|
|
return l->errs;
|
|
}
|
|
|
|
static void
|
|
patch_u32(u8 *p, u32 v)
|
|
{
|
|
p[0] = (u8)(v & 0xff);
|
|
p[1] = (u8)((v >> 8) & 0xff);
|
|
p[2] = (u8)((v >> 16) & 0xff);
|
|
p[3] = (u8)((v >> 24) & 0xff);
|
|
}
|
|
|
|
static void
|
|
patch_u64(u8 *p, u64 v)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
p[i] = (u8)((v >> (i * 8)) & 0xff);
|
|
}
|
|
|
|
int
|
|
l_relocate(Lnk *l, u64 text_va, u64 data_va)
|
|
{
|
|
for (Lrel *r = l->rels; r; r = r->next) {
|
|
if (r->sym == NULL) continue;
|
|
/* Dynamic syms are patched later, in l_emit_elf, once the
|
|
* PLT's virtual address is known. */
|
|
if (r->sym->is_dyn) continue;
|
|
if (!r->sym->defined) continue;
|
|
u64 sym_va = r->sym->in_data
|
|
? data_va + r->sym->val
|
|
: text_va + r->sym->val;
|
|
switch (r->kind) {
|
|
case R_X86_64_PC32:
|
|
case R_X86_64_PLT32: {
|
|
/* PC-relative 32-bit displacement; lands in .text. */
|
|
u64 site = text_va + r->off;
|
|
i64 rel = (i64)sym_va - (i64)site + r->addend;
|
|
patch_u32(l->text + r->off, (u32)(i32)rel);
|
|
break;
|
|
}
|
|
case R_X86_64_64: {
|
|
/* Absolute 64-bit: patch the 8-byte slot at the
|
|
* given offset within the section. Currently only
|
|
* used for data slots holding symbol addresses
|
|
* (DATAR). */
|
|
u64 v = (u64)((i64)sym_va + r->addend);
|
|
if (r->section == 1) {
|
|
patch_u64(l->data + r->off, v);
|
|
} else {
|
|
patch_u64(l->text + r->off, v);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
fprintf(stderr, "w6l: unsupported reloc kind %d\n",
|
|
r->kind);
|
|
l->errs++;
|
|
}
|
|
}
|
|
return l->errs;
|
|
}
|