diff --git a/lib/std/c.zig b/lib/std/c.zig index 0a1337c16e2d..0bf9f40bb1aa 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -3971,7 +3971,7 @@ pub const dl_phdr_info = switch (native_os) { /// Module name. name: ?[*:0]const u8, /// Pointer to module's phdr. - phdr: [*]std.elf.Phdr, + phdr: [*]std.elf.ElfN.Phdr, /// Number of entries in phdr. phnum: u16, /// Total number of loads. @@ -3984,7 +3984,7 @@ pub const dl_phdr_info = switch (native_os) { .illumos => extern struct { addr: std.elf.Addr, name: ?[*:0]const u8, - phdr: [*]std.elf.Phdr, + phdr: [*]std.elf.ElfN.Phdr, phnum: std.elf.Half, /// Incremented when a new object is mapped into the process. adds: u64, @@ -3995,7 +3995,7 @@ pub const dl_phdr_info = switch (native_os) { .openbsd, .haiku, .dragonfly, .netbsd, .serenity => extern struct { addr: usize, name: ?[*:0]const u8, - phdr: [*]std.elf.Phdr, + phdr: [*]std.elf.ElfN.Phdr, phnum: std.elf.Half, }, else => void, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 21319b01d420..5036d401977f 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -441,11 +441,11 @@ const DlIterContext = struct { // Populate `build_id` and `gnu_eh_frame` for (info.phdr[0..info.phnum]) |phdr| { - switch (phdr.p_type) { - std.elf.PT_NOTE => { + switch (phdr.type) { + .NOTE => { // Look for .note.gnu.build-id - const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); - var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.p_memsz]); + const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.vaddr); + var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.memsz]); const name_size = r.takeInt(u32, native_endian) catch continue; const desc_size = r.takeInt(u32, native_endian) catch continue; const note_type = r.takeInt(u32, native_endian) catch continue; @@ -455,9 +455,9 @@ const DlIterContext = struct { const desc = r.take(desc_size) catch continue; build_id = desc; }, - std.elf.PT_GNU_EH_FRAME => { - const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); - gnu_eh_frame = segment_ptr[0..phdr.p_memsz]; + std.elf.PT.GNU_EH_FRAME => { + const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.vaddr); + gnu_eh_frame = segment_ptr[0..phdr.memsz]; }, else => {}, } @@ -478,11 +478,11 @@ const DlIterContext = struct { }); for (info.phdr[0..info.phnum]) |phdr| { - if (phdr.p_type != std.elf.PT_LOAD) continue; + if (phdr.type != .LOAD) continue; try context.si.ranges.append(gpa, .{ // Overflowing addition handles VSDOs having p_vaddr = 0xffffffffff700000 - .start = info.addr +% phdr.p_vaddr, - .len = phdr.p_memsz, + .start = info.addr +% phdr.vaddr, + .len = phdr.memsz, .module_index = module_index, }); } diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 90f655b95bc6..b889f5116e0d 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -92,8 +92,7 @@ pub fn get_DYNAMIC() ?[*]const elf.Dyn { }); } -pub fn linkmap_iterator(phdrs: []const elf.Phdr) error{InvalidExe}!LinkMap.Iterator { - _ = phdrs; +pub fn linkmap_iterator() error{InvalidExe}!LinkMap.Iterator { const _DYNAMIC = get_DYNAMIC() orelse { // No PT_DYNAMIC means this is a statically-linked non-PIE program. return .{ .current = null }; diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 2f00cef22c64..163bbf283c10 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -50,6 +50,7 @@ pub const AT_L2_CACHESIZE = 44; pub const AT_L2_CACHEGEOMETRY = 45; pub const AT_L3_CACHESIZE = 46; pub const AT_L3_CACHEGEOMETRY = 47; +pub const AT_MINSIGSTKSZ = 51; pub const DT_NULL = 0; pub const DT_NEEDED = 1; @@ -286,105 +287,111 @@ pub const VER_FLG_BASE = 1; /// Weak version identifier pub const VER_FLG_WEAK = 2; -/// Program header table entry unused -pub const PT_NULL = 0; -/// Loadable program segment -pub const PT_LOAD = 1; -/// Dynamic linking information -pub const PT_DYNAMIC = 2; -/// Program interpreter -pub const PT_INTERP = 3; -/// Auxiliary information -pub const PT_NOTE = 4; -/// Reserved -pub const PT_SHLIB = 5; -/// Entry for header table itself -pub const PT_PHDR = 6; -/// Thread-local storage segment -pub const PT_TLS = 7; -/// Number of defined types -pub const PT_NUM = 8; -/// Start of OS-specific -pub const PT_LOOS = 0x60000000; -/// GCC .eh_frame_hdr segment -pub const PT_GNU_EH_FRAME = 0x6474e550; -/// Indicates stack executability -pub const PT_GNU_STACK = 0x6474e551; -/// Read-only after relocation -pub const PT_GNU_RELRO = 0x6474e552; -pub const PT_LOSUNW = 0x6ffffffa; -/// Sun specific segment -pub const PT_SUNWBSS = 0x6ffffffa; -/// Stack segment -pub const PT_SUNWSTACK = 0x6ffffffb; -pub const PT_HISUNW = 0x6fffffff; -/// End of OS-specific -pub const PT_HIOS = 0x6fffffff; -/// Start of processor-specific -pub const PT_LOPROC = 0x70000000; -/// End of processor-specific -pub const PT_HIPROC = 0x7fffffff; +/// Deprecated, use `@intFromEnum(std.elf.PT.NULL)` +pub const PT_NULL = @intFromEnum(std.elf.PT.NULL); +/// Deprecated, use `@intFromEnum(std.elf.PT.LOAD)` +pub const PT_LOAD = @intFromEnum(std.elf.PT.LOAD); +/// Deprecated, use `@intFromEnum(std.elf.PT.DYNAMIC)` +pub const PT_DYNAMIC = @intFromEnum(std.elf.PT.DYNAMIC); +/// Deprecated, use `@intFromEnum(std.elf.PT.INTERP)` +pub const PT_INTERP = @intFromEnum(std.elf.PT.INTERP); +/// Deprecated, use `@intFromEnum(std.elf.PT.NOTE)` +pub const PT_NOTE = @intFromEnum(std.elf.PT.NOTE); +/// Deprecated, use `@intFromEnum(std.elf.PT.SHLIB)` +pub const PT_SHLIB = @intFromEnum(std.elf.PT.SHLIB); +/// Deprecated, use `@intFromEnum(std.elf.PT.PHDR)` +pub const PT_PHDR = @intFromEnum(std.elf.PT.PHDR); +/// Deprecated, use `@intFromEnum(std.elf.PT.TLS)` +pub const PT_TLS = @intFromEnum(std.elf.PT.TLS); +/// Deprecated, use `std.elf.PT.NUM`. +pub const PT_NUM = PT.NUM; +/// Deprecated, use `@intFromEnum(std.elf.PT.LOOS)` +pub const PT_LOOS = @intFromEnum(std.elf.PT.LOOS); +/// Deprecated, use `@intFromEnum(std.elf.PT.GNU_EH_FRAME)` +pub const PT_GNU_EH_FRAME = @intFromEnum(std.elf.PT.GNU_EH_FRAME); +/// Deprecated, use `@intFromEnum(std.elf.PT.GNU_STACK)` +pub const PT_GNU_STACK = @intFromEnum(std.elf.PT.GNU_STACK); +/// Deprecated, use `@intFromEnum(std.elf.PT.GNU_RELRO)` +pub const PT_GNU_RELRO = @intFromEnum(std.elf.PT.GNU_RELRO); +/// Deprecated, use `@intFromEnum(std.elf.PT.LOSUNW)` +pub const PT_LOSUNW = @intFromEnum(std.elf.PT.LOSUNW); +/// Deprecated, use `@intFromEnum(std.elf.PT.SUNWBSS)` +pub const PT_SUNWBSS = @intFromEnum(std.elf.PT.SUNWBSS); +/// Deprecated, use `@intFromEnum(std.elf.PT.SUNWSTACK)` +pub const PT_SUNWSTACK = @intFromEnum(std.elf.PT.SUNWSTACK); +/// Deprecated, use `@intFromEnum(std.elf.PT.HISUNW)` +pub const PT_HISUNW = @intFromEnum(std.elf.PT.HISUNW); +/// Deprecated, use `@intFromEnum(std.elf.PT.HIOS)` +pub const PT_HIOS = @intFromEnum(std.elf.PT.HIOS); +/// Deprecated, use `@intFromEnum(std.elf.PT.LOPROC)` +pub const PT_LOPROC = @intFromEnum(std.elf.PT.LOPROC); +/// Deprecated, use `@intFromEnum(std.elf.PT.HIPROC)` +pub const PT_HIPROC = @intFromEnum(std.elf.PT.HIPROC); pub const PN_XNUM = 0xffff; -/// Section header table entry unused -pub const SHT_NULL = 0; -/// Program data -pub const SHT_PROGBITS = 1; -/// Symbol table -pub const SHT_SYMTAB = 2; -/// String table -pub const SHT_STRTAB = 3; -/// Relocation entries with addends -pub const SHT_RELA = 4; -/// Symbol hash table -pub const SHT_HASH = 5; -/// Dynamic linking information -pub const SHT_DYNAMIC = 6; -/// Notes -pub const SHT_NOTE = 7; -/// Program space with no data (bss) -pub const SHT_NOBITS = 8; -/// Relocation entries, no addends -pub const SHT_REL = 9; -/// Reserved -pub const SHT_SHLIB = 10; -/// Dynamic linker symbol table -pub const SHT_DYNSYM = 11; -/// Array of constructors -pub const SHT_INIT_ARRAY = 14; -/// Array of destructors -pub const SHT_FINI_ARRAY = 15; -/// Array of pre-constructors -pub const SHT_PREINIT_ARRAY = 16; -/// Section group -pub const SHT_GROUP = 17; -/// Extended section indices -pub const SHT_SYMTAB_SHNDX = 18; -/// Start of OS-specific -pub const SHT_LOOS = 0x60000000; -/// LLVM address-significance table -pub const SHT_LLVM_ADDRSIG = 0x6fff4c03; -/// GNU hash table -pub const SHT_GNU_HASH = 0x6ffffff6; -/// GNU version definition table -pub const SHT_GNU_VERDEF = 0x6ffffffd; -/// GNU needed versions table -pub const SHT_GNU_VERNEED = 0x6ffffffe; -/// GNU symbol version table -pub const SHT_GNU_VERSYM = 0x6fffffff; -/// End of OS-specific -pub const SHT_HIOS = 0x6fffffff; -/// Start of processor-specific -pub const SHT_LOPROC = 0x70000000; -/// Unwind information -pub const SHT_X86_64_UNWIND = 0x70000001; -/// End of processor-specific -pub const SHT_HIPROC = 0x7fffffff; -/// Start of application-specific -pub const SHT_LOUSER = 0x80000000; -/// End of application-specific -pub const SHT_HIUSER = 0xffffffff; +/// Deprecated, use `@intFromEnum(std.elf.SHT.NULL)` +pub const SHT_NULL = @intFromEnum(std.elf.SHT.NULL); +/// Deprecated, use `@intFromEnum(std.elf.SHT.PROGBITS)` +pub const SHT_PROGBITS = @intFromEnum(std.elf.SHT.PROGBITS); +/// Deprecated, use `@intFromEnum(std.elf.SHT.SYMTAB)` +pub const SHT_SYMTAB = @intFromEnum(std.elf.SHT.SYMTAB); +/// Deprecated, use `@intFromEnum(std.elf.SHT.STRTAB)` +pub const SHT_STRTAB = @intFromEnum(std.elf.SHT.STRTAB); +/// Deprecated, use `@intFromEnum(std.elf.SHT.RELA)` +pub const SHT_RELA = @intFromEnum(std.elf.SHT.RELA); +/// Deprecated, use `@intFromEnum(std.elf.SHT.HASH)` +pub const SHT_HASH = @intFromEnum(std.elf.SHT.HASH); +/// Deprecated, use `@intFromEnum(std.elf.SHT.DYNAMIC)` +pub const SHT_DYNAMIC = @intFromEnum(std.elf.SHT.DYNAMIC); +/// Deprecated, use `@intFromEnum(std.elf.SHT.NOTE)` +pub const SHT_NOTE = @intFromEnum(std.elf.SHT.NOTE); +/// Deprecated, use `@intFromEnum(std.elf.SHT.NOBITS)` +pub const SHT_NOBITS = @intFromEnum(std.elf.SHT.NOBITS); +/// Deprecated, use `@intFromEnum(std.elf.SHT.REL)` +pub const SHT_REL = @intFromEnum(std.elf.SHT.REL); +/// Deprecated, use `@intFromEnum(std.elf.SHT.SHLIB)` +pub const SHT_SHLIB = @intFromEnum(std.elf.SHT.SHLIB); +/// Deprecated, use `@intFromEnum(std.elf.SHT.DYNSYM)` +pub const SHT_DYNSYM = @intFromEnum(std.elf.SHT.DYNSYM); +/// Deprecated, use `@intFromEnum(std.elf.SHT.INIT_ARRAY)` +pub const SHT_INIT_ARRAY = @intFromEnum(std.elf.SHT.INIT_ARRAY); +/// Deprecated, use `@intFromEnum(std.elf.SHT.FINI_ARRAY)` +pub const SHT_FINI_ARRAY = @intFromEnum(std.elf.SHT.FINI_ARRAY); +/// Deprecated, use `@intFromEnum(std.elf.SHT.PREINIT_ARRAY)` +pub const SHT_PREINIT_ARRAY = @intFromEnum(std.elf.SHT.PREINIT_ARRAY); +/// Deprecated, use `@intFromEnum(std.elf.SHT.GROUP)` +pub const SHT_GROUP = @intFromEnum(std.elf.SHT.GROUP); +/// Deprecated, use `@intFromEnum(std.elf.SHT.SYMTAB_SHNDX)` +pub const SHT_SYMTAB_SHNDX = @intFromEnum(std.elf.SHT.SYMTAB_SHNDX); +/// Deprecated, use `@intFromEnum(std.elf.SHT.RELR)` +pub const SHT_RELR = @intFromEnum(std.elf.SHT.RELR); +/// Deprecated, use `std.elf.SHT.NUM`. +pub const SHT_NUM = SHT.NUM; +/// Deprecated, use `@intFromEnum(std.elf.SHT.LOOS)` +pub const SHT_LOOS = @intFromEnum(std.elf.SHT.LOOS); +/// Deprecated, use `@intFromEnum(std.elf.SHT.LLVM_ADDRSIG)` +pub const SHT_LLVM_ADDRSIG = @intFromEnum(std.elf.SHT.LLVM_ADDRSIG); +/// Deprecated, use `@intFromEnum(std.elf.SHT.GNU_HASH)` +pub const SHT_GNU_HASH = @intFromEnum(std.elf.SHT.GNU_HASH); +/// Deprecated, use `@intFromEnum(std.elf.SHT.GNU_VERDEF)` +pub const SHT_GNU_VERDEF = @intFromEnum(std.elf.SHT.GNU_VERDEF); +/// Deprecated, use `@intFromEnum(std.elf.SHT.GNU_VERNEED)` +pub const SHT_GNU_VERNEED = @intFromEnum(std.elf.SHT.GNU_VERNEED); +/// Deprecated, use `@intFromEnum(std.elf.SHT.GNU_VERSYM)` +pub const SHT_GNU_VERSYM = @intFromEnum(std.elf.SHT.GNU_VERSYM); +/// Deprecated, use `@intFromEnum(std.elf.SHT.HIOS)` +pub const SHT_HIOS = @intFromEnum(std.elf.SHT.HIOS); +/// Deprecated, use `@intFromEnum(std.elf.SHT.LOPROC)` +pub const SHT_LOPROC = @intFromEnum(std.elf.SHT.LOPROC); +/// Deprecated, use `@intFromEnum(std.elf.SHT.X86_64_UNWIND)` +pub const SHT_X86_64_UNWIND = @intFromEnum(std.elf.SHT.X86_64_UNWIND); +/// Deprecated, use `@intFromEnum(std.elf.SHT.HIPROC)` +pub const SHT_HIPROC = @intFromEnum(std.elf.SHT.HIPROC); +/// Deprecated, use `@intFromEnum(std.elf.SHT.LOUSER)` +pub const SHT_LOUSER = @intFromEnum(std.elf.SHT.LOUSER); +/// Deprecated, use `@intFromEnum(std.elf.SHT.HIUSER)` +pub const SHT_HIUSER = @intFromEnum(std.elf.SHT.HIUSER); // Note type for .note.gnu.build_id pub const NT_GNU_BUILD_ID = 3; @@ -454,6 +461,127 @@ pub const STT_ARM_TFUNC = @intFromEnum(STT.ARM_TFUNC); /// Deprecated, use `@intFromEnum(std.elf.STT.ARM_16BIT)` pub const STT_ARM_16BIT = @intFromEnum(STT.ARM_16BIT); +pub const PT = enum(Word) { + /// Program header table entry unused + NULL = 0, + /// Loadable program segment + LOAD = 1, + /// Dynamic linking information + DYNAMIC = 2, + /// Program interpreter + INTERP = 3, + /// Auxiliary information + NOTE = 4, + /// Reserved + SHLIB = 5, + /// Entry for header table itself + PHDR = 6, + /// Thread-local storage segment + TLS = 7, + _, + + /// Number of defined types + pub const NUM = @typeInfo(PT).@"enum".fields.len; + + /// Start of OS-specific + pub const LOOS: PT = @enumFromInt(0x60000000); + /// End of OS-specific + pub const HIOS: PT = @enumFromInt(0x6fffffff); + + /// GCC .eh_frame_hdr segment + pub const GNU_EH_FRAME: PT = @enumFromInt(0x6474e550); + /// Indicates stack executability + pub const GNU_STACK: PT = @enumFromInt(0x6474e551); + /// Read-only after relocation + pub const GNU_RELRO: PT = @enumFromInt(0x6474e552); + + pub const LOSUNW: PT = @enumFromInt(0x6ffffffa); + pub const HISUNW: PT = @enumFromInt(0x6fffffff); + + /// Sun specific segment + pub const SUNWBSS: PT = @enumFromInt(0x6ffffffa); + /// Stack segment + pub const SUNWSTACK: PT = @enumFromInt(0x6ffffffb); + + /// Start of processor-specific + pub const LOPROC: PT = @enumFromInt(0x70000000); + /// End of processor-specific + pub const HIPROC: PT = @enumFromInt(0x7fffffff); +}; + +pub const SHT = enum(Word) { + /// Section header table entry unused + NULL = 0, + /// Program data + PROGBITS = 1, + /// Symbol table + SYMTAB = 2, + /// String table + STRTAB = 3, + /// Relocation entries with addends + RELA = 4, + /// Symbol hash table + HASH = 5, + /// Dynamic linking information + DYNAMIC = 6, + /// Notes + NOTE = 7, + /// Program space with no data (bss) + NOBITS = 8, + /// Relocation entries, no addends + REL = 9, + /// Reserved + SHLIB = 10, + /// Dynamic linker symbol table + DYNSYM = 11, + /// Array of constructors + INIT_ARRAY = 14, + /// Array of destructors + FINI_ARRAY = 15, + /// Array of pre-constructors + PREINIT_ARRAY = 16, + /// Section group + GROUP = 17, + /// Extended section indices + SYMTAB_SHNDX = 18, + /// RELR relative relocations + RELR = 19, + _, + + /// Number of defined types + pub const NUM = @typeInfo(SHT).@"enum".fields.len; + + /// Start of OS-specific + pub const LOOS: SHT = @enumFromInt(0x60000000); + /// End of OS-specific + pub const HIOS: SHT = @enumFromInt(0x6fffffff); + + /// LLVM address-significance table + pub const LLVM_ADDRSIG: SHT = @enumFromInt(0x6fff4c03); + + /// GNU hash table + pub const GNU_HASH: SHT = @enumFromInt(0x6ffffff6); + /// GNU version definition table + pub const GNU_VERDEF: SHT = @enumFromInt(0x6ffffffd); + /// GNU needed versions table + pub const GNU_VERNEED: SHT = @enumFromInt(0x6ffffffe); + /// GNU symbol version table + pub const GNU_VERSYM: SHT = @enumFromInt(0x6fffffff); + + /// Start of processor-specific + pub const LOPROC: SHT = @enumFromInt(0x70000000); + /// End of processor-specific + pub const HIPROC: SHT = @enumFromInt(0x7fffffff); + + /// Unwind information + pub const X86_64_UNWIND: SHT = @enumFromInt(0x70000001); + + /// Start of application-specific + pub const LOUSER: SHT = @enumFromInt(0x80000000); + /// End of application-specific + pub const HIUSER: SHT = @enumFromInt(0xffffffff); +}; + pub const STB = enum(u4) { /// Local symbol LOCAL = 0, @@ -899,7 +1027,7 @@ pub const Elf32 = struct { shstrndx: Half, }; pub const Phdr = extern struct { - type: Word, + type: PT, offset: Elf32.Off, vaddr: Elf32.Addr, paddr: Elf32.Addr, @@ -910,7 +1038,7 @@ pub const Elf32 = struct { }; pub const Shdr = extern struct { name: Word, - type: Word, + type: SHT, flags: packed struct { shf: SHF }, addr: Elf32.Addr, offset: Elf32.Off, @@ -989,7 +1117,7 @@ pub const Elf64 = struct { shstrndx: Half, }; pub const Phdr = extern struct { - type: Word, + type: PT, flags: PF, offset: Elf64.Off, vaddr: Elf64.Addr, @@ -1000,7 +1128,7 @@ pub const Elf64 = struct { }; pub const Shdr = extern struct { name: Word, - type: Word, + type: SHT, flags: packed struct { shf: SHF, unused: Word = 0 }, addr: Elf64.Addr, offset: Elf64.Off, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 882de1f4584f..acb588bcaeb8 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -6101,7 +6101,7 @@ pub const dirent64 = extern struct { pub const dl_phdr_info = extern struct { addr: usize, name: ?[*:0]const u8, - phdr: [*]std.elf.Phdr, + phdr: [*]std.elf.ElfN.Phdr, phnum: u16, }; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 40dfe641549f..97c5c1b204cb 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -5048,6 +5048,13 @@ pub fn nanosleep(seconds: u64, nanoseconds: u64) void { } } +pub fn getSelfPhdrs() []std.elf.ElfN.Phdr { + const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval; + assert(getauxval(std.elf.AT_PHENT) == @sizeOf(std.elf.ElfN.Phdr)); + const phdrs: [*]std.elf.ElfN.Phdr = @ptrFromInt(getauxval(std.elf.AT_PHDR)); + return phdrs[0..getauxval(std.elf.AT_PHNUM)]; +} + pub fn dl_iterate_phdr( context: anytype, comptime Error: type, @@ -5075,34 +5082,24 @@ pub fn dl_iterate_phdr( } } - const elf_base = std.process.getBaseAddress(); - const ehdr: *elf.Ehdr = @ptrFromInt(elf_base); - // Make sure the base address points to an ELF image. - assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC)); - const n_phdr = ehdr.e_phnum; - const phdrs = (@as([*]elf.Phdr, @ptrFromInt(elf_base + ehdr.e_phoff)))[0..n_phdr]; - - var it = dl.linkmap_iterator(phdrs) catch unreachable; + var it = dl.linkmap_iterator() catch unreachable; // The executable has no dynamic link segment, create a single entry for // the whole ELF image. if (it.end()) { - // Find the base address for the ELF image, if this is a PIE the value - // is non-zero. - const base_address = for (phdrs) |*phdr| { - if (phdr.p_type == elf.PT_PHDR) { - break @intFromPtr(phdrs.ptr) - phdr.p_vaddr; - // We could try computing the difference between _DYNAMIC and - // the p_vaddr of the PT_DYNAMIC section, but using the phdr is - // good enough (Is it?). - } - } else unreachable; - - var info = dl_phdr_info{ - .addr = base_address, - .name = "/proc/self/exe", + const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval; + const phdrs = getSelfPhdrs(); + var info: dl_phdr_info = .{ + .addr = for (phdrs) |phdr| switch (phdr.type) { + .PHDR => break @intFromPtr(phdrs.ptr) - phdr.vaddr, + else => {}, + } else unreachable, + .name = switch (getauxval(std.elf.AT_EXECFN)) { + 0 => "/proc/self/exe", + else => |name| @ptrFromInt(name), + }, .phdr = phdrs.ptr, - .phnum = ehdr.e_phnum, + .phnum = @intCast(phdrs.len), }; return callback(&info, @sizeOf(dl_phdr_info), context); @@ -5110,24 +5107,18 @@ pub fn dl_iterate_phdr( // Last return value from the callback function. while (it.next()) |entry| { - var phdr: [*]elf.Phdr = undefined; - var phnum: u16 = undefined; - - if (entry.l_addr != 0) { - const elf_header: *elf.Ehdr = @ptrFromInt(entry.l_addr); - phdr = @ptrFromInt(entry.l_addr + elf_header.e_phoff); - phnum = elf_header.e_phnum; - } else { - // This is the running ELF image - phdr = @ptrFromInt(elf_base + ehdr.e_phoff); - phnum = ehdr.e_phnum; - } - - var info = dl_phdr_info{ + const phdrs: []elf.ElfN.Phdr = if (entry.l_addr != 0) phdrs: { + const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.l_addr); + assert(mem.eql(u8, ehdr.ident[0..4], elf.MAGIC)); + const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.l_addr + ehdr.phoff); + break :phdrs phdrs[0..ehdr.phnum]; + } else getSelfPhdrs(); + + var info: dl_phdr_info = .{ .addr = entry.l_addr, .name = entry.l_name, - .phdr = phdr, - .phnum = phnum, + .phdr = phdrs.ptr, + .phnum = @intCast(phdrs.len), }; try callback(&info, @sizeOf(dl_phdr_info), context); diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index d58bf306778b..b1daf11730b5 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -257,11 +257,11 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void { while (i < info.phnum) : (i += 1) { const phdr = info.phdr[i]; - if (phdr.p_type != elf.PT_LOAD) continue; + if (phdr.type != .LOAD) continue; - const reloc_addr = info.addr + phdr.p_vaddr; + const reloc_addr = info.addr + phdr.vaddr; // Find the ELF header - const elf_header = @as(*elf.Ehdr, @ptrFromInt(reloc_addr - phdr.p_offset)); + const elf_header = @as(*elf.Ehdr, @ptrFromInt(reloc_addr - phdr.offset)); // Validate the magic if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic; // Consistency check diff --git a/lib/std/process.zig b/lib/std/process.zig index eb748e3b3ee4..ee81bdad2631 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1658,13 +1658,13 @@ fn posixGetUserInfoPasswdStream(name: []const u8, reader: *std.Io.Reader) !UserI pub fn getBaseAddress() usize { switch (native_os) { .linux => { - const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval; - const base = getauxval(std.elf.AT_BASE); - if (base != 0) { - return base; - } - const phdr = getauxval(std.elf.AT_PHDR); - return phdr - @sizeOf(std.elf.Ehdr); + const phdrs = std.posix.getSelfPhdrs(); + var base: usize = 0; + for (phdrs) |phdr| switch (phdr.type) { + .LOAD => return base + phdr.vaddr, + .PHDR => base = @intFromPtr(phdrs.ptr) - phdr.vaddr, + else => {}, + } else unreachable; }, .driverkit, .ios, .macos, .tvos, .visionos, .watchos => { return @intFromPtr(&std.c._mh_execute_header); diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index e73017f7c057..01a74e5f3eee 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -3,6 +3,7 @@ options: link.File.OpenOptions, mf: MappedFile, ni: Node.Known, nodes: std.MultiArrayList(Node), +shdrs: std.ArrayList(Section), phdrs: std.ArrayList(MappedFile.Node.Index), si: Symbol.Known, symtab: std.ArrayList(Symbol), @@ -163,9 +164,29 @@ pub const Node = union(enum) { } }; +pub const Section = struct { + si: Symbol.Index, + rela_si: Symbol.Index, + rela_free: RelIndex, + + pub const RelIndex = enum(u32) { + none, + _, + + pub fn wrap(i: ?u32) RelIndex { + return @enumFromInt((i orelse return .none) + 1); + } + pub fn unwrap(ri: RelIndex) ?u32 { + return switch (ri) { + .none => null, + else => @intFromEnum(ri) - 1, + }; + } + }; +}; + pub const StringTable = struct { map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage), - size: u32, const Context = struct { slice: []const u8, @@ -193,14 +214,10 @@ pub const StringTable = struct { } }; - pub fn get( - st: *StringTable, - gpa: std.mem.Allocator, - mf: *MappedFile, - ni: MappedFile.Node.Index, - key: []const u8, - ) !u32 { - const slice_const = ni.sliceConst(mf); + pub fn get(st: *StringTable, elf: *Elf, si: Symbol.Index, key: []const u8) !u32 { + const gpa = elf.base.comp.gpa; + const ni = si.node(elf); + const slice_const = ni.sliceConst(&elf.mf); const gop = try st.map.getOrPutContextAdapted( gpa, key, @@ -208,11 +225,18 @@ pub const StringTable = struct { .{ .slice = slice_const }, ); if (gop.found_existing) return gop.key_ptr.*; - const old_size = st.size; - const new_size: u32 = @intCast(old_size + key.len + 1); - st.size = new_size; - try ni.resize(mf, gpa, new_size); - const slice = ni.slice(mf)[old_size..]; + const old_size, const new_size = size: switch (elf.shdrPtr(si.shndx(elf))) { + inline else => |shdr| { + const old_size: u32 = @intCast(elf.targetLoad(&shdr.size)); + const new_size: u32 = @intCast(old_size + key.len + 1); + elf.targetStore(&shdr.size, new_size); + break :size .{ old_size, new_size }; + }, + }; + _, const node_size = ni.location(&elf.mf).resolve(&elf.mf); + if (new_size > node_size) + try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor); + const slice = ni.slice(&elf.mf)[old_size..]; @memcpy(slice[0..key.len], key); slice[key.len] = 0; gop.key_ptr.* = old_size; @@ -226,7 +250,7 @@ pub const Symbol = struct { loc_relocs: Reloc.Index, /// Relocations targeting this symbol target_relocs: Reloc.Index, - unused: u32 = 0, + unused: u32, pub const Index = enum(u32) { null, @@ -252,6 +276,52 @@ pub const Symbol = struct { return @enumFromInt(@intFromEnum(si) + 1); } + pub const Shndx = enum(Tag) { + UNDEF = std.elf.SHN_UNDEF, + LIVEPATCH = reserve(std.elf.SHN_LIVEPATCH), + ABS = reserve(std.elf.SHN_ABS), + COMMON = reserve(std.elf.SHN_COMMON), + _, + + pub const Tag = u32; + + pub const LORESERVE: Shndx = .fromSection(std.elf.SHN_LORESERVE); + pub const HIRESERVE: Shndx = .fromSection(std.elf.SHN_HIRESERVE); + comptime { + assert(@intFromEnum(HIRESERVE) == std.math.maxInt(Tag)); + } + + fn reserve(sec: std.elf.Section) Tag { + assert(sec >= std.elf.SHN_LORESERVE and sec <= std.elf.SHN_HIRESERVE); + return @as(Tag, std.math.maxInt(Tag) - std.elf.SHN_HIRESERVE) + sec; + } + + pub fn fromSection(sec: std.elf.Section) Shndx { + return switch (sec) { + std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @enumFromInt(sec), + std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @enumFromInt(reserve(sec)), + }; + } + pub fn toSection(s: Shndx) ?std.elf.Section { + return switch (@intFromEnum(s)) { + std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => |sec| @intCast(sec), + std.elf.SHN_LORESERVE...reserve(std.elf.SHN_LORESERVE) - 1 => null, + reserve(std.elf.SHN_LORESERVE)...reserve(std.elf.SHN_HIRESERVE) => |sec| @intCast( + sec - reserve(std.elf.SHN_LORESERVE) + std.elf.SHN_LORESERVE, + ), + }; + } + + pub fn get(s: Shndx, elf: *Elf) *Section { + return &elf.shdrs.items[@intFromEnum(s)]; + } + }; + pub fn shndx(si: Symbol.Index, elf: *Elf) Shndx { + return .fromSection(switch (elf.symPtr(si)) { + inline else => |sym| elf.targetLoad(&sym.shndx), + }); + } + pub const InitOptions = struct { name: []const u8 = "", lib_name: ?[]const u8 = null, @@ -260,48 +330,83 @@ pub const Symbol = struct { type: std.elf.STT, bind: std.elf.STB = .LOCAL, visibility: std.elf.STV = .DEFAULT, - shndx: std.elf.Section = std.elf.SHN_UNDEF, + shndx: Shndx = .UNDEF, }; pub fn init(si: Symbol.Index, elf: *Elf, opts: InitOptions) !void { const gpa = elf.base.comp.gpa; const target_endian = elf.targetEndian(); - const sym_size: usize = switch (elf.identClass()) { - .NONE, _ => unreachable, - inline else => |class| @sizeOf(class.ElfN().Sym), - }; const name_strtab_entry = try elf.string(.strtab, opts.name); - try elf.si.symtab.node(elf).resize(&elf.mf, gpa, sym_size * elf.symtab.items.len); + switch (elf.shdrPtr(elf.si.symtab.shndx(elf))) { + inline else => |shdr| { + const old_size = elf.targetLoad(&shdr.size); + const ent_size = elf.targetLoad(&shdr.entsize); + const new_size = ent_size * elf.symtab.items.len; + if (new_size > old_size) { + elf.targetStore(&shdr.size, @intCast(new_size)); + const symtab_ni = elf.si.symtab.node(elf); + _, const node_size = symtab_ni.location(&elf.mf).resolve(&elf.mf); + if (new_size > node_size) try symtab_ni.resize( + &elf.mf, + gpa, + new_size +| new_size / MappedFile.growth_factor, + ); + } + }, + } switch (elf.symPtr(si)) { inline else => |sym, class| { + const Sym = class.ElfN().Sym; sym.* = .{ .name = name_strtab_entry, .value = @intCast(opts.value), .size = @intCast(opts.size), .info = .{ .type = opts.type, .bind = opts.bind }, .other = .{ .visibility = opts.visibility }, - .shndx = opts.shndx, + .shndx = opts.shndx.toSection().?, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(class.ElfN().Sym, sym); + if (target_endian != native_endian) std.mem.byteSwapAllFields(Sym, sym); }, } + switch (elf.shdrPtr(elf.si.symtab.shndx(elf))) { + inline else => |shdr| elf.targetStore(&shdr.info, @max( + elf.targetLoad(&shdr.info), + @intFromEnum(si) + 1, + )), + } if (opts.bind == .LOCAL or elf.si.dynsym == .null) return; const dsi = elf.dynsym.items.len; try elf.dynsym.append(gpa, si); - const dynsym_ni = elf.si.dynsym.node(elf); const name_dynstr_entry = try elf.string(.dynstr, opts.name); - try dynsym_ni.resize(&elf.mf, gpa, sym_size * elf.dynsym.items.len); + switch (elf.shdrPtr(elf.si.dynsym.shndx(elf))) { + inline else => |shdr| { + const old_size = elf.targetLoad(&shdr.size); + const ent_size = elf.targetLoad(&shdr.entsize); + const new_size = ent_size * elf.dynsym.items.len; + if (new_size > old_size) { + elf.targetStore(&shdr.size, @intCast(new_size)); + const dynsym_ni = elf.si.dynsym.node(elf); + _, const node_size = dynsym_ni.location(&elf.mf).resolve(&elf.mf); + if (new_size > node_size) try dynsym_ni.resize( + &elf.mf, + gpa, + new_size +| new_size / MappedFile.growth_factor, + ); + } + }, + } switch (elf.dynsymSlice()) { - inline else => |dynsym, class| { - const dsym = &dynsym[dsi]; - dsym.* = .{ + inline else => |dynsyms, class| { + const Sym = class.ElfN().Sym; + const dynsym = &dynsyms[dsi]; + dynsym.* = .{ .name = name_dynstr_entry, .value = @intCast(opts.value), .size = @intCast(opts.size), .info = .{ .type = opts.type, .bind = opts.bind }, .other = .{ .visibility = opts.visibility }, - .shndx = opts.shndx, + .shndx = opts.shndx.toSection().?, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(class.ElfN().Sym, dsym); + if (target_endian != native_endian) std.mem.byteSwapAllFields(Sym, dynsym); }, } } @@ -321,6 +426,7 @@ pub const Symbol = struct { } pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void { + if (elf.ehdrField(.type) == .REL) return; switch (si.get(elf).loc_relocs) { .none => {}, else => |loc_relocs| for (elf.relocs.items[@intFromEnum(loc_relocs)..]) |*reloc| { @@ -331,6 +437,7 @@ pub const Symbol = struct { } pub fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void { + if (elf.ehdrField(.type) == .REL) return; var ri = si.get(elf).target_relocs; while (ri != .none) { const reloc = ri.get(elf); @@ -374,7 +481,7 @@ pub const Reloc = extern struct { next: Reloc.Index, loc: Symbol.Index, target: Symbol.Index, - unused: u32, + index: Section.RelIndex, offset: u64, addend: i64, @@ -384,6 +491,16 @@ pub const Reloc = extern struct { RISCV: std.elf.R_RISCV, PPC64: std.elf.R_PPC64, + pub fn none(elf: *Elf) Reloc.Type { + return switch (elf.ehdrField(.machine)) { + else => unreachable, + .AARCH64 => .{ .AARCH64 = .NONE }, + .PPC64 => .{ .PPC64 = .NONE }, + .RISCV => .{ .RISCV = .NONE }, + .X86_64 => .{ .X86_64 = .NONE }, + }; + } + pub fn absAddr(elf: *Elf) Reloc.Type { return switch (elf.ehdrField(.machine)) { else => unreachable, @@ -393,12 +510,35 @@ pub const Reloc = extern struct { .X86_64 => .{ .X86_64 = .@"64" }, }; } + pub fn sizeAddr(elf: *Elf) Reloc.Type { return switch (elf.ehdrField(.machine)) { else => unreachable, .X86_64 => .{ .X86_64 = .SIZE64 }, }; } + + pub fn wrap(int: u32, elf: *Elf) Reloc.Type { + return switch (elf.ehdrField(.machine)) { + else => unreachable, + inline .AARCH64, + .PPC64, + .RISCV, + .X86_64, + => |machine| @unionInit(Reloc.Type, @tagName(machine), @enumFromInt(int)), + }; + } + + pub fn unwrap(rt: Reloc.Type, elf: *Elf) u32 { + return switch (elf.ehdrField(.machine)) { + else => unreachable, + inline .AARCH64, + .PPC64, + .RISCV, + .X86_64, + => |machine| @intFromEnum(@field(rt, @tagName(machine))), + }; + } }; pub const Index = enum(u32) { @@ -411,6 +551,7 @@ pub const Reloc = extern struct { }; pub fn apply(reloc: *const Reloc, elf: *Elf) void { + assert(elf.ehdrField(.type) != .REL); const loc_ni = reloc.loc.get(elf).ni; switch (loc_ni) { .none => return, @@ -462,7 +603,7 @@ pub const Reloc = extern struct { .TPOFF32 => { const phdr = @field(elf.phdrSlice(), @tagName(class)); const ph = &phdr[elf.getNode(elf.ni.tls).segment]; - assert(elf.targetLoad(&ph.type) == std.elf.PT_TLS); + assert(elf.targetLoad(&ph.type) == .TLS); std.mem.writeInt( i32, loc_slice[0..4], @@ -501,6 +642,33 @@ pub const Reloc = extern struct { .none => {}, else => |next| next.get(elf).prev = reloc.prev, } + switch (elf.ehdrField(.type)) { + .NONE, .CORE, _ => unreachable, + .REL => { + const sh = reloc.loc.shndx(elf).get(elf); + switch (elf.shdrPtr(sh.rela_si.shndx(elf))) { + inline else => |shdr, class| { + const Rela = class.ElfN().Rela; + const ent_size = elf.targetLoad(&shdr.entsize); + const start = ent_size * reloc.index.unwrap().?; + const rela_slice = sh.rela_si.node(elf).slice(&elf.mf); + const rela: *Rela = @ptrCast(@alignCast( + rela_slice[@intCast(start)..][0..@intCast(ent_size)], + )); + rela.* = .{ + .offset = @intFromEnum(sh.rela_free), + .info = .{ + .type = @intCast(Reloc.Type.none(elf).unwrap(elf)), + .sym = 0, + }, + .addend = 0, + }; + }, + } + sh.rela_free = reloc.index; + }, + .EXEC, .DYN => assert(reloc.index == .none), + } reloc.* = undefined; } @@ -601,6 +769,7 @@ fn create( .tls = .none, }, .nodes = .empty, + .shdrs = .empty, .phdrs = .empty, .si = .{ .dynsym = .null, @@ -611,16 +780,13 @@ fn create( .symtab = .empty, .shstrtab = .{ .map = .empty, - .size = 1, }, .strtab = .{ .map = .empty, - .size = 1, }, .dynsym = .empty, .dynstr = .{ .map = .empty, - .size = 1, }, .needed = .empty, .inputs = .empty, @@ -650,6 +816,7 @@ pub fn deinit(elf: *Elf) void { const gpa = elf.base.comp.gpa; elf.mf.deinit(gpa); elf.nodes.deinit(gpa); + elf.shdrs.deinit(gpa); elf.phdrs.deinit(gpa); elf.symtab.deinit(gpa); elf.shstrtab.map.deinit(gpa); @@ -681,11 +848,10 @@ fn initHeaders( const comp = elf.base.comp; const gpa = comp.gpa; const have_dynamic_section = switch (@"type") { - .NONE => unreachable, + .NONE, .CORE, _ => unreachable, .REL => false, .EXEC => comp.config.link_mode == .dynamic, .DYN => true, - .CORE, _ => unreachable, }; const addr_align: std.mem.Alignment = switch (class) { .NONE, _ => unreachable, @@ -693,6 +859,7 @@ fn initHeaders( .@"64" => .@"8", }; + const shnum: u32 = 1; var phnum: u32 = 0; const phdr_phndx = phnum; phnum += 1; @@ -715,8 +882,17 @@ fn initHeaders( break :phndx phnum; } else undefined; - const expected_nodes_len = 5 + phnum * 2 + @as(usize, 2) * @intFromBool(have_dynamic_section); + const expected_nodes_len = expected_nodes_len: switch (@"type") { + .NONE, .CORE, _ => unreachable, + .REL => { + defer phnum = 0; + break :expected_nodes_len 5 + phnum; + }, + .EXEC, .DYN => break :expected_nodes_len 5 + phnum * 2 + + @as(usize, 2) * @intFromBool(have_dynamic_section), + }; try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len); + try elf.shdrs.ensureTotalCapacity(gpa, shnum); try elf.phdrs.resize(gpa, phnum); elf.nodes.appendAssumeCapacity(.file); @@ -751,7 +927,7 @@ fn initHeaders( ehdr.phentsize = @sizeOf(ElfN.Phdr); ehdr.phnum = @min(phnum, std.elf.PN_XNUM); ehdr.shentsize = @sizeOf(ElfN.Shdr); - ehdr.shnum = 1; + ehdr.shnum = if (shnum < std.elf.SHN_LORESERVE) shnum else 0; ehdr.shstrndx = std.elf.SHN_UNDEF; if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); }, @@ -759,181 +935,187 @@ fn initHeaders( assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ .size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum), - .alignment = addr_align, + .alignment = elf.mf.flags.block_size, .moved = true, .resized = true, })); elf.nodes.appendAssumeCapacity(.shdr); - assert(elf.ni.rodata == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ - .alignment = elf.mf.flags.block_size, - .moved = true, - .bubbles_moved = false, - })); - elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx }); - elf.phdrs.items[rodata_phndx] = elf.ni.rodata; + var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: { + assert(elf.ni.rodata == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ + .alignment = elf.mf.flags.block_size, + .moved = true, + .bubbles_moved = false, + })); + elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx }); + elf.phdrs.items[rodata_phndx] = elf.ni.rodata; - assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ - .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum), - .alignment = addr_align, - .moved = true, - .resized = true, - .bubbles_moved = false, - })); - elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx }); - elf.phdrs.items[phdr_phndx] = elf.ni.phdr; + assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ + .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum), + .alignment = addr_align, + .moved = true, + .resized = true, + .bubbles_moved = false, + })); + elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx }); + elf.phdrs.items[phdr_phndx] = elf.ni.phdr; - assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ - .alignment = elf.mf.flags.block_size, - .moved = true, - .bubbles_moved = false, - })); - elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx }); - elf.phdrs.items[text_phndx] = elf.ni.text; + assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ + .alignment = elf.mf.flags.block_size, + .moved = true, + .bubbles_moved = false, + })); + elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx }); + elf.phdrs.items[text_phndx] = elf.ni.text; - assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ - .alignment = elf.mf.flags.block_size, - .moved = true, - .bubbles_moved = false, - })); - elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx }); - elf.phdrs.items[data_phndx] = elf.ni.data; - - var ph_vaddr: u32 = switch (elf.ehdrField(.type)) { - else => 0, - .EXEC => switch (elf.ehdrField(.machine)) { - .@"386" => 0x400000, - .AARCH64, .X86_64 => 0x200000, - .PPC, .PPC64 => 0x10000000, - .S390, .S390_OLD => 0x1000000, - .OLD_SPARCV9, .SPARCV9 => 0x100000, - else => 0x10000, - }, - }; + assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ + .alignment = elf.mf.flags.block_size, + .moved = true, + .bubbles_moved = false, + })); + elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx }); + elf.phdrs.items[data_phndx] = elf.ni.data; + + break :ph_vaddr switch (elf.ehdrField(.type)) { + .NONE, .CORE, _ => unreachable, + .REL, .DYN => 0, + .EXEC => switch (elf.ehdrField(.machine)) { + .@"386" => 0x400000, + .AARCH64, .X86_64 => 0x200000, + .PPC, .PPC64 => 0x10000000, + .S390, .S390_OLD => 0x1000000, + .OLD_SPARCV9, .SPARCV9 => 0x100000, + else => 0x10000, + }, + }; + } else undefined; switch (class) { .NONE, _ => unreachable, inline else => |ct_class| { const ElfN = ct_class.ElfN(); const target_endian = elf.targetEndian(); - const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf))); - const ph_phdr = &phdr[phdr_phndx]; - ph_phdr.* = .{ - .type = std.elf.PT_PHDR, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true }, - .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr); - - if (maybe_interp) |_| { - const ph_interp = &phdr[interp_phndx]; - ph_interp.* = .{ - .type = std.elf.PT_INTERP, + if (@"type" != .REL) { + const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf))); + const ph_phdr = &phdr[phdr_phndx]; + ph_phdr.* = .{ + .type = .PHDR, .offset = 0, .vaddr = 0, .paddr = 0, .filesz = 0, .memsz = 0, .flags = .{ .R = true }, - .@"align" = 1, + .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()), }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp); - } - - _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf); - const ph_rodata = &phdr[rodata_phndx]; - ph_rodata.* = .{ - .type = std.elf.PT_NULL, - .offset = 0, - .vaddr = ph_vaddr, - .paddr = ph_vaddr, - .filesz = @intCast(rodata_size), - .memsz = @intCast(rodata_size), - .flags = .{ .R = true }, - .@"align" = @intCast(elf.ni.rodata.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata); - ph_vaddr += @intCast(rodata_size); - - _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf); - const ph_text = &phdr[text_phndx]; - ph_text.* = .{ - .type = std.elf.PT_NULL, - .offset = 0, - .vaddr = ph_vaddr, - .paddr = ph_vaddr, - .filesz = @intCast(text_size), - .memsz = @intCast(text_size), - .flags = .{ .R = true, .X = true }, - .@"align" = @intCast(elf.ni.text.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text); - ph_vaddr += @intCast(text_size); + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr); + + if (maybe_interp) |_| { + const ph_interp = &phdr[interp_phndx]; + ph_interp.* = .{ + .type = .INTERP, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true }, + .@"align" = 1, + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp); + } - _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf); - const ph_data = &phdr[data_phndx]; - ph_data.* = .{ - .type = std.elf.PT_NULL, - .offset = 0, - .vaddr = ph_vaddr, - .paddr = ph_vaddr, - .filesz = @intCast(data_size), - .memsz = @intCast(data_size), - .flags = .{ .R = true, .W = true }, - .@"align" = @intCast(elf.ni.data.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data); - ph_vaddr += @intCast(data_size); + _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf); + const ph_rodata = &phdr[rodata_phndx]; + ph_rodata.* = .{ + .type = if (rodata_size == 0) .NULL else .LOAD, + .offset = 0, + .vaddr = ph_vaddr, + .paddr = ph_vaddr, + .filesz = @intCast(rodata_size), + .memsz = @intCast(rodata_size), + .flags = .{ .R = true }, + .@"align" = @intCast(elf.ni.rodata.alignment(&elf.mf).toByteUnits()), + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata); + ph_vaddr += @intCast(rodata_size); - if (have_dynamic_section) { - const ph_dynamic = &phdr[dynamic_phndx]; - ph_dynamic.* = .{ - .type = std.elf.PT_DYNAMIC, + _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf); + const ph_text = &phdr[text_phndx]; + ph_text.* = .{ + .type = if (text_size == 0) .NULL else .LOAD, .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true, .W = true }, - .@"align" = @intCast(addr_align.toByteUnits()), + .vaddr = ph_vaddr, + .paddr = ph_vaddr, + .filesz = @intCast(text_size), + .memsz = @intCast(text_size), + .flags = .{ .R = true, .X = true }, + .@"align" = @intCast(elf.ni.text.alignment(&elf.mf).toByteUnits()), }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic); - } + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text); + ph_vaddr += @intCast(text_size); - if (comp.config.any_non_single_threaded) { - const ph_tls = &phdr[tls_phndx]; - ph_tls.* = .{ - .type = std.elf.PT_TLS, + _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf); + const ph_data = &phdr[data_phndx]; + ph_data.* = .{ + .type = if (data_size == 0) .NULL else .LOAD, .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true }, - .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()), + .vaddr = ph_vaddr, + .paddr = ph_vaddr, + .filesz = @intCast(data_size), + .memsz = @intCast(data_size), + .flags = .{ .R = true, .W = true }, + .@"align" = @intCast(elf.ni.data.alignment(&elf.mf).toByteUnits()), }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls); + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data); + ph_vaddr += @intCast(data_size); + + if (have_dynamic_section) { + const ph_dynamic = &phdr[dynamic_phndx]; + ph_dynamic.* = .{ + .type = .DYNAMIC, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true, .W = true }, + .@"align" = @intCast(addr_align.toByteUnits()), + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic); + } + + if (comp.config.any_non_single_threaded) { + const ph_tls = &phdr[tls_phndx]; + ph_tls.* = .{ + .type = .TLS, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true }, + .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()), + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls); + } } - const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf))); - sh_null.* = .{ + const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf))); + sh_undef.* = .{ .name = try elf.string(.shstrtab, ""), - .type = std.elf.SHT_NULL, + .type = .NULL, .flags = .{ .shf = .{} }, .addr = 0, .offset = 0, - .size = 0, + .size = if (shnum < std.elf.SHN_LORESERVE) 0 else shnum, .link = 0, - .info = if (phnum >= std.elf.PN_XNUM) phnum else 0, + .info = if (phnum < std.elf.PN_XNUM) 0 else phnum, .addralign = 0, .entsize = 0, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null); + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef); + elf.shdrs.appendAssumeCapacity(.{ .si = .null, .rela_si = .null, .rela_free = .none }); try elf.symtab.ensureTotalCapacity(gpa, 1); elf.symtab.addOneAssumeCapacity().* = .{ @@ -943,10 +1125,11 @@ fn initHeaders( .unused = 0, }; assert(elf.si.symtab == try elf.addSection(elf.ni.file, .{ - .type = std.elf.SHT_SYMTAB, + .type = .SYMTAB, .size = @sizeOf(ElfN.Sym) * 1, .addralign = addr_align, .entsize = @sizeOf(ElfN.Sym), + .node_align = elf.mf.flags.block_size, })); const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class)); symtab_null.* = .{ @@ -960,13 +1143,14 @@ fn initHeaders( if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null); const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class)); - ehdr.shstrndx = ehdr.shnum; + elf.targetStore(&ehdr.shstrndx, ehdr.shnum); }, } assert(elf.si.shstrtab == try elf.addSection(elf.ni.file, .{ - .type = std.elf.SHT_STRTAB, - .addralign = elf.mf.flags.block_size, + .type = .STRTAB, + .size = 1, .entsize = 1, + .node_align = elf.mf.flags.block_size, })); try elf.renameSection(.symtab, ".symtab"); try elf.renameSection(.shstrtab, ".shstrtab"); @@ -974,10 +1158,10 @@ fn initHeaders( assert(elf.si.strtab == try elf.addSection(elf.ni.file, .{ .name = ".strtab", - .type = std.elf.SHT_STRTAB, + .type = .STRTAB, .size = 1, - .addralign = elf.mf.flags.block_size, .entsize = 1, + .node_align = elf.mf.flags.block_size, })); try elf.linkSections(.symtab, .strtab); elf.si.strtab.node(elf).slice(&elf.mf)[0] = 0; @@ -997,87 +1181,96 @@ fn initHeaders( .flags = .{ .WRITE = true, .ALLOC = true }, .addralign = elf.mf.flags.block_size, })); - if (maybe_interp) |interp| { - const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{ - .size = interp.len + 1, - .moved = true, - .resized = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx }); - elf.phdrs.items[interp_phndx] = interp_ni; - - const sec_interp_si = try elf.addSection(interp_ni, .{ - .name = ".interp", - .flags = .{ .ALLOC = true }, - .size = @intCast(interp.len + 1), - }); - const sec_interp = sec_interp_si.node(elf).slice(&elf.mf); - @memcpy(sec_interp[0..interp.len], interp); - sec_interp[interp.len] = 0; - } - if (have_dynamic_section) { - const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data, .{ - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx }); - elf.phdrs.items[dynamic_phndx] = dynamic_ni; - - switch (class) { - .NONE, _ => unreachable, - inline else => |ct_class| { - const ElfN = ct_class.ElfN(); - elf.si.dynsym = try elf.addSection(elf.ni.rodata, .{ - .name = ".dynsym", - .type = std.elf.SHT_DYNSYM, - .size = @sizeOf(ElfN.Sym) * 1, - .addralign = addr_align, - .entsize = @sizeOf(ElfN.Sym), - }); - const dynsym_null = &@field(elf.dynsymSlice(), @tagName(ct_class))[0]; - dynsym_null.* = .{ - .name = try elf.string(.dynstr, ""), - .value = 0, - .size = 0, - .info = .{ .type = .NOTYPE, .bind = .LOCAL }, - .other = .{ .visibility = .DEFAULT }, - .shndx = std.elf.SHN_UNDEF, - }; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Sym, dynsym_null); - }, + if (@"type" != .REL) { + if (maybe_interp) |interp| { + const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{ + .size = interp.len + 1, + .moved = true, + .resized = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx }); + elf.phdrs.items[interp_phndx] = interp_ni; + + const sec_interp_si = try elf.addSection(interp_ni, .{ + .type = .PROGBITS, + .name = ".interp", + .flags = .{ .ALLOC = true }, + .size = @intCast(interp.len + 1), + }); + const sec_interp = sec_interp_si.node(elf).slice(&elf.mf); + @memcpy(sec_interp[0..interp.len], interp); + sec_interp[interp.len] = 0; } - elf.si.dynstr = try elf.addSection(elf.ni.rodata, .{ - .name = ".dynstr", - .type = std.elf.SHT_STRTAB, - .size = 1, - .addralign = elf.mf.flags.block_size, - .entsize = 1, - }); - elf.si.dynamic = try elf.addSection(dynamic_ni, .{ - .name = ".dynamic", - .type = std.elf.SHT_DYNAMIC, - .flags = .{ .ALLOC = true, .WRITE = true }, - .addralign = addr_align, - }); - try elf.linkSections(elf.si.dynamic, elf.si.dynstr); - try elf.linkSections(elf.si.dynsym, elf.si.dynstr); - } - if (comp.config.any_non_single_threaded) { - elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{ - .alignment = elf.mf.flags.block_size, - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); - elf.phdrs.items[tls_phndx] = elf.ni.tls; + if (have_dynamic_section) { + const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data, .{ + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx }); + elf.phdrs.items[dynamic_phndx] = dynamic_ni; - elf.si.tdata = try elf.addSection(elf.ni.tls, .{ - .name = ".tdata", - .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, - .addralign = elf.mf.flags.block_size, - }); + switch (class) { + .NONE, _ => unreachable, + inline else => |ct_class| { + const Sym = ct_class.ElfN().Sym; + elf.si.dynsym = try elf.addSection(elf.ni.rodata, .{ + .name = ".dynsym", + .type = .DYNSYM, + .size = @sizeOf(Sym) * 1, + .addralign = addr_align, + .entsize = @sizeOf(Sym), + .node_align = elf.mf.flags.block_size, + }); + const dynsym_null = &@field(elf.dynsymSlice(), @tagName(ct_class))[0]; + dynsym_null.* = .{ + .name = try elf.string(.dynstr, ""), + .value = 0, + .size = 0, + .info = .{ .type = .NOTYPE, .bind = .LOCAL }, + .other = .{ .visibility = .DEFAULT }, + .shndx = std.elf.SHN_UNDEF, + }; + if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields( + Sym, + dynsym_null, + ); + }, + } + elf.si.dynstr = try elf.addSection(elf.ni.rodata, .{ + .name = ".dynstr", + .type = .STRTAB, + .size = 1, + .entsize = 1, + .node_align = elf.mf.flags.block_size, + }); + elf.si.dynamic = try elf.addSection(dynamic_ni, .{ + .name = ".dynamic", + .type = .DYNAMIC, + .flags = .{ .ALLOC = true, .WRITE = true }, + .node_align = addr_align, + }); + try elf.linkSections(elf.si.dynamic, elf.si.dynstr); + try elf.linkSections(elf.si.dynsym, elf.si.dynstr); + } + if (comp.config.any_non_single_threaded) { + elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{ + .alignment = elf.mf.flags.block_size, + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); + elf.phdrs.items[tls_phndx] = elf.ni.tls; + } + } else { + assert(maybe_interp == null); + assert(!have_dynamic_section); } + if (comp.config.any_non_single_threaded) elf.si.tdata = try elf.addSection(elf.ni.tls, .{ + .name = ".tdata", + .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, + .addralign = elf.mf.flags.block_size, + }); assert(elf.nodes.len == expected_nodes_len); } @@ -1116,7 +1309,7 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { .file => return 0, .ehdr, .shdr => unreachable, .segment => |phndx| break :parent_vaddr switch (elf.phdrSlice()) { - inline else => |ph| elf.targetLoad(&ph[phndx].vaddr), + inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr), }, .section => |si| si, .input_section => unreachable, @@ -1202,6 +1395,7 @@ pub const PhdrSlice = union(std.elf.CLASS) { @"64": []std.elf.Elf64.Phdr, }; pub fn phdrSlice(elf: *Elf) PhdrSlice { + assert(elf.ehdrField(.type) != .REL); const slice = elf.ni.phdr.slice(&elf.mf); return switch (elf.identClass()) { .NONE, _ => unreachable, @@ -1230,6 +1424,17 @@ pub fn shdrSlice(elf: *Elf) ShdrSlice { }; } +pub const ShdrPtr = union(std.elf.CLASS) { + NONE: noreturn, + @"32": *std.elf.Elf32.Shdr, + @"64": *std.elf.Elf64.Shdr, +}; +pub fn shdrPtr(elf: *Elf, shndx: Symbol.Index.Shndx) ShdrPtr { + return switch (elf.shdrSlice()) { + inline else => |shdrs, class| @unionInit(ShdrPtr, @tagName(class), &shdrs[@intFromEnum(shndx)]), + }; +} + pub const SymtabSlice = union(std.elf.CLASS) { NONE: noreturn, @"32": []std.elf.Elf32.Sym, @@ -1242,7 +1447,11 @@ pub fn symtabSlice(elf: *Elf) SymtabSlice { inline else => |class| @unionInit( SymtabSlice, @tagName(class), - @ptrCast(@alignCast(slice)), + @ptrCast(@alignCast(slice[0..std.mem.alignBackwardAnyAlign( + usize, + slice.len, + @sizeOf(class.ElfN().Sym), + )])), ), }; } @@ -1254,7 +1463,7 @@ pub const SymPtr = union(std.elf.CLASS) { }; pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr { return switch (elf.symtabSlice()) { - inline else => |sym, class| @unionInit(SymPtr, @tagName(class), &sym[@intFromEnum(si)]), + inline else => |syms, class| @unionInit(SymPtr, @tagName(class), &syms[@intFromEnum(si)]), }; } @@ -1544,7 +1753,7 @@ fn loadObject( .si = try elf.initSymbolAssumeCapacity(.{ .name = std.fs.path.stem(member orelse path.sub_path), .type = .FILE, - .shndx = std.elf.SHN_ABS, + .shndx = .ABS, }), }; const target_endian = elf.targetEndian(); @@ -1571,7 +1780,7 @@ fn loadObject( }; try r.discardAll(ehdr.shentsize); switch (section.shdr.type) { - std.elf.SHT_NULL, std.elf.SHT_NOBITS => {}, + .NULL, .NOBITS => {}, else => if (section.shdr.offset + section.shdr.size > fl.size) return diags.failParse(path, "bad section location", .{}), } @@ -1580,8 +1789,7 @@ fn loadObject( if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) return diags.failParse(path, "missing section names", .{}); const shdr = §ions[ehdr.shstrndx].shdr; - if (shdr.type != std.elf.SHT_STRTAB) - return diags.failParse(path, "invalid shstrtab type", .{}); + if (shdr.type != .STRTAB) return diags.failParse(path, "invalid shstrtab type", .{}); const shstrtab = try gpa.alloc(u8, @intCast(shdr.size)); errdefer gpa.free(shstrtab); try fr.seekTo(fl.offset + shdr.offset); @@ -1594,7 +1802,7 @@ fn loadObject( try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); for (sections[1..]) |*section| switch (section.shdr.type) { else => {}, - std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => { + .PROGBITS, .NOBITS => { if (section.shdr.name >= shstrtab.len) continue; const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); const parent_si = elf.namedSection(name) orelse continue; @@ -1611,7 +1819,7 @@ fn loadObject( }); section.si = try elf.initSymbolAssumeCapacity(.{ .type = .SECTION, - .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx), + .shndx = parent_si.shndx(elf), }); section.si.get(elf).ni = ni; elf.input_sections.addOneAssumeCapacity().* = .{ @@ -1629,14 +1837,14 @@ fn loadObject( defer symmap.deinit(gpa); for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { else => {}, - std.elf.SHT_SYMTAB => { + .SYMTAB => { if (symtab.shdr.entsize < @sizeOf(ElfN.Sym)) return diags.failParse(path, "unsupported symtab entsize", .{}); const strtab = strtab: { if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum) return diags.failParse(path, "missing symbol names", .{}); const shdr = §ions[symtab.shdr.link].shdr; - if (shdr.type != std.elf.SHT_STRTAB) + if (shdr.type != .STRTAB) return diags.failParse(path, "invalid strtab type", .{}); const strtab = try gpa.alloc(u8, @intCast(shdr.size)); errdefer gpa.free(strtab); @@ -1683,9 +1891,7 @@ fn loadObject( .type = input_sym.info.type, .bind = input_sym.info.bind, .visibility = input_sym.other.visibility, - .shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) { - inline else => |parent_sym| &parent_sym.shndx, - }), + .shndx = parent_si.shndx(elf), }); si.get(elf).ni = parent_si.get(elf).ni; switch (input_sym.info.bind) { @@ -1719,13 +1925,13 @@ fn loadObject( } for (sections[1..]) |*rels| switch (rels.shdr.type) { else => {}, - inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| { + inline .REL, .RELA => |sht| { if (rels.shdr.link != symtab_shndx or rels.shdr.info == std.elf.SHN_UNDEF or rels.shdr.info >= ehdr.shnum) continue; const Rel = switch (sht) { else => comptime unreachable, - std.elf.SHT_REL => ElfN.Rel, - std.elf.SHT_RELA => ElfN.Rela, + .REL => ElfN.Rel, + .RELA => ElfN.Rela, }; if (rels.shdr.entsize < @sizeOf(Rel)) return diags.failParse(path, "unsupported rel entsize", .{}); @@ -1740,7 +1946,7 @@ fn loadObject( "relocation section size (0x{x}) is not a multiple of entsize (0x{x})", .{ rels.shdr.size, rels.shdr.entsize }, ); - try elf.relocs.ensureUnusedCapacity(gpa, relnum); + try elf.ensureUnusedRelocCapacity(loc_sec.si, relnum); try fr.seekTo(fl.offset + rels.shdr.offset); for (0..relnum) |_| { const rel = try r.peekStruct(Rel, target_endian); @@ -1753,18 +1959,7 @@ fn loadObject( rel.offset - loc_sec.shdr.addr, target_si, rel.addend, - switch (elf.ehdrField(.machine)) { - else => unreachable, - inline .AARCH64, - .PPC64, - .RISCV, - .X86_64, - => |machine| @unionInit( - Reloc.Type, - @tagName(machine), - @enumFromInt(rel.info.type), - ), - }, + .wrap(rel.info.type, elf), ); } }, @@ -1800,7 +1995,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *std.Io.File.Reader) !void try r.discardAll(ehdr.phentsize); switch (ph.type) { else => {}, - std.elf.PT_DYNAMIC => break ph, + .DYNAMIC => break ph, } } else return diags.failParse(path, "no dynamic segment", .{}); const dynnum = std.math.divExact( @@ -1817,13 +2012,13 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *std.Io.File.Reader) !void var soname: ?ElfN.Addr = null; try fr.seekTo(dynamic_ph.offset); for (0..dynnum) |_| { - const key = try r.takeInt(ElfN.Addr, target_endian); - const value = try r.takeInt(ElfN.Addr, target_endian); - switch (key) { + const tag = try r.takeInt(ElfN.Addr, target_endian); + const val = try r.takeInt(ElfN.Addr, target_endian); + switch (tag) { else => {}, - std.elf.DT_STRTAB => strtab = value, - std.elf.DT_STRSZ => strsz = value, - std.elf.DT_SONAME => soname = value, + std.elf.DT_STRTAB => strtab = val, + std.elf.DT_STRSZ => strsz = val, + std.elf.DT_SONAME => soname = val, } } if (strtab == null or soname == null) @@ -1836,7 +2031,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *std.Io.File.Reader) !void try r.discardAll(ehdr.phentsize); switch (ph.type) { else => {}, - std.elf.PT_LOAD => if (strtab.? >= ph.vaddr and + .LOAD => if (strtab.? >= ph.vaddr and strtab.? + (strsz orelse 0) <= ph.vaddr + ph.filesz) break ph, } } else return diags.failParse(path, "strtab not part of a loaded segment", .{}); @@ -1868,11 +2063,7 @@ fn prelinkInner(elf: *Elf) !void { std.fs.path.stem(elf.base.emit.sub_path), }); defer gpa.free(zcu_name); - const si = try elf.initSymbolAssumeCapacity(.{ - .name = zcu_name, - .type = .FILE, - .shndx = std.elf.SHN_ABS, - }); + const si = try elf.initSymbolAssumeCapacity(.{ .name = zcu_name, .type = .FILE, .shndx = .ABS }); elf.inputs.addOneAssumeCapacity().* = .{ .path = elf.base.emit, .member = null, @@ -1972,63 +2163,87 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.In fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { name: []const u8 = "", - type: std.elf.Word = std.elf.SHT_NULL, + type: std.elf.SHT = .NULL, flags: std.elf.SHF = .{}, - size: std.elf.Word = 0, + size: std.elf.Xword = 0, + link: std.elf.Word = 0, + info: std.elf.Word = 0, addralign: std.mem.Alignment = .@"1", entsize: std.elf.Word = 0, + node_align: std.mem.Alignment = .@"1", }) !Symbol.Index { switch (opts.type) { - std.elf.SHT_NULL => assert(opts.size == 0), - std.elf.SHT_PROGBITS => assert(opts.size > 0), + .NULL => assert(opts.size == 0), + .PROGBITS => assert(opts.size > 0), else => {}, } const gpa = elf.base.comp.gpa; try elf.nodes.ensureUnusedCapacity(gpa, 1); + try elf.shdrs.ensureUnusedCapacity(gpa, 1); try elf.symtab.ensureUnusedCapacity(gpa, 1); const shstrtab_entry = try elf.string(.shstrtab, opts.name); - const shndx, const shdr_size = shndx: switch (elf.ehdrPtr()) { - inline else => |ehdr| { - const shndx = elf.targetLoad(&ehdr.shnum); - const shnum = shndx + 1; - elf.targetStore(&ehdr.shnum, shnum); - break :shndx .{ shndx, elf.targetLoad(&ehdr.shentsize) * shnum }; + const shndx: Symbol.Index.Shndx, const new_shdr_size = shndx: switch (elf.ehdrPtr()) { + inline else => |ehdr, class| { + const shndx, const shnum = alloc_shndx: switch (elf.targetLoad(&ehdr.shnum)) { + 1...std.elf.SHN_LORESERVE - 2 => |shndx| { + const shnum = shndx + 1; + elf.targetStore(&ehdr.shnum, shnum); + break :alloc_shndx .{ shndx, shnum }; + }, + std.elf.SHN_LORESERVE - 1 => |shndx| { + const shnum = shndx + 1; + elf.targetStore(&ehdr.shnum, 0); + elf.targetStore(&@field(elf.shdrPtr(.UNDEF), @tagName(class)).size, shnum); + break :alloc_shndx .{ shndx, shnum }; + }, + std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => unreachable, + 0 => { + const shnum_ptr = &@field(elf.shdrPtr(.UNDEF), @tagName(class)).size; + const shndx: u32 = @intCast(elf.targetLoad(shnum_ptr)); + const shnum = shndx + 1; + elf.targetStore(shnum_ptr, shnum); + break :alloc_shndx .{ shndx, shnum }; + }, + }; + assert(shndx < @intFromEnum(Symbol.Index.Shndx.LORESERVE)); + break :shndx .{ @enumFromInt(shndx), elf.targetLoad(&ehdr.shentsize) * shnum }; }, }; - try elf.ni.shdr.resize(&elf.mf, gpa, shdr_size); - const ni = try elf.mf.addLastChildNode(gpa, segment_ni, .{ - .alignment = opts.addralign, + _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf); + if (new_shdr_size > shdr_node_size) + try elf.ni.shdr.resize(&elf.mf, gpa, new_shdr_size +| new_shdr_size / MappedFile.growth_factor); + const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrField(.type)) { + .NONE, .CORE, _ => unreachable, + .REL => elf.ni.file, + .EXEC, .DYN => segment_ni, + }, .{ + .alignment = opts.addralign.max(opts.node_align), .size = opts.size, .resized = opts.size > 0, }); const si = elf.addSymbolAssumeCapacity(); elf.nodes.appendAssumeCapacity(.{ .section = si }); + elf.shdrs.appendAssumeCapacity(.{ .si = si, .rela_si = .null, .rela_free = .none }); si.get(elf).ni = ni; const addr = elf.computeNodeVAddr(ni); const offset = ni.fileLocation(&elf.mf, false).offset; - try si.init(elf, .{ - .value = addr, - .size = opts.size, - .type = .SECTION, - .shndx = shndx, - }); - switch (elf.shdrSlice()) { - inline else => |shdr| { - const sh = &shdr[shndx]; - sh.* = .{ + try si.init(elf, .{ .value = addr, .type = .SECTION, .shndx = shndx }); + switch (elf.shdrPtr(shndx)) { + inline else => |shdr, class| { + shdr.* = .{ .name = shstrtab_entry, .type = opts.type, .flags = .{ .shf = opts.flags }, .addr = @intCast(addr), .offset = @intCast(offset), - .size = opts.size, - .link = 0, - .info = 0, + .size = @intCast(opts.size), + .link = opts.link, + .info = opts.info, .addralign = @intCast(opts.addralign.toByteUnits()), .entsize = opts.entsize, }; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh); + if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr); }, } return si; @@ -2036,39 +2251,27 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void { const shstrtab_entry = try elf.string(.shstrtab, name); - switch (elf.shdrSlice()) { - inline else => |shdr, class| elf.targetStore( - &shdr[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].name, - shstrtab_entry, - ), + switch (elf.shdrPtr(si.shndx(elf))) { + inline else => |shdr| elf.targetStore(&shdr.name, shstrtab_entry), } } fn linkSections(elf: *Elf, si: Symbol.Index, link_si: Symbol.Index) !void { - switch (elf.shdrSlice()) { - inline else => |shdr, class| shdr[ - elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx) - ].link = @field(elf.symPtr(link_si), @tagName(class)).shndx, + switch (elf.shdrPtr(si.shndx(elf))) { + inline else => |shdr| elf.targetStore(&shdr.link, @intFromEnum(link_si.shndx(elf))), } } fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 { - const name = elf.si.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrSlice()) { - inline else => |shndx, class| elf.targetLoad( - &shndx[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].name, - ), + const name = elf.si.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrPtr(si.shndx(elf))) { + inline else => |shdr| elf.targetLoad(&shdr.name), }..]; return name[0..std.mem.indexOfScalar(u8, name, 0).? :0]; } fn string(elf: *Elf, comptime section: enum { shstrtab, strtab, dynstr }, key: []const u8) !u32 { if (key.len == 0) return 0; - return @field(elf, @tagName(section)).get( - elf.base.comp.gpa, - &elf.mf, - @field(elf.si, @tagName(section)).node(elf), - key, - ); + return @field(elf, @tagName(section)).get(elf, @field(elf.si, @tagName(section)), key); } pub fn addReloc( @@ -2079,9 +2282,51 @@ pub fn addReloc( addend: i64, @"type": Reloc.Type, ) !void { - try elf.relocs.ensureUnusedCapacity(elf.base.comp.gpa, 1); + try elf.ensureUnusedRelocCapacity(loc_si, 1); elf.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type"); } +pub fn ensureUnusedRelocCapacity(elf: *Elf, loc_si: Symbol.Index, len: usize) !void { + if (len == 0) return; + const gpa = elf.base.comp.gpa; + + try elf.relocs.ensureUnusedCapacity(gpa, len); + if (elf.ehdrField(.type) != .REL) return; + + const shndx = loc_si.shndx(elf); + const sh = shndx.get(elf); + if (sh.rela_si == .null) { + var stack = std.heap.stackFallback(32, gpa); + const allocator = stack.get(); + + const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{elf.sectionName(sh.si)}); + defer allocator.free(rela_name); + + const class = elf.identClass(); + sh.rela_si = try elf.addSection(.none, .{ + .name = rela_name, + .type = .RELA, + .link = @intFromEnum(elf.si.symtab.shndx(elf)), + .info = @intFromEnum(shndx), + .addralign = switch (class) { + .NONE, _ => unreachable, + .@"32" => .@"4", + .@"64" => .@"8", + }, + .entsize = switch (class) { + .NONE, _ => unreachable, + inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela), + }, + .node_align = elf.mf.flags.block_size, + }); + } + const rela_ni = sh.rela_si.node(elf); + _, const rela_node_size = rela_ni.location(&elf.mf).resolve(&elf.mf); + const rela_size = switch (elf.shdrPtr(sh.rela_si.shndx(elf))) { + inline else => |shdr| elf.targetLoad(&shdr.size) + elf.targetLoad(&shdr.entsize) * len, + }; + if (rela_size > rela_node_size) + try rela_ni.resize(&elf.mf, gpa, rela_size +| rela_size / MappedFile.growth_factor); +} pub fn addRelocAssumeCapacity( elf: *Elf, loc_si: Symbol.Index, @@ -2098,7 +2343,45 @@ pub fn addRelocAssumeCapacity( .next = target.target_relocs, .loc = loc_si, .target = target_si, - .unused = 0, + .index = index: switch (elf.ehdrField(.type)) { + .NONE, .CORE, _ => unreachable, + .REL => { + const sh = loc_si.shndx(elf).get(elf); + switch (elf.shdrPtr(sh.rela_si.shndx(elf))) { + inline else => |shdr, class| { + const Rela = class.ElfN().Rela; + const ent_size = elf.targetLoad(&shdr.entsize); + const rela_slice = sh.rela_si.node(elf).slice(&elf.mf); + const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: { + const rela: *Rela = @ptrCast(@alignCast( + rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], + )); + sh.rela_free = @enumFromInt(rela.offset); + break :alloc_index index; + } else alloc_index: { + const old_size = elf.targetLoad(&shdr.size); + const new_size = old_size + ent_size; + elf.targetStore(&shdr.size, @intCast(new_size)); + break :alloc_index @intCast(@divExact(old_size, ent_size)); + }; + const rela: *Rela = @ptrCast(@alignCast( + rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)], + )); + rela.* = .{ + .offset = @intCast(offset), + .info = .{ + .type = @intCast(@"type".unwrap(elf)), + .sym = @intCast(@intFromEnum(target_si)), + }, + .addend = @intCast(addend), + }; + if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(Rela, rela); + break :index .wrap(index); + }, + } + }, + .EXEC, .DYN => .none, + }, .offset = offset, .addend = addend, }; @@ -2464,7 +2747,8 @@ fn flushUav( switch (sym.ni) { .none => { try elf.nodes.ensureUnusedCapacity(gpa, 1); - const ni = try elf.mf.addLastChildNode(gpa, elf.si.data.node(elf), .{ + const sec_si = elf.si.data; + const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{ .alignment = uav_align.toStdMem(), .moved = true, }); @@ -2472,7 +2756,7 @@ fn flushUav( sym.ni = ni; switch (elf.symPtr(si)) { inline else => |sym_ptr, class| sym_ptr.shndx = - @field(elf.symPtr(.data), @tagName(class)).shndx, + @field(elf.symPtr(sec_si), @tagName(class)).shndx, } }, else => { @@ -2596,11 +2880,10 @@ fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void { var child_it = ni.children(&elf.mf); while (child_it.next()) |child_ni| try elf.flushFileOffset(child_ni); }, - .section => |si| switch (elf.shdrSlice()) { - inline else => |shdr, class| elf.targetStore( - &shdr[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].offset, - @intCast(ni.fileLocation(&elf.mf, false).offset), - ), + .section => |si| switch (elf.shdrPtr(si.shndx(elf))) { + inline else => |shdr| elf.targetStore(&shdr.offset, @intCast( + ni.fileLocation(&elf.mf, false).offset, + )), }, } } @@ -2616,10 +2899,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { const ph = &phdr[phndx]; switch (elf.targetLoad(&ph.type)) { else => unreachable, - std.elf.PT_NULL, std.elf.PT_LOAD => return, - std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {}, - std.elf.PT_PHDR => @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset, - std.elf.PT_TLS => {}, + .NULL, .LOAD => return, + .DYNAMIC, .INTERP => {}, + .PHDR => @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset, + .TLS => {}, } elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni))); ph.paddr = ph.vaddr; @@ -2629,14 +2912,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { .section => |si| { try elf.flushFileOffset(ni); const addr = elf.computeNodeVAddr(ni); - switch (elf.shdrSlice()) { + switch (elf.shdrPtr(si.shndx(elf))) { inline else => |shdr, class| { - const sym = @field(elf.symPtr(si), @tagName(class)); - const sh = &shdr[elf.targetLoad(&sym.shndx)]; - const flags = elf.targetLoad(&sh.flags).shf; + const flags = elf.targetLoad(&shdr.flags).shf; if (flags.ALLOC) { - elf.targetStore(&sh.addr, @intCast(addr)); - sym.value = sh.addr; + elf.targetStore(&shdr.addr, @intCast(addr)); + @field(elf.symPtr(si), @tagName(class)).value = shdr.addr; } }, } @@ -2684,22 +2965,18 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { elf.targetStore(&ph.memsz, @intCast(memsz)); switch (elf.targetLoad(&ph.type)) { else => unreachable, - std.elf.PT_NULL => if (size > 0) elf.targetStore(&ph.type, std.elf.PT_LOAD), - std.elf.PT_LOAD => if (size == 0) elf.targetStore(&ph.type, std.elf.PT_NULL), - std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => return, - std.elf.PT_TLS => return ni.childrenMoved(elf.base.comp.gpa, &elf.mf), + .NULL => if (size > 0) elf.targetStore(&ph.type, .LOAD), + .LOAD => if (size == 0) elf.targetStore(&ph.type, .NULL), + .DYNAMIC, .INTERP, .PHDR => return, + .TLS => return ni.childrenMoved(elf.base.comp.gpa, &elf.mf), } var vaddr = elf.targetLoad(&ph.vaddr); var new_phndx = phndx; for (phdr[phndx + 1 ..], phndx + 1..) |*next_ph, next_phndx| { switch (elf.targetLoad(&next_ph.type)) { else => unreachable, - std.elf.PT_NULL, std.elf.PT_LOAD => {}, - std.elf.PT_DYNAMIC, - std.elf.PT_INTERP, - std.elf.PT_PHDR, - std.elf.PT_TLS, - => break, + .NULL, .LOAD => {}, + .DYNAMIC, .INTERP, .PHDR, .TLS => break, } const next_vaddr = elf.targetLoad(&next_ph.vaddr); if (vaddr + memsz <= next_vaddr) break; @@ -2721,20 +2998,15 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { } }, }, - .section => |si| switch (elf.symPtr(si)) { - inline else => |sym, class| { - const sh = &@field(elf.shdrSlice(), @tagName(class))[elf.targetLoad(&sym.shndx)]; - elf.targetStore(&sh.size, @intCast(size)); - switch (elf.targetLoad(&sh.type)) { + .section => |si| switch (elf.shdrPtr(si.shndx(elf))) { + inline else => |shdr| { + switch (elf.targetLoad(&shdr.type)) { else => unreachable, - std.elf.SHT_NULL => if (size > 0) elf.targetStore(&sh.type, std.elf.SHT_PROGBITS), - std.elf.SHT_PROGBITS => if (size == 0) elf.targetStore(&sh.type, std.elf.SHT_NULL), - std.elf.SHT_SYMTAB, std.elf.SHT_DYNSYM => elf.targetStore( - &sh.info, - @intCast(@divExact(size, elf.targetLoad(&sh.entsize))), - ), - std.elf.SHT_STRTAB, std.elf.SHT_DYNAMIC => {}, + .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), + .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), + .SYMTAB, .STRTAB, .RELA, .DYNAMIC, .REL, .DYNSYM => return, } + elf.targetStore(&shdr.size, @intCast(size)); }, }, .input_section => {}, @@ -2860,6 +3132,26 @@ pub fn printNode( try w.writeAll(@tagName(node)); switch (node) { else => {}, + .segment => |phndx| switch (elf.phdrSlice()) { + inline else => |phdr| { + const ph = &phdr[phndx]; + try w.writeByte('('); + const pt = elf.targetLoad(&ph.type); + if (std.enums.tagName(std.elf.PT, pt)) |pt_name| + try w.writeAll(pt_name) + else inline for (@typeInfo(std.elf.PT).@"enum".decls) |decl| { + const decl_val = @field(std.elf.PT, decl.name); + if (@TypeOf(decl_val) != std.elf.PT) continue; + if (pt == @field(std.elf.PT, decl.name)) break try w.writeAll(decl.name); + } else try w.print("0x{x}", .{pt}); + try w.writeAll(", "); + const pf = elf.targetLoad(&ph.flags); + if (pf.R) try w.writeByte('R'); + if (pf.W) try w.writeByte('W'); + if (pf.X) try w.writeByte('X'); + try w.writeByte(')'); + }, + }, .section => |si| try w.print("({s})", .{elf.sectionName(si)}), .input_section => |isi| { const ii = isi.input(elf); diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index 8b4d67cf63e3..d12e5127386e 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -486,6 +486,14 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct { break :free .{ free_ni, free_node }; }, }; + switch (opts.prev) { + .none => opts.parent.get(mf).first = free_ni, + else => |prev_ni| prev_ni.get(mf).next = free_ni, + } + switch (opts.next) { + .none => opts.parent.get(mf).last = free_ni, + else => |next_ni| next_ni.get(mf).prev = free_ni, + } free_node.* = .{ .parent = opts.parent, .prev = opts.prev, @@ -535,13 +543,10 @@ pub fn addOnlyChildNode( try mf.nodes.ensureUnusedCapacity(gpa, 1); const parent = parent_ni.get(mf); assert(parent.first == .none and parent.last == .none); - const ni = try mf.addNode(gpa, .{ + return mf.addNode(gpa, .{ .parent = parent_ni, .add_node = opts, }); - parent.first = ni; - parent.last = ni; - return ni; } pub fn addFirstChildNode( @@ -552,17 +557,11 @@ pub fn addFirstChildNode( ) !Node.Index { try mf.nodes.ensureUnusedCapacity(gpa, 1); const parent = parent_ni.get(mf); - const ni = try mf.addNode(gpa, .{ + return mf.addNode(gpa, .{ .parent = parent_ni, .next = parent.first, .add_node = opts, }); - switch (parent.first) { - .none => parent.last = ni, - else => |first_ni| first_ni.get(mf).prev = ni, - } - parent.first = ni; - return ni; } pub fn addLastChildNode( @@ -573,7 +572,7 @@ pub fn addLastChildNode( ) !Node.Index { try mf.nodes.ensureUnusedCapacity(gpa, 1); const parent = parent_ni.get(mf); - const ni = try mf.addNode(gpa, .{ + return mf.addNode(gpa, .{ .parent = parent_ni, .prev = parent.last, .offset = offset: switch (parent.last) { @@ -585,12 +584,6 @@ pub fn addLastChildNode( }, .add_node = opts, }); - switch (parent.last) { - .none => parent.first = ni, - else => |last_ni| last_ni.get(mf).next = ni, - } - parent.last = ni; - return ni; } pub fn addNodeAfter( @@ -603,19 +596,13 @@ pub fn addNodeAfter( try mf.nodes.ensureUnusedCapacity(gpa, 1); const prev = prev_ni.get(mf); const prev_offset, const prev_size = prev.location().resolve(mf); - const ni = try mf.addNode(gpa, .{ + return mf.addNode(gpa, .{ .parent = prev.parent, .prev = prev_ni, .next = prev.next, .offset = prev_offset + prev_size, .add_node = opts, }); - switch (prev.next) { - .none => prev.parent.get(mf).last = ni, - else => |next_ni| next_ni.get(mf).prev = ni, - } - prev.next = ni; - return ni; } fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) !void {