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 }