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:
Baligh Uddin 2020-10-09 04:52:00 +00:00
parent b5e565d29b
commit ce7b63697e
413 changed files with 3 additions and 50519 deletions

1
libbacktrace Symbolic link
View file

@ -0,0 +1 @@
../unwinding/libbacktrace

View file

@ -1 +0,0 @@
../.clang-format-2

View file

@ -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",
],
}

View file

@ -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";
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -1,2 +0,0 @@
cferris@google.com
jmgao@google.com

View file

@ -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));
}

View file

@ -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

View file

@ -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);
}
}
}
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -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()

View file

@ -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

View file

@ -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*)

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -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*)

View file

@ -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&)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
libprocinfo Symbolic link
View file

@ -0,0 +1 @@
../libprocinfo

View file

@ -1 +0,0 @@
../.clang-format-2

View file

@ -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/*",
],
}

View file

@ -1 +0,0 @@
jmgao@google.com

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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();

View file

@ -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);
}

View file

@ -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));
}

File diff suppressed because it is too large Load diff

1
libunwindstack Symbolic link
View file

@ -0,0 +1 @@
../unwinding/libunwindstack

View file

@ -1 +0,0 @@
../.clang-format-2

View file

@ -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",
],
}

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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, &note_offset, &note_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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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