Files
ww/cmd/w6l/l.h
Hojun-Cho d998425391 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.
2026-05-12 12:45:11 +09:00

119 lines
3.5 KiB
C

/*
* l.h — w6l-private header. Loads relocatable ELF64 .o files (the
* format produced by w6a) and links them into a static executable.
*
* No archives yet (phase 8). No dynamic linking ever.
*/
#ifndef SIX_L_H
#define SIX_L_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef struct Lsym Lsym;
typedef struct Lrel Lrel;
typedef struct Lobj Lobj;
typedef struct Lso Lso;
typedef struct Lnk Lnk;
struct Lsym {
const char *name;
u64 val; /* offset within combined .text (or .data when
* in_data=1) once linked */
int defined; /* 1 if a Lobj defines this symbol */
int in_data; /* 1 if defined in .data (writable globals);
* 0 means .text (the default). Mutually
* exclusive with is_dyn. */
Lobj *owner;
int idx_in_owner;
/* Dynamic-linking fields. Set by l_resolve when an undefined sym
* is provided by some loaded Lso. Patched-in PLT slot index lets
* the relocator route PC32/PLT32 references through the stub. */
int is_dyn;
Lso *dyn_lib;
const char *dyn_version; /* matched export's version, NULL if none */
int plt_idx; /* 0..dyn_n-1, -1 if no PLT slot */
int dynsym_idx; /* index in emitted .dynsym, -1 otherwise */
Lsym *next;
};
struct Lrel {
u64 off; /* offset within the relocation's section */
int section; /* 0 = .text, 1 = .data */
int kind; /* R_X86_64_* */
Lsym *sym;
i64 addend;
Lrel *next;
};
struct Lobj {
const char *path;
u8 *buf; /* mmapped or read-in object bytes */
u64 len;
u64 text_off; /* offset of .text in combined output */
u64 text_size;
u64 data_off; /* offset of .data in combined output */
u64 data_size; /* bytes contributed to combined .data (0 if none) */
Lobj *next;
};
struct Lso {
const char *path; /* full filesystem path used to load */
const char *soname; /* DT_SONAME, or basename if missing */
char **exports; /* NULL-terminated list of GLOBAL/WEAK syms */
char **versions; /* parallel to exports[]; NULL for unversioned,
* else strdup'd version name e.g. "GLIBC_2.2.5" */
Lso *next;
};
struct Lnk {
Lobj *objs;
Lso *sos;
Lsym *syms;
Lrel *rels;
u8 *text; /* combined .text */
u64 textcap, textlen;
u8 *data; /* combined .data (writable). Empty unless any
* input .o has a .data PROGBITS section. */
u64 datacap, datalen;
int errs;
int dyn_n; /* number of symbols routed through PLT */
};
/* obj.c */
int l_load(Lnk*, const char *path);
int l_read_all(const char *path, u8 **out, u64 *len); /* shared helper */
/* dyn.c */
int l_load_so(Lnk*, const char *path);
int l_so_provides(Lso*, const char *name);
/* l_so_provides_v: same, but also returns the export's version name
* (NULL for unversioned globals) via *out_version on a hit. */
int l_so_provides_v(Lso*, const char *name, const char **out_version);
/* sym.c */
Lsym *l_intern(Lnk*, const char *name);
Lsym *l_lookup(Lnk*, const char *name);
/* pass.c */
int l_resolve(Lnk*);
/* Patch relocations in l->text. text_va is the VA where .text will be
* mapped; data_va is the VA where .data will be mapped (pass 0 if no
* data symbols). PC32/PLT32 displacements between text symbols cancel
* the absolute VA, but data targets need the real data_va. */
int l_relocate(Lnk*, u64 text_va, u64 data_va);
/* out.c */
int l_emit_elf(Lnk*, FILE *out, u64 base, u64 entry);
/* dynout.c — emit a dynamic-linked ELF executable. Called by
* l_emit_elf when l->sos is non-empty. */
int l_emit_dyn_elf(Lnk*, FILE *out, u64 base, u64 entry);
#endif