authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-06 15:48:37-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-05-06 15:48:37-04:00
log7432fb04d6e3c52b40e81911d6c608e4d17317df
tree0af452693b61d1ae0724df7c1dbc8e7e68e50c5a
parent7a41af2632e693cd63476576bbbb965126e45b9b
parenta095db0df71127b1fe9b595679cffb4a072b0a35
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2354 from LemonBoy/iterate_phdr_impl

dl_iterate_phdr implementation

5 files changed, 199 insertions(+), 0 deletions(-)

std/c/linux.zig+4
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1const linux = @import("../os/linux.zig");
1pub use @import("../os/linux/errno.zig");2pub use @import("../os/linux/errno.zig");
23
3pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;4pub 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 {...@@ -11,3 +12,6 @@ pub const pthread_attr_t = extern struct {
1112
12/// See std.elf for constants for this13/// See std.elf for constants for this
13pub extern fn getauxval(__type: c_ulong) c_ulong;14pub extern fn getauxval(__type: c_ulong) c_ulong;
15
16pub const dl_iterate_phdr_callback = extern fn (info: *linux.dl_phdr_info, size: usize, data: ?*c_void) c_int;
17pub extern fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
std/dynamic_library.zig+83
...@@ -19,6 +19,89 @@ pub const DynLib = switch (builtin.os) {...@@ -19,6 +19,89 @@ pub const DynLib = switch (builtin.os) {
19 else => void,19 else => void,
20};20};
2121
22// The link_map structure is not completely specified beside the fields
23// reported below, any libc is free to store additional data in the remaining
24// space.
25// An iterator is provided in order to traverse the linked list in a idiomatic
26// fashion.
27const LinkMap = extern struct {
28 l_addr: usize,
29 l_name: [*]const u8,
30 l_ld: ?*elf.Dyn,
31 l_next: ?*LinkMap,
32 l_prev: ?*LinkMap,
33
34 pub const Iterator = struct {
35 current: ?*LinkMap,
36
37 fn end(self: *Iterator) bool {
38 return self.current == null;
39 }
40
41 fn next(self: *Iterator) ?*LinkMap {
42 if (self.current) |it| {
43 self.current = it.l_next;
44 return it;
45 }
46 return null;
47 }
48 };
49};
50
51const RDebug = extern struct {
52 r_version: i32,
53 r_map: ?*LinkMap,
54 r_brk: usize,
55 r_ldbase: usize,
56};
57
58fn elf_get_va_offset(phdrs: []elf.Phdr) !usize {
59 for (phdrs) |*phdr| {
60 if (phdr.p_type == elf.PT_LOAD) {
61 return @ptrToInt(phdr) - phdr.p_vaddr;
62 }
63 }
64 return error.InvalidExe;
65}
66
67pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
68 const va_offset = try elf_get_va_offset(phdrs);
69
70 const dyn_table = init: {
71 for (phdrs) |*phdr| {
72 if (phdr.p_type == elf.PT_DYNAMIC) {
73 const ptr = @intToPtr([*]elf.Dyn, va_offset + phdr.p_vaddr);
74 break :init ptr[0..phdr.p_memsz / @sizeOf(elf.Dyn)];
75 }
76 }
77 // No PT_DYNAMIC means this is either a statically-linked program or a
78 // badly corrupted one
79 return LinkMap.Iterator{.current = null};
80 };
81
82 const link_map_ptr = init: {
83 for (dyn_table) |*dyn| {
84 switch (dyn.d_tag) {
85 elf.DT_DEBUG => {
86 const r_debug = @intToPtr(*RDebug, dyn.d_un.d_ptr);
87 if (r_debug.r_version != 1) return error.InvalidExe;
88 break :init r_debug.r_map;
89 },
90 elf.DT_PLTGOT => {
91 const got_table = @intToPtr([*]usize, dyn.d_un.d_ptr);
92 // The address to the link_map structure is stored in the
93 // second slot
94 break :init @intToPtr(?*LinkMap, got_table[1]);
95 },
96 else => { }
97 }
98 }
99 return error.InvalidExe;
100 };
101
102 return LinkMap.Iterator{.current = link_map_ptr};
103}
104
22pub const LinuxDynLib = struct {105pub const LinuxDynLib = struct {
23 elf_lib: ElfLib,106 elf_lib: ElfLib,
24 fd: i32,107 fd: i32,
std/elf.zig+5
...@@ -877,6 +877,11 @@ pub const Phdr = switch (@sizeOf(usize)) {...@@ -877,6 +877,11 @@ pub const Phdr = switch (@sizeOf(usize)) {
877 8 => Elf64_Phdr,877 8 => Elf64_Phdr,
878 else => @compileError("expected pointer size of 32 or 64"),878 else => @compileError("expected pointer size of 32 or 64"),
879};879};
880pub const Dyn = switch (@sizeOf(usize)) {
881 4 => Elf32_Dyn,
882 8 => Elf64_Dyn,
883 else => @compileError("expected pointer size of 32 or 64"),
884};
880pub const Shdr = switch (@sizeOf(usize)) {885pub const Shdr = switch (@sizeOf(usize)) {
881 4 => Elf32_Shdr,886 4 => Elf32_Shdr,
882 8 => Elf64_Shdr,887 8 => Elf64_Shdr,
std/os/linux.zig+66
...@@ -2,7 +2,9 @@ const std = @import("../std.zig");...@@ -2,7 +2,9 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const elf = std.elf;
5const vdso = @import("linux/vdso.zig");6const vdso = @import("linux/vdso.zig");
7const dl = @import("../dynamic_library.zig");
6pub use switch (builtin.arch) {8pub use switch (builtin.arch) {
7 builtin.Arch.x86_64 => @import("linux/x86_64.zig"),9 builtin.Arch.x86_64 => @import("linux/x86_64.zig"),
8 builtin.Arch.i386 => @import("linux/i386.zig"),10 builtin.Arch.i386 => @import("linux/i386.zig"),
...@@ -1585,6 +1587,70 @@ pub const dirent64 = extern struct {...@@ -1585,6 +1587,70 @@ pub const dirent64 = extern struct {
1585 d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/1731587 d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173
1586};1588};
15871589
1590pub const dl_phdr_info = extern struct {
1591 dlpi_addr: usize,
1592 dlpi_name: ?[*]const u8,
1593 dlpi_phdr: [*]elf.Phdr,
1594 dlpi_phnum: u16,
1595};
1596
1597// XXX: This should be weak
1598extern const __ehdr_start: elf.Ehdr = undefined;
1599
1600pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize {
1601 if (builtin.link_libc) {
1602 return std.c.dl_iterate_phdr(@ptrCast(std.c.dl_iterate_phdr_callback, callback), @ptrCast(?*c_void, data));
1603 }
1604
1605 const elf_base = @ptrToInt(&__ehdr_start);
1606 const n_phdr = __ehdr_start.e_phnum;
1607 const phdrs = (@intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff))[0..n_phdr];
1608
1609 var it = dl.linkmap_iterator(phdrs) catch return 0;
1610
1611 // The executable has no dynamic link segment, create a single entry for
1612 // the whole ELF image
1613 if (it.end()) {
1614 var info = dl_phdr_info{
1615 .dlpi_addr = elf_base,
1616 .dlpi_name = c"/proc/self/exe",
1617 .dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff),
1618 .dlpi_phnum = __ehdr_start.e_phnum,
1619 };
1620
1621 return callback(&info, @sizeOf(dl_phdr_info), data);
1622 }
1623
1624 // Last return value from the callback function
1625 var last_r: isize = 0;
1626 while (it.next()) |entry| {
1627 var dlpi_phdr: usize = undefined;
1628 var dlpi_phnum: u16 = undefined;
1629
1630 if (entry.l_addr != 0) {
1631 const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
1632 dlpi_phdr = entry.l_addr + elf_header.e_phoff;
1633 dlpi_phnum = elf_header.e_phnum;
1634 } else {
1635 // This is the running ELF image
1636 dlpi_phdr = elf_base + __ehdr_start.e_phoff;
1637 dlpi_phnum = __ehdr_start.e_phnum;
1638 }
1639
1640 var info = dl_phdr_info{
1641 .dlpi_addr = entry.l_addr,
1642 .dlpi_name = entry.l_name,
1643 .dlpi_phdr = @intToPtr([*]elf.Phdr, dlpi_phdr),
1644 .dlpi_phnum = dlpi_phnum,
1645 };
1646
1647 last_r = callback(&info, @sizeOf(dl_phdr_info), data);
1648 if (last_r != 0) break;
1649 }
1650
1651 return last_r;
1652}
1653
1588test "import" {1654test "import" {
1589 if (builtin.os == builtin.Os.linux) {1655 if (builtin.os == builtin.Os.linux) {
1590 _ = @import("linux/test.zig");1656 _ = @import("linux/test.zig");
std/os/linux/test.zig+41
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1const std = @import("../../std.zig");1const std = @import("../../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const linux = std.os.linux;3const linux = std.os.linux;
4const mem = std.mem;
5const elf = std.elf;
4const expect = std.testing.expect;6const expect = std.testing.expect;
57
6test "getpid" {8test "getpid" {
...@@ -42,3 +44,42 @@ test "timer" {...@@ -42,3 +44,42 @@ test "timer" {
42 // TODO implicit cast from *[N]T to [*]T44 // TODO implicit cast from *[N]T to [*]T
43 err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);45 err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);
44}46}
47
48export fn iter_fn(info: *linux.dl_phdr_info, size: usize, data: ?*usize) i32 {
49 var counter = data.?;
50 // Count how many libraries are loaded
51 counter.* += usize(1);
52
53 // The image should contain at least a PT_LOAD segment
54 if (info.dlpi_phnum < 1) return -1;
55
56 // Quick & dirty validation of the phdr pointers, make sure we're not
57 // pointing to some random gibberish
58 var i: usize = 0;
59 var found_load = false;
60 while (i < info.dlpi_phnum) : (i += 1) {
61 const phdr = info.dlpi_phdr[i];
62
63 if (phdr.p_type != elf.PT_LOAD) continue;
64
65 // Find the ELF header
66 const elf_header = @intToPtr(*elf.Ehdr, phdr.p_vaddr - phdr.p_offset);
67 // Validate the magic
68 if (!mem.eql(u8, elf_header.e_ident[0..], "\x7fELF")) return -1;
69 // Consistency check
70 if (elf_header.e_phnum != info.dlpi_phnum) return -1;
71
72 found_load = true;
73 break;
74 }
75
76 if (!found_load) return -1;
77
78 return 42;
79}
80
81test "dl_iterate_phdr" {
82 var counter: usize = 0;
83 expect(linux.dl_iterate_phdr(usize, iter_fn, &counter) != 0);
84 expect(counter != 0);
85}