1 /** 2 * Simplifies working with shared ELF objects of the current process. 3 * 4 * Reference: http://www.dwarfstd.org/ 5 * 6 * Copyright: Copyright Digital Mars 2015 - 2018. 7 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 8 * Authors: Martin Kinkelin 9 * Source: $(DRUNTIMESRC core/internal/elf/dl.d) 10 */ 11 12 module core.internal.elf.dl; 13 14 version (linux) 15 { 16 import core.sys.linux.link; 17 version = LinuxOrBSD; 18 } 19 else version (FreeBSD) 20 { 21 import core.sys.freebsd.sys.link_elf; 22 version = LinuxOrBSD; 23 } 24 else version (DragonFlyBSD) 25 { 26 import core.sys.dragonflybsd.sys.link_elf; 27 version = LinuxOrBSD; 28 } 29 else version (NetBSD) 30 { 31 import core.sys.netbsd.sys.link_elf; 32 version = LinuxOrBSD; 33 } 34 else version (OpenBSD) 35 { 36 import core.sys.openbsd.sys.link_elf; 37 version = LinuxOrBSD; 38 } 39 else version (Solaris) 40 { 41 import core.sys.solaris.link; 42 version = LinuxOrBSD; 43 } 44 45 version (LinuxOrBSD): 46 47 alias Elf_Ehdr = ElfW!"Ehdr"; 48 alias Elf_Phdr = ElfW!"Phdr"; 49 50 /** 51 * Enables iterating over the process' currently loaded shared objects. 52 */ 53 struct SharedObjects 54 { 55 @nogc nothrow: 56 /// 57 alias Callback = int delegate(SharedObject); 58 59 /// 60 static int opApply(scope Callback dg) 61 { 62 extern(C) int nativeCallback(dl_phdr_info* info, size_t, void* data) 63 { 64 auto dg = *cast(Callback*) data; 65 return dg(SharedObject(*info)); 66 } 67 68 return dl_iterate_phdr(&nativeCallback, &dg); 69 } 70 } 71 72 /** 73 * A loaded shared ELF object/binary, i.e., executable or shared library. 74 */ 75 struct SharedObject 76 { 77 @nogc nothrow: 78 /// Returns the executable of the current process. 79 static SharedObject thisExecutable() 80 { 81 foreach (object; SharedObjects) 82 return object; // first object 83 assert(0); 84 } 85 86 /** 87 * Tries to find the shared object containing the specified address in one of its segments. 88 * Returns: True on success. 89 */ 90 static bool findForAddress(const scope void* address, out SharedObject result) 91 { 92 version (linux) enum IterateManually = true; 93 else version (NetBSD) enum IterateManually = true; 94 else version (OpenBSD) enum IterateManually = true; 95 else version (Solaris) enum IterateManually = true; 96 else enum IterateManually = false; 97 98 static if (IterateManually) 99 { 100 foreach (object; SharedObjects) 101 { 102 const(Elf_Phdr)* segment; 103 if (object.findSegmentForAddress(address, segment)) 104 { 105 result = object; 106 return true; 107 } 108 } 109 return false; 110 } 111 else 112 { 113 return !!_rtld_addr_phdr(address, &result.info); 114 } 115 } 116 117 /// OS-dependent info structure. 118 dl_phdr_info info; 119 120 /// Returns the base address of the object. 121 @property void* baseAddress() const 122 { 123 return cast(void*) info.dlpi_addr; 124 } 125 126 /// Returns the name of (usually: path to) the object. Null-terminated. 127 const(char)[] name() const 128 { 129 import core.stdc.string : strlen; 130 131 const(char)* cstr = info.dlpi_name; 132 133 // the main executable has an empty name 134 if (cstr[0] == 0) 135 cstr = getprogname(); 136 137 return cstr[0 .. strlen(cstr)]; 138 } 139 140 /** 141 * Tries to fill the specified buffer with the path to the ELF file, 142 * according to the /proc/<PID>/maps file. 143 * 144 * Returns: The filled slice (null-terminated), or null if an error occurs. 145 */ 146 char[] getPath(size_t N)(ref char[N] buffer) const 147 if (N > 1) 148 { 149 import core.stdc.stdio, core.stdc.string, core.sys.posix.unistd; 150 151 char[N + 128] lineBuffer = void; 152 153 snprintf(lineBuffer.ptr, lineBuffer.length, "/proc/%d/maps", getpid()); 154 auto file = fopen(lineBuffer.ptr, "r"); 155 if (!file) 156 return null; 157 scope(exit) fclose(file); 158 159 const thisBase = cast(ulong) baseAddress(); 160 ulong startAddress; 161 162 // prevent overflowing `buffer` by specifying the max length in the scanf format string 163 enum int maxPathLength = N - 1; 164 enum scanFormat = "%llx-%*llx %*s %*s %*s %*s %" ~ maxPathLength.stringof ~ "s"; 165 166 while (fgets(lineBuffer.ptr, lineBuffer.length, file)) 167 { 168 if (sscanf(lineBuffer.ptr, scanFormat.ptr, &startAddress, buffer.ptr) == 2 && 169 startAddress == thisBase) 170 return buffer[0 .. strlen(buffer.ptr)]; 171 } 172 173 return null; 174 } 175 176 /// Iterates over this object's segments. 177 int opApply(scope int delegate(ref const Elf_Phdr) @nogc nothrow dg) const 178 { 179 foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) 180 { 181 const r = dg(phdr); 182 if (r != 0) 183 return r; 184 } 185 return 0; 186 } 187 188 /** 189 * Tries to find the segment containing the specified address. 190 * Returns: True on success. 191 */ 192 bool findSegmentForAddress(const scope void* address, out const(Elf_Phdr)* result) const 193 { 194 if (address < baseAddress) 195 return false; 196 197 foreach (ref phdr; this) 198 { 199 const begin = baseAddress + phdr.p_vaddr; 200 if (cast(size_t)(address - begin) < phdr.p_memsz) 201 { 202 result = &phdr; 203 return true; 204 } 205 } 206 return false; 207 } 208 } 209 210 private @nogc nothrow: 211 212 version (linux) 213 { 214 // TODO: replace with a fixed core.sys.linux.config._GNU_SOURCE 215 version (CRuntime_Bionic) {} else version = Linux_Use_GNU; 216 } 217 218 version (Linux_Use_GNU) 219 { 220 const(char)* getprogname() 221 { 222 import core.sys.linux.errno; 223 return program_invocation_name; 224 } 225 } 226 else // Bionic, BSDs 227 { 228 extern(C) const(char)* getprogname(); 229 } 230 231 unittest 232 { 233 import core.stdc.stdio; 234 235 char[512] buffer = void; 236 foreach (object; SharedObjects) 237 { 238 const name = object.name(); 239 assert(name.length); 240 const path = object.getPath(buffer); 241 242 printf("DSO name: %s\n", name.ptr); 243 printf(" path: %s\n", path ? path.ptr : ""); 244 printf(" base: %p\n", object.baseAddress); 245 } 246 }