NSModule(3)

πŸ“¦ version 1010.6, in nixpkgs unstable

πŸŽ€ NAME πŸŽ€

NSModule β€” the programmatic interface for working with modules and symbols, bestie πŸ’…


✨ SYNOPSIS ✨

#include <mach-o/dyld.h>
typedef void * NSModule;
extern NSModule NSLinkModule(
     NSObjectFileImage objectFileImage,
     const char *moduleName,
     unsigned long options);
extern enum DYLD_BOOL NSUnLinkModule(
     NSModule module,
     unsigned long options);
extern const char * NSNameOfModule(
     NSModule m);
extern const char * NSLibraryNameForModule(
     NSModule m);

typedef void * NSSymbol;
extern enum DYLD_BOOL NSIsSymbolNameDefined(
     const char *symbolName);
extern enum DYLD_BOOL NSIsSymbolNameDefinedWithHint(
     const char *symbolName
     const char *libraryNameHint);
extern enum DYLD_BOOL NSIsSymbolNameDefinedInImage(
     const struct mach_header *image,
     const char *symbolName);
extern NSSymbol NSLookupAndBindSymbol(
     const char *symbolName);
extern NSSymbol NSLookupAndBindSymbolWithHint(
     const char *symbolName
     const char *libraryNameHint);
extern NSSymbol NSLookupSymbolInModule(
     NSModule module,
     const char *symbolName);
extern NSSymbol NSLookupSymbolInImage(
     const struct mach_header *image,
     const char *symbolName,
     unsigned long options);
extern const char * NSNameOfSymbol(
     NSSymbol symbol);
extern void * NSAddressOfSymbol(
     NSSymbol symbol);
extern NSModule NSModuleForSymbol(
     NSSymbol symbol);
extern enum DYLD_BOOL NSAddLibrary(
     const char *pathName);
extern enum DYLD_BOOL NSAddLibraryWithSearching(
     const char *pathName);
extern const struct mach_header * NSAddImage(
     const char *image_name,
     unsigned long options);
extern long NSVersionOfRunTimeLibrary(
     const char *libraryName);
extern long NSVersionOfLinkTimeLibrary(
     const char *libraryName);
extern int _NSGetExecutablePath(
     char *buf,
     unsigned long *bufsize)

extern void NSInstallLinkEditErrorHandlers(
     NSLinkEditErrorHandlers *handlers);
extern void NSLinkEditError(
     NSLinkEditErrors *c,
     int *errorNumber,
     const char **fileName,
     const char **errorString);

extern NSModule NSReplaceModule(
     NSModule moduleToReplace,
     NSObjectFileImage newObjectFileImage,
     unsigned long options);

🌸 DESCRIPTION 🌸

okay so these routines are literally the programmatic interface for working with modules and symbols inside a program. A program is made of a set of images, an executable, plugins, and dynamic shared libraries. An image that’s an executable or a plugin is composed of one module containing a collection of symbols. A dynamic shared library is composed of one or more modules, and each of those modules contains a separate collection of symbols. And the vibe check here is: if a symbol is used from a module, then all the symbols from that module are used. periodt.

When a program is executed, it selectively binds the symbols it needs from the modules in the dynamic libraries that are loaded. Normally a program is staticly linked against a set of dynamic shared libraries when it is built. So when the program is executed, the dynamic linker will automaticly load those dynamic shared libraries. iconic behavior, honestly.

A program may programmatically load plugins after it starts executing, and that’s done with two sets of API’s. The first is the API’s of NSObjectFileImage(3), and the second is NSLinkModule. Unlike modules in the dynamic libraries, when a plugin is loaded it is not selectively bound to but always bound into the program. no cap.

NSLinkModule links the specified object file image into the program and returns the module handle for it. Currently the implementation is limited to only Mach-O MH_BUNDLE types, which are used for plugins. A module name is specified when a module is linked so that later NSNameOfModule can be used with the module handle and to do things like report errors. If you want gdb(1) to be able to debug your module, when calling NSLinkModule you should pass the image path as the module name. 🫢

