aboutsummaryrefslogtreecommitdiff
path: root/sources/android/crazy_linker
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@android.com>2013-09-17 11:48:28 +0200
committerDavid 'Digit' Turner <digit@android.com>2013-09-18 12:13:59 +0200
commitcc87d1d56b0e1cef0131d9193c702d528417b61a (patch)
treea1f1f4ebba86e8f2f64c376ea00b0202ff9ea62b /sources/android/crazy_linker
parent0f5c91926653588a6811e633fcfc9b15f6ef2e5f (diff)
downloadndk-cc87d1d56b0e1cef0131d9193c702d528417b61a.tar.gz
crazy_linker: refactor shared RELRO API.
This patch changes the way shared RELRO section are implemented to make them less confusing with regards of ashmem region ownership, and support "relocated RELROs". Now: - The client explicitely creates the shared RELRO region with crazy_library_create_shared_relro(), this allocates an Ashmem region, but does not map it in the address space. The client becomes the owner of the Ashmem file descriptor and is now responsible for closing it. - The client maps a shared RELRO region with crazy_library_use_shared_relro(), as before, this doesn't transfer ownership of the Ashmem region file descriptor to the library. Note that this function does _not_ close the file descriptor, unlike the previous crazy_library_use_relro_sharing(). - crazy_library_create_shared_relro() also supports "relocated RELRO", i.e. when one wants to create a RELRO section that will be populated with data corresponding to a _different_ load address than what the library is currently using in the current process. A new unit test (test_relocated_shared_relro) has been added to test this feature. + Some internal refactoring, to prepare for future work (e.g. the ability to lookup symbols directly into system libraries). Change-Id: I597b99889115d1d504ec2d172a9d259edbeee823
Diffstat (limited to 'sources/android/crazy_linker')
-rw-r--r--sources/android/crazy_linker/Android.mk5
-rw-r--r--sources/android/crazy_linker/include/crazy_linker.h34
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_api.cpp39
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_ashmem.h2
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp4
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_relocations.cpp (renamed from sources/android/crazy_linker/src/crazy_linker_elf_relocator.cpp)230
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_relocations.h97
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_relocator.h104
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_relro.cpp228
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_relro.h89
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp146
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_symbols.h76
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_view.cpp121
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_elf_view.h110
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_library_list.cpp56
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_library_view.cpp32
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_library_view.h23
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_shared_library.cpp681
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_shared_library.h163
-rw-r--r--sources/android/crazy_linker/src/crazy_linker_wrappers.cpp14
-rw-r--r--sources/android/crazy_linker/src/linker_phdr.cpp4
-rw-r--r--sources/android/crazy_linker/src/linker_phdr.h2
-rw-r--r--sources/android/crazy_linker/tests/Android.mk14
-rw-r--r--sources/android/crazy_linker/tests/test_relocated_shared_relro.cpp104
-rw-r--r--sources/android/crazy_linker/tests/test_relro_sharing.cpp154
-rw-r--r--sources/android/crazy_linker/tests/test_shared_relro.cpp104
-rw-r--r--sources/android/crazy_linker/tests/test_two_shared_relros.cpp (renamed from sources/android/crazy_linker/tests/test_relro_sharing_two_libs.cpp)90
-rw-r--r--sources/android/crazy_linker/tests/test_util.h95
28 files changed, 1687 insertions, 1134 deletions
diff --git a/sources/android/crazy_linker/Android.mk b/sources/android/crazy_linker/Android.mk
index 261fdc3da..ec69e49e3 100644
--- a/sources/android/crazy_linker/Android.mk
+++ b/sources/android/crazy_linker/Android.mk
@@ -9,7 +9,10 @@ crazy_linker_sources := \
src/crazy_linker_ashmem.cpp \
src/crazy_linker_debug.cpp \
src/crazy_linker_elf_loader.cpp \
- src/crazy_linker_elf_relocator.cpp \
+ src/crazy_linker_elf_relocations.cpp \
+ src/crazy_linker_elf_relro.cpp \
+ src/crazy_linker_elf_symbols.cpp \
+ src/crazy_linker_elf_view.cpp \
src/crazy_linker_error.cpp \
src/crazy_linker_globals.cpp \
src/crazy_linker_library_list.cpp \
diff --git a/sources/android/crazy_linker/include/crazy_linker.h b/sources/android/crazy_linker/include/crazy_linker.h
index 16a61e68f..4a579ef6c 100644
--- a/sources/android/crazy_linker/include/crazy_linker.h
+++ b/sources/android/crazy_linker/include/crazy_linker.h
@@ -168,7 +168,6 @@ typedef struct {
size_t load_size;
size_t relro_start;
size_t relro_size;
- int relro_fd;
} crazy_library_info_t;
// Retrieve information about a given library.
@@ -188,15 +187,23 @@ crazy_status_t crazy_library_get_info(crazy_library_t* library,
// the exploitation of this security issue in your code.
crazy_status_t crazy_system_can_share_relro(void);
-// Enable RELRO section sharing for this library. This can only be
-// called once per library loaded through crazy_library_open(), and
-// will only work for non-system libraries.
-// On success, return CRAZY_STATUS_SUCCESS and sets |*library_info| with
-// all relevant data. On failure, return CRAZY_STATUS_FAILURE and sets
-// the error message in |context|.
-crazy_status_t crazy_library_enable_relro_sharing(
+// Create an ashmem region containing a copy of the RELRO section for a given
+// |library|. This can be used with crazy_library_use_shared_relro().
+// |load_address| can be specified as non-0 to ensure that the content of the
+// ashmem region corresponds to a RELRO relocated for a new load address.
+// on success, return CRAZY_STATUS_SUCCESS and sets |*relro_start| to the
+// start of the RELRO section in memory, |*relro_size| to its size in bytes
+// and |*relro_fd| to a file descriptor to a read-only ashmem region containing
+// the data. On failure, return false and set error message in |context|.
+// NOTE: On success, the caller becomes the owner of |*relro_fd| and is shall
+// close it appropriately.
+crazy_status_t crazy_library_create_shared_relro(
crazy_library_t* library,
- crazy_context_t* context) _CRAZY_PUBLIC;
+ crazy_context_t* context,
+ size_t load_address,
+ size_t* relro_start,
+ size_t* relro_size,
+ int* relro_fd) _CRAZY_PUBLIC;
// Use the shared RELRO section of the same library loaded in a different
// address space. On success, return CRAZY_STATUS_SUCCESS and owns |relro_fd|.
@@ -208,14 +215,13 @@ crazy_status_t crazy_library_enable_relro_sharing(
// |context| will receive an error in case of failure.
// NOTE: This will fail if this is a system library, or if the RELRO
// parameters do not match the library's actual load address.
-// IMPORTANT NOTE: For security reasons, this _always_ closes the |relro_fd|
-// file descriptor, if it's non-negative, whether the function succeeds or not.
-crazy_status_t crazy_library_use_relro_sharing(
+// NOTE: The caller is responsible for closing the file descriptor after this call.
+crazy_status_t crazy_library_use_shared_relro(
crazy_library_t* library,
+ crazy_context_t* context,
size_t relro_start,
size_t relro_size,
- int relro_fd,
- crazy_context_t* context) _CRAZY_PUBLIC;
+ int relro_fd) _CRAZY_PUBLIC;
// Look for a library named |library_name| in the set of currently
// loaded libraries, and return a handle for it in |*library| on success.
diff --git a/sources/android/crazy_linker/src/crazy_linker_api.cpp b/sources/android/crazy_linker/src/crazy_linker_api.cpp
index d5f0b2dd7..b360e413e 100644
--- a/sources/android/crazy_linker/src/crazy_linker_api.cpp
+++ b/sources/android/crazy_linker/src/crazy_linker_api.cpp
@@ -175,7 +175,6 @@ crazy_status_t crazy_library_get_info(crazy_library_t* library,
&info->load_size,
&info->relro_start,
&info->relro_size,
- &info->relro_fd,
&context->error)) {
return CRAZY_STATUS_FAILURE;
}
@@ -187,47 +186,47 @@ crazy_status_t crazy_system_can_share_relro(void) {
crazy::AshmemRegion region;
if (!region.Allocate(PAGE_SIZE, NULL) ||
!region.SetProtectionFlags(PROT_READ) ||
- !crazy::AshmemRegion::CheckFileDescriptorIsReadOnly(region.Get()))
+ !crazy::AshmemRegion::CheckFileDescriptorIsReadOnly(region.fd()))
return CRAZY_STATUS_FAILURE;
return CRAZY_STATUS_SUCCESS;
}
-crazy_status_t crazy_library_enable_relro_sharing(crazy_library_t* library,
- crazy_context_t* context) {
+crazy_status_t crazy_library_create_shared_relro(crazy_library_t* library,
+ crazy_context_t* context,
+ size_t load_address,
+ size_t* relro_start,
+ size_t* relro_size,
+ int* relro_fd) {
LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
- if (!library) {
+ if (!library || !wrap->IsCrazy()) {
context->error = "Invalid library file handle";
return CRAZY_STATUS_FAILURE;
}
- if (!wrap->EnableSharedRelro(&context->error))
+ crazy::SharedLibrary* lib = wrap->GetCrazy();
+ if (!lib->CreateSharedRelro(
+ load_address, relro_start, relro_size, relro_fd, &context->error))
return CRAZY_STATUS_FAILURE;
return CRAZY_STATUS_SUCCESS;
}
-crazy_status_t crazy_library_use_relro_sharing(crazy_library_t* library,
- size_t relro_start,
- size_t relro_size,
- int relro_fd,
- crazy_context_t* context) {
+crazy_status_t crazy_library_use_shared_relro(crazy_library_t* library,
+ crazy_context_t* context,
+ size_t relro_start,
+ size_t relro_size,
+ int relro_fd) {
LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
- if (!library) {
+ if (!library || !wrap->IsCrazy()) {
context->error = "Invalid library file handle";
return CRAZY_STATUS_FAILURE;
}
- bool success =
- wrap->UseSharedRelro(relro_start, relro_size, relro_fd, &context->error);
-
- // Always close the file descriptor.
- if (relro_fd >= 0)
- TEMP_FAILURE_RETRY(::close(relro_fd));
-
- if (!success)
+ crazy::SharedLibrary* lib = wrap->GetCrazy();
+ if (!lib->UseSharedRelro(relro_start, relro_size, relro_fd, &context->error))
return CRAZY_STATUS_FAILURE;
return CRAZY_STATUS_SUCCESS;
diff --git a/sources/android/crazy_linker/src/crazy_linker_ashmem.h b/sources/android/crazy_linker/src/crazy_linker_ashmem.h
index 4233ba23c..9159caa74 100644
--- a/sources/android/crazy_linker/src/crazy_linker_ashmem.h
+++ b/sources/android/crazy_linker/src/crazy_linker_ashmem.h
@@ -16,7 +16,7 @@ class AshmemRegion {
~AshmemRegion() { Reset(-1); }
- int Get() const { return fd_; }
+ int fd() const { return fd_; }
int Release() {
int ret = fd_;
diff --git a/sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp
index 59b6a31dd..4a89111e5 100644
--- a/sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp
+++ b/sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp
@@ -12,7 +12,7 @@ namespace crazy {
TEST(AshmemRegion, Construction) {
AshmemRegion region;
- EXPECT_EQ(-1, region.Get());
+ EXPECT_EQ(-1, region.fd());
}
TEST(AshmemRegion, Allocate) {
@@ -23,7 +23,7 @@ TEST(AshmemRegion, Allocate) {
kSize,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED,
- region.Get(),
+ region.fd(),
0);
EXPECT_NE(MAP_FAILED, map);
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relocator.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.cpp
index 554ae2be8..574ef933f 100644
--- a/sources/android/crazy_linker/src/crazy_linker_elf_relocator.cpp
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.cpp
@@ -2,21 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crazy_linker_elf_relocator.h"
+#include "crazy_linker_elf_relocations.h"
#include <errno.h>
-#include <string.h>
#include "crazy_linker_debug.h"
+#include "crazy_linker_elf_symbols.h"
+#include "crazy_linker_elf_view.h"
+#include "crazy_linker_error.h"
#include "crazy_linker_util.h"
#include "linker_phdr.h"
-// Set to 1 to print debug traces during relocations.
-// Default is 0 since there are _tons_ of them.
#define DEBUG_RELOCATIONS 0
#define RLOG(...) LOG_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
-#define RLOG_ERRNO(...) LOG_IF_ERRNO(DEBUG_RELOCATIONS, __VA_ARGS__)
+#define RLOG_ERRNO(...) LOG_ERRNO_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
#ifndef DF_SYMBOLIC
#define DF_SYMBOLIC 2
@@ -108,35 +108,19 @@ RelocationType GetRelocationType(unsigned r_type) {
} // namespace
-ElfRelocator::ElfRelocator() { memset(this, 0, sizeof(*this)); }
-
-bool ElfRelocator::Init(const ELF::Phdr* phdr,
- size_t phdr_count,
- size_t load_bias,
- const ELF::Dyn* dyn,
- size_t count,
- Error* error) {
- phdr_ = phdr;
- phdr_count_ = phdr_count;
- load_bias_ = load_bias;
-
- LOG("%s: phdr=%p phdr_count=%d load_bias=%x dyn_table=%p dyn_count=%d\n",
- __FUNCTION__,
- phdr,
- (int)phdr_count,
- (int)load_bias,
- dyn,
- (int)count);
-
- for (; count > 0; dyn++, count--) {
- // Be paranoid.
- if (dyn->d_tag == DT_NULL)
- break;
-
- ELF::Addr dyn_value = dyn->d_un.d_val;
- uintptr_t dyn_addr = load_bias_ + dyn->d_un.d_ptr;
-
- switch (dyn->d_tag) {
+bool ElfRelocations::Init(const ElfView* view, Error* error) {
+ // Save these for later.
+ phdr_ = view->phdr();
+ phdr_count_ = view->phdr_count();
+ load_bias_ = view->load_bias();
+
+ // Parse the dynamic table.
+ ElfView::DynamicIterator dyn(view);
+ for (; dyn.HasNext(); dyn.GetNext()) {
+ ELF::Addr dyn_value = dyn.GetValue();
+ uintptr_t dyn_addr = dyn.GetAddress(view->load_bias());
+
+ switch (dyn.GetTag()) {
case DT_PLTREL:
// NOTE: Yes, there is nothing to record here, the content of
// plt_rel_ will come from DT_JMPREL instead.
@@ -148,23 +132,25 @@ bool ElfRelocator::Init(const ELF::Phdr* phdr,
break;
case DT_JMPREL:
RLOG(" DT_JMPREL addr=%p\n", dyn_addr);
- plt_rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
+ plt_relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
break;
case DT_PLTRELSZ:
- plt_rel_count_ = dyn_value / sizeof(ELF::Rel);
- RLOG(" DT_PLTRELSZ size=%d count=%d\n", dyn_value, plt_rel_count_);
+ plt_relocations_count_ = dyn_value / sizeof(ELF::Rel);
+ RLOG(" DT_PLTRELSZ size=%d count=%d\n",
+ dyn_value,
+ plt_relocations_count_);
break;
case DT_REL:
RLOG(" DT_REL addr=%p\n", dyn_addr);
- rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
+ relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
break;
case DT_RELSZ:
- rel_count_ = dyn_value / sizeof(ELF::Rel);
- RLOG(" DT_RELSZ size=%d count=%d\n", dyn_value, rel_count_);
+ relocations_count_ = dyn_value / sizeof(ELF::Rel);
+ RLOG(" DT_RELSZ size=%d count=%d\n", dyn_value, relocations_count_);
break;
case DT_PLTGOT:
- // NOTE from original linker:
- // Save this in case we decide to do lazy binding. We don't yet.
+ // Only used on MIPS currently. Could also be used on other platforms
+ // when lazy binding (i.e. RTLD_LAZY) is implemented.
RLOG(" DT_PLTGOT addr=%p\n", dyn_addr);
plt_got_ = reinterpret_cast<uintptr_t*>(dyn_addr);
break;
@@ -205,29 +191,17 @@ bool ElfRelocator::Init(const ELF::Phdr* phdr,
break;
#endif
default:
- RLOG(" UNKNOWN tag=%d value=%08x addr=%p\n",
- dyn->d_tag,
- dyn_value,
- (void*)dyn_addr);
;
}
}
- LOG("%s: Done!\n", __FUNCTION__);
+
return true;
}
-bool ElfRelocator::Apply(SymbolResolver* resolver,
- const char* string_table,
- const ELF::Sym* symbol_table,
- Error* error) {
- resolver_ = resolver;
- string_table_ = string_table;
- symbol_table_ = symbol_table;
-
- LOG("%s: string_table=%p sybol_table=%p\n",
- __FUNCTION__,
- string_table,
- symbol_table);
+bool ElfRelocations::ApplyAll(const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error) {
+ LOG("%s: Enter\n", __FUNCTION__);
if (has_text_relocations_) {
if (phdr_table_unprotect_segments(phdr_, phdr_count_, load_bias_) < 0) {
@@ -236,38 +210,37 @@ bool ElfRelocator::Apply(SymbolResolver* resolver,
}
}
- if (!ApplyRelocs(plt_rel_, plt_rel_count_, error) ||
- !ApplyRelocs(rel_, rel_count_, error)) {
+ if (!ApplyRelocs(plt_relocations_,
+ plt_relocations_count_,
+ symbols,
+ resolver,
+ error) ||
+ !ApplyRelocs(
+ relocations_, relocations_count_, symbols, resolver, error)) {
return false;
}
#ifdef __mips__
- if (!RelocateMipsGot(error))
+ if (!RelocateMipsGot(symbols, resolver, error))
return false;
#endif
if (has_text_relocations_) {
if (phdr_table_protect_segments(phdr_, phdr_count_, load_bias_) < 0) {
- error->Format("Can't protect loadable segments: %s", strerror(errno));
+ error->Format("Can't reprotect loadable segments: %s", strerror(errno));
return false;
}
}
- // Turn on GNU RELRO protection now.
- LOG("%s: Enabling GNU RELRO protection\n", __FUNCTION__);
-
- if (phdr_table_protect_gnu_relro(phdr_, phdr_count_, load_bias_) < 0) {
- error->Format("Can't enable GNU RELRO protection: %s", strerror(errno));
- return false;
- }
-
LOG("%s: Done\n", __FUNCTION__);
return true;
}
-bool ElfRelocator::ApplyRelocs(const ELF::Rel* rel,
- size_t rel_count,
- Error* error) {
+bool ElfRelocations::ApplyRelocs(const ELF::Rel* rel,
+ size_t rel_count,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error) {
RLOG("%s: rel=%p rel_count=%d\n", __FUNCTION__, rel, rel_count);
if (!rel)
@@ -294,9 +267,9 @@ bool ElfRelocator::ApplyRelocs(const ELF::Rel* rel,
// If this is a symbolic relocation, compute the symbol's address.
if (__builtin_expect(rel_symbol != 0, 0)) {
- const char* sym_name = string_table_ + symbol_table_[rel_symbol].st_name;
+ const char* sym_name = symbols->LookupNameById(rel_symbol);
RLOG(" symbol name='%s'\n", sym_name);
- void* address = resolver_->Lookup(sym_name);
+ void* address = resolver->Lookup(sym_name);
if (address) {
// The symbol was found, so compute its address.
RLOG("%s: symbol %s resolved to %p\n", __FUNCTION__, sym_name, address);
@@ -305,7 +278,7 @@ bool ElfRelocator::ApplyRelocs(const ELF::Rel* rel,
} else {
// The symbol was not found. Normally this is an error except
// if this is a weak reference.
- if (ELF_ST_BIND(symbol_table_[rel_symbol].st_info) != STB_WEAK) {
+ if (!symbols->IsWeakById(rel_symbol)) {
error->Format("Could not find symbol '%s'", sym_name);
return false;
}
@@ -437,7 +410,9 @@ bool ElfRelocator::ApplyRelocs(const ELF::Rel* rel,
}
#ifdef __mips__
-bool ElfRelocator::RelocateMipsGot(Error* error) {
+bool ElfRelocations::RelocateMipsGot(const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error) {
if (!plt_got_)
return true;
@@ -458,28 +433,103 @@ bool ElfRelocator::RelocateMipsGot(Error* error) {
// Handle the global GOT entries.
got += mips_local_got_count_;
- const ELF::Sym* sym = symbol_table_ + mips_gotsym_;
- const ELF::Sym* sym_limit = symbol_table_ + mips_symtab_count_;
- for (; sym < sym_limit; ++sym, ++got) {
- const char* sym_name = string_table_ + sym->st_name;
- void* sym_addr = resolver_->Lookup(sym_name);
+ for (size_t idx = mips_gotsym_; idx < mips_symtab_count_; idx++, got++) {
+ const char* sym_name = symbols->LookupNameById(idx);
+ void* sym_addr = resolver->Lookup(sym_name);
if (sym_addr) {
+ // Found symbol, update GOT entry.
*got = reinterpret_cast<ELF::Addr>(sym_addr);
continue;
}
- // Undefined symbols are only ok if this is a weak reference.
- if (ELF_ST_BIND(sym->st_info) != STB_WEAK) {
- error->Format("Cannot locate symbol %s", sym_name);
- return false;
+ if (symbols->IsWeakById(idx)) {
+ // Undefined symbols are only ok if this is a weak reference.
+ // Update GOT entry to 0 though.
+ *got = 0;
+ continue;
}
- // Leave undefined symbols from weak references to 0.
- *got = 0;
+ error->Format("Cannot locate symbol %s", sym_name);
+ return false;
}
return true;
}
#endif // __mips__
-} // namespace crazy
+void ElfRelocations::CopyAndRelocate(size_t src_addr,
+ size_t dst_addr,
+ size_t map_addr,
+ size_t size) {
+ // First, a straight copy.
+ ::memcpy(reinterpret_cast<void*>(dst_addr),
+ reinterpret_cast<void*>(src_addr),
+ size);
+
+ // Add this value to each source address to get the corresponding
+ // destination address.
+ size_t dst_delta = dst_addr - src_addr;
+ size_t map_delta = map_addr - src_addr;
+
+ // Ignore PLT relocations, which all target symbols (ignored here).
+ const ELF::Rel* rel = relocations_;
+ const ELF::Rel* rel_limit = rel + relocations_count_;
+
+ for (; rel < rel_limit; ++rel) {
+ unsigned rel_type = ELF_R_TYPE(rel->r_info);
+ unsigned rel_symbol = ELF_R_SYM(rel->r_info);
+ ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
+
+ if (rel_type == 0 || rel_symbol != 0) {
+ // Ignore empty and symbolic relocations
+ continue;
+ }
+
+ if (src_reloc < src_addr || src_reloc >= src_addr + size) {
+ // Ignore entries that don't relocate addresses inside the source section.
+ continue;
+ }
+
+ ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(src_reloc + dst_delta);
+
+ switch (rel_type) {
+#ifdef __arm__
+ case R_ARM_RELATIVE:
+ *dst_ptr += map_delta;
+ break;
+#endif // __arm__
+
+#ifdef __i386__
+ case R_386_RELATIVE:
+ *dst_ptr += map_delta;
+ break;
+#endif
+
+#ifdef __mips__
+ case R_MIPS_REL32:
+ *dst_ptr += map_delta;
+ break;
+#endif
+ default:
+ ;
+ }
+ }
+
+#ifdef __mips__
+ // Only relocate local GOT entries.
+ ELF::Addr* got = plt_got_;
+ if (got) {
+ for (ELF::Addr n = 2; n < mips_local_got_count_; ++n) {
+ size_t got_addr = reinterpret_cast<size_t>(&got[n]);
+ if (got_addr < src_addr || got_addr >= src_addr + size)
+ continue;
+ ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(got_addr + dst_delta);
+ *dst_ptr += map_delta;
+ }
+ }
+#endif
+
+ // Done
+}
+
+} // namespace crazy \ No newline at end of file
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relocations.h b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.h
new file mode 100644
index 000000000..c2fb0f9d8
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_relocations.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRAZY_LINKER_ELF_RELOCATIONS_H
+#define CRAZY_LINKER_ELF_RELOCATIONS_H
+
+#include <string.h>
+
+#include "elf_traits.h"
+
+namespace crazy {
+
+class ElfSymbols;
+class ElfView;
+class Error;
+
+// An ElfRelocations instance holds information about relocations in a mapped
+// ELF binary.
+class ElfRelocations {
+public:
+ ElfRelocations() { ::memset(this, 0, sizeof(*this)); }
+ ~ElfRelocations() {}
+
+ bool Init(const ElfView* view, Error* error);
+
+ // Abstract class used to resolve symbol names into addresses.
+ // Callers of ::ApplyAll() should pass the address of a derived class
+ // that properly implements the Lookup() method.
+ class SymbolResolver {
+ public:
+ SymbolResolver() {}
+ ~SymbolResolver() {}
+ virtual void* Lookup(const char* symbol_name) = 0;
+ };
+
+ // Apply all relocations to the target mapped ELF binary. Must be called
+ // after Init().
+ // |symbols| maps to the symbol entries for the target library only.
+ // |resolver| can resolve symbols out of the current library.
+ // On error, return false and set |error| message.
+ bool ApplyAll(const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error);
+
+ // This function is used to adjust relocated addresses in a copy of an
+ // existing section of an ELF binary. I.e. |src_addr|...|src_addr + size|
+ // must be inside the mapped ELF binary, this function will first copy its
+ // content into |dst_addr|...|dst_addr + size|, then adjust all relocated
+ // addresses inside the destination section as if it was loaded/mapped
+ // at |map_addr|...|map_addr + size|. Only relative relocations are processed,
+ // symbolic ones are ignored.
+ void CopyAndRelocate(size_t src_addr,
+ size_t dst_addr,
+ size_t map_addr,
+ size_t size);
+
+private:
+ bool ApplyRelocs(const ELF::Rel* relocs,
+ size_t relocs_count,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error);
+
+#if defined(__mips__)
+ bool RelocateMipsGot(const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error);
+#endif
+
+ const ELF::Phdr* phdr_;
+ size_t phdr_count_;
+ size_t load_bias_;
+
+ const ELF::Rel* plt_relocations_;
+ size_t plt_relocations_count_;
+ ELF::Addr* plt_got_;
+
+ const ELF::Rel* relocations_;
+ size_t relocations_count_;
+
+#if defined(__mips__)
+ // MIPS-specific relocation fields.
+ ELF::Word mips_symtab_count_;
+ ELF::Word mips_local_got_count_;
+ ELF::Word mips_gotsym_;
+#endif
+
+ bool has_text_relocations_;
+ bool has_symbolic_;
+
+};
+
+} // namespace crazy
+
+#endif //CRAZY_LINKER_ELF_RELOCATIONS_H
+
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relocator.h b/sources/android/crazy_linker/src/crazy_linker_elf_relocator.h
deleted file mode 100644
index a2d42c0f8..000000000
--- a/sources/android/crazy_linker/src/crazy_linker_elf_relocator.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRAZY_LINKER_ELF_RELOCATOR_H
-#define CRAZY_LINKER_ELF_RELOCATOR_H
-
-#include "crazy_linker_error.h"
-#include "elf_traits.h"
-
-namespace crazy {
-
-// A class used to perform ELF relocations in a library.
-// Usage is:
-// 1/ Create new instance.
-// 2/ Call Init().
-// 3/ Call Apply() to apply all relocations.
-//
-// If any of these steps fail, the library will be in inconsistent
-// state and must be unloaded/unmapped as soon as possible by the
-// caller to avoid any issue.
-class ElfRelocator {
- public:
- // Abstract class used to resolve symbol names into addresses.
- // Callers of ::Apply() should pass the address of a derived class
- // that properly implements the Lookup() method.
- class SymbolResolver {
- public:
- SymbolResolver() {}
- ~SymbolResolver() {}
- virtual void* Lookup(const char* symbol_name) = 0;
- };
-
- ElfRelocator();
-
- // Initialize the relocator. This will parse the library's dynamic
- // table to populate the structure.
- // |phdr| is the library's program header address.
- // |phdr_count| is the number of program header entries.
- // |load_bias| is the library load bias.
- // |dynamic| is the library's dynamic table.
- // |dyn_count| is the number of dynamic table entries.
- // On failure, return false and sets the |error| message.
- bool Init(const ELF::Phdr* phdr,
- size_t phdr_count,
- size_t load_bias,
- const ELF::Dyn* dynamic,
- size_t dyn_count,
- Error* error);
-
- // After initialization, apply all relocations in the library.
- // |resolver| must be an non-abstract instance of SymbolResolver
- // that will be used to resolve symbol lookups during relocations.
- // |string_table| is the library's string table.
- // |symbol_table| is the library's symbol table.
- // On failure, returns false and sets the |error| message.
- bool Apply(SymbolResolver* resolver,
- const char* string_table,
- const ELF::Sym* symbol_table,
- Error* error);
-
- private:
- bool ApplyRelocs(const ELF::Rel* rel, size_t rel_count, Error* error);
-
-#ifdef __mips__
- bool RelocateMipsGot(Error* error);
-#endif
-
- // Program header table
- const ELF::Phdr* phdr_;
- size_t phdr_count_;
-
- // Load bias to apply to all virtual address in the library.
- size_t load_bias_;
-
- // Relocations for the GOT and the PLT section that contains
- // the trampolines to call external symbols.
- ELF::Addr* plt_got_;
- ELF::Rel* plt_rel_;
- size_t plt_rel_count_;
-
- // Relocations for the rest of the library.
- ELF::Rel* rel_;
- size_t rel_count_;
-
-#if defined(__mips__)
- // MIPS-specific relocation fields.
- ELF::Word mips_symtab_count_;
- ELF::Word mips_local_got_count_;
- ELF::Word mips_gotsym_;
-#endif
-
- bool has_text_relocations_;
- bool has_symbolic_;
-
- // Used only during Apply().
- SymbolResolver* resolver_;
- const char* string_table_;
- const ELF::Sym* symbol_table_;
-};
-
-} // namespace crazy
-
-#endif // CRAZY_LINKER_ELF_RELOCATOR_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relro.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_relro.cpp
new file mode 100644
index 000000000..1c0bf00a8
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_relro.cpp
@@ -0,0 +1,228 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crazy_linker_elf_relro.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "crazy_linker_elf_relocations.h"
+#include "crazy_linker_elf_view.h"
+#include "crazy_linker_memory_mapping.h"
+#include "crazy_linker_util.h"
+
+namespace crazy {
+
+namespace {
+
+inline bool PageEquals(const char* p1, const char* p2) {
+ return ::memcmp(p1, p2, PAGE_SIZE) == 0;
+}
+
+// Swap pages between |addr| and |addr + size| with the bytes
+// from the ashmem region identified by |fd|, starting from
+// a given |offset|. On failure return false and set |error| message.
+bool SwapPagesFromFd(void* addr,
+ size_t size,
+ int fd,
+ size_t offset,
+ Error* error) {
+ // Unmap current pages.
+ if (::munmap(addr, size) < 0) {
+ error->Format("%s: Could not unmap %p-%p: %s",
+ __FUNCTION__,
+ addr,
+ (char*)addr + size,
+ strerror(errno));
+ return false;
+ }
+
+ // Remap the fd pages at the same location now.
+ void* new_map = ::mmap(addr,
+ size,
+ PROT_READ,
+ MAP_FIXED | MAP_SHARED,
+ fd,
+ static_cast<off_t>(offset));
+ if (new_map == MAP_FAILED) {
+ char* p = reinterpret_cast<char*>(addr);
+ error->Format("%s: Could not map %p-%p: %s",
+ __FUNCTION__,
+ p,
+ p + size,
+ strerror(errno));
+ return false;
+ }
+
+// TODO(digit): Is this necessary?
+#ifdef __arm__
+ __clear_cache(addr, (char*)addr + size);
+#endif
+
+ // Done.
+ return true;
+}
+
+} // namespace
+
+bool SharedRelro::Allocate(size_t relro_size,
+ const char* library_name,
+ Error* error) {
+ // Allocate a new ashmem region.
+ String name("RELRO:");
+ name += library_name;
+ if (!ashmem_.Allocate(relro_size, name.c_str())) {
+ error->Format("Could not allocate RELRO ashmem region for %s: %s",
+ library_name,
+ strerror(errno));
+ return false;
+ }
+
+ start_ = 0;
+ size_ = relro_size;
+ return true;
+}
+
+bool SharedRelro::CopyFrom(size_t relro_start,
+ size_t relro_size,
+ Error* error) {
+ // Map it in the process.
+ ScopedMemoryMapping map;
+ if (!map.Allocate(NULL, relro_size, MemoryMapping::CAN_WRITE, ashmem_.fd())) {
+ error->Format("Could not allocate RELRO mapping: %s", strerror(errno));
+ return false;
+ }
+
+ // Copy process' RELRO into it.
+ ::memcpy(map.Get(), reinterpret_cast<void*>(relro_start), relro_size);
+
+ // Unmap it.
+ map.Deallocate();
+
+ // Everything's good.
+ start_ = relro_start;
+ size_ = relro_size;
+ return true;
+}
+
+bool SharedRelro::CopyFromRelocated(const ElfView* view,
+ size_t load_address,
+ size_t relro_start,
+ size_t relro_size,
+ Error* error) {
+ // Offset of RELRO section in current library.
+ size_t relro_offset = relro_start - view->load_address();
+
+ ElfRelocations relocations;
+ if (!relocations.Init(view, error))
+ return false;
+
+ // Map the region in memory (any address).
+ ScopedMemoryMapping map;
+ if (!map.Allocate(
+ NULL, relro_size, MemoryMapping::CAN_READ_WRITE, ashmem_.fd())) {
+ error->Format("Could not allocate RELRO mapping for: %s", strerror(errno));
+ return false;
+ }
+
+ // Copy and relocate.
+ relocations.CopyAndRelocate(relro_start,
+ reinterpret_cast<size_t>(map.Get()),
+ load_address + relro_offset,
+ relro_size);
+ // Unmap it.
+ map.Deallocate();
+ start_ = load_address + relro_offset;
+ size_ = relro_size;
+ return true;
+}
+
+bool SharedRelro::ForceReadOnly(Error* error) {
+ // Ensure the ashmem region content isn't writable anymore.
+ if (!ashmem_.SetProtectionFlags(PROT_READ)) {
+ error->Format("Could not make RELRO ashmem region read-only: %s",
+ strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool SharedRelro::InitFrom(size_t relro_start,
+ size_t relro_size,
+ int ashmem_fd,
+ Error* error) {
+ // Create temporary mapping of the ashmem region.
+ ScopedMemoryMapping fd_map;
+
+ LOG("%s: Entering addr=%p size=%p fd=%d\n",
+ __FUNCTION__,
+ (void*)relro_start,
+ (void*)relro_size,
+ ashmem_fd);
+
+ // Sanity check: Ashmem file descriptor must be read-only.
+ if (!AshmemRegion::CheckFileDescriptorIsReadOnly(ashmem_fd)) {
+ error->Format("Ashmem file descriptor is not read-only: %s\n",
+ strerror(errno));
+ return false;
+ }
+
+ if (!fd_map.Allocate(NULL, relro_size, MemoryMapping::CAN_READ, ashmem_fd)) {
+ error->Format("Cannot map RELRO ashmem region as read-only: %s\n",
+ strerror(errno));
+ return false;
+ }
+
+ LOG("%s: mapping allocated at %p\n", __FUNCTION__, fd_map.Get());
+
+ char* cur_page = reinterpret_cast<char*>(relro_start);
+ char* fd_page = static_cast<char*>(fd_map.Get());
+ size_t p = 0;
+ size_t size = relro_size;
+ size_t similar_size = 0;
+
+ do {
+ // Skip over dissimilar pages.
+ while (p < size && !PageEquals(cur_page + p, fd_page + p)) {
+ p += PAGE_SIZE;
+ }
+
+ // Count similar pages.
+ size_t p2 = p;
+ while (p2 < size && PageEquals(cur_page + p2, fd_page + p2)) {
+ p2 += PAGE_SIZE;
+ }
+
+ if (p2 > p) {
+ // Swap pages between |pos| and |pos2|.
+ LOG("%s: Swap pages at %p-%p\n",
+ __FUNCTION__,
+ cur_page + p,
+ cur_page + p2);
+ if (!SwapPagesFromFd(cur_page + p, p2 - p, ashmem_fd, p, error))
+ return false;
+
+ similar_size += (p2 - p);
+ }
+
+ p = p2;
+ } while (p < size);
+
+ LOG("%s: Swapped %d pages over %d (%d %%, %d KB not shared)\n",
+ __FUNCTION__,
+ similar_size / PAGE_SIZE,
+ size / PAGE_SIZE,
+ similar_size * 100 / size,
+ (size - similar_size) / 4096);
+
+ if (similar_size == 0)
+ return false;
+
+ start_ = relro_start;
+ size_ = relro_size;
+ return true;
+}
+
+} // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relro.h b/sources/android/crazy_linker/src/crazy_linker_elf_relro.h
new file mode 100644
index 000000000..e8d510a53
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_relro.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRAZY_LINKER_ELF_RELRO_H
+#define CRAZY_LINKER_ELF_RELRO_H
+
+#include "crazy_linker_system.h"
+#include "crazy_linker_ashmem.h"
+#include "crazy_linker_memory_mapping.h"
+#include "elf_traits.h"
+
+namespace crazy {
+
+class ElfView;
+
+// A class used to model a shared RELRO section backed by an Ashmem region.
+// The region is always owned by the SharedRelro, unless DetachFd() is called.
+// The SharedRelro may or may not be mapped into the process.
+class SharedRelro {
+ public:
+ // Create a new SharedRelro. Note that the object becomes the owner of
+ // |ashmem_fd|, unless DetachFd() is called after this.
+ SharedRelro() : start_(0), size_(0), ashmem_() {}
+
+ ~SharedRelro() {}
+
+ size_t start() const { return start_; }
+ size_t end() const { return start_ + size_; }
+ size_t size() const { return size_; }
+ int fd() const { return ashmem_.fd(); }
+
+ // Return the ashmem region's file descriptor, and detach it from the object.
+ // After this call, fd() will always return -1.
+ int DetachFd() {
+ return ashmem_.Release();
+ }
+
+ // Allocate a new ashmem region of |relro_size| bytes for |library_name|.
+ // This operation doesn't change the process' mappings. On error, return
+ // false and set |error| message.
+ bool Allocate(size_t relro_size,
+ const char* library_name,
+ Error* error);
+
+ // Copy the content of the current process' RELRO into the ashmem region.
+ // |relro_start| is the RELRO address (page-aligned).
+ // |relro_size| is the RELRO size in bytes (page-aligned), and must match
+ // the allocation size passed to Allocate().
+ // On failure, return false and set |error| message.
+ bool CopyFrom(size_t relro_start,
+ size_t relro_size,
+ Error* error);
+
+ // Copy the contents of the current process' RELRO into the ashmem region
+ // but adjust any relocation targets within it to correspond to a new
+ // |load_address|. |view| must point to a mapped ELF binary for the current
+ // library. |relro_start| corresponds to the address of the current
+ // process' RELRO, i.e. is not relocated.
+ bool CopyFromRelocated(const ElfView* view,
+ size_t load_address,
+ size_t relro_start,
+ size_t relro_size,
+ Error* error);
+
+ // Force the section to be read-only.
+ bool ForceReadOnly(Error* error);
+
+ // Map the ashmem region's pages into the current process, doing a comparison
+ // to avoid corrupting parts of the RELRO section that are different in this
+ // one (e.g. due to symbolic relocations to randomized system libraries).
+ // This operation is _not_ atomic, i.e. no other thread should try to execute
+ // code that reads from the RELRO region during this call.
+ // On failure, return false and set |error| message.
+ // This operation does not transfer ownership of |ashmem_fd| to the object.
+ bool InitFrom(size_t relro_start,
+ size_t relro_size,
+ int ashmem_fd,
+ Error* error);
+
+ private:
+ size_t start_;
+ size_t size_;
+ AshmemRegion ashmem_;
+};
+
+} // namespace crazy
+
+#endif // CRAZY_LINKER_ELF_RELRO_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp
new file mode 100644
index 000000000..704076f18
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crazy_linker_elf_symbols.h"
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_elf_view.h"
+
+namespace crazy {
+
+namespace {
+
+// Compute the ELF hash of a given symbol.
+unsigned ElfHash(const char* name) {
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
+ unsigned h = 0;
+ while (*ptr) {
+ h = (h << 4) + *ptr++;
+ unsigned g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+ return h;
+}
+
+} // namespace
+
+bool ElfSymbols::Init(const ElfView* view) {
+ LOG("%s: Parsing dynamic table\n", __FUNCTION__);
+ ElfView::DynamicIterator dyn(view);
+ for (; dyn.HasNext(); dyn.GetNext()) {
+ uintptr_t dyn_addr = dyn.GetAddress(view->load_bias());
+ switch (dyn.GetTag()) {
+ case DT_HASH:
+ LOG(" DT_HASH addr=%p\n", dyn_addr);
+ {
+ ELF::Addr* data = reinterpret_cast<uintptr_t*>(dyn_addr);
+ hash_bucket_size_ = data[0];
+ hash_chain_size_ = data[1];
+ hash_bucket_ = data + 2;
+ hash_chain_ = data + 2 + hash_bucket_size_;
+ }
+ break;
+ case DT_STRTAB:
+ LOG(" DT_STRTAB addr=%p\n", dyn_addr);
+ string_table_ = reinterpret_cast<const char*>(dyn_addr);
+ break;
+ case DT_SYMTAB:
+ LOG(" DT_SYMTAB addr=%p\n", dyn_addr);
+ symbol_table_ = reinterpret_cast<const ELF::Sym*>(dyn_addr);
+ break;
+ default:
+ ;
+ }
+ }
+ if (symbol_table_ == NULL || string_table_ == NULL || hash_bucket_ == NULL)
+ return false;
+
+ return true;
+}
+
+const ELF::Sym* ElfSymbols::LookupByAddress(void* address,
+ size_t load_bias) const {
+ ELF::Addr elf_addr =
+ reinterpret_cast<ELF::Addr>(address) - static_cast<ELF::Addr>(load_bias);
+
+ for (size_t n = 0; n < hash_chain_size_; ++n) {
+ const ELF::Sym* sym = &symbol_table_[n];
+ if (sym->st_shndx != SHN_UNDEF && elf_addr >= sym->st_value &&
+ elf_addr < sym->st_value + sym->st_size) {
+ return sym;
+ }
+ }
+ return NULL;
+}
+
+bool ElfSymbols::LookupNearestByAddress(void* address,
+ size_t load_bias,
+ const char** sym_name,
+ void** sym_addr,
+ size_t* sym_size) const {
+ ELF::Addr elf_addr =
+ reinterpret_cast<ELF::Addr>(address) - static_cast<ELF::Addr>(load_bias);
+
+ const ELF::Sym* nearest_sym = NULL;
+ size_t nearest_diff = ~size_t(0);
+
+ for (size_t n = 0; n < hash_chain_size_; ++n) {
+ const ELF::Sym* sym = &symbol_table_[n];
+ if (sym->st_shndx == SHN_UNDEF)
+ continue;
+
+ if (elf_addr >= sym->st_value && elf_addr < sym->st_value + sym->st_size) {
+ // This is a perfect match.
+ nearest_sym = sym;
+ break;
+ }
+
+ // Otherwise, compute distance.
+ size_t diff;
+ if (elf_addr < sym->st_value)
+ diff = sym->st_value - elf_addr;
+ else
+ diff = elf_addr - sym->st_value - sym->st_size;
+
+ if (diff < nearest_diff) {
+ nearest_sym = sym;
+ nearest_diff = diff;
+ }
+ }
+
+ if (!nearest_sym)
+ return false;
+
+ *sym_name = string_table_ + nearest_sym->st_name;
+ *sym_addr = reinterpret_cast<void*>(nearest_sym->st_value + load_bias);
+ *sym_size = nearest_sym->st_size;
+ return true;
+}
+
+const ELF::Sym* ElfSymbols::LookupByName(const char* symbol_name) const {
+ unsigned hash = ElfHash(symbol_name);
+
+ for (unsigned n = hash_bucket_[hash % hash_bucket_size_]; n != 0;
+ n = hash_chain_[n]) {
+ const ELF::Sym* symbol = &symbol_table_[n];
+ // Check that the symbol has the appropriate name.
+ if (strcmp(string_table_ + symbol->st_name, symbol_name))
+ continue;
+ // Ignore undefined symbols.
+ if (symbol->st_shndx == SHN_UNDEF)
+ continue;
+ // Ignore anything that isn't a global or weak definition.
+ switch (ELF_ST_BIND(symbol->st_info)) {
+ case STB_GLOBAL:
+ case STB_WEAK:
+ return symbol;
+ default:
+ ;
+ }
+ }
+ return NULL;
+}
+
+} // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_symbols.h b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.h
new file mode 100644
index 000000000..c8f5dc37d
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_symbols.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRAZY_LINKER_ELF_SYMBOLS_H
+#define CRAZY_LINKER_ELF_SYMBOLS_H
+
+#include <string.h>
+
+#include "elf_traits.h"
+
+namespace crazy {
+
+class ElfView;
+
+// An ElfSymbols instance holds information about symbols in a mapped ELF binary.
+class ElfSymbols {
+ public:
+ ElfSymbols() { ::memset(this, 0, sizeof(*this)); }
+ ~ElfSymbols() {}
+
+ bool Init(const ElfView* view);
+
+ const ELF::Sym* LookupByName(const char* symbol_name) const;
+
+ const ELF::Sym* LookupById(size_t symbol_id) const {
+ return &symbol_table_[symbol_id];
+ }
+
+ const ELF::Sym* LookupByAddress(void* address, size_t load_bias) const;
+
+ // Returns true iff symbol with id |symbol_id| is weak.
+ bool IsWeakById(size_t symbol_id) const {
+ return ELF_ST_BIND(symbol_table_[symbol_id].st_info) == STB_WEAK;
+ }
+
+ const char* LookupNameById(size_t symbol_id) const {
+ const ELF::Sym* sym = LookupById(symbol_id);
+ if (!sym)
+ return NULL;
+ return string_table_ + sym->st_name;
+ }
+
+ void* LookupAddressByName(const char* symbol_name, size_t load_bias) const {
+ const ELF::Sym* sym = LookupByName(symbol_name);
+ if (!sym)
+ return NULL;
+ return reinterpret_cast<void*>(load_bias + sym->st_value);
+ }
+
+ bool LookupNearestByAddress(void* address,
+ size_t load_bias,
+ const char** sym_name,
+ void** sym_addr,
+ size_t* sym_size) const;
+
+ const char* GetStringById(size_t str_id) const {
+ return string_table_ + str_id;
+ }
+
+ // TODO(digit): Remove this once ElfRelocator is gone.
+ const ELF::Sym* symbol_table() const { return symbol_table_; }
+ const char* string_table() const { return string_table_; }
+
+ private:
+ const ELF::Sym* symbol_table_;
+ const char* string_table_;
+ ELF::Addr* hash_bucket_;
+ size_t hash_bucket_size_;
+ ELF::Addr* hash_chain_;
+ size_t hash_chain_size_;
+};
+
+} // namespace crazy
+
+#endif // CRAZY_LINKER_ELF_SYMBOLS_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_view.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_view.cpp
new file mode 100644
index 000000000..65ec28185
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_view.cpp
@@ -0,0 +1,121 @@
+#include "crazy_linker_elf_view.h"
+
+#include <errno.h>
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_error.h"
+#include "linker_phdr.h"
+
+namespace crazy {
+
+bool ElfView::InitUnmapped(size_t load_address,
+ const ELF::Phdr* phdr,
+ size_t phdr_count,
+ Error* error) {
+ // Compute load size and bias.
+ size_t min_vaddr = 0;
+ load_size_ = phdr_table_get_load_size(phdr, phdr_count, &min_vaddr, NULL);
+ if (load_size_ == 0) {
+ *error = "Invalid program header table";
+ return false;
+ }
+ load_address_ = (load_address ? load_address : min_vaddr);
+ load_bias_ = load_address - min_vaddr;
+
+ // Extract the dynamic table information.
+ phdr_table_get_dynamic_section(phdr,
+ phdr_count,
+ load_address,
+ &dynamic_,
+ &dynamic_count_,
+ &dynamic_flags_);
+ if (!dynamic_) {
+ *error = "No PT_DYNAMIC section!";
+ return false;
+ }
+
+ // Compute the program header table address relative to load_address.
+ // This is different from |phdr|..|phdr + phdr_count| which can actually
+ // be at a different location.
+ const ELF::Phdr* phdr0 = NULL;
+
+ // First, if there is a PT_PHDR, use it directly.
+ for (size_t n = 0; n < phdr_count; ++n) {
+ const ELF::Phdr* entry = &phdr[n];
+ if (entry->p_type == PT_PHDR) {
+ phdr0 = entry;
+ break;
+ }
+ }
+
+ // Otherwise, check the first loadable segment. If its file offset
+ // is 0, it starts with the ELF header, and we can trivially find the
+ // loaded program header from it.
+ if (!phdr0) {
+ for (size_t n = 0; n < phdr_count; ++n) {
+ const ELF::Phdr* entry = &phdr[n];
+ if (entry->p_type == PT_LOAD) {
+ if (entry->p_offset == 0) {
+ ELF::Addr elf_addr = load_bias_ + entry->p_vaddr;
+ const ELF::Ehdr* ehdr = reinterpret_cast<const ELF::Ehdr*>(elf_addr);
+ ELF::Addr offset = ehdr->e_phoff;
+ phdr0 = reinterpret_cast<const ELF::Phdr*>(elf_addr + offset);
+ }
+ break;
+ }
+ }
+ }
+
+ // Check that the program header table is indeed in a loadable segment,
+ // this helps catching malformed ELF binaries.
+ if (phdr0) {
+ ELF::Addr phdr0_addr = reinterpret_cast<ELF::Addr>(phdr0);
+ ELF::Addr phdr0_limit = phdr0_addr + sizeof(ELF::Phdr) * phdr_count;
+ bool found = false;
+ for (size_t n = 0; n < phdr_count; ++n) {
+ size_t seg_start = load_bias_ + phdr[n].p_vaddr;
+ size_t seg_end = seg_start + phdr[n].p_filesz;
+
+ if (seg_start <= phdr0_addr && phdr0_limit <= seg_end) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ phdr0 = NULL;
+ }
+
+ if (!phdr0) {
+ *error = "Malformed ELF binary";
+ return false;
+ }
+
+ phdr_ = phdr0;
+ phdr_count_ = phdr_count;
+
+ LOG("%s: New ELF view [load_address:%p, load_size:%p, load_bias:%p, phdr:%p, "
+ "phdr_count:%d, dynamic:%p, dynamic_count:%d, dynamic_flags:%d",
+ __FUNCTION__,
+ load_address_,
+ load_size_,
+ load_bias_,
+ phdr_,
+ phdr_count_,
+ dynamic_,
+ dynamic_count_,
+ dynamic_flags_);
+ return true;
+}
+
+bool ElfView::ProtectRelroSection(Error* error) {
+ LOG("%s: Enabling GNU RELRO protection\n", __FUNCTION__);
+
+ if (phdr_table_protect_gnu_relro(phdr_, phdr_count_, load_bias_) < 0) {
+ error->Format("Can't enable GNU RELRO protection: %s", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+} // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_view.h b/sources/android/crazy_linker/src/crazy_linker_elf_view.h
new file mode 100644
index 000000000..2dbf47bec
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_view.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRAZY_LINKER_ELF_VIEW_H
+#define CRAZY_LINKER_ELF_VIEW_H
+
+#include <string.h>
+
+#include "crazy_linker_error.h"
+#include "elf_traits.h"
+
+namespace crazy {
+
+class Error;
+
+// An ElfView holds information describing a given ELF binary file for
+// the crazy linker. This can be used to describe either system or crazy
+// libraries.
+class ElfView {
+public:
+ ElfView() { ::memset(this, 0, sizeof(*this)); }
+
+ ~ElfView() {}
+
+ // Initialize this ElfView from its load address and a copy of its program
+ // header table.
+ // |load_address| is the desired library load address.
+ // |phdr| is a pointer to the library's program header. Note that this can
+ // point to any memory location that contains a valid copy of the header.
+ // I.e. the library does not have to be mapped in the process.
+ // |phdr_count| number of entries in program header table.
+ // On failure, return false and set |error| message.
+ // On success, return true, and sets all fields of the ElfView to the
+ // appropriate values. Note that functions phdr() or dynamic() will always
+ // return an address relative to |load_address|, even if the binary was
+ // not loaded yet in the process.
+ bool InitUnmapped(size_t load_address,
+ const ELF::Phdr* phdr,
+ size_t phdr_count,
+ Error* error);
+
+ const ELF::Phdr* phdr() const { return phdr_; }
+ size_t phdr_count() const { return phdr_count_; }
+ const ELF::Dyn* dynamic() const { return dynamic_; }
+ size_t dynamic_count() const { return dynamic_count_; }
+ size_t dynamic_flags() const { return dynamic_flags_; }
+ size_t load_address() const { return load_address_; }
+ size_t load_size() const { return load_size_; }
+ size_t load_bias() const { return load_bias_; }
+
+ // Helper class to iterate over the dynamic table.
+ // Usage example:
+ // DynamicIterator iter;
+ // for ( ; iter.HasNext(); iter.SkipNext()) {
+ // if (iter.GetTag() == DT_SOME_TAG) {
+ // ... use iter.GetValue()
+ // ... or iter.GetAddress(load_address)
+ // }
+ // }
+ class DynamicIterator {
+ public:
+ DynamicIterator(const ElfView* view) {
+ dyn_ = view->dynamic();
+ dyn_limit_ = dyn_ + view->dynamic_count();
+ }
+
+ ~DynamicIterator() {}
+
+ bool HasNext() const { return dyn_ < dyn_limit_; }
+ void GetNext() { dyn_ += 1; }
+
+ ELF::Addr GetTag() const { return dyn_->d_tag; }
+
+ ELF::Addr GetValue() const { return dyn_->d_un.d_val; }
+
+ ELF::Addr* GetValuePointer() const {
+ return const_cast<ELF::Addr*>(&dyn_->d_un.d_val);
+ }
+
+ uintptr_t GetOffset() const { return dyn_->d_un.d_ptr; }
+
+ uintptr_t GetAddress(size_t load_bias) const {
+ return load_bias + dyn_->d_un.d_ptr;
+ }
+
+ private:
+ const ELF::Dyn* dyn_;
+ const ELF::Dyn* dyn_limit_;
+ };
+
+ // Ensure the RELRO section is read-only after relocations. Assume the
+ // ELF binary is mapped.On failure, return false and set |error| message.
+ bool ProtectRelroSection(Error* error);
+
+protected:
+ const ELF::Phdr* phdr_;
+ size_t phdr_count_;
+ const ELF::Dyn* dynamic_;
+ size_t dynamic_count_;
+ size_t dynamic_flags_;
+ size_t load_address_;
+ size_t load_size_;
+ size_t load_bias_;
+};
+
+} // namespace crazy
+
+
+#endif // CRAZY_LINKER_ELF_VIEW_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_library_list.cpp b/sources/android/crazy_linker/src/crazy_linker_library_list.cpp
index dd605d96c..6c6a11808 100644
--- a/sources/android/crazy_linker/src/crazy_linker_library_list.cpp
+++ b/sources/android/crazy_linker/src/crazy_linker_library_list.cpp
@@ -27,11 +27,11 @@ struct SymbolLookupState {
// Check a symbol entry.
bool CheckSymbol(const char* symbol, SharedLibrary* lib) {
- ELF::Sym* entry = lib->LookupSymbolEntry(symbol);
+ const ELF::Sym* entry = lib->LookupSymbolEntry(symbol);
if (!entry)
return false;
- void* address = reinterpret_cast<void*>(lib->base + entry->st_value);
+ void* address = reinterpret_cast<void*>(lib->load_bias() + entry->st_value);
// If this is a strong symbol, record it and return true.
if (ELF_ST_BIND(entry->st_info) == STB_GLOBAL) {
@@ -132,14 +132,12 @@ LibraryView* LibraryList::FindLibraryForAddress(void* address) {
// Linearly scan all libraries, looking for one that contains
// a given address. NOTE: This doesn't check that this falls
// inside one of the mapped library segments.
- uintptr_t addr = reinterpret_cast<uintptr_t>(address);
-
for (size_t n = 0; n < known_libraries_.GetCount(); ++n) {
LibraryView* wrap = known_libraries_[n];
// TODO(digit): Search addresses inside system libraries.
if (wrap->IsCrazy()) {
SharedLibrary* lib = wrap->GetCrazy();
- if (lib->base <= addr && addr < lib->base + lib->size)
+ if (lib->ContainsAddress(address))
return wrap;
}
}
@@ -148,12 +146,10 @@ LibraryView* LibraryList::FindLibraryForAddress(void* address) {
#ifdef __arm__
_Unwind_Ptr LibraryList::FindArmExIdx(void* pc, int* count) {
- uintptr_t addr = reinterpret_cast<uintptr_t>(pc);
-
- for (SharedLibrary* lib = head_; lib; lib = lib->list_next) {
- if (addr >= lib->base && addr < lib->base + lib->size) {
- *count = static_cast<int>(lib->ARM_exidx_count);
- return reinterpret_cast<_Unwind_Ptr>(lib->ARM_exidx);
+ for (SharedLibrary* lib = head_; lib; lib = lib->list_next_) {
+ if (lib->ContainsAddress(pc)) {
+ *count = static_cast<int>(lib->arm_exidx_count_);
+ return reinterpret_cast<_Unwind_Ptr>(lib->arm_exidx_);
}
}
*count = 0;
@@ -162,12 +158,12 @@ _Unwind_Ptr LibraryList::FindArmExIdx(void* pc, int* count) {
#else // !__arm__
int LibraryList::IteratePhdr(PhdrIterationCallback callback, void* data) {
int result = 0;
- for (SharedLibrary* lib = head_; lib; lib = lib->list_next) {
+ for (SharedLibrary* lib = head_; lib; lib = lib->list_next_) {
dl_phdr_info info;
- info.dlpi_addr = lib->link_map.l_addr;
- info.dlpi_name = lib->link_map.l_name;
- info.dlpi_phdr = lib->phdr;
- info.dlpi_phnum = lib->phnum;
+ info.dlpi_addr = lib->link_map_.l_addr;
+ info.dlpi_name = lib->link_map_.l_name;
+ info.dlpi_phdr = lib->phdr();
+ info.dlpi_phnum = lib->phdr_count();
result = callback(&info, sizeof(info), data);
if (result)
break;
@@ -194,12 +190,12 @@ void LibraryList::UnloadLibrary(LibraryView* wrap) {
SharedLibrary* lib = wrap->GetCrazy();
// Remove from internal list of crazy libraries.
- if (lib->list_next)
- lib->list_next->list_prev = lib->list_prev;
- if (lib->list_prev)
- lib->list_prev->list_next = lib->list_next;
+ if (lib->list_next_)
+ lib->list_next_->list_prev_ = lib->list_prev_;
+ if (lib->list_prev_)
+ lib->list_prev_->list_next_ = lib->list_next_;
if (lib == head_)
- head_ = lib->list_next;
+ head_ = lib->list_next_;
// Call JNI_OnUnload, if necessary, then the destructors.
lib->CallJniOnUnload();
@@ -214,7 +210,7 @@ void LibraryList::UnloadLibrary(LibraryView* wrap) {
}
// Tell GDB of this removal.
- Globals::GetRDebug()->DelEntry(&lib->link_map);
+ Globals::GetRDebug()->DelEntry(&lib->link_map_);
}
known_libraries_.Remove(wrap);
@@ -247,7 +243,7 @@ LibraryView* LibraryList::LoadLibrary(const char* lib_name,
load_address);
return NULL;
}
- uintptr_t actual_address = wrap->GetCrazy()->base;
+ uintptr_t actual_address = wrap->GetCrazy()->load_address();
if (actual_address != load_address) {
error->Format("Library already loaded at @%08x, can't load it at @%08x",
actual_address,
@@ -345,18 +341,18 @@ LibraryView* LibraryList::LoadLibrary(const char* lib_name,
return NULL;
// Notify GDB of load.
- lib->link_map.l_addr = lib->base;
- lib->link_map.l_name = const_cast<char*>(lib->base_name);
- lib->link_map.l_ld = reinterpret_cast<uintptr_t>(lib->dynamic);
- Globals::GetRDebug()->AddEntry(&lib->link_map);
+ lib->link_map_.l_addr = lib->load_address();
+ lib->link_map_.l_name = const_cast<char*>(lib->base_name_);
+ lib->link_map_.l_ld = reinterpret_cast<uintptr_t>(lib->view_.dynamic());
+ Globals::GetRDebug()->AddEntry(&lib->link_map_);
// The library was properly loaded, add it to the list of crazy
// libraries. IMPORTANT: Do this _before_ calling the constructors
// because these could call dlopen().
- lib->list_next = head_;
- lib->list_prev = NULL;
+ lib->list_next_ = head_;
+ lib->list_prev_ = NULL;
if (head_)
- head_->list_prev = lib.Get();
+ head_->list_prev_ = lib.Get();
head_ = lib.Get();
// Then create a new LibraryView for it.
diff --git a/sources/android/crazy_linker/src/crazy_linker_library_view.cpp b/sources/android/crazy_linker/src/crazy_linker_library_view.cpp
index c78e87cee..28fc62e5c 100644
--- a/sources/android/crazy_linker/src/crazy_linker_library_view.cpp
+++ b/sources/android/crazy_linker/src/crazy_linker_library_view.cpp
@@ -40,44 +40,14 @@ bool LibraryView::GetInfo(size_t* load_address,
size_t* load_size,
size_t* relro_start,
size_t* relro_size,
- int* relro_fd,
Error* error) {
if (type_ != TYPE_CRAZY) {
*error = "No RELRO sharing with system libraries";
return false;
}
- *load_address = crazy_->base;
- *load_size = crazy_->size;
- *relro_start = crazy_->relro_start;
- *relro_size = crazy_->relro_size;
- *relro_fd = crazy_->relro_fd;
+ crazy_->GetInfo(load_address, load_size, relro_start, relro_size);
return true;
}
-bool LibraryView::EnableSharedRelro(Error* error) {
- if (type_ != TYPE_CRAZY) {
- *error = "No RELRO sharing with system libraries";
- return false;
- }
-
- return crazy_->EnableSharedRelro(error);
-}
-
-bool LibraryView::UseSharedRelro(size_t relro_start,
- size_t relro_size,
- int relro_fd,
- Error* error) {
- if (type_ != TYPE_CRAZY) {
- *error = "No RELRO sharing with system libraries";
- return false;
- }
-
- // If there is no RELRO segment, don't do anything.
- if (relro_fd < 0 || relro_size == 0)
- return true;
-
- return crazy_->UseSharedRelro(relro_start, relro_size, relro_fd, error);
-}
-
} // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_library_view.h b/sources/android/crazy_linker/src/crazy_linker_library_view.h
index ed776d089..84a46f03e 100644
--- a/sources/android/crazy_linker/src/crazy_linker_library_view.h
+++ b/sources/android/crazy_linker/src/crazy_linker_library_view.h
@@ -69,31 +69,8 @@ class LibraryView {
size_t* load_size,
size_t* relro_start,
size_t* relro_size,
- int* relro_fd,
Error* error);
- // Enable RELRO section sharing. On This moves the RELRO section into
- // a shareable ashmem region. On success, return true and sets
- // |*load_address| to the library load address, |*load_size| to its
- // full load size, |*relro_start| to the start of the RELRO section
- // in memory, |*relro_size| to its size, and |*relro_fd| to the ashmem
- // region's file descriptor.
- // On failure, return false and sets |error| message.
- bool EnableSharedRelro(Error* error);
-
- // Use a shared RELRO section from another process. |relro_start|
- // and |relro_size| are the start and size of the RELRO section,
- // and |relro_fd| is a file descriptor for the ashmem region.
- // On success, return true. On failure, return false and sets
- // |error| message.
- // NOTE: This function doesn't do anything except return true
- // if |relro_fd| is negative, or |relro_size| is 0. This shall correspond
- // to the case where there is no RELRO section in a library.
- bool UseSharedRelro(size_t relro_start,
- size_t relro_size,
- int relro_fd,
- Error* error);
-
// Only used for debugging.
int ref_count() const { return ref_count_; }
diff --git a/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp b/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp
index 0f7a72e4e..f3128847a 100644
--- a/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp
+++ b/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp
@@ -13,7 +13,7 @@
#include "crazy_linker_ashmem.h"
#include "crazy_linker_debug.h"
#include "crazy_linker_elf_loader.h"
-#include "crazy_linker_elf_relocator.h"
+#include "crazy_linker_elf_relocations.h"
#include "crazy_linker_library_list.h"
#include "crazy_linker_library_view.h"
#include "crazy_linker_globals.h"
@@ -67,32 +67,6 @@ typedef SharedLibrary::linker_function_t linker_function_t;
typedef int (*JNI_OnLoadFunctionPtr)(void* vm, void* reserved);
typedef void (*JNI_OnUnloadFunctionPtr)(void* vm, void* reserved);
-// Compute the ELF hash of a given symbol.
-unsigned ElfHash(const char* name) {
- const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
- unsigned h = 0;
- while (*ptr) {
- h = (h << 4) + *ptr++;
- unsigned g = h & 0xf0000000;
- h ^= g;
- h ^= g >> 24;
- }
- return h;
-}
-
-ELF::Sym* LookupSymbolForAddress(SharedLibrary* lib, void* address) {
- ELF::Addr elf_addr = reinterpret_cast<ELF::Addr>(address) - lib->base;
-
- for (size_t n = 0; n < lib->nchain; ++n) {
- ELF::Sym* sym = &lib->symtab[n];
- if (sym->st_shndx != SHN_UNDEF && elf_addr >= sym->st_value &&
- elf_addr < sym->st_value + sym->st_size) {
- return sym;
- }
- }
- return NULL;
-}
-
// Call a constructor or destructor function pointer. Ignore
// NULL and -1 values intentionally. They correspond to markers
// in the tables, or deleted values.
@@ -106,179 +80,10 @@ void CallFunction(linker_function_t func, const char* func_type) {
func();
}
-// Parse the dynamic section of |lib| and populate various important
-// fields from it. On success, returns true. On failure, returns false,
-// and sets |error| message.
-bool ParseLibraryDynamicTable(SharedLibrary* lib, Error* error) {
- const ELF::Phdr* phdr = lib->phdr;
- size_t phdr_count = lib->phnum;
- ELF::Addr base = lib->base;
-
- phdr_table_get_dynamic_section(phdr,
- phdr_count,
- base,
- &lib->dynamic,
- &lib->dynamic_count,
- &lib->dynamic_flags);
- if (!lib->dynamic) {
- *error = "No PT_DYNAMIC section!";
- return false;
- }
-
-#ifdef __arm__
- (void)phdr_table_get_arm_exidx(
- phdr, phdr_count, base, &lib->ARM_exidx, &lib->ARM_exidx_count);
-#endif
-
- for (ELF::Dyn* dyn = lib->dynamic; dyn->d_tag != DT_NULL; ++dyn) {
- ELF::Addr dyn_value = dyn->d_un.d_val;
- uintptr_t dyn_addr = base + dyn->d_un.d_ptr;
- switch (dyn->d_tag) {
- case DT_HASH:
- LOG(" DT_HASH addr=%p\n", dyn_addr);
- {
- uintptr_t* data = reinterpret_cast<uintptr_t*>(dyn_addr);
- lib->nbucket = data[0];
- lib->nchain = data[1];
- lib->bucket = data + 2;
- lib->chain = data + 2 + lib->nbucket;
- }
- break;
- case DT_STRTAB:
- LOG(" DT_STRTAB addr=%p\n", dyn_addr);
- lib->strtab = reinterpret_cast<const char*>(dyn_addr);
- break;
- case DT_SYMTAB:
- LOG(" DT_SYMTAB addr=%p\n", dyn_addr);
- lib->symtab = reinterpret_cast<ELF::Sym*>(dyn_addr);
- break;
- case DT_DEBUG:
- // TODO(digit): Move this to a different location.
- if (lib->dynamic_flags & PF_W) {
- dyn->d_un.d_val =
- reinterpret_cast<uintptr_t>(Globals::GetRDebug()->GetAddress());
- }
- break;
- case DT_INIT:
- LOG(" DT_INIT addr=%p\n", dyn_addr);
- lib->init_func = reinterpret_cast<linker_function_t>(dyn_addr);
- break;
- case DT_FINI:
- LOG(" DT_FINI addr=%p\n", dyn_addr);
- lib->fini_func = reinterpret_cast<linker_function_t>(dyn_addr);
- break;
- case DT_INIT_ARRAY:
- LOG(" DT_INIT_ARRAY addr=%p\n", dyn_addr);
- lib->init_array = reinterpret_cast<linker_function_t*>(dyn_addr);
- break;
- case DT_INIT_ARRAYSZ:
- lib->init_array_count = dyn_value / sizeof(ELF::Addr);
- LOG(" DT_INIT_ARRAYSZ value=%p count=%p\n",
- dyn_value,
- lib->init_array_count);
- break;
- case DT_FINI_ARRAY:
- LOG(" DT_FINI_ARRAY addr=%p\n", dyn_addr);
- lib->fini_array = reinterpret_cast<linker_function_t*>(dyn_addr);
- break;
- case DT_FINI_ARRAYSZ:
- lib->fini_array_count = dyn_value / sizeof(ELF::Addr);
- LOG(" DT_FINI_ARRAYSZ value=%p count=%p\n",
- dyn_value,
- lib->fini_array_count);
- break;
- case DT_PREINIT_ARRAY:
- LOG(" DT_PREINIT_ARRAY addr=%p\n", dyn_addr);
- lib->preinit_array = reinterpret_cast<linker_function_t*>(dyn_addr);
- break;
- case DT_PREINIT_ARRAYSZ:
- lib->preinit_array_count = dyn_value / sizeof(ELF::Addr);
- LOG(" DT_PREINIT_ARRAYSZ value=%p count=%p\n",
- dyn_value,
- lib->preinit_array_count);
- break;
- case DT_SYMBOLIC:
- LOG(" DT_SYMBOLIC\n");
- lib->has_DT_SYMBOLIC = true;
- break;
- case DT_FLAGS:
- if (dyn_value & DF_SYMBOLIC)
- lib->has_DT_SYMBOLIC = true;
- break;
-#if defined(__mips__)
- case DT_MIPS_RLD_MAP:
- // TODO(digit): Move this to different location.
- dyn->d_un.d_ptr =
- reinterpret_cast<ELF::Addr>(Globals::GetRDebug()->GetAddress());
- break;
-#endif
- default:
- ;
- }
- }
-
- // Perform a few sanity checks.
- if (!lib->nbucket) {
- *error = "Missing DT_HASH entry (built with --hash-style=gnu?)";
- return false;
- }
- if (!lib->strtab) {
- *error = "Missing DT_STRTAB entry";
- return false;
- }
- if (!lib->symtab) {
- *error = "Missing DT_SYMTAB entry";
- return false;
- }
- return true;
-}
-
-bool LoadLibrary(SharedLibrary* lib,
- const char* full_path,
- size_t load_address,
- size_t file_offset,
- Error* error) {
- // First, record the path.
- LOG("%s: full path '%s'\n", __FUNCTION__, full_path);
-
- size_t full_path_len = strlen(full_path);
- if (full_path_len >= sizeof(lib->full_path)) {
- error->Format("Path too long: %s", full_path);
- return false;
- }
-
- strlcpy(lib->full_path, full_path, sizeof(lib->full_path));
- lib->base_name = GetBaseNamePtr(lib->full_path);
-
- // Load the ELF binary in memory.
- LOG("%s: Loading ELF segments for %s\n", __FUNCTION__, lib->base_name);
-
- {
- ElfLoader loader;
- if (!loader.LoadAt(lib->full_path, file_offset, load_address, error)) {
- return false;
- }
-
- lib->base = loader.load_start();
- lib->size = loader.load_size();
- lib->load_bias = loader.load_bias();
- lib->phnum = loader.phdr_count();
- lib->phdr = loader.loaded_phdr();
- }
-
- // Parse the dynamic table to extract useful information.
- LOG("%s: Parsing dynamic table of %s\n", __FUNCTION__, lib->base_name);
- if (!ParseLibraryDynamicTable(lib, error))
- return false;
-
- LOG("%s: Load complete for %s\n", __FUNCTION__, lib->base_name);
- return true;
-}
-
// An instance of ElfRelocator::SymbolResolver that can be used
// to resolve symbols in a shared library being loaded by
// LibraryList::LoadLibrary.
-class SharedLibraryResolver : public ElfRelocator::SymbolResolver {
+class SharedLibraryResolver : public ElfRelocations::SymbolResolver {
public:
SharedLibraryResolver(SharedLibrary* lib,
LibraryList* lib_list,
@@ -289,9 +94,9 @@ class SharedLibraryResolver : public ElfRelocator::SymbolResolver {
// TODO(digit): Add the ability to lookup inside the main executable.
// First, look inside the current library.
- ELF::Sym* entry = lib_->LookupSymbolEntry(symbol_name);
+ const ELF::Sym* entry = lib_->LookupSymbolEntry(symbol_name);
if (entry)
- return reinterpret_cast<void*>(lib_->load_bias + entry->st_value);
+ return reinterpret_cast<void*>(lib_->load_bias() + entry->st_value);
// Special case: redirect the dynamic linker symbols to our wrappers.
// This ensures that loaded libraries can call dlopen() / dlsym()
@@ -314,7 +119,7 @@ class SharedLibraryResolver : public ElfRelocator::SymbolResolver {
SharedLibrary* dep = wrap->GetCrazy();
entry = dep->LookupSymbolEntry(symbol_name);
if (entry)
- return reinterpret_cast<void*>(dep->load_bias + entry->st_value);
+ return reinterpret_cast<void*>(dep->load_bias() + entry->st_value);
}
}
@@ -327,281 +132,199 @@ class SharedLibraryResolver : public ElfRelocator::SymbolResolver {
Vector<LibraryView*>* dependencies_;
};
-bool RelocateLibrary(SharedLibrary* lib,
- LibraryList* lib_list,
- Vector<LibraryView*>* dependencies,
- Error* error) {
- // Apply relocations.
- LOG("%s: Applying relocations to %s\n", __FUNCTION__, lib->base_name);
-
- ElfRelocator relocator;
-
- if (!relocator.Init(lib->phdr,
- lib->phnum,
- lib->load_bias,
- lib->dynamic,
- lib->dynamic_count,
- error)) {
- return false;
- }
-
- SharedLibraryResolver resolver(lib, lib_list, dependencies);
- if (!relocator.Apply(&resolver, lib->strtab, lib->symtab, error))
- return false;
-
- LOG("%s: Relocations applied for %s\n", __FUNCTION__, lib->base_name);
- return true;
-}
-
-// Copy all the pages from |addr| and |addr + size| into an ashmem
-// region identified by |fd|.
-bool CopyPagesToFd(void* addr, size_t size, int fd, Error* error) {
-
- // Create temporary mapping of the ashmem region.
- // And copy current pages there.
- ScopedMemoryMapping fd_map;
-
- if (!fd_map.Allocate(NULL, size, MemoryMapping::CAN_READ_WRITE, fd)) {
- error->Format("Cannot map ashmem region of %d bytes as writable: %s",
- size,
- strerror(errno));
- return false;
- }
-
- // Copy current pages there.
- ::memcpy(fd_map.Get(), addr, size);
- return true;
-}
-
-// Swap pages between |addr| and |addr + size| with the bytes
-// from the ashmem region identified by |fd|, starting from
-// a given |offset|. On failure return false and set |error| message.
-bool SwapPagesFromFd(void* addr,
- size_t size,
- int fd,
- size_t offset,
- Error* error) {
- // Unmap current pages.
- if (::munmap(addr, size) < 0) {
- error->Format("%s: Could not unmap %p-%p: %s",
- __FUNCTION__,
- addr,
- (char*)addr + size,
- strerror(errno));
- return false;
- }
-
- // Remap the fd pages at the same location now.
- void* new_map = ::mmap(addr,
- size,
- PROT_READ,
- MAP_FIXED | MAP_SHARED,
- fd,
- static_cast<off_t>(offset));
- if (new_map == MAP_FAILED) {
- char* p = reinterpret_cast<char*>(addr);
- error->Format("%s: Could not map %p-%p: %s",
- __FUNCTION__,
- p,
- p + size,
- strerror(errno));
- return false;
- }
-
-#ifdef __arm__
- __clear_cache(addr, (char*)addr + size);
-#endif
+} // namespace
- // Done.
- return true;
-}
+SharedLibrary::SharedLibrary() { ::memset(this, 0, sizeof(*this)); }
-bool PageEquals(const char* p1, const char* p2) {
- return ::memcmp(p1, p2, PAGE_SIZE) == 0;
+SharedLibrary::~SharedLibrary() {
+ // Ensure the library is unmapped on destruction.
+ if (view_.load_address())
+ munmap(reinterpret_cast<void*>(view_.load_address()), view_.load_size());
}
-// Conditionally swap pages between |address| and |address + size| with
-// the ones from the ashmem region identified by |fd|. This only swaps
-// pages that have exactly the same content. On failure, return false
-// and set |error| message.
-bool SwapSimilarPagesFromFd(void* addr, size_t size, int fd, Error* error) {
- // Create temporary mapping of the ashmem region.
- ScopedMemoryMapping fd_map;
-
- LOG("%s: Entering addr=%p size=%p fd=%d\n",
- __FUNCTION__,
- addr,
- (void*)size,
- fd);
+bool SharedLibrary::Load(const char* full_path,
+ size_t load_address,
+ size_t file_offset,
+ Error* error) {
+ // First, record the path.
+ LOG("%s: full path '%s'\n", __FUNCTION__, full_path);
- if (!fd_map.Allocate(NULL, size, MemoryMapping::CAN_READ, fd)) {
- error->Format("Cannot map ashmem region as read-only: %s\n",
- strerror(errno));
+ size_t full_path_len = strlen(full_path);
+ if (full_path_len >= sizeof(full_path_)) {
+ error->Format("Path too long: %s", full_path);
return false;
}
- LOG("%s: mapping allocate at %p\n", __FUNCTION__, fd_map.Get());
+ strlcpy(full_path_, full_path, sizeof(full_path_));
+ base_name_ = GetBaseNamePtr(full_path_);
- char* cur_page = reinterpret_cast<char*>(addr);
- char* fd_page = reinterpret_cast<char*>(fd_map.Get());
- size_t p = 0;
- size_t similar_size = 0;
+ // Load the ELF binary in memory.
+ LOG("%s: Loading ELF segments for %s\n", __FUNCTION__, base_name_);
- do {
- // Skip over dissimilar pages.
- while (p < size && !PageEquals(cur_page + p, fd_page + p)) {
- p += PAGE_SIZE;
+ {
+ ElfLoader loader;
+ if (!loader.LoadAt(full_path_, file_offset, load_address, error)) {
+ return false;
}
- // Count similar pages.
- size_t p2 = p;
- while (p2 < size && PageEquals(cur_page + p2, fd_page + p2)) {
- p2 += PAGE_SIZE;
+ if (!view_.InitUnmapped(loader.load_start(),
+ loader.loaded_phdr(),
+ loader.phdr_count(),
+ error)) {
+ return false;
}
- if (p2 > p) {
- // Swap pages between |pos| and |pos2|.
- LOG("%s: Swap pages at %p-%p\n",
- __FUNCTION__,
- cur_page + p,
- cur_page + p2);
- if (!SwapPagesFromFd(cur_page + p, p2 - p, fd, p, error))
- return false;
-
- similar_size += (p2 - p);
+ if (!symbols_.Init(&view_)) {
+ *error = "Missing or malformed symbol table";
+ return false;
}
+ }
- p = p2;
- } while (p < size);
-
- LOG("%s: Swapped %d pages over %d (%d %%, %d KB not shared)\n",
- __FUNCTION__,
- similar_size / PAGE_SIZE,
- size / PAGE_SIZE,
- similar_size * 100 / size,
- (size - similar_size) / 4096);
-
- return true;
-}
-
-} // namespace
-
-SharedLibrary::SharedLibrary() {
- // Ensure all fields are empty.
- memset(this, 0, sizeof(*this));
- this->relro_fd = -1;
-}
-
-SharedLibrary::~SharedLibrary() {
- // Ensure the library is unmapped on destruction.
- if (this->base)
- munmap(reinterpret_cast<void*>(this->base), this->size);
- // Ensure the relro ashmem file descriptor is closed.
- if (this->relro_fd >= 0)
- close(this->relro_fd);
-}
-
-bool SharedLibrary::Load(const char* full_path,
- size_t load_address,
- size_t file_offset,
- Error* error) {
- return LoadLibrary(this, full_path, load_address, file_offset, error);
-}
+ if (phdr_table_get_relro_info(view_.phdr(),
+ view_.phdr_count(),
+ view_.load_bias(),
+ &relro_start_,
+ &relro_size_) < 0) {
+ relro_start_ = 0;
+ relro_size_ = 0;
+ }
-bool SharedLibrary::Relocate(LibraryList* lib_list,
- Vector<LibraryView*>* dependencies,
- Error* error) {
- return RelocateLibrary(this, lib_list, dependencies, error);
-}
+#ifdef __arm__
+ LOG("%s: Extracting ARM.exidx table for %s\n", __FUNCTION__, base_name_);
+ (void)phdr_table_get_arm_exidx(
+ phdr(), phdr_count(), load_bias(), &arm_exidx_, &arm_exidx_count_);
+#endif
-ELF::Sym* SharedLibrary::LookupSymbolEntry(const char* symbol_name) {
- unsigned hash = ElfHash(symbol_name);
- ELF::Sym* symbols = this->symtab;
- const char* strings = this->strtab;
-
- for (unsigned n = this->bucket[hash % this->nbucket]; n != 0;
- n = this->chain[n]) {
- ELF::Sym* symbol = &symbols[n];
- // Check that the symbol has the appropriate name.
- if (strcmp(strings + symbol->st_name, symbol_name))
- continue;
- // Ignore undefined symbols.
- if (symbol->st_shndx == SHN_UNDEF)
- continue;
- // Ignore anything that isn't a global or weak definition.
- switch (ELF_ST_BIND(symbol->st_info)) {
- case STB_GLOBAL:
- case STB_WEAK:
- return symbol;
+ LOG("%s: Parsing dynamic table for %s\n", __FUNCTION__, base_name_);
+ ElfView::DynamicIterator dyn(&view_);
+ for (; dyn.HasNext(); dyn.GetNext()) {
+ ELF::Addr dyn_value = dyn.GetValue();
+ uintptr_t dyn_addr = dyn.GetAddress(load_bias());
+ switch (dyn.GetTag()) {
+ case DT_DEBUG:
+ if (view_.dynamic_flags() & PF_W) {
+ *dyn.GetValuePointer() =
+ reinterpret_cast<uintptr_t>(Globals::GetRDebug()->GetAddress());
+ }
+ break;
+ case DT_INIT:
+ LOG(" DT_INIT addr=%p\n", dyn_addr);
+ init_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+ break;
+ case DT_FINI:
+ LOG(" DT_FINI addr=%p\n", dyn_addr);
+ fini_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+ break;
+ case DT_INIT_ARRAY:
+ LOG(" DT_INIT_ARRAY addr=%p\n", dyn_addr);
+ init_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+ break;
+ case DT_INIT_ARRAYSZ:
+ init_array_count_ = dyn_value / sizeof(ELF::Addr);
+ LOG(" DT_INIT_ARRAYSZ value=%p count=%p\n",
+ dyn_value,
+ init_array_count_);
+ break;
+ case DT_FINI_ARRAY:
+ LOG(" DT_FINI_ARRAY addr=%p\n", dyn_addr);
+ fini_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+ break;
+ case DT_FINI_ARRAYSZ:
+ fini_array_count_ = dyn_value / sizeof(ELF::Addr);
+ LOG(" DT_FINI_ARRAYSZ value=%p count=%p\n",
+ dyn_value,
+ fini_array_count_);
+ break;
+ case DT_PREINIT_ARRAY:
+ LOG(" DT_PREINIT_ARRAY addr=%p\n", dyn_addr);
+ preinit_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+ break;
+ case DT_PREINIT_ARRAYSZ:
+ preinit_array_count_ = dyn_value / sizeof(ELF::Addr);
+ LOG(" DT_PREINIT_ARRAYSZ value=%p count=%p\n",
+ dyn_value,
+ preinit_array_count_);
+ break;
+ case DT_SYMBOLIC:
+ LOG(" DT_SYMBOLIC\n");
+ has_DT_SYMBOLIC_ = true;
+ break;
+ case DT_FLAGS:
+ if (dyn_value & DF_SYMBOLIC)
+ has_DT_SYMBOLIC_ = true;
+ break;
+#if defined(__mips__)
+ case DT_MIPS_RLD_MAP:
+ *dyn.GetValuePointer() =
+ reinterpret_cast<ELF::Addr>(Globals::GetRDebug()->GetAddress());
+ break;
+#endif
default:
;
}
}
- return NULL;
+
+ LOG("%s: Load complete for %s\n", __FUNCTION__, base_name_);
+ return true;
}
-void* SharedLibrary::FindAddressForSymbol(const char* symbol_name) {
- ELF::Sym* entry = LookupSymbolEntry(symbol_name);
- if (!entry)
- return NULL;
+bool SharedLibrary::Relocate(LibraryList* lib_list,
+ Vector<LibraryView*>* dependencies,
+ Error* error) {
+ // Apply relocations.
+ LOG("%s: Applying relocations to %s\n", __FUNCTION__, base_name_);
- return reinterpret_cast<void*>(this->load_bias + entry->st_value);
-}
+ ElfRelocations relocations;
-ELF::Sym* SharedLibrary::FindSymbolForAddress(void* address) {
- return LookupSymbolForAddress(this, address);
-}
+ if (!relocations.Init(&view_, error))
+ return false;
-bool SharedLibrary::EnableSharedRelro(Error* error) {
- if (this->relro_used) {
- *error = "Shared RELRO already used in library";
+ SharedLibraryResolver resolver(this, lib_list, dependencies);
+ if (!relocations.ApplyAll(&symbols_, &resolver, error))
return false;
- }
- if (this->relro_fd != -1) {
- // Shared RELRO is already enabled, nothing to do here.
- return true;
- }
+ LOG("%s: Relocations applied for %s\n", __FUNCTION__, base_name_);
+ return true;
+}
- ELF::Addr relro_start, relro_size;
- if (phdr_table_get_relro_info(
- this->phdr, this->phnum, this->load_bias, &relro_start, &relro_size) <
- 0) {
- // No RELRO section to share.
- return true;
- }
+const ELF::Sym* SharedLibrary::LookupSymbolEntry(const char* symbol_name) {
+ return symbols_.LookupByName(symbol_name);
+}
- if (relro_size == 0) {
- // Probably means there is no real RELRO section.
- return true;
- }
+void* SharedLibrary::FindAddressForSymbol(const char* symbol_name) {
+ return symbols_.LookupAddressByName(symbol_name, view_.load_bias());
+}
- // Allocate new ashmem region.
- this->relro_start = relro_start;
- this->relro_size = relro_size;
- AshmemRegion ashmem;
- String relro_name("RELRO:");
- relro_name += this->base_name;
- if (!ashmem.Allocate(relro_size, relro_name.c_str())) {
- error->Format("Could not allocate ashmem region: %s", strerror(errno));
- return false;
- }
+bool SharedLibrary::CreateSharedRelro(size_t load_address,
+ size_t* relro_start,
+ size_t* relro_size,
+ int* relro_fd,
+ Error* error) {
+ SharedRelro relro;
- void* relro_addr = reinterpret_cast<void*>(relro_start);
- if (!CopyPagesToFd(relro_addr, relro_size, ashmem.Get(), error))
+ if (!relro.Allocate(relro_size_, base_name_, error))
return false;
- // Ensure the ashmem region content isn't writable anymore.
- if (!ashmem.SetProtectionFlags(PROT_READ)) {
- error->Format("Could not make ashmem region read-only: %s",
- strerror(errno));
- return false;
+ if (load_address != 0 && load_address != this->load_address()) {
+ // Need to relocate the content of the ashmem region first to accomodate
+ // for the new load address.
+ if (!relro.CopyFromRelocated(
+ &view_, load_address, relro_start_, relro_size_, error))
+ return false;
+ } else {
+ // Simply copy, no relocations.
+ if (!relro.CopyFrom(relro_start_, relro_size_, error))
+ return false;
}
- if (!SwapPagesFromFd(relro_addr, relro_size, ashmem.Get(), 0, error))
+ // Enforce read-only mode for the region's content.
+ if (!relro.ForceReadOnly(error))
return false;
- this->relro_fd = ashmem.Release();
+ // All good.
+ *relro_start = relro.start();
+ *relro_size = relro.size();
+ *relro_fd = relro.DetachFd();
return true;
}
@@ -609,76 +332,53 @@ bool SharedLibrary::UseSharedRelro(size_t relro_start,
size_t relro_size,
int relro_fd,
Error* error) {
- if (relro_fd < 0 || relro_size == 0) {
- // Nothing to do here.
- return true;
- }
-
LOG("%s: relro_start=%p relro_size=%p relro_fd=%d\n",
__FUNCTION__,
(void*)relro_start,
(void*)relro_size,
relro_fd);
- // Sanity check: A shared RELRO is not already used.
- if (this->relro_used) {
- *error = "Library already using shared RELRO section";
- return false;
- }
-
- // Sanity check: A shared RELRO is not enabled.
- if (this->relro_fd != -1) {
- *error = "Library already enabled its shared RELRO";
- return false;
+ if (relro_fd < 0 || relro_size == 0) {
+ // Nothing to do here.
+ return true;
}
- // Sanity check: Ashmem file descriptor must be read-only.
- if (!AshmemRegion::CheckFileDescriptorIsReadOnly(relro_fd)) {
- error->Format("Ashmem file descriptor is not read-only: %s\n",
- strerror(errno));
+ // Sanity check: A shared RELRO is not already used.
+ if (relro_used_) {
+ *error = "Library already using shared RELRO section";
return false;
}
// Sanity check: RELRO addresses must match.
- ELF::Addr lib_relro_start, lib_relro_size;
- if (phdr_table_get_relro_info(this->phdr,
- this->phnum,
- this->load_bias,
- &lib_relro_start,
- &lib_relro_size) < 0) {
- *error = "No RELRO section in library";
- return false;
- }
-
- if (lib_relro_start != relro_start || lib_relro_size != relro_size) {
+ if (relro_start_ != relro_start || relro_size_ != relro_size) {
error->Format("RELRO mismatch addr=%p size=%p (wanted addr=%p size=%p)",
- lib_relro_start,
- lib_relro_size,
+ relro_start_,
+ relro_size_,
relro_start,
relro_size);
return false;
}
// Everything's good, swap pages in this process's address space.
- void* relro_addr = reinterpret_cast<void*>(relro_start);
- if (!SwapSimilarPagesFromFd(relro_addr, relro_size, relro_fd, error))
+ SharedRelro relro;
+ if (!relro.InitFrom(relro_start, relro_size, relro_fd, error))
return false;
- this->relro_used = true;
+ relro_used_ = true;
return true;
}
void SharedLibrary::CallConstructors() {
- CallFunction(this->init_func, "DT_INIT");
- for (size_t n = 0; n < this->init_array_count; ++n)
- CallFunction(this->init_array[n], "DT_INIT_ARRAY");
+ CallFunction(init_func_, "DT_INIT");
+ for (size_t n = 0; n < init_array_count_; ++n)
+ CallFunction(init_array_[n], "DT_INIT_ARRAY");
}
void SharedLibrary::CallDestructors() {
- for (size_t n = this->fini_array_count; n > 0; --n) {
- CallFunction(this->fini_array[n - 1], "DT_FINI_ARRAY");
+ for (size_t n = fini_array_count_; n > 0; --n) {
+ CallFunction(fini_array_[n - 1], "DT_FINI_ARRAY");
}
- CallFunction(this->fini_func, "DT_FINI");
+ CallFunction(fini_func_, "DT_FINI");
}
bool SharedLibrary::SetJavaVM(void* java_vm,
@@ -689,26 +389,26 @@ bool SharedLibrary::SetJavaVM(void* java_vm,
// Lookup for JNI_OnLoad, exit if it doesn't exist.
JNI_OnLoadFunctionPtr jni_onload = reinterpret_cast<JNI_OnLoadFunctionPtr>(
- this->FindAddressForSymbol("JNI_OnLoad"));
+ FindAddressForSymbol("JNI_OnLoad"));
if (!jni_onload)
return true;
int jni_version = (*jni_onload)(java_vm, NULL);
if (jni_version < minimum_jni_version) {
error->Format("JNI_OnLoad() in %s returned %d, expected at least %d",
- this->full_path,
+ full_path_,
jni_version,
minimum_jni_version);
return false;
}
// Save the JavaVM handle for unload time.
- this->java_vm = java_vm;
+ java_vm_ = java_vm;
return true;
}
void SharedLibrary::CallJniOnUnload() {
- if (!this->java_vm)
+ if (!java_vm_)
return;
JNI_OnUnloadFunctionPtr jni_on_unload =
@@ -716,24 +416,19 @@ void SharedLibrary::CallJniOnUnload() {
this->FindAddressForSymbol("JNI_OnUnload"));
if (jni_on_unload)
- (*jni_on_unload)(this->java_vm, NULL);
+ (*jni_on_unload)(java_vm_, NULL);
}
bool SharedLibrary::DependencyIterator::GetNext() {
dep_name_ = NULL;
- for (;;) {
- if (dynamic_->d_tag == 0)
- return false;
-
- if (dynamic_->d_tag != DT_NEEDED) {
- dynamic_++;
- continue;
+ for (; iter_.HasNext(); iter_.GetNext()) {
+ if (iter_.GetTag() == DT_NEEDED) {
+ dep_name_ = symbols_->GetStringById(iter_.GetValue());
+ iter_.GetNext();
+ return true;
}
-
- dep_name_ = strtab_ + dynamic_->d_un.d_val;
- dynamic_++;
- return true;
}
+ return false;
}
} // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_shared_library.h b/sources/android/crazy_linker/src/crazy_linker_shared_library.h
index aa95a36ab..9cadf3d24 100644
--- a/sources/android/crazy_linker/src/crazy_linker_shared_library.h
+++ b/sources/android/crazy_linker/src/crazy_linker_shared_library.h
@@ -7,6 +7,9 @@
#include <link.h>
+#include "crazy_linker_elf_relro.h"
+#include "crazy_linker_elf_symbols.h"
+#include "crazy_linker_elf_view.h"
#include "crazy_linker_error.h"
#include "crazy_linker_rdebug.h"
#include "crazy_linker_util.h"
@@ -27,64 +30,16 @@ class LibraryView;
class SharedLibrary {
public:
- const ELF::Phdr* phdr;
- size_t phnum;
- ELF::Addr load_bias;
-
- ELF::Addr entry;
- ELF::Addr base;
- size_t size;
-
- ELF::Dyn* dynamic;
- size_t dynamic_count;
- ELF::Word dynamic_flags;
-
- SharedLibrary* list_next;
- SharedLibrary* list_prev;
- unsigned flags;
-
- const char* strtab;
- ELF::Sym* symtab;
-
- size_t nbucket;
- size_t nchain;
- unsigned* bucket;
- unsigned* chain;
-
- typedef void (*linker_function_t)();
-
- linker_function_t* preinit_array;
- size_t preinit_array_count;
- linker_function_t* init_array;
- size_t init_array_count;
- linker_function_t* fini_array;
- size_t fini_array_count;
- linker_function_t init_func;
- linker_function_t fini_func;
-
-#ifdef __arm__
- // ARM EABI section used for stack unwinding.
- unsigned* ARM_exidx;
- size_t ARM_exidx_count;
-#endif
-
- link_map_t link_map;
-
- bool has_DT_SYMBOLIC;
-
- size_t relro_start;
- size_t relro_size;
- int relro_fd;
- bool relro_used;
-
- void* java_vm;
-
- const char* base_name;
- char full_path[512];
-
SharedLibrary();
~SharedLibrary();
+ size_t load_address() const { return view_.load_address(); }
+ size_t load_size() const { return view_.load_size(); }
+ size_t load_bias() const { return view_.load_bias(); }
+ const ELF::Phdr* phdr() const { return view_.phdr(); }
+ size_t phdr_count() const { return view_.phdr_count(); }
+ const char* base_name() const { return base_name_; }
+
// Load a library (without its dependents) from an ELF file.
// Note: This does not apply relocations, nor runs constructors.
// |full_path| if the file full path.
@@ -107,6 +62,23 @@ class SharedLibrary {
Vector<LibraryView*>* dependencies,
Error* error);
+ void GetInfo(size_t* load_address,
+ size_t* load_size,
+ size_t* relro_start,
+ size_t* relro_size) {
+ *load_address = view_.load_address();
+ *load_size = view_.load_size();
+ *relro_start = relro_start_;
+ *relro_size = relro_size_;
+ }
+
+ // Returns true iff a given library is mapped to a virtual address range
+ // that contains a given address.
+ bool ContainsAddress(void* address) const {
+ size_t addr = reinterpret_cast<size_t>(address);
+ return load_address() <= addr && addr <= load_address() + load_size();
+ }
+
// Call all constructors in the library.
void CallConstructors();
@@ -115,21 +87,34 @@ class SharedLibrary {
// Return the ELF symbol entry for a given symbol, if defined by
// this library, or NULL otherwise.
- ELF::Sym* LookupSymbolEntry(const char* symbol_name);
+ const ELF::Sym* LookupSymbolEntry(const char* symbol_name);
+
+ // Find the nearest symbol near a given |address|. On success, return
+ // true and set |*sym_name| to the symbol name, |*sym_addr| to its address
+ // in memory, and |*sym_size| to its size in bytes, if any.
+ bool FindNearestSymbolForAddress(void* address,
+ const char** sym_name,
+ void** sym_addr,
+ size_t* sym_size) {
+ return symbols_.LookupNearestByAddress(
+ address, load_bias(), sym_name, sym_addr, sym_size);
+ }
// Return the address of a given |symbol_name| if it is exported
// by the library, NULL otherwise.
void* FindAddressForSymbol(const char* symbol_name);
- // Find the symbol entry in this library that matches a given
- // address in memory. Returns NULL on failure.
- ELF::Sym* FindSymbolForAddress(void* address);
-
- // Prepare the relro section for sharing with another process.
- // On success, return true and sets relro_fd to an ashmem file
- // descriptor holding the shared RELRO section. On failure
- // return false ands sets |error| message.
- bool EnableSharedRelro(Error* error);
+ // Create a new Ashmem region holding a copy of the library's RELRO section,
+ // potentially relocated for a new |load_address|. On success, return true
+ // and sets |*relro_start|, |*relro_size| and |*relro_fd|. Note that the
+ // RELRO start address is adjusted for |load_address|, and that the caller
+ // becomes the owner of |*relro_fd|. On failure, return false and set
+ // |error| message.
+ bool CreateSharedRelro(size_t load_address,
+ size_t* relro_start,
+ size_t* relro_size,
+ int* relro_fd,
+ Error* error);
// Try to use a shared relro section from another process.
// On success, return true. On failure return false and
@@ -161,7 +146,9 @@ class SharedLibrary {
class DependencyIterator {
public:
DependencyIterator(SharedLibrary* lib)
- : dynamic_(lib->dynamic), strtab_(lib->strtab), dep_name_(NULL) {}
+ : iter_(&lib->view_),
+ symbols_(&lib->symbols_),
+ dep_name_(NULL) {}
bool GetNext();
@@ -172,10 +159,50 @@ class SharedLibrary {
DependencyIterator(const DependencyIterator&);
DependencyIterator& operator=(const DependencyIterator&);
- ELF::Dyn* dynamic_;
- const char* strtab_;
+ ElfView::DynamicIterator iter_;
+ const ElfSymbols* symbols_;
const char* dep_name_;
};
+
+ typedef void (*linker_function_t)();
+
+ private:
+ friend class LibraryList;
+
+ ElfView view_;
+ ElfSymbols symbols_;
+
+ size_t relro_start_;
+ size_t relro_size_;
+ bool relro_used_;
+
+ SharedLibrary* list_next_;
+ SharedLibrary* list_prev_;
+ unsigned flags_;
+
+ linker_function_t* preinit_array_;
+ size_t preinit_array_count_;
+ linker_function_t* init_array_;
+ size_t init_array_count_;
+ linker_function_t* fini_array_;
+ size_t fini_array_count_;
+ linker_function_t init_func_;
+ linker_function_t fini_func_;
+
+#ifdef __arm__
+ // ARM EABI section used for stack unwinding.
+ unsigned* arm_exidx_;
+ size_t arm_exidx_count_;
+#endif
+
+ link_map_t link_map_;
+
+ bool has_DT_SYMBOLIC_;
+
+ void* java_vm_;
+
+ const char* base_name_;
+ char full_path_[512];
};
} // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_wrappers.cpp b/sources/android/crazy_linker/src/crazy_linker_wrappers.cpp
index 39cdf5f86..e177614ef 100644
--- a/sources/android/crazy_linker/src/crazy_linker_wrappers.cpp
+++ b/sources/android/crazy_linker/src/crazy_linker_wrappers.cpp
@@ -169,18 +169,16 @@ int WrapDladdr(void* address, Dl_info* info) {
LibraryList* lib_list = Globals::GetLibraries();
LibraryView* wrap = lib_list->FindLibraryForAddress(address);
if (wrap && wrap->IsCrazy()) {
+ size_t sym_size = 0;
+
SharedLibrary* lib = wrap->GetCrazy();
::memset(info, 0, sizeof(*info));
- info->dli_fname = lib->base_name;
- info->dli_fbase = reinterpret_cast<void*>(lib->base);
+ info->dli_fname = lib->base_name();
+ info->dli_fbase = reinterpret_cast<void*>(lib->load_address());
// Determine if any symbol in the library contains the specified address.
- ELF::Sym* sym = lib->FindSymbolForAddress(address);
- if (sym != NULL) {
- info->dli_sname = lib->strtab + sym->st_name;
- info->dli_saddr =
- reinterpret_cast<void*>(lib->load_bias + sym->st_value);
- }
+ (void)lib->FindNearestSymbolForAddress(
+ address, &info->dli_sname, &info->dli_saddr, &sym_size);
return 0;
}
}
diff --git a/sources/android/crazy_linker/src/linker_phdr.cpp b/sources/android/crazy_linker/src/linker_phdr.cpp
index 4f0a5b4ba..c22f5a19c 100644
--- a/sources/android/crazy_linker/src/linker_phdr.cpp
+++ b/sources/android/crazy_linker/src/linker_phdr.cpp
@@ -380,7 +380,7 @@ int phdr_table_get_arm_exidx(const ELF::Phdr* phdr_table,
void phdr_table_get_dynamic_section(const ELF::Phdr* phdr_table,
int phdr_count,
ELF::Addr load_bias,
- ELF::Dyn** dynamic,
+ const ELF::Dyn** dynamic,
size_t* dynamic_count,
ELF::Word* dynamic_flags) {
const ELF::Phdr* phdr = phdr_table;
@@ -391,7 +391,7 @@ void phdr_table_get_dynamic_section(const ELF::Phdr* phdr_table,
continue;
}
- *dynamic = reinterpret_cast<ELF::Dyn*>(load_bias + phdr->p_vaddr);
+ *dynamic = reinterpret_cast<const ELF::Dyn*>(load_bias + phdr->p_vaddr);
if (dynamic_count) {
*dynamic_count = (unsigned)(phdr->p_memsz / sizeof(ELF::Dyn));
}
diff --git a/sources/android/crazy_linker/src/linker_phdr.h b/sources/android/crazy_linker/src/linker_phdr.h
index 9ff02c7d4..ca554bf7a 100644
--- a/sources/android/crazy_linker/src/linker_phdr.h
+++ b/sources/android/crazy_linker/src/linker_phdr.h
@@ -75,7 +75,7 @@ int phdr_table_get_arm_exidx(const ELF::Phdr* phdr_table,
void phdr_table_get_dynamic_section(const ELF::Phdr* phdr_table,
int phdr_count,
ELF::Addr load_bias,
- ELF::Dyn** dynamic,
+ const ELF::Dyn** dynamic,
size_t* dynamic_count,
ELF::Word* dynamic_flags);
diff --git a/sources/android/crazy_linker/tests/Android.mk b/sources/android/crazy_linker/tests/Android.mk
index 25d360391..7dbc758a8 100644
--- a/sources/android/crazy_linker/tests/Android.mk
+++ b/sources/android/crazy_linker/tests/Android.mk
@@ -79,14 +79,20 @@ LOCAL_STATIC_LIBRARIES := crazy_linker
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
-LOCAL_MODULE := test_relro_sharing
-LOCAL_SRC_FILES := test_relro_sharing.cpp
+LOCAL_MODULE := test_shared_relro
+LOCAL_SRC_FILES := test_shared_relro.cpp
LOCAL_STATIC_LIBRARIES := crazy_linker
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
-LOCAL_MODULE := test_relro_sharing_two_libs
-LOCAL_SRC_FILES := test_relro_sharing_two_libs.cpp
+LOCAL_MODULE := test_relocated_shared_relro
+LOCAL_SRC_FILES := test_relocated_shared_relro.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_two_shared_relros
+LOCAL_SRC_FILES := test_two_shared_relros.cpp
LOCAL_STATIC_LIBRARIES := crazy_linker
include $(BUILD_EXECUTABLE)
diff --git a/sources/android/crazy_linker/tests/test_relocated_shared_relro.cpp b/sources/android/crazy_linker/tests/test_relocated_shared_relro.cpp
new file mode 100644
index 000000000..14e130f1e
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_relocated_shared_relro.cpp
@@ -0,0 +1,104 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+#define PARENT_ADDRESS 0x20000000
+#define CHILD_ADDRESS 0x20134000
+
+typedef void (*FunctionPtr)();
+
+int main() {
+
+ if (!crazy_system_can_share_relro()) {
+ fprintf(stderr, "WARNING: Test ignored due to broken kernel!!\n");
+ return 0;
+ }
+
+ crazy_context_t* context = crazy_context_create();
+
+ RelroLibrary foo;
+
+ int pipes[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes) < 0)
+ Panic("Could not create socket pair: %s", strerror(errno));
+
+ pid_t child = fork();
+ if (child < 0)
+ Panic("Could not fork test program!");
+
+ if (child == 0) {
+ // In the child.
+ crazy_context_set_load_address(context, CHILD_ADDRESS);
+ foo.Init("libfoo_with_relro.so", context);
+
+ printf("Child waiting for foo relro fd\n");
+
+ foo.ReceiveRelroInfo(pipes[0]);
+ foo.UseSharedRelro(context);
+
+ printf("RELRO used in child process\n");
+
+ CheckRelroMaps(1);
+
+ FunctionPtr foo_func;
+ if (!crazy_library_find_symbol(
+ foo.library, "Foo", reinterpret_cast<void**>(&foo_func)))
+ Panic("Could not find 'Foo' in library");
+
+ printf("Calling Foo()\n");
+ (*foo_func)();
+
+ printf("Foo called, exiting\n");
+
+ exit(0);
+
+ } else {
+ // In the parent.
+
+ // Load at fixed address to simplify testing.
+ crazy_context_set_load_address(context, PARENT_ADDRESS);
+ foo.Init("libfoo_with_relro.so", context);
+
+ printf("Library loaded\n");
+
+ printf("Parent enabling foo RELRO sharing\n");
+
+ foo.CreateSharedRelro(context, CHILD_ADDRESS);
+ foo.SendRelroInfo(pipes[1]);
+
+ printf("Relocated RELRO sent to child\n");
+
+ CheckRelroMaps(0);
+
+ printf("Parent waiting for child\n");
+
+ // Wait for child to complete.
+ int status;
+ waitpid(child, &status, 0);
+
+ if (WIFSIGNALED(status))
+ Panic("Child terminated by signal!!\n");
+ else if (WIFEXITED(status)) {
+ int child_status = WEXITSTATUS(status);
+ if (child_status != 0)
+ Panic("Child terminated with status=%d\n", child_status);
+ } else
+ Panic("Child exited for unknown reason!!\n");
+ }
+
+ crazy_context_destroy(context);
+ return 0;
+}
diff --git a/sources/android/crazy_linker/tests/test_relro_sharing.cpp b/sources/android/crazy_linker/tests/test_relro_sharing.cpp
deleted file mode 100644
index aebe7233b..000000000
--- a/sources/android/crazy_linker/tests/test_relro_sharing.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// A crazy linker test to:
-// - Load a library (libfoo.so) with the linker.
-// - Find the address of the "Foo" function in it.
-// - Call the function.
-// - Close the library.
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <crazy_linker.h>
-
-#include "test_util.h"
-
-typedef void (*FunctionPtr)();
-
-int main() {
-
- if (!crazy_system_can_share_relro()) {
- fprintf(stderr, "WARNING: Test ignored due to broken kernel!!\n");
- return 0;
- }
-
- crazy_context_t* context = crazy_context_create();
- crazy_library_t* library;
-
- // Load at fixed address to simplify testing.
- crazy_context_set_load_address(context, 0x20000000);
-
- // Load libfoo_with_relro.so
- if (!crazy_library_open(&library, "libfoo_with_relro.so", context)) {
- Panic("Could not open library: %s\n", crazy_context_get_error(context));
- }
-
- printf("Library loaded\n");
-
- int pipes[2];
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes) < 0)
- Panic("Could not create socket pair: %s", strerror(errno));
-
- pid_t child = fork();
- if (child < 0)
- Panic("Could not fork test program!");
-
- if (child == 0) {
- // In the child.
- crazy_library_info_t info;
-
- printf("Child waiting for relro fd\n");
- // Receive relro information from parent.
- int relro_fd = -1;
- if (ReceiveFd(pipes[0], &relro_fd) < 0)
- Panic("Could not receive relro descriptor from parent");
-
- printf("Child received relro fd %d\n", relro_fd);
-
- int ret = TEMP_FAILURE_RETRY(::read(pipes[0], &info, sizeof(info)));
- if (ret != static_cast<int>(sizeof(info)))
- Panic("Could not receive relro information from parent");
-
- info.relro_fd = relro_fd;
- printf("Child received relro load=%p start=%p size=%p\n",
- (void*)info.load_address,
- (void*)info.relro_start,
- (void*)info.relro_size);
-
- if (!crazy_library_use_relro_sharing(library,
- info.relro_start,
- info.relro_size,
- info.relro_fd,
- context)) {
- Panic("Could not use RELRO sharing: %s",
- crazy_context_get_error(context));
- }
-
- printf("RELRO used in child process\n");
-
- CheckRelroMaps(1);
-
- FunctionPtr foo_func;
- if (!crazy_library_find_symbol(
- library, "Foo", reinterpret_cast<void**>(&foo_func)))
- Panic("Could not find 'Foo' in library");
-
- printf("Calling Foo()\n");
- (*foo_func)();
-
- printf("Foo called, exiting\n");
-
- exit(0);
-
- } else {
- // In the parent.
- crazy_library_info_t info;
-
- printf("Parent enabling RELRO sharing\n");
-
- // Enable RELRO sharing.
- if (!crazy_library_enable_relro_sharing(library, context))
- Panic("Could not enable RELRO sharing: %s",
- crazy_context_get_error(context));
-
- if (!crazy_library_get_info(library, context, &info))
- Panic("Could not get library info: %s", crazy_context_get_error(context));
-
- printf(
- "Parent relro info load_addr=%p load_size=%p relro_start=%p "
- "relro_size=%p relro_fd=%d\n",
- (void*)info.load_address,
- (void*)info.load_size,
- (void*)info.relro_start,
- (void*)info.relro_size,
- info.relro_fd);
-
- CheckRelroMaps(1);
-
- if (SendFd(pipes[1], info.relro_fd) < 0)
- Panic("Parent could not send RELRO fd: %s", strerror(errno));
-
- int ret = TEMP_FAILURE_RETRY(::write(pipes[1], &info, sizeof(info)));
- if (ret != static_cast<int>(sizeof(info)))
- Panic("Parent could not send RELRO info: %s", strerror(errno));
-
- printf("Parent waiting for child\n");
-
- // Wait for child to complete.
- int status;
- waitpid(child, &status, 0);
-
- if (WIFSIGNALED(status))
- Panic("Child terminated by signal!!\n");
- else if (WIFEXITED(status)) {
- int child_status = WEXITSTATUS(status);
- if (child_status != 0)
- Panic("Child terminated with status=%d\n", child_status);
- } else
- Panic("Child exited for unknown reason!!\n");
- }
-
- printf("Closing library\n");
- crazy_library_close(library);
-
- crazy_context_destroy(context);
- return 0;
-}
diff --git a/sources/android/crazy_linker/tests/test_shared_relro.cpp b/sources/android/crazy_linker/tests/test_shared_relro.cpp
new file mode 100644
index 000000000..d9eace55c
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_shared_relro.cpp
@@ -0,0 +1,104 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A crazy linker test to:
+// - Load a library (libfoo.so) with the linker.
+// - Find the address of the "Foo" function in it.
+// - Call the function.
+// - Close the library.
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+int main() {
+
+ if (!crazy_system_can_share_relro()) {
+ fprintf(stderr, "WARNING: Test ignored due to broken kernel!!\n");
+ return 0;
+ }
+
+ crazy_context_t* context = crazy_context_create();
+
+ RelroLibrary foo;
+
+ // Load at fixed address to simplify testing.
+ crazy_context_set_load_address(context, 0x20000000);
+ foo.Init("libfoo_with_relro.so", context);
+
+ printf("Library loaded\n");
+
+ int pipes[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes) < 0)
+ Panic("Could not create socket pair: %s", strerror(errno));
+
+ pid_t child = fork();
+ if (child < 0)
+ Panic("Could not fork test program!");
+
+ if (child == 0) {
+ // In the child.
+ printf("Child waiting for foo relro fd\n");
+
+ foo.ReceiveRelroInfo(pipes[0]);
+ foo.UseSharedRelro(context);
+
+ printf("RELRO used in child process\n");
+
+ CheckRelroMaps(1);
+
+ FunctionPtr foo_func;
+ if (!crazy_library_find_symbol(
+ foo.library, "Foo", reinterpret_cast<void**>(&foo_func)))
+ Panic("Could not find 'Foo' in library");
+
+ printf("Calling Foo()\n");
+ (*foo_func)();
+
+ printf("Foo called, exiting\n");
+
+ exit(0);
+
+ } else {
+ // In the parent.
+
+ printf("Parent enabling foo RELRO sharing\n");
+
+ foo.EnableSharedRelro(context);
+ foo.SendRelroInfo(pipes[1]);
+
+ printf("RELRO enabled and sent to child\n");
+
+ CheckRelroMaps(1);
+
+ printf("Parent waiting for child\n");
+
+ // Wait for child to complete.
+ int status;
+ waitpid(child, &status, 0);
+
+ if (WIFSIGNALED(status))
+ Panic("Child terminated by signal!!\n");
+ else if (WIFEXITED(status)) {
+ int child_status = WEXITSTATUS(status);
+ if (child_status != 0)
+ Panic("Child terminated with status=%d\n", child_status);
+ } else
+ Panic("Child exited for unknown reason!!\n");
+ }
+
+ crazy_context_destroy(context);
+ return 0;
+}
diff --git a/sources/android/crazy_linker/tests/test_relro_sharing_two_libs.cpp b/sources/android/crazy_linker/tests/test_two_shared_relros.cpp
index 19aa70e0f..9af0eee6a 100644
--- a/sources/android/crazy_linker/tests/test_relro_sharing_two_libs.cpp
+++ b/sources/android/crazy_linker/tests/test_two_shared_relros.cpp
@@ -22,92 +22,6 @@
typedef void (*FunctionPtr)();
-struct Library {
- const char* name;
- crazy_library_t* library;
- crazy_library_info_t info;
-
- void Init(const char* name, crazy_context_t* context) {
- printf("Loading %s\n", name);
- this->name = name;
- if (!crazy_library_open(&this->library, name, context)) {
- Panic("Could not open %s: %s\n", name, crazy_context_get_error(context));
- }
- }
-
- void Close() { crazy_library_close(this->library); }
-
- void EnableSharedRelro(crazy_context_t* context) {
- if (!crazy_library_enable_relro_sharing(this->library, context)) {
- Panic("Could not enable %s RELRO sharing: %s",
- this->name,
- crazy_context_get_error(context));
- }
-
- if (!crazy_library_get_info(this->library, context, &this->info))
- Panic("Could not get %s library info: %s",
- this->name,
- crazy_context_get_error(context));
-
- printf(
- "Parent %s relro info load_addr=%p load_size=%p"
- " relro_start=%p relro_size=%p relro_fd=%d\n",
- this->name,
- (void*)this->info.load_address,
- (void*)this->info.load_size,
- (void*)this->info.relro_start,
- (void*)this->info.relro_size,
- this->info.relro_fd);
- }
-
- void SendRelroInfo(int fd) {
- if (SendFd(fd, this->info.relro_fd) < 0) {
- Panic("Could not send %s RELRO fd: %s", this->name, strerror(errno));
- }
-
- int ret = TEMP_FAILURE_RETRY(::write(fd, &this->info, sizeof(this->info)));
- if (ret != static_cast<int>(sizeof(this->info))) {
- Panic("Parent could not send %s RELRO info: %s",
- this->name,
- strerror(errno));
- }
- }
-
- void ReceiveRelroInfo(int fd) {
- // Receive relro information from parent.
- int relro_fd = -1;
- if (ReceiveFd(fd, &relro_fd) < 0) {
- Panic("Could not receive %s relro descriptor from parent", this->name);
- }
-
- printf("Child received %s relro fd %d\n", this->name, relro_fd);
-
- int ret = TEMP_FAILURE_RETRY(::read(fd, &this->info, sizeof(this->info)));
- if (ret != static_cast<int>(sizeof(this->info))) {
- Panic("Could not receive %s relro information from parent", this->name);
- }
-
- this->info.relro_fd = relro_fd;
- printf("Child received %s relro load=%p start=%p size=%p\n",
- this->name,
- (void*)this->info.load_address,
- (void*)this->info.relro_start,
- (void*)this->info.relro_size);
- }
-
- void UseSharedRelro(crazy_context_t* context) {
- if (!crazy_library_use_relro_sharing(this->library,
- this->info.relro_start,
- this->info.relro_size,
- this->info.relro_fd,
- context)) {
- Panic("Could not use %s shared RELRO: %s\n",
- this->name,
- crazy_context_get_error(context));
- }
- }
-};
-
int main() {
if (!crazy_system_can_share_relro()) {
@@ -117,8 +31,8 @@ int main() {
crazy_context_t* context = crazy_context_create();
- Library foo;
- Library bar;
+ RelroLibrary foo;
+ RelroLibrary bar;
crazy_context_add_search_path_for_address(context, (void*)&main);
diff --git a/sources/android/crazy_linker/tests/test_util.h b/sources/android/crazy_linker/tests/test_util.h
index c2367522d..0fef81341 100644
--- a/sources/android/crazy_linker/tests/test_util.h
+++ b/sources/android/crazy_linker/tests/test_util.h
@@ -22,6 +22,8 @@
#include <sys/uio.h>
#include <unistd.h>
+#include <crazy_linker.h>
+
namespace {
// Print an error message and exit the process.
@@ -365,6 +367,99 @@ inline void CheckRelroMaps(int expected_count) {
printf("RELRO count check ok!\n");
}
+struct RelroInfo {
+ size_t start;
+ size_t size;
+ int fd;
+};
+
+struct RelroLibrary {
+ const char* name;
+ crazy_library_t* library;
+ RelroInfo relro;
+
+ void Init(const char* name, crazy_context_t* context) {
+ printf("Loading %s\n", name);
+ this->name = name;
+ if (!crazy_library_open(&this->library, name, context)) {
+ Panic("Could not open %s: %s\n", name, crazy_context_get_error(context));
+ }
+ }
+
+ void Close() { crazy_library_close(this->library); }
+
+ void CreateSharedRelro(crazy_context_t* context, size_t load_address) {
+ if (!crazy_library_create_shared_relro(this->library,
+ context,
+ load_address,
+ &this->relro.start,
+ &this->relro.size,
+ &this->relro.fd)) {
+ Panic("Could not create shared RELRO for %s: %s",
+ this->name,
+ crazy_context_get_error(context));
+ }
+
+ printf(
+ "Parent %s relro info relro_start=%p relro_size=%p relro_fd=%d\n",
+ this->name,
+ (void*)this->relro.start,
+ (void*)this->relro.size,
+ this->relro.fd);
+ }
+
+ void EnableSharedRelro(crazy_context_t* context) {
+ CreateSharedRelro(context, 0);
+ UseSharedRelro(context);
+ }
+
+ void SendRelroInfo(int fd) {
+ if (SendFd(fd, this->relro.fd) < 0) {
+ Panic("Could not send %s RELRO fd: %s", this->name, strerror(errno));
+ }
+
+ int ret = TEMP_FAILURE_RETRY(::write(fd, &this->relro, sizeof(this->relro)));
+ if (ret != static_cast<int>(sizeof(this->relro))) {
+ Panic("Parent could not send %s RELRO info: %s",
+ this->name,
+ strerror(errno));
+ }
+ }
+
+ void ReceiveRelroInfo(int fd) {
+ // Receive relro information from parent.
+ int relro_fd = -1;
+ if (ReceiveFd(fd, &relro_fd) < 0) {
+ Panic("Could not receive %s relro descriptor from parent", this->name);
+ }
+
+ printf("Child received %s relro fd %d\n", this->name, relro_fd);
+
+ int ret = TEMP_FAILURE_RETRY(::read(fd, &this->relro, sizeof(this->relro)));
+ if (ret != static_cast<int>(sizeof(this->relro))) {
+ Panic("Could not receive %s relro information from parent", this->name);
+ }
+
+ this->relro.fd = relro_fd;
+ printf("Child received %s relro start=%p size=%p\n",
+ this->name,
+ (void*)this->relro.start,
+ (void*)this->relro.size);
+ }
+
+ void UseSharedRelro(crazy_context_t* context) {
+ if (!crazy_library_use_shared_relro(this->library,
+ context,
+ this->relro.start,
+ this->relro.size,
+ this->relro.fd)) {
+ Panic("Could not use %s shared RELRO: %s\n",
+ this->name,
+ crazy_context_get_error(context));
+ }
+ }
+};
+
} // namespace
#endif // TEST_UTIL_H