blob: 46ad6eac54d3740e9bf8a9e23ae1b13864f0af37 [file] [log] [blame]
// 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.
// Implements the crazy linker C-based API exposed by <crazy_linker.h>
#include <crazy_linker.h>
#include <string.h>
#include "crazy_linker_error.h"
#include "crazy_linker_ashmem.h"
#include "crazy_linker_globals.h"
#include "crazy_linker_proc_maps.h"
#include "crazy_linker_search_path_list.h"
#include "crazy_linker_shared_library.h"
#include "crazy_linker_thread.h"
#include "crazy_linker_util.h"
#include "crazy_linker_library_view.h"
#include "crazy_linker_system.h"
using crazy::Globals;
using crazy::Error;
using crazy::SearchPathList;
using crazy::ScopedGlobalLock;
using crazy::LibraryView;
//
// crazy_context_t
//
struct crazy_context_t {
public:
crazy_context_t()
: load_address(0),
file_offset(0),
error(),
search_paths(),
java_vm(NULL),
minimum_jni_version(0),
callback_poster(NULL),
callback_poster_opaque(NULL) {
ResetSearchPaths();
}
void ResetSearchPaths();
size_t load_address;
size_t file_offset;
Error error;
SearchPathList search_paths;
void* java_vm;
int minimum_jni_version;
crazy_callback_poster_t callback_poster;
void* callback_poster_opaque;
};
void crazy_context_t::ResetSearchPaths() {
search_paths.ResetFromEnv("LD_LIBRARY_PATH");
}
//
// API functions
//
extern "C" {
crazy_context_t* crazy_context_create(void) { return new crazy_context_t(); }
const char* crazy_context_get_error(crazy_context_t* context) {
const char* error = context->error.c_str();
return (error[0] != '\0') ? error : NULL;
}
// Clear error in a given context.
void crazy_context_clear_error(crazy_context_t* context) {
context->error = "";
}
void crazy_context_set_load_address(crazy_context_t* context,
size_t load_address) {
context->load_address = load_address;
}
size_t crazy_context_get_load_address(crazy_context_t* context) {
return context->load_address;
}
void crazy_context_set_file_offset(crazy_context_t* context,
size_t file_offset) {
context->file_offset = file_offset;
}
size_t crazy_context_get_file_offset(crazy_context_t* context) {
return context->file_offset;
}
crazy_status_t crazy_context_add_search_path(crazy_context_t* context,
const char* file_path) {
context->search_paths.AddPaths(file_path);
return CRAZY_STATUS_SUCCESS;
}
crazy_status_t crazy_context_add_search_path_for_address(
crazy_context_t* context,
void* address) {
uintptr_t load_address;
char path[512];
char* p;
if (crazy::FindElfBinaryForAddress(
address, &load_address, path, sizeof(path)) &&
(p = strrchr(path, '/')) != NULL && p[1]) {
*p = '\0';
return crazy_context_add_search_path(context, path);
}
context->error.Format("Could not find ELF binary at address @%p", address);
return CRAZY_STATUS_FAILURE;
}
void crazy_context_reset_search_paths(crazy_context_t* context) {
context->ResetSearchPaths();
}
void crazy_context_set_java_vm(crazy_context_t* context,
void* java_vm,
int minimum_jni_version) {
context->java_vm = java_vm;
context->minimum_jni_version = minimum_jni_version;
}
void crazy_context_get_java_vm(crazy_context_t* context,
void** java_vm,
int* minimum_jni_version) {
*java_vm = context->java_vm;
*minimum_jni_version = context->minimum_jni_version;
}
void crazy_context_set_callback_poster(crazy_context_t* context,
crazy_callback_poster_t poster,
void* poster_opaque) {
context->callback_poster = poster;
context->callback_poster_opaque = poster_opaque;
}
void crazy_context_get_callback_poster(crazy_context_t* context,
crazy_callback_poster_t* poster,
void** poster_opaque) {
*poster = context->callback_poster;
*poster_opaque = context->callback_poster_opaque;
}
void crazy_callback_run(crazy_callback_t* callback) {
(*callback->handler)(callback->opaque);
}
void crazy_context_destroy(crazy_context_t* context) { delete context; }
// Scoped delayed execution, removes RDebug callbacks on scope exit. No-op
// if callback is NULL.
class ScopedDelayedCallbackPoster {
public:
ScopedDelayedCallbackPoster(crazy_context_t* context) {
if (context && context->callback_poster) {
crazy::Globals::GetRDebug()->SetDelayedCallbackPoster(&PostFromContext,
context);
set_delayed_callback_poster_ = true;
} else {
set_delayed_callback_poster_ = false;
}
}
~ScopedDelayedCallbackPoster() {
if (set_delayed_callback_poster_)
crazy::Globals::GetRDebug()->SetDelayedCallbackPoster(NULL, NULL);
}
private:
// Wrap callback hander and opaque into a call to a crazy_context_poster_t.
static bool PostFromContext(void* crazy_context,
crazy_callback_handler_t handler,
void* opaque) {
crazy_context_t* context = static_cast<crazy_context_t*>(crazy_context);
crazy_callback_t callback;
callback.handler = handler;
callback.opaque = opaque;
return context->callback_poster(&callback,
context->callback_poster_opaque);
}
// True if the context offered a callback_poster, otherwise false.
bool set_delayed_callback_poster_;
};
crazy_status_t crazy_library_open(crazy_library_t** library,
const char* lib_name,
crazy_context_t* context) {
ScopedDelayedCallbackPoster poster(context);
ScopedGlobalLock lock;
LibraryView* wrap =
crazy::Globals::GetLibraries()->LoadLibrary(lib_name,
RTLD_NOW,
context->load_address,
context->file_offset,
&context->search_paths,
&context->error);
if (!wrap)
return CRAZY_STATUS_FAILURE;
if (context->java_vm != NULL && wrap->IsCrazy()) {
crazy::SharedLibrary* lib = wrap->GetCrazy();
if (!lib->SetJavaVM(
context->java_vm, context->minimum_jni_version, &context->error)) {
crazy::Globals::GetLibraries()->UnloadLibrary(wrap);
return CRAZY_STATUS_FAILURE;
}
}
*library = reinterpret_cast<crazy_library_t*>(wrap);
return CRAZY_STATUS_SUCCESS;
}
crazy_status_t crazy_library_get_info(crazy_library_t* library,
crazy_context_t* context,
crazy_library_info_t* info) {
if (!library) {
context->error = "Invalid library file handle";
return CRAZY_STATUS_FAILURE;
}
LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
if (!wrap->GetInfo(&info->load_address,
&info->load_size,
&info->relro_start,
&info->relro_size,
&context->error)) {
return CRAZY_STATUS_FAILURE;
}
return CRAZY_STATUS_SUCCESS;
}
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.fd()))
return CRAZY_STATUS_FAILURE;
return CRAZY_STATUS_SUCCESS;
}
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 || !wrap->IsCrazy()) {
context->error = "Invalid library file handle";
return CRAZY_STATUS_FAILURE;
}
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_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 || !wrap->IsCrazy()) {
context->error = "Invalid library file handle";
return CRAZY_STATUS_FAILURE;
}
crazy::SharedLibrary* lib = wrap->GetCrazy();
if (!lib->UseSharedRelro(relro_start, relro_size, relro_fd, &context->error))
return CRAZY_STATUS_FAILURE;
return CRAZY_STATUS_SUCCESS;
}
crazy_status_t crazy_library_find_by_name(const char* library_name,
crazy_library_t** library) {
{
ScopedGlobalLock lock;
LibraryView* wrap =
Globals::GetLibraries()->FindLibraryByName(library_name);
if (!wrap)
return CRAZY_STATUS_FAILURE;
wrap->AddRef();
*library = reinterpret_cast<crazy_library_t*>(wrap);
}
return CRAZY_STATUS_SUCCESS;
}
crazy_status_t crazy_library_find_symbol(crazy_library_t* library,
const char* symbol_name,
void** symbol_address) {
LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
// TODO(digit): Handle NULL symbols properly.
*symbol_address = wrap->LookupSymbol(symbol_name);
return (*symbol_address == NULL) ? CRAZY_STATUS_FAILURE
: CRAZY_STATUS_SUCCESS;
}
crazy_status_t crazy_linker_find_symbol(const char* symbol_name,
void** symbol_address) {
// TODO(digit): Implement this.
return CRAZY_STATUS_FAILURE;
}
crazy_status_t crazy_library_find_from_address(void* address,
crazy_library_t** library) {
{
ScopedGlobalLock lock;
LibraryView* wrap = Globals::GetLibraries()->FindLibraryForAddress(address);
if (!wrap)
return CRAZY_STATUS_FAILURE;
wrap->AddRef();
*library = reinterpret_cast<crazy_library_t*>(wrap);
return CRAZY_STATUS_SUCCESS;
}
}
void crazy_library_close(crazy_library_t* library) {
crazy_library_close_with_context(library, NULL);
}
void crazy_library_close_with_context(crazy_library_t* library,
crazy_context_t* context) {
if (library) {
ScopedDelayedCallbackPoster poster(context);
ScopedGlobalLock lock;
LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
Globals::GetLibraries()->UnloadLibrary(wrap);
}
}
} // extern "C"