| 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,3 +1,4 @@ |
| 1 | const linux = @import("../os/linux.zig"); | ||
| 1 | pub use @import("../os/linux/errno.zig"); | 2 | pub use @import("../os/linux/errno.zig"); |
| 2 | 3 | ||
| 3 | pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int; | 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,3 +12,6 @@ pub const pthread_attr_t = extern struct { |
| 11 | 12 | ||
| 12 | /// See std.elf for constants for this | 13 | /// See std.elf for constants for this |
| 13 | pub extern fn getauxval(__type: c_ulong) c_ulong; | 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,6 +19,89 @@ pub const DynLib = switch (builtin.os) { |
| 19 | else => void, | 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 | pub const LinuxDynLib = struct { | 105 | pub 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 | }; |
| 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 | pub const Shdr = switch (@sizeOf(usize)) { | 885 | pub 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"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 5 | const elf = std.elf; | ||
| 5 | const vdso = @import("linux/vdso.zig"); | 6 | const vdso = @import("linux/vdso.zig"); |
| 7 | const dl = @import("../dynamic_library.zig"); | ||
| 6 | pub use switch (builtin.arch) { | 8 | pub 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/173 | 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 | test "import" { | 1654 | test "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 @@ |
| 1 | const std = @import("../../std.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const linux = std.os.linux; | 3 | const linux = std.os.linux; |
| 4 | const mem = std.mem; | ||
| 5 | const elf = std.elf; | ||
| 4 | const expect = std.testing.expect; | 6 | const expect = std.testing.expect; |
| 5 | 7 | ||
| 6 | test "getpid" { | 8 | test "getpid" { |
| ... | @@ -42,3 +44,42 @@ test "timer" { | ... | @@ -42,3 +44,42 @@ test "timer" { |
| 42 | // TODO implicit cast from *[N]T to [*]T | 44 | // 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 | |||
| 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 | } |