w6a+w6l: DATAR directive for absolute-address relocs in .data
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.
This commit is contained in:
@@ -48,7 +48,8 @@ struct Lsym {
|
||||
};
|
||||
|
||||
struct Lrel {
|
||||
u64 off; /* offset within combined .text */
|
||||
u64 off; /* offset within the relocation's section */
|
||||
int section; /* 0 = .text, 1 = .data */
|
||||
int kind; /* R_X86_64_* */
|
||||
Lsym *sym;
|
||||
i64 addend;
|
||||
|
||||
@@ -278,9 +278,10 @@ 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, .data (optional), .symtab, .strtab, .rela.text */
|
||||
/* find .text, .data (optional), .symtab, .strtab, .rela.text,
|
||||
* .rela.data (optional) */
|
||||
int idx_text = -1, idx_data = -1, idx_symtab = -1, idx_strtab = -1;
|
||||
int idx_rela = -1;
|
||||
int idx_rela = -1, idx_relad = -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)
|
||||
@@ -291,6 +292,8 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len)
|
||||
idx_symtab = i;
|
||||
else if (sh[i].sh_type == SHT_RELA && strcmp(nm, ".rela.text") == 0)
|
||||
idx_rela = i;
|
||||
else if (sh[i].sh_type == SHT_RELA && strcmp(nm, ".rela.data") == 0)
|
||||
idx_relad = i;
|
||||
}
|
||||
if (idx_text < 0 || idx_symtab < 0) {
|
||||
fprintf(stderr, "w6l: %s: missing .text or .symtab\n", path);
|
||||
@@ -356,6 +359,25 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len)
|
||||
for (u64 i = 0; i < nrel; i++) {
|
||||
Lrel *r = calloc(1, sizeof *r);
|
||||
r->off = ob->text_off + rt[i].r_offset;
|
||||
r->section = 0;
|
||||
r->kind = (int)ELF64_R_TYPE(rt[i].r_info);
|
||||
u32 sidx = ELF64_R_SYM(rt[i].r_info);
|
||||
r->sym = (sidx < nsyms) ? map[sidx] : NULL;
|
||||
r->addend = rt[i].r_addend;
|
||||
r->next = l->rels;
|
||||
l->rels = r;
|
||||
}
|
||||
}
|
||||
/* per-object: collect data relocations from .rela.data. The
|
||||
* .data section in the .o starts at a per-object 0; we shift
|
||||
* by ob->data_off so r->off indexes the combined .data buffer. */
|
||||
if (idx_relad >= 0) {
|
||||
Rela64 *rt = (Rela64 *)(buf + sh[idx_relad].sh_offset);
|
||||
u64 nrel = sh[idx_relad].sh_size / sizeof(Rela64);
|
||||
for (u64 i = 0; i < nrel; i++) {
|
||||
Lrel *r = calloc(1, sizeof *r);
|
||||
r->off = ob->data_off + rt[i].r_offset;
|
||||
r->section = 1;
|
||||
r->kind = (int)ELF64_R_TYPE(rt[i].r_info);
|
||||
u32 sidx = ELF64_R_SYM(rt[i].r_info);
|
||||
r->sym = (sidx < nsyms) ? map[sidx] : NULL;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define R_X86_64_64 1
|
||||
#define R_X86_64_PC32 2
|
||||
#define R_X86_64_PLT32 4
|
||||
|
||||
@@ -67,6 +68,13 @@ patch_u32(u8 *p, u32 v)
|
||||
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)
|
||||
{
|
||||
@@ -76,17 +84,31 @@ l_relocate(Lnk *l, u64 text_va, u64 data_va)
|
||||
* 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;
|
||||
u64 sym_va = r->sym->in_data
|
||||
? data_va + r->sym->val
|
||||
: text_va + r->sym->val;
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user