Remove libprocinfo, libbacktrace, libunwindstack
These projects have moved to a different location. platform/system/core [libprocinfo] -> platform/system/libprocinfo platform/system/core [libbacktrace] -> platform/system/unwinding [libbacktrace] platform/system/core [libunwindstack] -> platform/system/unwinding [libunwindstack] BUG: 163786882 Test: Local build + TH Change-Id: Id6d278d917236df0ffd40b5c32593856e112cb5b
This commit is contained in:
parent
b5e565d29b
commit
ce7b63697e
413 changed files with 3 additions and 50519 deletions
1
libbacktrace
Symbolic link
1
libbacktrace
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../unwinding/libbacktrace
|
||||
|
|
@ -1 +0,0 @@
|
|||
../.clang-format-2
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "libbacktrace_common",
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
libbacktrace_sources = [
|
||||
"Backtrace.cpp",
|
||||
"BacktraceCurrent.cpp",
|
||||
"BacktracePtrace.cpp",
|
||||
"ThreadEntry.cpp",
|
||||
"UnwindStack.cpp",
|
||||
"UnwindStackMap.cpp",
|
||||
]
|
||||
|
||||
cc_library_headers {
|
||||
name: "libbacktrace_headers",
|
||||
vendor_available: true,
|
||||
recovery_available: true,
|
||||
native_bridge_supported: true,
|
||||
export_include_dirs: ["include"],
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"//apex_available:anyapex",
|
||||
],
|
||||
min_sdk_version: "apex_inherit",
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "libbacktrace_defaults",
|
||||
defaults: ["libbacktrace_common"],
|
||||
|
||||
cflags: [
|
||||
"-Wexit-time-destructors",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"BacktraceMap.cpp",
|
||||
],
|
||||
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: true,
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
],
|
||||
},
|
||||
linux: {
|
||||
srcs: libbacktrace_sources,
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libprocinfo",
|
||||
],
|
||||
},
|
||||
android: {
|
||||
static_libs: ["libasync_safe"],
|
||||
static: {
|
||||
whole_static_libs: ["libasync_safe"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libbacktrace",
|
||||
vendor_available: false,
|
||||
// TODO(b/153609531): remove when no longer needed.
|
||||
native_bridge_supported: true,
|
||||
recovery_available: true,
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"//apex_available:anyapex",
|
||||
],
|
||||
vndk: {
|
||||
enabled: true,
|
||||
support_system_process: true,
|
||||
},
|
||||
host_supported: true,
|
||||
defaults: ["libbacktrace_defaults"],
|
||||
|
||||
target: {
|
||||
linux: {
|
||||
shared_libs: [
|
||||
"libunwindstack",
|
||||
],
|
||||
},
|
||||
vendor: {
|
||||
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
|
||||
},
|
||||
recovery: {
|
||||
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
|
||||
},
|
||||
native_bridge: {
|
||||
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Static library without DEX support to avoid dependencies on the ART APEX.
|
||||
cc_library_static {
|
||||
name: "libbacktrace_no_dex",
|
||||
visibility: [
|
||||
"//system/core/debuggerd",
|
||||
"//system/core/init",
|
||||
],
|
||||
defaults: ["libbacktrace_defaults"],
|
||||
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
|
||||
target: {
|
||||
linux: {
|
||||
static_libs: [
|
||||
"libunwindstack_no_dex",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "libbacktrace_test",
|
||||
defaults: ["libbacktrace_common"],
|
||||
host_supported: true,
|
||||
strip: {
|
||||
none: true,
|
||||
},
|
||||
cflags: ["-O0"],
|
||||
srcs: ["backtrace_testlib.cpp"],
|
||||
|
||||
shared_libs: [
|
||||
"libunwindstack",
|
||||
],
|
||||
relative_install_path: "backtrace_test_libs",
|
||||
|
||||
target: {
|
||||
linux_glibc: {
|
||||
// This forces the creation of eh_frame with unwind information
|
||||
// for host.
|
||||
cflags: [
|
||||
"-fcxx-exceptions"
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// The backtrace_test executable.
|
||||
//-------------------------------------------------------------------------
|
||||
cc_test {
|
||||
name: "backtrace_test",
|
||||
isolated: true,
|
||||
defaults: ["libbacktrace_common"],
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"backtrace_test.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-fno-builtin",
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbacktrace",
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libunwindstack",
|
||||
],
|
||||
|
||||
group_static_libs: true,
|
||||
|
||||
// So that the dlopen can find the libbacktrace_test.so.
|
||||
ldflags: [
|
||||
"-Wl,--rpath,${ORIGIN}/../backtrace_test_libs",
|
||||
],
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
data: [
|
||||
"testdata/arm/*",
|
||||
"testdata/arm64/*",
|
||||
"testdata/x86/*",
|
||||
"testdata/x86_64/*",
|
||||
],
|
||||
required: [
|
||||
"libbacktrace_test",
|
||||
],
|
||||
}
|
||||
|
||||
cc_benchmark {
|
||||
name: "backtrace_benchmarks",
|
||||
defaults: ["libbacktrace_common"],
|
||||
|
||||
srcs: [
|
||||
"backtrace_benchmarks.cpp",
|
||||
"backtrace_read_benchmarks.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbacktrace",
|
||||
"libbase",
|
||||
"libunwindstack",
|
||||
],
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/threads.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "UnwindStack.h"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Backtrace functions.
|
||||
//-------------------------------------------------------------------------
|
||||
Backtrace::Backtrace(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: pid_(pid), tid_(tid), map_(map), map_shared_(true) {
|
||||
if (map_ == nullptr) {
|
||||
map_ = BacktraceMap::Create(pid);
|
||||
map_shared_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
Backtrace::~Backtrace() {
|
||||
if (map_ && !map_shared_) {
|
||||
delete map_;
|
||||
map_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Backtrace::GetFunctionName(uint64_t pc, uint64_t* offset, const backtrace_map_t* map) {
|
||||
backtrace_map_t map_value;
|
||||
if (map == nullptr) {
|
||||
FillInMap(pc, &map_value);
|
||||
map = &map_value;
|
||||
}
|
||||
// If no map is found, or this map is backed by a device, then return nothing.
|
||||
if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
|
||||
return "";
|
||||
}
|
||||
std::string name(GetFunctionNameRaw(pc, offset));
|
||||
char* demangled_name = __cxa_demangle(name.c_str(), nullptr, nullptr, nullptr);
|
||||
if (demangled_name != nullptr) {
|
||||
name = demangled_name;
|
||||
free(demangled_name);
|
||||
return name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool Backtrace::VerifyReadWordArgs(uint64_t ptr, word_t* out_value) {
|
||||
if (ptr & (sizeof(word_t)-1)) {
|
||||
BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr));
|
||||
*out_value = static_cast<word_t>(-1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Backtrace::FormatFrameData(size_t frame_num) {
|
||||
if (frame_num >= frames_.size()) {
|
||||
return "";
|
||||
}
|
||||
return FormatFrameData(&frames_[frame_num]);
|
||||
}
|
||||
|
||||
std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
|
||||
std::string map_name;
|
||||
if (BacktraceMap::IsValid(frame->map)) {
|
||||
map_name = frame->map.Name();
|
||||
if (!frame->map.name.empty()) {
|
||||
if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
|
||||
map_name.resize(map_name.size() - 1);
|
||||
map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map_name = "<unknown>";
|
||||
}
|
||||
|
||||
std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, frame->rel_pc));
|
||||
line += map_name;
|
||||
// Special handling for non-zero offset maps, we need to print that
|
||||
// information.
|
||||
if (frame->map.offset != 0) {
|
||||
line += " (offset " + StringPrintf("0x%" PRIx64, frame->map.offset) + ")";
|
||||
}
|
||||
if (!frame->func_name.empty()) {
|
||||
line += " (" + frame->func_name;
|
||||
if (frame->func_offset) {
|
||||
line += StringPrintf("+%" PRIu64, frame->func_offset);
|
||||
}
|
||||
line += ')';
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void Backtrace::FillInMap(uint64_t pc, backtrace_map_t* map) {
|
||||
if (map_ != nullptr) {
|
||||
map_->FillIn(pc, map);
|
||||
}
|
||||
}
|
||||
|
||||
Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
|
||||
if (pid == BACKTRACE_CURRENT_PROCESS) {
|
||||
pid = getpid();
|
||||
if (tid == BACKTRACE_CURRENT_THREAD) {
|
||||
tid = android::base::GetThreadId();
|
||||
}
|
||||
} else if (tid == BACKTRACE_CURRENT_THREAD) {
|
||||
tid = pid;
|
||||
}
|
||||
|
||||
if (pid == getpid()) {
|
||||
return new UnwindStackCurrent(pid, tid, map);
|
||||
} else {
|
||||
return new UnwindStackPtrace(pid, tid, map);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
|
||||
switch (error.error_code) {
|
||||
case BACKTRACE_UNWIND_NO_ERROR:
|
||||
return "No error";
|
||||
case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
|
||||
return "Setup failed";
|
||||
case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
|
||||
return "No map found";
|
||||
case BACKTRACE_UNWIND_ERROR_INTERNAL:
|
||||
return "Internal libbacktrace error, please submit a bugreport";
|
||||
case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
|
||||
return "Thread doesn't exist";
|
||||
case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
|
||||
return "Thread has not responded to signal in time";
|
||||
case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
|
||||
return "Attempt to use an unsupported feature";
|
||||
case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
|
||||
return "Attempt to do an offline unwind without a context";
|
||||
case BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT:
|
||||
return "Exceed MAX_BACKTRACE_FRAMES limit";
|
||||
case BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED:
|
||||
return android::base::StringPrintf("Failed to read memory at addr 0x%" PRIx64,
|
||||
error.error_info.addr);
|
||||
case BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED:
|
||||
return android::base::StringPrintf("Failed to read register %" PRIu64, error.error_info.regno);
|
||||
case BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED:
|
||||
return "Failed to find a function in debug sections";
|
||||
case BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED:
|
||||
return "Failed to execute dwarf instructions in debug sections";
|
||||
case BACKTRACE_UNWIND_ERROR_UNWIND_INFO:
|
||||
return "Failed to unwind due to invalid unwind information";
|
||||
case BACKTRACE_UNWIND_ERROR_REPEATED_FRAME:
|
||||
return "Failed to unwind due to same sp/pc repeating";
|
||||
case BACKTRACE_UNWIND_ERROR_INVALID_ELF:
|
||||
return "Failed to unwind due to invalid elf";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
#include <async_safe/log.h>
|
||||
|
||||
// Logging macros for use in signal handler, only available on target.
|
||||
#define BACK_ASYNC_SAFE_LOGW(format, ...) \
|
||||
async_safe_format_log(ANDROID_LOG_WARN, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define BACK_ASYNC_SAFE_LOGE(format, ...) \
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
#define BACK_ASYNC_SAFE_LOGW(format, ...)
|
||||
|
||||
#define BACK_ASYNC_SAFE_LOGE(format, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/threads.h>
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceAsyncSafeLog.h"
|
||||
#include "BacktraceCurrent.h"
|
||||
#include "ThreadEntry.h"
|
||||
|
||||
bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
|
||||
#if defined(__aarch64__)
|
||||
// Tagged pointer after Android R would lead top byte to have random values
|
||||
// https://source.android.com/devices/tech/debug/tagged-pointers
|
||||
ptr &= (1ULL << 56) - 1;
|
||||
#endif
|
||||
|
||||
if (!VerifyReadWordArgs(ptr, out_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
backtrace_map_t map;
|
||||
FillInMap(ptr, &map);
|
||||
if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) {
|
||||
*out_value = *reinterpret_cast<word_t*>(ptr);
|
||||
return true;
|
||||
} else {
|
||||
BACK_ASYNC_SAFE_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
|
||||
*out_value = static_cast<word_t>(-1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BacktraceCurrent::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
|
||||
#if defined(__aarch64__)
|
||||
// Tagged pointer after Android R would lead top byte to have random values
|
||||
// https://source.android.com/devices/tech/debug/tagged-pointers
|
||||
addr &= (1ULL << 56) - 1;
|
||||
#endif
|
||||
|
||||
backtrace_map_t map;
|
||||
FillInMap(addr, &map);
|
||||
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
|
||||
return 0;
|
||||
}
|
||||
bytes = MIN(map.end - addr, bytes);
|
||||
memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool BacktraceCurrent::Unwind(size_t num_ignore_frames, void* ucontext) {
|
||||
if (GetMap() == nullptr) {
|
||||
// Without a map object, we can't do anything.
|
||||
error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
|
||||
return false;
|
||||
}
|
||||
|
||||
error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
|
||||
if (ucontext) {
|
||||
return UnwindFromContext(num_ignore_frames, ucontext);
|
||||
}
|
||||
|
||||
if (Tid() != static_cast<pid_t>(android::base::GetThreadId())) {
|
||||
return UnwindThread(num_ignore_frames);
|
||||
}
|
||||
|
||||
return UnwindFromContext(num_ignore_frames, nullptr);
|
||||
}
|
||||
|
||||
bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
|
||||
if (BacktraceMap::IsValid(frame.map)) {
|
||||
const std::string library = basename(frame.map.name.c_str());
|
||||
if (library == "libunwind.so" || library == "libbacktrace.so") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Since errno is stored per thread, changing it in the signal handler
|
||||
// modifies the value on the thread in which the signal handler executes.
|
||||
// If a signal occurs between a call and an errno check, it's possible
|
||||
// to get the errno set here. Always save and restore it just in case
|
||||
// code would modify it.
|
||||
class ErrnoRestorer {
|
||||
public:
|
||||
ErrnoRestorer() : saved_errno_(errno) {}
|
||||
~ErrnoRestorer() {
|
||||
errno = saved_errno_;
|
||||
}
|
||||
|
||||
private:
|
||||
int saved_errno_;
|
||||
};
|
||||
|
||||
static void SignalLogOnly(int, siginfo_t*, void*) {
|
||||
ErrnoRestorer restore;
|
||||
|
||||
BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(),
|
||||
static_cast<int>(android::base::GetThreadId()), THREAD_SIGNAL);
|
||||
}
|
||||
|
||||
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
|
||||
ErrnoRestorer restore;
|
||||
|
||||
ThreadEntry* entry = ThreadEntry::Get(getpid(), android::base::GetThreadId(), false);
|
||||
if (!entry) {
|
||||
BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(),
|
||||
static_cast<int>(android::base::GetThreadId()));
|
||||
return;
|
||||
}
|
||||
|
||||
entry->CopyUcontextFromSigcontext(sigcontext);
|
||||
|
||||
// Indicate the ucontext is now valid.
|
||||
entry->Wake();
|
||||
|
||||
// Pause the thread until the unwind is complete. This avoids having
|
||||
// the thread run ahead causing problems.
|
||||
// The number indicates that we are waiting for the second Wake() call
|
||||
// overall which is made by the thread requesting an unwind.
|
||||
if (entry->Wait(2)) {
|
||||
// Do not remove the entry here because that can result in a deadlock
|
||||
// if the code cannot properly send a signal to the thread under test.
|
||||
entry->Wake();
|
||||
} else {
|
||||
// At this point, it is possible that entry has been freed, so just exit.
|
||||
BACK_ASYNC_SAFE_LOGE("Timed out waiting for unwind thread to indicate it completed.");
|
||||
}
|
||||
}
|
||||
|
||||
bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
|
||||
// Prevent multiple threads trying to set the trigger action on different
|
||||
// threads at the same time.
|
||||
pthread_mutex_lock(&g_sigaction_mutex);
|
||||
|
||||
ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
|
||||
entry->Lock();
|
||||
|
||||
struct sigaction act, oldact;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = SignalHandler;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&act.sa_mask);
|
||||
if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
|
||||
BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
|
||||
ThreadEntry::Remove(entry);
|
||||
pthread_mutex_unlock(&g_sigaction_mutex);
|
||||
error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
|
||||
// Do not emit an error message, this might be expected. Set the
|
||||
// error and let the caller decide.
|
||||
if (errno == ESRCH) {
|
||||
error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
|
||||
} else {
|
||||
error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
sigaction(THREAD_SIGNAL, &oldact, nullptr);
|
||||
ThreadEntry::Remove(entry);
|
||||
pthread_mutex_unlock(&g_sigaction_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for the thread to get the ucontext. The number indicates
|
||||
// that we are waiting for the first Wake() call made by the thread.
|
||||
bool wait_completed = entry->Wait(1);
|
||||
|
||||
if (!wait_completed && oldact.sa_sigaction == nullptr) {
|
||||
// If the wait failed, it could be that the signal could not be delivered
|
||||
// within the timeout. Add a signal handler that's simply going to log
|
||||
// something so that we don't crash if the signal eventually gets
|
||||
// delivered. Only do this if there isn't already an action set up.
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = SignalLogOnly;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&act.sa_mask);
|
||||
sigaction(THREAD_SIGNAL, &act, nullptr);
|
||||
} else {
|
||||
sigaction(THREAD_SIGNAL, &oldact, nullptr);
|
||||
}
|
||||
// After the thread has received the signal, allow other unwinders to
|
||||
// continue.
|
||||
pthread_mutex_unlock(&g_sigaction_mutex);
|
||||
|
||||
bool unwind_done = false;
|
||||
if (wait_completed) {
|
||||
unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
|
||||
|
||||
// Tell the signal handler to exit and release the entry.
|
||||
entry->Wake();
|
||||
|
||||
// Wait for the thread to indicate it is done with the ThreadEntry.
|
||||
if (!entry->Wait(3)) {
|
||||
// Send a warning, but do not mark as a failure to unwind.
|
||||
BACK_ASYNC_SAFE_LOGW("Timed out waiting for signal handler to indicate it finished.");
|
||||
}
|
||||
} else {
|
||||
// Check to see if the thread has disappeared.
|
||||
if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
|
||||
error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
|
||||
} else {
|
||||
error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
|
||||
BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
|
||||
}
|
||||
}
|
||||
|
||||
ThreadEntry::Remove(entry);
|
||||
|
||||
return unwind_done;
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_BACKTRACE_CURRENT_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_CURRENT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
// The signal used to cause a thread to dump the stack.
|
||||
#if defined(__GLIBC__)
|
||||
// In order to run the backtrace_tests on the host, we can't use
|
||||
// the internal real time signals used by GLIBC. To avoid this,
|
||||
// use SIGRTMIN for the signal to dump the stack.
|
||||
#define THREAD_SIGNAL SIGRTMIN
|
||||
#else
|
||||
#define THREAD_SIGNAL (__SIGRTMIN+1)
|
||||
#endif
|
||||
|
||||
class BacktraceMap;
|
||||
|
||||
class BacktraceCurrent : public Backtrace {
|
||||
public:
|
||||
BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
|
||||
virtual ~BacktraceCurrent() {}
|
||||
|
||||
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
|
||||
|
||||
bool ReadWord(uint64_t ptr, word_t* out_value) override;
|
||||
|
||||
bool Unwind(size_t num_ignore_frames, void* ucontext) override;
|
||||
|
||||
protected:
|
||||
bool DiscardFrame(const backtrace_frame_data_t& frame);
|
||||
|
||||
private:
|
||||
bool UnwindThread(size_t num_ignore_frames);
|
||||
|
||||
virtual bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) = 0;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_LOG_H
|
||||
|
||||
#define LOG_TAG "libbacktrace"
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
// Macro to log the function name along with the warning message.
|
||||
#define BACK_LOGW(format, ...) \
|
||||
ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
#define BACK_LOGE(format, ...) \
|
||||
ALOGE("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_LOG_H
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "backtrace-map"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <backtrace/backtrace_constants.h>
|
||||
#if defined(__linux__)
|
||||
#include <procinfo/process_map.h>
|
||||
#endif
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
std::string backtrace_map_t::Name() const {
|
||||
if (!name.empty()) return name;
|
||||
if (start == 0 && end == 0) return "";
|
||||
return StringPrintf("<anonymous:%" PRIPTR ">", start);
|
||||
}
|
||||
|
||||
BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
|
||||
if (pid_ < 0) {
|
||||
pid_ = getpid();
|
||||
}
|
||||
}
|
||||
|
||||
BacktraceMap::~BacktraceMap() {}
|
||||
|
||||
void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
|
||||
ScopedBacktraceMapIteratorLock lock(this);
|
||||
for (auto it = begin(); it != end(); ++it) {
|
||||
const backtrace_map_t* entry = *it;
|
||||
if (addr >= entry->start && addr < entry->end) {
|
||||
*map = *entry;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*map = {};
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
static bool ParseLine(const char* line, backtrace_map_t* map) {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
char permissions[5];
|
||||
int name_pos;
|
||||
|
||||
// Mac OS vmmap(1) output:
|
||||
// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW
|
||||
// /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c %n", &start, &end,
|
||||
permissions, &name_pos) != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
map->start = start;
|
||||
map->end = end;
|
||||
map->flags = PROT_NONE;
|
||||
if (permissions[0] == 'r') {
|
||||
map->flags |= PROT_READ;
|
||||
}
|
||||
if (permissions[1] == 'w') {
|
||||
map->flags |= PROT_WRITE;
|
||||
}
|
||||
if (permissions[2] == 'x') {
|
||||
map->flags |= PROT_EXEC;
|
||||
}
|
||||
|
||||
map->name = line + name_pos;
|
||||
if (!map->name.empty() && map->name[map->name.length() - 1] == '\n') {
|
||||
map->name.erase(map->name.length() - 1);
|
||||
}
|
||||
|
||||
ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", reinterpret_cast<void*>(map->start),
|
||||
reinterpret_cast<void*>(map->end), map->flags, map->name.c_str());
|
||||
return true;
|
||||
}
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
bool BacktraceMap::Build() {
|
||||
#if defined(__APPLE__)
|
||||
char
|
||||
cmd[sizeof(pid_t) * 3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
|
||||
char line[1024];
|
||||
// cmd is guaranteed to always be big enough to hold this string.
|
||||
snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
|
||||
FILE* fp = popen(cmd, "r");
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
backtrace_map_t map;
|
||||
if (ParseLine(line, &map)) {
|
||||
maps_.push_back(map);
|
||||
}
|
||||
}
|
||||
pclose(fp);
|
||||
return true;
|
||||
#else
|
||||
return android::procinfo::ReadProcessMaps(
|
||||
pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t, const char* name) {
|
||||
maps_.resize(maps_.size() + 1);
|
||||
backtrace_map_t& map = maps_.back();
|
||||
map.start = start;
|
||||
map.end = end;
|
||||
map.flags = flags;
|
||||
map.name = name;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// Corkscrew and libunwind don't compile on the mac, so create a generic
|
||||
// map object.
|
||||
BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
|
||||
BacktraceMap* map = new BacktraceMap(pid);
|
||||
if (!map->Build()) {
|
||||
delete map;
|
||||
return nullptr;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "BacktracePtrace.h"
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
static bool PtraceRead(pid_t tid, uint64_t addr, word_t* out_value) {
|
||||
// ptrace() returns -1 and sets errno when the operation fails.
|
||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||
errno = 0;
|
||||
*out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), nullptr);
|
||||
if (*out_value == static_cast<word_t>(-1) && errno) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool BacktracePtrace::ReadWord(uint64_t ptr, word_t* out_value) {
|
||||
#if defined(__APPLE__)
|
||||
BACK_LOGW("MacOS does not support reading from another pid.");
|
||||
return false;
|
||||
#else
|
||||
if (!VerifyReadWordArgs(ptr, out_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
backtrace_map_t map;
|
||||
FillInMap(ptr, &map);
|
||||
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return PtraceRead(Tid(), ptr, out_value);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t BacktracePtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
|
||||
#if defined(__APPLE__)
|
||||
BACK_LOGW("MacOS does not support reading from another pid.");
|
||||
return 0;
|
||||
#else
|
||||
backtrace_map_t map;
|
||||
FillInMap(addr, &map);
|
||||
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes = MIN(map.end - addr, bytes);
|
||||
size_t bytes_read = 0;
|
||||
word_t data_word;
|
||||
size_t align_bytes = addr & (sizeof(word_t) - 1);
|
||||
if (align_bytes != 0) {
|
||||
if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
|
||||
return 0;
|
||||
}
|
||||
size_t copy_bytes = MIN(sizeof(word_t) - align_bytes, bytes);
|
||||
memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + align_bytes, copy_bytes);
|
||||
addr += copy_bytes;
|
||||
buffer += copy_bytes;
|
||||
bytes -= copy_bytes;
|
||||
bytes_read += copy_bytes;
|
||||
}
|
||||
|
||||
size_t num_words = bytes / sizeof(word_t);
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
if (!PtraceRead(Tid(), addr, &data_word)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(buffer, &data_word, sizeof(word_t));
|
||||
buffer += sizeof(word_t);
|
||||
addr += sizeof(word_t);
|
||||
bytes_read += sizeof(word_t);
|
||||
}
|
||||
|
||||
size_t left_over = bytes & (sizeof(word_t) - 1);
|
||||
if (left_over) {
|
||||
if (!PtraceRead(Tid(), addr, &data_word)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(buffer, &data_word, left_over);
|
||||
bytes_read += left_over;
|
||||
}
|
||||
return bytes_read;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_BACKTRACE_PTRACE_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_PTRACE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
class BacktraceMap;
|
||||
|
||||
class BacktracePtrace : public Backtrace {
|
||||
public:
|
||||
BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
|
||||
virtual ~BacktracePtrace() {}
|
||||
|
||||
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
|
||||
|
||||
bool ReadWord(uint64_t ptr, word_t* out_value) override;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_BACKTRACE_TEST_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_TEST_H
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class BacktraceTest : public ::testing::Test {
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
dl_handle_ = dlopen("libbacktrace_test.so", RTLD_NOW | RTLD_LOCAL);
|
||||
|
||||
test_level_one_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
|
||||
dlsym(dl_handle_, "test_level_one"));
|
||||
|
||||
test_level_two_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
|
||||
dlsym(dl_handle_, "test_level_two"));
|
||||
|
||||
test_level_three_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
|
||||
dlsym(dl_handle_, "test_level_three"));
|
||||
|
||||
test_level_four_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
|
||||
dlsym(dl_handle_, "test_level_four"));
|
||||
|
||||
test_recursive_call_ = reinterpret_cast<int (*)(int, void (*)(void*), void*)>(
|
||||
dlsym(dl_handle_, "test_recursive_call"));
|
||||
|
||||
test_get_context_and_wait_ = reinterpret_cast<void (*)(void*, volatile int*)>(
|
||||
dlsym(dl_handle_, "test_get_context_and_wait"));
|
||||
|
||||
test_signal_action_ =
|
||||
reinterpret_cast<void (*)(int, siginfo_t*, void*)>(dlsym(dl_handle_, "test_signal_action"));
|
||||
|
||||
test_signal_handler_ =
|
||||
reinterpret_cast<void (*)(int)>(dlsym(dl_handle_, "test_signal_handler"));
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_TRUE(dl_handle_ != nullptr);
|
||||
ASSERT_TRUE(test_level_one_ != nullptr);
|
||||
ASSERT_TRUE(test_level_two_ != nullptr);
|
||||
ASSERT_TRUE(test_level_three_ != nullptr);
|
||||
ASSERT_TRUE(test_level_four_ != nullptr);
|
||||
ASSERT_TRUE(test_recursive_call_ != nullptr);
|
||||
ASSERT_TRUE(test_get_context_and_wait_ != nullptr);
|
||||
ASSERT_TRUE(test_signal_action_ != nullptr);
|
||||
ASSERT_TRUE(test_signal_handler_ != nullptr);
|
||||
}
|
||||
|
||||
public:
|
||||
static void* dl_handle_;
|
||||
static int (*test_level_one_)(int, int, int, int, void (*)(void*), void*);
|
||||
static int (*test_level_two_)(int, int, int, int, void (*)(void*), void*);
|
||||
static int (*test_level_three_)(int, int, int, int, void (*)(void*), void*);
|
||||
static int (*test_level_four_)(int, int, int, int, void (*)(void*), void*);
|
||||
static int (*test_recursive_call_)(int, void (*)(void*), void*);
|
||||
static void (*test_get_context_and_wait_)(void*, volatile int*);
|
||||
static void (*test_signal_action_)(int, siginfo_t*, void*);
|
||||
static void (*test_signal_handler_)(int);
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_TEST_H
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
cferris@google.com
|
||||
jmgao@google.com
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include "BacktraceAsyncSafeLog.h"
|
||||
#include "ThreadEntry.h"
|
||||
|
||||
// Initialize static member variables.
|
||||
ThreadEntry* ThreadEntry::list_ = nullptr;
|
||||
pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Assumes that ThreadEntry::list_mutex_ has already been locked before
|
||||
// creating a ThreadEntry object.
|
||||
ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
|
||||
: pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
|
||||
wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
|
||||
next_(ThreadEntry::list_), prev_(nullptr) {
|
||||
pthread_condattr_t attr;
|
||||
pthread_condattr_init(&attr);
|
||||
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
|
||||
pthread_cond_init(&wait_cond_, &attr);
|
||||
|
||||
// Add ourselves to the list.
|
||||
if (ThreadEntry::list_) {
|
||||
ThreadEntry::list_->prev_ = this;
|
||||
}
|
||||
ThreadEntry::list_ = this;
|
||||
}
|
||||
|
||||
ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
|
||||
pthread_mutex_lock(&ThreadEntry::list_mutex_);
|
||||
ThreadEntry* entry = list_;
|
||||
while (entry != nullptr) {
|
||||
if (entry->Match(pid, tid)) {
|
||||
break;
|
||||
}
|
||||
entry = entry->next_;
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
if (create) {
|
||||
entry = new ThreadEntry(pid, tid);
|
||||
}
|
||||
} else {
|
||||
entry->ref_count_++;
|
||||
}
|
||||
pthread_mutex_unlock(&ThreadEntry::list_mutex_);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void ThreadEntry::Remove(ThreadEntry* entry) {
|
||||
entry->Unlock();
|
||||
|
||||
pthread_mutex_lock(&ThreadEntry::list_mutex_);
|
||||
if (--entry->ref_count_ == 0) {
|
||||
delete entry;
|
||||
}
|
||||
pthread_mutex_unlock(&ThreadEntry::list_mutex_);
|
||||
}
|
||||
|
||||
// Assumes that ThreadEntry::list_mutex_ has already been locked before
|
||||
// deleting a ThreadEntry object.
|
||||
ThreadEntry::~ThreadEntry() {
|
||||
if (list_ == this) {
|
||||
list_ = next_;
|
||||
} else {
|
||||
if (next_) {
|
||||
next_->prev_ = prev_;
|
||||
}
|
||||
prev_->next_ = next_;
|
||||
}
|
||||
|
||||
next_ = nullptr;
|
||||
prev_ = nullptr;
|
||||
|
||||
pthread_cond_destroy(&wait_cond_);
|
||||
}
|
||||
|
||||
bool ThreadEntry::Wait(int value) {
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
ts.tv_sec += 5;
|
||||
|
||||
bool wait_completed = true;
|
||||
pthread_mutex_lock(&wait_mutex_);
|
||||
while (wait_value_ != value) {
|
||||
int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
|
||||
if (ret != 0) {
|
||||
BACK_ASYNC_SAFE_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
|
||||
wait_completed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&wait_mutex_);
|
||||
|
||||
return wait_completed;
|
||||
}
|
||||
|
||||
void ThreadEntry::Wake() {
|
||||
pthread_mutex_lock(&wait_mutex_);
|
||||
wait_value_++;
|
||||
pthread_mutex_unlock(&wait_mutex_);
|
||||
|
||||
pthread_cond_signal(&wait_cond_);
|
||||
}
|
||||
|
||||
void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
|
||||
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
|
||||
// The only thing the unwinder cares about is the mcontext data.
|
||||
memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_THREAD_ENTRY_H
|
||||
#define _LIBBACKTRACE_THREAD_ENTRY_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
class ThreadEntry {
|
||||
public:
|
||||
static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
|
||||
|
||||
static void Remove(ThreadEntry* entry);
|
||||
|
||||
void Wake();
|
||||
|
||||
bool Wait(int);
|
||||
|
||||
void CopyUcontextFromSigcontext(void*);
|
||||
|
||||
inline void Lock() {
|
||||
pthread_mutex_lock(&mutex_);
|
||||
|
||||
// Always reset the wait value since this could be the first or nth
|
||||
// time this entry is locked.
|
||||
wait_value_ = 0;
|
||||
}
|
||||
|
||||
inline void Unlock() {
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
inline ucontext_t* GetUcontext() { return &ucontext_; }
|
||||
|
||||
private:
|
||||
ThreadEntry(pid_t pid, pid_t tid);
|
||||
~ThreadEntry();
|
||||
|
||||
bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); }
|
||||
|
||||
pid_t pid_;
|
||||
pid_t tid_;
|
||||
int ref_count_;
|
||||
pthread_mutex_t mutex_;
|
||||
pthread_mutex_t wait_mutex_;
|
||||
pthread_cond_t wait_cond_;
|
||||
int wait_value_;
|
||||
ThreadEntry* next_;
|
||||
ThreadEntry* prev_;
|
||||
ucontext_t ucontext_;
|
||||
|
||||
static ThreadEntry* list_;
|
||||
static pthread_mutex_t list_mutex_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_THREAD_ENTRY_H
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include <libunwind.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "UnwindMap.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// libunwind has a single shared address space for the current process
|
||||
// aka local. If multiple maps are created for the current pid, then
|
||||
// only update the local address space once, and keep a reference count
|
||||
// of maps using the same map cursor.
|
||||
//-------------------------------------------------------------------------
|
||||
UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
|
||||
unw_map_cursor_clear(&map_cursor_);
|
||||
}
|
||||
|
||||
UnwindMapRemote::UnwindMapRemote(pid_t pid) : UnwindMap(pid) {
|
||||
}
|
||||
|
||||
UnwindMapRemote::~UnwindMapRemote() {
|
||||
unw_map_cursor_destroy(&map_cursor_);
|
||||
unw_map_cursor_clear(&map_cursor_);
|
||||
}
|
||||
|
||||
bool UnwindMapRemote::GenerateMap() {
|
||||
// Use the map_cursor information to construct the BacktraceMap data
|
||||
// rather than reparsing /proc/self/maps.
|
||||
unw_map_cursor_reset(&map_cursor_);
|
||||
|
||||
unw_map_t unw_map;
|
||||
while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) {
|
||||
backtrace_map_t map;
|
||||
|
||||
map.start = unw_map.start;
|
||||
map.end = unw_map.end;
|
||||
map.offset = unw_map.offset;
|
||||
map.load_bias = unw_map.load_base;
|
||||
map.flags = unw_map.flags;
|
||||
map.name = unw_map.path;
|
||||
|
||||
// The maps are in descending order, but we want them in ascending order.
|
||||
maps_.push_front(map);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnwindMapRemote::Build() {
|
||||
return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
|
||||
}
|
||||
|
||||
UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
|
||||
pthread_rwlock_init(&map_lock_, nullptr);
|
||||
}
|
||||
|
||||
UnwindMapLocal::~UnwindMapLocal() {
|
||||
if (map_created_) {
|
||||
unw_map_local_destroy();
|
||||
unw_map_cursor_clear(&map_cursor_);
|
||||
}
|
||||
}
|
||||
|
||||
bool UnwindMapLocal::GenerateMap() {
|
||||
// Lock so that multiple threads cannot modify the maps data at the
|
||||
// same time.
|
||||
pthread_rwlock_wrlock(&map_lock_);
|
||||
|
||||
// It's possible for the map to be regenerated while this loop is occurring.
|
||||
// If that happens, get the map again, but only try at most three times
|
||||
// before giving up.
|
||||
bool generated = false;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
maps_.clear();
|
||||
|
||||
// Save the map data retrieved so we can tell if it changes.
|
||||
unw_map_local_cursor_get(&map_cursor_);
|
||||
|
||||
unw_map_t unw_map;
|
||||
int ret;
|
||||
while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) {
|
||||
backtrace_map_t map;
|
||||
|
||||
map.start = unw_map.start;
|
||||
map.end = unw_map.end;
|
||||
map.offset = unw_map.offset;
|
||||
map.load_bias = unw_map.load_base;
|
||||
map.flags = unw_map.flags;
|
||||
map.name = unw_map.path;
|
||||
|
||||
free(unw_map.path);
|
||||
|
||||
// The maps are in descending order, but we want them in ascending order.
|
||||
maps_.push_front(map);
|
||||
}
|
||||
// Check to see if the map changed while getting the data.
|
||||
if (ret != -UNW_EINVAL) {
|
||||
generated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&map_lock_);
|
||||
|
||||
if (!generated) {
|
||||
BACK_LOGW("Unable to generate the map.");
|
||||
}
|
||||
return generated;
|
||||
}
|
||||
|
||||
bool UnwindMapLocal::Build() {
|
||||
return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
|
||||
}
|
||||
|
||||
void UnwindMapLocal::FillIn(uint64_t addr, backtrace_map_t* map) {
|
||||
BacktraceMap::FillIn(addr, map);
|
||||
if (!IsValid(*map)) {
|
||||
// Check to see if the underlying map changed and regenerate the map
|
||||
// if it did.
|
||||
if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
|
||||
if (GenerateMap()) {
|
||||
BacktraceMap::FillIn(addr, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWIND_MAP_H
|
||||
#define _LIBBACKTRACE_UNWIND_MAP_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
// The unw_map_cursor_t structure is different depending on whether it is
|
||||
// the local or remote version. In order to get the correct version, include
|
||||
// libunwind.h first then this header.
|
||||
|
||||
class UnwindMap : public BacktraceMap {
|
||||
public:
|
||||
explicit UnwindMap(pid_t pid);
|
||||
|
||||
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
|
||||
|
||||
protected:
|
||||
unw_map_cursor_t map_cursor_;
|
||||
};
|
||||
|
||||
class UnwindMapRemote : public UnwindMap {
|
||||
public:
|
||||
explicit UnwindMapRemote(pid_t pid);
|
||||
virtual ~UnwindMapRemote();
|
||||
|
||||
bool Build() override;
|
||||
|
||||
private:
|
||||
bool GenerateMap();
|
||||
};
|
||||
|
||||
class UnwindMapLocal : public UnwindMap {
|
||||
public:
|
||||
UnwindMapLocal();
|
||||
virtual ~UnwindMapLocal();
|
||||
|
||||
bool Build() override;
|
||||
|
||||
void FillIn(uint64_t addr, backtrace_map_t* map) override;
|
||||
|
||||
void LockIterator() override { pthread_rwlock_rdlock(&map_lock_); }
|
||||
void UnlockIterator() override { pthread_rwlock_unlock(&map_lock_); }
|
||||
|
||||
private:
|
||||
bool GenerateMap();
|
||||
|
||||
bool map_created_;
|
||||
|
||||
pthread_rwlock_t map_lock_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_MAP_H
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/RegsGetLocal.h>
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#endif
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "UnwindStack.h"
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
|
||||
|
||||
bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
||||
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
|
||||
std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
|
||||
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
|
||||
auto process_memory = stack_map->process_memory();
|
||||
unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
|
||||
regs, stack_map->process_memory());
|
||||
unwinder.SetResolveNames(stack_map->ResolveNames());
|
||||
stack_map->SetArch(regs->Arch());
|
||||
if (stack_map->GetJitDebug() != nullptr) {
|
||||
unwinder.SetJitDebug(stack_map->GetJitDebug());
|
||||
}
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
if (stack_map->GetDexFiles() != nullptr) {
|
||||
unwinder.SetDexFiles(stack_map->GetDexFiles());
|
||||
}
|
||||
#endif
|
||||
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
|
||||
if (error != nullptr) {
|
||||
switch (unwinder.LastErrorCode()) {
|
||||
case unwindstack::ERROR_NONE:
|
||||
error->error_code = BACKTRACE_UNWIND_NO_ERROR;
|
||||
break;
|
||||
|
||||
case unwindstack::ERROR_MEMORY_INVALID:
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED;
|
||||
error->error_info.addr = unwinder.LastErrorAddress();
|
||||
break;
|
||||
|
||||
case unwindstack::ERROR_UNWIND_INFO:
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_UNWIND_INFO;
|
||||
break;
|
||||
|
||||
case unwindstack::ERROR_UNSUPPORTED:
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
|
||||
break;
|
||||
|
||||
case unwindstack::ERROR_INVALID_MAP:
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
|
||||
break;
|
||||
|
||||
case unwindstack::ERROR_MAX_FRAMES_EXCEEDED:
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT;
|
||||
break;
|
||||
|
||||
case unwindstack::ERROR_REPEATED_FRAME:
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_REPEATED_FRAME;
|
||||
break;
|
||||
|
||||
case unwindstack::ERROR_INVALID_ELF:
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_INVALID_ELF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_ignore_frames >= unwinder.NumFrames()) {
|
||||
frames->resize(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto unwinder_frames = unwinder.frames();
|
||||
frames->resize(unwinder.NumFrames() - num_ignore_frames);
|
||||
size_t cur_frame = 0;
|
||||
for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++) {
|
||||
auto frame = &unwinder_frames[i];
|
||||
|
||||
backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
|
||||
|
||||
back_frame->num = cur_frame++;
|
||||
|
||||
back_frame->rel_pc = frame->rel_pc;
|
||||
back_frame->pc = frame->pc;
|
||||
back_frame->sp = frame->sp;
|
||||
|
||||
char* demangled_name = __cxa_demangle(frame->function_name.c_str(), nullptr, nullptr, nullptr);
|
||||
if (demangled_name != nullptr) {
|
||||
back_frame->func_name = demangled_name;
|
||||
free(demangled_name);
|
||||
} else {
|
||||
back_frame->func_name = frame->function_name;
|
||||
}
|
||||
back_frame->func_offset = frame->function_offset;
|
||||
|
||||
back_frame->map.name = frame->map_name;
|
||||
back_frame->map.start = frame->map_start;
|
||||
back_frame->map.end = frame->map_end;
|
||||
back_frame->map.offset = frame->map_elf_start_offset;
|
||||
back_frame->map.load_bias = frame->map_load_bias;
|
||||
back_frame->map.flags = frame->map_flags;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: BacktraceCurrent(pid, tid, map) {}
|
||||
|
||||
std::string UnwindStackCurrent::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
|
||||
return GetMap()->GetFunctionName(pc, offset);
|
||||
}
|
||||
|
||||
bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, void* ucontext) {
|
||||
std::unique_ptr<unwindstack::Regs> regs;
|
||||
if (ucontext == nullptr) {
|
||||
regs.reset(unwindstack::Regs::CreateFromLocal());
|
||||
// Fill in the registers from this function. Do it here to avoid
|
||||
// one extra function call appearing in the unwind.
|
||||
unwindstack::RegsGetLocal(regs.get());
|
||||
} else {
|
||||
regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
|
||||
}
|
||||
|
||||
std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
|
||||
if (!skip_frames_) {
|
||||
skip_names.clear();
|
||||
}
|
||||
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names, &error_);
|
||||
}
|
||||
|
||||
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: BacktracePtrace(pid, tid, map), memory_(unwindstack::Memory::CreateProcessMemory(pid)) {}
|
||||
|
||||
std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
|
||||
return GetMap()->GetFunctionName(pc, offset);
|
||||
}
|
||||
|
||||
bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, void* context) {
|
||||
std::unique_ptr<unwindstack::Regs> regs;
|
||||
if (context == nullptr) {
|
||||
regs.reset(unwindstack::Regs::RemoteGet(Tid()));
|
||||
} else {
|
||||
regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), context));
|
||||
}
|
||||
|
||||
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
|
||||
}
|
||||
|
||||
size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
|
||||
#if defined(__aarch64__)
|
||||
// Tagged pointer after Android R would lead top byte to have random values
|
||||
// https://source.android.com/devices/tech/debug/tagged-pointers
|
||||
addr &= (1ULL << 56) - 1;
|
||||
#endif
|
||||
return memory_->Read(addr, buffer, bytes);
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWIND_STACK_H
|
||||
#define _LIBBACKTRACE_UNWIND_STACK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "BacktraceCurrent.h"
|
||||
#include "BacktracePtrace.h"
|
||||
|
||||
class UnwindStackCurrent : public BacktraceCurrent {
|
||||
public:
|
||||
UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
|
||||
virtual ~UnwindStackCurrent() = default;
|
||||
|
||||
std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
|
||||
|
||||
bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) override;
|
||||
};
|
||||
|
||||
class UnwindStackPtrace : public BacktracePtrace {
|
||||
public:
|
||||
UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
|
||||
virtual ~UnwindStackPtrace() = default;
|
||||
|
||||
bool Unwind(size_t num_ignore_frames, void* context) override;
|
||||
|
||||
std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
|
||||
|
||||
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<unwindstack::Memory> memory_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_STACK_H
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
|
||||
|
||||
bool UnwindStackMap::Build() {
|
||||
if (pid_ == 0) {
|
||||
pid_ = getpid();
|
||||
stack_maps_.reset(new unwindstack::LocalMaps);
|
||||
} else {
|
||||
stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
|
||||
}
|
||||
|
||||
// Create the process memory object.
|
||||
process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
|
||||
|
||||
// Create a JitDebug object for getting jit unwind information.
|
||||
std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
|
||||
jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
|
||||
#endif
|
||||
|
||||
if (!stack_maps_->Parse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate through the maps and fill in the backtrace_map_t structure.
|
||||
for (const auto& map_info : *stack_maps_) {
|
||||
backtrace_map_t map;
|
||||
map.start = map_info->start;
|
||||
map.end = map_info->end;
|
||||
map.offset = map_info->offset;
|
||||
// Set to -1 so that it is demand loaded.
|
||||
map.load_bias = static_cast<uint64_t>(-1);
|
||||
map.flags = map_info->flags;
|
||||
map.name = map_info->name;
|
||||
|
||||
maps_.push_back(map);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnwindStackMap::FillIn(uint64_t addr, backtrace_map_t* map) {
|
||||
BacktraceMap::FillIn(addr, map);
|
||||
if (map->load_bias != static_cast<uint64_t>(-1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill in the load_bias.
|
||||
unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
|
||||
if (map_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
map->load_bias = map_info->GetLoadBias(process_memory_);
|
||||
}
|
||||
|
||||
uint64_t UnwindStackMap::GetLoadBias(size_t index) {
|
||||
if (index >= stack_maps_->Total()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unwindstack::MapInfo* map_info = stack_maps_->Get(index);
|
||||
if (map_info == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return map_info->GetLoadBias(process_memory_);
|
||||
}
|
||||
|
||||
std::string UnwindStackMap::GetFunctionName(uint64_t pc, uint64_t* offset) {
|
||||
*offset = 0;
|
||||
unwindstack::Maps* maps = stack_maps();
|
||||
|
||||
// Get the map for this
|
||||
unwindstack::MapInfo* map_info = maps->Find(pc);
|
||||
if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (arch_ == unwindstack::ARCH_UNKNOWN) {
|
||||
if (pid_ == getpid()) {
|
||||
arch_ = unwindstack::Regs::CurrentArch();
|
||||
} else {
|
||||
// Create a remote regs, to figure out the architecture.
|
||||
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid_));
|
||||
arch_ = regs->Arch();
|
||||
}
|
||||
}
|
||||
|
||||
unwindstack::Elf* elf = map_info->GetElf(process_memory(), arch_);
|
||||
|
||||
std::string name;
|
||||
uint64_t func_offset;
|
||||
if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
|
||||
return "";
|
||||
}
|
||||
*offset = func_offset;
|
||||
return name;
|
||||
}
|
||||
|
||||
std::shared_ptr<unwindstack::Memory> UnwindStackMap::GetProcessMemory() {
|
||||
return process_memory_;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// BacktraceMap create function.
|
||||
//-------------------------------------------------------------------------
|
||||
BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
|
||||
BacktraceMap* map;
|
||||
|
||||
if (uncached) {
|
||||
// Force use of the base class to parse the maps when this call is made.
|
||||
map = new BacktraceMap(pid);
|
||||
} else if (pid == getpid()) {
|
||||
map = new UnwindStackMap(0);
|
||||
} else {
|
||||
map = new UnwindStackMap(pid);
|
||||
}
|
||||
if (!map->Build()) {
|
||||
delete map;
|
||||
return nullptr;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
#define _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#endif
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
// Forward declarations.
|
||||
class UnwindDexFile;
|
||||
|
||||
class UnwindStackMap : public BacktraceMap {
|
||||
public:
|
||||
explicit UnwindStackMap(pid_t pid);
|
||||
~UnwindStackMap() = default;
|
||||
|
||||
bool Build() override;
|
||||
|
||||
void FillIn(uint64_t addr, backtrace_map_t* map) override;
|
||||
|
||||
virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset) override;
|
||||
virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() override final;
|
||||
|
||||
unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
|
||||
|
||||
const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
|
||||
|
||||
unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
|
||||
#endif
|
||||
|
||||
void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
|
||||
|
||||
protected:
|
||||
uint64_t GetLoadBias(size_t index) override;
|
||||
|
||||
std::unique_ptr<unwindstack::Maps> stack_maps_;
|
||||
std::shared_ptr<unwindstack::Memory> process_memory_;
|
||||
std::unique_ptr<unwindstack::JitDebug> jit_debug_;
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
std::unique_ptr<unwindstack::DexFiles> dex_files_;
|
||||
#endif
|
||||
|
||||
unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/threads.h>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
constexpr size_t kNumMaps = 2000;
|
||||
|
||||
static bool CountMaps(pid_t pid, size_t* num_maps) {
|
||||
// Minimize the calls that might allocate memory. If too much memory
|
||||
// gets allocated, then this routine will add extra maps and the next
|
||||
// call will fail to get the same number of maps as before.
|
||||
int fd =
|
||||
open((std::string("/proc/") + std::to_string(pid) + "/maps").c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Cannot open map file for pid %d: %s\n", pid, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
*num_maps = 0;
|
||||
while (true) {
|
||||
char buffer[2048];
|
||||
ssize_t bytes = read(fd, buffer, sizeof(buffer));
|
||||
if (bytes <= 0) {
|
||||
break;
|
||||
}
|
||||
// Count the '\n'.
|
||||
for (size_t i = 0; i < static_cast<size_t>(bytes); i++) {
|
||||
if (buffer[i] == '\n') {
|
||||
++*num_maps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CreateMap(benchmark::State& state, BacktraceMap* (*map_func)(pid_t, bool)) {
|
||||
// Create a remote process so that the map data is exactly the same.
|
||||
// Also, so that we can create a set number of maps.
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
size_t num_maps;
|
||||
if (!CountMaps(getpid(), &num_maps)) {
|
||||
exit(1);
|
||||
}
|
||||
// Create uniquely named maps.
|
||||
std::vector<void*> maps;
|
||||
for (size_t i = num_maps; i < kNumMaps; i++) {
|
||||
int flags = PROT_READ | PROT_WRITE;
|
||||
// Alternate page type to make sure a map entry is added for each call.
|
||||
if ((i % 2) == 0) {
|
||||
flags |= PROT_EXEC;
|
||||
}
|
||||
void* memory = mmap(nullptr, PAGE_SIZE, flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (memory == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to create map: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
memset(memory, 0x1, PAGE_SIZE);
|
||||
#if defined(PR_SET_VMA)
|
||||
if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") == -1) {
|
||||
fprintf(stderr, "Failed: %s\n", strerror(errno));
|
||||
}
|
||||
#endif
|
||||
maps.push_back(memory);
|
||||
}
|
||||
|
||||
if (!CountMaps(getpid(), &num_maps)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (num_maps < kNumMaps) {
|
||||
fprintf(stderr, "Maps set incorrectly: %zu found, %zu expected at least.\n", num_maps,
|
||||
kNumMaps);
|
||||
std::string str;
|
||||
android::base::ReadFileToString("/proc/self/maps", &str);
|
||||
fprintf(stderr, "%s\n", str.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Wait for an hour at most.
|
||||
sleep(3600);
|
||||
exit(1);
|
||||
} else if (pid < 0) {
|
||||
fprintf(stderr, "Fork failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
size_t num_maps = 0;
|
||||
for (size_t i = 0; i < 2000; i++) {
|
||||
if (CountMaps(pid, &num_maps) && num_maps >= kNumMaps) {
|
||||
break;
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
if (num_maps < kNumMaps) {
|
||||
fprintf(stderr, "Timed out waiting for the number of maps available: %zu\n", num_maps);
|
||||
return;
|
||||
}
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
BacktraceMap* map = map_func(pid, false);
|
||||
if (map == nullptr) {
|
||||
fprintf(stderr, "Failed to create map\n");
|
||||
return;
|
||||
}
|
||||
delete map;
|
||||
}
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
waitpid(pid, nullptr, 0);
|
||||
}
|
||||
|
||||
static void BM_create_map(benchmark::State& state) {
|
||||
CreateMap(state, BacktraceMap::Create);
|
||||
}
|
||||
BENCHMARK(BM_create_map);
|
||||
|
||||
using BacktraceCreateFn = decltype(Backtrace::Create);
|
||||
|
||||
static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
|
||||
while (state.KeepRunning()) {
|
||||
std::unique_ptr<Backtrace> backtrace(fn(getpid(), android::base::GetThreadId(), map));
|
||||
backtrace->Unwind(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void BM_create_backtrace(benchmark::State& state) {
|
||||
std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid()));
|
||||
CreateBacktrace(state, backtrace_map.get(), Backtrace::Create);
|
||||
}
|
||||
BENCHMARK(BM_create_backtrace);
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
|
||||
#define AT_COMMON_SIZES Arg(1)->Arg(4)->Arg(8)->Arg(16)->Arg(100)->Arg(200)->Arg(500)->Arg(1024)
|
||||
|
||||
static void Attach(pid_t pid) {
|
||||
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
|
||||
perror("Failed to attach");
|
||||
abort();
|
||||
}
|
||||
|
||||
siginfo_t si;
|
||||
// Wait for up to 5 seconds.
|
||||
for (size_t i = 0; i < 5000; i++) {
|
||||
if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
|
||||
return;
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
printf("Remote process failed to stop in five seconds.\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
class ScopedPidReaper {
|
||||
public:
|
||||
ScopedPidReaper(pid_t pid) : pid_(pid) {}
|
||||
~ScopedPidReaper() {
|
||||
kill(pid_, SIGKILL);
|
||||
waitpid(pid_, nullptr, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
pid_t pid_;
|
||||
};
|
||||
|
||||
static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
|
||||
struct iovec dst_iov = {
|
||||
.iov_base = dst, .iov_len = len,
|
||||
};
|
||||
|
||||
struct iovec src_iov = {
|
||||
.iov_base = reinterpret_cast<void*>(remote_src), .iov_len = len,
|
||||
};
|
||||
|
||||
ssize_t rc = process_vm_readv(pid, &dst_iov, 1, &src_iov, 1, 0);
|
||||
return rc == -1 ? 0 : rc;
|
||||
}
|
||||
|
||||
static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
|
||||
// ptrace() returns -1 and sets errno when the operation fails.
|
||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||
errno = 0;
|
||||
*value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
|
||||
if (*value == -1 && errno) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t PtraceRead(pid_t pid, uint64_t addr, void* dst, size_t bytes) {
|
||||
size_t bytes_read = 0;
|
||||
long data;
|
||||
for (size_t i = 0; i < bytes / sizeof(long); i++) {
|
||||
if (!PtraceReadLong(pid, addr, &data)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(dst, &data, sizeof(long));
|
||||
dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
|
||||
addr += sizeof(long);
|
||||
bytes_read += sizeof(long);
|
||||
}
|
||||
|
||||
size_t left_over = bytes & (sizeof(long) - 1);
|
||||
if (left_over) {
|
||||
if (!PtraceReadLong(pid, addr, &data)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(dst, &data, left_over);
|
||||
bytes_read += left_over;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static void CreateRemoteProcess(size_t size, void** map, pid_t* pid) {
|
||||
*map = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (*map == MAP_FAILED) {
|
||||
perror("Can't allocate memory");
|
||||
abort();
|
||||
}
|
||||
memset(*map, 0xaa, size);
|
||||
|
||||
if ((*pid = fork()) == 0) {
|
||||
for (volatile int i = 0;; i++)
|
||||
;
|
||||
exit(1);
|
||||
}
|
||||
if (*pid < 0) {
|
||||
perror("Failed to fork");
|
||||
abort();
|
||||
}
|
||||
Attach(*pid);
|
||||
// Don't need this map in the current process any more.
|
||||
munmap(*map, size);
|
||||
}
|
||||
|
||||
static void BM_read_with_ptrace(benchmark::State& state) {
|
||||
void* map;
|
||||
pid_t pid;
|
||||
CreateRemoteProcess(state.range(0), &map, &pid);
|
||||
ScopedPidReaper reap(pid);
|
||||
|
||||
std::vector<uint8_t> read_buffer(state.range(0));
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(map);
|
||||
while (state.KeepRunning()) {
|
||||
if (PtraceRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
|
||||
printf("Unexpected bad read.\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
||||
}
|
||||
BENCHMARK(BM_read_with_ptrace)->AT_COMMON_SIZES;
|
||||
|
||||
static void BM_read_with_process_vm_read(benchmark::State& state) {
|
||||
void* map;
|
||||
pid_t pid;
|
||||
CreateRemoteProcess(state.range(0), &map, &pid);
|
||||
ScopedPidReaper reap(pid);
|
||||
|
||||
std::vector<uint8_t> read_buffer(state.range(0));
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(map);
|
||||
while (state.KeepRunning()) {
|
||||
if (ProcessVmRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
|
||||
printf("Unexpected bad read.\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
||||
}
|
||||
BENCHMARK(BM_read_with_process_vm_read)->AT_COMMON_SIZES;
|
||||
|
||||
static void BM_read_with_backtrace_object(benchmark::State& state) {
|
||||
void* map;
|
||||
pid_t pid;
|
||||
CreateRemoteProcess(state.range(0), &map, &pid);
|
||||
ScopedPidReaper reap(pid);
|
||||
|
||||
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
|
||||
if (backtrace.get() == nullptr) {
|
||||
printf("Failed to create backtrace.\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(map);
|
||||
std::vector<uint8_t> read_buffer(state.range(0));
|
||||
while (state.KeepRunning()) {
|
||||
if (backtrace->Read(addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
|
||||
printf("Unexpected bad read.\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
||||
}
|
||||
BENCHMARK(BM_read_with_backtrace_object)->AT_COMMON_SIZES;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/RegsGetLocal.h>
|
||||
|
||||
#include "backtrace_testlib.h"
|
||||
|
||||
void test_loop_forever() {
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
void test_signal_handler(int) { test_loop_forever(); }
|
||||
|
||||
void test_signal_action(int, siginfo_t*, void*) { test_loop_forever(); }
|
||||
|
||||
int test_level_four(int one, int two, int three, int four, void (*callback_func)(void*),
|
||||
void* data) {
|
||||
if (callback_func != NULL) {
|
||||
callback_func(data);
|
||||
} else {
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
return one + two + three + four;
|
||||
}
|
||||
|
||||
int test_level_three(int one, int two, int three, int four, void (*callback_func)(void*),
|
||||
void* data) {
|
||||
return test_level_four(one + 3, two + 6, three + 9, four + 12, callback_func, data) + 3;
|
||||
}
|
||||
|
||||
int test_level_two(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
|
||||
return test_level_three(one + 2, two + 4, three + 6, four + 8, callback_func, data) + 2;
|
||||
}
|
||||
|
||||
int test_level_one(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
|
||||
return test_level_two(one + 1, two + 2, three + 3, four + 4, callback_func, data) + 1;
|
||||
}
|
||||
|
||||
int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
|
||||
if (level > 0) {
|
||||
return test_recursive_call(level - 1, callback_func, data) + level;
|
||||
} else if (callback_func != NULL) {
|
||||
callback_func(data);
|
||||
} else {
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
std::vector<uint8_t>* ucontext;
|
||||
volatile int* exit_flag;
|
||||
} GetContextArg;
|
||||
|
||||
static void GetContextAndExit(void* data) {
|
||||
GetContextArg* arg = reinterpret_cast<GetContextArg*>(data);
|
||||
|
||||
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
|
||||
unwindstack::RegsGetLocal(regs.get());
|
||||
|
||||
ucontext_t ucontext;
|
||||
memset(&ucontext, 0, sizeof(ucontext));
|
||||
#if defined(__arm__)
|
||||
memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint32_t) * 16);
|
||||
#elif defined(__aarch64__)
|
||||
memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint64_t) * 33);
|
||||
#elif defined(__i386__)
|
||||
uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
|
||||
ucontext.uc_mcontext.gregs[0] = reg_data[15];
|
||||
ucontext.uc_mcontext.gregs[1] = reg_data[14];
|
||||
ucontext.uc_mcontext.gregs[2] = reg_data[13];
|
||||
ucontext.uc_mcontext.gregs[3] = reg_data[12];
|
||||
ucontext.uc_mcontext.gregs[4] = reg_data[7];
|
||||
ucontext.uc_mcontext.gregs[5] = reg_data[6];
|
||||
ucontext.uc_mcontext.gregs[6] = reg_data[5];
|
||||
ucontext.uc_mcontext.gregs[7] = reg_data[4];
|
||||
ucontext.uc_mcontext.gregs[8] = reg_data[3];
|
||||
ucontext.uc_mcontext.gregs[9] = reg_data[2];
|
||||
ucontext.uc_mcontext.gregs[10] = reg_data[1];
|
||||
ucontext.uc_mcontext.gregs[11] = reg_data[0];
|
||||
ucontext.uc_mcontext.gregs[14] = reg_data[8];
|
||||
ucontext.uc_mcontext.gregs[15] = reg_data[10];
|
||||
#elif defined(__x86_64__)
|
||||
uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
|
||||
ucontext.uc_mcontext.gregs[0] = reg_data[8];
|
||||
ucontext.uc_mcontext.gregs[1] = reg_data[9];
|
||||
ucontext.uc_mcontext.gregs[2] = reg_data[10];
|
||||
ucontext.uc_mcontext.gregs[3] = reg_data[11];
|
||||
ucontext.uc_mcontext.gregs[4] = reg_data[12];
|
||||
ucontext.uc_mcontext.gregs[5] = reg_data[13];
|
||||
ucontext.uc_mcontext.gregs[6] = reg_data[14];
|
||||
ucontext.uc_mcontext.gregs[7] = reg_data[15];
|
||||
ucontext.uc_mcontext.gregs[8] = reg_data[5];
|
||||
ucontext.uc_mcontext.gregs[9] = reg_data[4];
|
||||
ucontext.uc_mcontext.gregs[10] = reg_data[6];
|
||||
ucontext.uc_mcontext.gregs[11] = reg_data[3];
|
||||
ucontext.uc_mcontext.gregs[12] = reg_data[1];
|
||||
ucontext.uc_mcontext.gregs[13] = reg_data[0];
|
||||
ucontext.uc_mcontext.gregs[14] = reg_data[2];
|
||||
ucontext.uc_mcontext.gregs[15] = reg_data[7];
|
||||
ucontext.uc_mcontext.gregs[16] = reg_data[16];
|
||||
#endif
|
||||
|
||||
arg->ucontext->resize(sizeof(ucontext));
|
||||
memcpy(arg->ucontext->data(), &ucontext, sizeof(ucontext));
|
||||
|
||||
// Don't touch the stack anymore.
|
||||
while (*arg->exit_flag == 0) {
|
||||
}
|
||||
}
|
||||
|
||||
void test_get_context_and_wait(void* ucontext, volatile int* exit_flag) {
|
||||
GetContextArg arg;
|
||||
arg.ucontext = reinterpret_cast<std::vector<uint8_t>*>(ucontext);
|
||||
arg.exit_flag = exit_flag;
|
||||
test_level_one(1, 2, 3, 4, GetContextAndExit, &arg);
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_BACKTRACE_TESTLIB_H
|
||||
#define _LIBBACKTRACE_BACKTRACE_TESTLIB_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
void test_loop_forever();
|
||||
void test_signal_handler(int);
|
||||
void test_signal_action(int, siginfo_t*, void*);
|
||||
int test_level_four(int, int, int, int, void (*)(void*), void*);
|
||||
int test_level_three(int, int, int, int, void (*)(void*), void*);
|
||||
int test_level_two(int, int, int, int, void (*)(void*), void*);
|
||||
int test_level_one(int, int, int, int, void (*)(void*), void*);
|
||||
int test_recursive_call(int, void (*)(void*), void*);
|
||||
void test_get_context_and_wait(void*, volatile int*);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif // _LIBBACKTRACE_BACKTRACE_TESTLIB_H
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BACKTRACE_BACKTRACE_H
|
||||
#define _BACKTRACE_BACKTRACE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <backtrace/backtrace_constants.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define PRIPTR "016" PRIx64
|
||||
typedef uint64_t word_t;
|
||||
#else
|
||||
#define PRIPTR "08" PRIx64
|
||||
typedef uint32_t word_t;
|
||||
#endif
|
||||
|
||||
enum BacktraceUnwindErrorCode : uint32_t {
|
||||
BACKTRACE_UNWIND_NO_ERROR,
|
||||
// Something failed while trying to perform the setup to begin the unwind.
|
||||
BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
|
||||
// There is no map information to use with the unwind.
|
||||
BACKTRACE_UNWIND_ERROR_MAP_MISSING,
|
||||
// An error occurred that indicates a programming error.
|
||||
BACKTRACE_UNWIND_ERROR_INTERNAL,
|
||||
// The thread to unwind has disappeared before the unwind can begin.
|
||||
BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST,
|
||||
// The thread to unwind has not responded to a signal in a timely manner.
|
||||
BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT,
|
||||
// Attempt to do an unsupported operation.
|
||||
BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
|
||||
// Attempt to do an offline unwind without a context.
|
||||
BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
|
||||
// The count of frames exceed MAX_BACKTRACE_FRAMES.
|
||||
BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT,
|
||||
// Failed to read memory.
|
||||
BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED,
|
||||
// Failed to read registers.
|
||||
BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED,
|
||||
// Failed to find a function in debug sections.
|
||||
BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED,
|
||||
// Failed to execute dwarf instructions in debug sections.
|
||||
BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED,
|
||||
// Unwind information is incorrect.
|
||||
BACKTRACE_UNWIND_ERROR_UNWIND_INFO,
|
||||
// Unwind information stopped due to sp/pc repeating.
|
||||
BACKTRACE_UNWIND_ERROR_REPEATED_FRAME,
|
||||
// Unwind information stopped due to invalid elf.
|
||||
BACKTRACE_UNWIND_ERROR_INVALID_ELF,
|
||||
};
|
||||
|
||||
struct BacktraceUnwindError {
|
||||
enum BacktraceUnwindErrorCode error_code;
|
||||
|
||||
union {
|
||||
// for BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED
|
||||
uint64_t addr;
|
||||
// for BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED
|
||||
uint64_t regno;
|
||||
} error_info;
|
||||
|
||||
BacktraceUnwindError() : error_code(BACKTRACE_UNWIND_NO_ERROR) {}
|
||||
};
|
||||
|
||||
struct backtrace_frame_data_t {
|
||||
size_t num; // The current fame number.
|
||||
uint64_t pc; // The absolute pc.
|
||||
uint64_t rel_pc; // The relative pc.
|
||||
uint64_t sp; // The top of the stack.
|
||||
size_t stack_size; // The size of the stack, zero indicate an unknown stack size.
|
||||
backtrace_map_t map; // The map associated with the given pc.
|
||||
std::string func_name; // The function name associated with this pc, NULL if not found.
|
||||
uint64_t func_offset; // pc relative to the start of the function, only valid if func_name is not
|
||||
// NULL.
|
||||
};
|
||||
|
||||
struct backtrace_stackinfo_t {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
const uint8_t* data;
|
||||
};
|
||||
|
||||
namespace unwindstack {
|
||||
class Regs;
|
||||
}
|
||||
|
||||
class Backtrace {
|
||||
public:
|
||||
enum ArchEnum : uint8_t {
|
||||
ARCH_ARM,
|
||||
ARCH_ARM64,
|
||||
ARCH_X86,
|
||||
ARCH_X86_64,
|
||||
};
|
||||
|
||||
static void SetGlobalElfCache(bool enable);
|
||||
|
||||
// Create the correct Backtrace object based on what is to be unwound.
|
||||
// If pid < 0 or equals the current pid, then the Backtrace object
|
||||
// corresponds to the current process.
|
||||
// If pid < 0 or equals the current pid and tid >= 0, then the Backtrace
|
||||
// object corresponds to a thread in the current process.
|
||||
// If pid >= 0 and tid < 0, then the Backtrace object corresponds to a
|
||||
// different process.
|
||||
// Tracing a thread in a different process is not supported.
|
||||
// If map is NULL, then create the map and manage it internally.
|
||||
// If map is not NULL, the map is still owned by the caller.
|
||||
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
|
||||
|
||||
virtual ~Backtrace();
|
||||
|
||||
// Get the current stack trace and store in the backtrace_ structure.
|
||||
virtual bool Unwind(size_t num_ignore_frames, void* context = nullptr) = 0;
|
||||
|
||||
static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
||||
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
|
||||
std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
|
||||
|
||||
// Get the function name and offset into the function given the pc.
|
||||
// If the string is empty, then no valid function name was found,
|
||||
// or the pc is not in any valid map.
|
||||
virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset,
|
||||
const backtrace_map_t* map = nullptr);
|
||||
|
||||
// Fill in the map data associated with the given pc.
|
||||
virtual void FillInMap(uint64_t pc, backtrace_map_t* map);
|
||||
|
||||
// Read the data at a specific address.
|
||||
virtual bool ReadWord(uint64_t ptr, word_t* out_value) = 0;
|
||||
|
||||
// Read arbitrary data from a specific address. If a read request would
|
||||
// span from one map to another, this call only reads up until the end
|
||||
// of the current map.
|
||||
// Returns the total number of bytes actually read.
|
||||
virtual size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) = 0;
|
||||
|
||||
// Create a string representing the formatted line of backtrace information
|
||||
// for a single frame.
|
||||
virtual std::string FormatFrameData(size_t frame_num);
|
||||
static std::string FormatFrameData(const backtrace_frame_data_t* frame);
|
||||
|
||||
pid_t Pid() const { return pid_; }
|
||||
pid_t Tid() const { return tid_; }
|
||||
size_t NumFrames() const { return frames_.size(); }
|
||||
|
||||
const backtrace_frame_data_t* GetFrame(size_t frame_num) {
|
||||
if (frame_num >= frames_.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &frames_[frame_num];
|
||||
}
|
||||
|
||||
typedef std::vector<backtrace_frame_data_t>::iterator iterator;
|
||||
iterator begin() { return frames_.begin(); }
|
||||
iterator end() { return frames_.end(); }
|
||||
|
||||
typedef std::vector<backtrace_frame_data_t>::const_iterator const_iterator;
|
||||
const_iterator begin() const { return frames_.begin(); }
|
||||
const_iterator end() const { return frames_.end(); }
|
||||
|
||||
BacktraceMap* GetMap() { return map_; }
|
||||
|
||||
BacktraceUnwindError GetError() { return error_; }
|
||||
|
||||
std::string GetErrorString(BacktraceUnwindError error);
|
||||
|
||||
// Set whether to skip frames in libbacktrace/libunwindstack when doing a local unwind.
|
||||
void SetSkipFrames(bool skip_frames) { skip_frames_ = skip_frames; }
|
||||
|
||||
protected:
|
||||
Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
|
||||
|
||||
// The name returned is not demangled, GetFunctionName() takes care of
|
||||
// demangling the name.
|
||||
virtual std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) = 0;
|
||||
|
||||
virtual bool VerifyReadWordArgs(uint64_t ptr, word_t* out_value);
|
||||
|
||||
bool BuildMap();
|
||||
|
||||
pid_t pid_;
|
||||
pid_t tid_;
|
||||
|
||||
BacktraceMap* map_;
|
||||
bool map_shared_;
|
||||
|
||||
std::vector<backtrace_frame_data_t> frames_;
|
||||
|
||||
// Skip frames in libbacktrace/libunwindstack when doing a local unwind.
|
||||
bool skip_frames_ = true;
|
||||
|
||||
BacktraceUnwindError error_;
|
||||
};
|
||||
|
||||
#endif // _BACKTRACE_BACKTRACE_H
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BACKTRACE_BACKTRACE_MAP_H
|
||||
#define _BACKTRACE_BACKTRACE_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef _WIN32
|
||||
// MINGW does not define these constants.
|
||||
#define PROT_NONE 0
|
||||
#define PROT_READ 0x1
|
||||
#define PROT_WRITE 0x2
|
||||
#define PROT_EXEC 0x4
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward declaration.
|
||||
struct backtrace_stackinfo_t;
|
||||
|
||||
// Special flag to indicate a map is in /dev/. However, a map in
|
||||
// /dev/ashmem/... does not set this flag.
|
||||
static constexpr int PROT_DEVICE_MAP = 0x8000;
|
||||
// Special flag to indicate that this map represents an elf file
|
||||
// created by ART for use with the gdb jit debug interface.
|
||||
// This should only ever appear in offline maps data.
|
||||
static constexpr int PROT_JIT_SYMFILE_MAP = 0x4000;
|
||||
|
||||
struct backtrace_map_t {
|
||||
uint64_t start = 0;
|
||||
uint64_t end = 0;
|
||||
uint64_t offset = 0;
|
||||
uint64_t load_bias = 0;
|
||||
int flags = 0;
|
||||
std::string name;
|
||||
|
||||
// Returns `name` if non-empty, or `<anonymous:0x...>` otherwise.
|
||||
std::string Name() const;
|
||||
};
|
||||
|
||||
namespace unwindstack {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
class BacktraceMap {
|
||||
public:
|
||||
// If uncached is true, then parse the current process map as of the call.
|
||||
// Passing a map created with uncached set to true to Backtrace::Create()
|
||||
// is unsupported.
|
||||
static BacktraceMap* Create(pid_t pid, bool uncached = false);
|
||||
|
||||
virtual ~BacktraceMap();
|
||||
|
||||
class iterator : public std::iterator<std::bidirectional_iterator_tag, backtrace_map_t*> {
|
||||
public:
|
||||
iterator(BacktraceMap* map, size_t index) : map_(map), index_(index) {}
|
||||
|
||||
iterator& operator++() {
|
||||
index_++;
|
||||
return *this;
|
||||
}
|
||||
const iterator operator++(int increment) {
|
||||
index_ += increment;
|
||||
return *this;
|
||||
}
|
||||
iterator& operator--() {
|
||||
index_--;
|
||||
return *this;
|
||||
}
|
||||
const iterator operator--(int decrement) {
|
||||
index_ -= decrement;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
|
||||
bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
|
||||
|
||||
const backtrace_map_t* operator*() {
|
||||
if (index_ >= map_->size()) {
|
||||
return nullptr;
|
||||
}
|
||||
backtrace_map_t* map = &map_->maps_[index_];
|
||||
if (map->load_bias == static_cast<uint64_t>(-1)) {
|
||||
map->load_bias = map_->GetLoadBias(index_);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private:
|
||||
BacktraceMap* map_ = nullptr;
|
||||
size_t index_ = 0;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
iterator end() { return iterator(this, maps_.size()); }
|
||||
|
||||
// Fill in the map data structure for the given address.
|
||||
virtual void FillIn(uint64_t addr, backtrace_map_t* map);
|
||||
|
||||
// Only supported with the new unwinder.
|
||||
virtual std::string GetFunctionName(uint64_t /*pc*/, uint64_t* /*offset*/) { return ""; }
|
||||
virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() { return nullptr; }
|
||||
|
||||
// The flags returned are the same flags as used by the mmap call.
|
||||
// The values are PROT_*.
|
||||
int GetFlags(uint64_t pc) {
|
||||
backtrace_map_t map;
|
||||
FillIn(pc, &map);
|
||||
if (IsValid(map)) {
|
||||
return map.flags;
|
||||
}
|
||||
return PROT_NONE;
|
||||
}
|
||||
|
||||
bool IsReadable(uint64_t pc) { return GetFlags(pc) & PROT_READ; }
|
||||
bool IsWritable(uint64_t pc) { return GetFlags(pc) & PROT_WRITE; }
|
||||
bool IsExecutable(uint64_t pc) { return GetFlags(pc) & PROT_EXEC; }
|
||||
|
||||
// In order to use the iterators on this object, a caller must
|
||||
// call the LockIterator and UnlockIterator function to guarantee
|
||||
// that the data does not change while it's being used.
|
||||
virtual void LockIterator() {}
|
||||
virtual void UnlockIterator() {}
|
||||
|
||||
size_t size() const { return maps_.size(); }
|
||||
|
||||
virtual bool Build();
|
||||
|
||||
static inline bool IsValid(const backtrace_map_t& map) {
|
||||
return map.end > 0;
|
||||
}
|
||||
|
||||
void SetSuffixesToIgnore(std::vector<std::string> suffixes) {
|
||||
suffixes_to_ignore_.insert(suffixes_to_ignore_.end(), suffixes.begin(), suffixes.end());
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetSuffixesToIgnore() { return suffixes_to_ignore_; }
|
||||
|
||||
// Disabling the resolving of names results in the function name being
|
||||
// set to an empty string and the function offset being set to zero
|
||||
// in the frame data when unwinding.
|
||||
void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
|
||||
|
||||
bool ResolveNames() { return resolve_names_; }
|
||||
|
||||
protected:
|
||||
BacktraceMap(pid_t pid);
|
||||
|
||||
virtual uint64_t GetLoadBias(size_t /* index */) { return 0; }
|
||||
|
||||
pid_t pid_;
|
||||
std::deque<backtrace_map_t> maps_;
|
||||
std::vector<std::string> suffixes_to_ignore_;
|
||||
bool resolve_names_ = true;
|
||||
};
|
||||
|
||||
class ScopedBacktraceMapIteratorLock {
|
||||
public:
|
||||
explicit ScopedBacktraceMapIteratorLock(BacktraceMap* map) : map_(map) {
|
||||
map->LockIterator();
|
||||
}
|
||||
|
||||
~ScopedBacktraceMapIteratorLock() {
|
||||
map_->UnlockIterator();
|
||||
}
|
||||
|
||||
private:
|
||||
BacktraceMap* map_;
|
||||
};
|
||||
|
||||
#endif // _BACKTRACE_BACKTRACE_MAP_H
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BACKTRACE_BACKTRACE_CONSTANTS_H
|
||||
#define _BACKTRACE_BACKTRACE_CONSTANTS_H
|
||||
|
||||
// When the pid to be traced is set to this value, then trace the current
|
||||
// process. If the tid value is not BACKTRACE_NO_TID, then the specified
|
||||
// thread from the current process will be traced.
|
||||
#define BACKTRACE_CURRENT_PROCESS (-1)
|
||||
// When the tid to be traced is set to this value, then trace the specified
|
||||
// current thread of the specified pid.
|
||||
#define BACKTRACE_CURRENT_THREAD (-1)
|
||||
|
||||
#define MAX_BACKTRACE_FRAMES 256
|
||||
|
||||
#endif // _BACKTRACE_BACKTRACE_CONSTANTS_H
|
||||
BIN
libbacktrace/testdata/arm/libGLESv2_adreno.so
vendored
BIN
libbacktrace/testdata/arm/libGLESv2_adreno.so
vendored
Binary file not shown.
BIN
libbacktrace/testdata/arm/libandroid_runtime.so
vendored
BIN
libbacktrace/testdata/arm/libandroid_runtime.so
vendored
Binary file not shown.
BIN
libbacktrace/testdata/arm/libart.so
vendored
BIN
libbacktrace/testdata/arm/libart.so
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
105
libbacktrace/testdata/arm/offline_testdata
vendored
105
libbacktrace/testdata/arm/offline_testdata
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +0,0 @@
|
|||
pid: 7288 tid: 31656
|
||||
ucontext: 104 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f017cc00000000356241cc0000000000000000
|
||||
map: start: cc361000 end: cc758000 offset: 0 load_bias: 9000 flags: 5 name: /vendor/lib/egl/libGLESv2_adreno.so
|
||||
stack: start: cc17f234 end: cc17f258 size: 36 0000000000000000000000000000000000000000000000000000000000000000b36141cc
|
||||
function: start: be1f0 end: be304 name: EsxContext::Clear(unsigned int, unsigned int, unsigned int, EsxClearValues*)
|
||||
function: start: be058 end: be1f0 name: EsxContext::ClearBuffersForDebug()
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
pid: 7288 tid: 31656
|
||||
ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003069fed80000000049dcf6f10000000000000000
|
||||
map: start: f1f10000 end: f2049000 offset: 0 load_bias: 10000 flags: 5 name: /system/lib/libandroid_runtime.so
|
||||
stack: start: d8fe6948 end: d8fe6958 size: 16 000000000000000000000000e7dcf6f1
|
||||
function: start: 6dbf9 end: 6dce5 name: android::AndroidRuntime::javaThreadShell
|
||||
function: start: 6dce5 end: 6dd79 name: android::AndroidRuntime::javaCreateThreadEtc
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
pid: 32232 tid: 32233
|
||||
ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e90000000000000000
|
||||
map: start: e9380000 end: e9766000 offset: 0 load_bias: b000 flags: 5 name: /system/lib/libart.so
|
||||
stack: start: ffd12dc0 end: ffd1306c size: 684 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d89524
|
||||
function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal
|
||||
function: start: 3a66a5 end: 3a6787 name: art_quick_invoke_static_stub
|
||||
function: start: a7129 end: a72f1 name: art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
|
||||
function: start: 2fbd35 end: 2fc789 name: art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)
|
||||
function: start: 2fcf75 end: 2fd88d name: art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)
|
||||
function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
|
||||
BIN
libbacktrace/testdata/arm64/eglSubDriverAndroid.so
vendored
BIN
libbacktrace/testdata/arm64/eglSubDriverAndroid.so
vendored
Binary file not shown.
Binary file not shown.
BIN
libbacktrace/testdata/arm64/libskia.so
vendored
BIN
libbacktrace/testdata/arm64/libskia.so
vendored
Binary file not shown.
107
libbacktrace/testdata/arm64/offline_testdata
vendored
107
libbacktrace/testdata/arm64/offline_testdata
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +0,0 @@
|
|||
pid: 12276 tid: 12303
|
||||
map: start: 7b8c01e000 end: 7b8c030000 offset: 0 load_bias: 0 flags: 5 name: /vendor/lib64/egl/eglSubDriverAndroid.so
|
||||
ucontext: 464 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004070158c7b00000000000000000000001070158c7b000000647f028c7b00000000000000000000000000000000000000
|
||||
stack: start: 7b8c157020 end: 7b8c157050 size: 48 00000000000000000000000000000000000000000000000000000000000000000000000000000000547e028c7b000000
|
||||
function: start: 9ed8 end: a1b0 name: EglAndroidWindowSurface::Initialize(EglAndroidConfig*, int const*)
|
||||
function: start: 9dcc end: 9ed8 name: EglAndroidWindowSurface::Create(ANativeWindow*, EglAndroidConfig*, EglAndroidWindowSurface**, int const*)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
pid: 32232 tid: 32233
|
||||
map: start: 7c24c80000 end: 7c25413000 offset: 0 load_bias: 5f000 flags: 5 name: /system/lib64/libskia.so
|
||||
ucontext: 464 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b04b158c7b0000000000000000000000504b158c7b0000000c9a18257c00000000000000000000000000000000000000
|
||||
stack: start: 7b8c154b80 end: 7b8c154bc0 size: 64 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec43f2247c000000
|
||||
function: start: 568970 end: 568c08 name: SkScalerContext_FreeType::generateImage(SkGlyph const&)
|
||||
function: start: 30330c end: 3044b0 name: SkScalerContext::getImage(SkGlyph const&)
|
||||
Binary file not shown.
Binary file not shown.
82
libbacktrace/testdata/x86/offline_testdata
vendored
82
libbacktrace/testdata/x86/offline_testdata
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
93
libbacktrace/testdata/x86_64/offline_testdata
vendored
93
libbacktrace/testdata/x86_64/offline_testdata
vendored
File diff suppressed because one or more lines are too long
1
libprocinfo
Symbolic link
1
libprocinfo
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../libprocinfo
|
||||
|
|
@ -1 +0,0 @@
|
|||
../.clang-format-2
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2015 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "libprocinfo_defaults",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libprocinfo",
|
||||
defaults: ["libprocinfo_defaults"],
|
||||
vendor_available: true,
|
||||
// TODO(b/153609531): remove when no longer needed.
|
||||
native_bridge_supported: true,
|
||||
recovery_available: true,
|
||||
vndk: {
|
||||
enabled: true,
|
||||
},
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"process.cpp",
|
||||
"process_map.cpp",
|
||||
],
|
||||
|
||||
local_include_dirs: ["include"],
|
||||
export_include_dirs: ["include"],
|
||||
shared_libs: ["libbase"],
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
linux_bionic: {
|
||||
enabled: true,
|
||||
},
|
||||
windows: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.art.debug",
|
||||
"com.android.art.release",
|
||||
],
|
||||
}
|
||||
|
||||
// Tests
|
||||
// ------------------------------------------------------------------------------
|
||||
cc_test {
|
||||
name: "libprocinfo_test",
|
||||
defaults: ["libprocinfo_defaults"],
|
||||
host_supported: true,
|
||||
isolated: true,
|
||||
srcs: [
|
||||
"process_test.cpp",
|
||||
"process_map_test.cpp",
|
||||
],
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
windows: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libprocinfo",
|
||||
],
|
||||
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
|
||||
data: [
|
||||
"testdata/*",
|
||||
],
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
}
|
||||
|
||||
cc_benchmark {
|
||||
name: "libprocinfo_benchmark",
|
||||
defaults: ["libprocinfo_defaults"],
|
||||
srcs: [
|
||||
"process_map_benchmark.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbacktrace",
|
||||
"libbase",
|
||||
"libprocinfo",
|
||||
"libunwindstack",
|
||||
],
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
|
||||
data: [
|
||||
"testdata/*",
|
||||
],
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
jmgao@google.com
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
namespace android {
|
||||
namespace procinfo {
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
enum ProcessState {
|
||||
kProcessStateUnknown,
|
||||
kProcessStateRunning,
|
||||
kProcessStateSleeping,
|
||||
kProcessStateUninterruptibleWait,
|
||||
kProcessStateStopped,
|
||||
kProcessStateZombie,
|
||||
};
|
||||
|
||||
struct ProcessInfo {
|
||||
std::string name;
|
||||
ProcessState state;
|
||||
pid_t tid;
|
||||
pid_t pid;
|
||||
pid_t ppid;
|
||||
pid_t tracer;
|
||||
uid_t uid;
|
||||
uid_t gid;
|
||||
};
|
||||
|
||||
// Parse the contents of /proc/<tid>/status into |process_info|.
|
||||
bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error = nullptr);
|
||||
|
||||
// Parse the contents of <fd>/status into |process_info|.
|
||||
// |fd| should be an fd pointing at a /proc/<pid> directory.
|
||||
bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error = nullptr);
|
||||
|
||||
// Fetch the list of threads from a given process's /proc/<pid> directory.
|
||||
// |fd| should be an fd pointing at a /proc/<pid> directory.
|
||||
template <typename Collection>
|
||||
auto GetProcessTidsFromProcPidFd(int fd, Collection* out, std::string* error = nullptr) ->
|
||||
typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
|
||||
out->clear();
|
||||
|
||||
int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
|
||||
std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
|
||||
if (!dir) {
|
||||
if (error != nullptr) {
|
||||
*error = "failed to open task directory";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent* dent;
|
||||
while ((dent = readdir(dir.get()))) {
|
||||
if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
|
||||
pid_t tid;
|
||||
if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
|
||||
if (error != nullptr) {
|
||||
*error = std::string("failed to parse task id: ") + dent->d_name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
out->insert(out->end(), tid);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Collection>
|
||||
auto GetProcessTids(pid_t pid, Collection* out, std::string* error = nullptr) ->
|
||||
typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
|
||||
char task_path[PATH_MAX];
|
||||
if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
|
||||
if (error != nullptr) {
|
||||
*error = "task path overflow (pid = " + std::to_string(pid) + ")";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
|
||||
if (fd == -1) {
|
||||
if (error != nullptr) {
|
||||
*error = std::string("failed to open ") + task_path;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetProcessTidsFromProcPidFd(fd.get(), out, error);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} /* namespace procinfo */
|
||||
} /* namespace android */
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
namespace android {
|
||||
namespace procinfo {
|
||||
|
||||
template <class CallbackType>
|
||||
bool ReadMapFileContent(char* content, const CallbackType& callback) {
|
||||
uint64_t start_addr;
|
||||
uint64_t end_addr;
|
||||
uint16_t flags;
|
||||
uint64_t pgoff;
|
||||
ino_t inode;
|
||||
char* next_line = content;
|
||||
char* p;
|
||||
|
||||
auto pass_space = [&]() {
|
||||
if (*p != ' ') {
|
||||
return false;
|
||||
}
|
||||
while (*p == ' ') {
|
||||
p++;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
auto pass_xdigit = [&]() {
|
||||
if (!isxdigit(*p)) {
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
p++;
|
||||
} while (isxdigit(*p));
|
||||
return true;
|
||||
};
|
||||
|
||||
while (next_line != nullptr && *next_line != '\0') {
|
||||
p = next_line;
|
||||
next_line = strchr(next_line, '\n');
|
||||
if (next_line != nullptr) {
|
||||
*next_line = '\0';
|
||||
next_line++;
|
||||
}
|
||||
// Parse line like: 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
|
||||
char* end;
|
||||
// start_addr
|
||||
start_addr = strtoull(p, &end, 16);
|
||||
if (end == p || *end != '-') {
|
||||
return false;
|
||||
}
|
||||
p = end + 1;
|
||||
// end_addr
|
||||
end_addr = strtoull(p, &end, 16);
|
||||
if (end == p) {
|
||||
return false;
|
||||
}
|
||||
p = end;
|
||||
if (!pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// flags
|
||||
flags = 0;
|
||||
if (*p == 'r') {
|
||||
flags |= PROT_READ;
|
||||
} else if (*p != '-') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (*p == 'w') {
|
||||
flags |= PROT_WRITE;
|
||||
} else if (*p != '-') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (*p == 'x') {
|
||||
flags |= PROT_EXEC;
|
||||
} else if (*p != '-') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (*p != 'p' && *p != 's') {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
if (!pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// pgoff
|
||||
pgoff = strtoull(p, &end, 16);
|
||||
if (end == p) {
|
||||
return false;
|
||||
}
|
||||
p = end;
|
||||
if (!pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// major:minor
|
||||
if (!pass_xdigit() || *p++ != ':' || !pass_xdigit() || !pass_space()) {
|
||||
return false;
|
||||
}
|
||||
// inode
|
||||
inode = strtoull(p, &end, 10);
|
||||
if (end == p) {
|
||||
return false;
|
||||
}
|
||||
p = end;
|
||||
|
||||
if (*p != '\0' && !pass_space()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// filename
|
||||
callback(start_addr, end_addr, flags, pgoff, inode, p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ReadMapFile(const std::string& map_file,
|
||||
const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
|
||||
const char*)>& callback) {
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString(map_file, &content)) {
|
||||
return false;
|
||||
}
|
||||
return ReadMapFileContent(&content[0], callback);
|
||||
}
|
||||
|
||||
inline bool ReadProcessMaps(pid_t pid,
|
||||
const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
|
||||
const char*)>& callback) {
|
||||
return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
|
||||
}
|
||||
|
||||
struct MapInfo {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint16_t flags;
|
||||
uint64_t pgoff;
|
||||
ino_t inode;
|
||||
std::string name;
|
||||
|
||||
MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
|
||||
const char* name)
|
||||
: start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
|
||||
};
|
||||
|
||||
inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
|
||||
return ReadProcessMaps(
|
||||
pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
|
||||
const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
|
||||
}
|
||||
|
||||
bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
|
||||
const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
|
||||
const char*)>& callback);
|
||||
|
||||
} /* namespace procinfo */
|
||||
} /* namespace android */
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <procinfo/process.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace android {
|
||||
namespace procinfo {
|
||||
|
||||
bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error) {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/proc/%d", tid);
|
||||
|
||||
unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
|
||||
if (dirfd == -1) {
|
||||
if (error != nullptr) {
|
||||
*error = std::string("failed to open ") + path;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetProcessInfoFromProcPidFd(dirfd.get(), process_info, error);
|
||||
}
|
||||
|
||||
static ProcessState parse_state(const char* state) {
|
||||
switch (*state) {
|
||||
case 'R':
|
||||
return kProcessStateRunning;
|
||||
case 'S':
|
||||
return kProcessStateSleeping;
|
||||
case 'D':
|
||||
return kProcessStateUninterruptibleWait;
|
||||
case 'T':
|
||||
return kProcessStateStopped;
|
||||
case 'Z':
|
||||
return kProcessStateZombie;
|
||||
default:
|
||||
return kProcessStateUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error) {
|
||||
int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
if (status_fd == -1) {
|
||||
if (error != nullptr) {
|
||||
*error = "failed to open status fd in GetProcessInfoFromProcPidFd";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
|
||||
if (!fp) {
|
||||
if (error != nullptr) {
|
||||
*error = "failed to open status file in GetProcessInfoFromProcPidFd";
|
||||
}
|
||||
close(status_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
int field_bitmap = 0;
|
||||
static constexpr int finished_bitmap = 255;
|
||||
char* line = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) {
|
||||
char* tab = strchr(line, '\t');
|
||||
if (tab == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t header_len = tab - line;
|
||||
std::string header = std::string(line, header_len);
|
||||
if (header == "Name:") {
|
||||
std::string name = line + header_len + 1;
|
||||
|
||||
// line includes the trailing newline.
|
||||
name.pop_back();
|
||||
process_info->name = std::move(name);
|
||||
|
||||
field_bitmap |= 1;
|
||||
} else if (header == "Pid:") {
|
||||
process_info->tid = atoi(tab + 1);
|
||||
field_bitmap |= 2;
|
||||
} else if (header == "Tgid:") {
|
||||
process_info->pid = atoi(tab + 1);
|
||||
field_bitmap |= 4;
|
||||
} else if (header == "PPid:") {
|
||||
process_info->ppid = atoi(tab + 1);
|
||||
field_bitmap |= 8;
|
||||
} else if (header == "TracerPid:") {
|
||||
process_info->tracer = atoi(tab + 1);
|
||||
field_bitmap |= 16;
|
||||
} else if (header == "Uid:") {
|
||||
process_info->uid = atoi(tab + 1);
|
||||
field_bitmap |= 32;
|
||||
} else if (header == "Gid:") {
|
||||
process_info->gid = atoi(tab + 1);
|
||||
field_bitmap |= 64;
|
||||
} else if (header == "State:") {
|
||||
process_info->state = parse_state(tab + 1);
|
||||
field_bitmap |= 128;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
return field_bitmap == finished_bitmap;
|
||||
}
|
||||
|
||||
} /* namespace procinfo */
|
||||
} /* namespace android */
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <procinfo/process.h>
|
||||
|
||||
namespace android {
|
||||
namespace procinfo {
|
||||
|
||||
bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
|
||||
const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
|
||||
const char*)>& callback) {
|
||||
if (buffer == nullptr || buffer_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = open(map_file, O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* char_buffer = reinterpret_cast<char*>(buffer);
|
||||
size_t start = 0;
|
||||
size_t read_bytes = 0;
|
||||
char* line = nullptr;
|
||||
bool read_complete = false;
|
||||
while (true) {
|
||||
ssize_t bytes =
|
||||
TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
|
||||
if (bytes <= 0) {
|
||||
if (read_bytes == 0) {
|
||||
close(fd);
|
||||
return bytes == 0;
|
||||
}
|
||||
// Treat the last piece of data as the last line.
|
||||
char_buffer[start + read_bytes] = '\n';
|
||||
bytes = 1;
|
||||
read_complete = true;
|
||||
}
|
||||
read_bytes += bytes;
|
||||
|
||||
while (read_bytes > 0) {
|
||||
char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
|
||||
if (newline == nullptr) {
|
||||
break;
|
||||
}
|
||||
*newline = '\0';
|
||||
line = &char_buffer[start];
|
||||
start = newline - char_buffer + 1;
|
||||
read_bytes -= newline - line + 1;
|
||||
|
||||
// Ignore the return code, errors are okay.
|
||||
ReadMapFileContent(line, callback);
|
||||
}
|
||||
|
||||
if (read_complete) {
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (start == 0 && read_bytes == buffer_size - 1) {
|
||||
// The buffer provided is too small to contain this line, give up
|
||||
// and indicate failure.
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy any leftover data to the front of the buffer.
|
||||
if (start > 0) {
|
||||
if (read_bytes > 0) {
|
||||
memmove(char_buffer, &char_buffer[start], read_bytes);
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace procinfo */
|
||||
} /* namespace android */
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
static void BM_ReadMapFile(benchmark::State& state) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
for (auto _ : state) {
|
||||
std::vector<android::procinfo::MapInfo> maps;
|
||||
android::procinfo::ReadMapFile(map_file, [&](uint64_t start, uint64_t end, uint16_t flags,
|
||||
uint64_t pgoff, ino_t inode, const char* name) {
|
||||
maps.emplace_back(start, end, flags, pgoff, inode, name);
|
||||
});
|
||||
CHECK_EQ(maps.size(), 2043u);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ReadMapFile);
|
||||
|
||||
static void BM_unwindstack_FileMaps(benchmark::State& state) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
for (auto _ : state) {
|
||||
unwindstack::FileMaps maps(map_file);
|
||||
maps.Parse();
|
||||
CHECK_EQ(maps.Total(), 2043u);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_unwindstack_FileMaps);
|
||||
|
||||
static void BM_unwindstack_BufferMaps(benchmark::State& state) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
std::string content;
|
||||
CHECK(android::base::ReadFileToString(map_file, &content));
|
||||
for (auto _ : state) {
|
||||
unwindstack::BufferMaps maps(content.c_str());
|
||||
maps.Parse();
|
||||
CHECK_EQ(maps.Total(), 2043u);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_unwindstack_BufferMaps);
|
||||
|
||||
static void BM_backtrace_BacktraceMap(benchmark::State& state) {
|
||||
pid_t pid = getpid();
|
||||
for (auto _ : state) {
|
||||
BacktraceMap* map = BacktraceMap::Create(pid, true);
|
||||
CHECK(map != nullptr);
|
||||
delete map;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_backtrace_BacktraceMap);
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -1,282 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(process_map, ReadMapFile) {
|
||||
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
|
||||
std::vector<android::procinfo::MapInfo> maps;
|
||||
ASSERT_TRUE(android::procinfo::ReadMapFile(
|
||||
map_file,
|
||||
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
|
||||
const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
|
||||
ASSERT_EQ(2043u, maps.size());
|
||||
ASSERT_EQ(maps[0].start, 0x12c00000ULL);
|
||||
ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
|
||||
ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
|
||||
ASSERT_EQ(maps[0].pgoff, 0ULL);
|
||||
ASSERT_EQ(maps[0].inode, 10267643UL);
|
||||
ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
|
||||
ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
|
||||
ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
|
||||
ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
|
||||
ASSERT_EQ(maps[876].pgoff, 0ULL);
|
||||
ASSERT_EQ(maps[876].inode, 2407UL);
|
||||
ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
|
||||
ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
|
||||
ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
|
||||
ASSERT_EQ(maps[1260].flags, PROT_READ);
|
||||
ASSERT_EQ(maps[1260].pgoff, 0ULL);
|
||||
ASSERT_EQ(maps[1260].inode, 10266154UL);
|
||||
ASSERT_EQ(maps[1260].name,
|
||||
"[anon:dalvik-classes.dex extracted in memory from "
|
||||
"/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
|
||||
}
|
||||
|
||||
TEST(process_map, ReadProcessMaps) {
|
||||
std::vector<android::procinfo::MapInfo> maps;
|
||||
ASSERT_TRUE(android::procinfo::ReadProcessMaps(
|
||||
getpid(),
|
||||
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
|
||||
const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
|
||||
ASSERT_GT(maps.size(), 0u);
|
||||
maps.clear();
|
||||
ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
|
||||
ASSERT_GT(maps.size(), 0u);
|
||||
}
|
||||
|
||||
extern "C" void malloc_disable();
|
||||
extern "C" void malloc_enable();
|
||||
|
||||
struct TestMapInfo {
|
||||
TestMapInfo() = default;
|
||||
TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
|
||||
const char* new_name)
|
||||
: start(start), end(end), flags(flags), pgoff(pgoff), inode(inode) {
|
||||
strcpy(name, new_name);
|
||||
}
|
||||
uint64_t start = 0;
|
||||
uint64_t end = 0;
|
||||
uint16_t flags = 0;
|
||||
uint64_t pgoff = 0;
|
||||
ino_t inode = 0;
|
||||
char name[100] = {};
|
||||
};
|
||||
|
||||
void VerifyReadMapFileAsyncSafe(const char* maps_data,
|
||||
const std::vector<TestMapInfo>& expected_info) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
|
||||
|
||||
std::vector<TestMapInfo> saved_info(expected_info.size());
|
||||
size_t num_maps = 0;
|
||||
|
||||
auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
|
||||
const char* name) {
|
||||
if (num_maps != saved_info.size()) {
|
||||
TestMapInfo& saved = saved_info[num_maps];
|
||||
saved.start = start;
|
||||
saved.end = end;
|
||||
saved.flags = flags;
|
||||
saved.pgoff = pgoff;
|
||||
saved.inode = inode;
|
||||
strcpy(saved.name, name);
|
||||
}
|
||||
num_maps++;
|
||||
};
|
||||
|
||||
std::vector<char> buffer(64 * 1024);
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
// Any allocations will block after this call.
|
||||
malloc_disable();
|
||||
#endif
|
||||
|
||||
bool parsed =
|
||||
android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
malloc_enable();
|
||||
#endif
|
||||
|
||||
ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
|
||||
ASSERT_EQ(expected_info.size(), num_maps);
|
||||
for (size_t i = 0; i < expected_info.size(); i++) {
|
||||
const TestMapInfo& expected = expected_info[i];
|
||||
const TestMapInfo& saved = saved_info[i];
|
||||
EXPECT_EQ(expected.start, saved.start);
|
||||
EXPECT_EQ(expected.end, saved.end);
|
||||
EXPECT_EQ(expected.flags, saved.flags);
|
||||
EXPECT_EQ(expected.pgoff, saved.pgoff);
|
||||
EXPECT_EQ(expected.inode, saved.inode);
|
||||
EXPECT_STREQ(expected.name, saved.name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_invalid) {
|
||||
std::vector<TestMapInfo> expected_info;
|
||||
|
||||
VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_single) {
|
||||
std::vector<TestMapInfo> expected_info;
|
||||
expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
|
||||
"/lib/fake.so");
|
||||
|
||||
VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
|
||||
expected_info);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
|
||||
std::vector<TestMapInfo> expected_info;
|
||||
expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
|
||||
"/lib/fake.so");
|
||||
|
||||
VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
|
||||
expected_info);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
|
||||
std::vector<TestMapInfo> expected_info;
|
||||
expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "");
|
||||
|
||||
VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_multiple) {
|
||||
std::vector<TestMapInfo> expected_info;
|
||||
expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "");
|
||||
expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so");
|
||||
expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so");
|
||||
expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]");
|
||||
|
||||
std::string map_data =
|
||||
"0a0000-0c0000 rwxp 00000001 00:05 100\n"
|
||||
"0d0000-0e0000 r--p 00000002 00:05 101 /lib/libsomething1.so\n"
|
||||
"0f0000-100000 -w-p 00000003 00:05 102 /lib/libsomething2.so\n"
|
||||
"110000-120000 --xp 00000004 00:05 103 [anon:something or another]\n";
|
||||
|
||||
VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
|
||||
std::vector<TestMapInfo> expected_info;
|
||||
std::string map_data;
|
||||
uint64_t start = 0xa0000;
|
||||
for (size_t i = 0; i < 10000; i++) {
|
||||
map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
|
||||
start, start + 0x1000, i, 1000 + i);
|
||||
expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so");
|
||||
}
|
||||
|
||||
VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
|
||||
size_t num_calls = 0;
|
||||
auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
// Any allocations will block after this call.
|
||||
malloc_disable();
|
||||
#endif
|
||||
|
||||
bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
malloc_enable();
|
||||
#endif
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
EXPECT_EQ(0UL, num_calls);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
|
||||
size_t num_calls = 0;
|
||||
auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
// Any allocations will block after this call.
|
||||
malloc_disable();
|
||||
#endif
|
||||
|
||||
char buffer[10];
|
||||
bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
malloc_enable();
|
||||
#endif
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
EXPECT_EQ(0UL, num_calls);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
|
||||
size_t num_calls = 0;
|
||||
auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
// Any allocations will block after this call.
|
||||
malloc_disable();
|
||||
#endif
|
||||
|
||||
char buffer[10];
|
||||
bool parsed =
|
||||
android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
malloc_enable();
|
||||
#endif
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
EXPECT_EQ(0UL, num_calls);
|
||||
}
|
||||
|
||||
TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(android::base::WriteStringToFd(
|
||||
"0a0000-0c0000 rwxp 00000001 00:05 100 /fake/lib.so\n", tf.fd));
|
||||
|
||||
size_t num_calls = 0;
|
||||
auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
// Any allocations will block after this call.
|
||||
malloc_disable();
|
||||
#endif
|
||||
|
||||
char buffer[39];
|
||||
bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
malloc_enable();
|
||||
#endif
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
EXPECT_EQ(0UL, num_calls);
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <procinfo/process.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#if !defined(__BIONIC__)
|
||||
#include <syscall.h>
|
||||
static pid_t gettid() {
|
||||
return syscall(__NR_gettid);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(process_info, process_info_smoke) {
|
||||
android::procinfo::ProcessInfo self;
|
||||
ASSERT_TRUE(android::procinfo::GetProcessInfo(gettid(), &self));
|
||||
ASSERT_EQ(gettid(), self.tid);
|
||||
ASSERT_EQ(getpid(), self.pid);
|
||||
ASSERT_EQ(getppid(), self.ppid);
|
||||
ASSERT_EQ(getuid(), self.uid);
|
||||
ASSERT_EQ(getgid(), self.gid);
|
||||
}
|
||||
|
||||
TEST(process_info, process_info_proc_pid_fd_smoke) {
|
||||
android::procinfo::ProcessInfo self;
|
||||
int fd = open(android::base::StringPrintf("/proc/%d", gettid()).c_str(), O_DIRECTORY | O_RDONLY);
|
||||
ASSERT_NE(-1, fd);
|
||||
ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, &self));
|
||||
|
||||
// Process name is capped at 15 bytes.
|
||||
ASSERT_EQ("libprocinfo_tes", self.name);
|
||||
ASSERT_EQ(gettid(), self.tid);
|
||||
ASSERT_EQ(getpid(), self.pid);
|
||||
ASSERT_EQ(getppid(), self.ppid);
|
||||
ASSERT_EQ(getuid(), self.uid);
|
||||
ASSERT_EQ(getgid(), self.gid);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(process_info, process_tids_smoke) {
|
||||
pid_t main_tid = gettid();
|
||||
std::thread([main_tid]() {
|
||||
pid_t thread_tid = gettid();
|
||||
|
||||
{
|
||||
std::vector<pid_t> vec;
|
||||
ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &vec));
|
||||
ASSERT_EQ(1, std::count(vec.begin(), vec.end(), main_tid));
|
||||
ASSERT_EQ(1, std::count(vec.begin(), vec.end(), thread_tid));
|
||||
}
|
||||
|
||||
{
|
||||
std::set<pid_t> set;
|
||||
ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &set));
|
||||
ASSERT_EQ(1, std::count(set.begin(), set.end(), main_tid));
|
||||
ASSERT_EQ(1, std::count(set.begin(), set.end(), thread_tid));
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
|
||||
TEST(process_info, process_state) {
|
||||
int pipefd[2];
|
||||
ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
|
||||
pid_t forkpid = fork();
|
||||
|
||||
ASSERT_NE(-1, forkpid);
|
||||
if (forkpid == 0) {
|
||||
close(pipefd[1]);
|
||||
char buf;
|
||||
TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1));
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
// Give the child some time to get to the read.
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
android::procinfo::ProcessInfo procinfo;
|
||||
ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
|
||||
ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state);
|
||||
|
||||
ASSERT_EQ(0, kill(forkpid, SIGKILL));
|
||||
|
||||
// Give the kernel some time to kill the child.
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
|
||||
ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state);
|
||||
|
||||
ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
|
||||
}
|
||||
2043
libprocinfo/testdata/maps
vendored
2043
libprocinfo/testdata/maps
vendored
File diff suppressed because it is too large
Load diff
1
libunwindstack
Symbolic link
1
libunwindstack
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../unwinding/libunwindstack
|
||||
|
|
@ -1 +0,0 @@
|
|||
../.clang-format-2
|
||||
|
|
@ -1,496 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2017 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "libunwindstack_flags",
|
||||
|
||||
host_supported: true,
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
],
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
linux_bionic: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "libunwindstack_defaults",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
srcs: [
|
||||
"ArmExidx.cpp",
|
||||
"DexFiles.cpp",
|
||||
"DwarfCfa.cpp",
|
||||
"DwarfEhFrameWithHdr.cpp",
|
||||
"DwarfMemory.cpp",
|
||||
"DwarfOp.cpp",
|
||||
"DwarfSection.cpp",
|
||||
"Elf.cpp",
|
||||
"ElfInterface.cpp",
|
||||
"ElfInterfaceArm.cpp",
|
||||
"Global.cpp",
|
||||
"JitDebug.cpp",
|
||||
"Log.cpp",
|
||||
"MapInfo.cpp",
|
||||
"Maps.cpp",
|
||||
"Memory.cpp",
|
||||
"MemoryMte.cpp",
|
||||
"LocalUnwinder.cpp",
|
||||
"Regs.cpp",
|
||||
"RegsArm.cpp",
|
||||
"RegsArm64.cpp",
|
||||
"RegsX86.cpp",
|
||||
"RegsX86_64.cpp",
|
||||
"RegsMips.cpp",
|
||||
"RegsMips64.cpp",
|
||||
"Unwinder.cpp",
|
||||
"Symbols.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wexit-time-destructors",
|
||||
],
|
||||
|
||||
target: {
|
||||
host: {
|
||||
// Always disable optimizations for host to make it easier to debug.
|
||||
cflags: [
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
},
|
||||
android: {
|
||||
header_libs: ["bionic_libc_platform_headers"],
|
||||
product_variables: {
|
||||
experimental_mte: {
|
||||
cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
|
||||
},
|
||||
},
|
||||
},
|
||||
linux_bionic: {
|
||||
header_libs: ["bionic_libc_platform_headers"],
|
||||
product_variables: {
|
||||
experimental_mte: {
|
||||
cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
arch: {
|
||||
x86: {
|
||||
srcs: ["AsmGetRegsX86.S"],
|
||||
},
|
||||
x86_64: {
|
||||
srcs: ["AsmGetRegsX86_64.S"],
|
||||
},
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libprocinfo",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"liblzma",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libunwindstack",
|
||||
vendor_available: true,
|
||||
recovery_available: true,
|
||||
// TODO(b/153609531): remove when no longer needed.
|
||||
native_bridge_supported: true,
|
||||
vndk: {
|
||||
enabled: true,
|
||||
support_system_process: true,
|
||||
},
|
||||
defaults: ["libunwindstack_defaults"],
|
||||
srcs: ["DexFile.cpp"],
|
||||
cflags: ["-DDEXFILE_SUPPORT"],
|
||||
shared_libs: ["libdexfile_support"],
|
||||
|
||||
target: {
|
||||
vendor: {
|
||||
cflags: ["-UDEXFILE_SUPPORT"],
|
||||
exclude_srcs: ["DexFile.cpp"],
|
||||
exclude_shared_libs: ["libdexfile_support"],
|
||||
},
|
||||
recovery: {
|
||||
cflags: ["-UDEXFILE_SUPPORT"],
|
||||
exclude_srcs: ["DexFile.cpp"],
|
||||
exclude_shared_libs: ["libdexfile_support"],
|
||||
},
|
||||
native_bridge: {
|
||||
cflags: ["-UDEXFILE_SUPPORT"],
|
||||
exclude_srcs: ["DexFile.cpp"],
|
||||
exclude_shared_libs: ["libdexfile_support"],
|
||||
},
|
||||
},
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.art.debug",
|
||||
"com.android.art.release",
|
||||
],
|
||||
}
|
||||
|
||||
// Static library without DEX support to avoid dependencies on the ART APEX.
|
||||
cc_library_static {
|
||||
name: "libunwindstack_no_dex",
|
||||
recovery_available: true,
|
||||
defaults: ["libunwindstack_defaults"],
|
||||
|
||||
visibility: [
|
||||
"//external/gwp_asan",
|
||||
"//system/core/debuggerd",
|
||||
"//system/core/init",
|
||||
"//system/core/libbacktrace",
|
||||
// Temporary add the new path in preparation for migration.
|
||||
"//system/unwinding/libbacktrace",
|
||||
],
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.runtime",
|
||||
],
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Unit Tests
|
||||
//-------------------------------------------------------------------------
|
||||
cc_test_library {
|
||||
name: "libunwindstack_local",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
srcs: ["tests/TestLocal.cpp"],
|
||||
|
||||
cflags: [
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libunwindstack",
|
||||
],
|
||||
relative_install_path: "libunwindstack_test",
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "libunwindstack_testlib_flags",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
srcs: [
|
||||
"tests/ArmExidxDecodeTest.cpp",
|
||||
"tests/ArmExidxExtractTest.cpp",
|
||||
"tests/DexFileTest.cpp",
|
||||
"tests/DexFilesTest.cpp",
|
||||
"tests/DwarfCfaLogTest.cpp",
|
||||
"tests/DwarfCfaTest.cpp",
|
||||
"tests/DwarfDebugFrameTest.cpp",
|
||||
"tests/DwarfEhFrameTest.cpp",
|
||||
"tests/DwarfEhFrameWithHdrTest.cpp",
|
||||
"tests/DwarfMemoryTest.cpp",
|
||||
"tests/DwarfOpLogTest.cpp",
|
||||
"tests/DwarfOpTest.cpp",
|
||||
"tests/DwarfSectionTest.cpp",
|
||||
"tests/DwarfSectionImplTest.cpp",
|
||||
"tests/ElfCacheTest.cpp",
|
||||
"tests/ElfFake.cpp",
|
||||
"tests/ElfInterfaceArmTest.cpp",
|
||||
"tests/ElfInterfaceTest.cpp",
|
||||
"tests/ElfTest.cpp",
|
||||
"tests/ElfTestUtils.cpp",
|
||||
"tests/IsolatedSettings.cpp",
|
||||
"tests/JitDebugTest.cpp",
|
||||
"tests/LocalUpdatableMapsTest.cpp",
|
||||
"tests/LogFake.cpp",
|
||||
"tests/MapInfoCreateMemoryTest.cpp",
|
||||
"tests/MapInfoGetBuildIDTest.cpp",
|
||||
"tests/MapInfoGetElfTest.cpp",
|
||||
"tests/MapInfoGetLoadBiasTest.cpp",
|
||||
"tests/MapInfoTest.cpp",
|
||||
"tests/MapsTest.cpp",
|
||||
"tests/MemoryBufferTest.cpp",
|
||||
"tests/MemoryCacheTest.cpp",
|
||||
"tests/MemoryFake.cpp",
|
||||
"tests/MemoryFileTest.cpp",
|
||||
"tests/MemoryLocalTest.cpp",
|
||||
"tests/MemoryOfflineBufferTest.cpp",
|
||||
"tests/MemoryOfflineTest.cpp",
|
||||
"tests/MemoryRangeTest.cpp",
|
||||
"tests/MemoryRangesTest.cpp",
|
||||
"tests/MemoryRemoteTest.cpp",
|
||||
"tests/MemoryTest.cpp",
|
||||
"tests/MemoryMteTest.cpp",
|
||||
"tests/RegsInfoTest.cpp",
|
||||
"tests/RegsIterateTest.cpp",
|
||||
"tests/RegsStepIfSignalHandlerTest.cpp",
|
||||
"tests/RegsTest.cpp",
|
||||
"tests/SymbolsTest.cpp",
|
||||
"tests/TestUtils.cpp",
|
||||
"tests/UnwindOfflineTest.cpp",
|
||||
"tests/UnwindTest.cpp",
|
||||
"tests/UnwinderTest.cpp",
|
||||
"tests/VerifyBionicTerminationTest.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"liblzma",
|
||||
"libunwindstack",
|
||||
"libdexfile_support",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
],
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
data: [
|
||||
"tests/files/elf32.xz",
|
||||
"tests/files/elf64.xz",
|
||||
"tests/files/offline/art_quick_osr_stub_arm/*",
|
||||
"tests/files/offline/bad_eh_frame_hdr_arm64/*",
|
||||
"tests/files/offline/debug_frame_first_x86/*",
|
||||
"tests/files/offline/debug_frame_load_bias_arm/*",
|
||||
"tests/files/offline/eh_frame_bias_x86/*",
|
||||
"tests/files/offline/eh_frame_hdr_begin_x86_64/*",
|
||||
"tests/files/offline/empty_arm64/*",
|
||||
"tests/files/offline/invalid_elf_offset_arm/*",
|
||||
"tests/files/offline/jit_debug_arm/*",
|
||||
"tests/files/offline/jit_debug_x86/*",
|
||||
"tests/files/offline/jit_map_arm/*",
|
||||
"tests/files/offline/gnu_debugdata_arm/*",
|
||||
"tests/files/offline/load_bias_different_section_bias_arm64/*",
|
||||
"tests/files/offline/load_bias_ro_rx_x86_64/*",
|
||||
"tests/files/offline/offset_arm/*",
|
||||
"tests/files/offline/shared_lib_in_apk_arm64/*",
|
||||
"tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
|
||||
"tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
|
||||
"tests/files/offline/signal_load_bias_arm/*",
|
||||
"tests/files/offline/signal_fde_x86/*",
|
||||
"tests/files/offline/signal_fde_x86_64/*",
|
||||
"tests/files/offline/straddle_arm/*",
|
||||
"tests/files/offline/straddle_arm64/*",
|
||||
],
|
||||
|
||||
target: {
|
||||
android: {
|
||||
header_libs: ["bionic_libc_platform_headers"],
|
||||
product_variables: {
|
||||
experimental_mte: {
|
||||
cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
|
||||
},
|
||||
},
|
||||
},
|
||||
linux_bionic: {
|
||||
header_libs: ["bionic_libc_platform_headers"],
|
||||
product_variables: {
|
||||
experimental_mte: {
|
||||
cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libunwindstack_test",
|
||||
defaults: ["libunwindstack_testlib_flags"],
|
||||
isolated: true,
|
||||
|
||||
srcs: [
|
||||
"tests/LocalUnwinderTest.cpp",
|
||||
],
|
||||
required: [
|
||||
"libunwindstack_local",
|
||||
],
|
||||
}
|
||||
|
||||
// Skip LocalUnwinderTest until atest understands required properly.
|
||||
cc_test {
|
||||
name: "libunwindstack_unit_test",
|
||||
defaults: ["libunwindstack_testlib_flags"],
|
||||
isolated: true,
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Fuzzers
|
||||
//-------------------------------------------------------------------------
|
||||
cc_defaults {
|
||||
name: "libunwindstack_fuzz_defaults",
|
||||
host_supported: true,
|
||||
defaults: ["libunwindstack_flags"],
|
||||
cflags: [
|
||||
"-Wno-exit-time-destructors",
|
||||
"-g",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"liblzma",
|
||||
"libunwindstack",
|
||||
"libdexfile_support",
|
||||
],
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "libunwindstack_fuzz_unwinder",
|
||||
defaults: ["libunwindstack_fuzz_defaults"],
|
||||
srcs: [
|
||||
"tests/MemoryFake.cpp",
|
||||
"tests/ElfFake.cpp",
|
||||
"tests/fuzz/UnwinderComponentCreator.cpp",
|
||||
"tests/fuzz/UnwinderFuzz.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Tools
|
||||
//-------------------------------------------------------------------------
|
||||
cc_defaults {
|
||||
name: "libunwindstack_tools",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
shared_libs: [
|
||||
"libunwindstack",
|
||||
"libbase",
|
||||
"liblzma",
|
||||
],
|
||||
target: {
|
||||
// Always disable optimizations for host to make it easier to debug.
|
||||
host: {
|
||||
cflags: [
|
||||
"-O0",
|
||||
"-g",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "unwind",
|
||||
defaults: ["libunwindstack_tools"],
|
||||
|
||||
srcs: [
|
||||
"tools/unwind.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "unwind_info",
|
||||
defaults: ["libunwindstack_tools"],
|
||||
|
||||
srcs: [
|
||||
"tools/unwind_info.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "unwind_symbols",
|
||||
defaults: ["libunwindstack_tools"],
|
||||
|
||||
srcs: [
|
||||
"tools/unwind_symbols.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "unwind_for_offline",
|
||||
defaults: ["libunwindstack_tools"],
|
||||
|
||||
srcs: [
|
||||
"tools/unwind_for_offline.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "unwind_reg_info",
|
||||
defaults: ["libunwindstack_tools"],
|
||||
|
||||
srcs: [
|
||||
"tools/unwind_reg_info.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Benchmarks
|
||||
//-------------------------------------------------------------------------
|
||||
cc_benchmark {
|
||||
name: "unwind_benchmarks",
|
||||
host_supported: true,
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
// Disable optimizations so that all of the calls are not optimized away.
|
||||
cflags: [
|
||||
"-O0",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"benchmarks/unwind_benchmarks.cpp",
|
||||
"benchmarks/ElfBenchmark.cpp",
|
||||
"benchmarks/MapsBenchmark.cpp",
|
||||
"benchmarks/SymbolBenchmark.cpp",
|
||||
"benchmarks/Utils.cpp",
|
||||
],
|
||||
|
||||
data: [
|
||||
"benchmarks/files/*",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libunwindstack",
|
||||
],
|
||||
|
||||
target: {
|
||||
android: {
|
||||
static_libs: [
|
||||
"libmeminfo",
|
||||
"libprocinfo",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Generates the elf data for use in the tests for .gnu_debugdata frames.
|
||||
// Once these files are generated, use the xz command to compress the data.
|
||||
cc_binary_host {
|
||||
name: "gen_gnudebugdata",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
srcs: [
|
||||
"tests/GenGnuDebugdata.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
# Unwinder Support Per Android Release
|
||||
This document describes the changes in the way the libunwindstack
|
||||
unwinder works on different Android versions. It does not describe
|
||||
every change in the code made between different versions, but is
|
||||
meant to allow an app developer to know what might be supported
|
||||
on different versions. It also describes the different way an unwind
|
||||
will display on different versions of Android.
|
||||
|
||||
## Android P
|
||||
libunwindstack was first introduced in Android P.
|
||||
|
||||
* Supports up to and including Dwarf 4 unwinding information.
|
||||
See http://dwarfstd.org/ for Dwarf standards.
|
||||
* Supports Arm exidx unwinding.
|
||||
* Supports the gdb JIT unwinding interface, which is how ART creates unwinding
|
||||
information for the JIT'd Java frames.
|
||||
* Supports special frames added to represent an ART Java interpreter frame.
|
||||
ART has marked the dex pc using cfi information that the unwinder
|
||||
understands and handles by adding a new frame in the stacktrace.
|
||||
|
||||
## Note
|
||||
By default, lld creates two separate maps of the elf in memory, one read-only
|
||||
and one read/executable. The libunwindstack on P and the unwinder on older
|
||||
versions of Android will not unwind properly in this case. For apps that
|
||||
target Android P or older, make sure that `-Wl,--no-rosegment` is
|
||||
included in linker arguments when using lld.
|
||||
|
||||
## Android Q
|
||||
* Fix bug (b/109824792) that handled load bias data incorrectly when
|
||||
FDEs use pc relative addressing in the eh\_frame\_hdr.
|
||||
Unfortunately, this wasn't fixed correctly in Q since it assumes
|
||||
that the bias is coming from the program header for the executable
|
||||
load. The real fix was to use the bias from the actual section data and
|
||||
is not completely fixed until Android R. For apps targeting Android Q,
|
||||
if it is being compiled with the llvm linker lld, it might be necessary
|
||||
to add the linker option `-Wl,-zseparate-code` to avoid creating an elf
|
||||
created this way.
|
||||
* Change the way the exidx section offset is found (b/110704153). Before
|
||||
the p\_vaddr value from the program header minus the load bias was used
|
||||
to find the start of the exidx data. Changed to use the p\_offset since
|
||||
it doesn't require any load bias manipulations.
|
||||
* Fix bug handling of dwarf sections without any header (b/110235461).
|
||||
Previously, the code assumed that FDEs are non-overlapping, and the FDEs
|
||||
are always in sorted order from low pc to high pc. Thus the code would
|
||||
read the entire set of CIEs/FDEs and then do a binary search to find
|
||||
the appropriate FDE for a given pc. Now the code does a sequential read
|
||||
and stops when it finds the FDE for a pc. It also understands the
|
||||
overlapping FDEs, so find the first FDE that matches a pc. In practice,
|
||||
elf files with this format only ever occurs if the file was generated
|
||||
without an eh\_frame/eh\_frame\_hdr section and only a debug\_frame. The
|
||||
other way this has been observed is when running simpleperf to unwind since
|
||||
sometimes there is not enough information in the eh\_frame for all points
|
||||
in the executable. On Android P, this would result in some incorrect
|
||||
unwinds coming from simpleperf. Nearly all crashes from Android P should
|
||||
be correct since the eh\_frame information was enough to do the unwind
|
||||
properly.
|
||||
* Be permissive of badly formed elf files. Previously, any detected error
|
||||
would result in unwinds stopping even if there is enough valid information
|
||||
to do an unwind.
|
||||
* The code now allows program header/section header offsets to point
|
||||
to unreadable memory. As long as the code can find the unwind tables,
|
||||
that is good enough.
|
||||
* The code allows program headers/section headers to be missing.
|
||||
* Allow a symbol table section header to point to invalid symbol table
|
||||
values.
|
||||
* Support for the linker read-only segment option (b/109657296).
|
||||
This is a feature of lld whereby there are two sections that
|
||||
contain elf data. The first is read-only and contains the elf header data,
|
||||
and the second is read-execute or execute only that
|
||||
contains the executable code from the elf. Before this, the unwinder
|
||||
always assumed that there was only a single read-execute section that
|
||||
contained the elf header data and the executable code.
|
||||
* Build ID information for elf objects added. This will display the
|
||||
NT\_GNU\_BUILD\_ID note found in elf files. This information can be used
|
||||
to identify the exact version of a shared library to help get symbol
|
||||
information when looking at a crash.
|
||||
* Add support for displaying the soname from an apk frame. Previously,
|
||||
a frame map name would be only the apk, but now if the shared library
|
||||
in the apk has set a soname, the map name will be `app.apk!libexample.so`
|
||||
instead of only `app.apk`.
|
||||
* Minimal support for Dwarf 5. This merely treats a Dwarf 5 version
|
||||
elf file as Dwarf 4. It does not support the new dwarf ops in Dwarf 5.
|
||||
Since the new ops are not likely to be used very often, this allows
|
||||
continuing to unwind even when encountering Dwarf 5 elf files.
|
||||
* Fix bug in pc handling of signal frames (b/130302288). In the previous
|
||||
version, the pc would be wrong in the signal frame. The rest of the
|
||||
unwind was correct, only the frame in the signal handler was incorrect
|
||||
in Android P.
|
||||
* Detect when an elf file is not readable so that a message can be
|
||||
displayed indicating that. This can happen when an app puts the shared
|
||||
libraries in non-standard locations that are not readable due to
|
||||
security restrictions (selinux rules).
|
||||
|
||||
## Android R
|
||||
* Display the offsets for Java interpreter frames. If this frame came
|
||||
from a non-zero offset map, no offset is printed. Previously, the
|
||||
line would look like:
|
||||
|
||||
#17 pc 00500d7a GoogleCamera.apk (com.google.camera.AndroidPriorityThread.run+10)
|
||||
|
||||
to:
|
||||
|
||||
#17 pc 00500d7a GoogleCamera.apk (offset 0x11d0000) (com.google.camera.AndroidPriorityThread.run+10)
|
||||
* Fix bug where the load bias was set from the first PT\_LOAD program
|
||||
header that has a zero p\_offset value. Now it is set from the first
|
||||
executable PT\_LOAD program header. This has only ever been a problem
|
||||
for host executables compiled for the x86\_64 architecture.
|
||||
* Switched to the libc++ demangler for function names. Previously, the
|
||||
demangler used was not complete, so some less common demangled function
|
||||
names would not be properly demangled or the function name would not be
|
||||
demangled at all.
|
||||
* Fix bug in load bias handling. If the unwind information in the eh\_frame
|
||||
or eh\_frame\_hdr does not have the same bias as the executable section,
|
||||
and uses pc relative FDEs, the unwind will be incorrect. This tends
|
||||
to truncate unwinds since the unwinder could not find the correct unwind
|
||||
information for a given pc.
|
||||
|
|
@ -1,862 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/Log.h>
|
||||
#include <unwindstack/MachineArm.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/RegsArm.h>
|
||||
|
||||
#include "ArmExidx.h"
|
||||
#include "Check.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
static constexpr uint8_t LOG_CFA_REG = 64;
|
||||
|
||||
void ArmExidx::LogRawData() {
|
||||
std::string log_str("Raw Data:");
|
||||
for (const uint8_t data : data_) {
|
||||
log_str += android::base::StringPrintf(" 0x%02x", data);
|
||||
}
|
||||
log(log_indent_, log_str.c_str());
|
||||
}
|
||||
|
||||
bool ArmExidx::ExtractEntryData(uint32_t entry_offset) {
|
||||
data_.clear();
|
||||
status_ = ARM_STATUS_NONE;
|
||||
|
||||
if (entry_offset & 1) {
|
||||
// The offset needs to be at least two byte aligned.
|
||||
status_ = ARM_STATUS_INVALID_ALIGNMENT;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Each entry is a 32 bit prel31 offset followed by 32 bits
|
||||
// of unwind information. If bit 31 of the unwind data is zero,
|
||||
// then this is a prel31 offset to the start of the unwind data.
|
||||
// If the unwind data is 1, then this is a cant unwind entry.
|
||||
// Otherwise, this data is the compact form of the unwind information.
|
||||
uint32_t data;
|
||||
if (!elf_memory_->Read32(entry_offset + 4, &data)) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = entry_offset + 4;
|
||||
return false;
|
||||
}
|
||||
if (data == 1) {
|
||||
// This is a CANT UNWIND entry.
|
||||
status_ = ARM_STATUS_NO_UNWIND;
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
|
||||
}
|
||||
log(log_indent_, "[cantunwind]");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data & (1UL << 31)) {
|
||||
// This is a compact table entry.
|
||||
if ((data >> 24) & 0xf) {
|
||||
// This is a non-zero index, this code doesn't support
|
||||
// other formats.
|
||||
status_ = ARM_STATUS_INVALID_PERSONALITY;
|
||||
return false;
|
||||
}
|
||||
data_.push_back((data >> 16) & 0xff);
|
||||
data_.push_back((data >> 8) & 0xff);
|
||||
uint8_t last_op = data & 0xff;
|
||||
data_.push_back(last_op);
|
||||
if (last_op != ARM_OP_FINISH) {
|
||||
// If this didn't end with a finish op, add one.
|
||||
data_.push_back(ARM_OP_FINISH);
|
||||
}
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
LogRawData();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the address of the ops.
|
||||
// Sign extend the data value if necessary.
|
||||
int32_t signed_data = static_cast<int32_t>(data << 1) >> 1;
|
||||
uint32_t addr = (entry_offset + 4) + signed_data;
|
||||
if (!elf_memory_->Read32(addr, &data)) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = addr;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_table_words;
|
||||
if (data & (1UL << 31)) {
|
||||
// Compact model.
|
||||
switch ((data >> 24) & 0xf) {
|
||||
case 0:
|
||||
num_table_words = 0;
|
||||
data_.push_back((data >> 16) & 0xff);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
num_table_words = (data >> 16) & 0xff;
|
||||
addr += 4;
|
||||
break;
|
||||
default:
|
||||
// Only a personality of 0, 1, 2 is valid.
|
||||
status_ = ARM_STATUS_INVALID_PERSONALITY;
|
||||
return false;
|
||||
}
|
||||
data_.push_back((data >> 8) & 0xff);
|
||||
data_.push_back(data & 0xff);
|
||||
} else {
|
||||
// Generic model.
|
||||
|
||||
// Skip the personality routine data, it doesn't contain any data
|
||||
// needed to decode the unwind information.
|
||||
addr += 4;
|
||||
if (!elf_memory_->Read32(addr, &data)) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = addr;
|
||||
return false;
|
||||
}
|
||||
num_table_words = (data >> 24) & 0xff;
|
||||
data_.push_back((data >> 16) & 0xff);
|
||||
data_.push_back((data >> 8) & 0xff);
|
||||
data_.push_back(data & 0xff);
|
||||
addr += 4;
|
||||
}
|
||||
|
||||
if (num_table_words > 5) {
|
||||
status_ = ARM_STATUS_MALFORMED;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_table_words; i++) {
|
||||
if (!elf_memory_->Read32(addr, &data)) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = addr;
|
||||
return false;
|
||||
}
|
||||
data_.push_back((data >> 24) & 0xff);
|
||||
data_.push_back((data >> 16) & 0xff);
|
||||
data_.push_back((data >> 8) & 0xff);
|
||||
data_.push_back(data & 0xff);
|
||||
addr += 4;
|
||||
}
|
||||
|
||||
if (data_.back() != ARM_OP_FINISH) {
|
||||
// If this didn't end with a finish op, add one.
|
||||
data_.push_back(ARM_OP_FINISH);
|
||||
}
|
||||
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
LogRawData();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::GetByte(uint8_t* byte) {
|
||||
if (data_.empty()) {
|
||||
status_ = ARM_STATUS_TRUNCATED;
|
||||
return false;
|
||||
}
|
||||
*byte = data_.front();
|
||||
data_.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
|
||||
CHECK((byte >> 4) == 0x8);
|
||||
|
||||
uint16_t registers = (byte & 0xf) << 8;
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
registers |= byte;
|
||||
if (registers == 0) {
|
||||
// 10000000 00000000: Refuse to unwind
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Refuse to unwind");
|
||||
}
|
||||
status_ = ARM_STATUS_NO_UNWIND;
|
||||
return false;
|
||||
}
|
||||
// 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
|
||||
registers <<= 4;
|
||||
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
bool add_comma = false;
|
||||
std::string msg = "pop {";
|
||||
for (size_t reg = 4; reg < 16; reg++) {
|
||||
if (registers & (1 << reg)) {
|
||||
if (add_comma) {
|
||||
msg += ", ";
|
||||
}
|
||||
msg += android::base::StringPrintf("r%zu", reg);
|
||||
add_comma = true;
|
||||
}
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
uint32_t cfa_offset = __builtin_popcount(registers) * 4;
|
||||
log_cfa_offset_ += cfa_offset;
|
||||
for (size_t reg = 4; reg < 16; reg++) {
|
||||
if (registers & (1 << reg)) {
|
||||
log_regs_[reg] = cfa_offset;
|
||||
cfa_offset -= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t reg = 4; reg < 16; reg++) {
|
||||
if (registers & (1 << reg)) {
|
||||
if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = cfa_;
|
||||
return false;
|
||||
}
|
||||
cfa_ += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// If the sp register is modified, change the cfa value.
|
||||
if (registers & (1 << ARM_REG_SP)) {
|
||||
cfa_ = (*regs_)[ARM_REG_SP];
|
||||
}
|
||||
|
||||
// Indicate if the pc register was set.
|
||||
if (registers & (1 << ARM_REG_PC)) {
|
||||
pc_set_ = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
|
||||
CHECK((byte >> 4) == 0x9);
|
||||
|
||||
uint8_t bits = byte & 0xf;
|
||||
if (bits == 13 || bits == 15) {
|
||||
// 10011101: Reserved as prefix for ARM register to register moves
|
||||
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "[Reserved]");
|
||||
}
|
||||
status_ = ARM_STATUS_RESERVED;
|
||||
return false;
|
||||
}
|
||||
// 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
log(log_indent_, "vsp = r%d", bits);
|
||||
} else {
|
||||
log_regs_[LOG_CFA_REG] = bits;
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// It is impossible for bits to be larger than the total number of
|
||||
// arm registers, so don't bother checking if bits is a valid register.
|
||||
cfa_ = (*regs_)[bits];
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
|
||||
CHECK((byte >> 4) == 0xa);
|
||||
|
||||
// 10100nnn: Pop r4-r[4+nnn]
|
||||
// 10101nnn: Pop r4-r[4+nnn], r14
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
uint8_t end_reg = byte & 0x7;
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
std::string msg = "pop {r4";
|
||||
if (end_reg) {
|
||||
msg += android::base::StringPrintf("-r%d", 4 + end_reg);
|
||||
}
|
||||
if (byte & 0x8) {
|
||||
log(log_indent_, "%s, r14}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
}
|
||||
} else {
|
||||
end_reg += 4;
|
||||
uint32_t cfa_offset = (end_reg - 3) * 4;
|
||||
if (byte & 0x8) {
|
||||
cfa_offset += 4;
|
||||
}
|
||||
log_cfa_offset_ += cfa_offset;
|
||||
|
||||
for (uint8_t reg = 4; reg <= end_reg; reg++) {
|
||||
log_regs_[reg] = cfa_offset;
|
||||
cfa_offset -= 4;
|
||||
}
|
||||
|
||||
if (byte & 0x8) {
|
||||
log_regs_[14] = cfa_offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 4; i <= 4 + (byte & 0x7); i++) {
|
||||
if (!process_memory_->Read32(cfa_, &(*regs_)[i])) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = cfa_;
|
||||
return false;
|
||||
}
|
||||
cfa_ += 4;
|
||||
}
|
||||
if (byte & 0x8) {
|
||||
if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = cfa_;
|
||||
return false;
|
||||
}
|
||||
cfa_ += 4;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_0000() {
|
||||
// 10110000: Finish
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
log(log_indent_, "finish");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
status_ = ARM_STATUS_FINISH;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
status_ = ARM_STATUS_FINISH;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_0001() {
|
||||
uint8_t byte;
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (byte == 0) {
|
||||
// 10110001 00000000: Spare
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
if (byte >> 4) {
|
||||
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
bool add_comma = false;
|
||||
std::string msg = "pop {";
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
if (byte & (1 << i)) {
|
||||
if (add_comma) {
|
||||
msg += ", ";
|
||||
}
|
||||
msg += android::base::StringPrintf("r%zu", i);
|
||||
add_comma = true;
|
||||
}
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
byte &= 0xf;
|
||||
uint32_t cfa_offset = __builtin_popcount(byte) * 4;
|
||||
log_cfa_offset_ += cfa_offset;
|
||||
for (size_t reg = 0; reg < 4; reg++) {
|
||||
if (byte & (1 << reg)) {
|
||||
log_regs_[reg] = cfa_offset;
|
||||
cfa_offset -= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t reg = 0; reg < 4; reg++) {
|
||||
if (byte & (1 << reg)) {
|
||||
if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
|
||||
status_ = ARM_STATUS_READ_FAILED;
|
||||
status_address_ = cfa_;
|
||||
return false;
|
||||
}
|
||||
cfa_ += 4;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void ArmExidx::AdjustRegisters(int32_t offset) {
|
||||
for (auto& entry : log_regs_) {
|
||||
if (entry.first >= LOG_CFA_REG) {
|
||||
break;
|
||||
}
|
||||
entry.second += offset;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_0010() {
|
||||
// 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
|
||||
uint32_t result = 0;
|
||||
uint32_t shift = 0;
|
||||
uint8_t byte;
|
||||
do {
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result |= (byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (byte & 0x80);
|
||||
result <<= 2;
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
int32_t cfa_offset = 0x204 + result;
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
log(log_indent_, "vsp = vsp + %d", cfa_offset);
|
||||
} else {
|
||||
log_cfa_offset_ += cfa_offset;
|
||||
}
|
||||
AdjustRegisters(cfa_offset);
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cfa_ += 0x204 + result;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_0011() {
|
||||
// 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
|
||||
uint8_t byte;
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
uint8_t start_reg = byte >> 4;
|
||||
uint8_t end_reg = start_reg + (byte & 0xf);
|
||||
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
|
||||
if (end_reg) {
|
||||
msg += android::base::StringPrintf("-d%d", end_reg);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported DX register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cfa_ += (byte & 0xf) * 8 + 12;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_01nn() {
|
||||
// 101101nn: Spare
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
|
||||
CHECK((byte & ~0x07) == 0xb8);
|
||||
|
||||
// 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
uint8_t last_reg = (byte & 0x7);
|
||||
std::string msg = "pop {d8";
|
||||
if (last_reg) {
|
||||
msg += android::base::StringPrintf("-d%d", last_reg + 8);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported DX register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += (byte & 0x7) * 8 + 12;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
|
||||
CHECK((byte >> 6) == 0x2);
|
||||
|
||||
switch ((byte >> 4) & 0x3) {
|
||||
case 0:
|
||||
return DecodePrefix_10_00(byte);
|
||||
case 1:
|
||||
return DecodePrefix_10_01(byte);
|
||||
case 2:
|
||||
return DecodePrefix_10_10(byte);
|
||||
default:
|
||||
switch (byte & 0xf) {
|
||||
case 0:
|
||||
return DecodePrefix_10_11_0000();
|
||||
case 1:
|
||||
return DecodePrefix_10_11_0001();
|
||||
case 2:
|
||||
return DecodePrefix_10_11_0010();
|
||||
case 3:
|
||||
return DecodePrefix_10_11_0011();
|
||||
default:
|
||||
if (byte & 0x8) {
|
||||
return DecodePrefix_10_11_1nnn(byte);
|
||||
} else {
|
||||
return DecodePrefix_10_11_01nn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
|
||||
CHECK((byte & ~0x07) == 0xc0);
|
||||
|
||||
uint8_t bits = byte & 0x7;
|
||||
if (bits == 6) {
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
uint8_t start_reg = byte >> 4;
|
||||
std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
|
||||
uint8_t end_reg = byte & 0xf;
|
||||
if (end_reg) {
|
||||
msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported wRX register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += (byte & 0xf) * 8 + 8;
|
||||
} else if (bits == 7) {
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (byte == 0) {
|
||||
// 11000111 00000000: Spare
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
} else if ((byte >> 4) == 0) {
|
||||
// 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
bool add_comma = false;
|
||||
std::string msg = "pop {";
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
if (byte & (1 << i)) {
|
||||
if (add_comma) {
|
||||
msg += ", ";
|
||||
}
|
||||
msg += android::base::StringPrintf("wCGR%zu", i);
|
||||
add_comma = true;
|
||||
}
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported wCGR register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += __builtin_popcount(byte) * 4;
|
||||
} else {
|
||||
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
std::string msg = "pop {wR10";
|
||||
uint8_t nnn = byte & 0x7;
|
||||
if (nnn) {
|
||||
msg += android::base::StringPrintf("-wR%d", 10 + nnn);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported wRX register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += (byte & 0x7) * 8 + 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
|
||||
CHECK((byte & ~0x07) == 0xc8);
|
||||
|
||||
uint8_t bits = byte & 0x7;
|
||||
if (bits == 0) {
|
||||
// 11001000 sssscccc: Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] by VPUSH
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
uint8_t start_reg = byte >> 4;
|
||||
std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
|
||||
uint8_t end_reg = byte & 0xf;
|
||||
if (end_reg) {
|
||||
msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported DX register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += (byte & 0xf) * 8 + 8;
|
||||
} else if (bits == 1) {
|
||||
// 11001001 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by VPUSH
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
uint8_t start_reg = byte >> 4;
|
||||
std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
|
||||
uint8_t end_reg = byte & 0xf;
|
||||
if (end_reg) {
|
||||
msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported DX register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Only update the cfa.
|
||||
cfa_ += (byte & 0xf) * 8 + 8;
|
||||
} else {
|
||||
// 11001yyy: Spare (yyy != 000, 001)
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
|
||||
CHECK((byte & ~0x07) == 0xd0);
|
||||
|
||||
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
std::string msg = "pop {d8";
|
||||
uint8_t end_reg = byte & 0x7;
|
||||
if (end_reg) {
|
||||
msg += android::base::StringPrintf("-d%d", 8 + end_reg);
|
||||
}
|
||||
log(log_indent_, "%s}", msg.c_str());
|
||||
} else {
|
||||
log(log_indent_, "Unsupported DX register display");
|
||||
}
|
||||
|
||||
if (log_skip_execution_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cfa_ += (byte & 0x7) * 8 + 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
|
||||
CHECK((byte >> 6) == 0x3);
|
||||
|
||||
switch ((byte >> 3) & 0x7) {
|
||||
case 0:
|
||||
return DecodePrefix_11_000(byte);
|
||||
case 1:
|
||||
return DecodePrefix_11_001(byte);
|
||||
case 2:
|
||||
return DecodePrefix_11_010(byte);
|
||||
default:
|
||||
// 11xxxyyy: Spare (xxx != 000, 001, 010)
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
log(log_indent_, "Spare");
|
||||
}
|
||||
status_ = ARM_STATUS_SPARE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArmExidx::Decode() {
|
||||
status_ = ARM_STATUS_NONE;
|
||||
uint8_t byte;
|
||||
if (!GetByte(&byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (byte >> 6) {
|
||||
case 0:
|
||||
// 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
log(log_indent_, "vsp = vsp + %d", cfa_offset);
|
||||
} else {
|
||||
log_cfa_offset_ += cfa_offset;
|
||||
}
|
||||
AdjustRegisters(cfa_offset);
|
||||
|
||||
if (log_skip_execution_) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cfa_ += ((byte & 0x3f) << 2) + 4;
|
||||
break;
|
||||
case 1:
|
||||
// 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
|
||||
if (log_type_ != ARM_LOG_NONE) {
|
||||
uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
|
||||
if (log_type_ == ARM_LOG_FULL) {
|
||||
log(log_indent_, "vsp = vsp - %d", cfa_offset);
|
||||
} else {
|
||||
log_cfa_offset_ -= cfa_offset;
|
||||
}
|
||||
AdjustRegisters(-cfa_offset);
|
||||
|
||||
if (log_skip_execution_) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cfa_ -= ((byte & 0x3f) << 2) + 4;
|
||||
break;
|
||||
case 2:
|
||||
return DecodePrefix_10(byte);
|
||||
default:
|
||||
return DecodePrefix_11(byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArmExidx::Eval() {
|
||||
pc_set_ = false;
|
||||
while (Decode());
|
||||
return status_ == ARM_STATUS_FINISH;
|
||||
}
|
||||
|
||||
void ArmExidx::LogByReg() {
|
||||
if (log_type_ != ARM_LOG_BY_REG) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t cfa_reg;
|
||||
if (log_regs_.count(LOG_CFA_REG) == 0) {
|
||||
cfa_reg = 13;
|
||||
} else {
|
||||
cfa_reg = log_regs_[LOG_CFA_REG];
|
||||
}
|
||||
|
||||
if (log_cfa_offset_ != 0) {
|
||||
char sign = (log_cfa_offset_ > 0) ? '+' : '-';
|
||||
log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
|
||||
} else {
|
||||
log(log_indent_, "cfa = r%zu", cfa_reg);
|
||||
}
|
||||
|
||||
for (const auto& entry : log_regs_) {
|
||||
if (entry.first >= LOG_CFA_REG) {
|
||||
break;
|
||||
}
|
||||
if (entry.second == 0) {
|
||||
log(log_indent_, "r%zu = [cfa]", entry.first);
|
||||
} else {
|
||||
char sign = (entry.second > 0) ? '-' : '+';
|
||||
log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_ARM_EXIDX_H
|
||||
#define _LIBUNWINDSTACK_ARM_EXIDX_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Forward declarations.
|
||||
class Memory;
|
||||
class RegsArm;
|
||||
|
||||
enum ArmStatus : size_t {
|
||||
ARM_STATUS_NONE = 0,
|
||||
ARM_STATUS_NO_UNWIND,
|
||||
ARM_STATUS_FINISH,
|
||||
ARM_STATUS_RESERVED,
|
||||
ARM_STATUS_SPARE,
|
||||
ARM_STATUS_TRUNCATED,
|
||||
ARM_STATUS_READ_FAILED,
|
||||
ARM_STATUS_MALFORMED,
|
||||
ARM_STATUS_INVALID_ALIGNMENT,
|
||||
ARM_STATUS_INVALID_PERSONALITY,
|
||||
};
|
||||
|
||||
enum ArmOp : uint8_t {
|
||||
ARM_OP_FINISH = 0xb0,
|
||||
};
|
||||
|
||||
enum ArmLogType : uint8_t {
|
||||
ARM_LOG_NONE,
|
||||
ARM_LOG_FULL,
|
||||
ARM_LOG_BY_REG,
|
||||
};
|
||||
|
||||
class ArmExidx {
|
||||
public:
|
||||
ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory)
|
||||
: regs_(regs), elf_memory_(elf_memory), process_memory_(process_memory) {}
|
||||
virtual ~ArmExidx() {}
|
||||
|
||||
void LogRawData();
|
||||
|
||||
void LogByReg();
|
||||
|
||||
bool ExtractEntryData(uint32_t entry_offset);
|
||||
|
||||
bool Eval();
|
||||
|
||||
bool Decode();
|
||||
|
||||
std::deque<uint8_t>* data() { return &data_; }
|
||||
|
||||
ArmStatus status() { return status_; }
|
||||
uint64_t status_address() { return status_address_; }
|
||||
|
||||
RegsArm* regs() { return regs_; }
|
||||
|
||||
uint32_t cfa() { return cfa_; }
|
||||
void set_cfa(uint32_t cfa) { cfa_ = cfa; }
|
||||
|
||||
bool pc_set() { return pc_set_; }
|
||||
void set_pc_set(bool pc_set) { pc_set_ = pc_set; }
|
||||
|
||||
void set_log(ArmLogType log_type) { log_type_ = log_type; }
|
||||
void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
|
||||
void set_log_indent(uint8_t indent) { log_indent_ = indent; }
|
||||
|
||||
private:
|
||||
bool GetByte(uint8_t* byte);
|
||||
void AdjustRegisters(int32_t offset);
|
||||
|
||||
bool DecodePrefix_10_00(uint8_t byte);
|
||||
bool DecodePrefix_10_01(uint8_t byte);
|
||||
bool DecodePrefix_10_10(uint8_t byte);
|
||||
bool DecodePrefix_10_11_0000();
|
||||
bool DecodePrefix_10_11_0001();
|
||||
bool DecodePrefix_10_11_0010();
|
||||
bool DecodePrefix_10_11_0011();
|
||||
bool DecodePrefix_10_11_01nn();
|
||||
bool DecodePrefix_10_11_1nnn(uint8_t byte);
|
||||
bool DecodePrefix_10(uint8_t byte);
|
||||
|
||||
bool DecodePrefix_11_000(uint8_t byte);
|
||||
bool DecodePrefix_11_001(uint8_t byte);
|
||||
bool DecodePrefix_11_010(uint8_t byte);
|
||||
bool DecodePrefix_11(uint8_t byte);
|
||||
|
||||
RegsArm* regs_ = nullptr;
|
||||
uint32_t cfa_ = 0;
|
||||
std::deque<uint8_t> data_;
|
||||
ArmStatus status_ = ARM_STATUS_NONE;
|
||||
uint64_t status_address_ = 0;
|
||||
|
||||
Memory* elf_memory_;
|
||||
Memory* process_memory_;
|
||||
|
||||
ArmLogType log_type_ = ARM_LOG_NONE;
|
||||
uint8_t log_indent_ = 0;
|
||||
bool log_skip_execution_ = false;
|
||||
bool pc_set_ = false;
|
||||
int32_t log_cfa_offset_ = 0;
|
||||
std::map<uint8_t, int32_t> log_regs_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_ARM_EXIDX_H
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
.text
|
||||
.global AsmGetRegs
|
||||
.balign 16
|
||||
.type AsmGetRegs, @function
|
||||
AsmGetRegs:
|
||||
.cfi_startproc
|
||||
mov 4(%esp), %eax
|
||||
movl $0, (%eax)
|
||||
movl %ecx, 4(%eax)
|
||||
movl %edx, 8(%eax)
|
||||
movl %ebx, 12(%eax)
|
||||
|
||||
/* ESP */
|
||||
leal 4(%esp), %ecx
|
||||
movl %ecx, 16(%eax)
|
||||
|
||||
movl %ebp, 20(%eax)
|
||||
movl %esi, 24(%eax)
|
||||
movl %edi, 28(%eax)
|
||||
|
||||
/* EIP */
|
||||
movl (%esp), %ecx
|
||||
movl %ecx, 32(%eax)
|
||||
|
||||
mov %cs, 36(%eax)
|
||||
mov %ss, 40(%eax)
|
||||
mov %ds, 44(%eax)
|
||||
mov %es, 48(%eax)
|
||||
mov %fs, 52(%eax)
|
||||
mov %gs, 56(%eax)
|
||||
ret
|
||||
|
||||
.cfi_endproc
|
||||
.size AsmGetRegs, .-AsmGetRegs
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
.text
|
||||
.global AsmGetRegs
|
||||
.balign 16
|
||||
.type AsmGetRegs, @function
|
||||
AsmGetRegs:
|
||||
.cfi_startproc
|
||||
movq %rax, (%rdi)
|
||||
movq %rdx, 8(%rdi)
|
||||
movq %rcx, 16(%rdi)
|
||||
movq %rbx, 24(%rdi)
|
||||
movq %rsi, 32(%rdi)
|
||||
movq %rdi, 40(%rdi)
|
||||
movq %rbp, 48(%rdi)
|
||||
|
||||
/* RSP */
|
||||
lea 8(%rsp), %rax
|
||||
movq %rax, 56(%rdi)
|
||||
|
||||
movq %r8, 64(%rdi)
|
||||
movq %r9, 72(%rdi)
|
||||
movq %r10, 80(%rdi)
|
||||
movq %r11, 88(%rdi)
|
||||
movq %r12, 96(%rdi)
|
||||
movq %r13, 104(%rdi)
|
||||
movq %r14, 112(%rdi)
|
||||
movq %r15, 120(%rdi)
|
||||
|
||||
/* RIP */
|
||||
movq (%rsp), %rax
|
||||
movq %rax, 128(%rdi)
|
||||
ret
|
||||
|
||||
.cfi_endproc
|
||||
.size AsmGetRegs, .-AsmGetRegs
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_CHECK_H
|
||||
#define _LIBUNWINDSTACK_CHECK_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <unwindstack/Log.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
#define CHECK(assertion) \
|
||||
if (__builtin_expect(!(assertion), false)) { \
|
||||
log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_CHECK_H
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define LOG_TAG "unwind"
|
||||
#include <log/log.h>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <art_api/dex_file_support.h>
|
||||
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "DexFile.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
static bool CheckDexSupport() {
|
||||
if (std::string err_msg; !art_api::dex::TryLoadLibdexfileExternal(&err_msg)) {
|
||||
ALOGW("Failed to initialize DEX file support: %s", err_msg.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HasDexSupport() {
|
||||
static bool has_dex_support = CheckDexSupport();
|
||||
return has_dex_support;
|
||||
}
|
||||
|
||||
std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
|
||||
MapInfo* info) {
|
||||
if (UNLIKELY(!HasDexSupport())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t max_size = info->end - dex_file_offset_in_memory;
|
||||
if (memory->IsLocal()) {
|
||||
size_t size = max_size;
|
||||
|
||||
std::string err_msg;
|
||||
std::unique_ptr<art_api::dex::DexFile> art_dex_file = DexFile::OpenFromMemory(
|
||||
reinterpret_cast<void const*>(dex_file_offset_in_memory), &size, info->name, &err_msg);
|
||||
if (art_dex_file != nullptr && size <= max_size) {
|
||||
return std::unique_ptr<DexFile>(new DexFile(art_dex_file));
|
||||
}
|
||||
}
|
||||
|
||||
if (!info->name.empty()) {
|
||||
std::unique_ptr<DexFile> dex_file =
|
||||
DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
|
||||
if (dex_file) {
|
||||
return dex_file;
|
||||
}
|
||||
}
|
||||
return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name, max_size);
|
||||
}
|
||||
|
||||
bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
|
||||
uint64_t* method_offset) {
|
||||
art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false);
|
||||
if (method_info.offset == 0) {
|
||||
return false;
|
||||
}
|
||||
*method_name = method_info.name;
|
||||
*method_offset = dex_offset - method_info.offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(uint64_t dex_file_offset_in_file,
|
||||
const std::string& file) {
|
||||
if (UNLIKELY(!HasDexSupport())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
|
||||
if (fd == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string error_msg;
|
||||
std::unique_ptr<art_api::dex::DexFile> art_dex_file =
|
||||
OpenFromFd(fd, dex_file_offset_in_file, file, &error_msg);
|
||||
if (art_dex_file == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(art_dex_file));
|
||||
}
|
||||
|
||||
std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
|
||||
Memory* memory,
|
||||
const std::string& name,
|
||||
size_t max_size) {
|
||||
if (UNLIKELY(!HasDexSupport())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> backing_memory;
|
||||
|
||||
for (size_t size = 0;;) {
|
||||
std::string error_msg;
|
||||
std::unique_ptr<art_api::dex::DexFile> art_dex_file =
|
||||
OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
|
||||
if (size > max_size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (art_dex_file != nullptr) {
|
||||
return std::unique_ptr<DexFileFromMemory>(
|
||||
new DexFileFromMemory(art_dex_file, std::move(backing_memory)));
|
||||
}
|
||||
|
||||
if (!error_msg.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
backing_memory.resize(size);
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, backing_memory.data(),
|
||||
backing_memory.size())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DEX_FILE_H
|
||||
#define _LIBUNWINDSTACK_DEX_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <art_api/dex_file_support.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class DexFile : protected art_api::dex::DexFile {
|
||||
public:
|
||||
virtual ~DexFile() = default;
|
||||
|
||||
bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
|
||||
|
||||
static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
|
||||
MapInfo* info);
|
||||
|
||||
protected:
|
||||
DexFile(std::unique_ptr<art_api::dex::DexFile>& art_dex_file)
|
||||
: art_api::dex::DexFile(art_dex_file) {}
|
||||
};
|
||||
|
||||
class DexFileFromFile : public DexFile {
|
||||
public:
|
||||
static std::unique_ptr<DexFileFromFile> Create(uint64_t dex_file_offset_in_file,
|
||||
const std::string& file);
|
||||
|
||||
private:
|
||||
DexFileFromFile(std::unique_ptr<art_api::dex::DexFile>& art_dex_file) : DexFile(art_dex_file) {}
|
||||
};
|
||||
|
||||
class DexFileFromMemory : public DexFile {
|
||||
public:
|
||||
static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
|
||||
Memory* memory, const std::string& name,
|
||||
size_t max_size);
|
||||
|
||||
private:
|
||||
DexFileFromMemory(std::unique_ptr<art_api::dex::DexFile>& art_dex_file,
|
||||
std::vector<uint8_t>&& memory)
|
||||
: DexFile(art_dex_file), memory_(std::move(memory)) {}
|
||||
|
||||
std::vector<uint8_t> memory_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DEX_FILE_H
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#if defined(DEXFILE_SUPPORT)
|
||||
#include "DexFile.h"
|
||||
#endif
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
#if !defined(DEXFILE_SUPPORT)
|
||||
// Empty class definition.
|
||||
class DexFile {
|
||||
public:
|
||||
DexFile() = default;
|
||||
virtual ~DexFile() = default;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct DEXFileEntry32 {
|
||||
uint32_t next;
|
||||
uint32_t prev;
|
||||
uint32_t dex_file;
|
||||
};
|
||||
|
||||
struct DEXFileEntry64 {
|
||||
uint64_t next;
|
||||
uint64_t prev;
|
||||
uint64_t dex_file;
|
||||
};
|
||||
|
||||
DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {}
|
||||
|
||||
DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
|
||||
: Global(memory, search_libs) {}
|
||||
|
||||
DexFiles::~DexFiles() {}
|
||||
|
||||
void DexFiles::ProcessArch() {
|
||||
switch (arch()) {
|
||||
case ARCH_ARM:
|
||||
case ARCH_MIPS:
|
||||
case ARCH_X86:
|
||||
read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
|
||||
read_entry_func_ = &DexFiles::ReadEntry32;
|
||||
break;
|
||||
|
||||
case ARCH_ARM64:
|
||||
case ARCH_MIPS64:
|
||||
case ARCH_X86_64:
|
||||
read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
|
||||
read_entry_func_ = &DexFiles::ReadEntry64;
|
||||
break;
|
||||
|
||||
case ARCH_UNKNOWN:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
|
||||
uint32_t entry;
|
||||
const uint32_t field_offset = 12; // offset of first_entry_ in the descriptor struct.
|
||||
if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
|
||||
return 0;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
|
||||
uint64_t entry;
|
||||
const uint32_t field_offset = 16; // offset of first_entry_ in the descriptor struct.
|
||||
if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
|
||||
return 0;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool DexFiles::ReadEntry32() {
|
||||
DEXFileEntry32 entry;
|
||||
if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
|
||||
entry_addr_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
addrs_.push_back(entry.dex_file);
|
||||
entry_addr_ = entry.next;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DexFiles::ReadEntry64() {
|
||||
DEXFileEntry64 entry;
|
||||
if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
|
||||
entry_addr_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
addrs_.push_back(entry.dex_file);
|
||||
entry_addr_ = entry.next;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DexFiles::ReadVariableData(uint64_t ptr_offset) {
|
||||
entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset);
|
||||
return entry_addr_ != 0;
|
||||
}
|
||||
|
||||
void DexFiles::Init(Maps* maps) {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
initialized_ = true;
|
||||
entry_addr_ = 0;
|
||||
|
||||
FindAndReadVariable(maps, "__dex_debug_descriptor");
|
||||
}
|
||||
|
||||
#if defined(DEXFILE_SUPPORT)
|
||||
DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
|
||||
// Lock while processing the data.
|
||||
DexFile* dex_file;
|
||||
auto entry = files_.find(dex_file_offset);
|
||||
if (entry == files_.end()) {
|
||||
std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
|
||||
dex_file = new_dex_file.get();
|
||||
files_[dex_file_offset] = std::move(new_dex_file);
|
||||
} else {
|
||||
dex_file = entry->second.get();
|
||||
}
|
||||
return dex_file;
|
||||
}
|
||||
#else
|
||||
DexFile* DexFiles::GetDexFile(uint64_t, MapInfo*) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
|
||||
if (index < addrs_.size()) {
|
||||
*addr = addrs_[index];
|
||||
return true;
|
||||
}
|
||||
if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
|
||||
*addr = addrs_.back();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(DEXFILE_SUPPORT)
|
||||
void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
|
||||
std::string* method_name, uint64_t* method_offset) {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
if (!initialized_) {
|
||||
Init(maps);
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
uint64_t addr;
|
||||
while (GetAddr(index++, &addr)) {
|
||||
if (addr < info->start || addr >= info->end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DexFile* dex_file = GetDexFile(addr, info);
|
||||
if (dex_file != nullptr &&
|
||||
dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void DexFiles::GetMethodInformation(Maps*, MapInfo*, uint64_t, std::string*, uint64_t*) {}
|
||||
#endif
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,768 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/DwarfError.h>
|
||||
#include <unwindstack/DwarfLocation.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/Log.h>
|
||||
#include <unwindstack/MachineArm64.h>
|
||||
|
||||
#include "DwarfCfa.h"
|
||||
#include "DwarfEncoding.h"
|
||||
#include "DwarfOp.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
template <typename AddressType>
|
||||
constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
|
||||
dwarf_loc_regs_t* loc_regs) {
|
||||
if (cie_loc_regs_ != nullptr) {
|
||||
for (const auto& entry : *cie_loc_regs_) {
|
||||
(*loc_regs)[entry.first] = entry.second;
|
||||
}
|
||||
}
|
||||
last_error_.code = DWARF_ERROR_NONE;
|
||||
last_error_.address = 0;
|
||||
|
||||
memory_->set_cur_offset(start_offset);
|
||||
uint64_t cfa_offset;
|
||||
cur_pc_ = fde_->pc_start;
|
||||
loc_regs->pc_start = cur_pc_;
|
||||
while (true) {
|
||||
if (cur_pc_ > pc) {
|
||||
loc_regs->pc_end = cur_pc_;
|
||||
return true;
|
||||
}
|
||||
if ((cfa_offset = memory_->cur_offset()) >= end_offset) {
|
||||
loc_regs->pc_end = fde_->pc_end;
|
||||
return true;
|
||||
}
|
||||
loc_regs->pc_start = cur_pc_;
|
||||
operands_.clear();
|
||||
// Read the cfa information.
|
||||
uint8_t cfa_value;
|
||||
if (!memory_->ReadBytes(&cfa_value, 1)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_->cur_offset();
|
||||
return false;
|
||||
}
|
||||
uint8_t cfa_low = cfa_value & 0x3f;
|
||||
// Check the 2 high bits.
|
||||
switch (cfa_value >> 6) {
|
||||
case 1:
|
||||
cur_pc_ += cfa_low * fde_->cie->code_alignment_factor;
|
||||
break;
|
||||
case 2: {
|
||||
uint64_t offset;
|
||||
if (!memory_->ReadULEB128(&offset)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_->cur_offset();
|
||||
return false;
|
||||
}
|
||||
SignedType signed_offset =
|
||||
static_cast<SignedType>(offset) * fde_->cie->data_alignment_factor;
|
||||
(*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET,
|
||||
.values = {static_cast<uint64_t>(signed_offset)}};
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (cie_loc_regs_ == nullptr) {
|
||||
log(0, "restore while processing cie");
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto reg_entry = cie_loc_regs_->find(cfa_low);
|
||||
if (reg_entry == cie_loc_regs_->end()) {
|
||||
loc_regs->erase(cfa_low);
|
||||
} else {
|
||||
(*loc_regs)[cfa_low] = reg_entry->second;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
|
||||
if (handle_func == nullptr) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto cfa = &DwarfCfaInfo::kTable[cfa_low];
|
||||
for (size_t i = 0; i < cfa->num_operands; i++) {
|
||||
if (cfa->operands[i] == DW_EH_PE_block) {
|
||||
uint64_t block_length;
|
||||
if (!memory_->ReadULEB128(&block_length)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_->cur_offset();
|
||||
return false;
|
||||
}
|
||||
operands_.push_back(block_length);
|
||||
memory_->set_cur_offset(memory_->cur_offset() + block_length);
|
||||
continue;
|
||||
}
|
||||
uint64_t value;
|
||||
if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_->cur_offset();
|
||||
return false;
|
||||
}
|
||||
operands_.push_back(value);
|
||||
}
|
||||
|
||||
if (!(this->*handle_func)(loc_regs)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
std::string DwarfCfa<AddressType>::GetOperandString(uint8_t operand, uint64_t value,
|
||||
uint64_t* cur_pc) {
|
||||
std::string string;
|
||||
switch (operand) {
|
||||
case DwarfCfaInfo::DWARF_DISPLAY_REGISTER:
|
||||
string = " register(" + std::to_string(value) + ")";
|
||||
break;
|
||||
case DwarfCfaInfo::DWARF_DISPLAY_SIGNED_NUMBER:
|
||||
string += " " + std::to_string(static_cast<SignedType>(value));
|
||||
break;
|
||||
case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
|
||||
*cur_pc += value;
|
||||
FALLTHROUGH_INTENDED;
|
||||
// Fall through to log the value.
|
||||
case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
|
||||
string += " " + std::to_string(value);
|
||||
break;
|
||||
case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
|
||||
*cur_pc = value;
|
||||
FALLTHROUGH_INTENDED;
|
||||
// Fall through to log the value.
|
||||
case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
|
||||
if (std::is_same<AddressType, uint32_t>::value) {
|
||||
string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
|
||||
} else {
|
||||
string += android::base::StringPrintf(" 0x%" PRIx64, static_cast<uint64_t>(value));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
string = " unknown";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset,
|
||||
uint8_t reg) {
|
||||
uint64_t offset;
|
||||
if (!memory_->ReadULEB128(&offset)) {
|
||||
return false;
|
||||
}
|
||||
uint64_t end_offset = memory_->cur_offset();
|
||||
memory_->set_cur_offset(cfa_offset);
|
||||
|
||||
std::string raw_data = "Raw Data:";
|
||||
for (uint64_t i = cfa_offset; i < end_offset; i++) {
|
||||
uint8_t value;
|
||||
if (!memory_->ReadBytes(&value, 1)) {
|
||||
return false;
|
||||
}
|
||||
raw_data += android::base::StringPrintf(" 0x%02x", value);
|
||||
}
|
||||
log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
|
||||
log(indent, "%s", raw_data.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
|
||||
uint64_t* cur_pc) {
|
||||
const auto* cfa = &DwarfCfaInfo::kTable[op];
|
||||
if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
|
||||
if (op == 0x2d) {
|
||||
log(indent, "Illegal (Only valid on aarch64)");
|
||||
} else {
|
||||
log(indent, "Illegal");
|
||||
}
|
||||
log(indent, "Raw Data: 0x%02x", op);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string log_string(cfa->name);
|
||||
std::vector<std::string> expression_lines;
|
||||
for (size_t i = 0; i < cfa->num_operands; i++) {
|
||||
if (cfa->operands[i] == DW_EH_PE_block) {
|
||||
// This is a Dwarf Expression.
|
||||
uint64_t end_offset;
|
||||
if (!memory_->ReadULEB128(&end_offset)) {
|
||||
return false;
|
||||
}
|
||||
log_string += " " + std::to_string(end_offset);
|
||||
end_offset += memory_->cur_offset();
|
||||
|
||||
DwarfOp<AddressType> op(memory_, nullptr);
|
||||
op.GetLogInfo(memory_->cur_offset(), end_offset, &expression_lines);
|
||||
memory_->set_cur_offset(end_offset);
|
||||
} else {
|
||||
uint64_t value;
|
||||
if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
|
||||
return false;
|
||||
}
|
||||
log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
|
||||
}
|
||||
}
|
||||
log(indent, "%s", log_string.c_str());
|
||||
|
||||
// Get the raw bytes of the data.
|
||||
uint64_t end_offset = memory_->cur_offset();
|
||||
memory_->set_cur_offset(cfa_offset);
|
||||
std::string raw_data("Raw Data:");
|
||||
for (uint64_t i = 0; i < end_offset - cfa_offset; i++) {
|
||||
uint8_t value;
|
||||
if (!memory_->ReadBytes(&value, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only show 10 raw bytes per line.
|
||||
if ((i % 10) == 0 && i != 0) {
|
||||
log(indent, "%s", raw_data.c_str());
|
||||
raw_data.clear();
|
||||
}
|
||||
if (raw_data.empty()) {
|
||||
raw_data = "Raw Data:";
|
||||
}
|
||||
raw_data += android::base::StringPrintf(" 0x%02x", value);
|
||||
}
|
||||
if (!raw_data.empty()) {
|
||||
log(indent, "%s", raw_data.c_str());
|
||||
}
|
||||
|
||||
// Log any of the expression data.
|
||||
for (const auto& line : expression_lines) {
|
||||
log(indent + 1, "%s", line.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t start_offset,
|
||||
uint64_t end_offset) {
|
||||
memory_->set_cur_offset(start_offset);
|
||||
uint64_t cfa_offset;
|
||||
uint64_t cur_pc = fde_->pc_start;
|
||||
uint64_t old_pc = cur_pc;
|
||||
while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc <= pc) {
|
||||
// Read the cfa information.
|
||||
uint8_t cfa_value;
|
||||
if (!memory_->ReadBytes(&cfa_value, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the 2 high bits.
|
||||
uint8_t cfa_low = cfa_value & 0x3f;
|
||||
switch (cfa_value >> 6) {
|
||||
case 0:
|
||||
if (!LogInstruction(indent, cfa_offset, cfa_low, &cur_pc)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
log(indent, "DW_CFA_advance_loc %d", cfa_low);
|
||||
log(indent, "Raw Data: 0x%02x", cfa_value);
|
||||
cur_pc += cfa_low * fde_->cie->code_alignment_factor;
|
||||
break;
|
||||
case 2:
|
||||
if (!LogOffsetRegisterString(indent, cfa_offset, cfa_low)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
log(indent, "DW_CFA_restore register(%d)", cfa_low);
|
||||
log(indent, "Raw Data: 0x%02x", cfa_value);
|
||||
break;
|
||||
}
|
||||
if (cur_pc != old_pc) {
|
||||
log(0, "");
|
||||
log(indent, "PC 0x%" PRIx64, cur_pc);
|
||||
}
|
||||
old_pc = cur_pc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Static data.
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_nop(dwarf_loc_regs_t*) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_set_loc(dwarf_loc_regs_t*) {
|
||||
AddressType cur_pc = cur_pc_;
|
||||
AddressType new_pc = operands_[0];
|
||||
if (new_pc < cur_pc) {
|
||||
if (std::is_same<AddressType, uint32_t>::value) {
|
||||
log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
|
||||
} else {
|
||||
log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
|
||||
}
|
||||
}
|
||||
cur_pc_ = new_pc;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_advance_loc(dwarf_loc_regs_t*) {
|
||||
cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_offset(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {operands_[1]}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_restore(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
if (cie_loc_regs_ == nullptr) {
|
||||
log(0, "restore while processing cie");
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
auto reg_entry = cie_loc_regs_->find(reg);
|
||||
if (reg_entry == cie_loc_regs_->end()) {
|
||||
loc_regs->erase(reg);
|
||||
} else {
|
||||
(*loc_regs)[reg] = reg_entry->second;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_undefined(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_same_value(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
loc_regs->erase(reg);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_register(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
AddressType reg_dst = operands_[1];
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_REGISTER, .values = {reg_dst}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_remember_state(dwarf_loc_regs_t* loc_regs) {
|
||||
loc_reg_state_.push(*loc_regs);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_restore_state(dwarf_loc_regs_t* loc_regs) {
|
||||
if (loc_reg_state_.size() == 0) {
|
||||
log(0, "Warning: Attempt to restore without remember.");
|
||||
return true;
|
||||
}
|
||||
*loc_regs = loc_reg_state_.top();
|
||||
loc_reg_state_.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_def_cfa(dwarf_loc_regs_t* loc_regs) {
|
||||
(*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {operands_[0], operands_[1]}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_def_cfa_register(dwarf_loc_regs_t* loc_regs) {
|
||||
auto cfa_location = loc_regs->find(CFA_REG);
|
||||
if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
|
||||
log(0, "Attempt to set new register, but cfa is not already set to a register.");
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
|
||||
cfa_location->second.values[0] = operands_[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_def_cfa_offset(dwarf_loc_regs_t* loc_regs) {
|
||||
// Changing the offset if this is not a register is illegal.
|
||||
auto cfa_location = loc_regs->find(CFA_REG);
|
||||
if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
|
||||
log(0, "Attempt to set offset, but cfa is not set to a register.");
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
cfa_location->second.values[1] = operands_[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
|
||||
// There is only one type of expression for CFA evaluation and the DWARF
|
||||
// specification is unclear whether it returns the address or the
|
||||
// dereferenced value. GDB expects the value, so will we.
|
||||
(*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
|
||||
.values = {operands_[0], memory_->cur_offset()}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_expression(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_EXPRESSION,
|
||||
.values = {operands_[1], memory_->cur_offset()}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
SignedType value = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(value)}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) {
|
||||
SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
|
||||
(*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER,
|
||||
.values = {operands_[0], static_cast<uint64_t>(offset)}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_def_cfa_offset_sf(dwarf_loc_regs_t* loc_regs) {
|
||||
// Changing the offset if this is not a register is illegal.
|
||||
auto cfa_location = loc_regs->find(CFA_REG);
|
||||
if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
|
||||
log(0, "Attempt to set offset, but cfa is not set to a register.");
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
|
||||
cfa_location->second.values[1] = static_cast<uint64_t>(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_val_offset(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_val_expression(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
|
||||
.values = {operands_[1], memory_->cur_offset()}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) {
|
||||
AddressType reg = operands_[0];
|
||||
SignedType offset = -static_cast<SignedType>(operands_[1]);
|
||||
(*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(offset)}};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfCfa<AddressType>::cfa_aarch64_negate_ra_state(dwarf_loc_regs_t* loc_regs) {
|
||||
// Only supported on aarch64.
|
||||
if (arch_ != ARCH_ARM64) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cfa_location = loc_regs->find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
|
||||
if (cfa_location == loc_regs->end()) {
|
||||
(*loc_regs)[Arm64Reg::ARM64_PREG_RA_SIGN_STATE] = {.type = DWARF_LOCATION_PSEUDO_REGISTER,
|
||||
.values = {1}};
|
||||
} else {
|
||||
cfa_location->second.values[0] ^= 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
|
||||
{
|
||||
// 0x00 DW_CFA_nop
|
||||
"DW_CFA_nop",
|
||||
2,
|
||||
0,
|
||||
{},
|
||||
{},
|
||||
},
|
||||
{
|
||||
"DW_CFA_set_loc", // 0x01 DW_CFA_set_loc
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_absptr},
|
||||
{DWARF_DISPLAY_SET_LOC},
|
||||
},
|
||||
{
|
||||
"DW_CFA_advance_loc1", // 0x02 DW_CFA_advance_loc1
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_udata1},
|
||||
{DWARF_DISPLAY_ADVANCE_LOC},
|
||||
},
|
||||
{
|
||||
"DW_CFA_advance_loc2", // 0x03 DW_CFA_advance_loc2
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_udata2},
|
||||
{DWARF_DISPLAY_ADVANCE_LOC},
|
||||
},
|
||||
{
|
||||
"DW_CFA_advance_loc4", // 0x04 DW_CFA_advance_loc4
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_udata4},
|
||||
{DWARF_DISPLAY_ADVANCE_LOC},
|
||||
},
|
||||
{
|
||||
"DW_CFA_offset_extended", // 0x05 DW_CFA_offset_extended
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_restore_extended", // 0x06 DW_CFA_restore_extended
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_undefined", // 0x07 DW_CFA_undefined
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_same_value", // 0x08 DW_CFA_same_value
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_register", // 0x09 DW_CFA_register
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_REGISTER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_remember_state", // 0x0a DW_CFA_remember_state
|
||||
2,
|
||||
0,
|
||||
{},
|
||||
{},
|
||||
},
|
||||
{
|
||||
"DW_CFA_restore_state", // 0x0b DW_CFA_restore_state
|
||||
2,
|
||||
0,
|
||||
{},
|
||||
{},
|
||||
},
|
||||
{
|
||||
"DW_CFA_def_cfa", // 0x0c DW_CFA_def_cfa
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_def_cfa_register", // 0x0d DW_CFA_def_cfa_register
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_def_cfa_offset", // 0x0e DW_CFA_def_cfa_offset
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_def_cfa_expression", // 0x0f DW_CFA_def_cfa_expression
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_block},
|
||||
{DWARF_DISPLAY_EVAL_BLOCK},
|
||||
},
|
||||
{
|
||||
"DW_CFA_expression", // 0x10 DW_CFA_expression
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_block},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
|
||||
},
|
||||
{
|
||||
"DW_CFA_offset_extended_sf", // 0x11 DW_CFA_offset_extend_sf
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_sleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_def_cfa_sf", // 0x12 DW_CFA_def_cfa_sf
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_sleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_def_cfa_offset_sf", // 0x13 DW_CFA_def_cfa_offset_sf
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_sleb128},
|
||||
{DWARF_DISPLAY_SIGNED_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_val_offset", // 0x14 DW_CFA_val_offset
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_val_offset_sf", // 0x15 DW_CFA_val_offset_sf
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_sleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_val_expression", // 0x16 DW_CFA_val_expression
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_block},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
|
||||
},
|
||||
{"", 0, 0, {}, {}}, // 0x17 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x18 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x19 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x1a illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x1b illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
|
||||
{"", 0, 0, {}, {}}, // 0x1d illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x1e illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x1f illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x20 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x21 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x22 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x23 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x24 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x25 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x26 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x27 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x28 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x29 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x2a illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x2b illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x2c illegal cfa
|
||||
{
|
||||
"DW_CFA_AARCH64_negate_ra_state", // 0x2d DW_CFA_AARCH64_negate_ra_state
|
||||
3,
|
||||
0,
|
||||
{},
|
||||
{},
|
||||
},
|
||||
{
|
||||
"DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
|
||||
2,
|
||||
1,
|
||||
{DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_NUMBER},
|
||||
},
|
||||
{
|
||||
"DW_CFA_GNU_negative_offset_extended", // 0x2f DW_CFA_GNU_negative_offset_extended
|
||||
2,
|
||||
2,
|
||||
{DW_EH_PE_uleb128, DW_EH_PE_uleb128},
|
||||
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
|
||||
},
|
||||
{"", 0, 0, {}, {}}, // 0x31 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x32 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x33 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x34 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x35 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x36 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x37 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x38 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x39 illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x3a illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x3b illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x3c illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x3d illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x3e illegal cfa
|
||||
{"", 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
|
||||
};
|
||||
|
||||
// Explicitly instantiate DwarfCfa.
|
||||
template class DwarfCfa<uint32_t>;
|
||||
template class DwarfCfa<uint64_t>;
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DWARF_CFA_H
|
||||
#define _LIBUNWINDSTACK_DWARF_CFA_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/DwarfError.h>
|
||||
#include <unwindstack/DwarfLocation.h>
|
||||
#include <unwindstack/DwarfMemory.h>
|
||||
#include <unwindstack/DwarfStructs.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Forward declarations.
|
||||
enum ArchEnum : uint8_t;
|
||||
|
||||
// DWARF Standard home: http://dwarfstd.org/
|
||||
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
|
||||
// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
|
||||
|
||||
class DwarfCfaInfo {
|
||||
public:
|
||||
enum DisplayType : uint8_t {
|
||||
DWARF_DISPLAY_NONE = 0,
|
||||
DWARF_DISPLAY_REGISTER,
|
||||
DWARF_DISPLAY_NUMBER,
|
||||
DWARF_DISPLAY_SIGNED_NUMBER,
|
||||
DWARF_DISPLAY_EVAL_BLOCK,
|
||||
DWARF_DISPLAY_ADDRESS,
|
||||
DWARF_DISPLAY_SET_LOC,
|
||||
DWARF_DISPLAY_ADVANCE_LOC,
|
||||
};
|
||||
|
||||
struct Info {
|
||||
// It may seem cleaner to just change the type of 'name' to 'const char *'.
|
||||
// However, having a pointer here would require relocation at runtime,
|
||||
// causing 'kTable' to be placed in data.rel.ro section instead of rodata
|
||||
// section, adding memory pressure to the system. Note that this is only
|
||||
// safe because this is only used in C++ code. C++ standard, unlike C
|
||||
// standard, mandates the array size to be large enough to hold the NULL
|
||||
// terminator when initialized with a string literal.
|
||||
const char name[36];
|
||||
uint8_t supported_version;
|
||||
uint8_t num_operands;
|
||||
uint8_t operands[2];
|
||||
uint8_t display_operands[2];
|
||||
};
|
||||
|
||||
const static Info kTable[64];
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
class DwarfCfa {
|
||||
// Signed version of AddressType
|
||||
typedef typename std::make_signed<AddressType>::type SignedType;
|
||||
|
||||
public:
|
||||
DwarfCfa(DwarfMemory* memory, const DwarfFde* fde, ArchEnum arch)
|
||||
: memory_(memory), fde_(fde), arch_(arch) {}
|
||||
virtual ~DwarfCfa() = default;
|
||||
|
||||
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
|
||||
dwarf_loc_regs_t* loc_regs);
|
||||
|
||||
bool Log(uint32_t indent, uint64_t pc, uint64_t start_offset, uint64_t end_offset);
|
||||
|
||||
const DwarfErrorData& last_error() { return last_error_; }
|
||||
DwarfErrorCode LastErrorCode() { return last_error_.code; }
|
||||
uint64_t LastErrorAddress() { return last_error_.address; }
|
||||
|
||||
AddressType cur_pc() { return cur_pc_; }
|
||||
|
||||
void set_cie_loc_regs(const dwarf_loc_regs_t* cie_loc_regs) { cie_loc_regs_ = cie_loc_regs; }
|
||||
|
||||
protected:
|
||||
std::string GetOperandString(uint8_t operand, uint64_t value, uint64_t* cur_pc);
|
||||
|
||||
bool LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, uint8_t reg);
|
||||
|
||||
bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
|
||||
|
||||
private:
|
||||
DwarfErrorData last_error_;
|
||||
DwarfMemory* memory_;
|
||||
const DwarfFde* fde_;
|
||||
ArchEnum arch_;
|
||||
|
||||
AddressType cur_pc_;
|
||||
const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
|
||||
std::vector<AddressType> operands_;
|
||||
std::stack<dwarf_loc_regs_t> loc_reg_state_;
|
||||
|
||||
// CFA processing functions.
|
||||
bool cfa_nop(dwarf_loc_regs_t*);
|
||||
bool cfa_set_loc(dwarf_loc_regs_t*);
|
||||
bool cfa_advance_loc(dwarf_loc_regs_t*);
|
||||
bool cfa_offset(dwarf_loc_regs_t*);
|
||||
bool cfa_restore(dwarf_loc_regs_t*);
|
||||
bool cfa_undefined(dwarf_loc_regs_t*);
|
||||
bool cfa_same_value(dwarf_loc_regs_t*);
|
||||
bool cfa_register(dwarf_loc_regs_t*);
|
||||
bool cfa_remember_state(dwarf_loc_regs_t*);
|
||||
bool cfa_restore_state(dwarf_loc_regs_t*);
|
||||
bool cfa_def_cfa(dwarf_loc_regs_t*);
|
||||
bool cfa_def_cfa_register(dwarf_loc_regs_t*);
|
||||
bool cfa_def_cfa_offset(dwarf_loc_regs_t*);
|
||||
bool cfa_def_cfa_expression(dwarf_loc_regs_t*);
|
||||
bool cfa_expression(dwarf_loc_regs_t*);
|
||||
bool cfa_offset_extended_sf(dwarf_loc_regs_t*);
|
||||
bool cfa_def_cfa_sf(dwarf_loc_regs_t*);
|
||||
bool cfa_def_cfa_offset_sf(dwarf_loc_regs_t*);
|
||||
bool cfa_val_offset(dwarf_loc_regs_t*);
|
||||
bool cfa_val_offset_sf(dwarf_loc_regs_t*);
|
||||
bool cfa_val_expression(dwarf_loc_regs_t*);
|
||||
bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
|
||||
bool cfa_aarch64_negate_ra_state(dwarf_loc_regs_t*);
|
||||
|
||||
using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
|
||||
constexpr static process_func kCallbackTable[64] = {
|
||||
// 0x00 DW_CFA_nop
|
||||
&DwarfCfa::cfa_nop,
|
||||
// 0x01 DW_CFA_set_loc
|
||||
&DwarfCfa::cfa_set_loc,
|
||||
// 0x02 DW_CFA_advance_loc1
|
||||
&DwarfCfa::cfa_advance_loc,
|
||||
// 0x03 DW_CFA_advance_loc2
|
||||
&DwarfCfa::cfa_advance_loc,
|
||||
// 0x04 DW_CFA_advance_loc4
|
||||
&DwarfCfa::cfa_advance_loc,
|
||||
// 0x05 DW_CFA_offset_extended
|
||||
&DwarfCfa::cfa_offset,
|
||||
// 0x06 DW_CFA_restore_extended
|
||||
&DwarfCfa::cfa_restore,
|
||||
// 0x07 DW_CFA_undefined
|
||||
&DwarfCfa::cfa_undefined,
|
||||
// 0x08 DW_CFA_same_value
|
||||
&DwarfCfa::cfa_same_value,
|
||||
// 0x09 DW_CFA_register
|
||||
&DwarfCfa::cfa_register,
|
||||
// 0x0a DW_CFA_remember_state
|
||||
&DwarfCfa::cfa_remember_state,
|
||||
// 0x0b DW_CFA_restore_state
|
||||
&DwarfCfa::cfa_restore_state,
|
||||
// 0x0c DW_CFA_def_cfa
|
||||
&DwarfCfa::cfa_def_cfa,
|
||||
// 0x0d DW_CFA_def_cfa_register
|
||||
&DwarfCfa::cfa_def_cfa_register,
|
||||
// 0x0e DW_CFA_def_cfa_offset
|
||||
&DwarfCfa::cfa_def_cfa_offset,
|
||||
// 0x0f DW_CFA_def_cfa_expression
|
||||
&DwarfCfa::cfa_def_cfa_expression,
|
||||
// 0x10 DW_CFA_expression
|
||||
&DwarfCfa::cfa_expression,
|
||||
// 0x11 DW_CFA_offset_extended_sf
|
||||
&DwarfCfa::cfa_offset_extended_sf,
|
||||
// 0x12 DW_CFA_def_cfa_sf
|
||||
&DwarfCfa::cfa_def_cfa_sf,
|
||||
// 0x13 DW_CFA_def_cfa_offset_sf
|
||||
&DwarfCfa::cfa_def_cfa_offset_sf,
|
||||
// 0x14 DW_CFA_val_offset
|
||||
&DwarfCfa::cfa_val_offset,
|
||||
// 0x15 DW_CFA_val_offset_sf
|
||||
&DwarfCfa::cfa_val_offset_sf,
|
||||
// 0x16 DW_CFA_val_expression
|
||||
&DwarfCfa::cfa_val_expression,
|
||||
// 0x17 illegal cfa
|
||||
nullptr,
|
||||
// 0x18 illegal cfa
|
||||
nullptr,
|
||||
// 0x19 illegal cfa
|
||||
nullptr,
|
||||
// 0x1a illegal cfa
|
||||
nullptr,
|
||||
// 0x1b illegal cfa
|
||||
nullptr,
|
||||
// 0x1c DW_CFA_lo_user (Treat this as illegal)
|
||||
nullptr,
|
||||
// 0x1d illegal cfa
|
||||
nullptr,
|
||||
// 0x1e illegal cfa
|
||||
nullptr,
|
||||
// 0x1f illegal cfa
|
||||
nullptr,
|
||||
// 0x20 illegal cfa
|
||||
nullptr,
|
||||
// 0x21 illegal cfa
|
||||
nullptr,
|
||||
// 0x22 illegal cfa
|
||||
nullptr,
|
||||
// 0x23 illegal cfa
|
||||
nullptr,
|
||||
// 0x24 illegal cfa
|
||||
nullptr,
|
||||
// 0x25 illegal cfa
|
||||
nullptr,
|
||||
// 0x26 illegal cfa
|
||||
nullptr,
|
||||
// 0x27 illegal cfa
|
||||
nullptr,
|
||||
// 0x28 illegal cfa
|
||||
nullptr,
|
||||
// 0x29 illegal cfa
|
||||
nullptr,
|
||||
// 0x2a illegal cfa
|
||||
nullptr,
|
||||
// 0x2b illegal cfa
|
||||
nullptr,
|
||||
// 0x2c illegal cfa
|
||||
nullptr,
|
||||
// 0x2d DW_CFA_AARCH64_negate_ra_state (aarch64 only)
|
||||
// DW_CFA_GNU_window_save on other architectures.
|
||||
&DwarfCfa::cfa_aarch64_negate_ra_state,
|
||||
// 0x2e DW_CFA_GNU_args_size
|
||||
&DwarfCfa::cfa_nop,
|
||||
// 0x2f DW_CFA_GNU_negative_offset_extended
|
||||
&DwarfCfa::cfa_gnu_negative_offset_extended,
|
||||
// 0x30 illegal cfa
|
||||
nullptr,
|
||||
// 0x31 illegal cfa
|
||||
nullptr,
|
||||
// 0x32 illegal cfa
|
||||
nullptr,
|
||||
// 0x33 illegal cfa
|
||||
nullptr,
|
||||
// 0x34 illegal cfa
|
||||
nullptr,
|
||||
// 0x35 illegal cfa
|
||||
nullptr,
|
||||
// 0x36 illegal cfa
|
||||
nullptr,
|
||||
// 0x37 illegal cfa
|
||||
nullptr,
|
||||
// 0x38 illegal cfa
|
||||
nullptr,
|
||||
// 0x39 illegal cfa
|
||||
nullptr,
|
||||
// 0x3a illegal cfa
|
||||
nullptr,
|
||||
// 0x3b illegal cfa
|
||||
nullptr,
|
||||
// 0x3c illegal cfa
|
||||
nullptr,
|
||||
// 0x3d illegal cfa
|
||||
nullptr,
|
||||
// 0x3e illegal cfa
|
||||
nullptr,
|
||||
// 0x3f DW_CFA_hi_user (Treat this as illegal)
|
||||
nullptr,
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_CFA_H
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
|
||||
#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/DwarfSection.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
template <typename AddressType>
|
||||
class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
|
||||
public:
|
||||
DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
|
||||
this->cie32_value_ = static_cast<uint32_t>(-1);
|
||||
this->cie64_value_ = static_cast<uint64_t>(-1);
|
||||
}
|
||||
virtual ~DwarfDebugFrame() = default;
|
||||
|
||||
uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
|
||||
return this->entries_offset_ + pointer;
|
||||
}
|
||||
|
||||
uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
|
||||
return this->entries_offset_ + pointer;
|
||||
}
|
||||
|
||||
uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
|
||||
#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/DwarfSection.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
template <typename AddressType>
|
||||
class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
|
||||
public:
|
||||
DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
|
||||
virtual ~DwarfEhFrame() = default;
|
||||
|
||||
uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
|
||||
return this->memory_.cur_offset() - pointer - 4;
|
||||
}
|
||||
|
||||
uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
|
||||
return this->memory_.cur_offset() - pointer - 8;
|
||||
}
|
||||
|
||||
uint64_t AdjustPcFromFde(uint64_t pc) override {
|
||||
// The eh_frame uses relative pcs.
|
||||
return pc + this->memory_.cur_offset() - 4;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/DwarfError.h>
|
||||
#include <unwindstack/DwarfStructs.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "Check.h"
|
||||
#include "DwarfEhFrameWithHdr.h"
|
||||
#include "DwarfEncoding.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
static inline bool IsEncodingRelative(uint8_t encoding) {
|
||||
encoding >>= 4;
|
||||
return encoding > 0 && encoding <= DW_EH_PE_funcrel;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfEhFrameWithHdr<AddressType>::EhFrameInit(uint64_t offset, uint64_t size,
|
||||
int64_t section_bias) {
|
||||
return DwarfSectionImpl<AddressType>::Init(offset, size, section_bias);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t, int64_t section_bias) {
|
||||
memory_.clear_func_offset();
|
||||
memory_.clear_text_offset();
|
||||
memory_.set_data_offset(offset);
|
||||
memory_.set_cur_offset(offset);
|
||||
|
||||
hdr_section_bias_ = section_bias;
|
||||
|
||||
// Read the first four bytes all at once.
|
||||
uint8_t data[4];
|
||||
if (!memory_.ReadBytes(data, 4)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
version_ = data[0];
|
||||
if (version_ != 1) {
|
||||
// Unknown version.
|
||||
last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t ptr_encoding = data[1];
|
||||
uint8_t fde_count_encoding = data[2];
|
||||
table_encoding_ = data[3];
|
||||
table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
|
||||
|
||||
// If we can't perform a binary search on the entries, it's not worth
|
||||
// using this object. The calling code will fall back to the DwarfEhFrame
|
||||
// object in this case.
|
||||
if (table_entry_size_ == 0) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_.set_pc_offset(memory_.cur_offset());
|
||||
uint64_t ptr_offset;
|
||||
if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding, &ptr_offset)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_.set_pc_offset(memory_.cur_offset());
|
||||
if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fde_count_ == 0) {
|
||||
last_error_.code = DWARF_ERROR_NO_FDES;
|
||||
return false;
|
||||
}
|
||||
|
||||
hdr_entries_offset_ = memory_.cur_offset();
|
||||
hdr_entries_data_offset_ = offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
|
||||
uint64_t fde_offset;
|
||||
if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
const DwarfFde* fde = this->GetFdeFromOffset(fde_offset);
|
||||
if (fde == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// There is a possibility that this entry points to a zero length FDE
|
||||
// due to a bug. If this happens, try and find the non-zero length FDE
|
||||
// from eh_frame directly. See b/142483624.
|
||||
if (fde->pc_start == fde->pc_end) {
|
||||
fde = DwarfSectionImpl<AddressType>::GetFdeFromPc(pc);
|
||||
if (fde == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Guaranteed pc >= pc_start, need to check pc in the fde range.
|
||||
if (pc < fde->pc_end) {
|
||||
return fde;
|
||||
}
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
|
||||
DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
|
||||
auto entry = fde_info_.find(index);
|
||||
if (entry != fde_info_.end()) {
|
||||
return &fde_info_[index];
|
||||
}
|
||||
FdeInfo* info = &fde_info_[index];
|
||||
|
||||
memory_.set_data_offset(hdr_entries_data_offset_);
|
||||
memory_.set_cur_offset(hdr_entries_offset_ + 2 * index * table_entry_size_);
|
||||
memory_.set_pc_offset(0);
|
||||
uint64_t value;
|
||||
if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
|
||||
!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
fde_info_.erase(index);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Relative encodings require adding in the load bias.
|
||||
if (IsEncodingRelative(table_encoding_)) {
|
||||
value += hdr_section_bias_;
|
||||
}
|
||||
info->pc = value;
|
||||
return info;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
|
||||
if (fde_count_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t first = 0;
|
||||
size_t last = fde_count_;
|
||||
while (first < last) {
|
||||
size_t current = (first + last) / 2;
|
||||
const FdeInfo* info = GetFdeInfoFromIndex(current);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (pc == info->pc) {
|
||||
*fde_offset = info->offset;
|
||||
return true;
|
||||
}
|
||||
if (pc < info->pc) {
|
||||
last = current;
|
||||
} else {
|
||||
first = current + 1;
|
||||
}
|
||||
}
|
||||
if (last != 0) {
|
||||
const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
|
||||
if (info == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*fde_offset = info->offset;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
|
||||
for (size_t i = 0; i < fde_count_; i++) {
|
||||
const FdeInfo* info = GetFdeInfoFromIndex(i);
|
||||
if (info == nullptr) {
|
||||
break;
|
||||
}
|
||||
const DwarfFde* fde = this->GetFdeFromOffset(info->offset);
|
||||
if (fde == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
// There is a possibility that this entry points to a zero length FDE
|
||||
// due to a bug. If this happens, try and find the non-zero length FDE
|
||||
// from eh_frame directly. See b/142483624.
|
||||
if (fde->pc_start == fde->pc_end) {
|
||||
const DwarfFde* fde_real = DwarfSectionImpl<AddressType>::GetFdeFromPc(fde->pc_start);
|
||||
if (fde_real != nullptr) {
|
||||
fde = fde_real;
|
||||
}
|
||||
}
|
||||
fdes->push_back(fde);
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly instantiate DwarfEhFrameWithHdr
|
||||
template class DwarfEhFrameWithHdr<uint32_t>;
|
||||
template class DwarfEhFrameWithHdr<uint64_t>;
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
|
||||
#define _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <unwindstack/DwarfSection.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Forward declarations.
|
||||
class Memory;
|
||||
|
||||
template <typename AddressType>
|
||||
class DwarfEhFrameWithHdr : public DwarfSectionImpl<AddressType> {
|
||||
public:
|
||||
// Add these so that the protected members of DwarfSectionImpl
|
||||
// can be accessed without needing a this->.
|
||||
using DwarfSectionImpl<AddressType>::memory_;
|
||||
using DwarfSectionImpl<AddressType>::last_error_;
|
||||
|
||||
struct FdeInfo {
|
||||
AddressType pc;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
DwarfEhFrameWithHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
|
||||
virtual ~DwarfEhFrameWithHdr() = default;
|
||||
|
||||
uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
|
||||
return memory_.cur_offset() - pointer - 4;
|
||||
}
|
||||
|
||||
uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
|
||||
return memory_.cur_offset() - pointer - 8;
|
||||
}
|
||||
|
||||
uint64_t AdjustPcFromFde(uint64_t pc) override {
|
||||
// The eh_frame uses relative pcs.
|
||||
return pc + memory_.cur_offset() - 4;
|
||||
}
|
||||
|
||||
bool EhFrameInit(uint64_t offset, uint64_t size, int64_t section_bias);
|
||||
bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
|
||||
|
||||
const DwarfFde* GetFdeFromPc(uint64_t pc) override;
|
||||
|
||||
bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset);
|
||||
|
||||
const FdeInfo* GetFdeInfoFromIndex(size_t index);
|
||||
|
||||
void GetFdes(std::vector<const DwarfFde*>* fdes) override;
|
||||
|
||||
protected:
|
||||
uint8_t version_ = 0;
|
||||
uint8_t table_encoding_ = 0;
|
||||
size_t table_entry_size_ = 0;
|
||||
|
||||
uint64_t hdr_entries_offset_ = 0;
|
||||
uint64_t hdr_entries_data_offset_ = 0;
|
||||
uint64_t hdr_section_bias_ = 0;
|
||||
|
||||
uint64_t fde_count_ = 0;
|
||||
std::unordered_map<uint64_t, FdeInfo> fde_info_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DWARF_ENCODING_H
|
||||
#define _LIBUNWINDSTACK_DWARF_ENCODING_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
enum DwarfEncoding : uint8_t {
|
||||
DW_EH_PE_omit = 0xff,
|
||||
|
||||
DW_EH_PE_absptr = 0x00,
|
||||
DW_EH_PE_uleb128 = 0x01,
|
||||
DW_EH_PE_udata2 = 0x02,
|
||||
DW_EH_PE_udata4 = 0x03,
|
||||
DW_EH_PE_udata8 = 0x04,
|
||||
DW_EH_PE_sleb128 = 0x09,
|
||||
DW_EH_PE_sdata2 = 0x0a,
|
||||
DW_EH_PE_sdata4 = 0x0b,
|
||||
DW_EH_PE_sdata8 = 0x0c,
|
||||
|
||||
DW_EH_PE_pcrel = 0x10,
|
||||
DW_EH_PE_textrel = 0x20,
|
||||
DW_EH_PE_datarel = 0x30,
|
||||
DW_EH_PE_funcrel = 0x40,
|
||||
DW_EH_PE_aligned = 0x50,
|
||||
|
||||
// The following are special values used to encode CFA and OP operands.
|
||||
DW_EH_PE_udata1 = 0x0d,
|
||||
DW_EH_PE_sdata1 = 0x0e,
|
||||
DW_EH_PE_block = 0x0f,
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <unwindstack/DwarfMemory.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "Check.h"
|
||||
#include "DwarfEncoding.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
|
||||
if (!memory_->ReadFully(cur_offset_, dst, num_bytes)) {
|
||||
return false;
|
||||
}
|
||||
cur_offset_ += num_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename SignedType>
|
||||
bool DwarfMemory::ReadSigned(uint64_t* value) {
|
||||
SignedType signed_value;
|
||||
if (!ReadBytes(&signed_value, sizeof(SignedType))) {
|
||||
return false;
|
||||
}
|
||||
*value = static_cast<int64_t>(signed_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfMemory::ReadULEB128(uint64_t* value) {
|
||||
uint64_t cur_value = 0;
|
||||
uint64_t shift = 0;
|
||||
uint8_t byte;
|
||||
do {
|
||||
if (!ReadBytes(&byte, 1)) {
|
||||
return false;
|
||||
}
|
||||
cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (byte & 0x80);
|
||||
*value = cur_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfMemory::ReadSLEB128(int64_t* value) {
|
||||
uint64_t cur_value = 0;
|
||||
uint64_t shift = 0;
|
||||
uint8_t byte;
|
||||
do {
|
||||
if (!ReadBytes(&byte, 1)) {
|
||||
return false;
|
||||
}
|
||||
cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (byte & 0x80);
|
||||
if (byte & 0x40) {
|
||||
// Negative value, need to sign extend.
|
||||
cur_value |= static_cast<uint64_t>(-1) << shift;
|
||||
}
|
||||
*value = static_cast<int64_t>(cur_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
size_t DwarfMemory::GetEncodedSize(uint8_t encoding) {
|
||||
switch (encoding & 0x0f) {
|
||||
case DW_EH_PE_absptr:
|
||||
return sizeof(AddressType);
|
||||
case DW_EH_PE_udata1:
|
||||
case DW_EH_PE_sdata1:
|
||||
return 1;
|
||||
case DW_EH_PE_udata2:
|
||||
case DW_EH_PE_sdata2:
|
||||
return 2;
|
||||
case DW_EH_PE_udata4:
|
||||
case DW_EH_PE_sdata4:
|
||||
return 4;
|
||||
case DW_EH_PE_udata8:
|
||||
case DW_EH_PE_sdata8:
|
||||
return 8;
|
||||
case DW_EH_PE_uleb128:
|
||||
case DW_EH_PE_sleb128:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
|
||||
CHECK((encoding & 0x0f) == 0);
|
||||
|
||||
// Handle the encoding.
|
||||
switch (encoding) {
|
||||
case DW_EH_PE_absptr:
|
||||
// Nothing to do.
|
||||
break;
|
||||
case DW_EH_PE_pcrel:
|
||||
if (pc_offset_ == INT64_MAX) {
|
||||
// Unsupported encoding.
|
||||
return false;
|
||||
}
|
||||
*value += pc_offset_;
|
||||
break;
|
||||
case DW_EH_PE_textrel:
|
||||
if (text_offset_ == static_cast<uint64_t>(-1)) {
|
||||
// Unsupported encoding.
|
||||
return false;
|
||||
}
|
||||
*value += text_offset_;
|
||||
break;
|
||||
case DW_EH_PE_datarel:
|
||||
if (data_offset_ == static_cast<uint64_t>(-1)) {
|
||||
// Unsupported encoding.
|
||||
return false;
|
||||
}
|
||||
*value += data_offset_;
|
||||
break;
|
||||
case DW_EH_PE_funcrel:
|
||||
if (func_offset_ == static_cast<uint64_t>(-1)) {
|
||||
// Unsupported encoding.
|
||||
return false;
|
||||
}
|
||||
*value += func_offset_;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) {
|
||||
if (encoding == DW_EH_PE_omit) {
|
||||
*value = 0;
|
||||
return true;
|
||||
} else if (encoding == DW_EH_PE_aligned) {
|
||||
if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) {
|
||||
return false;
|
||||
}
|
||||
cur_offset_ &= -sizeof(AddressType);
|
||||
|
||||
if (sizeof(AddressType) != sizeof(uint64_t)) {
|
||||
*value = 0;
|
||||
}
|
||||
return ReadBytes(value, sizeof(AddressType));
|
||||
}
|
||||
|
||||
// Get the data.
|
||||
switch (encoding & 0x0f) {
|
||||
case DW_EH_PE_absptr:
|
||||
if (sizeof(AddressType) != sizeof(uint64_t)) {
|
||||
*value = 0;
|
||||
}
|
||||
if (!ReadBytes(value, sizeof(AddressType))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DW_EH_PE_uleb128:
|
||||
if (!ReadULEB128(value)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DW_EH_PE_sleb128:
|
||||
int64_t signed_value;
|
||||
if (!ReadSLEB128(&signed_value)) {
|
||||
return false;
|
||||
}
|
||||
*value = static_cast<uint64_t>(signed_value);
|
||||
break;
|
||||
case DW_EH_PE_udata1: {
|
||||
uint8_t value8;
|
||||
if (!ReadBytes(&value8, 1)) {
|
||||
return false;
|
||||
}
|
||||
*value = value8;
|
||||
} break;
|
||||
case DW_EH_PE_sdata1:
|
||||
if (!ReadSigned<int8_t>(value)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DW_EH_PE_udata2: {
|
||||
uint16_t value16;
|
||||
if (!ReadBytes(&value16, 2)) {
|
||||
return false;
|
||||
}
|
||||
*value = value16;
|
||||
} break;
|
||||
case DW_EH_PE_sdata2:
|
||||
if (!ReadSigned<int16_t>(value)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DW_EH_PE_udata4: {
|
||||
uint32_t value32;
|
||||
if (!ReadBytes(&value32, 4)) {
|
||||
return false;
|
||||
}
|
||||
*value = value32;
|
||||
} break;
|
||||
case DW_EH_PE_sdata4:
|
||||
if (!ReadSigned<int32_t>(value)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DW_EH_PE_udata8:
|
||||
if (!ReadBytes(value, sizeof(uint64_t))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DW_EH_PE_sdata8:
|
||||
if (!ReadSigned<int64_t>(value)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return AdjustEncodedValue(encoding & 0x70, value);
|
||||
}
|
||||
|
||||
// Instantiate all of the needed template functions.
|
||||
template bool DwarfMemory::ReadSigned<int8_t>(uint64_t*);
|
||||
template bool DwarfMemory::ReadSigned<int16_t>(uint64_t*);
|
||||
template bool DwarfMemory::ReadSigned<int32_t>(uint64_t*);
|
||||
template bool DwarfMemory::ReadSigned<int64_t>(uint64_t*);
|
||||
|
||||
template size_t DwarfMemory::GetEncodedSize<uint32_t>(uint8_t);
|
||||
template size_t DwarfMemory::GetEncodedSize<uint64_t>(uint8_t);
|
||||
|
||||
template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
|
||||
template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
|
||||
|
||||
} // namespace unwindstack
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DWARF_OP_H
|
||||
#define _LIBUNWINDSTACK_DWARF_OP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/DwarfError.h>
|
||||
|
||||
#include "DwarfEncoding.h"
|
||||
#include "RegsInfo.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Forward declarations.
|
||||
class DwarfMemory;
|
||||
class Memory;
|
||||
template <typename AddressType>
|
||||
class RegsImpl;
|
||||
|
||||
template <typename AddressType>
|
||||
class DwarfOp {
|
||||
// Signed version of AddressType
|
||||
typedef typename std::make_signed<AddressType>::type SignedType;
|
||||
|
||||
public:
|
||||
DwarfOp(DwarfMemory* memory, Memory* regular_memory)
|
||||
: memory_(memory), regular_memory_(regular_memory) {}
|
||||
virtual ~DwarfOp() = default;
|
||||
|
||||
bool Decode();
|
||||
|
||||
bool Eval(uint64_t start, uint64_t end);
|
||||
|
||||
void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
|
||||
|
||||
AddressType StackAt(size_t index) { return stack_[index]; }
|
||||
size_t StackSize() { return stack_.size(); }
|
||||
|
||||
void set_regs_info(RegsInfo<AddressType>* regs_info) { regs_info_ = regs_info; }
|
||||
|
||||
const DwarfErrorData& last_error() { return last_error_; }
|
||||
DwarfErrorCode LastErrorCode() { return last_error_.code; }
|
||||
uint64_t LastErrorAddress() { return last_error_.address; }
|
||||
|
||||
bool dex_pc_set() { return dex_pc_set_; }
|
||||
|
||||
bool is_register() { return is_register_; }
|
||||
|
||||
uint8_t cur_op() { return cur_op_; }
|
||||
|
||||
Memory* regular_memory() { return regular_memory_; }
|
||||
|
||||
protected:
|
||||
AddressType OperandAt(size_t index) { return operands_[index]; }
|
||||
size_t OperandsSize() { return operands_.size(); }
|
||||
|
||||
AddressType StackPop() {
|
||||
AddressType value = stack_.front();
|
||||
stack_.pop_front();
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
DwarfMemory* memory_;
|
||||
Memory* regular_memory_;
|
||||
|
||||
RegsInfo<AddressType>* regs_info_;
|
||||
bool dex_pc_set_ = false;
|
||||
bool is_register_ = false;
|
||||
DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
|
||||
uint8_t cur_op_;
|
||||
std::vector<AddressType> operands_;
|
||||
std::deque<AddressType> stack_;
|
||||
|
||||
inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; }
|
||||
|
||||
// Op processing functions.
|
||||
bool op_deref();
|
||||
bool op_deref_size();
|
||||
bool op_push();
|
||||
bool op_dup();
|
||||
bool op_drop();
|
||||
bool op_over();
|
||||
bool op_pick();
|
||||
bool op_swap();
|
||||
bool op_rot();
|
||||
bool op_abs();
|
||||
bool op_and();
|
||||
bool op_div();
|
||||
bool op_minus();
|
||||
bool op_mod();
|
||||
bool op_mul();
|
||||
bool op_neg();
|
||||
bool op_not();
|
||||
bool op_or();
|
||||
bool op_plus();
|
||||
bool op_plus_uconst();
|
||||
bool op_shl();
|
||||
bool op_shr();
|
||||
bool op_shra();
|
||||
bool op_xor();
|
||||
bool op_bra();
|
||||
bool op_eq();
|
||||
bool op_ge();
|
||||
bool op_gt();
|
||||
bool op_le();
|
||||
bool op_lt();
|
||||
bool op_ne();
|
||||
bool op_skip();
|
||||
bool op_lit();
|
||||
bool op_reg();
|
||||
bool op_regx();
|
||||
bool op_breg();
|
||||
bool op_bregx();
|
||||
bool op_nop();
|
||||
bool op_not_implemented();
|
||||
|
||||
using OpHandleFuncPtr = bool (DwarfOp::*)();
|
||||
static const OpHandleFuncPtr kOpHandleFuncList[];
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_OP_H
|
||||
|
|
@ -1,830 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/DwarfError.h>
|
||||
#include <unwindstack/DwarfLocation.h>
|
||||
#include <unwindstack/DwarfMemory.h>
|
||||
#include <unwindstack/DwarfSection.h>
|
||||
#include <unwindstack/DwarfStructs.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/Log.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#include "DwarfCfa.h"
|
||||
#include "DwarfDebugFrame.h"
|
||||
#include "DwarfEhFrame.h"
|
||||
#include "DwarfEncoding.h"
|
||||
#include "DwarfOp.h"
|
||||
#include "RegsInfo.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
|
||||
|
||||
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
|
||||
bool* is_signal_frame) {
|
||||
// Lookup the pc in the cache.
|
||||
auto it = loc_regs_.upper_bound(pc);
|
||||
if (it == loc_regs_.end() || pc < it->second.pc_start) {
|
||||
last_error_.code = DWARF_ERROR_NONE;
|
||||
const DwarfFde* fde = GetFdeFromPc(pc);
|
||||
if (fde == nullptr || fde->cie == nullptr) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now get the location information for this pc.
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
|
||||
return false;
|
||||
}
|
||||
loc_regs.cie = fde->cie;
|
||||
|
||||
// Store it in the cache.
|
||||
it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
|
||||
}
|
||||
|
||||
*is_signal_frame = it->second.cie->is_signal_frame;
|
||||
|
||||
// Now eval the actual registers.
|
||||
return Eval(it->second.cie, process_memory, it->second, regs, finished);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
const DwarfCie* DwarfSectionImpl<AddressType>::GetCieFromOffset(uint64_t offset) {
|
||||
auto cie_entry = cie_entries_.find(offset);
|
||||
if (cie_entry != cie_entries_.end()) {
|
||||
return &cie_entry->second;
|
||||
}
|
||||
DwarfCie* cie = &cie_entries_[offset];
|
||||
memory_.set_data_offset(entries_offset_);
|
||||
memory_.set_cur_offset(offset);
|
||||
if (!FillInCieHeader(cie) || !FillInCie(cie)) {
|
||||
// Erase the cached entry.
|
||||
cie_entries_.erase(offset);
|
||||
return nullptr;
|
||||
}
|
||||
return cie;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::FillInCieHeader(DwarfCie* cie) {
|
||||
cie->lsda_encoding = DW_EH_PE_omit;
|
||||
uint32_t length32;
|
||||
if (!memory_.ReadBytes(&length32, sizeof(length32))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
if (length32 == static_cast<uint32_t>(-1)) {
|
||||
// 64 bit Cie
|
||||
uint64_t length64;
|
||||
if (!memory_.ReadBytes(&length64, sizeof(length64))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
cie->cfa_instructions_end = memory_.cur_offset() + length64;
|
||||
cie->fde_address_encoding = DW_EH_PE_sdata8;
|
||||
|
||||
uint64_t cie_id;
|
||||
if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
if (cie_id != cie64_value_) {
|
||||
// This is not a Cie, something has gone horribly wrong.
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 32 bit Cie
|
||||
cie->cfa_instructions_end = memory_.cur_offset() + length32;
|
||||
cie->fde_address_encoding = DW_EH_PE_sdata4;
|
||||
|
||||
uint32_t cie_id;
|
||||
if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
if (cie_id != cie32_value_) {
|
||||
// This is not a Cie, something has gone horribly wrong.
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
|
||||
if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cie->version != 1 && cie->version != 3 && cie->version != 4 && cie->version != 5) {
|
||||
// Unrecognized version.
|
||||
last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the augmentation string.
|
||||
char aug_value;
|
||||
do {
|
||||
if (!memory_.ReadBytes(&aug_value, 1)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
cie->augmentation_string.push_back(aug_value);
|
||||
} while (aug_value != '\0');
|
||||
|
||||
if (cie->version == 4 || cie->version == 5) {
|
||||
// Skip the Address Size field since we only use it for validation.
|
||||
memory_.set_cur_offset(memory_.cur_offset() + 1);
|
||||
|
||||
// Segment Size
|
||||
if (!memory_.ReadBytes(&cie->segment_size, 1)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Code Alignment Factor
|
||||
if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Data Alignment Factor
|
||||
if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cie->version == 1) {
|
||||
// Return Address is a single byte.
|
||||
uint8_t return_address_register;
|
||||
if (!memory_.ReadBytes(&return_address_register, 1)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
cie->return_address_register = return_address_register;
|
||||
} else if (!memory_.ReadULEB128(&cie->return_address_register)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cie->augmentation_string[0] != 'z') {
|
||||
cie->cfa_instructions_offset = memory_.cur_offset();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t aug_length;
|
||||
if (!memory_.ReadULEB128(&aug_length)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
|
||||
|
||||
for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
|
||||
switch (cie->augmentation_string[i]) {
|
||||
case 'L':
|
||||
if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'P': {
|
||||
uint8_t encoding;
|
||||
if (!memory_.ReadBytes(&encoding, 1)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
memory_.set_pc_offset(pc_offset_);
|
||||
if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case 'R':
|
||||
if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
cie->is_signal_frame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
|
||||
auto fde_entry = fde_entries_.find(offset);
|
||||
if (fde_entry != fde_entries_.end()) {
|
||||
return &fde_entry->second;
|
||||
}
|
||||
DwarfFde* fde = &fde_entries_[offset];
|
||||
memory_.set_data_offset(entries_offset_);
|
||||
memory_.set_cur_offset(offset);
|
||||
if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
|
||||
fde_entries_.erase(offset);
|
||||
return nullptr;
|
||||
}
|
||||
return fde;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::FillInFdeHeader(DwarfFde* fde) {
|
||||
uint32_t length32;
|
||||
if (!memory_.ReadBytes(&length32, sizeof(length32))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length32 == static_cast<uint32_t>(-1)) {
|
||||
// 64 bit Fde.
|
||||
uint64_t length64;
|
||||
if (!memory_.ReadBytes(&length64, sizeof(length64))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
fde->cfa_instructions_end = memory_.cur_offset() + length64;
|
||||
|
||||
uint64_t value64;
|
||||
if (!memory_.ReadBytes(&value64, sizeof(value64))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
if (value64 == cie64_value_) {
|
||||
// This is a Cie, this means something has gone wrong.
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the Cie pointer, which is necessary to properly read the rest of
|
||||
// of the Fde information.
|
||||
fde->cie_offset = GetCieOffsetFromFde64(value64);
|
||||
} else {
|
||||
// 32 bit Fde.
|
||||
fde->cfa_instructions_end = memory_.cur_offset() + length32;
|
||||
|
||||
uint32_t value32;
|
||||
if (!memory_.ReadBytes(&value32, sizeof(value32))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
if (value32 == cie32_value_) {
|
||||
// This is a Cie, this means something has gone wrong.
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the Cie pointer, which is necessary to properly read the rest of
|
||||
// of the Fde information.
|
||||
fde->cie_offset = GetCieOffsetFromFde32(value32);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
|
||||
uint64_t cur_offset = memory_.cur_offset();
|
||||
|
||||
const DwarfCie* cie = GetCieFromOffset(fde->cie_offset);
|
||||
if (cie == nullptr) {
|
||||
return false;
|
||||
}
|
||||
fde->cie = cie;
|
||||
|
||||
if (cie->segment_size != 0) {
|
||||
// Skip over the segment selector for now.
|
||||
cur_offset += cie->segment_size;
|
||||
}
|
||||
memory_.set_cur_offset(cur_offset);
|
||||
|
||||
// The load bias only applies to the start.
|
||||
memory_.set_pc_offset(section_bias_);
|
||||
bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
|
||||
fde->pc_start = AdjustPcFromFde(fde->pc_start);
|
||||
|
||||
memory_.set_pc_offset(0);
|
||||
if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
fde->pc_end += fde->pc_start;
|
||||
|
||||
if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
|
||||
// Augmentation Size
|
||||
uint64_t aug_length;
|
||||
if (!memory_.ReadULEB128(&aug_length)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
uint64_t cur_offset = memory_.cur_offset();
|
||||
|
||||
memory_.set_pc_offset(pc_offset_);
|
||||
if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set our position to after all of the augmentation data.
|
||||
memory_.set_cur_offset(cur_offset + aug_length);
|
||||
}
|
||||
fde->cfa_instructions_offset = memory_.cur_offset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
|
||||
AddressType* value,
|
||||
RegsInfo<AddressType>* regs_info,
|
||||
bool* is_dex_pc) {
|
||||
DwarfOp<AddressType> op(&memory_, regular_memory);
|
||||
op.set_regs_info(regs_info);
|
||||
|
||||
// Need to evaluate the op data.
|
||||
uint64_t end = loc.values[1];
|
||||
uint64_t start = end - loc.values[0];
|
||||
if (!op.Eval(start, end)) {
|
||||
last_error_ = op.last_error();
|
||||
return false;
|
||||
}
|
||||
if (op.StackSize() == 0) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
// We don't support an expression that evaluates to a register number.
|
||||
if (op.is_register()) {
|
||||
last_error_.code = DWARF_ERROR_NOT_IMPLEMENTED;
|
||||
return false;
|
||||
}
|
||||
*value = op.StackAt(0);
|
||||
if (is_dex_pc != nullptr && op.dex_pc_set()) {
|
||||
*is_dex_pc = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
struct EvalInfo {
|
||||
const dwarf_loc_regs_t* loc_regs;
|
||||
const DwarfCie* cie;
|
||||
Memory* regular_memory;
|
||||
AddressType cfa;
|
||||
bool return_address_undefined = false;
|
||||
RegsInfo<AddressType> regs_info;
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint32_t reg,
|
||||
AddressType* reg_ptr, void* info) {
|
||||
EvalInfo<AddressType>* eval_info = reinterpret_cast<EvalInfo<AddressType>*>(info);
|
||||
Memory* regular_memory = eval_info->regular_memory;
|
||||
switch (loc->type) {
|
||||
case DWARF_LOCATION_OFFSET:
|
||||
if (!regular_memory->ReadFully(eval_info->cfa + loc->values[0], reg_ptr, sizeof(AddressType))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = eval_info->cfa + loc->values[0];
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DWARF_LOCATION_VAL_OFFSET:
|
||||
*reg_ptr = eval_info->cfa + loc->values[0];
|
||||
break;
|
||||
case DWARF_LOCATION_REGISTER: {
|
||||
uint32_t cur_reg = loc->values[0];
|
||||
if (cur_reg >= eval_info->regs_info.Total()) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
*reg_ptr = eval_info->regs_info.Get(cur_reg) + loc->values[1];
|
||||
break;
|
||||
}
|
||||
case DWARF_LOCATION_EXPRESSION:
|
||||
case DWARF_LOCATION_VAL_EXPRESSION: {
|
||||
AddressType value;
|
||||
bool is_dex_pc = false;
|
||||
if (!EvalExpression(*loc, regular_memory, &value, &eval_info->regs_info, &is_dex_pc)) {
|
||||
return false;
|
||||
}
|
||||
if (loc->type == DWARF_LOCATION_EXPRESSION) {
|
||||
if (!regular_memory->ReadFully(value, reg_ptr, sizeof(AddressType))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = value;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*reg_ptr = value;
|
||||
if (is_dex_pc) {
|
||||
eval_info->regs_info.regs->set_dex_pc(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DWARF_LOCATION_UNDEFINED:
|
||||
if (reg == eval_info->cie->return_address_register) {
|
||||
eval_info->return_address_undefined = true;
|
||||
}
|
||||
break;
|
||||
case DWARF_LOCATION_PSEUDO_REGISTER:
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
|
||||
const dwarf_loc_regs_t& loc_regs, Regs* regs,
|
||||
bool* finished) {
|
||||
RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
|
||||
if (cie->return_address_register >= cur_regs->total_regs()) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the cfa value;
|
||||
auto cfa_entry = loc_regs.find(CFA_REG);
|
||||
if (cfa_entry == loc_regs.end()) {
|
||||
last_error_.code = DWARF_ERROR_CFA_NOT_DEFINED;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always set the dex pc to zero when evaluating.
|
||||
cur_regs->set_dex_pc(0);
|
||||
|
||||
// Reset necessary pseudo registers before evaluation.
|
||||
// This is needed for ARM64, for example.
|
||||
regs->ResetPseudoRegisters();
|
||||
|
||||
EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
|
||||
.cie = cie,
|
||||
.regular_memory = regular_memory,
|
||||
.regs_info = RegsInfo<AddressType>(cur_regs)};
|
||||
const DwarfLocation* loc = &cfa_entry->second;
|
||||
// Only a few location types are valid for the cfa.
|
||||
switch (loc->type) {
|
||||
case DWARF_LOCATION_REGISTER:
|
||||
if (loc->values[0] >= cur_regs->total_regs()) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
eval_info.cfa = (*cur_regs)[loc->values[0]];
|
||||
eval_info.cfa += loc->values[1];
|
||||
break;
|
||||
case DWARF_LOCATION_VAL_EXPRESSION: {
|
||||
AddressType value;
|
||||
if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
// There is only one type of valid expression for CFA evaluation.
|
||||
eval_info.cfa = value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& entry : loc_regs) {
|
||||
uint32_t reg = entry.first;
|
||||
// Already handled the CFA register.
|
||||
if (reg == CFA_REG) continue;
|
||||
|
||||
AddressType* reg_ptr;
|
||||
if (reg >= cur_regs->total_regs()) {
|
||||
if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
|
||||
// Skip this unknown register.
|
||||
continue;
|
||||
}
|
||||
if (!eval_info.regs_info.regs->SetPseudoRegister(reg, entry.second.values[0])) {
|
||||
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
reg_ptr = eval_info.regs_info.Save(reg);
|
||||
if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the return address location.
|
||||
if (eval_info.return_address_undefined) {
|
||||
cur_regs->set_pc(0);
|
||||
} else {
|
||||
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
|
||||
}
|
||||
|
||||
// If the pc was set to zero, consider this the final frame. Exception: if
|
||||
// this is the sigreturn frame, then we want to try to recover the real PC
|
||||
// using the return address (from LR or the stack), so keep going.
|
||||
*finished = (cur_regs->pc() == 0 && !cie->is_signal_frame) ? true : false;
|
||||
|
||||
cur_regs->set_sp(eval_info.cfa);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
|
||||
dwarf_loc_regs_t* loc_regs, ArchEnum arch) {
|
||||
DwarfCfa<AddressType> cfa(&memory_, fde, arch);
|
||||
|
||||
// Look for the cached copy of the cie data.
|
||||
auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
|
||||
if (reg_entry == cie_loc_regs_.end()) {
|
||||
if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
|
||||
loc_regs)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
cie_loc_regs_[fde->cie_offset] = *loc_regs;
|
||||
}
|
||||
cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
|
||||
if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde,
|
||||
ArchEnum arch) {
|
||||
DwarfCfa<AddressType> cfa(&memory_, fde, arch);
|
||||
|
||||
// Always print the cie information.
|
||||
const DwarfCie* cie = fde->cie;
|
||||
if (!cfa.Log(indent, pc, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
if (!cfa.Log(indent, pc, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
|
||||
section_bias_ = section_bias;
|
||||
entries_offset_ = offset;
|
||||
next_entries_offset_ = offset;
|
||||
entries_end_ = offset + size;
|
||||
|
||||
memory_.clear_func_offset();
|
||||
memory_.clear_text_offset();
|
||||
memory_.set_cur_offset(offset);
|
||||
pc_offset_ = offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a cached version of the fde information such that it is a std::map
|
||||
// that is indexed by end pc and contains a pair that represents the start pc
|
||||
// followed by the fde object. The fde pointers are owned by fde_entries_
|
||||
// and not by the map object.
|
||||
// It is possible for an fde to be represented by multiple entries in
|
||||
// the map. This can happen if the the start pc and end pc overlap already
|
||||
// existing entries. For example, if there is already an entry of 0x400, 0x200,
|
||||
// and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
|
||||
// will be added: 0x200, 0x100 and 0x500, 0x400.
|
||||
template <typename AddressType>
|
||||
void DwarfSectionImpl<AddressType>::InsertFde(const DwarfFde* fde) {
|
||||
uint64_t start = fde->pc_start;
|
||||
uint64_t end = fde->pc_end;
|
||||
auto it = fdes_.upper_bound(start);
|
||||
while (it != fdes_.end() && start < end && it->second.first < end) {
|
||||
if (start < it->second.first) {
|
||||
fdes_[it->second.first] = std::make_pair(start, fde);
|
||||
}
|
||||
start = it->first;
|
||||
++it;
|
||||
}
|
||||
if (start < end) {
|
||||
fdes_[end] = std::make_pair(start, fde);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::GetNextCieOrFde(const DwarfFde** fde_entry) {
|
||||
uint64_t start_offset = next_entries_offset_;
|
||||
|
||||
memory_.set_data_offset(entries_offset_);
|
||||
memory_.set_cur_offset(next_entries_offset_);
|
||||
uint32_t value32;
|
||||
if (!memory_.ReadBytes(&value32, sizeof(value32))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t cie_offset;
|
||||
uint8_t cie_fde_encoding;
|
||||
bool entry_is_cie = false;
|
||||
if (value32 == static_cast<uint32_t>(-1)) {
|
||||
// 64 bit entry.
|
||||
uint64_t value64;
|
||||
if (!memory_.ReadBytes(&value64, sizeof(value64))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
next_entries_offset_ = memory_.cur_offset() + value64;
|
||||
// Read the Cie Id of a Cie or the pointer of the Fde.
|
||||
if (!memory_.ReadBytes(&value64, sizeof(value64))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value64 == cie64_value_) {
|
||||
entry_is_cie = true;
|
||||
cie_fde_encoding = DW_EH_PE_sdata8;
|
||||
} else {
|
||||
cie_offset = GetCieOffsetFromFde64(value64);
|
||||
}
|
||||
} else {
|
||||
next_entries_offset_ = memory_.cur_offset() + value32;
|
||||
|
||||
// 32 bit Cie
|
||||
if (!memory_.ReadBytes(&value32, sizeof(value32))) {
|
||||
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
|
||||
last_error_.address = memory_.cur_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value32 == cie32_value_) {
|
||||
entry_is_cie = true;
|
||||
cie_fde_encoding = DW_EH_PE_sdata4;
|
||||
} else {
|
||||
cie_offset = GetCieOffsetFromFde32(value32);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry_is_cie) {
|
||||
auto entry = cie_entries_.find(start_offset);
|
||||
if (entry == cie_entries_.end()) {
|
||||
DwarfCie* cie = &cie_entries_[start_offset];
|
||||
cie->lsda_encoding = DW_EH_PE_omit;
|
||||
cie->cfa_instructions_end = next_entries_offset_;
|
||||
cie->fde_address_encoding = cie_fde_encoding;
|
||||
|
||||
if (!FillInCie(cie)) {
|
||||
cie_entries_.erase(start_offset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*fde_entry = nullptr;
|
||||
} else {
|
||||
auto entry = fde_entries_.find(start_offset);
|
||||
if (entry != fde_entries_.end()) {
|
||||
*fde_entry = &entry->second;
|
||||
} else {
|
||||
DwarfFde* fde = &fde_entries_[start_offset];
|
||||
fde->cfa_instructions_end = next_entries_offset_;
|
||||
fde->cie_offset = cie_offset;
|
||||
|
||||
if (!FillInFde(fde)) {
|
||||
fde_entries_.erase(start_offset);
|
||||
return false;
|
||||
}
|
||||
*fde_entry = fde;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void DwarfSectionImpl<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
|
||||
// Loop through the already cached entries.
|
||||
uint64_t entry_offset = entries_offset_;
|
||||
while (entry_offset < next_entries_offset_) {
|
||||
auto cie_it = cie_entries_.find(entry_offset);
|
||||
if (cie_it != cie_entries_.end()) {
|
||||
entry_offset = cie_it->second.cfa_instructions_end;
|
||||
} else {
|
||||
auto fde_it = fde_entries_.find(entry_offset);
|
||||
if (fde_it == fde_entries_.end()) {
|
||||
// No fde or cie at this entry, should not be possible.
|
||||
return;
|
||||
}
|
||||
entry_offset = fde_it->second.cfa_instructions_end;
|
||||
fdes->push_back(&fde_it->second);
|
||||
}
|
||||
}
|
||||
|
||||
while (next_entries_offset_ < entries_end_) {
|
||||
const DwarfFde* fde;
|
||||
if (!GetNextCieOrFde(&fde)) {
|
||||
break;
|
||||
}
|
||||
if (fde != nullptr) {
|
||||
InsertFde(fde);
|
||||
fdes->push_back(fde);
|
||||
}
|
||||
|
||||
if (next_entries_offset_ < memory_.cur_offset()) {
|
||||
// Simply consider the processing done in this case.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromPc(uint64_t pc) {
|
||||
// Search in the list of fdes we already have.
|
||||
auto it = fdes_.upper_bound(pc);
|
||||
if (it != fdes_.end()) {
|
||||
if (pc >= it->second.first) {
|
||||
return it->second.second;
|
||||
}
|
||||
}
|
||||
|
||||
// The section might have overlapping pcs in fdes, so it is necessary
|
||||
// to do a linear search of the fdes by pc. As fdes are read, a cached
|
||||
// search map is created.
|
||||
while (next_entries_offset_ < entries_end_) {
|
||||
const DwarfFde* fde;
|
||||
if (!GetNextCieOrFde(&fde)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (fde != nullptr) {
|
||||
InsertFde(fde);
|
||||
if (pc >= fde->pc_start && pc < fde->pc_end) {
|
||||
return fde;
|
||||
}
|
||||
}
|
||||
|
||||
if (next_entries_offset_ < memory_.cur_offset()) {
|
||||
// Simply consider the processing done in this case.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Explicitly instantiate DwarfSectionImpl
|
||||
template class DwarfSectionImpl<uint32_t>;
|
||||
template class DwarfSectionImpl<uint64_t>;
|
||||
|
||||
// Explicitly instantiate DwarfDebugFrame
|
||||
template class DwarfDebugFrame<uint32_t>;
|
||||
template class DwarfDebugFrame<uint64_t>;
|
||||
|
||||
// Explicitly instantiate DwarfEhFrame
|
||||
template class DwarfEhFrame<uint32_t>;
|
||||
template class DwarfEhFrame<uint64_t>;
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,422 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <elf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#define LOG_TAG "unwind"
|
||||
#include <log/log.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#include "ElfInterfaceArm.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
bool Elf::cache_enabled_;
|
||||
std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
|
||||
std::mutex* Elf::cache_lock_;
|
||||
|
||||
bool Elf::Init() {
|
||||
load_bias_ = 0;
|
||||
if (!memory_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
interface_.reset(CreateInterfaceFromMemory(memory_.get()));
|
||||
if (!interface_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
valid_ = interface_->Init(&load_bias_);
|
||||
if (valid_) {
|
||||
interface_->InitHeaders();
|
||||
InitGnuDebugdata();
|
||||
} else {
|
||||
interface_.reset(nullptr);
|
||||
}
|
||||
return valid_;
|
||||
}
|
||||
|
||||
// It is expensive to initialize the .gnu_debugdata section. Provide a method
|
||||
// to initialize this data separately.
|
||||
void Elf::InitGnuDebugdata() {
|
||||
if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory());
|
||||
gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
|
||||
ElfInterface* gnu = gnu_debugdata_interface_.get();
|
||||
if (gnu == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore the load_bias from the compressed section, the correct load bias
|
||||
// is in the uncompressed data.
|
||||
int64_t load_bias;
|
||||
if (gnu->Init(&load_bias)) {
|
||||
gnu->InitHeaders();
|
||||
interface_->SetGnuDebugdataInterface(gnu);
|
||||
} else {
|
||||
// Free all of the memory associated with the gnu_debugdata section.
|
||||
gnu_debugdata_memory_.reset(nullptr);
|
||||
gnu_debugdata_interface_.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Elf::Invalidate() {
|
||||
interface_.reset(nullptr);
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
std::string Elf::GetSoname() {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
if (!valid_) {
|
||||
return "";
|
||||
}
|
||||
return interface_->GetSoname();
|
||||
}
|
||||
|
||||
uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
|
||||
return pc - map_info->start + load_bias_ + map_info->elf_offset;
|
||||
}
|
||||
|
||||
bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
|
||||
(gnu_debugdata_interface_ &&
|
||||
gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
|
||||
}
|
||||
|
||||
bool Elf::GetGlobalVariableOffset(const std::string& name, uint64_t* memory_offset) {
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t vaddr;
|
||||
if (!interface_->GetGlobalVariable(name, &vaddr) &&
|
||||
(gnu_debugdata_interface_ == nullptr ||
|
||||
!gnu_debugdata_interface_->GetGlobalVariable(name, &vaddr))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arch() == ARCH_ARM64) {
|
||||
// Tagged pointer after Android R would lead top byte to have random values
|
||||
// https://source.android.com/devices/tech/debug/tagged-pointers
|
||||
vaddr &= (1ULL << 56) - 1;
|
||||
}
|
||||
|
||||
// Check the .data section.
|
||||
uint64_t vaddr_start = interface_->data_vaddr_start();
|
||||
if (vaddr >= vaddr_start && vaddr < interface_->data_vaddr_end()) {
|
||||
*memory_offset = vaddr - vaddr_start + interface_->data_offset();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the .dynamic section.
|
||||
vaddr_start = interface_->dynamic_vaddr_start();
|
||||
if (vaddr >= vaddr_start && vaddr < interface_->dynamic_vaddr_end()) {
|
||||
*memory_offset = vaddr - vaddr_start + interface_->dynamic_offset();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Elf::GetBuildID() {
|
||||
if (!valid_) {
|
||||
return "";
|
||||
}
|
||||
return interface_->GetBuildID();
|
||||
}
|
||||
|
||||
void Elf::GetLastError(ErrorData* data) {
|
||||
if (valid_) {
|
||||
*data = interface_->last_error();
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode Elf::GetLastErrorCode() {
|
||||
if (valid_) {
|
||||
return interface_->LastErrorCode();
|
||||
}
|
||||
return ERROR_INVALID_ELF;
|
||||
}
|
||||
|
||||
uint64_t Elf::GetLastErrorAddress() {
|
||||
if (valid_) {
|
||||
return interface_->LastErrorAddress();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The relative pc expectd by this function is relative to the start of the elf.
|
||||
bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the rel_pc to an elf_offset.
|
||||
if (rel_pc < static_cast<uint64_t>(load_bias_)) {
|
||||
return false;
|
||||
}
|
||||
return regs->StepIfSignalHandler(rel_pc - load_bias_, this, process_memory);
|
||||
}
|
||||
|
||||
// The relative pc is always relative to the start of the map from which it comes.
|
||||
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished,
|
||||
bool* is_signal_frame) {
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lock during the step which can update information in the object.
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
return interface_->Step(rel_pc, regs, process_memory, finished, is_signal_frame);
|
||||
}
|
||||
|
||||
bool Elf::IsValidElf(Memory* memory) {
|
||||
if (memory == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify that this is a valid elf file.
|
||||
uint8_t e_ident[SELFMAG + 1];
|
||||
if (!memory->ReadFully(0, e_ident, SELFMAG)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Elf::GetInfo(Memory* memory, uint64_t* size) {
|
||||
if (!IsValidElf(memory)) {
|
||||
return false;
|
||||
}
|
||||
*size = 0;
|
||||
|
||||
uint8_t class_type;
|
||||
if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the maximum size of the elf data from the header.
|
||||
if (class_type == ELFCLASS32) {
|
||||
ElfInterface32::GetMaxSize(memory, size);
|
||||
} else if (class_type == ELFCLASS64) {
|
||||
ElfInterface64::GetMaxSize(memory, size);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Elf::IsValidPc(uint64_t pc) {
|
||||
if (!valid_ || (load_bias_ > 0 && pc < static_cast<uint64_t>(load_bias_))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (interface_->IsValidPc(pc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->IsValidPc(pc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
|
||||
if (!IsValidElf(memory)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ElfInterface> interface;
|
||||
if (!memory->ReadFully(EI_CLASS, &class_type_, 1)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (class_type_ == ELFCLASS32) {
|
||||
Elf32_Half e_machine;
|
||||
if (!memory->ReadFully(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
machine_type_ = e_machine;
|
||||
if (e_machine == EM_ARM) {
|
||||
arch_ = ARCH_ARM;
|
||||
interface.reset(new ElfInterfaceArm(memory));
|
||||
} else if (e_machine == EM_386) {
|
||||
arch_ = ARCH_X86;
|
||||
interface.reset(new ElfInterface32(memory));
|
||||
} else if (e_machine == EM_MIPS) {
|
||||
arch_ = ARCH_MIPS;
|
||||
interface.reset(new ElfInterface32(memory));
|
||||
} else {
|
||||
// Unsupported.
|
||||
ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine);
|
||||
return nullptr;
|
||||
}
|
||||
} else if (class_type_ == ELFCLASS64) {
|
||||
Elf64_Half e_machine;
|
||||
if (!memory->ReadFully(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
machine_type_ = e_machine;
|
||||
if (e_machine == EM_AARCH64) {
|
||||
arch_ = ARCH_ARM64;
|
||||
} else if (e_machine == EM_X86_64) {
|
||||
arch_ = ARCH_X86_64;
|
||||
} else if (e_machine == EM_MIPS) {
|
||||
arch_ = ARCH_MIPS64;
|
||||
} else {
|
||||
// Unsupported.
|
||||
ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n",
|
||||
e_machine);
|
||||
return nullptr;
|
||||
}
|
||||
interface.reset(new ElfInterface64(memory));
|
||||
}
|
||||
|
||||
return interface.release();
|
||||
}
|
||||
|
||||
int64_t Elf::GetLoadBias(Memory* memory) {
|
||||
if (!IsValidElf(memory)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t class_type;
|
||||
if (!memory->Read(EI_CLASS, &class_type, 1)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (class_type == ELFCLASS32) {
|
||||
return ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(memory);
|
||||
} else if (class_type == ELFCLASS64) {
|
||||
return ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(memory);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Elf::SetCachingEnabled(bool enable) {
|
||||
if (!cache_enabled_ && enable) {
|
||||
cache_enabled_ = true;
|
||||
cache_ = new std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>;
|
||||
cache_lock_ = new std::mutex;
|
||||
} else if (cache_enabled_ && !enable) {
|
||||
cache_enabled_ = false;
|
||||
delete cache_;
|
||||
delete cache_lock_;
|
||||
}
|
||||
}
|
||||
|
||||
void Elf::CacheLock() {
|
||||
cache_lock_->lock();
|
||||
}
|
||||
|
||||
void Elf::CacheUnlock() {
|
||||
cache_lock_->unlock();
|
||||
}
|
||||
|
||||
void Elf::CacheAdd(MapInfo* info) {
|
||||
// If elf_offset != 0, then cache both name:offset and name.
|
||||
// The cached name is used to do lookups if multiple maps for the same
|
||||
// named elf file exist.
|
||||
// For example, if there are two maps boot.odex:1000 and boot.odex:2000
|
||||
// where each reference the entire boot.odex, the cache will properly
|
||||
// use the same cached elf object.
|
||||
|
||||
if (info->offset == 0 || info->elf_offset != 0) {
|
||||
(*cache_)[info->name] = std::make_pair(info->elf, true);
|
||||
}
|
||||
|
||||
if (info->offset != 0) {
|
||||
// The second element in the pair indicates whether elf_offset should
|
||||
// be set to offset when getting out of the cache.
|
||||
(*cache_)[info->name + ':' + std::to_string(info->offset)] =
|
||||
std::make_pair(info->elf, info->elf_offset != 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Elf::CacheAfterCreateMemory(MapInfo* info) {
|
||||
if (info->name.empty() || info->offset == 0 || info->elf_offset == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto entry = cache_->find(info->name);
|
||||
if (entry == cache_->end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In this case, the whole file is the elf, and the name has already
|
||||
// been cached. Add an entry at name:offset to get this directly out
|
||||
// of the cache next time.
|
||||
info->elf = entry->second.first;
|
||||
(*cache_)[info->name + ':' + std::to_string(info->offset)] = std::make_pair(info->elf, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Elf::CacheGet(MapInfo* info) {
|
||||
std::string name(info->name);
|
||||
if (info->offset != 0) {
|
||||
name += ':' + std::to_string(info->offset);
|
||||
}
|
||||
auto entry = cache_->find(name);
|
||||
if (entry != cache_->end()) {
|
||||
info->elf = entry->second.first;
|
||||
if (entry->second.second) {
|
||||
info->elf_offset = info->offset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Elf::GetBuildID(Memory* memory) {
|
||||
if (!IsValidElf(memory)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
uint8_t class_type;
|
||||
if (!memory->Read(EI_CLASS, &class_type, 1)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (class_type == ELFCLASS32) {
|
||||
return ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(memory);
|
||||
} else if (class_type == ELFCLASS64) {
|
||||
return ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(memory);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,720 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <7zCrc.h>
|
||||
#include <Xz.h>
|
||||
#include <XzCrc64.h>
|
||||
|
||||
#include <unwindstack/DwarfError.h>
|
||||
#include <unwindstack/DwarfSection.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/Log.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#include "DwarfDebugFrame.h"
|
||||
#include "DwarfEhFrame.h"
|
||||
#include "DwarfEhFrameWithHdr.h"
|
||||
#include "MemoryBuffer.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
ElfInterface::~ElfInterface() {
|
||||
for (auto symbol : symbols_) {
|
||||
delete symbol;
|
||||
}
|
||||
}
|
||||
|
||||
bool ElfInterface::IsValidPc(uint64_t pc) {
|
||||
if (!pt_loads_.empty()) {
|
||||
for (auto& entry : pt_loads_) {
|
||||
uint64_t start = entry.second.table_offset;
|
||||
uint64_t end = start + entry.second.table_size;
|
||||
if (pc >= start && pc < end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// No PT_LOAD data, look for a fde for this pc in the section data.
|
||||
if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Memory* ElfInterface::CreateGnuDebugdataMemory() {
|
||||
if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: Only call these initialization functions once.
|
||||
CrcGenerateTable();
|
||||
Crc64GenerateTable();
|
||||
|
||||
// Verify the request is not larger than the max size_t value.
|
||||
if (gnu_debugdata_size_ > SIZE_MAX) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t initial_buffer_size;
|
||||
if (__builtin_mul_overflow(5, gnu_debugdata_size_, &initial_buffer_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t buffer_increment;
|
||||
if (__builtin_mul_overflow(2, gnu_debugdata_size_, &buffer_increment)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> src(new (std::nothrow) uint8_t[gnu_debugdata_size_]);
|
||||
if (src.get() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
|
||||
if (!dst->Resize(initial_buffer_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!memory_->ReadFully(gnu_debugdata_offset_, src.get(), gnu_debugdata_size_)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ISzAlloc alloc;
|
||||
CXzUnpacker state;
|
||||
alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
|
||||
alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
|
||||
XzUnpacker_Construct(&state, &alloc);
|
||||
|
||||
int return_val;
|
||||
size_t src_offset = 0;
|
||||
size_t dst_offset = 0;
|
||||
ECoderStatus status;
|
||||
do {
|
||||
size_t src_remaining = gnu_debugdata_size_ - src_offset;
|
||||
size_t dst_remaining = dst->Size() - dst_offset;
|
||||
if (dst_remaining < buffer_increment) {
|
||||
size_t new_size;
|
||||
if (__builtin_add_overflow(dst->Size(), buffer_increment, &new_size) ||
|
||||
!dst->Resize(new_size)) {
|
||||
XzUnpacker_Free(&state);
|
||||
return nullptr;
|
||||
}
|
||||
dst_remaining += buffer_increment;
|
||||
}
|
||||
return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
|
||||
&src_remaining, true, CODER_FINISH_ANY, &status);
|
||||
src_offset += src_remaining;
|
||||
dst_offset += dst_remaining;
|
||||
} while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
|
||||
XzUnpacker_Free(&state);
|
||||
if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Shrink back down to the exact size.
|
||||
if (!dst->Resize(dst_offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dst.release();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void ElfInterface::InitHeadersWithTemplate() {
|
||||
if (eh_frame_hdr_offset_ != 0) {
|
||||
DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_);
|
||||
eh_frame_.reset(eh_frame_hdr);
|
||||
if (!eh_frame_hdr->EhFrameInit(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_) ||
|
||||
!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
|
||||
eh_frame_.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (eh_frame_.get() == nullptr && eh_frame_offset_ != 0) {
|
||||
// If there is an eh_frame section without an eh_frame_hdr section,
|
||||
// or using the frame hdr object failed to init.
|
||||
eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
|
||||
if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_)) {
|
||||
eh_frame_.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (eh_frame_.get() == nullptr) {
|
||||
eh_frame_hdr_offset_ = 0;
|
||||
eh_frame_hdr_section_bias_ = 0;
|
||||
eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
|
||||
eh_frame_offset_ = 0;
|
||||
eh_frame_section_bias_ = 0;
|
||||
eh_frame_size_ = static_cast<uint64_t>(-1);
|
||||
}
|
||||
|
||||
if (debug_frame_offset_ != 0) {
|
||||
debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
|
||||
if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, debug_frame_section_bias_)) {
|
||||
debug_frame_.reset(nullptr);
|
||||
debug_frame_offset_ = 0;
|
||||
debug_frame_size_ = static_cast<uint64_t>(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename EhdrType, typename PhdrType, typename ShdrType>
|
||||
bool ElfInterface::ReadAllHeaders(int64_t* load_bias) {
|
||||
EhdrType ehdr;
|
||||
if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have enough information that this is an elf file, then allow
|
||||
// malformed program and section headers.
|
||||
ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias);
|
||||
ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename EhdrType, typename PhdrType>
|
||||
int64_t ElfInterface::GetLoadBias(Memory* memory) {
|
||||
EhdrType ehdr;
|
||||
if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t offset = ehdr.e_phoff;
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
|
||||
PhdrType phdr;
|
||||
if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the first executable load when looking for the load bias.
|
||||
if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) {
|
||||
return static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename EhdrType, typename PhdrType>
|
||||
void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias) {
|
||||
uint64_t offset = ehdr.e_phoff;
|
||||
bool first_exec_load_header = true;
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
|
||||
PhdrType phdr;
|
||||
if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phdr.p_type) {
|
||||
case PT_LOAD:
|
||||
{
|
||||
if ((phdr.p_flags & PF_X) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
|
||||
static_cast<size_t>(phdr.p_memsz)};
|
||||
// Only set the load bias from the first executable load header.
|
||||
if (first_exec_load_header) {
|
||||
*load_bias = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
|
||||
}
|
||||
first_exec_load_header = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PT_GNU_EH_FRAME:
|
||||
// This is really the pointer to the .eh_frame_hdr section.
|
||||
eh_frame_hdr_offset_ = phdr.p_offset;
|
||||
eh_frame_hdr_section_bias_ = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
|
||||
eh_frame_hdr_size_ = phdr.p_memsz;
|
||||
break;
|
||||
|
||||
case PT_DYNAMIC:
|
||||
dynamic_offset_ = phdr.p_offset;
|
||||
dynamic_vaddr_start_ = phdr.p_vaddr;
|
||||
if (__builtin_add_overflow(dynamic_vaddr_start_, phdr.p_memsz, &dynamic_vaddr_end_)) {
|
||||
dynamic_offset_ = 0;
|
||||
dynamic_vaddr_start_ = 0;
|
||||
dynamic_vaddr_end_ = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename NhdrType>
|
||||
std::string ElfInterface::ReadBuildID() {
|
||||
// Ensure there is no overflow in any of the calulations below.
|
||||
uint64_t tmp;
|
||||
if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
uint64_t offset = 0;
|
||||
while (offset < gnu_build_id_size_) {
|
||||
if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
|
||||
return "";
|
||||
}
|
||||
NhdrType hdr;
|
||||
if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
|
||||
return "";
|
||||
}
|
||||
offset += sizeof(hdr);
|
||||
|
||||
if (gnu_build_id_size_ - offset < hdr.n_namesz) {
|
||||
return "";
|
||||
}
|
||||
if (hdr.n_namesz > 0) {
|
||||
std::string name(hdr.n_namesz, '\0');
|
||||
if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Trim trailing \0 as GNU is stored as a C string in the ELF file.
|
||||
if (name.back() == '\0')
|
||||
name.resize(name.size() - 1);
|
||||
|
||||
// Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
|
||||
offset += (hdr.n_namesz + 3) & ~3;
|
||||
|
||||
if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
|
||||
if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
|
||||
return "";
|
||||
}
|
||||
std::string build_id(hdr.n_descsz, '\0');
|
||||
if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
|
||||
return build_id;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
// Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
|
||||
offset += (hdr.n_descsz + 3) & ~3;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
template <typename EhdrType, typename ShdrType>
|
||||
void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
|
||||
uint64_t offset = ehdr.e_shoff;
|
||||
uint64_t sec_offset = 0;
|
||||
uint64_t sec_size = 0;
|
||||
|
||||
// Get the location of the section header names.
|
||||
// If something is malformed in the header table data, we aren't going
|
||||
// to terminate, we'll simply ignore this part.
|
||||
ShdrType shdr;
|
||||
if (ehdr.e_shstrndx < ehdr.e_shnum) {
|
||||
uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
|
||||
if (memory_->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
|
||||
sec_offset = shdr.sh_offset;
|
||||
sec_size = shdr.sh_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the first header, it's always going to be NULL.
|
||||
offset += ehdr.e_shentsize;
|
||||
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
|
||||
if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
|
||||
// Need to go get the information about the section that contains
|
||||
// the string terminated names.
|
||||
ShdrType str_shdr;
|
||||
if (shdr.sh_link >= ehdr.e_shnum) {
|
||||
continue;
|
||||
}
|
||||
uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
|
||||
if (!memory_->ReadFully(str_offset, &str_shdr, sizeof(str_shdr))) {
|
||||
continue;
|
||||
}
|
||||
if (str_shdr.sh_type != SHT_STRTAB) {
|
||||
continue;
|
||||
}
|
||||
symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
|
||||
str_shdr.sh_offset, str_shdr.sh_size));
|
||||
} else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
|
||||
// Look for the .debug_frame and .gnu_debugdata.
|
||||
if (shdr.sh_name < sec_size) {
|
||||
std::string name;
|
||||
if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name)) {
|
||||
if (name == ".debug_frame") {
|
||||
debug_frame_offset_ = shdr.sh_offset;
|
||||
debug_frame_size_ = shdr.sh_size;
|
||||
debug_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
|
||||
} else if (name == ".gnu_debugdata") {
|
||||
gnu_debugdata_offset_ = shdr.sh_offset;
|
||||
gnu_debugdata_size_ = shdr.sh_size;
|
||||
} else if (name == ".eh_frame") {
|
||||
eh_frame_offset_ = shdr.sh_offset;
|
||||
eh_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
|
||||
eh_frame_size_ = shdr.sh_size;
|
||||
} else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
|
||||
eh_frame_hdr_offset_ = shdr.sh_offset;
|
||||
eh_frame_hdr_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
|
||||
eh_frame_hdr_size_ = shdr.sh_size;
|
||||
} else if (name == ".data") {
|
||||
data_offset_ = shdr.sh_offset;
|
||||
data_vaddr_start_ = shdr.sh_addr;
|
||||
if (__builtin_add_overflow(data_vaddr_start_, shdr.sh_size, &data_vaddr_end_)) {
|
||||
data_offset_ = 0;
|
||||
data_vaddr_start_ = 0;
|
||||
data_vaddr_end_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (shdr.sh_type == SHT_STRTAB) {
|
||||
// In order to read soname, keep track of address to offset mapping.
|
||||
strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
|
||||
static_cast<uint64_t>(shdr.sh_offset)));
|
||||
} else if (shdr.sh_type == SHT_NOTE) {
|
||||
if (shdr.sh_name < sec_size) {
|
||||
std::string name;
|
||||
if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
|
||||
name == ".note.gnu.build-id") {
|
||||
gnu_build_id_offset_ = shdr.sh_offset;
|
||||
gnu_build_id_size_ = shdr.sh_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DynType>
|
||||
std::string ElfInterface::GetSonameWithTemplate() {
|
||||
if (soname_type_ == SONAME_INVALID) {
|
||||
return "";
|
||||
}
|
||||
if (soname_type_ == SONAME_VALID) {
|
||||
return soname_;
|
||||
}
|
||||
|
||||
soname_type_ = SONAME_INVALID;
|
||||
|
||||
uint64_t soname_offset = 0;
|
||||
uint64_t strtab_addr = 0;
|
||||
uint64_t strtab_size = 0;
|
||||
|
||||
// Find the soname location from the dynamic headers section.
|
||||
DynType dyn;
|
||||
uint64_t offset = dynamic_offset_;
|
||||
uint64_t max_offset = offset + dynamic_vaddr_end_ - dynamic_vaddr_start_;
|
||||
for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
|
||||
if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = offset;
|
||||
return "";
|
||||
}
|
||||
|
||||
if (dyn.d_tag == DT_STRTAB) {
|
||||
strtab_addr = dyn.d_un.d_ptr;
|
||||
} else if (dyn.d_tag == DT_STRSZ) {
|
||||
strtab_size = dyn.d_un.d_val;
|
||||
} else if (dyn.d_tag == DT_SONAME) {
|
||||
soname_offset = dyn.d_un.d_val;
|
||||
} else if (dyn.d_tag == DT_NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to map the strtab address to the real offset.
|
||||
for (const auto& entry : strtabs_) {
|
||||
if (entry.first == strtab_addr) {
|
||||
soname_offset = entry.second + soname_offset;
|
||||
uint64_t soname_max = entry.second + strtab_size;
|
||||
if (soname_offset >= soname_max) {
|
||||
return "";
|
||||
}
|
||||
if (!memory_->ReadString(soname_offset, &soname_, soname_max - soname_offset)) {
|
||||
return "";
|
||||
}
|
||||
soname_type_ = SONAME_VALID;
|
||||
return soname_;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
template <typename SymType>
|
||||
bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
|
||||
uint64_t* func_offset) {
|
||||
if (symbols_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto symbol : symbols_) {
|
||||
if (symbol->GetName<SymType>(addr, memory_, name, func_offset)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename SymType>
|
||||
bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) {
|
||||
if (symbols_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto symbol : symbols_) {
|
||||
if (symbol->GetGlobal<SymType>(memory_, name, memory_address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
|
||||
bool* is_signal_frame) {
|
||||
last_error_.code = ERROR_NONE;
|
||||
last_error_.address = 0;
|
||||
|
||||
// Try the debug_frame first since it contains the most specific unwind
|
||||
// information.
|
||||
DwarfSection* debug_frame = debug_frame_.get();
|
||||
if (debug_frame != nullptr &&
|
||||
debug_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try the eh_frame next.
|
||||
DwarfSection* eh_frame = eh_frame_.get();
|
||||
if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gnu_debugdata_interface_ != nullptr &&
|
||||
gnu_debugdata_interface_->Step(pc, regs, process_memory, finished, is_signal_frame)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the error code based on the first error encountered.
|
||||
DwarfSection* section = nullptr;
|
||||
if (debug_frame_ != nullptr) {
|
||||
section = debug_frame_.get();
|
||||
} else if (eh_frame_ != nullptr) {
|
||||
section = eh_frame_.get();
|
||||
} else if (gnu_debugdata_interface_ != nullptr) {
|
||||
last_error_ = gnu_debugdata_interface_->last_error();
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the DWARF ERROR to an external error.
|
||||
DwarfErrorCode code = section->LastErrorCode();
|
||||
switch (code) {
|
||||
case DWARF_ERROR_NONE:
|
||||
last_error_.code = ERROR_NONE;
|
||||
break;
|
||||
|
||||
case DWARF_ERROR_MEMORY_INVALID:
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = section->LastErrorAddress();
|
||||
break;
|
||||
|
||||
case DWARF_ERROR_ILLEGAL_VALUE:
|
||||
case DWARF_ERROR_ILLEGAL_STATE:
|
||||
case DWARF_ERROR_STACK_INDEX_NOT_VALID:
|
||||
case DWARF_ERROR_TOO_MANY_ITERATIONS:
|
||||
case DWARF_ERROR_CFA_NOT_DEFINED:
|
||||
case DWARF_ERROR_NO_FDES:
|
||||
last_error_.code = ERROR_UNWIND_INFO;
|
||||
break;
|
||||
|
||||
case DWARF_ERROR_NOT_IMPLEMENTED:
|
||||
case DWARF_ERROR_UNSUPPORTED_VERSION:
|
||||
last_error_.code = ERROR_UNSUPPORTED;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is an estimation of the size of the elf file using the location
|
||||
// of the section headers and size. This assumes that the section headers
|
||||
// are at the end of the elf file. If the elf has a load bias, the size
|
||||
// will be too large, but this is acceptable.
|
||||
template <typename EhdrType>
|
||||
void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
|
||||
EhdrType ehdr;
|
||||
if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
|
||||
return;
|
||||
}
|
||||
if (ehdr.e_shnum == 0) {
|
||||
return;
|
||||
}
|
||||
*size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
|
||||
}
|
||||
|
||||
template <typename EhdrType, typename ShdrType>
|
||||
bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
|
||||
EhdrType ehdr;
|
||||
if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t offset = ehdr.e_shoff;
|
||||
uint64_t sec_offset;
|
||||
uint64_t sec_size;
|
||||
ShdrType shdr;
|
||||
if (ehdr.e_shstrndx >= ehdr.e_shnum) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
|
||||
if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
|
||||
return false;
|
||||
}
|
||||
sec_offset = shdr.sh_offset;
|
||||
sec_size = shdr.sh_size;
|
||||
|
||||
// Skip the first header, it's always going to be NULL.
|
||||
offset += ehdr.e_shentsize;
|
||||
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
|
||||
if (!memory->ReadFully(offset, &shdr, sizeof(shdr))) {
|
||||
return false;
|
||||
}
|
||||
std::string name;
|
||||
if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
|
||||
memory->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
|
||||
name == ".note.gnu.build-id") {
|
||||
*build_id_offset = shdr.sh_offset;
|
||||
*build_id_size = shdr.sh_size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename EhdrType, typename ShdrType, typename NhdrType>
|
||||
std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
|
||||
uint64_t note_offset;
|
||||
uint64_t note_size;
|
||||
if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, ¬e_offset, ¬e_size)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Ensure there is no overflow in any of the calculations below.
|
||||
uint64_t tmp;
|
||||
if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
uint64_t offset = 0;
|
||||
while (offset < note_size) {
|
||||
if (note_size - offset < sizeof(NhdrType)) {
|
||||
return "";
|
||||
}
|
||||
NhdrType hdr;
|
||||
if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
|
||||
return "";
|
||||
}
|
||||
offset += sizeof(hdr);
|
||||
|
||||
if (note_size - offset < hdr.n_namesz) {
|
||||
return "";
|
||||
}
|
||||
if (hdr.n_namesz > 0) {
|
||||
std::string name(hdr.n_namesz, '\0');
|
||||
if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Trim trailing \0 as GNU is stored as a C string in the ELF file.
|
||||
if (name.back() == '\0') name.resize(name.size() - 1);
|
||||
|
||||
// Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
|
||||
offset += (hdr.n_namesz + 3) & ~3;
|
||||
|
||||
if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
|
||||
if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
|
||||
return "";
|
||||
}
|
||||
std::string build_id(hdr.n_descsz, '\0');
|
||||
if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
|
||||
return build_id;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
// Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
|
||||
offset += (hdr.n_descsz + 3) & ~3;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Instantiate all of the needed template functions.
|
||||
template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
|
||||
template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
|
||||
|
||||
template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(int64_t*);
|
||||
template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(int64_t*);
|
||||
|
||||
template void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, int64_t*);
|
||||
template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, int64_t*);
|
||||
|
||||
template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
|
||||
template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
|
||||
|
||||
template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
|
||||
template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
|
||||
|
||||
template std::string ElfInterface::GetSonameWithTemplate<Elf32_Dyn>();
|
||||
template std::string ElfInterface::GetSonameWithTemplate<Elf64_Dyn>();
|
||||
|
||||
template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
|
||||
uint64_t*);
|
||||
template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
|
||||
uint64_t*);
|
||||
|
||||
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
|
||||
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(const std::string&, uint64_t*);
|
||||
|
||||
template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
|
||||
template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
|
||||
|
||||
template int64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
|
||||
template int64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
|
||||
|
||||
template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
|
||||
Memory*);
|
||||
template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
|
||||
Memory*);
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/MachineArm.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/RegsArm.h>
|
||||
|
||||
#include "ArmExidx.h"
|
||||
#include "ElfInterfaceArm.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
bool ElfInterfaceArm::Init(int64_t* load_bias) {
|
||||
if (!ElfInterface32::Init(load_bias)) {
|
||||
return false;
|
||||
}
|
||||
load_bias_ = *load_bias;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
|
||||
if (start_offset_ == 0 || total_entries_ == 0) {
|
||||
last_error_.code = ERROR_UNWIND_INFO;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t first = 0;
|
||||
size_t last = total_entries_;
|
||||
while (first < last) {
|
||||
size_t current = (first + last) / 2;
|
||||
uint32_t addr = addrs_[current];
|
||||
if (addr == 0) {
|
||||
if (!GetPrel31Addr(start_offset_ + current * 8, &addr)) {
|
||||
return false;
|
||||
}
|
||||
addrs_[current] = addr;
|
||||
}
|
||||
if (pc == addr) {
|
||||
*entry_offset = start_offset_ + current * 8;
|
||||
return true;
|
||||
}
|
||||
if (pc < addr) {
|
||||
last = current;
|
||||
} else {
|
||||
first = current + 1;
|
||||
}
|
||||
}
|
||||
if (last != 0) {
|
||||
*entry_offset = start_offset_ + (last - 1) * 8;
|
||||
return true;
|
||||
}
|
||||
last_error_.code = ERROR_UNWIND_INFO;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) {
|
||||
uint32_t data;
|
||||
if (!memory_->Read32(offset, &data)) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sign extend the value if necessary.
|
||||
int32_t value = (static_cast<int32_t>(data) << 1) >> 1;
|
||||
*addr = offset + value;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(PT_ARM_EXIDX)
|
||||
#define PT_ARM_EXIDX 0x70000001
|
||||
#endif
|
||||
|
||||
void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) {
|
||||
if (type != PT_ARM_EXIDX) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The offset already takes into account the load bias.
|
||||
start_offset_ = ph_offset;
|
||||
|
||||
// Always use filesz instead of memsz. In most cases they are the same,
|
||||
// but some shared libraries wind up setting one correctly and not the other.
|
||||
total_entries_ = ph_filesz / 8;
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
|
||||
bool* is_signal_frame) {
|
||||
// Dwarf unwind information is precise about whether a pc is covered or not,
|
||||
// but arm unwind information only has ranges of pc. In order to avoid
|
||||
// incorrectly doing a bad unwind using arm unwind information for a
|
||||
// different function, always try and unwind with the dwarf information first.
|
||||
return ElfInterface32::Step(pc, regs, process_memory, finished, is_signal_frame) ||
|
||||
StepExidx(pc, regs, process_memory, finished);
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
// Adjust the load bias to get the real relative pc.
|
||||
if (pc < load_bias_) {
|
||||
last_error_.code = ERROR_UNWIND_INFO;
|
||||
return false;
|
||||
}
|
||||
pc -= load_bias_;
|
||||
|
||||
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
|
||||
uint64_t entry_offset;
|
||||
if (!FindEntry(pc, &entry_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArmExidx arm(regs_arm, memory_, process_memory);
|
||||
arm.set_cfa(regs_arm->sp());
|
||||
bool return_value = false;
|
||||
if (arm.ExtractEntryData(entry_offset) && arm.Eval()) {
|
||||
// If the pc was not set, then use the LR registers for the PC.
|
||||
if (!arm.pc_set()) {
|
||||
(*regs_arm)[ARM_REG_PC] = (*regs_arm)[ARM_REG_LR];
|
||||
}
|
||||
(*regs_arm)[ARM_REG_SP] = arm.cfa();
|
||||
return_value = true;
|
||||
|
||||
// If the pc was set to zero, consider this the final frame.
|
||||
*finished = (regs_arm->pc() == 0) ? true : false;
|
||||
}
|
||||
|
||||
if (arm.status() == ARM_STATUS_NO_UNWIND) {
|
||||
*finished = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!return_value) {
|
||||
switch (arm.status()) {
|
||||
case ARM_STATUS_NONE:
|
||||
case ARM_STATUS_NO_UNWIND:
|
||||
case ARM_STATUS_FINISH:
|
||||
last_error_.code = ERROR_NONE;
|
||||
break;
|
||||
|
||||
case ARM_STATUS_RESERVED:
|
||||
case ARM_STATUS_SPARE:
|
||||
case ARM_STATUS_TRUNCATED:
|
||||
case ARM_STATUS_MALFORMED:
|
||||
case ARM_STATUS_INVALID_ALIGNMENT:
|
||||
case ARM_STATUS_INVALID_PERSONALITY:
|
||||
last_error_.code = ERROR_UNWIND_INFO;
|
||||
break;
|
||||
|
||||
case ARM_STATUS_READ_FAILED:
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = arm.status_address();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return return_value;
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) {
|
||||
// For ARM, thumb function symbols have bit 0 set, but the address passed
|
||||
// in here might not have this bit set and result in a failure to find
|
||||
// the thumb function names. Adjust the address and offset to account
|
||||
// for this possible case.
|
||||
if (ElfInterface32::GetFunctionName(addr | 1, name, offset)) {
|
||||
*offset &= ~1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
|
||||
#define _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class ElfInterfaceArm : public ElfInterface32 {
|
||||
public:
|
||||
ElfInterfaceArm(Memory* memory) : ElfInterface32(memory) {}
|
||||
virtual ~ElfInterfaceArm() = default;
|
||||
|
||||
class iterator : public std::iterator<std::bidirectional_iterator_tag, uint32_t> {
|
||||
public:
|
||||
iterator(ElfInterfaceArm* interface, size_t index) : interface_(interface), index_(index) { }
|
||||
|
||||
iterator& operator++() { index_++; return *this; }
|
||||
iterator& operator++(int increment) { index_ += increment; return *this; }
|
||||
iterator& operator--() { index_--; return *this; }
|
||||
iterator& operator--(int decrement) { index_ -= decrement; return *this; }
|
||||
|
||||
bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
|
||||
bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
|
||||
|
||||
uint32_t operator*() {
|
||||
uint32_t addr = interface_->addrs_[index_];
|
||||
if (addr == 0) {
|
||||
if (!interface_->GetPrel31Addr(interface_->start_offset_ + index_ * 8, &addr)) {
|
||||
return 0;
|
||||
}
|
||||
interface_->addrs_[index_] = addr;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
private:
|
||||
ElfInterfaceArm* interface_ = nullptr;
|
||||
size_t index_ = 0;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
iterator end() { return iterator(this, total_entries_); }
|
||||
|
||||
bool Init(int64_t* section_bias) override;
|
||||
|
||||
bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
|
||||
|
||||
bool FindEntry(uint32_t pc, uint64_t* entry_offset);
|
||||
|
||||
void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
|
||||
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
|
||||
bool* is_signal_frame) override;
|
||||
|
||||
bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
|
||||
|
||||
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
|
||||
|
||||
uint64_t start_offset() { return start_offset_; }
|
||||
|
||||
size_t total_entries() { return total_entries_; }
|
||||
|
||||
void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
|
||||
|
||||
protected:
|
||||
uint64_t start_offset_ = 0;
|
||||
size_t total_entries_ = 0;
|
||||
uint64_t load_bias_ = 0;
|
||||
|
||||
std::unordered_map<size_t, uint32_t> addrs_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Global.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
Global::Global(std::shared_ptr<Memory>& memory) : memory_(memory) {}
|
||||
Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
|
||||
: memory_(memory), search_libs_(search_libs) {}
|
||||
|
||||
void Global::SetArch(ArchEnum arch) {
|
||||
if (arch_ == ARCH_UNKNOWN) {
|
||||
arch_ = arch;
|
||||
ProcessArch();
|
||||
}
|
||||
}
|
||||
|
||||
bool Global::Searchable(const std::string& name) {
|
||||
if (search_libs_.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* base_name = basename(name.c_str());
|
||||
for (const std::string& lib : search_libs_) {
|
||||
if (base_name == lib) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
|
||||
std::string variable(var_str);
|
||||
// When looking for global variables, do not arbitrarily search every
|
||||
// readable map. Instead look for a specific pattern that must exist.
|
||||
// The pattern should be a readable map, followed by a read-write
|
||||
// map with a non-zero offset.
|
||||
// For example:
|
||||
// f0000-f1000 0 r-- /system/lib/libc.so
|
||||
// f1000-f2000 1000 r-x /system/lib/libc.so
|
||||
// f2000-f3000 2000 rw- /system/lib/libc.so
|
||||
// This also works:
|
||||
// f0000-f2000 0 r-- /system/lib/libc.so
|
||||
// f2000-f3000 2000 rw- /system/lib/libc.so
|
||||
// It is also possible to see empty maps after the read-only like so:
|
||||
// f0000-f1000 0 r-- /system/lib/libc.so
|
||||
// f1000-f2000 0 ---
|
||||
// f2000-f3000 1000 r-x /system/lib/libc.so
|
||||
// f3000-f4000 2000 rw- /system/lib/libc.so
|
||||
MapInfo* map_zero = nullptr;
|
||||
for (const auto& info : *maps) {
|
||||
if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
|
||||
map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
|
||||
Elf* elf = map_zero->GetElf(memory_, arch());
|
||||
uint64_t ptr;
|
||||
if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
|
||||
uint64_t offset_end = info->offset + info->end - info->start;
|
||||
if (ptr >= info->offset && ptr < offset_end) {
|
||||
ptr = info->start + ptr - info->offset;
|
||||
if (ReadVariableData(ptr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (info->offset == 0 && !info->name.empty()) {
|
||||
map_zero = info.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
#include "MemoryRange.h"
|
||||
|
||||
// This implements the JIT Compilation Interface.
|
||||
// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
struct JITCodeEntry32Pack {
|
||||
uint32_t next;
|
||||
uint32_t prev;
|
||||
uint32_t symfile_addr;
|
||||
uint64_t symfile_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct JITCodeEntry32Pad {
|
||||
uint32_t next;
|
||||
uint32_t prev;
|
||||
uint32_t symfile_addr;
|
||||
uint32_t pad;
|
||||
uint64_t symfile_size;
|
||||
};
|
||||
|
||||
struct JITCodeEntry64 {
|
||||
uint64_t next;
|
||||
uint64_t prev;
|
||||
uint64_t symfile_addr;
|
||||
uint64_t symfile_size;
|
||||
};
|
||||
|
||||
struct JITDescriptorHeader {
|
||||
uint32_t version;
|
||||
uint32_t action_flag;
|
||||
};
|
||||
|
||||
struct JITDescriptor32 {
|
||||
JITDescriptorHeader header;
|
||||
uint32_t relevant_entry;
|
||||
uint32_t first_entry;
|
||||
};
|
||||
|
||||
struct JITDescriptor64 {
|
||||
JITDescriptorHeader header;
|
||||
uint64_t relevant_entry;
|
||||
uint64_t first_entry;
|
||||
};
|
||||
|
||||
JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {}
|
||||
|
||||
JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
|
||||
: Global(memory, search_libs) {}
|
||||
|
||||
JitDebug::~JitDebug() {
|
||||
for (auto* elf : elf_list_) {
|
||||
delete elf;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
|
||||
JITDescriptor32 desc;
|
||||
if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (desc.header.version != 1 || desc.first_entry == 0) {
|
||||
// Either unknown version, or no jit entries.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return desc.first_entry;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
|
||||
JITDescriptor64 desc;
|
||||
if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (desc.header.version != 1 || desc.first_entry == 0) {
|
||||
// Either unknown version, or no jit entries.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return desc.first_entry;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
|
||||
JITCodeEntry32Pack code;
|
||||
if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = code.symfile_addr;
|
||||
*size = code.symfile_size;
|
||||
return code.next;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
|
||||
JITCodeEntry32Pad code;
|
||||
if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = code.symfile_addr;
|
||||
*size = code.symfile_size;
|
||||
return code.next;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
|
||||
JITCodeEntry64 code;
|
||||
if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = code.symfile_addr;
|
||||
*size = code.symfile_size;
|
||||
return code.next;
|
||||
}
|
||||
|
||||
void JitDebug::ProcessArch() {
|
||||
switch (arch()) {
|
||||
case ARCH_X86:
|
||||
read_descriptor_func_ = &JitDebug::ReadDescriptor32;
|
||||
read_entry_func_ = &JitDebug::ReadEntry32Pack;
|
||||
break;
|
||||
|
||||
case ARCH_ARM:
|
||||
case ARCH_MIPS:
|
||||
read_descriptor_func_ = &JitDebug::ReadDescriptor32;
|
||||
read_entry_func_ = &JitDebug::ReadEntry32Pad;
|
||||
break;
|
||||
|
||||
case ARCH_ARM64:
|
||||
case ARCH_X86_64:
|
||||
case ARCH_MIPS64:
|
||||
read_descriptor_func_ = &JitDebug::ReadDescriptor64;
|
||||
read_entry_func_ = &JitDebug::ReadEntry64;
|
||||
break;
|
||||
case ARCH_UNKNOWN:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool JitDebug::ReadVariableData(uint64_t ptr) {
|
||||
entry_addr_ = (this->*read_descriptor_func_)(ptr);
|
||||
return entry_addr_ != 0;
|
||||
}
|
||||
|
||||
void JitDebug::Init(Maps* maps) {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
// Regardless of what happens below, consider the init finished.
|
||||
initialized_ = true;
|
||||
|
||||
FindAndReadVariable(maps, "__jit_debug_descriptor");
|
||||
}
|
||||
|
||||
Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
|
||||
// Use a single lock, this object should be used so infrequently that
|
||||
// a fine grain lock is unnecessary.
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
if (!initialized_) {
|
||||
Init(maps);
|
||||
}
|
||||
|
||||
// Search the existing elf object first.
|
||||
for (Elf* elf : elf_list_) {
|
||||
if (elf->IsValidPc(pc)) {
|
||||
return elf;
|
||||
}
|
||||
}
|
||||
|
||||
while (entry_addr_ != 0) {
|
||||
uint64_t start;
|
||||
uint64_t size;
|
||||
entry_addr_ = (this->*read_entry_func_)(&start, &size);
|
||||
|
||||
Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
|
||||
elf->Init();
|
||||
if (!elf->valid()) {
|
||||
// The data is not formatted in a way we understand, do not attempt
|
||||
// to process any other entries.
|
||||
entry_addr_ = 0;
|
||||
delete elf;
|
||||
return nullptr;
|
||||
}
|
||||
elf_list_.push_back(elf);
|
||||
|
||||
if (elf->IsValidPc(pc)) {
|
||||
return elf;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/LocalUnwinder.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/RegsGetLocal.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
bool LocalUnwinder::Init() {
|
||||
pthread_rwlock_init(&maps_rwlock_, nullptr);
|
||||
|
||||
// Create the maps.
|
||||
maps_.reset(new unwindstack::LocalUpdatableMaps());
|
||||
if (!maps_->Parse()) {
|
||||
maps_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
|
||||
for (const std::string& skip_library : skip_libraries_) {
|
||||
if (skip_library == map_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
|
||||
pthread_rwlock_rdlock(&maps_rwlock_);
|
||||
MapInfo* map_info = maps_->Find(pc);
|
||||
pthread_rwlock_unlock(&maps_rwlock_);
|
||||
|
||||
if (map_info == nullptr) {
|
||||
pthread_rwlock_wrlock(&maps_rwlock_);
|
||||
// This is guaranteed not to invalidate any previous MapInfo objects so
|
||||
// we don't need to worry about any MapInfo* values already in use.
|
||||
if (maps_->Reparse()) {
|
||||
map_info = maps_->Find(pc);
|
||||
}
|
||||
pthread_rwlock_unlock(&maps_rwlock_);
|
||||
}
|
||||
|
||||
return map_info;
|
||||
}
|
||||
|
||||
bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
|
||||
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
|
||||
unwindstack::RegsGetLocal(regs.get());
|
||||
ArchEnum arch = regs->Arch();
|
||||
|
||||
size_t num_frames = 0;
|
||||
bool adjust_pc = false;
|
||||
while (true) {
|
||||
uint64_t cur_pc = regs->pc();
|
||||
uint64_t cur_sp = regs->sp();
|
||||
|
||||
MapInfo* map_info = GetMapInfo(cur_pc);
|
||||
if (map_info == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
Elf* elf = map_info->GetElf(process_memory_, arch);
|
||||
uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
|
||||
uint64_t step_pc = rel_pc;
|
||||
uint64_t pc_adjustment;
|
||||
if (adjust_pc) {
|
||||
pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
|
||||
} else {
|
||||
pc_adjustment = 0;
|
||||
}
|
||||
step_pc -= pc_adjustment;
|
||||
|
||||
bool finished = false;
|
||||
bool is_signal_frame = false;
|
||||
if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
|
||||
step_pc = rel_pc;
|
||||
} else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished,
|
||||
&is_signal_frame)) {
|
||||
finished = true;
|
||||
}
|
||||
|
||||
// Skip any locations that are within this library.
|
||||
if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
|
||||
// Add frame information.
|
||||
std::string func_name;
|
||||
uint64_t func_offset;
|
||||
if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
|
||||
frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
|
||||
func_name, func_offset);
|
||||
} else {
|
||||
frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
|
||||
}
|
||||
num_frames++;
|
||||
}
|
||||
|
||||
if (finished || frame_info->size() == max_frames ||
|
||||
(cur_pc == regs->pc() && cur_sp == regs->sp())) {
|
||||
break;
|
||||
}
|
||||
adjust_pc = true;
|
||||
}
|
||||
return num_frames != 0;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#define LOG_TAG "unwind"
|
||||
#include <log/log.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/Log.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
static bool g_print_to_stdout = false;
|
||||
|
||||
void log_to_stdout(bool enable) {
|
||||
g_print_to_stdout = enable;
|
||||
}
|
||||
|
||||
// Send the data to the log.
|
||||
void log(uint8_t indent, const char* format, ...) {
|
||||
std::string real_format;
|
||||
if (indent > 0) {
|
||||
real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
|
||||
} else {
|
||||
real_format = format;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (g_print_to_stdout) {
|
||||
real_format += '\n';
|
||||
vprintf(real_format.c_str(), args);
|
||||
} else {
|
||||
LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, real_format.c_str(), args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,352 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
#include "MemoryFileAtOffset.h"
|
||||
#include "MemoryRange.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
|
||||
// One last attempt, see if the previous map is read-only with the
|
||||
// same name and stretches across this map.
|
||||
if (prev_real_map == nullptr || prev_real_map->flags != PROT_READ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t map_size = end - prev_real_map->end;
|
||||
if (!memory->Init(name, prev_real_map->offset, map_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t max_size;
|
||||
if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!memory->Init(name, prev_real_map->offset, max_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
elf_offset = offset - prev_real_map->offset;
|
||||
elf_start_offset = prev_real_map->offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
Memory* MapInfo::GetFileMemory() {
|
||||
std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
|
||||
if (offset == 0) {
|
||||
if (memory->Init(name, 0)) {
|
||||
return memory.release();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// These are the possibilities when the offset is non-zero.
|
||||
// - There is an elf file embedded in a file, and the offset is the
|
||||
// the start of the elf in the file.
|
||||
// - There is an elf file embedded in a file, and the offset is the
|
||||
// the start of the executable part of the file. The actual start
|
||||
// of the elf is in the read-only segment preceeding this map.
|
||||
// - The whole file is an elf file, and the offset needs to be saved.
|
||||
//
|
||||
// Map in just the part of the file for the map. If this is not
|
||||
// a valid elf, then reinit as if the whole file is an elf file.
|
||||
// If the offset is a valid elf, then determine the size of the map
|
||||
// and reinit to that size. This is needed because the dynamic linker
|
||||
// only maps in a portion of the original elf, and never the symbol
|
||||
// file data.
|
||||
uint64_t map_size = end - start;
|
||||
if (!memory->Init(name, offset, map_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the start of this map is an embedded elf.
|
||||
uint64_t max_size = 0;
|
||||
if (Elf::GetInfo(memory.get(), &max_size)) {
|
||||
elf_start_offset = offset;
|
||||
if (max_size > map_size) {
|
||||
if (memory->Init(name, offset, max_size)) {
|
||||
return memory.release();
|
||||
}
|
||||
// Try to reinit using the default map_size.
|
||||
if (memory->Init(name, offset, map_size)) {
|
||||
return memory.release();
|
||||
}
|
||||
elf_start_offset = 0;
|
||||
return nullptr;
|
||||
}
|
||||
return memory.release();
|
||||
}
|
||||
|
||||
// No elf at offset, try to init as if the whole file is an elf.
|
||||
if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
|
||||
elf_offset = offset;
|
||||
// Need to check how to set the elf start offset. If this map is not
|
||||
// the r-x map of a r-- map, then use the real offset value. Otherwise,
|
||||
// use 0.
|
||||
if (prev_real_map == nullptr || prev_real_map->offset != 0 ||
|
||||
prev_real_map->flags != PROT_READ || prev_real_map->name != name) {
|
||||
elf_start_offset = offset;
|
||||
}
|
||||
return memory.release();
|
||||
}
|
||||
|
||||
// See if the map previous to this one contains a read-only map
|
||||
// that represents the real start of the elf data.
|
||||
if (InitFileMemoryFromPreviousReadOnlyMap(memory.get())) {
|
||||
return memory.release();
|
||||
}
|
||||
|
||||
// Failed to find elf at start of file or at read-only map, return
|
||||
// file object from the current map.
|
||||
if (memory->Init(name, offset, map_size)) {
|
||||
return memory.release();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
|
||||
if (end <= start) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
elf_offset = 0;
|
||||
|
||||
// Fail on device maps.
|
||||
if (flags & MAPS_FLAGS_DEVICE_MAP) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// First try and use the file associated with the info.
|
||||
if (!name.empty()) {
|
||||
Memory* memory = GetFileMemory();
|
||||
if (memory != nullptr) {
|
||||
return memory;
|
||||
}
|
||||
}
|
||||
|
||||
if (process_memory == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Need to verify that this elf is valid. It's possible that
|
||||
// only part of the elf file to be mapped into memory is in the executable
|
||||
// map. In this case, there will be another read-only map that includes the
|
||||
// first part of the elf file. This is done if the linker rosegment
|
||||
// option is used.
|
||||
std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
|
||||
if (Elf::IsValidElf(memory.get())) {
|
||||
memory_backed_elf = true;
|
||||
return memory.release();
|
||||
}
|
||||
|
||||
// Find the read-only map by looking at the previous map. The linker
|
||||
// doesn't guarantee that this invariant will always be true. However,
|
||||
// if that changes, there is likely something else that will change and
|
||||
// break something.
|
||||
if (offset == 0 || name.empty() || prev_real_map == nullptr || prev_real_map->name != name ||
|
||||
prev_real_map->offset >= offset) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure that relative pc values are corrected properly.
|
||||
elf_offset = offset - prev_real_map->offset;
|
||||
// Use this as the elf start offset, otherwise, you always get offsets into
|
||||
// the r-x section, which is not quite the right information.
|
||||
elf_start_offset = prev_real_map->offset;
|
||||
|
||||
MemoryRanges* ranges = new MemoryRanges;
|
||||
ranges->Insert(new MemoryRange(process_memory, prev_real_map->start,
|
||||
prev_real_map->end - prev_real_map->start, 0));
|
||||
ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
|
||||
|
||||
memory_backed_elf = true;
|
||||
return ranges;
|
||||
}
|
||||
|
||||
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
|
||||
{
|
||||
// Make sure no other thread is trying to add the elf to this map.
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
|
||||
if (elf.get() != nullptr) {
|
||||
return elf.get();
|
||||
}
|
||||
|
||||
bool locked = false;
|
||||
if (Elf::CachingEnabled() && !name.empty()) {
|
||||
Elf::CacheLock();
|
||||
locked = true;
|
||||
if (Elf::CacheGet(this)) {
|
||||
Elf::CacheUnlock();
|
||||
return elf.get();
|
||||
}
|
||||
}
|
||||
|
||||
Memory* memory = CreateMemory(process_memory);
|
||||
if (locked) {
|
||||
if (Elf::CacheAfterCreateMemory(this)) {
|
||||
delete memory;
|
||||
Elf::CacheUnlock();
|
||||
return elf.get();
|
||||
}
|
||||
}
|
||||
elf.reset(new Elf(memory));
|
||||
// If the init fails, keep the elf around as an invalid object so we
|
||||
// don't try to reinit the object.
|
||||
elf->Init();
|
||||
if (elf->valid() && expected_arch != elf->arch()) {
|
||||
// Make the elf invalid, mismatch between arch and expected arch.
|
||||
elf->Invalidate();
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
Elf::CacheAdd(this);
|
||||
Elf::CacheUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
if (!elf->valid()) {
|
||||
elf_start_offset = offset;
|
||||
} else if (prev_real_map != nullptr && elf_start_offset != offset &&
|
||||
prev_real_map->offset == elf_start_offset && prev_real_map->name == name) {
|
||||
// If there is a read-only map then a read-execute map that represents the
|
||||
// same elf object, make sure the previous map is using the same elf
|
||||
// object if it hasn't already been set.
|
||||
std::lock_guard<std::mutex> guard(prev_real_map->mutex_);
|
||||
if (prev_real_map->elf.get() == nullptr) {
|
||||
prev_real_map->elf = elf;
|
||||
prev_real_map->memory_backed_elf = memory_backed_elf;
|
||||
}
|
||||
}
|
||||
return elf.get();
|
||||
}
|
||||
|
||||
bool MapInfo::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
|
||||
{
|
||||
// Make sure no other thread is trying to update this elf object.
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
if (elf == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// No longer need the lock, once the elf object is created, it is not deleted
|
||||
// until this object is deleted.
|
||||
return elf->GetFunctionName(addr, name, func_offset);
|
||||
}
|
||||
|
||||
uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
|
||||
int64_t cur_load_bias = load_bias.load();
|
||||
if (cur_load_bias != INT64_MAX) {
|
||||
return cur_load_bias;
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure no other thread is trying to add the elf to this map.
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
if (elf != nullptr) {
|
||||
if (elf->valid()) {
|
||||
cur_load_bias = elf->GetLoadBias();
|
||||
load_bias = cur_load_bias;
|
||||
return cur_load_bias;
|
||||
} else {
|
||||
load_bias = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call lightweight static function that will only read enough of the
|
||||
// elf data to get the load bias.
|
||||
std::unique_ptr<Memory> memory(CreateMemory(process_memory));
|
||||
cur_load_bias = Elf::GetLoadBias(memory.get());
|
||||
load_bias = cur_load_bias;
|
||||
return cur_load_bias;
|
||||
}
|
||||
|
||||
MapInfo::~MapInfo() {
|
||||
uintptr_t id = build_id.load();
|
||||
if (id != 0) {
|
||||
delete reinterpret_cast<std::string*>(id);
|
||||
}
|
||||
}
|
||||
|
||||
std::string MapInfo::GetBuildID() {
|
||||
uintptr_t id = build_id.load();
|
||||
if (id != 0) {
|
||||
return *reinterpret_cast<std::string*>(id);
|
||||
}
|
||||
|
||||
// No need to lock, at worst if multiple threads do this at the same
|
||||
// time it should be detected and only one thread should win and
|
||||
// save the data.
|
||||
std::unique_ptr<std::string> cur_build_id(new std::string);
|
||||
|
||||
// Now need to see if the elf object exists.
|
||||
// Make sure no other thread is trying to add the elf to this map.
|
||||
mutex_.lock();
|
||||
Elf* elf_obj = elf.get();
|
||||
mutex_.unlock();
|
||||
if (elf_obj != nullptr) {
|
||||
*cur_build_id = elf_obj->GetBuildID();
|
||||
} else {
|
||||
// This will only work if we can get the file associated with this memory.
|
||||
// If this is only available in memory, then the section name information
|
||||
// is not present and we will not be able to find the build id info.
|
||||
std::unique_ptr<Memory> memory(GetFileMemory());
|
||||
if (memory != nullptr) {
|
||||
*cur_build_id = Elf::GetBuildID(memory.get());
|
||||
}
|
||||
}
|
||||
|
||||
id = reinterpret_cast<uintptr_t>(cur_build_id.get());
|
||||
uintptr_t expected_id = 0;
|
||||
if (build_id.compare_exchange_weak(expected_id, id)) {
|
||||
// Value saved, so make sure the memory is not freed.
|
||||
cur_build_id.release();
|
||||
}
|
||||
return *reinterpret_cast<std::string*>(id);
|
||||
}
|
||||
|
||||
std::string MapInfo::GetPrintableBuildID() {
|
||||
std::string raw_build_id = GetBuildID();
|
||||
if (raw_build_id.empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string printable_build_id;
|
||||
for (const char& c : raw_build_id) {
|
||||
// Use %hhx to avoid sign extension on abis that have signed chars.
|
||||
printable_build_id += android::base::StringPrintf("%02hhx", c);
|
||||
}
|
||||
return printable_build_id;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <procinfo/process_map.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
MapInfo* Maps::Find(uint64_t pc) {
|
||||
if (maps_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t first = 0;
|
||||
size_t last = maps_.size();
|
||||
while (first < last) {
|
||||
size_t index = (first + last) / 2;
|
||||
const auto& cur = maps_[index];
|
||||
if (pc >= cur->start && pc < cur->end) {
|
||||
return cur.get();
|
||||
} else if (pc < cur->start) {
|
||||
last = index;
|
||||
} else {
|
||||
first = index + 1;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Maps::Parse() {
|
||||
MapInfo* prev_map = nullptr;
|
||||
MapInfo* prev_real_map = nullptr;
|
||||
return android::procinfo::ReadMapFile(
|
||||
GetMapsFile(),
|
||||
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
|
||||
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
|
||||
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
|
||||
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
|
||||
}
|
||||
maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
|
||||
prev_map = maps_.back().get();
|
||||
if (!prev_map->IsBlank()) {
|
||||
prev_real_map = prev_map;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
|
||||
const std::string& name, uint64_t load_bias) {
|
||||
MapInfo* prev_map = maps_.empty() ? nullptr : maps_.back().get();
|
||||
MapInfo* prev_real_map = prev_map;
|
||||
while (prev_real_map != nullptr && prev_real_map->IsBlank()) {
|
||||
prev_real_map = prev_real_map->prev_map;
|
||||
}
|
||||
|
||||
auto map_info =
|
||||
std::make_unique<MapInfo>(prev_map, prev_real_map, start, end, offset, flags, name);
|
||||
map_info->load_bias = load_bias;
|
||||
maps_.emplace_back(std::move(map_info));
|
||||
}
|
||||
|
||||
void Maps::Sort() {
|
||||
std::sort(maps_.begin(), maps_.end(),
|
||||
[](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
|
||||
return a->start < b->start; });
|
||||
|
||||
// Set the prev_map values on the info objects.
|
||||
MapInfo* prev_map = nullptr;
|
||||
MapInfo* prev_real_map = nullptr;
|
||||
for (const auto& map_info : maps_) {
|
||||
map_info->prev_map = prev_map;
|
||||
map_info->prev_real_map = prev_real_map;
|
||||
prev_map = map_info.get();
|
||||
if (!prev_map->IsBlank()) {
|
||||
prev_real_map = prev_map;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BufferMaps::Parse() {
|
||||
std::string content(buffer_);
|
||||
MapInfo* prev_map = nullptr;
|
||||
MapInfo* prev_real_map = nullptr;
|
||||
return android::procinfo::ReadMapFileContent(
|
||||
&content[0],
|
||||
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
|
||||
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
|
||||
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
|
||||
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
|
||||
}
|
||||
maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
|
||||
prev_map = maps_.back().get();
|
||||
if (!prev_map->IsBlank()) {
|
||||
prev_real_map = prev_map;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const std::string RemoteMaps::GetMapsFile() const {
|
||||
return "/proc/" + std::to_string(pid_) + "/maps";
|
||||
}
|
||||
|
||||
const std::string LocalUpdatableMaps::GetMapsFile() const {
|
||||
return "/proc/self/maps";
|
||||
}
|
||||
|
||||
bool LocalUpdatableMaps::Reparse() {
|
||||
// New maps will be added at the end without deleting the old ones.
|
||||
size_t last_map_idx = maps_.size();
|
||||
if (!Parse()) {
|
||||
maps_.resize(last_map_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t total_entries = maps_.size();
|
||||
size_t search_map_idx = 0;
|
||||
for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
|
||||
auto& new_map_info = maps_[new_map_idx];
|
||||
uint64_t start = new_map_info->start;
|
||||
uint64_t end = new_map_info->end;
|
||||
uint64_t flags = new_map_info->flags;
|
||||
std::string* name = &new_map_info->name;
|
||||
for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
|
||||
auto& info = maps_[old_map_idx];
|
||||
if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
|
||||
// No need to check
|
||||
search_map_idx = old_map_idx + 1;
|
||||
if (new_map_idx + 1 < maps_.size()) {
|
||||
maps_[new_map_idx + 1]->prev_map = info.get();
|
||||
maps_[new_map_idx + 1]->prev_real_map =
|
||||
info->IsBlank() ? info->prev_real_map : info.get();
|
||||
}
|
||||
maps_[new_map_idx] = nullptr;
|
||||
total_entries--;
|
||||
break;
|
||||
} else if (info->start > start) {
|
||||
// Stop, there isn't going to be a match.
|
||||
search_map_idx = old_map_idx;
|
||||
break;
|
||||
}
|
||||
|
||||
// Never delete these maps, they may be in use. The assumption is
|
||||
// that there will only every be a handful of these so waiting
|
||||
// to destroy them is not too expensive.
|
||||
saved_maps_.emplace_back(std::move(info));
|
||||
search_map_idx = old_map_idx + 1;
|
||||
maps_[old_map_idx] = nullptr;
|
||||
total_entries--;
|
||||
}
|
||||
if (search_map_idx >= last_map_idx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now move out any of the maps that never were found.
|
||||
for (size_t i = search_map_idx; i < last_map_idx; i++) {
|
||||
saved_maps_.emplace_back(std::move(maps_[i]));
|
||||
maps_[i] = nullptr;
|
||||
total_entries--;
|
||||
}
|
||||
|
||||
// Sort all of the values such that the nullptrs wind up at the end, then
|
||||
// resize them away.
|
||||
std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
|
||||
if (a == nullptr) {
|
||||
return false;
|
||||
} else if (b == nullptr) {
|
||||
return true;
|
||||
}
|
||||
return a->start < b->start;
|
||||
});
|
||||
maps_.resize(total_entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,498 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "Check.h"
|
||||
#include "MemoryBuffer.h"
|
||||
#include "MemoryCache.h"
|
||||
#include "MemoryFileAtOffset.h"
|
||||
#include "MemoryLocal.h"
|
||||
#include "MemoryOffline.h"
|
||||
#include "MemoryOfflineBuffer.h"
|
||||
#include "MemoryRange.h"
|
||||
#include "MemoryRemote.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
|
||||
|
||||
// Split up the remote read across page boundaries.
|
||||
// From the manpage:
|
||||
// A partial read/write may result if one of the remote_iov elements points to an invalid
|
||||
// memory region in the remote process.
|
||||
//
|
||||
// Partial transfers apply at the granularity of iovec elements. These system calls won't
|
||||
// perform a partial transfer that splits a single iovec element.
|
||||
constexpr size_t kMaxIovecs = 64;
|
||||
struct iovec src_iovs[kMaxIovecs];
|
||||
|
||||
uint64_t cur = remote_src;
|
||||
size_t total_read = 0;
|
||||
while (len > 0) {
|
||||
struct iovec dst_iov = {
|
||||
.iov_base = &reinterpret_cast<uint8_t*>(dst)[total_read], .iov_len = len,
|
||||
};
|
||||
|
||||
size_t iovecs_used = 0;
|
||||
while (len > 0) {
|
||||
if (iovecs_used == kMaxIovecs) {
|
||||
break;
|
||||
}
|
||||
|
||||
// struct iovec uses void* for iov_base.
|
||||
if (cur >= UINTPTR_MAX) {
|
||||
errno = EFAULT;
|
||||
return total_read;
|
||||
}
|
||||
|
||||
src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
|
||||
|
||||
uintptr_t misalignment = cur & (getpagesize() - 1);
|
||||
size_t iov_len = getpagesize() - misalignment;
|
||||
iov_len = std::min(iov_len, len);
|
||||
|
||||
len -= iov_len;
|
||||
if (__builtin_add_overflow(cur, iov_len, &cur)) {
|
||||
errno = EFAULT;
|
||||
return total_read;
|
||||
}
|
||||
|
||||
src_iovs[iovecs_used].iov_len = iov_len;
|
||||
++iovecs_used;
|
||||
}
|
||||
|
||||
ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
|
||||
if (rc == -1) {
|
||||
return total_read;
|
||||
}
|
||||
total_read += rc;
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
|
||||
// ptrace() returns -1 and sets errno when the operation fails.
|
||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||
errno = 0;
|
||||
*value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
|
||||
if (*value == -1 && errno) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t PtraceRead(pid_t pid, uint64_t addr, void* dst, size_t bytes) {
|
||||
// Make sure that there is no overflow.
|
||||
uint64_t max_size;
|
||||
if (__builtin_add_overflow(addr, bytes, &max_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bytes_read = 0;
|
||||
long data;
|
||||
size_t align_bytes = addr & (sizeof(long) - 1);
|
||||
if (align_bytes != 0) {
|
||||
if (!PtraceReadLong(pid, addr & ~(sizeof(long) - 1), &data)) {
|
||||
return 0;
|
||||
}
|
||||
size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
|
||||
memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
|
||||
addr += copy_bytes;
|
||||
dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
|
||||
bytes -= copy_bytes;
|
||||
bytes_read += copy_bytes;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bytes / sizeof(long); i++) {
|
||||
if (!PtraceReadLong(pid, addr, &data)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(dst, &data, sizeof(long));
|
||||
dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
|
||||
addr += sizeof(long);
|
||||
bytes_read += sizeof(long);
|
||||
}
|
||||
|
||||
size_t left_over = bytes & (sizeof(long) - 1);
|
||||
if (left_over) {
|
||||
if (!PtraceReadLong(pid, addr, &data)) {
|
||||
return bytes_read;
|
||||
}
|
||||
memcpy(dst, &data, left_over);
|
||||
bytes_read += left_over;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
bool Memory::ReadFully(uint64_t addr, void* dst, size_t size) {
|
||||
size_t rc = Read(addr, dst, size);
|
||||
return rc == size;
|
||||
}
|
||||
|
||||
bool Memory::ReadString(uint64_t addr, std::string* dst, size_t max_read) {
|
||||
char buffer[256]; // Large enough for 99% of symbol names.
|
||||
size_t size = 0; // Number of bytes which were read into the buffer.
|
||||
for (size_t offset = 0; offset < max_read; offset += size) {
|
||||
// Look for null-terminator first, so we can allocate string of exact size.
|
||||
// If we know the end of valid memory range, do the reads in larger blocks.
|
||||
size_t read = std::min(sizeof(buffer), max_read - offset);
|
||||
size = Read(addr + offset, buffer, read);
|
||||
if (size == 0) {
|
||||
return false; // We have not found end of string yet and we can not read more data.
|
||||
}
|
||||
size_t length = strnlen(buffer, size); // Index of the null-terminator.
|
||||
if (length < size) {
|
||||
// We found the null-terminator. Allocate the string and set its content.
|
||||
if (offset == 0) {
|
||||
// We did just single read, so the buffer already contains the whole string.
|
||||
dst->assign(buffer, length);
|
||||
return true;
|
||||
} else {
|
||||
// The buffer contains only the last block. Read the whole string again.
|
||||
dst->assign(offset + length, '\0');
|
||||
return ReadFully(addr, dst->data(), dst->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Memory> Memory::CreateFileMemory(const std::string& path, uint64_t offset) {
|
||||
auto memory = std::make_unique<MemoryFileAtOffset>();
|
||||
|
||||
if (memory->Init(path, offset)) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
|
||||
if (pid == getpid()) {
|
||||
return std::shared_ptr<Memory>(new MemoryLocal());
|
||||
}
|
||||
return std::shared_ptr<Memory>(new MemoryRemote(pid));
|
||||
}
|
||||
|
||||
std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
|
||||
if (pid == getpid()) {
|
||||
return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal()));
|
||||
}
|
||||
return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
|
||||
}
|
||||
|
||||
std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_t start,
|
||||
uint64_t end) {
|
||||
return std::shared_ptr<Memory>(new MemoryOfflineBuffer(data, start, end));
|
||||
}
|
||||
|
||||
size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (addr >= size_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bytes_left = size_ - static_cast<size_t>(addr);
|
||||
const unsigned char* actual_base = static_cast<const unsigned char*>(raw_) + addr;
|
||||
size_t actual_len = std::min(bytes_left, size);
|
||||
|
||||
memcpy(dst, actual_base, actual_len);
|
||||
return actual_len;
|
||||
}
|
||||
|
||||
uint8_t* MemoryBuffer::GetPtr(size_t offset) {
|
||||
if (offset < size_) {
|
||||
return &raw_[offset];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemoryFileAtOffset::~MemoryFileAtOffset() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void MemoryFileAtOffset::Clear() {
|
||||
if (data_) {
|
||||
munmap(&data_[-offset_], size_ + offset_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t size) {
|
||||
// Clear out any previous data if it exists.
|
||||
Clear();
|
||||
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) == -1) {
|
||||
return false;
|
||||
}
|
||||
if (offset >= static_cast<uint64_t>(buf.st_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset_ = offset & (getpagesize() - 1);
|
||||
uint64_t aligned_offset = offset & ~(getpagesize() - 1);
|
||||
if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
|
||||
offset > static_cast<uint64_t>(buf.st_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_ = buf.st_size - aligned_offset;
|
||||
uint64_t max_size;
|
||||
if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
|
||||
// Truncate the mapped size.
|
||||
size_ = max_size;
|
||||
}
|
||||
void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
|
||||
if (map == MAP_FAILED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data_ = &reinterpret_cast<uint8_t*>(map)[offset_];
|
||||
size_ -= offset_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (addr >= size_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bytes_left = size_ - static_cast<size_t>(addr);
|
||||
const unsigned char* actual_base = static_cast<const unsigned char*>(data_) + addr;
|
||||
size_t actual_len = std::min(bytes_left, size);
|
||||
|
||||
memcpy(dst, actual_base, actual_len);
|
||||
return actual_len;
|
||||
}
|
||||
|
||||
size_t MemoryRemote::Read(uint64_t addr, void* dst, size_t size) {
|
||||
#if !defined(__LP64__)
|
||||
// Cannot read an address greater than 32 bits in a 32 bit context.
|
||||
if (addr > UINT32_MAX) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t (*read_func)(pid_t, uint64_t, void*, size_t) =
|
||||
reinterpret_cast<size_t (*)(pid_t, uint64_t, void*, size_t)>(read_redirect_func_.load());
|
||||
if (read_func != nullptr) {
|
||||
return read_func(pid_, addr, dst, size);
|
||||
} else {
|
||||
// Prefer process_vm_read, try it first. If it doesn't work, use the
|
||||
// ptrace function. If at least one of them returns at least some data,
|
||||
// set that as the permanent function to use.
|
||||
// This assumes that if process_vm_read works once, it will continue
|
||||
// to work.
|
||||
size_t bytes = ProcessVmRead(pid_, addr, dst, size);
|
||||
if (bytes > 0) {
|
||||
read_redirect_func_ = reinterpret_cast<uintptr_t>(ProcessVmRead);
|
||||
return bytes;
|
||||
}
|
||||
bytes = PtraceRead(pid_, addr, dst, size);
|
||||
if (bytes > 0) {
|
||||
read_redirect_func_ = reinterpret_cast<uintptr_t>(PtraceRead);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
size_t MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
|
||||
return ProcessVmRead(getpid(), addr, dst, size);
|
||||
}
|
||||
|
||||
#if !defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
long MemoryRemote::ReadTag(uint64_t) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
long MemoryLocal::ReadTag(uint64_t) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
|
||||
uint64_t offset)
|
||||
: memory_(memory), begin_(begin), length_(length), offset_(offset) {}
|
||||
|
||||
size_t MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (addr < offset_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t read_offset = addr - offset_;
|
||||
if (read_offset >= length_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t read_length = std::min(static_cast<uint64_t>(size), length_ - read_offset);
|
||||
uint64_t read_addr;
|
||||
if (__builtin_add_overflow(read_offset, begin_, &read_addr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return memory_->Read(read_addr, dst, read_length);
|
||||
}
|
||||
|
||||
void MemoryRanges::Insert(MemoryRange* memory) {
|
||||
maps_.emplace(memory->offset() + memory->length(), memory);
|
||||
}
|
||||
|
||||
size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
|
||||
auto entry = maps_.upper_bound(addr);
|
||||
if (entry != maps_.end()) {
|
||||
return entry->second->Read(addr, dst, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
|
||||
auto memory_file = std::make_shared<MemoryFileAtOffset>();
|
||||
if (!memory_file->Init(file, offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first uint64_t value is the start of memory.
|
||||
uint64_t start;
|
||||
if (!memory_file->ReadFully(0, &start, sizeof(start))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t size = memory_file->Size();
|
||||
if (__builtin_sub_overflow(size, sizeof(start), &size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_ = std::make_unique<MemoryRange>(memory_file, sizeof(start), size, start);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (!memory_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return memory_->Read(addr, dst, size);
|
||||
}
|
||||
|
||||
MemoryOfflineBuffer::MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end)
|
||||
: data_(data), start_(start), end_(end) {}
|
||||
|
||||
void MemoryOfflineBuffer::Reset(const uint8_t* data, uint64_t start, uint64_t end) {
|
||||
data_ = data;
|
||||
start_ = start;
|
||||
end_ = end;
|
||||
}
|
||||
|
||||
size_t MemoryOfflineBuffer::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (addr < start_ || addr >= end_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t read_length = std::min(size, static_cast<size_t>(end_ - addr));
|
||||
memcpy(dst, &data_[addr - start_], read_length);
|
||||
return read_length;
|
||||
}
|
||||
|
||||
MemoryOfflineParts::~MemoryOfflineParts() {
|
||||
for (auto memory : memories_) {
|
||||
delete memory;
|
||||
}
|
||||
}
|
||||
|
||||
size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (memories_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Do a read on each memory object, no support for reading across the
|
||||
// different memory objects.
|
||||
for (MemoryOffline* memory : memories_) {
|
||||
size_t bytes = memory->Read(addr, dst, size);
|
||||
if (bytes != 0) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) {
|
||||
// Only bother caching and looking at the cache if this is a small read for now.
|
||||
if (size > 64) {
|
||||
return impl_->Read(addr, dst, size);
|
||||
}
|
||||
|
||||
uint64_t addr_page = addr >> kCacheBits;
|
||||
auto entry = cache_.find(addr_page);
|
||||
uint8_t* cache_dst;
|
||||
if (entry != cache_.end()) {
|
||||
cache_dst = entry->second;
|
||||
} else {
|
||||
cache_dst = cache_[addr_page];
|
||||
if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
|
||||
// Erase the entry.
|
||||
cache_.erase(addr_page);
|
||||
return impl_->Read(addr, dst, size);
|
||||
}
|
||||
}
|
||||
size_t max_read = ((addr_page + 1) << kCacheBits) - addr;
|
||||
if (size <= max_read) {
|
||||
memcpy(dst, &cache_dst[addr & kCacheMask], size);
|
||||
return size;
|
||||
}
|
||||
|
||||
// The read crossed into another cached entry, since a read can only cross
|
||||
// into one extra cached page, duplicate the code rather than looping.
|
||||
memcpy(dst, &cache_dst[addr & kCacheMask], max_read);
|
||||
dst = &reinterpret_cast<uint8_t*>(dst)[max_read];
|
||||
addr_page++;
|
||||
|
||||
entry = cache_.find(addr_page);
|
||||
if (entry != cache_.end()) {
|
||||
cache_dst = entry->second;
|
||||
} else {
|
||||
cache_dst = cache_[addr_page];
|
||||
if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
|
||||
// Erase the entry.
|
||||
cache_.erase(addr_page);
|
||||
return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read;
|
||||
}
|
||||
}
|
||||
memcpy(dst, cache_dst, size - max_read);
|
||||
return size;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_MEMORY_BUFFER_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryBuffer : public Memory {
|
||||
public:
|
||||
MemoryBuffer() = default;
|
||||
virtual ~MemoryBuffer() { free(raw_); }
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
uint8_t* GetPtr(size_t offset);
|
||||
|
||||
bool Resize(size_t size) {
|
||||
raw_ = reinterpret_cast<uint8_t*>(realloc(raw_, size));
|
||||
if (raw_ == nullptr) {
|
||||
size_ = 0;
|
||||
return false;
|
||||
}
|
||||
size_ = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t Size() { return size_; }
|
||||
|
||||
private:
|
||||
uint8_t* raw_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_BUFFER_H
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_MEMORY_CACHE_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_CACHE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryCache : public Memory {
|
||||
public:
|
||||
MemoryCache(Memory* memory) : impl_(memory) {}
|
||||
virtual ~MemoryCache() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
long ReadTag(uint64_t addr) override { return impl_->ReadTag(addr); }
|
||||
|
||||
void Clear() override { cache_.clear(); }
|
||||
|
||||
private:
|
||||
constexpr static size_t kCacheBits = 12;
|
||||
constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
|
||||
constexpr static size_t kCacheSize = 1 << kCacheBits;
|
||||
std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
|
||||
|
||||
std::unique_ptr<Memory> impl_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_CACHE_H
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryFileAtOffset : public Memory {
|
||||
public:
|
||||
MemoryFileAtOffset() = default;
|
||||
virtual ~MemoryFileAtOffset();
|
||||
|
||||
bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
size_t Size() { return size_; }
|
||||
|
||||
void Clear() override;
|
||||
|
||||
protected:
|
||||
size_t size_ = 0;
|
||||
size_t offset_ = 0;
|
||||
uint8_t* data_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_MEMORY_LOCAL_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryLocal : public Memory {
|
||||
public:
|
||||
MemoryLocal() = default;
|
||||
virtual ~MemoryLocal() = default;
|
||||
|
||||
bool IsLocal() const override { return true; }
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
long ReadTag(uint64_t addr) override;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_LOCAL_H
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue