posix: reduce the number of assumptions made by dl_iterate_phdr

Not yet fully compatible with the new linker, but still progress.

Closes #25786
This commit is contained in:
Jacob Young 2025-11-08 23:03:10 -05:00
parent d33c00cad0
commit 57889cae80
8 changed files with 55 additions and 64 deletions

View File

@ -3971,7 +3971,7 @@ pub const dl_phdr_info = switch (native_os) {
/// Module name. /// Module name.
name: ?[*:0]const u8, name: ?[*:0]const u8,
/// Pointer to module's phdr. /// Pointer to module's phdr.
phdr: [*]std.elf.Phdr, phdr: [*]std.elf.ElfN.Phdr,
/// Number of entries in phdr. /// Number of entries in phdr.
phnum: u16, phnum: u16,
/// Total number of loads. /// Total number of loads.
@ -3984,7 +3984,7 @@ pub const dl_phdr_info = switch (native_os) {
.illumos => extern struct { .illumos => extern struct {
addr: std.elf.Addr, addr: std.elf.Addr,
name: ?[*:0]const u8, name: ?[*:0]const u8,
phdr: [*]std.elf.Phdr, phdr: [*]std.elf.ElfN.Phdr,
phnum: std.elf.Half, phnum: std.elf.Half,
/// Incremented when a new object is mapped into the process. /// Incremented when a new object is mapped into the process.
adds: u64, adds: u64,
@ -3995,7 +3995,7 @@ pub const dl_phdr_info = switch (native_os) {
.openbsd, .haiku, .dragonfly, .netbsd, .serenity => extern struct { .openbsd, .haiku, .dragonfly, .netbsd, .serenity => extern struct {
addr: usize, addr: usize,
name: ?[*:0]const u8, name: ?[*:0]const u8,
phdr: [*]std.elf.Phdr, phdr: [*]std.elf.ElfN.Phdr,
phnum: std.elf.Half, phnum: std.elf.Half,
}, },
else => void, else => void,

View File

@ -441,11 +441,11 @@ const DlIterContext = struct {
// Populate `build_id` and `gnu_eh_frame` // Populate `build_id` and `gnu_eh_frame`
for (info.phdr[0..info.phnum]) |phdr| { for (info.phdr[0..info.phnum]) |phdr| {
switch (phdr.p_type) { switch (phdr.type) {
std.elf.PT_NOTE => { .NOTE => {
// Look for .note.gnu.build-id // Look for .note.gnu.build-id
const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.vaddr);
var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.p_memsz]); var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.memsz]);
const name_size = r.takeInt(u32, native_endian) catch continue; const name_size = r.takeInt(u32, native_endian) catch continue;
const desc_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; 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; const desc = r.take(desc_size) catch continue;
build_id = desc; build_id = desc;
}, },
std.elf.PT_GNU_EH_FRAME => { std.elf.PT.GNU_EH_FRAME => {
const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.vaddr);
gnu_eh_frame = segment_ptr[0..phdr.p_memsz]; gnu_eh_frame = segment_ptr[0..phdr.memsz];
}, },
else => {}, else => {},
} }
@ -478,11 +478,11 @@ const DlIterContext = struct {
}); });
for (info.phdr[0..info.phnum]) |phdr| { 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, .{ try context.si.ranges.append(gpa, .{
// Overflowing addition handles VSDOs having p_vaddr = 0xffffffffff700000 // Overflowing addition handles VSDOs having p_vaddr = 0xffffffffff700000
.start = info.addr +% phdr.p_vaddr, .start = info.addr +% phdr.vaddr,
.len = phdr.p_memsz, .len = phdr.memsz,
.module_index = module_index, .module_index = module_index,
}); });
} }

View File

