Location>code7788 >text

dyld: mysterious __dso_handle

Popularity:625 ℃/2024-11-19 03:03:45

iOSdynamic linkerdyldThere is a mysterious variable in__dso_handle:

// dyld/
static const MachOAnalyzer* getDyldMH()
{
#if __LP64__
    // herald __dso_handle
    extern const MachOAnalyzer __dso_handle;
    return &__dso_handle;
#else
    ...
#endif // __LP64__
}

This function internally declares a variable__dso_handleIts type isstruct MachOAnalyzer

ferret outstruct MachOAnalyzerdefinition, which inherits fromstruct mach_header:

image

struct mach_header(emphasizes that sth. is precisely or exactly as stated)XNUInside the kernel, the definition ofMach-OHeader.

// EXTENERL_HEADERS/mach-o/
struct mach_header {
	uint32_t	magic;		/* mach magic number identifier */
	cpu_type_t	cputype;	/* cpu specifier */
	cpu_subtype_t	cpusubtype;	/* machine specifier */
	uint32_t	filetype;	/* type of file */
	uint32_t	ncmds;		/* number of load commands */
	uint32_t	sizeofcmds;	/* the size of all the load commands */
	uint32_t	flags;		/* flags */
};

From the above functiongetDyldMHname, it returns the name of thedyldthis oneMach-Ofile's header, and this does match the variable__dso_handleThe type definition of the

But a strange thing happened, searching the entiredyldThe source code repository, neither of which can find the variable__dso_handleThe definition. All the places I can search are just definitions of this variable__dso_handleThe statement.

It is well known that dynamic connectorsdylditself is statically linked.

In other words, dynamic connectorsdylditself is not dependent on any other dynamic library.

Therefore, this variable__dso_handleIt is not possible to define in other dynamic libraries.

In that case, the dynamic linkerdyldHow does the itself statically link through?

The answer could only be a static linkerldTinkered with the linking process.

View Static LinkerldThe source code of thellvmThe following code can be found in the source code for the

// lld/MachO/
void macho::createSyntheticSymbols() {
  // addHeaderSymbol (used form a nominal expression) lamba displayed formula
  auto addHeaderSymbol = [](const char *name) {
    symtab->addSynthetic(name, ->isec, /*value=*/0,
                         /*isPrivateExtern=*/true, /*includeInSymtab=*/false,
                         /*referencedDynamically=*/false);
  };

  ...

  // The Itanium C++ ABI requires dylibs to pass a pointer to __cxa_atexit
  // which does . cleanup of static global variables. The ABI document
  // says that the pointer can point to any address in one of the dylib's
  // segments, but in practice ld64 seems to set it to point to the header,
  // so that's what's implemented here.
  addHeaderSymbol("___dso_handle");
}

The above code defines aaddHeaderSymbol(used form a nominal expression)lamdaexpression, and then uses it to add a symbol that is none other than the__dso_handle

call (programming)addHeaderSymbolThe notes above use thechatGPTThe translation is as follows.

The Itanium C++ ABI requires dynamic libraries to pass a pointer to __cxa_atexit, which is responsible for, for example, the cleanup of static global variables.The ABI documentation states that the pointer can point to an arbitrary address in one of the dynamic library's segments, but in practice, ld64 (Apple's linker) seems to set it to point to the header, so this is implemented here.

The notes refer to theItanium C++ ABIOriginally developed jointly for Intel and Hewlett-PackardItaniumprocessor architecture design.

However, its influence has extended beyond the scope of the architecture for which it was originally designed and has been widely used in other architectures, such as thex86cap (a poem)x86-64A variety of compilers on theGCCcap (a poem)Clang

Also, the notes mention that the__dso_handleIn Apple's implementation, it's pointing to theMach-OThe head.

So far, the mystery is solved~.