link_dyn.h (5616B)
1 #ifndef KIT_OBJ_ELF_LINK_DYN_H 2 #define KIT_OBJ_ELF_LINK_DYN_H 3 4 /* ELF dynamic-link synthesis state. 5 * 6 * DynSymRec / DynRela are ELF64 wire-format records and LinkDynState is the 7 * ELF linker's working state for .dynsym / .dynstr / .gnu.hash / .rela.* / 8 * .plt / .got.plt / .dynamic. They live here, in an ELF-only header, so the 9 * format-neutral link_internal.h can carry an opaque `LinkDynState* dyn` on 10 * LinkImage without leaking ELF field names into the COFF/Mach-O linkers. 11 * Only src/obj/elf/link.c and src/obj/elf/link_dyn.c include this. */ 12 13 #include "core/core.h" 14 #include "link/link.h" 15 16 /* ---- Dynamic-link synthesis state (Phase 4) ---- 17 * 18 * Owned by LinkImage when emit_pie is set. Holds the synthesized 19 * .interp / .dynsym / .dynstr / .gnu.hash / .rela.dyn / .rela.plt / 20 * .plt / .got.plt / .dynamic content plus the section ids the emit 21 * pass needs to fill PT_DYNAMIC and the .dynamic body. 22 * 23 * Phase 4 builds the dynsym/dynstr/gnu.hash content and the JUMP_SLOT 24 * .rela.plt records (one per imported function, against its synthetic 25 * .got.plt slot). The .plt body is allocated but not emitted (Phase 5). 26 * Phase 6 populates .rela.dyn with R_AARCH64_RELATIVE records for any 27 * internal absolute reloc seen during reloc-apply. 28 * 29 * Layout invariants this struct enforces: 30 * - dynsym entry 0 is the reserved STN_UNDEF slot (zero-filled). 31 * - dynsym entries 1..nimport_func+nimport_data are imports, in the 32 * order PLT-functions first, then GOT-data. 33 * - PLT slots and JUMP_SLOT entries match the import_func order 1:1. 34 * - .got.plt has 3 reserved leading u64 slots (per AArch64 psABI: 35 * slot 0 = &.dynamic, slot 1 = link_map cookie, slot 2 = 36 * _dl_runtime_resolve), then one slot per imported function. 37 */ 38 39 typedef struct DynSymRec { 40 u32 st_name; /* offset into .dynstr */ 41 u8 st_info; 42 u8 st_other; 43 u16 st_shndx; 44 u64 st_value; 45 u64 st_size; 46 } DynSymRec; 47 48 typedef struct DynRela { 49 u64 r_offset; /* image-relative vaddr of the patch site */ 50 u64 r_info; /* ELF64_R_INFO(dynsym_index, elf_reloc_type) */ 51 i64 r_addend; 52 } DynRela; 53 54 typedef struct LinkDynState { 55 /* PT_INTERP / .interp. interp_path is interned in compiler->global. */ 56 Sym interp_path; 57 LinkSectionId sec_interp; 58 59 /* DT_SONAME / DT_RPATH / DT_RUNPATH strings, interned in compiler->global. */ 60 Sym soname; 61 Sym* rpaths; 62 u32 nrpaths; 63 Sym* runpaths; 64 u32 nrunpaths; 65 66 /* .dynsym */ 67 LinkSectionId sec_dynsym; 68 DynSymRec* dynsym; 69 u32 ndynsym; /* incl. slot-0 STN_UNDEF */ 70 u32 first_global; /* sh_info value: index of first non-local entry */ 71 72 /* .dynstr */ 73 LinkSectionId sec_dynstr; 74 u8* dynstr; 75 u32 dynstr_len; 76 77 /* .gnu.hash */ 78 LinkSectionId sec_gnu_hash; 79 u8* gnu_hash; 80 u32 gnu_hash_len; 81 82 /* GNU symbol versioning. Emitted only when at least one imported symbol 83 * binds to a versioned DSO export (nverneed > 0); otherwise all three are 84 * zero and no version sections / DT_VER* entries are produced (musl/static 85 * links are unchanged). .gnu.version is one u16 per .dynsym entry; 86 * .gnu.version_r holds Verneed/Vernaux requirements keyed by DT_NEEDED 87 * soname. Both carry only .dynstr offsets + version indices (no vaddrs), so 88 * the bytes are final at layout time and copied verbatim during emit. */ 89 LinkSectionId sec_gnu_version; 90 u8* versym; /* ndynsym * 2 bytes */ 91 u32 versym_len; 92 LinkSectionId sec_gnu_version_r; 93 u8* verneed; /* nverneed Verneed records + their Vernaux */ 94 u32 verneed_len; 95 u32 nverneed; /* DT_VERNEEDNUM */ 96 97 /* .rela.dyn — R_AARCH64_GLOB_DAT (imports against .got slots) and 98 * R_AARCH64_RELATIVE (PIE internal abs64 fixups, populated during 99 * Phase 6 emit). Pre-sized at layout time; the RELATIVE tail is 100 * filled in during emit. */ 101 LinkSectionId sec_rela_dyn; 102 DynRela* rela_dyn; 103 u32 nrela_dyn; /* number of records currently populated */ 104 u32 cap_rela_dyn; /* allocation capacity (records, not bytes) */ 105 106 /* .rela.plt — R_AARCH64_JUMP_SLOT, one per imported function. */ 107 LinkSectionId sec_rela_plt; 108 DynRela* rela_plt; 109 u32 nrela_plt; 110 111 /* .plt — 32-byte PLT0 stub + 16 bytes per imported function. Body 112 * is allocated (zero-initialized) but not emitted in Phase 4. */ 113 LinkSectionId sec_plt; 114 u32 nplt; /* number of imported functions */ 115 u64 plt_vaddr; /* image-relative .plt base */ 116 u64 plt_size; 117 118 /* .got.plt — 24 reserved bytes + 8 per PLT slot. */ 119 LinkSectionId sec_got_plt; 120 u64 got_plt_vaddr; 121 u64 got_plt_size; 122 123 /* .dynamic — PT_DYNAMIC body. Built at layout time; its size is 124 * fixed once we know the DT_NEEDED count. */ 125 LinkSectionId sec_dynamic; 126 u64 dynamic_vaddr; 127 u64 dynamic_size; 128 u32 ndyn_entries; 129 130 /* DT_NEEDED list (interned soname Syms, in input order). */ 131 Sym* needed; 132 u32 nneeded; 133 134 /* Per-import dynsym index, indexed by LinkSymId. 0 means "not 135 * imported / not in dynsym". Used by GLOB_DAT / JUMP_SLOT emit. */ 136 u32* sym_dynidx; /* size = sym_dynidx_size */ 137 u32 sym_dynidx_size; 138 139 /* Per-import PLT entry vaddr, indexed by LinkSymId (Phase 5). Set 140 * for every imported function: vaddr of its 16-byte PLT stub inside 141 * `.plt`. 0 means "no PLT stub" (symbol is data-only or not 142 * imported). apply_all_relocs reads this when redirecting a 143 * CALL26/JUMP26 against an imported function — S becomes the PLT 144 * entry vaddr instead of the symbol's (zero) vaddr. The vaddrs 145 * stored here track the post-shift values (shift_image_addresses 146 * bumps them along with .plt's segment vaddr). */ 147 u64* sym_plt_vaddr; /* size = sym_dynidx_size */ 148 } LinkDynState; 149 150 #endif