A special linker for ASan executables.

Setup a /system/bin/linker_asan as a symlink to "linker".
Read the linker name from PT_INTERP, and if it is linker_asan,
switch default library lookup paths to the ASan set, which
starts with the path to the instrumented libraries
(/data/lib), followed by /system/lib as a fallback.

This ensures that ASan binaries prefer ASan libraries, when
available. This approach is way better then RPATH/RUNPATH and even
better than LD_LIBRARY_PATH:
- RUNPATH is per-DSO, while default paths are global.
- LD_LIBRARY_PATH is overwritten by android_update_LD_LIBRARY_PATH.
- neither RUNPATH nor LD_LIBRARY_PATH appear in
  android_get_LD_LIBRARY_PATH which is used to build java.lang.path.
  Having ASan libraries in java.lang.path is a good thing.

Bug: 22355945
Change-Id: I1d2791fbf5740618f18f71a3ae3d873714669d3f
This commit is contained in:
Evgenii Stepanov 2015-07-10 17:54:01 -07:00
parent 60a11dcb44
commit d640b225ec
4 changed files with 84 additions and 6 deletions

View File

@ -83,4 +83,26 @@ LOCAL_POST_LINK_CMD = $(hide) $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJCOPY) \
include $(BUILD_EXECUTABLE)
ifeq (address, $(strip $(SANITIZE_TARGET)))
define add-linker-symlink
$(eval _from := $(TARGET_OUT)/bin/$(1))
$(eval _to:=$(2))
$(_from): $(LOCAL_MODULE_MAKEFILE)
@echo "Symlink: $$@ -> $(_to)"
@mkdir -p $$(dir $$@)
@rm -rf $$@
$(hide) ln -sf $(_to) $$@
ALL_MODULES.linker.INSTALLED += $(_from)
linker: $(_from)
endef
$(eval $(call add-linker-symlink,linker_asan,linker))
ifeq ($(TARGET_IS_64_BIT),true)
$(eval $(call add-linker-symlink,linker_asan64,linker64))
endif
ALL_MODULES += linker
endif
include $(call first-makefiles-under,$(LOCAL_PATH))

View File

@ -85,9 +85,25 @@ static const char* const kDefaultLdPaths[] = {
nullptr
};
static const char* const kAsanDefaultLdPaths[] = {
#if defined(__LP64__)
"/data/vendor/lib64",
"/vendor/lib64",
"/data/lib64",
"/system/lib64",
#else
"/data/vendor/lib",
"/vendor/lib",
"/data/lib",
"/system/lib",
#endif
nullptr
};
static const ElfW(Versym) kVersymNotNeeded = 0;
static const ElfW(Versym) kVersymGlobal = 1;
static const char* const* g_default_ld_paths;
static std::vector<std::string> g_ld_library_paths;
static std::vector<std::string> g_ld_preload_names;
@ -1186,9 +1202,9 @@ static bool format_path(char* buf, size_t buf_size, const char* path, const char
}
static int open_library_on_default_path(const char* name, off64_t* file_offset) {
for (size_t i = 0; kDefaultLdPaths[i] != nullptr; ++i) {
for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
char buf[512];
if (!format_path(buf, sizeof(buf), kDefaultLdPaths[i], name)) {
if (!format_path(buf, sizeof(buf), g_default_ld_paths[i], name)) {
continue;
}
@ -1706,14 +1722,19 @@ void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
// See b/17302493 for further details.
// Once the above bug is fixed, this code can be modified to use
// snprintf again.
size_t required_len = strlen(kDefaultLdPaths[0]) + strlen(kDefaultLdPaths[1]) + 2;
size_t required_len = 0;
for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
required_len += strlen(g_default_ld_paths[i]) + 1;
}
if (buffer_size < required_len) {
__libc_fatal("android_get_LD_LIBRARY_PATH failed, buffer too small: "
"buffer len %zu, required len %zu", buffer_size, required_len);
}
char* end = stpcpy(buffer, kDefaultLdPaths[0]);
*end = ':';
strcpy(end + 1, kDefaultLdPaths[1]);
char* end = buffer;
for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
if (i > 0) *end++ = ':';
end = stpcpy(end, g_default_ld_paths[i]);
}
}
void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path) {
@ -3156,6 +3177,16 @@ static void init_linker_info_for_gdb(ElfW(Addr) linker_base) {
insert_soinfo_into_debug_map(linker_soinfo_for_gdb);
}
static void init_default_ld_library_path() {
const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
somain->load_bias);
const char* bname = basename(interp);
if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0))
g_default_ld_paths = kAsanDefaultLdPaths;
else
g_default_ld_paths = kDefaultLdPaths;
};
extern "C" int __system_properties_init(void);
/*
@ -3246,6 +3277,8 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
somain = si;
init_default_ld_library_path();
if (!si->prelink_image()) {
__libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
exit(EXIT_FAILURE);

View File

@ -771,6 +771,26 @@ void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_co
}
}
/* Return the program interpreter string, or nullptr if missing.
*
* Input:
* phdr_table -> program header table
* phdr_count -> number of entries in tables
* load_bias -> load bias
* Return:
* pointer to the program interpreter string.
*/
const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias) {
for (size_t i = 0; i<phdr_count; ++i) {
const ElfW(Phdr)& phdr = phdr_table[i];
if (phdr.p_type == PT_INTERP) {
return reinterpret_cast<const char*>(load_bias + phdr.p_vaddr);
}
}
return nullptr;
}
// Sets loaded_phdr_ to the address of the program header table as it appears
// in the loaded segments in memory. This is in contrast with phdr_table_,
// which is temporary and will be released before the library is relocated.

View File

@ -109,4 +109,7 @@ void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_co
ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
ElfW(Word)* dynamic_flags);
const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias);
#endif /* LINKER_PHDR_H */