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.
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,

View File

@ -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,
});
}

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 {
_ = 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 };

View File

@ -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;

View File

@ -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,
};

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(
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;
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();
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{
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);

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) {
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

View File

@ -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);