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 §s[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 §s[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 }