1 /**
2     POSIX Implementation for nulib.system.mod
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.posix.mod;
12 
13 version (OSX)
14     version = Darwin;
15 else version (iOS)
16     version = Darwin;
17 else version (TVOS)
18     version = Darwin;
19 else version (WatchOS)
20     version = Darwin;
21 else version (VisionOS)
22     version = Darwin;
23 
24 version(Posix):
25 
26 import numem;
27 
28 /**
29     A symbol within a section.
30 */
31 struct Symbol {
32 @nogc:
33 
34     /**
35         Name of the symbol
36     */
37     string name;
38     
39     /**
40         Pointer to the symbol
41     */
42     void* ptr;
43 }
44 
45 /**
46     Information about sections.
47 */
48 struct SectionInfo {
49 @nogc:
50     
51     /**
52         Segment of the section, if applicable.
53     */
54     string segment;
55 
56     /**
57         Name of the section
58     */
59     string section;
60 
61     /**
62         Start address of the section
63     */
64     void* start;
65 
66     /**
67         End address of the section
68     */
69     void* end;
70 }
71 
72 //
73 //          FOR IMPLEMENTORS
74 //
75 
76 private extern (C):
77 
78 /**/
79 export
80 bool _nu_module_utf16_paths() @nogc nothrow {
81     return false;
82 }
83 
84 /*
85     Function which loads a module from the given path.
86 
87     This is implemented by backends to abstract away the OS intricaices
88     of loading modules and finding symbols within.
89 */
90 export
91 void* _nu_module_open(void* path) @nogc nothrow {
92     version(Darwin) {
93         if (path is null)
94             path = cast(void*)_dyld_get_image_name(0);
95     }
96 
97     void* handle = dlopen(cast(const(char)*)path, 0);
98     return handle;
99 }
100 
101 /*
102     Function which loads a module from the given path.
103 
104     This is implemented by backends to abstract away the OS intricaices
105     of loading modules and finding symbols within.
106 */
107 export
108 void _nu_module_close(void* module_) @nogc nothrow {
109     cast(void) dlclose(module_);
110 }
111 
112 /*
113     Function which finds a symbol within a given module
114 
115     This is implemented by backends to abstract away the OS intricaices
116     of loading modules and finding symbols within.
117 */
118 export
119 void* _nu_module_get_symbol(void* module_, const(char)* symbol) @nogc nothrow {
120     return dlsym(module_, symbol);
121 }
122 
123 /*
124     Function which gets the "base address" of the module.
125 
126     This is backend implementation defined; but is what should allow
127     $(D _nu_module_enumerate_sections) and $(D _nu_module_enumerate_symbols)
128     to function.
129 */
130 export
131 void* _nu_module_get_base_address(void* module_) @nogc nothrow {
132     version(Darwin) {
133         foreach(i; 0.._dyld_image_count()) {
134 
135             // Find the image.
136             const(char)* imageName = _dyld_get_image_name(i);
137             void* imageHandle = dlopen(imageName, 0);
138             cast(void)dlclose(imageHandle);
139 
140             if (module_ == imageHandle) {
141                 return nogc_new!mach_image(
142                     cast(mach_header_64*)_dyld_get_image_header(i),
143                     _dyld_get_image_vmaddr_slide(i)
144                 );
145             }
146         }
147         return null;
148     } else {
149 
150         Dl_info info;
151         if (dladdr(module_, info) != 0)
152             return info.dli_fbase;
153 
154         return null;
155     }
156 }
157 
158 export
159 void _nu_module_release_base_address(void* module_) @nogc nothrow {
160     version(Darwin) {
161         if (module_)
162             nu_free(cast(mach_image*)module_);
163     }
164 }
165 
166 export
167 SectionInfo[] _nu_module_enumerate_sections(void* base) @nogc nothrow {
168     version(Darwin) {
169         mach_image* image = cast(mach_image*)base;
170         if (!_nu_module_darwin_get_is_supported(image))
171             return null;
172         
173         uint symtabCount;
174         SectionInfo[] allSections;
175         load_command* lcmd = _nu_module_get_first_command(image);
176         foreach(i; 0..image.header.ncmds) {
177 
178             SectionInfo[] sections;
179             if (lcmd.cmd == LC_SEGMENT) {
180 
181                 segment_command_32* segcmd = cast(segment_command_32*)(cast(void*)lcmd);
182                 sections = sections.nu_resize(segcmd.nsects);
183                 section_32* sect = cast(section_32*)(cast(void*)lcmd+segment_command_32.sizeof);
184                 foreach(j; 0..segcmd.nsects) {
185 
186                     sections[j].segment = _nu_module_darwin_get_name(sect.segname);
187                     sections[j].section = _nu_module_darwin_get_name(sect.sectname);
188                     sections[j].start = cast(void*)sect.addr+image.vmaddrSlide;
189                     sections[j].end = cast(void*)sect.addr+image.vmaddrSlide+sect.size;
190 
191                     // Next section.
192                     sect++;
193                 }
194 
195                 allSections = _nu_module_darwin_combine_sect_infos(allSections, sections);
196             } else if (lcmd.cmd == LC_SEGMENT_64) {
197                 
198                 segment_command_64* segcmd = cast(segment_command_64*)(cast(void*)lcmd);
199                 sections = sections.nu_resize(segcmd.nsects);
200                 section_64* sect = cast(section_64*)(cast(void*)lcmd+segment_command_64.sizeof);
201                 foreach(j; 0..segcmd.nsects) {
202 
203                     sections[j].segment = _nu_module_darwin_get_name(sect.segname);
204                     sections[j].section = _nu_module_darwin_get_name(sect.sectname);
205                     sections[j].start = cast(void*)sect.addr+image.vmaddrSlide;
206                     sections[j].end = cast(void*)sect.addr+image.vmaddrSlide+sect.size;
207 
208                     // Next section.
209                     sect++;
210                 }
211                 
212                 allSections = _nu_module_darwin_combine_sect_infos(allSections, sections);
213             }
214 
215             if (lcmd.cmd == LC_SYMTAB)
216                 symtabCount++;
217 
218             lcmd = _nu_module_darwin_next_command(lcmd);
219         }
220 
221         return allSections;
222     } else {
223         return null;
224     }
225 }
226 
227 export
228 Symbol[] _nu_module_enumerate_symbols(void* base) @nogc nothrow {
229     version(Darwin) {
230         mach_image* image = cast(mach_image*)base;
231         if (!_nu_module_darwin_get_is_supported(image))
232             return null;
233 
234         // MH_DYLIB_IN_CACHE
235         if (image.header.flags & 0x80000000)
236             return null;
237 
238         // Locate nlists
239         symtab_command* symcmd = cast(symtab_command*)_nu_module_darwin_find_command(image, LC_SYMTAB);
240         if (!symcmd)
241             return null;
242         
243         const(char)* strtab = cast(const(char)*)(base + image.vmaddrSlide + symcmd.stroff);
244         nlist[]      symtab = (cast(nlist*)(base + symcmd.symoff))[0..symcmd.nsyms];
245 
246         // Fill out symbols.
247         size_t nlen = 0;
248         Symbol[] allSymbols;
249         allSymbols = allSymbols.nu_resize(symcmd.nsyms);
250         foreach(j; 0..symcmd.nsyms) {
251             nlist nl = symtab[j];
252             if (nl.n_strx == 0)
253                 continue;
254             
255             // Externally defined.
256             if (nl.n_type & 0x01)
257                 continue;
258 
259             void* addr = base+nl.n_value;
260             ubyte typeFlag = nl.n_type & 0x0e;
261 
262             // No symbol
263             if (typeFlag == 0x00)
264                 addr = null;
265             
266             // Absolute symbol.
267             if (typeFlag == 0x02)
268                 addr = cast(void*)nl.n_value;
269             
270             // TODO: Implement indirect symbols.
271             if (typeFlag == 0xa0)
272                 addr = null;
273 
274             allSymbols[nlen++] = Symbol(_nu_strz(strtab+nl.n_strx), addr);
275         }
276 
277         // If we found no symbols just empty the list.
278         if (nlen == 0) {
279             allSymbols = allSymbols.nu_resize(0);
280             return null;
281         }
282         return allSymbols[0..nlen];
283     } else {
284         return null;
285     }
286 }
287 
288 
289 
290 
291 
292 //
293 //          SYSTEM SPECIFIC HELPERS
294 //
295 
296 version(Darwin) {
297     string _nu_module_darwin_get_name(ref char[16] name) @nogc nothrow {
298         foreach(i; 0..name.length) {
299             if (name[i] == '\0')
300                 return cast(string)name[0..i];
301         }
302         return cast(string)name;
303     }
304 
305     SectionInfo[] _nu_module_darwin_combine_sect_infos(SectionInfo[] a, SectionInfo[] b) @nogc nothrow {
306         if (!a && b) return b;
307 	if (a && !b) return a;        
308 
309         SectionInfo[] c;
310         c = c.nu_resize(a.length+b.length);
311         c[0..a.length]   = a[0..$];
312         c[$-b.length..$] = b[0..$];
313         
314         a = a.nu_resize(0);
315         b = b.nu_resize(0);
316         return c;
317     }
318 
319     /**
320         Gets whether the given image is valid
321     */
322     bool _nu_module_darwin_get_is_supported(mach_image* image) @nogc nothrow {
323         return image !is null && (
324             image.header.magic == MH_MAGIC || 
325             image.header.magic == MH_MAGIC_64
326         );
327     }
328 
329     /*
330         Gets the first load command
331     */
332     load_command* _nu_module_get_first_command(mach_image* image) @nogc nothrow {
333         return cast(load_command*)(
334             image.header.magic == MH_MAGIC ?
335                 (cast(void*)image.header)+mach_header_32.sizeof :
336                 (cast(void*)image.header)+mach_header_64.sizeof
337         );
338     }
339 
340     /*
341         Gets the load command with the requested name.
342     */
343     load_command* _nu_module_darwin_find_command(mach_image* image, uint magic) @nogc nothrow {
344         load_command* cmd = _nu_module_get_first_command(image);
345         foreach(i; 0..image.header.ncmds) {
346             if (cmd.cmd == magic)
347                 return cmd;
348 
349             cmd = _nu_module_darwin_next_command(cmd);
350         }
351         return null;
352     }
353 
354     /*
355         Gets the next command.
356     */
357     pragma(inline, true)
358     load_command* _nu_module_darwin_next_command(load_command* cmd) @nogc nothrow {
359         return cast(load_command*)((cast(void*)cmd)+cmd.cmdsize);
360     }
361 
362     Symbol[] _nu_module_darwin_combine_syms(Symbol[] a, Symbol[] b) @nogc nothrow {
363         if (!a && b) return b;
364 	if (a && !b) return a;        
365 
366         Symbol[] c;
367         c = c.nu_resize(a.length+b.length);
368         c[0..a.length]   = a[0..$];
369         c[$-b.length..$] = b[0..$];
370         
371         a = a.nu_resize(0);
372         b = b.nu_resize(0);
373         return c;
374     }
375     
376 } else {
377     
378 }
379 
380 
381 //
382 //          HELPERS
383 //
384 
385 extern(D)
386 string _nu_strz(const(char)* str) @nogc nothrow {
387     size_t i = 0;
388     while(str[i] != '\0') i++;
389     return cast(string)str[0..i];
390 }
391 
392 
393 //
394 //          LOCAL BINDINGS
395 //
396 
397 extern(C) extern void* dlopen(const(char)*, int) @nogc nothrow;
398 extern(C) extern void* dlsym(void*, const(char)*) @nogc nothrow;
399 extern(C) extern int dladdr(const(void)*, ref Dl_info) @nogc nothrow;
400 extern(C) extern int dlclose(void*) @nogc nothrow;
401 
402 struct Dl_info {
403     const(char)* dli_fname;
404     void* dli_fbase;
405     const(char)* dli_sname;
406     void* dli_saddr;
407 }
408 
409 version(Darwin) {
410     extern(C) extern int _dyld_image_count() @nogc nothrow;
411     extern(C) extern void* _dyld_get_image_header(uint) @nogc nothrow;
412     extern(C) extern const(char)* _dyld_get_image_name(uint) @nogc nothrow;
413     extern(C) extern ptrdiff_t _dyld_get_image_vmaddr_slide(uint) @nogc nothrow;
414 
415     enum uint
416         MH_MAGIC_64 = 0xfeedfacf,
417         MH_MAGIC = 0xfeedface;
418     
419     enum uint
420         LC_SYMTAB = 0x2,
421         LC_SEGMENT = 0x1,
422         LC_SEGMENT_64 = 0x19;
423 
424     struct mach_image {
425         mach_header_64* header;
426         ptrdiff_t vmaddrSlide;
427     }
428 
429     struct mach_header_32 {
430         uint magic;
431         int cputype;
432         int cpusubtype;
433         uint filetype;
434         uint ncmds;
435         uint sizeofcmds;
436         uint flags;
437     }
438 
439     struct mach_header_64 {
440         uint magic;
441         int cputype;
442         int cpusubtype;
443         uint filetype;
444         uint ncmds;
445         uint sizeofcmds;
446         uint flags;
447         uint reserved;
448     }
449 
450     struct load_command {
451         uint cmd;
452         uint cmdsize;
453     }
454 
455     struct segment_command_32 {
456         uint cmd;
457         uint cmdsize;
458         char[16] segname = 0;
459         uint vmaddr;
460         uint vmsize;
461         uint fileoff;
462         uint filesize;
463         int maxprot;
464         int initprot;
465         uint nsects;
466         uint flags;
467     }
468 
469     struct segment_command_64 {
470         uint cmd;
471         uint cmdsize;
472         char[16] segname = 0;
473         ulong vmaddr;
474         ulong vmsize;
475         ulong fileoff;
476         ulong filesize;
477         int maxprot;
478         int initprot;
479         uint nsects;
480         uint flags;
481     }
482 
483     struct section_32 {
484         char[16] sectname = 0;
485         char[16] segname = 0;
486         uint addr;
487         uint size;
488         uint offset;
489         uint align_;
490         uint reloff;
491         uint nreloc;
492         uint flags;
493         uint reserved1;
494         uint reserved2;
495     }
496 
497     struct section_64 {
498         char[16] sectname = 0;
499         char[16] segname = 0;
500         ulong addr;
501         ulong size;
502         uint offset;
503         uint align_;
504         uint reloff;
505         uint nreloc;
506         uint flags;
507         uint reserved1;
508         uint reserved2;
509         uint reserved3;
510     }
511 
512     struct nlist {
513         uint n_strx;
514         ubyte n_type;
515         ubyte n_sect;
516         ushort n_desc;
517         size_t n_value;
518     }
519 
520     struct symtab_command {
521         uint cmd;
522         uint cmdsize;
523         uint symoff;
524         uint nsyms;
525         uint stroff;
526         uint strsize;
527     }
528 }