Commit 57889cae80

Jacob Young <jacobly0@users.noreply.github.com>
2025-11-09 05:03:10
posix: reduce the number of assumptions made by `dl_iterate_phdr`
Not yet fully compatible with the new linker, but still progress. Closes #25786
1 parent d33c00c
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,
             });
         }
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,
 };
 
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
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,
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 };
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;
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);
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);