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.
118 lines
3.5 KiB
C
118 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 combined .text */
|
|
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
|