Commit ecd5d8b5 authored by David 'Digit' Turner's avatar David 'Digit' Turner Committed by Commit Bot

crazy-linker: Add android_dlopen_ext() wrapper.

This is the last CL required to implement proper wrapping
of android_dlopen_ext() with the crazy linker.

R=pasko@chromium.org, rmcilroy@chromium.org, agrieve@chromium.org, cjgrant@chromium.org, tiborg@chromium.org

BUG=936001

Change-Id: I89070de677de3bf3ec590c2afcb1284bb607a81e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1609838
Commit-Queue: David Turner <digit@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarChristopher Grant <cjgrant@chromium.org>
Reviewed-by: default avatarEgor Pasko <pasko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#659420}
parent aab278dc
......@@ -29,6 +29,7 @@ if (is_android) {
":crazy_linker_test_dl_wrappers",
":crazy_linker_test_dl_wrappers_recursive",
":crazy_linker_test_dl_wrappers_valid_handles",
":crazy_linker_test_dl_wrappers_with_android_dlopen_ext",
":crazy_linker_test_dl_wrappers_with_system_handle",
":crazy_linker_test_jni_hooks",
":crazy_linker_test_load_library",
......@@ -312,6 +313,14 @@ if (is_android) {
]
}
crazy_linker_test_library(
"crazy_linker_tests_libzoo_with_android_dlopen_ext") {
sources = [
"src/tests/zoo_with_android_dlopen_ext.cpp",
]
libs = [ "dl" ]
}
crazy_linker_test_library("crazy_linker_tests_libzoo_with_dlopen_handle") {
sources = [
"src/tests/zoo_with_dlopen_handle.cpp",
......@@ -504,6 +513,18 @@ if (is_android) {
]
}
executable("crazy_linker_test_dl_wrappers_with_android_dlopen_ext") {
sources = [
"src/tests/test_dl_wrappers_with_android_dlopen_ext.cpp",
]
data_deps = [
":crazy_linker_tests_libzoo_with_android_dlopen_ext",
]
deps = [
":android_crazy_linker",
]
}
executable("crazy_linker_test_dl_wrappers_valid_handles") {
sources = [
"src/tests/test_dl_wrappers_valid_handles.cpp",
......
......@@ -24,6 +24,8 @@ run () {
fi
}
ADB=${ADB:-adb}
# Run a command through adb shell, strip the extra \r from the output
# and return the correct status code to detect failures. This assumes
# that the adb shell command prints a final \n to stdout.
......@@ -34,7 +36,6 @@ run () {
adb_shell () {
local TMPOUT="$(mktemp)"
local LASTLINE RET
local ADB=${ADB:-adb}
# The weird sed rule is to strip the final \r on each output line
# Since 'adb shell' never returns the command's proper exit/status code,
......@@ -156,6 +157,7 @@ libcrazy_linker_tests_libzoo.so \
libcrazy_linker_tests_libzoo_dlopen_in_initializer.so \
libcrazy_linker_tests_libzoo_dlopen_in_initializer_inner.so \
libcrazy_linker_tests_libzoo_with_dlopen_handle.so \
libcrazy_linker_tests_libzoo_with_android_dlopen_ext.so \
"
TEST_FILES="\
......@@ -163,8 +165,9 @@ crazy_linker_bench_load_library \
crazy_linker_test_constructors_destructors \
crazy_linker_test_dl_wrappers \
crazy_linker_test_dl_wrappers_recursive \
crazy_linker_test_dl_wrappers_with_system_handle \
crazy_linker_test_dl_wrappers_valid_handles \
crazy_linker_test_dl_wrappers_with_android_dlopen_ext \
crazy_linker_test_dl_wrappers_with_system_handle \
crazy_linker_test_jni_hooks \
crazy_linker_test_load_library \
crazy_linker_test_load_library_depends \
......@@ -199,7 +202,14 @@ fi
run_test () {
local TEST_NAME=$1
shift
run adb_shell LD_LIBRARY_PATH=$RUN_DIR $RUN_DIR/$TEST_NAME "$@"
if [ "$VERBOSE" -ge 1 ]; then
# Using adb_shell doesn't print stderr, but it gives a status code.
# Consider that this is lesser important when debugging the test execution
# and run "adb shell" from the command-line instead.
echo "cd $RUN_DIR && LD_LIBRARY_PATH=. ./$TEST_NAME $@" | "$ADB" shell
else
run adb_shell "cd $RUN_DIR && LD_LIBRARY_PATH=. ./$TEST_NAME $@"
fi
}
if [ -n "$DO_UNIT_TESTS" ]; then
......
......@@ -107,7 +107,7 @@ InternalElfLoader::~InternalElfLoader() {
bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
const char* lib_path = params.library_path.c_str();
LOG("lib_path='%s', file_fd=%d, file_offset=%p, load_address=%lx"
LOG("lib_path='%s', file_fd=%d, file_offset=%p, load_address=%lx "
"reserved_size=%lx reserved_load_fallback=%s",
lib_path, params.library_fd, params.library_offset,
static_cast<unsigned long>(params.wanted_address),
......@@ -165,26 +165,8 @@ bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
path_ = lib_path;
if (!ReadElfHeader(error) || !ReadProgramHeader(error) ||
!ReserveAddressSpace(params, error)) {
return false;
}
bool success = LoadSegments(error);
if (!success) {
// If loading the segments fail, but |reserved_size| was not 0, and
// |reserved_size_load_fallback| is true, then try again at a not-fixed
// address.
if (params.reserved_size > 0 && params.reserved_load_fallback) {
LOG("Loading to reserved mapping failed, falling back to random one");
LoadParams params2 = params;
params2.wanted_address = 0;
params2.reserved_size = 0;
params2.reserved_load_fallback = false;
success = ReserveAddressSpace(params2, error) && LoadSegments(error);
}
}
success = success && FindPhdr(error);
if (!success) {
!ReserveAddressSpace(params, error) || !LoadSegments(error) ||
!FindPhdr(error)) {
reserved_map_.Deallocate();
return false;
}
......@@ -297,6 +279,16 @@ bool InternalElfLoader::ReserveAddressSpace(const LoadParams& params,
void* start = reinterpret_cast<void*>(params.wanted_address);
size_t reserved_size = params.reserved_size;
if (reserved_size > 0 && reserved_size < load_size_ &&
params.reserved_load_fallback) {
LOG("Reserved size is too small (%ld < %ld), allocating new mapping!",
static_cast<unsigned long>(reserved_size),
static_cast<unsigned long>(load_size_));
reserved_size = 0;
addr = nullptr;
mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
}
if (!reserved_size) {
// Reserve the area ourselves.
reserved_size = load_size_;
......
......@@ -10,6 +10,13 @@
namespace crazy {
// <android/dlext.h> does not declare android_dlopen_ext() if __ANDROID_API__
// is smaller than 21, so declare it here as a weak function. This will allow
// detecting its availability at runtime. For API level 21 or higher, the
// attribute is ignored due to the previous declaration.
extern "C" void* android_dlopen_ext(const char*, int, const android_dlextinfo*)
__attribute__((weak));
// static
void* SystemLinker::Open(const char* path, int mode) {
// NOTE: The system linker will likely modify the global _r_debug link map
......@@ -19,6 +26,27 @@ void* SystemLinker::Open(const char* path, int mode) {
return ::dlopen(path, mode);
}
#ifdef __ANDROID__
// static
bool SystemLinker::HasAndroidOpenExt() {
return android_dlopen_ext != nullptr;
}
// static
void* SystemLinker::AndroidOpenExt(const char* path,
int mode,
const android_dlextinfo* info) {
// NOTE: The system linker will likely modify the global _r_debug link map
// so ensure this doesn't conflict with other threads performing delayed
// updates on it.
ScopedLinkMapLocker locker;
if (android_dlopen_ext != nullptr) {
return android_dlopen_ext(path, mode, info);
}
return nullptr;
}
#endif // __ANDROID__
// static
int SystemLinker::Close(void* handle) {
// Similarly, though unlikely, this operation may modify the global link map.
......
......@@ -5,6 +5,10 @@
#ifndef CRAZY_LINKER_SYSTEM_LINKER_H
#define CRAZY_LINKER_SYSTEM_LINKER_H
#ifdef __ANDROID__
#include <android/dlext.h>
#endif
#include <dlfcn.h>
namespace crazy {
......@@ -18,6 +22,17 @@ struct SystemLinker {
// Wrapper for dlopen().
static void* Open(const char* path, int flags);
#ifdef __ANDROID__
// Returns true iff this system linker provides android_dlopen_ext().
static bool HasAndroidOpenExt();
// Calls android_dlopen_ext() if available, returns nullptr if it is not
// available otherwise.
static void* AndroidOpenExt(const char* path,
int flags,
const android_dlextinfo* info);
#endif // __ANDROID__
// Wrapper for dlclose().
static int Close(void* handle);
......
......@@ -15,6 +15,10 @@
#include "crazy_linker_thread_data.h"
#include "crazy_linker_util.h"
#ifdef __ANDROID__
#include <android/dlext.h>
#endif
#ifdef __arm__
// On ARM, this function is exported by the dynamic linker but never
// declared in any official header. It is used at runtime to
......@@ -95,9 +99,9 @@ void* WrapDlopen(const char* path, int mode) {
// corresponding to the current executable. This can't be a crazy
// library, so don't try to handle it with the crazy linker.
if (path) {
Error error;
LibraryView* view = libs->FindKnownLibrary(path);
if (!view) {
Error error;
LoadParams params;
if (libs->LocateLibraryFile(path, *globals->search_path_list(), &params,
&error)) {
......@@ -125,6 +129,64 @@ void* WrapDlopen(const char* path, int mode) {
return view;
}
#ifdef __ANDROID__
// Prepare LoadParams according to |path| and |info|.
static LoadParams PrepareLoadParamsFrom(const char* path,
const android_dlextinfo* info) {
LoadParams params;
if (info->flags & ANDROID_DLEXT_USE_LIBRARY_FD) {
params.library_fd = info->library_fd;
if (info->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET)
params.library_offset = info->library_fd_offset;
} else {
params.library_path = path;
}
if (info->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {
params.wanted_address = reinterpret_cast<uintptr_t>(info->reserved_addr);
params.reserved_size = info->reserved_size;
}
if (info->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT)
params.reserved_load_fallback = true;
return params;
}
void* WrapAndroidDlopenExt(const char* path,
int mode,
const android_dlextinfo* info) {
if (!info)
return WrapDlopen(path, mode);
ScopedLockedGlobals globals;
const uint64_t kSupportedFlags =
ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET |
ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT;
const uint64_t unsupported_flags = (info->flags & ~kSupportedFlags);
if (unsupported_flags) {
SetLinkerError("%s", "unsupported android_dlextinfo flags %08llx",
"android_dlopen_ext",
static_cast<unsigned long long>(unsupported_flags));
return nullptr;
}
if (!path && !(info->flags & ANDROID_DLEXT_USE_LIBRARY_FD)) {
SetLinkerError("%s: missing path or file descriptor.",
"android_dlopen_ext");
return nullptr;
}
Error error;
LibraryView* view = globals->libraries()->LoadLibraryInternal(
PrepareLoadParamsFrom(path, info), &error);
if (!view) {
SetLinkerError("%s: %s", "android_dlopen_ext", error.c_str());
return nullptr;
}
globals->valid_handles()->Add(view);
return view;
}
#endif // __ANDROID__
void* WrapDlsym(void* lib_handle, const char* symbol_name) {
if (!symbol_name) {
SetLinkerError("dlsym: NULL symbol name");
......@@ -313,6 +375,10 @@ void* WrapLinkerSymbol(const char* name) {
#ifdef __arm__
if (name[0] == '_' && !strcmp("__aeabi_atexit", name))
return reinterpret_cast<void*>(&__aeabi_atexit);
#endif
#ifdef __ANDROID__
if (name[0] == 'a' && !strcmp("android_dlopen_ext", name))
return reinterpret_cast<void*>(&WrapAndroidDlopenExt);
#endif
return NULL;
}
......
// Copyright 2019 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 (LIB_NAME) with the linker.
// - Find the address of the "OpenExtFindRunClose" function in LIB_NAME.
// - Call the OpenExtFindRunClose() function, which will use
// android_dlopen_ext() /
// dlsym() to find libbar.so (which depends on libfoo.so).
// - Close the library.
// This tests the android_dlopen_ext/dlsym/dlclose wrappers provided by the
// crazy linker to loaded libraries.
#include <crazy_linker.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#ifdef __ANDROID__
#include <android/dlext.h>
#else
#error "This source file only compiles for Android!"
#endif
#include "test_util.h"
typedef bool (*OpenExtFindRunCloseFunctionPtr)(const char* lib_name,
const char* func_name,
const android_dlextinfo* info);
#define LIB_NAME "libcrazy_linker_tests_libzoo_with_android_dlopen_ext.so"
#define FUNC_NAME "OpenExtFindRunClose"
#define LIB2_NAME "libcrazy_linker_tests_libbar.so"
#define FUNC2_NAME "Bar"
int main() {
crazy_context_t* context = crazy_context_create();
crazy_library_t* library;
// Load LIB_NAME
if (!crazy_library_open(&library, LIB_NAME, context)) {
Panic("Could not open library: %s\n", crazy_context_get_error(context));
}
// Find the "OpenExtFindRunClose" symbol from LIB_NAME
OpenExtFindRunCloseFunctionPtr lib_func;
if (!crazy_library_find_symbol(library, "OpenExtFindRunClose",
reinterpret_cast<void**>(&lib_func))) {
Panic("Could not find '" FUNC_NAME "' in " LIB_NAME "\n");
}
bool ret;
// Call it, without dlext info.
printf("////////////////////////// FIRST CALL WITHOUT DLEXT INFO\n");
ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, nullptr);
if (!ret)
Panic("'" FUNC_NAME "' function failed!");
// Call it again, this time load the library from a file descriptor.
printf("////////////////////////// SECOND CALL WITH LIBRARY FD\n");
{
android_dlextinfo info = {};
info.flags = ANDROID_DLEXT_USE_LIBRARY_FD;
info.library_fd = ::open(LIB2_NAME, O_RDONLY | O_CLOEXEC);
if (info.library_fd < 0)
PanicErrno("Could not open library file directly");
ret = (*lib_func)(nullptr, FUNC2_NAME, &info);
if (!ret)
Panic("'" FUNC_NAME "' function failed with file descriptor!");
close(info.library_fd);
}
fflush(stdout);
printf("////////////////////////// THIRD CALL WITH RESERVED MAP\n");
{
android_dlextinfo info = {};
info.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
info.reserved_size = 64 * 4096;
info.reserved_addr = ::mmap(nullptr, info.reserved_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (info.reserved_addr == MAP_FAILED)
PanicErrno("Could not reserve address map region");
ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, &info);
if (!ret)
Panic("'" FUNC_NAME "' function failed with reserved map!");
::munmap(info.reserved_addr, info.reserved_size);
}
fflush(stdout);
printf("////////////////////////// FOURTH CALL WITH TINY RESERVED MAP\n");
{
android_dlextinfo info = {};
info.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
info.reserved_size = 1 * 4096;
info.reserved_addr = ::mmap(nullptr, info.reserved_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (info.reserved_addr == MAP_FAILED)
PanicErrno("Could not reserve address map region");
ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, &info);
if (ret)
Panic("'" FUNC_NAME "' function succeeded with tiny reserved map!");
::munmap(info.reserved_addr, info.reserved_size);
}
fflush(stdout);
printf("////////////////////////// FIFTH CALL WITH RESERVED MAP + HINT\n");
{
android_dlextinfo info = {};
info.flags =
ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT;
info.reserved_size = 1 * 4096;
info.reserved_addr = ::mmap(nullptr, info.reserved_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (info.reserved_addr == MAP_FAILED)
PanicErrno("Could not reserve address map region");
ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, &info);
if (!ret)
Panic("'" FUNC_NAME "' failed with tiny reserved map and hint!");
::munmap(info.reserved_addr, info.reserved_size);
}
fflush(stdout);
printf("//////////////////////////\n");
// Close the library.
printf("Closing " LIB_NAME "\n");
crazy_library_close(library);
crazy_context_destroy(context);
printf("OK\n");
return 0;
}
// Copyright 2019 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 <dlfcn.h>
#include <stdio.h>
// This source file is used to exercise the dlopen()/dlsym() wrappers
// inside a library that was loaded by the crazy linker. It should be compiled
// into a standalone library (e.g. libzoo.so) that is loaded with the crazy
// linker.
#ifdef __ANDROID__
#include <android/dlext.h>
// <android/dlext.h> does not declare android_dlopen_ext() if __ANDROID_API__
// is smaller than 21, so declare it here as a weak function. This will allow
// detecting its availability at runtime. For API level 21 or higher, the
// attribute is ignored due to the previous declaration.
// NOTE: The crazy linker always provides android_dlopen_ext(), even on older
// platforms, this is required to compile this source, and will allow
// us to check that the crazy linker resolves weak symbol imports
// to itself properly.
extern "C" void* android_dlopen_ext(const char*, int, const android_dlextinfo*)
__attribute__((weak));
#else
#error "This source file can only build for Android."
#endif
// Try to load library |lib_name| with android_dlopen_ext(), then locate
// function |func_name| in it to run it, then close the library.
// Return true in case of success, false otherwise.
extern "C" bool OpenExtFindRunClose(const char* lib_name,
const char* func_name,
const android_dlextinfo* android_info) {
printf("%s: Entering\n", __FUNCTION__);
if (!android_dlopen_ext) {
fprintf(stderr, "Cannot find android_dlopen_ext symbol!");
return false;
}
void* lib_handle = android_dlopen_ext(lib_name, RTLD_NOW, android_info);
if (!lib_handle) {
fprintf(stderr, "Could not open %s: %s\n", lib_name ? lib_name : "(NULL)",
dlerror());
return false;
}
if (!lib_name)
lib_name = "(FROM_FD)";
printf("%s: Opened %s @%p\n", __FUNCTION__, lib_name, lib_handle);
auto* func_ptr = reinterpret_cast<void (*)()>(dlsym(lib_handle, func_name));
if (!func_ptr) {
fprintf(stderr, "Could not find 'Bar' symbol in %s: %s\n", lib_name,
dlerror());
return false;
}
printf("%s: Found '%s' symbol at @%p\n", __FUNCTION__, func_name, func_ptr);
// Call it.
printf("%s: Calling %s\n", __FUNCTION__, func_name);
(*func_ptr)();
printf("%s: Closing %s\n", __FUNCTION__, lib_name);
int ret = dlclose(lib_handle);
if (ret != 0) {
printf("ERROR: Failed to close library: %s\n", dlerror());
return false;
}
printf("%s: Exiting\n", __FUNCTION__);
return true;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment