nx_nxld.nx
buildroot/runtime/nx_nxld.nx
about
nxld.nx -- Nishi sovereign linker.
Replaces gcc-ld in verify.sh. Reads RV64 object files (ELF
relocatable), resolves symbols across them, and produces a
statically-linked RV64 Linux ELF executable.
Scope (v0.0.1):
- Single-output static linking (no .so / dynamic linking)
- RV64 only (Sv39 virtual memory layout)
- ELF relocations: R_RISCV_64, R_RISCV_BRANCH, R_RISCV_JAL,
R_RISCV_CALL, R_RISCV_HI20, R_RISCV_LO12_I, R_RISCV_LO12_S,
R_RISCV_PCREL_HI20, R_RISCV_PCREL_LO12_I
- Single .text section + single .data section + single .bss
- Entry point = _start (RV64 Linux convention)
- Output base vaddr = 0x10000 (matches elf_writer.nx)
Out of scope (v0.0.1):
- Section ordering directives
- Linker scripts
- LTO bytecode handling
- Position-independent code
- Garbage collection of unreferenced sections
- Debug info preservation (DWARF stripped)
These are deliberate v0.0.1 limits. v0.1.0 closes them.
Pipeline:
1. Open + parse each input ELF (header, sections, symbols, relocs)
2. Group sections by type: .text, .data, .bss
3. Resolve undefined symbols across input objects
4. Lay out output sections (text starts at 0x10000 + 0x1000 header)
5. Apply relocations now that final addresses are known
6. Build output ELF (header + program headers + sections)
7. Write to output file
dependencies 2 imports · 1 importers
imports: nx_syscalls.nxelf_writer.nx
imported by: nx_nxld_main.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| 81 | struct ElfObject |
| 102 | struct Symbol |
| 347 | struct Reloc |
consts
| 46 | const ELF_MAGIC0: i64 = 0x7F |
| 47 | const ELF_MAGIC1: i64 = 0x45 // 'E' |
| 48 | const ELF_MAGIC2: i64 = 0x4C // 'L' |
| 49 | const ELF_MAGIC3: i64 = 0x46 // 'F' |
| 50 | const EI_CLASS64: i64 = 2 |
| 51 | const EI_DATA2LSB: i64 = 1 |
| 52 | const ET_EXEC: i64 = 2 |
| 53 | const ET_REL: i64 = 1 |
| 54 | const EM_RISCV: i64 = 0xF3 |
| 56 | const SHT_NULL: i64 = 0 |
| 57 | const SHT_PROGBITS: i64 = 1 |
| 58 | const SHT_SYMTAB: i64 = 2 |
| 59 | const SHT_STRTAB: i64 = 3 |
| 60 | const SHT_RELA: i64 = 4 |
| 61 | const SHT_NOBITS: i64 = 8 |
| 63 | const PT_LOAD: i64 = 1 |
| 64 | const PF_R: i64 = 4 |
| 65 | const PF_W: i64 = 2 |
| 66 | const PF_X: i64 = 1 |
| 69 | const R_RISCV_64: i64 = 2 |
| 70 | const R_RISCV_BRANCH: i64 = 16 |
| 71 | const R_RISCV_JAL: i64 = 17 |
| 72 | const R_RISCV_CALL: i64 = 18 |
| 73 | const R_RISCV_PCREL_HI20: i64 = 23 |
| 74 | const R_RISCV_PCREL_LO12_I: i64 = 24 |
| 75 | const R_RISCV_HI20: i64 = 26 |
| 76 | const R_RISCV_LO12_I: i64 = 27 |
| 77 | const R_RISCV_LO12_S: i64 = 28 |
| 252 | const SYMBOL_BYTES: i64 = 24 |
| 253 | const STN_UNDEF: i64 = 0 |
| 254 | const STB_LOCAL: i64 = 0 |
| 255 | const STB_GLOBAL: i64 = 1 |
| 256 | const STB_WEAK: i64 = 2 |
| 257 | const STT_NOTYPE: i64 = 0 |
| 258 | const STT_OBJECT: i64 = 1 |
| 259 | const STT_FUNC: i64 = 2 |
| 260 | const STT_SECTION: i64 = 3 |
| 345 | const RELOC_BYTES: i64 = 24 |
| 578 | const NXLD_BASE_VADDR: i64 = 0x10000 |
| 579 | const NXLD_HEADER_BYTES: i64 = 120 // 64 ELF hdr + 56 phdr |
functions
| 114 | func read_u16_le(buf: *u8, off: i64) -> i64 |
| 118 | func read_u32_le(buf: *u8, off: i64) -> i64 |
| 128 | func read_u64_le(buf: *u8, off: i64) -> i64 |
| 142 | func sec_name_eq(np: *u8, lit: *u8, lit_len: i64) -> i64 called by 1: nxld_open |
| 154 | func nxld_open(obj: *ElfObject, base: *u8, length: i64) -> i64 |
| 264 | func nxld_symbol_count(obj: *ElfObject) -> i64 |
| 272 | func nxld_read_symbol(obj: *ElfObject, idx: i64, sym: *Symbol) -> i64 called by 3: nxld_find_symbolnxld_linkmain calls 4: nxld_symbol_countread_u32_leread_u16_leread_u64_le |
| 304 | func sym_name_eq(np: *u8, np_len: i64, lit: *u8, lit_len: i64) -> i64 called by 1: nxld_find_symbol |
| 316 | func nxld_find_symbol(obj: *ElfObject, name: *u8, name_len: i64) -> i64 |
| 356 | func nxld_reloc_count(obj: *ElfObject) -> i64 |
| 363 | func nxld_read_reloc(obj: *ElfObject, idx: i64, r: *Reloc) -> i64 |
| 406 | func write_u32_le(buf: *u8, off: i64, v: i64) -> i64 called by 1: nxld_apply_one |
| 416 | func patch_i_imm(word: i64, imm: i64) -> i64 called by 1: nxld_apply_one |
| 424 | func patch_s_imm(word: i64, imm: i64) -> i64 called by 1: nxld_apply_one |
| 432 | func patch_u_imm(word: i64, imm_hi: i64) -> i64 called by 1: nxld_apply_one |
| 438 | func patch_j_imm(word: i64, imm: i64) -> i64 called by 1: nxld_apply_one |
| 449 | func patch_b_imm(word: i64, imm: i64) -> i64 called by 1: nxld_apply_one |
| 462 | func split_hi20(value: i64) -> i64 |
| 465 | func split_lo12(value: i64) -> i64 |
| 472 | func nxld_apply_one(r: *Reloc, text_buf: *u8, text_vaddr: i64, called by 2: nxld_linkmain calls 8: patch_b_immwrite_u32_lepatch_j_immsplit_hi20split_lo12patch_u_imm+2 |
| 581 | func nxld_layout(obj: *ElfObject, syms_pool: *Symbol, n_syms: i64, |
| 624 | func nxld_link(obj: *ElfObject, fd: i64) -> i64 called by 1: main calls 8: nxld_symbol_countsys_mmapnxld_read_symbolnxld_layoutnxld_reloc_countnxld_read_reloc+2 |
| 675 | func main() -> i64 |