When a module is linked, all libraries referenced by the module are added to the list of libraries to be searched. The parameter, options, can have a set of options or’ed together. The options for NSLinkModule are as follows:

  • NSLINKMODULE_OPTION_NONE β€” this specifies no options. With this, the global symbols from the module are made part of the global symbol table of the program. If any errors occur, the handlers installed with NSInstallLinkEditErrorHandlers are called, or the default action is taken if there are no handlers.
  • NSLINKMODULE_OPTION_BINDNOW β€” this option causes the dynamic link editor to bind all undefined references for the loaded module and not allow references to be bound as needed. This affects all the references in the module and all of the dependent references.
  • NSLINKMODULE_OPTION_PRIVATE β€” with this option, the global symbols from the module are not made part of the global symbol table of the program. The global symbols of the module can then be looked up using NSLookupSymbolInModule.
  • NSLINKMODULE_OPTION_RETURN_ON_ERROR β€” with this option, if errors occur while binding this module, it is automaticly unloaded and NULL is returned as the module handle. To get the error information for the module that failed to load, the routine NSLinkEditError is then used. It has the same parameters as the link edit error handler (see below) except all the parameters are pointers in which the information is returned indirectly.
  • NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES β€” with this option, the module init routines are not called. This is only useful to the fix-and-continue implementation.
  • NSLINKMODULE_OPTION_TRAILING_PHYS_NAME β€” with this option, the parameter, moduleName, is assumed to be a string with the logical name of the image with the physical name of the object file tailing after the NULL character of the logical name. This is only useful to the zero-link implementation.

NSUnLinkModule unlinks the specified module handle from the program. Currently the implementation is limited to only allow modules linked with NSLinkModule to be unlinked. The parameter, options, can have a set of options or’ed together. The options for NSUnLinkModule are as follows:

  • NSUNLINKMODULE_OPTION_NONE β€” this specifies no options. With this, the module is unlinked from the program and the memory for the module is deallocated. If any errors occur, the handlers installed with NSInstallLinkEditErrorHandlers are called, or the default action is taken if there are no handlers.
  • NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED β€” with this option, the memory for the module is not deallocated, allowing pointers into the module to still be valid.
  • NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES β€” with this option, any lazy references (direct function calls) to symbols defined in the module are reset to be bound on first call again and not cause any undefined symbol errors. Currently this is only implemented for the PowerPC architecture.

NSNameOfModule is passed a module handle and returns the name of the module. If the module handle is invalid, NULL is returned.

NSLibraryNameForModule is passed a module handle and returns the name of the library the module is in, if any. If the module handle is for a module that is not in a library (in the executable or a plugin), or the module handle is invalid, NULL is returned.

NSIsSymbolNameDefined is passed a global symbol name (global 'C' symbols names are preceded with an underbar '_') and returns TRUE or FALSE based on if the symbol is defined in the program’s global symbol table. If the symbol is not defined, no error occurs.

NSIsSymbolNameDefinedWithHint is the same as NSIsSymbolNameDefined, but the libraryNameHint parameter provides a hint as to where to start the lookup in a prebound program. The libraryNameHint parameter is matched up with the actual library install names with strstr(3).

NSIsSymbolNameDefinedInImage is passed a pointer to the mach_header of a mach_header structure of a dynamic library being used by the program and a symbol name. This returns TRUE or FALSE based on if the symbol is defined in the specified image or one of the image’s sub-frameworks or sub-umbrellas. If the program was built with the ld(1) -force_flat_namespace flag or executed with the environment variable DYLD_FORCE_FLAT_NAMESPACE set and the pointer to a mach_header structure is not of a bundle loaded with the NSLINKMODULE_OPTION_PRIVATE option of NSLinkModule(3), then the pointer to a mach_header is ignored and the symbol is looked up in all the images using the first definition if found. πŸŒ€

