]>
Commit | Line | Data |
---|---|---|
b804b9cd | 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 | #if defined(_MSC_VER) | |
55 | #pragma warning(pop) | |
56 | #endif | |
57 | ||
58 | static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) | |
59 | { | |
60 | wchar_t buffer1[MAX_PATH]; | |
61 | wchar_t buffer2[MAX_PATH]; | |
62 | wchar_t* path = NULL; | |
63 | int length = -1; | |
64 | ||
65 | for (;;) | |
66 | { | |
67 | DWORD size; | |
68 | int length_, length__; | |
69 | ||
70 | size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); | |
71 | ||
72 | if (size == 0) | |
73 | break; | |
74 | else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0]))) | |
75 | { | |
76 | DWORD size_ = size; | |
77 | do | |
78 | { | |
79 | wchar_t* path_; | |
80 | ||
81 | path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2); | |
82 | if (!path_) | |
83 | break; | |
84 | size_ *= 2; | |
85 | path = path_; | |
86 | size = GetModuleFileNameW(module, path, size_); | |
87 | } | |
88 | while (size == size_); | |
89 | ||
90 | if (size == size_) | |
91 | break; | |
92 | } | |
93 | else | |
94 | path = buffer1; | |
95 | ||
96 | if (!_wfullpath(buffer2, path, MAX_PATH)) | |
97 | break; | |
98 | length_ = (int)wcslen(buffer2); | |
99 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL); | |
100 | ||
101 | if (length__ == 0) | |
102 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); | |
103 | if (length__ == 0) | |
104 | break; | |
105 | ||
106 | if (length__ <= capacity && dirname_length) | |
107 | { | |
108 | int i; | |
109 | ||
110 | for (i = length__ - 1; i >= 0; --i) | |
111 | { | |
112 | if (out[i] == '\\') | |
113 | { | |
114 | *dirname_length = i; | |
115 | break; | |
116 | } | |
117 | } | |
118 | } | |
119 | ||
120 | length = length__; | |
121 | ||
122 | break; | |
123 | } | |
124 | ||
125 | if (path != buffer1) | |
126 | WAI_FREE(path); | |
127 | ||
128 | return length; | |
129 | } | |
130 | ||
131 | WAI_NOINLINE | |
132 | WAI_FUNCSPEC | |
133 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) | |
134 | { | |
135 | return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length); | |
136 | } | |
137 | ||
d32691f1 | 138 | /* |
b804b9cd | 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 | |
d32691f1 | 150 | if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module)) |
b804b9cd | 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 | } | |
d32691f1 | 160 | */ |
b804b9cd | 161 | |
162 | #elif defined(__linux__) | |
b87ad796 | 163 | #define _DEFAULT_SOURCE |
b804b9cd | 164 | #include <stdio.h> |
165 | #include <stdlib.h> | |
166 | #include <string.h> | |
167 | #include <linux/limits.h> | |
168 | #ifndef __STDC_FORMAT_MACROS | |
169 | #define __STDC_FORMAT_MACROS | |
170 | #endif | |
171 | #include <inttypes.h> | |
172 | ||
173 | #if !defined(WAI_PROC_SELF_EXE) | |
174 | #define WAI_PROC_SELF_EXE "/proc/self/exe" | |
175 | #endif | |
176 | ||
177 | WAI_FUNCSPEC | |
178 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) | |
179 | { | |
180 | char buffer[PATH_MAX]; | |
181 | char* resolved = NULL; | |
182 | int length = -1; | |
183 | ||
184 | for (;;) | |
185 | { | |
186 | resolved = realpath(WAI_PROC_SELF_EXE, buffer); | |
187 | if (!resolved) | |
188 | break; | |
189 | ||
190 | length = (int)strlen(resolved); | |
191 | if (length <= capacity) | |
192 | { | |
193 | memcpy(out, resolved, length); | |
194 | ||
195 | if (dirname_length) | |
196 | { | |
197 | int i; | |
198 | ||
199 | for (i = length - 1; i >= 0; --i) | |
200 | { | |
201 | if (out[i] == '/') | |
202 | { | |
203 | *dirname_length = i; | |
204 | break; | |
205 | } | |
206 | } | |
207 | } | |
208 | } | |
209 | ||
210 | break; | |
211 | } | |
212 | ||
213 | return length; | |
214 | } | |
215 | ||
216 | #if !defined(WAI_PROC_SELF_MAPS_RETRY) | |
217 | #define WAI_PROC_SELF_MAPS_RETRY 5 | |
218 | #endif | |
219 | ||
220 | #if !defined(WAI_PROC_SELF_MAPS) | |
221 | #define WAI_PROC_SELF_MAPS "/proc/self/maps" | |
222 | #endif | |
223 | ||
224 | #if defined(__ANDROID__) || defined(ANDROID) | |
225 | #include <fcntl.h> | |
226 | #include <sys/mman.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 i; | |
312 | ||
313 | for (i = length - 1; i >= 0; --i) | |
314 | { | |
315 | if (out[i] == '/') | |
316 | { | |
317 | *dirname_length = i; | |
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 |