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