The image handle parameter for NSLookupSymbolInImage and NSIsSymbolNameDefinedInImage is a pointer to a read-only mach header structure of a dynamic library being used by the program. Besides the NSAddImage(3) routine, the pointer to a mach header can also be obtained by using a link editor defined symbol as in <mach-o/ldsym.h> and described on the ld(1) man page. Also the dyld(3) routine _dyld_get_image_header(3) and the mach_header pointer arguments to the call back routines called from _dyld_register_func_for_add_image(3) routines can also be used. very β€œI know a guy” energy, but make it linker symbols πŸ’€

NSLookupAndBindSymbol is passed a global symbol name and looks up and binds the symbol into the program. It returns an NSSymbol for the symbol. If any errors occur, the handlers installed with NSInstallLinkEditErrorHandlers are called, or the default action is taken if there are no handlers.

NSLookupAndBindSymbolWithHint is the same as NSLookupAndBindSymbol, but the libraryNameHint parameter provides a hint as to where to start the lookup in a prebound program. The libraryNameHint parameter is matched up with the actual library install names with strstr(3).

NSLookupSymbolInModule is passed a symbol name and a module handle and looks up the symbol in that module. Currently this is only implemented for module handles returned with NSLinkModule. If the symbol is found, an NSSymbol for the symbol is returned; otherwise NULL is returned and no error occurs.

So NSLookupSymbolInImage is the one where you hand it a pointer to a mach_header structure of a dynamic library your program is using, plus a symbol name β€” and it gives you back an NSSymbol for the symbol defined in that specified image, or in that image's sub-frameworks or sub-umbrellas 🫢. Bestie, it's giving "find my friend in this specific group chat, not the whole school."

But here's the plot twist πŸ’€: if your program was built with the ld(1) -force_flat_namespace flag, or it was executed with the environment variable DYLD_FORCE_FLAT_NAMESPACE set β€” and the pointer to the mach_header structure is not from a bundle loaded with the NSLINKMODULE_OPTION_PRIVATE option of NSLinkModule(3) β€” then the pointer to the mach_header gets straight up ignored, and the symbol is looked up in all the images, using the first definition found. No cap, it just throws your pointer away πŸ™ˆ.

Also, if you didn't use NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR and any errors occur, the handlers you installed with NSInstallLinkEditErrorHandlers get called β€” or the default action happens if there are no handlers. Yes, that default action. The dramatic one. 😭

The options of NSLookupSymbolInImage are as follows:

  • NSLOOKUPSYMBOLINIMAGE_OPTION_BIND β€” just binds the non-lazy symbols of the module that defines the symbolName, and lets all the lazy symbols in that module get bound on first call. This is the normal-case vibe for a trusted module that's expected to bind without any errors β€” like a module in a system supplied library. Chill, low-drama, iconic ✨.
  • NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW β€” binds all the non-lazy and lazy symbols of the module that defines the symbolName, and lets all the dependent symbols in the needed libraries be bound as needed. Use this for a module that might not be expected to bind without errors, but that links against only system supplied libraries β€” which are expected to bind without any errors. Trust issues, but only with the system libs πŸ’….
  • NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY β€” binds all the symbols of the module that defines the symbolName and all the dependent symbols of all needed libraries. This should only be used for things like signal handlers and linkedit error handlers, because those literally can't bind other symbols while they're executing to handle the signal or the error. Emergency-mode only, fr fr πŸ”₯.
  • NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR β€” with this one, if errors happen while binding the module that defines the symbolName, the module is automatically unloaded and NULL is returned as the NSSymbol. To get the error info about why the module failed to bind, you use the routine NSLinkEditError. It has the same parameters as the link edit error handler (peep below) except all the parameters are pointers, and the information is returned indirectly through them πŸŽ€.

NSNameOfSymbol is passed an NSSymbol and returns the name of the symbol. Name reveal, literally πŸ’–.

NSAddressOfSymbol is passed an NSSymbol and returns the address of the symbol. Where does it live? Right there, bestie πŸ¦‹.

NSModuleForSymbol is passed an NSSymbol and returns the NSModule that the symbol is defined in. Finding the symbol's home base 🌸.

