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