diff options
author | Christopher R. Palmer <crpalmer@gmail.com> | 2015-11-03 16:44:44 -0500 |
---|---|---|
committer | Steve Kondik <shade@chemlab.org> | 2015-11-07 01:16:16 -0800 |
commit | 8a044e6e632b6221b8a0dc2df918d7b01a77fb98 (patch) | |
tree | 59b8feb2605ddcb238ad49402b6295c8fb34350d | |
parent | cc20e751ce2c40ead4938cc7addc94100f9793c6 (diff) | |
download | bionic-8a044e6e632b6221b8a0dc2df918d7b01a77fb98.zip bionic-8a044e6e632b6221b8a0dc2df918d7b01a77fb98.tar.gz bionic-8a044e6e632b6221b8a0dc2df918d7b01a77fb98.tar.bz2 |
linker: Add support for dynamic "shim" libs
Add a new environment variable
LD_SHIM_LIBS
that is a colon (":") separated list of vertical bar ("|") separated pairs.
The pairs are the name for a soinfo reference (executable or shared library)
followed by the name of the shim library to load. For example:
LD_SHIM_LIBS=rmt_storage|libshim_ioprio.so:/system/lib/libicuuv.so|libshim_icu53.so
will instruct the linker to load the dynamic library libshim_ioprio.so
whenver rmt_storage is executed [*] and will load libshim_icu53.so whenever
any executable or other shared library links against /system/lib/libicuuv.so.
There are no restrictions against circular references. In this example,
libshim_icu53.so can link against libicuuv.so which provides a simple and
convenient means of adding compatibility symbols.
[*] Note that the absolute path is not available to the linker and therefore
using the name of executables does depend on the invocation and therefore
should only be used if absolutely necessary. That is, running
/system/bin/rmt_storage would not load any shim libs in this example because
it does not match the name of the invocation of the command.
If you have trouble determining the sonames being loaded, you can also set
the environment variable LD_DEBUG=1 which will cause additional information
to be logged to help trace the detection of the shim libs.
Change-Id: I0ef80fa466167f7bcb7dac90842bef1c3cf879b6
-rw-r--r-- | linker/linker.cpp | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index b860f70..146937a 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -882,6 +882,60 @@ typedef linked_list_t<soinfo> SoinfoLinkedList; typedef linked_list_t<const char> StringLinkedList; typedef linked_list_t<LoadTask> LoadTaskList; +static soinfo* find_library(const char* name, int rtld_flags, const android_dlextinfo* extinfo); + +// g_ld_all_shim_libs maintains the references to memory as it used +// in the soinfo structures and in the g_active_shim_libs list. + +static std::vector<std::string> g_ld_all_shim_libs; + +// g_active_shim_libs are all shim libs that are still eligible +// to be loaded. We must remove a shim lib from the list before +// we load the library to avoid recursive loops (load shim libA +// for libB where libA also links against libB). + +static linked_list_t<const std::string> g_active_shim_libs; + +static void parse_LD_SHIM_LIBS(const char* path) { + parse_path(path, " :", &g_ld_all_shim_libs); + for (const auto& pair : g_ld_all_shim_libs) { + g_active_shim_libs.push_back(&pair); + } +} + +static bool shim_lib_matches(const char *shim_lib, const char *realpath) { + const char *sep = strchr(shim_lib, '|'); + return sep != nullptr && strncmp(realpath, shim_lib, sep - shim_lib) == 0; +} + +template<typename F> +static bool shim_libs_for_each(const char *const path, F action) { + if (path == nullptr) return true; + INFO("finding shim libs for \"%s\"\n", path); + std::vector<const std::string *> matched; + + g_active_shim_libs.for_each([&](const std::string *a_pair) { + const char *pair = a_pair->c_str(); + if (shim_lib_matches(pair, path)) { + matched.push_back(a_pair); + } + }); + + g_active_shim_libs.remove_if([&](const std::string *a_pair) { + const char *pair = a_pair->c_str(); + return shim_lib_matches(pair, path); + }); + + for (const auto& one_pair : matched) { + const char* const pair = one_pair->c_str(); + const char* sep = strchr(pair, '|'); + INFO("found shim lib \"%s\"\n", sep+1); + soinfo *child = find_library(sep+1, RTLD_GLOBAL, nullptr); + if (! child) return false; + action(child); + } + return true; +} // This function walks down the tree of soinfo dependencies // in breadth-first order and @@ -914,6 +968,12 @@ static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_s si->get_children().for_each([&](soinfo* child) { visit_list.push_back(child); }); + + if (!shim_libs_for_each(si->get_realpath(), [&](soinfo* child) { + visit_list.push_back(child); + })) { + return false; + } } return true; @@ -3154,9 +3214,11 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( // doesn't cost us anything. const char* ldpath_env = nullptr; const char* ldpreload_env = nullptr; + const char* ldshim_libs_env = nullptr; if (!getauxval(AT_SECURE)) { ldpath_env = getenv("LD_LIBRARY_PATH"); ldpreload_env = getenv("LD_PRELOAD"); + ldshim_libs_env = getenv("LD_SHIM_LIBS"); } INFO("[ android linker & debugger ]"); @@ -3210,6 +3272,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid). parse_LD_LIBRARY_PATH(ldpath_env); parse_LD_PRELOAD(ldpreload_env); + parse_LD_SHIM_LIBS(ldshim_libs_env); somain = si; |