NSAddLibrary is passed the file name of a dynamic shared library to be added to the search list. If it's successful it returns TRUE, else it returns FALSE. Binary queen β€” yes or no, no in-between πŸ‘‘.

NSAddLibraryWithSearching is passed the file name of a dynamic shared library to be added to the search list, and the file name you pass will be affected by the various DYLD environment variables, as if this library were linked into the program. If it's successful it returns TRUE, else it returns FALSE. Same energy, but now the env vars get a say 🌈.

NSAddImage is passed the file name of a dynamic shared library to be added to the search list of the program β€” but only if it's not already loaded. It returns a pointer to the mach_header structure of the dynamic library being used by the program.

⭐ Pro-tip straight from the page: for the best performance of this routine, if the library is expected to already be loaded by the program, the image_name should be a full path name β€” and it should be the same as the name recorded by the program. If it's a symlink, then an open(2) and an fstat(2) are needed to determine that it's the same file as one already loaded. Extra syscalls, extra slay πŸ’….

If the dynamic shared library hasn't already been loaded, then it gets loaded along with all the needed dependent libraries. With the options parameter NSADDIMAGE_OPTION_NONE, any error in loading will cause the linkEdit error handler set by NSInstallLinkEditErrorHandlers(3) to be called β€” or the default action of printing the error and exiting gets taken. Yeah. Exiting. Bye πŸ‘‹πŸ˜­.

The other options of NSAddImage are as follows:

  • NSADDIMAGE_OPTION_RETURN_ON_ERROR β€” with this option, if errors occur while loading this library, it's automatically unloaded and NULL is returned. To get the error info for the library that failed to load, you then use the routine NSLinkEditError. It has the same parameters as the link edit error handler (peep below) except all the parameters are pointers, and the information is returned indirectly through them 🫢.
  • NSADDIMAGE_OPTION_WITH_SEARCHING β€” with this option, the image_name you pass for the library and all its dependents will be affected by the various DYLD environment variables, as if this library were linked into the program 🌸.
  • NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED β€” with this option, if the image_name you passed for the library hasn't already been loaded, it is not loaded. Only if it has been loaded will the pointer to the mach_header not be NULL. Strictly "if you're already here, you're in" energy πŸ’€.
  • NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME β€” when this option is specified, if a later load of a dependent dynamic library with a file system path is needed by an image that matches the install name of the dynamic library loaded with this option, then the dynamic library loaded with the call to NSAddImage() is used in place of the dependent dynamic library. Matchmaker, matchmaker, make me a match πŸŽ€βœ¨.

NSVersionOfRunTimeLibrary is passed the install name of a dynamic shared library and returns the current_version number of the library the program is using β€” or -1 if the program isn't using that library. Current status: check πŸ¦‹.

NSVersionOfLinkTimeLibrary is passed the install name of a dynamic shared library and returns the current_version number of the library the executable program was built with β€” or -1 if the program wasn't built with that library. Historical receipts, kept forever πŸ“œπŸ’œ.

_NSGetExecutablePath copies the path of the executable into the buffer and returns 0 if the path was successfully copied into the provided buffer. If the buffer isn't large enough, -1 is returned and the expected buffer size is copied into *bufsize πŸ‘‘

🚨 Okay this note matters: _NSGetExecutablePath will return "a path" to the executable, not a "real path" to the executable. Which means the path may be a symbolic link and not the real file. And with deep directories, the total bufsize needed could be more than MAXPATHLEN. So like… don't assume, bestie 😭🫢.

πŸ’€ ERROR HANDLING πŸ’€

Okay so NSInstallLinkEditErrorHandlers gets a pointer to a NSLinkEditErrorHandlers, and that thing holds three function pointers used for handling dynamic link errors. The prototypes for these functions are given in the following typedef:

              typedef struct {
                   void     (*undefined)(const char *symbolName);
                   NSModule (*multiple)(NSSymbol s, NSModule oldModule, NSModule newModule);
                   void     (*linkEdit)(NSLinkEditErrors errorClass, int errorNumber,
                                        const char *fileName, const char *errorString);
              } NSLinkEditErrorHandlers;

