whereami.cpp (13460B)
1 // (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses 2 // without any warranty. 3 // by Gregory Pakosz (@gpakosz) 4 // https://github.com/gpakosz/whereami 5 6 // in case you want to #include "whereami.c" in a larger compilation unit 7 #if !defined(WHEREAMI_H) 8 #include "whereami.h" 9 #endif 10 11 #ifdef __cplusplus 12 extern "C" { 13 #endif 14 15 #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) 16 #include <stdlib.h> 17 #endif 18 19 #if !defined(WAI_MALLOC) 20 #define WAI_MALLOC(size) malloc(size) 21 #endif 22 23 #if !defined(WAI_FREE) 24 #define WAI_FREE(p) free(p) 25 #endif 26 27 #if !defined(WAI_REALLOC) 28 #define WAI_REALLOC(p, size) realloc(p, size) 29 #endif 30 31 #ifndef WAI_NOINLINE 32 #if defined(_MSC_VER) 33 #define WAI_NOINLINE __declspec(noinline) 34 #elif defined(__GNUC__) 35 #define WAI_NOINLINE __attribute__((noinline)) 36 #else 37 #error unsupported compiler 38 #endif 39 #endif 40 41 #if defined(_MSC_VER) 42 #define WAI_RETURN_ADDRESS() _ReturnAddress() 43 #elif defined(__GNUC__) 44 #define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) 45 #else 46 #error unsupported compiler 47 #endif 48 49 #if defined(_WIN32) 50 51 #ifndef WIN32_LEAN_AND_MEAN 52 #define WIN32_LEAN_AND_MEAN 53 #endif 54 #if defined(_MSC_VER) 55 #pragma warning(push, 3) 56 #endif 57 #include <windows.h> 58 #include <intrin.h> 59 #if defined(_MSC_VER) 60 #pragma warning(pop) 61 #endif 62 63 static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) 64 { 65 wchar_t buffer1[MAX_PATH]; 66 wchar_t buffer2[MAX_PATH]; 67 wchar_t* path = NULL; 68 int length = -1; 69 70 for (;;) 71 { 72 DWORD size; 73 int length_, length__; 74 75 size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); 76 77 if (size == 0) 78 break; 79 else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0]))) 80 { 81 DWORD size_ = size; 82 do 83 { 84 wchar_t* path_; 85 86 path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2); 87 if (!path_) 88 break; 89 size_ *= 2; 90 path = path_; 91 size = GetModuleFileNameW(module, path, size_); 92 } 93 while (size == size_); 94 95 if (size == size_) 96 break; 97 } 98 else 99 path = buffer1; 100 101 if (!_wfullpath(buffer2, path, MAX_PATH)) 102 break; 103 length_ = (int)wcslen(buffer2); 104 length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL); 105 106 if (length__ == 0) 107 length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); 108 if (length__ == 0) 109 break; 110 111 if (length__ <= capacity && dirname_length) 112 { 113 int i; 114 115 for (i = length__ - 1; i >= 0; --i) 116 { 117 if (out[i] == '\\') 118 { 119 *dirname_length = i; 120 break; 121 } 122 } 123 } 124 125 length = length__; 126 127 break; 128 } 129 130 if (path != buffer1) 131 WAI_FREE(path); 132 133 return length; 134 } 135 136 WAI_NOINLINE WAI_FUNCSPEC 137 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 138 { 139 return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length); 140 } 141 142 WAI_NOINLINE WAI_FUNCSPEC 143 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 144 { 145 HMODULE module; 146 int length = -1; 147 148 #if defined(_MSC_VER) 149 #pragma warning(push) 150 #pragma warning(disable: 4054) 151 #endif 152 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module)) 153 #if defined(_MSC_VER) 154 #pragma warning(pop) 155 #endif 156 { 157 length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length); 158 } 159 160 return length; 161 } 162 163 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE) 164 165 #include <stdio.h> 166 #include <stdlib.h> 167 #include <string.h> 168 #if defined(__linux__) 169 #include <linux/limits.h> 170 #else 171 #include <limits.h> 172 #endif 173 #ifndef __STDC_FORMAT_MACROS 174 #define __STDC_FORMAT_MACROS 175 #endif 176 #include <inttypes.h> 177 178 #if !defined(WAI_PROC_SELF_EXE) 179 #if defined(__sun) 180 #define WAI_PROC_SELF_EXE "/proc/self/path/a.out" 181 #else 182 #define WAI_PROC_SELF_EXE "/proc/self/exe" 183 #endif 184 #endif 185 186 WAI_FUNCSPEC 187 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 188 { 189 char buffer[PATH_MAX]; 190 char* resolved = NULL; 191 int length = -1; 192 193 for (;;) 194 { 195 resolved = realpath(WAI_PROC_SELF_EXE, buffer); 196 if (!resolved) 197 break; 198 199 length = (int)strlen(resolved); 200 if (length <= capacity) 201 { 202 memcpy(out, resolved, length); 203 204 if (dirname_length) 205 { 206 int i; 207 208 for (i = length - 1; i >= 0; --i) 209 { 210 if (out[i] == '/') 211 { 212 *dirname_length = i; 213 break; 214 } 215 } 216 } 217 } 218 219 break; 220 } 221 222 return length; 223 } 224 225 #if !defined(WAI_PROC_SELF_MAPS_RETRY) 226 #define WAI_PROC_SELF_MAPS_RETRY 5 227 #endif 228 229 #if !defined(WAI_PROC_SELF_MAPS) 230 #if defined(__sun) 231 #define WAI_PROC_SELF_MAPS "/proc/self/map" 232 #else 233 #define WAI_PROC_SELF_MAPS "/proc/self/maps" 234 #endif 235 #endif 236 237 #if defined(__ANDROID__) || defined(ANDROID) 238 #include <fcntl.h> 239 #include <sys/mman.h> 240 #include <unistd.h> 241 #endif 242 243 WAI_NOINLINE WAI_FUNCSPEC 244 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 245 { 246 int length = -1; 247 FILE* maps = NULL; 248 249 for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r) 250 { 251 maps = fopen(WAI_PROC_SELF_MAPS, "r"); 252 if (!maps) 253 break; 254 255 for (;;) 256 { 257 char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX]; 258 uint64_t low, high; 259 char perms[5]; 260 uint64_t offset; 261 uint32_t major, minor; 262 char path[PATH_MAX]; 263 uint32_t inode; 264 265 if (!fgets(buffer, sizeof(buffer), maps)) 266 break; 267 268 if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8) 269 { 270 uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS(); 271 if (low <= addr && addr <= high) 272 { 273 char* resolved; 274 275 resolved = realpath(path, buffer); 276 if (!resolved) 277 break; 278 279 length = (int)strlen(resolved); 280 #if defined(__ANDROID__) || defined(ANDROID) 281 if (length > 4 282 &&buffer[length - 1] == 'k' 283 &&buffer[length - 2] == 'p' 284 &&buffer[length - 3] == 'a' 285 &&buffer[length - 4] == '.') 286 { 287 int fd = open(path, O_RDONLY); 288 char* begin; 289 char* p; 290 291 begin = (char*)mmap(0, offset + sizeof(p), PROT_READ, MAP_SHARED, fd, 0); 292 p = begin + offset; 293 294 while (p >= begin) // scan backwards 295 { 296 if (*((uint32_t*)p) == 0x04034b50UL) // local file header found 297 { 298 uint16_t length_ = *((uint16_t*)(p + 26)); 299 300 if (length + 2 + length_ < (int)sizeof(buffer)) 301 { 302 memcpy(&buffer[length], "!/", 2); 303 memcpy(&buffer[length + 2], p + 30, length_); 304 length += 2 + length_; 305 } 306 307 break; 308 } 309 310 --p; 311 } 312 313 munmap(begin, offset); 314 close(fd); 315 } 316 #endif 317 if (length <= capacity) 318 { 319 memcpy(out, resolved, length); 320 321 if (dirname_length) 322 { 323 int i; 324 325 for (i = length - 1; i >= 0; --i) 326 { 327 if (out[i] == '/') 328 { 329 *dirname_length = i; 330 break; 331 } 332 } 333 } 334 } 335 336 break; 337 } 338 } 339 } 340 341 fclose(maps); 342 maps = NULL; 343 344 if (length != -1) 345 break; 346 } 347 348 if (maps) 349 fclose(maps); 350 351 return length; 352 } 353 354 #elif defined(__APPLE__) 355 356 #define _DARWIN_BETTER_REALPATH 357 #include <mach-o/dyld.h> 358 #include <limits.h> 359 #include <stdlib.h> 360 #include <string.h> 361 #include <dlfcn.h> 362 363 WAI_FUNCSPEC 364 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 365 { 366 char buffer1[PATH_MAX]; 367 char buffer2[PATH_MAX]; 368 char* path = buffer1; 369 char* resolved = NULL; 370 int length = -1; 371 372 for (;;) 373 { 374 uint32_t size = (uint32_t)sizeof(buffer1); 375 if (_NSGetExecutablePath(path, &size) == -1) 376 { 377 path = (char*)WAI_MALLOC(size); 378 if (!_NSGetExecutablePath(path, &size)) 379 break; 380 } 381 382 resolved = realpath(path, buffer2); 383 if (!resolved) 384 break; 385 386 length = (int)strlen(resolved); 387 if (length <= capacity) 388 { 389 memcpy(out, resolved, length); 390 391 if (dirname_length) 392 { 393 int i; 394 395 for (i = length - 1; i >= 0; --i) 396 { 397 if (out[i] == '/') 398 { 399 *dirname_length = i; 400 break; 401 } 402 } 403 } 404 } 405 406 break; 407 } 408 409 if (path != buffer1) 410 WAI_FREE(path); 411 412 return length; 413 } 414 415 WAI_NOINLINE WAI_FUNCSPEC 416 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 417 { 418 char buffer[PATH_MAX]; 419 char* resolved = NULL; 420 int length = -1; 421 422 for(;;) 423 { 424 Dl_info info; 425 426 if (dladdr(WAI_RETURN_ADDRESS(), &info)) 427 { 428 resolved = realpath(info.dli_fname, buffer); 429 if (!resolved) 430 break; 431 432 length = (int)strlen(resolved); 433 if (length <= capacity) 434 { 435 memcpy(out, resolved, length); 436 437 if (dirname_length) 438 { 439 int i; 440 441 for (i = length - 1; i >= 0; --i) 442 { 443 if (out[i] == '/') 444 { 445 *dirname_length = i; 446 break; 447 } 448 } 449 } 450 } 451 } 452 453 break; 454 } 455 456 return length; 457 } 458 459 #elif defined(__QNXNTO__) 460 461 #include <limits.h> 462 #include <stdio.h> 463 #include <stdlib.h> 464 #include <string.h> 465 #include <dlfcn.h> 466 467 #if !defined(WAI_PROC_SELF_EXE) 468 #define WAI_PROC_SELF_EXE "/proc/self/exefile" 469 #endif 470 471 WAI_FUNCSPEC 472 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 473 { 474 char buffer1[PATH_MAX]; 475 char buffer2[PATH_MAX]; 476 char* resolved = NULL; 477 FILE* self_exe = NULL; 478 int length = -1; 479 480 for (;;) 481 { 482 self_exe = fopen(WAI_PROC_SELF_EXE, "r"); 483 if (!self_exe) 484 break; 485 486 if (!fgets(buffer1, sizeof(buffer1), self_exe)) 487 break; 488 489 resolved = realpath(buffer1, buffer2); 490 if (!resolved) 491 break; 492 493 length = (int)strlen(resolved); 494 if (length <= capacity) 495 { 496 memcpy(out, resolved, length); 497 498 if (dirname_length) 499 { 500 int i; 501 502 for (i = length - 1; i >= 0; --i) 503 { 504 if (out[i] == '/') 505 { 506 *dirname_length = i; 507 break; 508 } 509 } 510 } 511 } 512 513 break; 514 } 515 516 fclose(self_exe); 517 518 return length; 519 } 520 521 WAI_FUNCSPEC 522 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 523 { 524 char buffer[PATH_MAX]; 525 char* resolved = NULL; 526 int length = -1; 527 528 for(;;) 529 { 530 Dl_info info; 531 532 if (dladdr(WAI_RETURN_ADDRESS(), &info)) 533 { 534 resolved = realpath(info.dli_fname, buffer); 535 if (!resolved) 536 break; 537 538 length = (int)strlen(resolved); 539 if (length <= capacity) 540 { 541 memcpy(out, resolved, length); 542 543 if (dirname_length) 544 { 545 int i; 546 547 for (i = length - 1; i >= 0; --i) 548 { 549 if (out[i] == '/') 550 { 551 *dirname_length = i; 552 break; 553 } 554 } 555 } 556 } 557 } 558 559 break; 560 } 561 562 return length; 563 } 564 565 #elif defined(__DragonFly__) || defined(__FreeBSD__) || \ 566 defined(__FreeBSD_kernel__) || defined(__NetBSD__) 567 568 #include <limits.h> 569 #include <stdlib.h> 570 #include <string.h> 571 #include <sys/types.h> 572 #include <sys/sysctl.h> 573 #include <dlfcn.h> 574 575 WAI_FUNCSPEC 576 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 577 { 578 char buffer1[PATH_MAX]; 579 char buffer2[PATH_MAX]; 580 char* path = buffer1; 581 char* resolved = NULL; 582 int length = -1; 583 584 for (;;) 585 { 586 #if defined(__NetBSD__) 587 int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; 588 #else 589 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; 590 #endif 591 size_t size = sizeof(buffer1); 592 593 if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0) 594 break; 595 596 resolved = realpath(path, buffer2); 597 if (!resolved) 598 break; 599 600 length = (int)strlen(resolved); 601 if (length <= capacity) 602 { 603 memcpy(out, resolved, length); 604 605 if (dirname_length) 606 { 607 int i; 608 609 for (i = length - 1; i >= 0; --i) 610 { 611 if (out[i] == '/') 612 { 613 *dirname_length = i; 614 break; 615 } 616 } 617 } 618 } 619 620 break; 621 } 622 623 if (path != buffer1) 624 WAI_FREE(path); 625 626 return length; 627 } 628 629 WAI_NOINLINE WAI_FUNCSPEC 630 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 631 { 632 char buffer[PATH_MAX]; 633 char* resolved = NULL; 634 int length = -1; 635 636 for(;;) 637 { 638 Dl_info info; 639 640 if (dladdr(WAI_RETURN_ADDRESS(), &info)) 641 { 642 resolved = realpath(info.dli_fname, buffer); 643 if (!resolved) 644 break; 645 646 length = (int)strlen(resolved); 647 if (length <= capacity) 648 { 649 memcpy(out, resolved, length); 650 651 if (dirname_length) 652 { 653 int i; 654 655 for (i = length - 1; i >= 0; --i) 656 { 657 if (out[i] == '/') 658 { 659 *dirname_length = i; 660 break; 661 } 662 } 663 } 664 } 665 } 666 667 break; 668 } 669 670 return length; 671 } 672 673 #else 674 675 #error unsupported platform 676 677 #endif 678 679 #ifdef __cplusplus 680 } 681 #endif