@ -92,8 +92,7 @@ pub fn get_DYNAMIC() ?[*]const elf.Dyn {
}); });
} }
pub fn linkmap_iterator(phdrs: []const elf.Phdr) error{InvalidExe}!LinkMap.Iterator { pub fn linkmap_iterator() error{InvalidExe}!LinkMap.Iterator {
_ = phdrs;
const _DYNAMIC = get_DYNAMIC() orelse { const _DYNAMIC = get_DYNAMIC() orelse {
// No PT_DYNAMIC means this is a statically-linked non-PIE program. // No PT_DYNAMIC means this is a statically-linked non-PIE program.
return .{ .current = null }; return .{ .current = null };

View File

@ -50,6 +50,7 @@ pub const AT_L2_CACHESIZE = 44;
pub const AT_L2_CACHEGEOMETRY = 45; pub const AT_L2_CACHEGEOMETRY = 45;
pub const AT_L3_CACHESIZE = 46; pub const AT_L3_CACHESIZE = 46;
pub const AT_L3_CACHEGEOMETRY = 47; pub const AT_L3_CACHEGEOMETRY = 47;
pub const AT_MINSIGSTKSZ = 51;
pub const DT_NULL = 0; pub const DT_NULL = 0;
pub const DT_NEEDED = 1; pub const DT_NEEDED = 1;

View File

@ -6101,7 +6101,7 @@ pub const dirent64 = extern struct {
pub const dl_phdr_info = extern struct { pub const dl_phdr_info = extern struct {
addr: usize, addr: usize,
name: ?[*:0]const u8, name: ?[*:0]const u8,
phdr: [*]std.elf.Phdr, phdr: [*]std.elf.ElfN.Phdr,
phnum: u16, phnum: u16,
}; };

View File

@ -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( pub fn dl_iterate_phdr(
context: anytype, context: anytype,
comptime Error: type, comptime Error: type,
@ -5075,34 +5082,24 @@ pub fn dl_iterate_phdr(
} }
} }
const elf_base = std.process.getBaseAddress(); var it = dl.linkmap_iterator() catch unreachable;
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;
// The executable has no dynamic link segment, create a single entry for // The executable has no dynamic link segment, create a single entry for
// the whole ELF image. // the whole ELF image.
if (it.end()) { if (it.end()) {
// Find the base address for the ELF image, if this is a PIE the value const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval;
// is non-zero. const phdrs = getSelfPhdrs();
const base_address = for (phdrs) |*phdr| { var info: dl_phdr_info = .{
if (phdr.p_type == elf.PT_PHDR) { .addr = for (phdrs) |phdr| switch (phdr.type) {
break @intFromPtr(phdrs.ptr) - phdr.p_vaddr; .PHDR => break @intFromPtr(phdrs.ptr) - phdr.vaddr,
// We could try computing the difference between _DYNAMIC and else => {},
// the p_vaddr of the PT_DYNAMIC section, but using the phdr is } else unreachable,
// good enough (Is it?). .name = switch (getauxval(std.elf.AT_EXECFN)) {
} 0 => "/proc/self/exe",
} else unreachable; else => |name| @ptrFromInt(name),
},
var info = dl_phdr_info{
.addr = base_address,
.name = "/proc/self/exe",
.phdr = phdrs.ptr, .phdr = phdrs.ptr,
.phnum = ehdr.e_phnum, .phnum = @intCast(phdrs.len),
}; };
return callback(&info, @sizeOf(dl_phdr_info), context); return callback(&info, @sizeOf(dl_phdr_info), context);
@ -5110,24 +5107,18 @@ pub fn dl_iterate_phdr(
// Last return value from the callback function. // Last return value from the callback function.
while (it.next()) |entry| { while (it.next()) |entry| {
var phdr: [*]elf.Phdr = undefined; const phdrs: []elf.ElfN.Phdr = if (entry.l_addr != 0) phdrs: {
var phnum: u16 = undefined; 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();
if (entry.l_addr != 0) { var info: dl_phdr_info = .{
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{
.addr = entry.l_addr, .addr = entry.l_addr,
.name = entry.l_name, .name = entry.l_name,
.phdr = phdr, .phdr = phdrs.ptr,
.phnum = phnum, .phnum = @intCast(phdrs.len),
}; };
try callback(&info, @sizeOf(dl_phdr_info), context); try callback(&info, @sizeOf(dl_phdr_info), context);

View File

@ -257,11 +257,11 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
while (i < info.phnum) : (i += 1) { while (i < info.phnum) : (i += 1) {
const phdr = info.phdr[i]; 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 // 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 // Validate the magic
if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic; if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic;
// Consistency check // Consistency check

View File

@ -1658,13 +1658,13 @@ fn posixGetUserInfoPasswdStream(name: []const u8, reader: *std.Io.Reader) !UserI
pub fn getBaseAddress() usize { pub fn getBaseAddress() usize {
switch (native_os) { switch (native_os) {
.linux => { .linux => {
const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval; const phdrs = std.posix.getSelfPhdrs();
const base = getauxval(std.elf.AT_BASE); var base: usize = 0;
if (base != 0) { for (phdrs) |phdr| switch (phdr.type) {
return base; .LOAD => return base + phdr.vaddr,
} .PHDR => base = @intFromPtr(phdrs.ptr) - phdr.vaddr,
const phdr = getauxval(std.elf.AT_PHDR); else => {},
return phdr - @sizeOf(std.elf.Ehdr); } else unreachable;
}, },
.driverkit, .ios, .macos, .tvos, .visionos, .watchos => { .driverkit, .ios, .macos, .tvos, .visionos, .watchos => {
return @intFromPtr(&std.c._mh_execute_header); return @intFromPtr(&std.c._mh_execute_header);