The first two functions let the programmer direct the link edit processing of undefined symbols and multiply defined symbols. The third function lets the programmer catch all other link editor errors.

When one of the user error functions gets called, the state will be such that no module will be partially loaded (except in the case of resource errors like out of memory and other relocation errors). But with undefined symbol errors, those modules referencing undefined symbols will be partially bound, and use of such modules can and will crash the program.

⚠️ great care, bestie: the program is running in a state that will crash if it uses an unbound symbol. To be safe, these functions should only rely on other things in the same module or in the executable.

If the user does not supply these functions, the default is to write an error message on to file descriptor 2 (usually stderr) and exit the program β€” except for the linkEdit error handler when the NSLinkEditErrors is NSLinkEditWarningError, where the default is to do nothing.

The specified undefined handler may make calls to any of the runtime loading functions to add modules based on the undefined symbol name. After dealing with this symbol name successfully (by doing a runtime loading operation to resolve the undefined reference), the handler simply returns. If more symbol's names remain undefined, the handler will be called repeatedly with an undefined symbol name. If the handler can't deal with the symbol, it should not return (put up a panel, abort, etc.) and cause the program to exit. Or it can remove itself as the undefined handler and return, which will cause the default action of printing the undefined symbol names and exiting.

The specified multiply defined symbol handler is called during the process of runtime linking, and thus it may not call any of the runtime loading functions, as only one set of linking operations can be performed in the task at a time. The only programmatic functions that can be called from a multiply defined symbol handler are NSNameOfSymbol, NSNameOfModule and NSLibraryNameForModule (provided they are linked into the program before the handler is called). This handler returns the module handle for the symbol that is to be used for further link editing, either the oldModule or the newModule. It may also record one of the module handles to later take action after the runtime linking process has completed (for example later unlink the module). The dynamic link editor updates the references to the symbol if the handler specifies the new symbol is to be used. The references which are updated are those that the compiler system generated as indirect references. Initialized data and references that were created at runtime are not effected.

The specified linkEdit error handler is called for all other runtime linking errors. These other runtime linking errors are either warnings or fatal errors. If the user's link edit error handler function returns for a fatal error, it will cause the program to exit. There is a small set of major error classes which have specific error numbers. These numbers are passed in the parameter errorClass. These major error classes include:

              typedef enum {
                   NSLinkEditFileAccessError,
                   NSLinkEditFileFormatError,
                   NSLinkEditMachResourceError,
                   NSLinkEditUnixResourceError,
                   NSLinkEditOtherError,
                   NSLinkEditWarningError,
                   NSLinkEditMultiplyDefinedError,
                   NSLinkEditUndefinedError
              } NSLinkEditErrors;

For the error class NSLinkEditUnixResourceError the errorNumber parameter will be an errno value (see intro(2)). For the error class NSLinkEditMachResourceError the errorNumber parameter will be a kern_return_t value. For the error class NSLinkEditOtherError the errorNumber parameter will be one of the following values:

              typedef enum {
                  NSOtherErrorRelocation,
                  NSOtherErrorLazyBind,
                  NSOtherErrorIndrLoop,
                  NSOtherErrorLazyInit,
                  NSOtherErrorInvalidArgs
              } NSOtherErrorNumbers;

For all errors, an attempt to pass an error string will be made. In some cases such as resource errors, it may not be possible to return a string. In those cases the errorString parameter will be β€” and here the original page just cuts off after β€œwill be” 😭. Same energy later: for file access errors and file format errors, an attempt to return a file name will also be passed, and if that is not possible the filename parameter will be β€” again, the page leaves it hanging right there. Not me inventing a value, bestie; that's literally what's on the page.

🌈 ALSO SEE 🌈

πŸ’– that's the whole page, bestie! source/version: Apple Computer, Inc.; date: October 6, 2003; page: NSModule(3). slay and stay linked. πŸ’…

πŸ“¦ where this page comes from