1 /**
2     Shared Library Modules
3 
4     Copyright:
5         Copyright © 2025, Kitsunebi Games
6         Copyright © 2025, Inochi2D Project
7     
8     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9     Authors:   Luna Nielsen
10 */
11 module nulib.system.mod;
12 import nulib.collections.vector;
13 import nulib.system;
14 import nulib.string;
15 import numem;
16 
17 /**
18     A higher level wrapper over OS level modules/shared libraries.
19 
20     The module is lazy-loading, meaning segment and symbol lists
21     will only be enumerated when requested; this in turn means
22     modules can be relatively lightweight.
23 */
24 class Module : NuRefCounted {
25 private:
26 @nogc:
27     __gshared Module _self;
28     Handle handle;
29 
30     void* baseaddr;
31     SectionInfo[] sections_;
32     Symbol[] symbols_;
33 
34     // Cleanup function.
35     void cleanup() {
36         this.sections_ = sections_.nu_resize(0);
37         this.symbols_ = symbols_.nu_resize(0);
38     }
39 
40     // Converts UTF8 path to a native path.
41     void* getNativePath(string path) {
42         if (!path)
43             return null;
44 
45         if (_nu_module_utf16_paths()) {
46             
47             nwstring tmp = path;
48             const(wchar)[] utf16 = tmp.ptr[0..tmp.realLength].nu_dup();
49             return cast(void*)utf16.ptr;
50         }
51 
52         nstring tmp = path;
53         const(char)[] utf8 = tmp.ptr[0..tmp.realLength].nu_dup();
54         return cast(void*)utf8.ptr;
55     }
56 
57 public:
58 
59     /**
60         List of all sections in the module.
61     */
62     final
63     @property SectionInfo[] sections() {
64         if (!baseaddr)
65             return null;
66         
67         if (!sections_)
68             this.sections_ = _nu_module_enumerate_sections(baseaddr);
69         return sections_;
70     }
71 
72     /**
73         List of all exported symbols in the module.
74     */
75     final
76     @property Symbol[] symbols() {
77         if (!baseaddr)
78             return null;
79         
80         if (!this.symbols_)
81             this.symbols_ = _nu_module_enumerate_symbols(baseaddr);
82         return symbols_; 
83     }
84 
85     /**
86         The base address the module is loaded into.
87     */
88     final
89     @property void* baseAddress() {
90         return baseaddr;
91     }
92 
93     /**
94         Gets the calling module.
95     */
96     static @property Module self() {
97         if (!_self)
98             _self = nogc_new!Module(null);
99         
100         return _self;
101     }
102 
103     /**
104         Loads a module from the given path.
105 
106         This function has more "smarts" than the constructor, and will
107         transform the incoming path to find the library in question.
108 
109         This includes things like looking up macOS bundles, and the like.
110 
111         Params:
112             pathOrName = Path to; or name of the module to load.
113         
114         Returns:
115             A new module, or $(D null) if the module failed to load.
116     */
117     static Module load(string pathOrName) {
118         if (!pathOrName)
119             return null;
120 
121         Module mod = nogc_new!Module(_nu_module_transform_path(pathOrName));
122         if (mod.handle is null) {
123             mod.release();
124             return null;
125         }
126 
127         return mod;
128     }
129 
130     /**
131         Loads a module from a path into this application's address space,
132         without doing any path transformations.
133 
134         It is recommended to use $(D Module.load) instead of this.
135 
136         Params:
137             path = Path to a module.
138     */
139     this(string path) {
140         void* rpath = getNativePath(path);
141         this.handle = cast(Handle)_nu_module_open(rpath);
142         this.baseaddr = _nu_module_get_base_address(handle);
143         
144         if (rpath) nu_free(rpath);
145     }
146 
147     /**
148         Destructor
149     */
150     ~this() {
151         this.cleanup();
152         _nu_module_release_base_address(handle);
153         _nu_module_close(handle);
154     }
155     
156     /**
157         Gets a symbol from the module.
158 
159         Params:
160             sym = The name of the symbol to get.
161         
162         Returns:
163             A pointer to the symbol or $(D null) if the
164             symbol was not found.
165     */
166     final
167     void* getSymbol(string sym) {
168         nstring psym = sym;
169         return _nu_module_get_symbol(handle, psym.ptr);
170     }
171 
172     /**
173         Lists all symbols within the given section.
174 
175         Params:
176             section = The section to query.
177         
178         Returns:
179             A weak vector containing the symbols within the given
180             section's memory space..
181     */
182     weak_vector!Symbol createSymbolListFor(SectionInfo section) {
183         weak_vector!Symbol rsymbols;
184 
185         Symbol[] syms = symbols;
186         foreach(i; 0..syms.length) 
187             if (syms[i].ptr >= section.start && syms[i].ptr <= section.end)
188                 rsymbols ~= syms[i];
189         
190         return rsymbols;
191     }
192 
193     /**
194         Finds a section within the module, and optionally limits the
195         search to be within a specific segment, if applicable.
196 
197         Params:
198             section = The section to find
199             segment = The segment to find it in, or $(D null).
200         
201         Returns:
202             A reference to a section info object describing the section, 
203             owned by the module, or $(D null) if not found.
204     */
205     final
206     SectionInfo* findSection(string section, string segment = null) {
207         SectionInfo[] sects = sections;
208         foreach(i; 0..sects.length) {
209             if (sects[i].section == section) {
210                 if (segment && sects[i].segment != segment)
211                     continue;
212                 
213                 return &sects[i];
214             }
215         }
216         
217         return null;
218     }
219 
220     /**
221         Gets the section which a symbol belongs to.
222 
223         Params:
224             sym = Pointer to a symbol in the module, as returned by $(D getSymbol).
225         
226         Returns:
227             A reference to a section info object describing the section, 
228             owned by the module, or $(D null) if not found.
229     */
230     final
231     SectionInfo* getSymbolSection(void* sym) {
232         SectionInfo[] sects = sections;
233         foreach(i; 0..sects.length) 
234             if (sym >= sects[i].start && sym <= sects[i].end)
235                 return &sects[i];
236 
237         // Not found.
238         return null;
239     }
240 }
241 
242 /**
243     A symbol within a section.
244 */
245 struct Symbol {
246 @nogc:
247 
248     /**
249         Name of the symbol
250     */
251     string name;
252     
253     /**
254         Pointer to the symbol
255     */
256     void* ptr;
257 }
258 
259 /**
260     Information about sections.
261 */
262 struct SectionInfo {
263 @nogc:
264     
265     /**
266         Segment of the section, if applicable.
267     */
268     string segment;
269 
270     /**
271         Name of the section
272     */
273     string section;
274 
275     /**
276         Start address of the section
277     */
278     void* start;
279 
280     /**
281         End address of the section
282     */
283     void* end;
284 }
285 
286 //
287 //          FOR IMPLEMENTORS
288 //
289 
290 private extern(C):
291 import core.attribute : weak;
292 
293 /*
294     Optional helper function for platforms which have special kinds of "Modules",
295     eg. Framework Bundles on macOS.
296 
297     See: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
298 */
299 string _nu_module_transform_path(string path) @weak @nogc nothrow {
300     return path;
301 }
302 
303 /**
304     Optional helper that defines whether paths are in UTF-16 format.
305 */
306 bool _nu_module_utf16_paths() @weak @nogc nothrow {
307     return false;
308 }
309 
310 /*
311     Function which loads a module from the given path.
312 
313     This is implemented by backends to abstract away the OS intricaices
314     of loading modules and finding symbols within.
315 */
316 void* _nu_module_open(void* path) @weak @nogc nothrow {
317     return null;
318 }
319 
320 /*
321     Function which loads a module from the given path.
322 
323     This is implemented by backends to abstract away the OS intricaices
324     of loading modules and finding symbols within.
325 */
326 void _nu_module_close(void* module_) @weak @nogc nothrow {
327     return;
328 }
329 
330 /*
331     Function which finds a symbol within a given module
332 
333     This is implemented by backends to abstract away the OS intricaices
334     of loading modules and finding symbols within.
335 */
336 void* _nu_module_get_symbol(void* module_, const(char)* symbol) @weak @nogc nothrow {
337     return null;
338 }
339 
340 /*
341     Function which gets the "base address" of the module.
342 
343     This is backend implementation defined; but is what should allow
344     $(D _nu_module_enumerate_sections) and $(D _nu_module_enumerate_symbols)
345     to function.
346 */
347 void* _nu_module_get_base_address(void* module_) @weak @nogc nothrow {
348     return null;
349 }
350 
351 /*
352     Function which releases the "base address" of the module.
353 
354     This is backend implementation defined; but is what should allow
355     $(D _nu_module_enumerate_sections) and $(D _nu_module_enumerate_symbols)
356     to function.
357 */
358 void _nu_module_release_base_address(void* module_) @weak @nogc nothrow {
359     return;
360 }
361 
362 /*
363     Function which enumerates all of the sections within a module.
364 */
365 SectionInfo[] _nu_module_enumerate_sections(void* base) @weak @nogc nothrow {
366     return null;
367 }
368 
369 /*
370     Function which enumerates all of the exported symbols.
371 */
372 Symbol[] _nu_module_enumerate_symbols(void* base) @weak @nogc nothrow {
373     return null;
374 }