| author | |
| committer | |
| log | 7432fb04d6e3c52b40e81911d6c608e4d17317df |
| tree | 0af452693b61d1ae0724df7c1dbc8e7e68e50c5a |
| parent | 7a41af2632e693cd63476576bbbb965126e45b9b |
| parent | a095db0df71127b1fe9b595679cffb4a072b0a35 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
dl_iterate_phdr implementation5 files changed, 199 insertions(+), 0 deletions(-)
std/c/linux.zig+4| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | const linux = @import("../os/linux.zig"); | |
| 1 | 2 | pub use @import("../os/linux/errno.zig"); |
| 2 | 3 | |
| 3 | 4 | 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 { |
| 11 | 12 | |
| 12 | 13 | /// See std.elf for constants for this |
| 13 | 14 | pub extern fn getauxval(__type: c_ulong) c_ulong; |
| 15 | ||
| 16 | pub const dl_iterate_phdr_callback = extern fn (info: *linux.dl_phdr_info, size: usize, data: ?*c_void) c_int; | |
| 17 | pub 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 | 19 | else => void, |
| 20 | 20 | }; |
| 21 | 21 | |
| 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. | |
| 27 | const 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 | ||
| 51 | const RDebug = extern struct { | |
| 52 | r_version: i32, | |
| 53 | r_map: ?*LinkMap, | |
| 54 | r_brk: usize, | |
| 55 | r_ldbase: usize, | |
| 56 | }; | |
| 57 | ||
| 58 | fn 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 | ||
| 67 | pub 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 | ||
| 22 | 105 | pub const LinuxDynLib = struct { |
| 23 | 106 | elf_lib: ElfLib, |
| 24 | 107 | fd: i32, |
std/elf.zig+5| ... | ... | @@ -877,6 +877,11 @@ pub const Phdr = switch (@sizeOf(usize)) { |
| 877 | 877 | 8 => Elf64_Phdr, |
| 878 | 878 | else => @compileError("expected pointer size of 32 or 64"), |
| 879 | 879 | }; |
| 880 | pub const Dyn = switch (@sizeOf(usize)) { | |
| 881 | 4 => Elf32_Dyn, | |
| 882 | 8 => Elf64_Dyn, | |
| 883 | else => @compileError("expected pointer size of 32 or 64"), | |
| 884 | }; | |
| 880 | 885 | pub const Shdr = switch (@sizeOf(usize)) { |
| 881 | 886 | 4 => Elf32_Shdr, |
| 882 | 887 | 8 => Elf64_Shdr, |
std/os/linux.zig+66| ... | ... | @@ -2,7 +2,9 @@ const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | const elf = std.elf; | |
| 5 | 6 | const vdso = @import("linux/vdso.zig"); |
| 7 | const dl = @import("../dynamic_library.zig"); | |
| 6 | 8 | pub use switch (builtin.arch) { |
| 7 | 9 | builtin.Arch.x86_64 => @import("linux/x86_64.zig"), |
| 8 | 10 | builtin.Arch.i386 => @import("linux/i386.zig"), |
| ... | ... | @@ -1585,6 +1587,70 @@ pub const dirent64 = extern struct { |
| 1585 | 1587 | d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 |
| 1586 | 1588 | }; |
| 1587 | 1589 | |
| 1590 | pub 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 | |
| 1598 | extern const __ehdr_start: elf.Ehdr = undefined; | |
| 1599 | ||
| 1600 | pub 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 | ||
| 1588 | 1654 | test "import" { |
| 1589 | 1655 | if (builtin.os == builtin.Os.linux) { |
| 1590 | 1656 | _ = @import("linux/test.zig"); |
std/os/linux/test.zig+41| ... | ... | @@ -1,6 +1,8 @@ |
| 1 | 1 | const std = @import("../../std.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const linux = std.os.linux; |
| 4 | const mem = std.mem; | |
| 5 | const elf = std.elf; | |
| 4 | 6 | const expect = std.testing.expect; |
| 5 | 7 | |
| 6 | 8 | test "getpid" { |
| ... | ... | @@ -42,3 +44,42 @@ test "timer" { |
| 42 | 44 | // TODO implicit cast from *[N]T to [*]T |
| 43 | 45 | err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); |
| 44 | 46 | } |
| 47 | ||
| 48 | export 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 | ||
| 81 | test "dl_iterate_phdr" { | |
| 82 | var counter: usize = 0; | |
| 83 | expect(linux.dl_iterate_phdr(usize, iter_fn, &counter) != 0); | |
| 84 | expect(counter != 0); | |
| 85 | } |