Commit 80c961159b

Jacob Young <jacobly0@users.noreply.github.com>
2025-11-06 04:33:02
Elf2: elide unused nodes when emitting objects
1 parent 7bcefe5
Changed files (2)
lib
src
lib/std/elf.zig
@@ -286,105 +286,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 +460,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 +1026,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 +1037,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 +1116,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 +1127,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,
src/link/Elf2.zig
@@ -462,7 +462,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],
@@ -715,12 +715,15 @@ fn initHeaders(
         break :phndx phnum;
     } else undefined;
 
-    const expected_nodes_len = expected_nodes_len: {
-        const expected_nodes_len = 5 + phnum * 2 + @as(usize, 2) * @intFromBool(have_dynamic_section);
-        if (@"type" != .REL) break :expected_nodes_len expected_nodes_len;
-        phnum = 0;
-        break :expected_nodes_len expected_nodes_len -
-            @intFromBool(comp.config.any_non_single_threaded);
+    const expected_nodes_len = expected_nodes_len: switch (@"type") {
+        .NONE => 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),
+        .CORE, _ => unreachable,
     };
     try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
     try elf.phdrs.resize(gpa, phnum);
@@ -771,51 +774,53 @@ fn initHeaders(
     }));
     elf.nodes.appendAssumeCapacity(.shdr);
 
-    assert(elf.ni.rodata == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
-        .alignment = elf.mf.flags.block_size,
-        .moved = @"type" != .REL,
-        .bubbles_moved = false,
-    }));
-    elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });
-    if (@"type" != .REL) 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 = @"type" != .REL,
-        .resized = @"type" != .REL,
-        .bubbles_moved = false,
-    }));
-    elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });
-    if (@"type" != .REL) 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 = @"type" != .REL,
-        .bubbles_moved = false,
-    }));
-    elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });
-    if (@"type" != .REL) 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 = @"type" != .REL,
-        .bubbles_moved = false,
-    }));
-    elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });
-    if (@"type" != .REL) 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)) {
+            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,
+            },
+        };
+    } else undefined;
     switch (class) {
         .NONE, _ => unreachable,
         inline else => |ct_class| {
@@ -826,7 +831,7 @@ fn initHeaders(
                 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,
+                    .type = .PHDR,
                     .offset = 0,
                     .vaddr = 0,
                     .paddr = 0,
@@ -840,7 +845,7 @@ fn initHeaders(
                 if (maybe_interp) |_| {
                     const ph_interp = &phdr[interp_phndx];
                     ph_interp.* = .{
-                        .type = std.elf.PT_INTERP,
+                        .type = .INTERP,
                         .offset = 0,
                         .vaddr = 0,
                         .paddr = 0,
@@ -855,7 +860,7 @@ fn initHeaders(
                 _, 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,
+                    .type = if (rodata_size == 0) .NULL else .LOAD,
                     .offset = 0,
                     .vaddr = ph_vaddr,
                     .paddr = ph_vaddr,
@@ -870,7 +875,7 @@ fn initHeaders(
                 _, 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,
+                    .type = if (text_size == 0) .NULL else .LOAD,
                     .offset = 0,
                     .vaddr = ph_vaddr,
                     .paddr = ph_vaddr,
@@ -885,7 +890,7 @@ fn initHeaders(
                 _, 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,
+                    .type = if (data_size == 0) .NULL else .LOAD,
                     .offset = 0,
                     .vaddr = ph_vaddr,
                     .paddr = ph_vaddr,
@@ -900,7 +905,7 @@ fn initHeaders(
                 if (have_dynamic_section) {
                     const ph_dynamic = &phdr[dynamic_phndx];
                     ph_dynamic.* = .{
-                        .type = std.elf.PT_DYNAMIC,
+                        .type = .DYNAMIC,
                         .offset = 0,
                         .vaddr = 0,
                         .paddr = 0,
@@ -915,7 +920,7 @@ fn initHeaders(
                 if (comp.config.any_non_single_threaded) {
                     const ph_tls = &phdr[tls_phndx];
                     ph_tls.* = .{
-                        .type = std.elf.PT_TLS,
+                        .type = .TLS,
                         .offset = 0,
                         .vaddr = 0,
                         .paddr = 0,
@@ -931,7 +936,7 @@ fn initHeaders(
             const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
             sh_null.* = .{
                 .name = try elf.string(.shstrtab, ""),
-                .type = std.elf.SHT_NULL,
+                .type = .NULL,
                 .flags = .{ .shf = .{} },
                 .addr = 0,
                 .offset = 0,
@@ -951,7 +956,7 @@ 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),
@@ -972,7 +977,7 @@ fn initHeaders(
         },
     }
     assert(elf.si.shstrtab == try elf.addSection(elf.ni.file, .{
-        .type = std.elf.SHT_STRTAB,
+        .type = .STRTAB,
         .addralign = elf.mf.flags.block_size,
         .entsize = 1,
     }));
@@ -982,7 +987,7 @@ 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,
@@ -1016,6 +1021,7 @@ fn initHeaders(
         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),
@@ -1038,7 +1044,7 @@ fn initHeaders(
                 const ElfN = ct_class.ElfN();
                 elf.si.dynsym = try elf.addSection(elf.ni.rodata, .{
                     .name = ".dynsym",
-                    .type = std.elf.SHT_DYNSYM,
+                    .type = .DYNSYM,
                     .size = @sizeOf(ElfN.Sym) * 1,
                     .addralign = addr_align,
                     .entsize = @sizeOf(ElfN.Sym),
@@ -1057,14 +1063,14 @@ fn initHeaders(
         }
         elf.si.dynstr = try elf.addSection(elf.ni.rodata, .{
             .name = ".dynstr",
-            .type = std.elf.SHT_STRTAB,
+            .type = .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,
+            .type = .DYNAMIC,
             .flags = .{ .ALLOC = true, .WRITE = true },
             .addralign = addr_align,
         });
@@ -1126,7 +1132,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,
@@ -1581,7 +1587,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", .{}),
                 }
@@ -1590,8 +1596,7 @@ fn loadObject(
                 if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)
                     return diags.failParse(path, "missing section names", .{});
                 const shdr = &sections[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);
@@ -1604,7 +1609,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;
@@ -1639,14 +1644,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 = &sections[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);
@@ -1729,13 +1734,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", .{});
@@ -1810,7 +1815,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(
@@ -1846,7 +1851,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", .{});
@@ -1982,15 +1987,15 @@ 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,
     addralign: std.mem.Alignment = .@"1",
     entsize: std.elf.Word = 0,
 }) !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;
@@ -2626,10 +2631,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;
@@ -2694,22 +2699,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;
@@ -2737,13 +2738,13 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
                 elf.targetStore(&sh.size, @intCast(size));
                 switch (elf.targetLoad(&sh.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(
+                    .NULL => if (size > 0) elf.targetStore(&sh.type, .PROGBITS),
+                    .PROGBITS => if (size == 0) elf.targetStore(&sh.type, .NULL),
+                    .SYMTAB, .DYNSYM => elf.targetStore(
                         &sh.info,
                         @intCast(@divExact(size, elf.targetLoad(&sh.entsize))),
                     ),
-                    std.elf.SHT_STRTAB, std.elf.SHT_DYNAMIC => {},
+                    .STRTAB, .DYNAMIC => {},
                 }
             },
         },
@@ -2870,6 +2871,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);