DESIGN.TXT 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. The design of crazy_linker:
  2. ===========================
  3. Introduction:
  4. -------------
  5. A system linker (e.g. ld.so on Linux, or /system/bin/linker on Android), is a
  6. particularly sophisticated piece of code because it is used to load and start
  7. _executables_ on the system. This requires dealing with really low-level
  8. details like:
  9. - The way the kernel loads and initializes binaries into a new process.
  10. - The way it passes initialization data (e.g. command-line arguments) to
  11. the process being launched.
  12. - Setting up the C runtime library, thread-local storage, and others properly
  13. before calling main().
  14. - Be very careful in the way it operates, due to the fact that it will be used
  15. to load set-uid programs.
  16. - Need to support a flurry of exotic flags and environment variables that
  17. affect runtime behaviour in "interesting" but mostly unpredictable ways
  18. (see the manpages for dlopen, dlsym and ld.so for details).
  19. Add to this that most of this must be done without the C library being loaded or
  20. initialized yet. No wonder this code is really complex.
  21. By contrast, crazy_linker is a static library whose only purpose is to load
  22. ELF shared libraries, inside an _existing_ executable process. This makes it
  23. considerably simpler:
  24. - The runtime environment (C library, libstdc++) is available and properly
  25. initialized.
  26. - No need to care about kernel interfaces. Everything uses mmap() and simple
  27. file accesses.
  28. - The API is simple, and straightforward (no hidden behaviour changes due to
  29. environment variables).
  30. This document explains how the crazy_linker works. A good understanding of the
  31. ELF file format is recommended, though not necessary.
  32. I. ELF Loading Basics:
  33. ----------------------
  34. When it comes to loading shared libraries, an ELF file mainly consists in the
  35. following parts:
  36. - A fixed-size header that identifies the file as an ELF file and gives
  37. offsets/sizes to other tables.
  38. - A table (called the "program header table"), containing entries describing
  39. 'segments' of interest in the ELF file.
  40. - A table (called the "dynamic table"), containing entries describing
  41. properties of the ELF library. The most interesting ones are the list
  42. of libraries the current one depends on.
  43. - A table describing the symbols (function or global names) that the library
  44. references or exports.
  45. - One or more tables containing 'relocations'. Because libraries can be loaded
  46. at any page-aligned address in memory, numerical pointers they contain must
  47. be adjusted after load. That's what the relocation entries do. They can
  48. also reference symbols to be found in other libraries.
  49. The process of loading a given ELF shared library can be decomposed into 4 steps:
  50. 1) Map loadable segments into memory.
  51. This step parses the program header table to identify 'loadable' segments,
  52. reserve the corresponding address space, then map them directly into
  53. memory with mmap().
  54. Related: src/crazy_linker_elf_loader.cpp
  55. 2) Load library dependencies.
  56. This step parses the dynamic table to identify all the other shared
  57. libraries the current one depends on, then will _recursively_ load them.
  58. Related: src/crazy_linker_library_list.cpp
  59. (crazy::LibraryList::LoadLibrary())
  60. 3) Apply all relocations.
  61. This steps adjusts all pointers within the library for the actual load
  62. address. This can also reference symbols that appear in other libraries
  63. loaded in step 2).
  64. Related: src/crazy_linker_elf_relocator.cpp
  65. 4) Run constructors.
  66. Libraries include a list of functions to be run at load time, typically
  67. to perform static C++ initialization.
  68. Related: src/crazy_linker_shared_library.cpp
  69. (SharedLibrary::RunConstructors())
  70. Unloading a library is similar, but in reverse order:
  71. 1) Run destructors.
  72. 2) Unload dependencies recursively.
  73. 3) Unmap loadable segments.
  74. II. Managing the list of libraries:
  75. -----------------------------------
  76. It is crucial to avoid loading the same library twice in the same process,
  77. otherwise some really bad undefined behaviour may happen.
  78. This implies that, inside an Android application process, all system libraries
  79. should be loaded by the system linker (because otherwise, the Dalvik-based
  80. framework might load the same library on demand, at an unpredictable time).
  81. To handle this, the crazy_linker uses a custom class (crazy::LibraryList) where
  82. each entry (crazy::LibraryView) is reference-counted, and either references:
  83. - An application shared libraries, loaded by the crazy_linker itself.
  84. - A system shared libraries, loaded through the system dlopen().
  85. Libraries loaded by the crazy_linker are modelled by a crazy::SharedLibrary
  86. object. The source code comments often refer to these objects as
  87. "crazy libraries", as opposed to "system libraries".
  88. As an example, here's a diagram that shows the list after loading a library
  89. 'libfoo.so' that depends on the system libraries 'libc.so', 'libm.so' and
  90. 'libOpenSLES.so'.
  91. +-------------+
  92. | LibraryList |
  93. +-------------+
  94. |
  95. | +-------------+
  96. +----| LibraryView | ----> libc.so
  97. | +-------------+
  98. |
  99. | +-------------+
  100. +----| LibraryView | ----> libm.so
  101. | +-------------+
  102. |
  103. | +-------------+
  104. +----| LibraryView | ----> libOpenSLES.so
  105. | +-------------+
  106. |
  107. | +-------------+ +-------------+
  108. +----| LibraryView |----->|SharedLibrary| ---> libfoo.so
  109. | +-------------+ +-------------+
  110. |
  111. ___
  112. _
  113. System libraries are identified by name. Only the official NDK-official system
  114. libraries are listed. It is likely that using crazy_linker to load non-NDK
  115. system libraries will not work correctly, so don't do it.
  116. III. Wrapping of linker symbols within crazy ones:
  117. --------------------------------------------------
  118. Libraries loaded by the crazy linker are not visible to the system linker.
  119. This means that directly calling the system dlopen() or dlsym() from a library
  120. code loaded by the crazy_linker will not work properly.
  121. To work-around this, crazy_linker redirects all linker symbols to its own
  122. wrapper implementation. This redirection happens transparently.
  123. Related: src/crazy_linker_wrappers.cpp
  124. This also includes a few "hidden" dynamic linker symbols which are used for
  125. stack-unwinding. This guarantees that C++ exception propagation works.
  126. IV. GDB support:
  127. ----------------
  128. The crazy_linker contains support code to ensure that libraries loaded with it
  129. are visible through GDB at runtime. For more details, see the extensive comments
  130. in src/crazy_linker_rdebug.h
  131. V. Other Implementation details:
  132. --------------------------------
  133. The crazy_linker is written in C++, but its API is completely C-based.
  134. The implementation doesn't require any C++ STL feature (except for new
  135. and delete).
  136. Very little of the code is actually Android-specific. The target system's
  137. bitness is abstracted through a C++ traits class (see src/elf_traits.h).
  138. Written originally for Chrome, so follows the Chromium coding style. Which can
  139. be enforced by using the 'clang-format' tool with:
  140. cd /path/to/crazy_linker/
  141. find . -name "*.h" -o -name "*.cpp" | xargs clang-format -style Chromium -i