1+ /*
2+ Copyright © 2024, Kitsunebi Games EMV
3+ Distributed under the Boost Software License, Version 1.0,
4+ see LICENSE file.
5+
6+ Authors: Luna Nielsen
7+ */
8+
9+ /**
10+ Extensions to dyld API.
11+ */
12+ module os.mach.dyld ;
13+ import dyld = core.sys.darwin.mach.dyld ;
14+ import core.sys.darwin.dlfcn ;
15+ import core.sys.darwin.mach.loader ;
16+ import core.sys.darwin.mach.dyld :
17+ _dyld_get_image_header,
18+ _dyld_get_image_name,
19+ _dyld_image_count;
20+
21+ version (OSX )
22+ version = Darwin;
23+ else version (iOS)
24+ version = Darwin;
25+ else version (TVOS)
26+ version = Darwin;
27+ else version (WatchOS)
28+ version = Darwin;
29+
30+ version (Darwin):
31+ extern (C ) nothrow @nogc :
32+
33+ /**
34+ 32-bit mach header.
35+ */
36+ alias mach_header_32 = dyld.mach_header;
37+
38+ /**
39+ 64-bit mach header.
40+ */
41+ alias mach_header_64 = dyld.mach_header_64;
42+
43+ /**
44+ 32-bit mach section.
45+ */
46+ alias mach_section_32 = dyld.section;
47+
48+ /**
49+ 64-bit mach section.
50+ */
51+ alias mach_section_64 = dyld.section_64;
52+
53+ /**
54+ 32-bit mach section.
55+ */
56+ alias mach_segment_32 = dyld.segment_command;
57+
58+ /**
59+ 64-bit mach section.
60+ */
61+ alias mach_segment_64 = dyld.segment_command_64;
62+
63+ /**
64+ A mach header which matches the current device's architecture.
65+ */
66+ version (D_LP64 ) alias mach_header = mach_header_64;
67+ else alias mach_header = mach_header_32;
68+
69+ /**
70+ A mach section which matches the current device's architecture.
71+ */
72+ version (D_LP64 ) alias mach_section = mach_section_64;
73+ else alias mach_section = mach_section_32;
74+
75+ /**
76+ A mach segment which matches the current device's architecture.
77+ */
78+ version (D_LP64 ) alias mach_segment = mach_segment_64;
79+ else alias mach_segment = mach_segment_32;
80+
81+ // Mask value for the mode value added to a dlopen handle.
82+ enum size_t RTLD_MODEMASK = cast (size_t )- 4 ;
83+
84+ /**
85+ Attempt to find a mach_header from its dlopen handle.
86+
87+ Returns: mach_header address or `null` on failure.
88+ */
89+ const (mach_header)* dyld_get_dlopen_image_header (void * handle) {
90+ auto idx = dylib_get_dlopen_image_index(handle);
91+ if (idx >= 0 ) {
92+ return cast (const (mach_header)* )_dyld_get_image_header(cast (uint )idx);
93+ }
94+ return null ;
95+ }
96+
97+ /**
98+ Attempts to get the image path of a dlopen handle.
99+
100+ Returns: Image path on success, `null` on failure.
101+ */
102+ const (char )* dylib_get_dlopen_image_path (void * handle) {
103+ auto idx = dylib_get_dlopen_image_index(handle);
104+ if (idx >= 0 ) {
105+ return _dyld_get_image_name (cast (uint )idx);
106+ }
107+ return null ;
108+ }
109+
110+ /**
111+ Attempts to get the image index of a dlopen handle.
112+
113+ Returns: Image index on success, `-1` on failure.
114+ */
115+ ptrdiff_t dylib_get_dlopen_image_index (void * handle) {
116+ foreach (i; 0 .. _dyld_image_count()) {
117+
118+ // By passing RTLD_NOLOAD we won't be attempting to
119+ // load an unloaded library.
120+ // This prevents oopsies where you're trying to get the index
121+ // of an image that is somehow not loaded (name mismatch??)
122+ const (char )* name = _dyld_get_image_name(i);
123+ if (void * probeHandle = dlopen(name, RTLD_NOLOAD | RTLD_LAZY )) {
124+ dlclose(probeHandle);
125+
126+ // We strip the mode bits off the handle.
127+ // So that we can match handles despite which RTLD parameters were passed.
128+ if ((cast (size_t )handle & RTLD_MODEMASK ) ==
129+ (cast (size_t )probeHandle & RTLD_MODEMASK ))
130+ return i;
131+ }
132+ }
133+ return - 1 ;
134+ }
135+
136+ /**
137+ Finds dlopen handle associated with the given mach_header.
138+
139+ Returns: Handle on success, `-1` on failure.
140+ */
141+ void * dylib_get_handle_for_header (const (mach_header)* header) {
142+ Dl_info info;
143+ if (dladdr(header, &info)) {
144+
145+ // Lookup the handle by getting the dli_fname from the header.
146+ // since the header is located *within* the address space of
147+ // the dylib.
148+ void * handle = dlopen(info.dli_fname, RTLD_NOLOAD | RTLD_LAZY );
149+ dlclose(handle);
150+ return handle;
151+ }
152+
153+ // Not found.
154+ return null ;
155+ }
0 commit comments