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