mirror of
https://github.com/ziglang/zig.git
synced 2026-02-19 07:48:31 +00:00
Merge pull request #2354 from LemonBoy/iterate_phdr_impl
dl_iterate_phdr implementation
This commit is contained in:
commit
7432fb04d6
@ -1,3 +1,4 @@
|
||||
const linux = @import("../os/linux.zig");
|
||||
pub use @import("../os/linux/errno.zig");
|
||||
|
||||
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;
|
||||
@ -11,3 +12,6 @@ pub const pthread_attr_t = extern struct {
|
||||
|
||||
/// See std.elf for constants for this
|
||||
pub extern fn getauxval(__type: c_ulong) c_ulong;
|
||||
|
||||
pub const dl_iterate_phdr_callback = extern fn (info: *linux.dl_phdr_info, size: usize, data: ?*c_void) c_int;
|
||||
pub extern fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
|
||||
|
||||
@ -19,6 +19,89 @@ pub const DynLib = switch (builtin.os) {
|
||||
else => void,
|
||||
};
|
||||
|
||||
// The link_map structure is not completely specified beside the fields
|
||||
// reported below, any libc is free to store additional data in the remaining
|
||||
// space.
|
||||
// An iterator is provided in order to traverse the linked list in a idiomatic
|
||||
// fashion.
|
||||
const LinkMap = extern struct {
|
||||
l_addr: usize,
|
||||
l_name: [*]const u8,
|
||||
l_ld: ?*elf.Dyn,
|
||||
l_next: ?*LinkMap,
|
||||
l_prev: ?*LinkMap,
|
||||
|
||||
pub const Iterator = struct {
|
||||
current: ?*LinkMap,
|
||||
|
||||
fn end(self: *Iterator) bool {
|
||||
return self.current == null;
|
||||
}
|
||||
|
||||
fn next(self: *Iterator) ?*LinkMap {
|
||||
if (self.current) |it| {
|
||||
self.current = it.l_next;
|
||||
return it;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const RDebug = extern struct {
|
||||
r_version: i32,
|
||||
r_map: ?*LinkMap,
|
||||
r_brk: usize,
|
||||
r_ldbase: usize,
|
||||
};
|
||||
|
||||
fn elf_get_va_offset(phdrs: []elf.Phdr) !usize {
|
||||
for (phdrs) |*phdr| {
|
||||
if (phdr.p_type == elf.PT_LOAD) {
|
||||
return @ptrToInt(phdr) - phdr.p_vaddr;
|
||||
}
|
||||
}
|
||||
return error.InvalidExe;
|
||||
}
|
||||
|
||||
pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
|
||||
const va_offset = try elf_get_va_offset(phdrs);
|
||||
|
||||
const dyn_table = init: {
|
||||
for (phdrs) |*phdr| {
|
||||
if (phdr.p_type == elf.PT_DYNAMIC) {
|
||||
const ptr = @intToPtr([*]elf.Dyn, va_offset + phdr.p_vaddr);
|
||||
break :init ptr[0..phdr.p_memsz / @sizeOf(elf.Dyn)];
|
||||
}
|
||||
}
|
||||
// No PT_DYNAMIC means this is either a statically-linked program or a
|
||||
// badly corrupted one
|
||||
return LinkMap.Iterator{.current = null};
|
||||
};
|
||||
|
||||
const link_map_ptr = init: {
|
||||
for (dyn_table) |*dyn| {
|
||||
switch (dyn.d_tag) {
|
||||
elf.DT_DEBUG => {
|
||||
const r_debug = @intToPtr(*RDebug, dyn.d_un.d_ptr);
|
||||
if (r_debug.r_version != 1) return error.InvalidExe;
|
||||
break :init r_debug.r_map;
|
||||
},
|
||||
elf.DT_PLTGOT => {
|
||||
const got_table = @intToPtr([*]usize, dyn.d_un.d_ptr);
|
||||
// The address to the link_map structure is stored in the
|
||||
// second slot
|
||||
break :init @intToPtr(?*LinkMap, got_table[1]);
|
||||
},
|
||||
else => { }
|
||||
}
|
||||
}
|
||||
return error.InvalidExe;
|
||||
};
|
||||
|
||||
return LinkMap.Iterator{.current = link_map_ptr};
|
||||
}
|
||||
|
||||
pub const LinuxDynLib = struct {
|
||||
elf_lib: ElfLib,
|
||||
fd: i32,
|
||||
|
||||
@ -877,6 +877,11 @@ pub const Phdr = switch (@sizeOf(usize)) {
|
||||
8 => Elf64_Phdr,
|
||||
else => @compileError("expected pointer size of 32 or 64"),
|
||||
};
|
||||
pub const Dyn = switch (@sizeOf(usize)) {
|
||||
4 => Elf32_Dyn,
|
||||
8 => Elf64_Dyn,
|
||||
else => @compileError("expected pointer size of 32 or 64"),
|
||||
};
|
||||
pub const Shdr = switch (@sizeOf(usize)) {
|
||||
4 => Elf32_Shdr,
|
||||
8 => Elf64_Shdr,
|
||||
|
||||
@ -2,7 +2,9 @@ const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
const maxInt = std.math.maxInt;
|
||||
const elf = std.elf;
|
||||
const vdso = @import("linux/vdso.zig");
|
||||
const dl = @import("../dynamic_library.zig");
|
||||
pub use switch (builtin.arch) {
|
||||
builtin.Arch.x86_64 => @import("linux/x86_64.zig"),
|
||||
builtin.Arch.i386 => @import("linux/i386.zig"),
|
||||
@ -1585,6 +1587,70 @@ pub const dirent64 = extern struct {
|
||||
d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173
|
||||
};
|
||||
|
||||
pub const dl_phdr_info = extern struct {
|
||||
dlpi_addr: usize,
|
||||
dlpi_name: ?[*]const u8,
|
||||
dlpi_phdr: [*]elf.Phdr,
|
||||
dlpi_phnum: u16,
|
||||
};
|
||||
|
||||
// XXX: This should be weak
|
||||
extern const __ehdr_start: elf.Ehdr = undefined;
|
||||
|
||||
pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize {
|
||||
if (builtin.link_libc) {
|
||||
return std.c.dl_iterate_phdr(@ptrCast(std.c.dl_iterate_phdr_callback, callback), @ptrCast(?*c_void, data));
|
||||
}
|
||||
|
||||
const elf_base = @ptrToInt(&__ehdr_start);
|
||||
const n_phdr = __ehdr_start.e_phnum;
|
||||
const phdrs = (@intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff))[0..n_phdr];
|
||||
|
||||
var it = dl.linkmap_iterator(phdrs) catch return 0;
|
||||
|
||||
// The executable has no dynamic link segment, create a single entry for
|
||||
// the whole ELF image
|
||||
if (it.end()) {
|
||||
var info = dl_phdr_info{
|
||||
.dlpi_addr = elf_base,
|
||||
.dlpi_name = c"/proc/self/exe",
|
||||
.dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff),
|
||||
.dlpi_phnum = __ehdr_start.e_phnum,
|
||||
};
|
||||
|
||||
return callback(&info, @sizeOf(dl_phdr_info), data);
|
||||
}
|
||||
|
||||
// Last return value from the callback function
|
||||
var last_r: isize = 0;
|
||||
while (it.next()) |entry| {
|
||||
var dlpi_phdr: usize = undefined;
|
||||
var dlpi_phnum: u16 = undefined;
|
||||
|
||||
if (entry.l_addr != 0) {
|
||||
const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
|
||||
dlpi_phdr = entry.l_addr + elf_header.e_phoff;
|
||||
dlpi_phnum = elf_header.e_phnum;
|
||||
} else {
|
||||
// This is the running ELF image
|
||||
dlpi_phdr = elf_base + __ehdr_start.e_phoff;
|
||||
dlpi_phnum = __ehdr_start.e_phnum;
|
||||
}
|
||||
|
||||
var info = dl_phdr_info{
|
||||
.dlpi_addr = entry.l_addr,
|
||||
.dlpi_name = entry.l_name,
|
||||
.dlpi_phdr = @intToPtr([*]elf.Phdr, dlpi_phdr),
|
||||
.dlpi_phnum = dlpi_phnum,
|
||||
};
|
||||
|
||||
last_r = callback(&info, @sizeOf(dl_phdr_info), data);
|
||||
if (last_r != 0) break;
|
||||
}
|
||||
|
||||
return last_r;
|
||||
}
|
||||
|
||||
test "import" {
|
||||
if (builtin.os == builtin.Os.linux) {
|
||||
_ = @import("linux/test.zig");
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
const std = @import("../../std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const linux = std.os.linux;
|
||||
const mem = std.mem;
|
||||
const elf = std.elf;
|
||||
const expect = std.testing.expect;
|
||||
|
||||
test "getpid" {
|
||||
@ -42,3 +44,42 @@ test "timer" {
|
||||
// TODO implicit cast from *[N]T to [*]T
|
||||
err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);
|
||||
}
|
||||
|
||||
export fn iter_fn(info: *linux.dl_phdr_info, size: usize, data: ?*usize) i32 {
|
||||
var counter = data.?;
|
||||
// Count how many libraries are loaded
|
||||
counter.* += usize(1);
|
||||
|
||||
// The image should contain at least a PT_LOAD segment
|
||||
if (info.dlpi_phnum < 1) return -1;
|
||||
|
||||
// Quick & dirty validation of the phdr pointers, make sure we're not
|
||||
// pointing to some random gibberish
|
||||
var i: usize = 0;
|
||||
var found_load = false;
|
||||
while (i < info.dlpi_phnum) : (i += 1) {
|
||||
const phdr = info.dlpi_phdr[i];
|
||||
|
||||
if (phdr.p_type != elf.PT_LOAD) continue;
|
||||
|
||||
// Find the ELF header
|
||||
const elf_header = @intToPtr(*elf.Ehdr, phdr.p_vaddr - phdr.p_offset);
|
||||
// Validate the magic
|
||||
if (!mem.eql(u8, elf_header.e_ident[0..], "\x7fELF")) return -1;
|
||||
// Consistency check
|
||||
if (elf_header.e_phnum != info.dlpi_phnum) return -1;
|
||||
|
||||
found_load = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found_load) return -1;
|
||||
|
||||
return 42;
|
||||
}
|
||||
|
||||
test "dl_iterate_phdr" {
|
||||
var counter: usize = 0;
|
||||
expect(linux.dl_iterate_phdr(usize, iter_fn, &counter) != 0);
|
||||
expect(counter != 0);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user