|
13 | 13 | #include <sys/time.h>
|
14 | 14 | #include <sys/types.h>
|
15 | 15 | #include <sys/ucontext.h>
|
| 16 | +#include <sys/user.h> |
16 | 17 |
|
17 | 18 | #include <sys/fcntl.h> // open
|
18 | 19 | #include <sys/mman.h> // mmap & munmap
|
19 | 20 | #include <sys/stat.h> // open
|
| 21 | +#include <sys/sysctl.h> |
20 | 22 | #include <unistd.h> // getpagesize
|
21 | 23 | // If you don't have execinfo.h then you need devel/libexecinfo from ports.
|
22 | 24 | #include <errno.h>
|
@@ -46,41 +48,47 @@ static unsigned StringToLong(char* buffer) {
|
46 | 48 |
|
47 | 49 | std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
|
48 | 50 | std::vector<SharedLibraryAddress> result;
|
49 |
| - static const int MAP_LENGTH = 1024; |
50 |
| - int fd = open("/proc/self/maps", O_RDONLY); |
51 |
| - if (fd < 0) return result; |
52 |
| - while (true) { |
53 |
| - char addr_buffer[11]; |
54 |
| - addr_buffer[0] = '0'; |
55 |
| - addr_buffer[1] = 'x'; |
56 |
| - addr_buffer[10] = 0; |
57 |
| - ssize_t bytes_read = read(fd, addr_buffer + 2, 8); |
58 |
| - if (bytes_read < 8) break; |
59 |
| - unsigned start = StringToLong(addr_buffer); |
60 |
| - bytes_read = read(fd, addr_buffer + 2, 1); |
61 |
| - if (bytes_read < 1) break; |
62 |
| - if (addr_buffer[2] != '-') break; |
63 |
| - bytes_read = read(fd, addr_buffer + 2, 8); |
64 |
| - if (bytes_read < 8) break; |
65 |
| - unsigned end = StringToLong(addr_buffer); |
66 |
| - char buffer[MAP_LENGTH]; |
67 |
| - bytes_read = -1; |
68 |
| - do { |
69 |
| - bytes_read++; |
70 |
| - if (bytes_read >= MAP_LENGTH - 1) break; |
71 |
| - bytes_read = read(fd, buffer + bytes_read, 1); |
72 |
| - if (bytes_read < 1) break; |
73 |
| - } while (buffer[bytes_read] != '\n'); |
74 |
| - buffer[bytes_read] = 0; |
75 |
| - // Ignore mappings that are not executable. |
76 |
| - if (buffer[3] != 'x') continue; |
77 |
| - char* start_of_path = index(buffer, '/'); |
78 |
| - // There may be no filename in this line. Skip to next. |
79 |
| - if (start_of_path == nullptr) continue; |
80 |
| - buffer[bytes_read] = 0; |
81 |
| - result.push_back(SharedLibraryAddress(start_of_path, start, end)); |
| 51 | + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; |
| 52 | + size_t miblen = sizeof(mib) / sizeof(mib[0]); |
| 53 | + size_t buffer_size; |
| 54 | + if (sysctl(mib, miblen, nullptr, &buffer_size, nullptr, 0) == 0) { |
| 55 | + // Overallocate the buffer by 1/3 to account for concurrent |
| 56 | + // kinfo_vmentry change. 1/3 is an arbitrary constant that |
| 57 | + // works in practice. |
| 58 | + buffer_size = buffer_size * 4 / 3; |
| 59 | + std::vector<char> buffer(buffer_size); |
| 60 | + int ret = sysctl(mib, miblen, buffer.data(), &buffer_size, nullptr, 0); |
| 61 | + |
| 62 | + if (ret == 0 || (ret == -1 && errno == ENOMEM)) { |
| 63 | + char* start = buffer.data(); |
| 64 | + char* end = start + buffer_size; |
| 65 | + |
| 66 | + while (start < end) { |
| 67 | + struct kinfo_vmentry* map = |
| 68 | + reinterpret_cast<struct kinfo_vmentry*>(start); |
| 69 | + const size_t ssize = map->kve_structsize; |
| 70 | + char* path = map->kve_path; |
| 71 | + |
| 72 | + CHECK_NE(0, ssize); |
| 73 | + |
| 74 | + if ((map->kve_protection & KVME_PROT_READ) != 0 && |
| 75 | + (map->kve_protection & KVME_PROT_EXEC) != 0 && path[0] != '\0') { |
| 76 | + char* sep = strrchr(path, '/'); |
| 77 | + std::string lib_name; |
| 78 | + if (sep != nullptr) { |
| 79 | + lib_name = std::string(++sep); |
| 80 | + } else { |
| 81 | + lib_name = std::string(path); |
| 82 | + } |
| 83 | + result.push_back(SharedLibraryAddress( |
| 84 | + lib_name, reinterpret_cast<uintptr_t>(map->kve_start), |
| 85 | + reinterpret_cast<uintptr_t>(map->kve_end))); |
| 86 | + } |
| 87 | + |
| 88 | + start += ssize; |
| 89 | + } |
| 90 | + } |
82 | 91 | }
|
83 |
| - close(fd); |
84 | 92 | return result;
|
85 | 93 | }
|
86 | 94 |
|
|
0 commit comments