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

android: crazy-linker: Load from file descriptor and reserved mappings.

This CL extends the LoadParams struct to allow loading a library from
an existing file descriptor and/or a reserved memory mapping as well.

This will be used later to implement a wrapper for android_dlopen_ext()
that support ANDROID_DLEXT_{USE_LIBRARY_FD, USE_LIBRARY_FD_OFFSET,
RESERVED_ADDRESS, RESERVED_ADDRESS_HINT}.

+ Provide new tests to verify that the feature works properly.
+ Fix FileDescriptor move assignment operator for 64-bit unit tests.

BUG=936001
R=pasko@chromium.org, agrieve@chromium.org, cjgrant@chromium.org

Change-Id: I1ed174dfa268f780c5397f6e76c659f8fa57c935
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1564111
Commit-Queue: David Turner <digit@chromium.org>
Reviewed-by: default avatarEgor Pasko <pasko@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#658519}
parent 7abbb59b
...@@ -33,8 +33,10 @@ if (is_android) { ...@@ -33,8 +33,10 @@ if (is_android) {
":crazy_linker_test_jni_hooks", ":crazy_linker_test_jni_hooks",
":crazy_linker_test_load_library", ":crazy_linker_test_load_library",
":crazy_linker_test_load_library_depends", ":crazy_linker_test_load_library_depends",
":crazy_linker_test_load_library_with_fd",
":crazy_linker_test_load_library_with_gnu_hash_table", ":crazy_linker_test_load_library_with_gnu_hash_table",
":crazy_linker_test_load_library_with_relr_relocations", ":crazy_linker_test_load_library_with_relr_relocations",
":crazy_linker_test_load_library_with_reserved_map",
":crazy_linker_test_relocated_shared_relro", ":crazy_linker_test_relocated_shared_relro",
":crazy_linker_test_search_path_list", ":crazy_linker_test_search_path_list",
":crazy_linker_test_shared_relro", ":crazy_linker_test_shared_relro",
...@@ -390,6 +392,30 @@ if (is_android) { ...@@ -390,6 +392,30 @@ if (is_android) {
] ]
} }
executable("crazy_linker_test_load_library_with_fd") {
sources = [
"src/tests/test_load_library_with_fd.cpp",
]
data_deps = [
":crazy_linker_tests_libfoo",
]
deps = [
":android_crazy_linker",
]
}
executable("crazy_linker_test_load_library_with_reserved_map") {
sources = [
"src/tests/test_load_library_with_reserved_map.cpp",
]
data_deps = [
":crazy_linker_tests_libfoo",
]
deps = [
":android_crazy_linker",
]
}
executable("crazy_linker_test_load_library_with_relr_relocations") { executable("crazy_linker_test_load_library_with_relr_relocations") {
sources = [ sources = [
"src/tests/test_load_library.cpp", "src/tests/test_load_library.cpp",
......
...@@ -82,6 +82,25 @@ void crazy_context_set_load_address(crazy_context_t* context, ...@@ -82,6 +82,25 @@ void crazy_context_set_load_address(crazy_context_t* context,
// Return the current load address in a context. // Return the current load address in a context.
size_t crazy_context_get_load_address(crazy_context_t* context) _CRAZY_PUBLIC; size_t crazy_context_get_load_address(crazy_context_t* context) _CRAZY_PUBLIC;
// Set the explicit library file descriptor in a context object. Values >= 0
// will be used during the next crazy_library_open() call to read the library
// file, instead of opening it using its path.
void crazy_context_set_library_fd(crazy_context_t* context,
int fd) _CRAZY_PUBLIC;
// Return the current library file descriptor in a context.
int crazy_context_get_library_fd(crazy_context_t* context) _CRAZY_PUBLIC;
// Set an explicit reserved memory mapping to be used on the next library
// load. |reserved_address| is the page-aligned reserved address,
// |reserved_size| is the page-aligned reserved size, and if |load_fallback|
// is true, then the linker will try to load the library at a different
// address if it fails to load its segments at the reserved address range.
void crazy_context_set_reserved_map(crazy_context_t* context,
uintptr_t reserved_address,
size_t reserved_size,
bool load_fallback) _CRAZY_PUBLIC;
// Destroy a given context object. // Destroy a given context object.
void crazy_context_destroy(crazy_context_t* context) _CRAZY_PUBLIC; void crazy_context_destroy(crazy_context_t* context) _CRAZY_PUBLIC;
......
...@@ -168,8 +168,10 @@ crazy_linker_test_dl_wrappers_valid_handles \ ...@@ -168,8 +168,10 @@ crazy_linker_test_dl_wrappers_valid_handles \
crazy_linker_test_jni_hooks \ crazy_linker_test_jni_hooks \
crazy_linker_test_load_library \ crazy_linker_test_load_library \
crazy_linker_test_load_library_depends \ crazy_linker_test_load_library_depends \
crazy_linker_test_load_library_with_fd \
crazy_linker_test_load_library_with_gnu_hash_table \ crazy_linker_test_load_library_with_gnu_hash_table \
crazy_linker_test_load_library_with_relr_relocations \ crazy_linker_test_load_library_with_relr_relocations \
crazy_linker_test_load_library_with_reserved_map \
crazy_linker_test_relocated_shared_relro \ crazy_linker_test_relocated_shared_relro \
crazy_linker_test_search_path_list \ crazy_linker_test_search_path_list \
crazy_linker_test_shared_relro \ crazy_linker_test_shared_relro \
......
...@@ -32,6 +32,9 @@ using crazy::LibraryView; ...@@ -32,6 +32,9 @@ using crazy::LibraryView;
struct crazy_context_t { struct crazy_context_t {
size_t load_address = 0; size_t load_address = 0;
int library_fd = -1;
size_t reserved_size = 0;
bool reserved_load_fallback = false;
Error error; Error error;
}; };
...@@ -64,6 +67,23 @@ size_t crazy_context_get_load_address(crazy_context_t* context) { ...@@ -64,6 +67,23 @@ size_t crazy_context_get_load_address(crazy_context_t* context) {
return context->load_address; return context->load_address;
} }
void crazy_context_set_library_fd(crazy_context_t* context, int fd) {
context->library_fd = fd;
}
int crazy_context_get_library_fd(crazy_context_t* context) {
return context->library_fd;
}
void crazy_context_set_reserved_map(crazy_context_t* context,
uintptr_t reserved_address,
size_t reserved_size,
bool load_fallback) {
context->load_address = reserved_address;
context->reserved_size = reserved_size;
context->reserved_load_fallback = load_fallback;
}
void crazy_context_destroy(crazy_context_t* context) { void crazy_context_destroy(crazy_context_t* context) {
delete context; delete context;
} }
...@@ -112,6 +132,8 @@ crazy_status_t crazy_library_open(crazy_library_t** library, ...@@ -112,6 +132,8 @@ crazy_status_t crazy_library_open(crazy_library_t** library,
crazy::LibraryList* libs = globals->libraries(); crazy::LibraryList* libs = globals->libraries();
crazy::LoadParams params; crazy::LoadParams params;
params.wanted_address = context->load_address; params.wanted_address = context->load_address;
params.reserved_size = context->reserved_size;
params.reserved_load_fallback = context->reserved_load_fallback;
crazy::Expected<LibraryView*> found = crazy::Expected<LibraryView*> found =
libs->FindAndCheckLoadedLibrary(lib_name, params, &context->error); libs->FindAndCheckLoadedLibrary(lib_name, params, &context->error);
if (!found.has_value()) if (!found.has_value())
...@@ -119,11 +141,22 @@ crazy_status_t crazy_library_open(crazy_library_t** library, ...@@ -119,11 +141,22 @@ crazy_status_t crazy_library_open(crazy_library_t** library,
LibraryView* view = found.value(); LibraryView* view = found.value();
if (!view) { if (!view) {
if (!libs->LocateLibraryFile(lib_name, *globals->search_path_list(), if (context->library_fd >= 0) {
&params, &context->error)) { params.library_path = lib_name;
return CRAZY_STATUS_FAILURE; params.library_fd = context->library_fd;
} else {
if (!libs->LocateLibraryFile(lib_name, *globals->search_path_list(),
&params, &context->error)) {
return CRAZY_STATUS_FAILURE;
}
} }
view = libs->LoadLibraryInternal(params, &context->error); view = libs->LoadLibraryInternal(params, &context->error);
// Cleanup context.
context->library_fd = -1;
context->load_address = 0;
context->reserved_size = 0;
context->reserved_load_fallback = false;
} }
if (!view) if (!view)
......
...@@ -23,6 +23,31 @@ namespace crazy { ...@@ -23,6 +23,31 @@ namespace crazy {
namespace { namespace {
// A FileDescriptor sub-class that can also avoid closing the descriptor
// on scope exit if DontCloseOnExit() is called.
class LibraryFd : public FileDescriptor {
public:
LibraryFd() = default;
explicit LibraryFd(int fd) : FileDescriptor(fd) {}
explicit LibraryFd(const char* path) : FileDescriptor(path) {}
~LibraryFd() {
if (!close_on_exit_)
Release();
}
LibraryFd(LibraryFd&& other) = default;
LibraryFd& operator=(LibraryFd&& other) = default;
// Ensure the file descriptor is not closed in the destructor.
void DontCloseOnExit() { close_on_exit_ = false; }
private:
bool close_on_exit_ = true;
};
class InternalElfLoader { class InternalElfLoader {
public: public:
~InternalElfLoader(); ~InternalElfLoader();
...@@ -42,7 +67,7 @@ class InternalElfLoader { ...@@ -42,7 +67,7 @@ class InternalElfLoader {
MemoryMapping ReleaseMapping() { return std::move(reserved_map_); } MemoryMapping ReleaseMapping() { return std::move(reserved_map_); }
private: private:
FileDescriptor fd_; LibraryFd fd_;
const char* path_ = nullptr; const char* path_ = nullptr;
ELF::Ehdr header_ = {}; ELF::Ehdr header_ = {};
...@@ -82,8 +107,12 @@ InternalElfLoader::~InternalElfLoader() { ...@@ -82,8 +107,12 @@ InternalElfLoader::~InternalElfLoader() {
bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) { bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
const char* lib_path = params.library_path.c_str(); const char* lib_path = params.library_path.c_str();
LOG("lib_path='%s', file_offset=%p, load_address=%lx", lib_path, LOG("lib_path='%s', file_fd=%d, file_offset=%p, load_address=%lx"
params.library_offset, static_cast<unsigned long>(params.wanted_address)); "reserved_size=%lx reserved_load_fallback=%s",
lib_path, params.library_fd, params.library_offset,
static_cast<unsigned long>(params.wanted_address),
static_cast<unsigned long>(params.reserved_size),
params.reserved_load_fallback ? "true" : "false");
// Check that the load address is properly page-aligned. // Check that the load address is properly page-aligned.
uintptr_t wanted_address = params.wanted_address; uintptr_t wanted_address = params.wanted_address;
...@@ -92,6 +121,19 @@ bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) { ...@@ -92,6 +121,19 @@ bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
return false; return false;
} }
if (params.reserved_size != 0) {
if (!wanted_address) {
error->Format("Reserved size 0x%08lx has not reserved address",
static_cast<unsigned long>(params.reserved_size));
return false;
}
if (params.reserved_size != PAGE_START(params.reserved_size)) {
error->Format("Reserved size 0x%08lx is not page-aligned",
static_cast<unsigned long>(params.reserved_size));
return false;
}
}
// Check that the file offset is also properly page-aligned. // Check that the file offset is also properly page-aligned.
// PAGE_START() can't be used here due to the compiler complaining about // PAGE_START() can't be used here due to the compiler complaining about
// comparing signed (off_t) and unsigned (size_t) values. // comparing signed (off_t) and unsigned (size_t) values.
...@@ -104,9 +146,14 @@ bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) { ...@@ -104,9 +146,14 @@ bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
file_offset_ = file_offset; file_offset_ = file_offset;
// Open the file. // Open the file.
if (!fd_.OpenReadOnly(lib_path)) { if (params.library_fd >= 0) {
error->Format("Can't open file: %s", strerror(errno)); fd_ = LibraryFd(params.library_fd);
return false; fd_.DontCloseOnExit();
} else {
if (!fd_.OpenReadOnly(lib_path)) {
error->Format("Can't open file: %s", strerror(errno));
return false;
}
} }
if (file_offset && fd_.SeekTo(file_offset) < 0) { if (file_offset && fd_.SeekTo(file_offset) < 0) {
...@@ -122,13 +169,25 @@ bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) { ...@@ -122,13 +169,25 @@ bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
return false; return false;
} }
if (!LoadSegments(error) || !FindPhdr(error)) { bool success = LoadSegments(error);
// An error occured, cleanup the address space by un-mapping the if (!success) {
// range that was reserved by ReserveAddressSpace(). // 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) {
reserved_map_.Deallocate(); reserved_map_.Deallocate();
return false; return false;
} }
return true; return true;
} }
...@@ -235,27 +294,38 @@ bool InternalElfLoader::ReserveAddressSpace(const LoadParams& params, ...@@ -235,27 +294,38 @@ bool InternalElfLoader::ReserveAddressSpace(const LoadParams& params,
mmap_flags |= MAP_FIXED; mmap_flags |= MAP_FIXED;
} }
size_t reserved_size = load_size_; void* start = reinterpret_cast<void*>(params.wanted_address);
size_t reserved_size = params.reserved_size;
LOG("Trying to reserve memory address=%p size=%lu (0x%lx)", addr,
static_cast<unsigned long>(load_size_), if (!reserved_size) {
static_cast<unsigned long>(load_size_)); // Reserve the area ourselves.
reserved_size = load_size_;
void* start = mmap(addr, reserved_size, PROT_NONE, mmap_flags, -1, 0); LOG("Trying to reserve memory address=%p size=%lu (0x%lx)", addr,
if (start == MAP_FAILED) { static_cast<unsigned long>(load_size_),
error->Format("Could not reserve %lu bytes of address space", static_cast<unsigned long>(load_size_));
static_cast<unsigned long>(reserved_size));
return false; start = mmap(addr, reserved_size, PROT_NONE, mmap_flags, -1, 0);
} if (start == MAP_FAILED) {
if (addr && start != addr) { error->Format("Could not reserve %lu bytes of address space",
error->Format("Could not map at %p requested, backing out", addr); static_cast<unsigned long>(reserved_size));
munmap(start, reserved_size); return false;
}
if (addr && start != addr) {
error->Format("Could not map at %p requested, backing out", addr);
munmap(start, reserved_size);
return false;
}
// Take ownership of the mapping here.
reserved_map_ = MemoryMapping(start, reserved_size);
} else if (reserved_size < load_size_) {
error->Format("Reserved map size is too small 0x%lx (0x%lx required)",
static_cast<unsigned long>(reserved_size),
static_cast<unsigned long>(load_size_));
return false; return false;
} else {
LOG("Using client-allocated mapping!");
} }
// Take ownership of the mapping here.
reserved_map_ = MemoryMapping(start, reserved_size);
load_start_ = start; load_start_ = start;
load_bias_ = reinterpret_cast<ELF::Addr>(load_start_) - min_vaddr; load_bias_ = reinterpret_cast<ELF::Addr>(load_start_) - min_vaddr;
......
...@@ -63,6 +63,11 @@ bool CheckSystemLibraryLoadParams(const char* lib_name, ...@@ -63,6 +63,11 @@ bool CheckSystemLibraryLoadParams(const char* lib_name,
if (!lib_name) if (!lib_name)
lib_name = params.library_path.c_str(); lib_name = params.library_path.c_str();
if (params.library_fd >= 0) {
error->Format("Cannot load system library from fd %d: %s",
params.library_fd, lib_name);
return false;
}
if (params.library_offset != 0) { if (params.library_offset != 0) {
error->Format("Cannot load system library from offset 0x%08lx: %s", error->Format("Cannot load system library from offset 0x%08lx: %s",
static_cast<unsigned long>(params.library_offset), lib_name); static_cast<unsigned long>(params.library_offset), lib_name);
...@@ -73,6 +78,10 @@ bool CheckSystemLibraryLoadParams(const char* lib_name, ...@@ -73,6 +78,10 @@ bool CheckSystemLibraryLoadParams(const char* lib_name,
static_cast<unsigned long>(params.wanted_address), lib_name); static_cast<unsigned long>(params.wanted_address), lib_name);
return false; return false;
} }
if (params.reserved_size != 0) {
error->Format("Cannot load system library in reserved memory map: %s",
lib_name);
}
return true; return true;
} }
......
...@@ -17,14 +17,24 @@ namespace crazy { ...@@ -17,14 +17,24 @@ namespace crazy {
// into the current process' address space. // into the current process' address space.
// //
// |library_path| is either the full library path. // |library_path| is either the full library path.
// |library_fd| is a file descriptor. If >= 0, the |library_path| is ignored.
// |library_offset| is the page-aligned offset where the library starts in // |library_offset| is the page-aligned offset where the library starts in
// its input file (typically > 0 when reading from Android APKs). // its input file (typically > 0 when reading from Android APKs).
// |wanted_address| is either 0, or the address where the library should // |wanted_address| is either 0, or the address where the library should
// be loaded. // be loaded.
// |reserved_size| is either 0, or a page-aligned size in bytes corresponding
// to a reserved memory area where to load the library, starting from
// |wanted_address|.
// |reserved_load_fallback| is ignored if |reserved_size| is 0. Otherwise, a
// value of true means that if the load fails at the reserved address range,
// the linker will try again at a different address.
struct LoadParams { struct LoadParams {
String library_path; String library_path;
int library_fd = -1;
off_t library_offset = 0; off_t library_offset = 0;
uintptr_t wanted_address = 0; uintptr_t wanted_address = 0;
uintptr_t reserved_size = 0;
bool reserved_load_fallback = false;
}; };
} // namespace crazy } // namespace crazy
......
...@@ -229,16 +229,19 @@ SharedLibrary::~SharedLibrary() = default; ...@@ -229,16 +229,19 @@ SharedLibrary::~SharedLibrary() = default;
bool SharedLibrary::Load(const LoadParams& params, Error* error) { bool SharedLibrary::Load(const LoadParams& params, Error* error) {
// First, record the path. // First, record the path.
const char* full_path = params.library_path.c_str(); const char* full_path = params.library_path.c_str();
LOG("full path '%s'", full_path); if (params.library_fd >= 0) {
snprintf(full_path_, sizeof(full_path_), "fd(%d):%s", params.library_fd,
size_t full_path_len = strlen(full_path); full_path);
if (full_path_len >= sizeof(full_path_)) { } else {
error->Format("Path too long: %s", full_path); size_t full_path_len = strlen(full_path);
return false; if (full_path_len >= sizeof(full_path_)) {
error->Format("Path too long: %s", full_path);
return false;
}
strlcpy(full_path_, full_path, sizeof(full_path_));
} }
strlcpy(full_path_, full_path, sizeof(full_path_));
base_name_ = GetBaseNamePtr(full_path_); base_name_ = GetBaseNamePtr(full_path_);
LOG("full path '%s'", full_path_);
// Default value of |soname_| will be |base_name_| unless overidden // Default value of |soname_| will be |base_name_| unless overidden
// by a DT_SONAME entry. This helps deal with broken libraries that don't // by a DT_SONAME entry. This helps deal with broken libraries that don't
...@@ -263,6 +266,9 @@ bool SharedLibrary::Load(const LoadParams& params, Error* error) { ...@@ -263,6 +266,9 @@ bool SharedLibrary::Load(const LoadParams& params, Error* error) {
} }
reserved_map_ = std::move(ret.reserved_mapping); reserved_map_ = std::move(ret.reserved_mapping);
LOG("Reserved mapping %p size=0x%lx", reserved_map_.address(),
static_cast<unsigned long>(reserved_map_.size()));
} }
if (phdr_table_get_relro_info(view_.phdr(), if (phdr_table_get_relro_info(view_.phdr(),
......
...@@ -77,12 +77,6 @@ bool IsSystemLibraryPath(const char* lib_path) { ...@@ -77,12 +77,6 @@ bool IsSystemLibraryPath(const char* lib_path) {
return false; return false;
} }
} // namespace crazy
#ifndef UNIT_TEST
namespace crazy {
FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept { FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept {
if (this != &other) { if (this != &other) {
if (fd_ != kEmptyFD) { if (fd_ != kEmptyFD) {
...@@ -94,6 +88,12 @@ FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept { ...@@ -94,6 +88,12 @@ FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept {
return *this; return *this;
} }
} // namespace crazy
#ifndef UNIT_TEST
namespace crazy {
ssize_t FileDescriptor::Read(void* buffer, size_t buffer_size) { ssize_t FileDescriptor::Read(void* buffer, size_t buffer_size) {
return TEMP_FAILURE_RETRY(::read(fd_, buffer, buffer_size)); return TEMP_FAILURE_RETRY(::read(fd_, buffer, buffer_size));
} }
......
// Copyright 2014 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 <crazy_linker.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include "test_util.h"
typedef void (*FunctionPtr)();
#ifndef LIB_NAME
#define LIB_NAME "libcrazy_linker_tests_libfoo.so"
#endif
std::string GetProgramDirectory(const char* argv0) {
const char* sep = strrchr(argv0, '/');
if (!sep) {
return ".";
}
return std::string(argv0, sep - argv0);
}
int main(int argc, const char** argv) {
crazy_context_t* context = crazy_context_create();
crazy_library_t* library;
// DEBUG
crazy_context_set_load_address(context, 0x20000000);
// Assume library file is in the same directory as this executable.
std::string program_dir = GetProgramDirectory(argv[0]);
std::string lib_path = program_dir + "/" LIB_NAME;
int fd = open(lib_path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
Panic("Could not find library file %s: %s\n", LIB_NAME, strerror(errno));
}
crazy_context_set_library_fd(context, fd);
int context_fd = crazy_context_get_library_fd(context);
if (context_fd != fd)
Panic("Invalid context fd %d (expected %d)\n", context_fd, fd);
// Load libfoo.so
if (!crazy_library_open(&library, LIB_NAME, context)) {
Panic("Could not open library: %s\n", crazy_context_get_error(context));
}
// Find the "Foo" symbol.
FunctionPtr foo_func;
if (!crazy_library_find_symbol(library, "Foo",
reinterpret_cast<void**>(&foo_func))) {
Panic("Could not find 'Foo' in %s\n", LIB_NAME);
}
// Call it.
(*foo_func)();
// Close the library.
printf("Closing %s\n", LIB_NAME);
crazy_library_close(library);
// Check that the descriptor is not closed, by trying to read from it.
char header[4];
if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) < 0 ||
TEMP_FAILURE_RETRY(read(fd, header, 4)) < 0) {
Panic("Could not read from file descriptor after library close!: %s",
strerror(errno));
}
close(fd);
context_fd = crazy_context_get_library_fd(context);
if (context_fd != -1)
Panic("Invalid context fd after library load %d (expected -1)", context_fd);
crazy_context_destroy(context);
printf("OK\n");
return 0;
}
// Copyright 2014 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 <crazy_linker.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include "test_util.h"
typedef void (*FunctionPtr)();
#ifndef LIB_NAME
#define LIB_NAME "libcrazy_linker_tests_libfoo.so"
#endif
std::string GetProgramDirectory(const char* argv0) {
const char* sep = strrchr(argv0, '/');
if (!sep) {
return ".";
}
return std::string(argv0, sep - argv0);
}
int main(int argc, const char** argv) {
crazy_context_t* context = crazy_context_create();
crazy_library_t* library;
// Allocate a memory map range large enough for our library.
const size_t kMapSize = 1024 * 1024; // 1 MiB should be enough.
void* reserved_map =
mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (reserved_map == MAP_FAILED)
Panic("mmap() failed: %s", strerror(errno));
crazy_context_set_reserved_map(
context, reinterpret_cast<uintptr_t>(reserved_map), kMapSize, false);
// Load libfoo.so
if (!crazy_library_open(&library, LIB_NAME, context)) {
Panic("Could not open library: %s\n", crazy_context_get_error(context));
}
// Find the "Foo" symbol.
FunctionPtr foo_func;
if (!crazy_library_find_symbol(library, "Foo",
reinterpret_cast<void**>(&foo_func))) {
Panic("Could not find 'Foo' in %s\n", LIB_NAME);
}
// Call it.
(*foo_func)();
// Close the library.
printf("Closing %s\n", LIB_NAME);
crazy_library_close(library);
// Check that the memory map is still there by writing to it.
printf("Erasing memory map\n");
if (mprotect(reserved_map, kMapSize, PROT_WRITE) < 0)
Panic("Could not mprotect() range: %s", strerror(errno));
memset(reserved_map, 1, kMapSize);
printf("Trying to reload inside smaller reserved map (no fallback).\n");
// Try to load the library again at the same address, without a reserved
// size. This should fail.
crazy_context_set_reserved_map(
context, reinterpret_cast<uintptr_t>(reserved_map), 4096, false);
if (crazy_library_open(&library, LIB_NAME, context)) {
Panic("Could unexpectedly load library in small mapping!");
}
printf("Trying to reload inside smaller reserved map (with fallback)\n.");
if (!crazy_library_open(&library, LIB_NAME, context)) {
Panic("Could not reload library with fallback in small mapping!");
}
crazy_library_close(library);
printf("Unmapping reserved mapping.\n");
if (munmap(reserved_map, kMapSize) < 0)
Panic("Could not unmap reserved mapping: %s", strerror(errno));
crazy_context_destroy(context);
printf("OK\n");
return 0;
}
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