Merge "Fix off by 4 error handling eh_frame hdr."
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5fddddc..364fca5 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -55,7 +55,7 @@
using android::base::unique_fd;
using unwindstack::Regs;
-extern "C" void __linker_enable_fallback_allocator();
+extern "C" bool __linker_enable_fallback_allocator();
extern "C" void __linker_disable_fallback_allocator();
// This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
@@ -65,7 +65,11 @@
// This isn't the default method of dumping because it can fail in cases such as address space
// exhaustion.
static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
- __linker_enable_fallback_allocator();
+ if (!__linker_enable_fallback_allocator()) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
+ return;
+ }
+
{
std::unique_ptr<Regs> regs;
@@ -84,7 +88,11 @@
static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo,
void* abort_message) {
- __linker_enable_fallback_allocator();
+ if (!__linker_enable_fallback_allocator()) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
+ return;
+ }
+
engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo,
ucontext);
__linker_disable_fallback_allocator();
@@ -116,7 +124,7 @@
closedir(dir);
}
-static bool forward_output(int src_fd, int dst_fd) {
+static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
// Make sure the thread actually got the signal.
struct pollfd pfd = {
.fd = src_fd, .events = POLLIN,
@@ -127,6 +135,18 @@
return false;
}
+ pid_t tid;
+ if (TEMP_FAILURE_RETRY(read(src_fd, &tid, sizeof(tid))) != sizeof(tid)) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to read tid");
+ return false;
+ }
+
+ if (tid != expected_tid) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "received tid %d, expected %d", tid,
+ expected_tid);
+ return false;
+ }
+
while (true) {
char buf[512];
ssize_t rc = TEMP_FAILURE_RETRY(read(src_fd, buf, sizeof(buf)));
@@ -144,16 +164,54 @@
}
}
+struct __attribute__((__packed__)) packed_thread_output {
+ int32_t tid;
+ int32_t fd;
+};
+
+static uint64_t pack_thread_fd(pid_t tid, int fd) {
+ packed_thread_output packed = {.tid = tid, .fd = fd};
+ uint64_t result;
+ static_assert(sizeof(packed) == sizeof(result));
+ memcpy(&result, &packed, sizeof(packed));
+ return result;
+}
+
+static std::pair<pid_t, int> unpack_thread_fd(uint64_t value) {
+ packed_thread_output result;
+ memcpy(&result, &value, sizeof(value));
+ return std::make_pair(result.tid, result.fd);
+}
+
static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
- static std::atomic<int> trace_output_fd(-1);
+ static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
if (info->si_value.sival_int == ~0) {
// Asked to dump by the original signal recipient.
- debuggerd_fallback_trace(trace_output_fd, ucontext);
+ uint64_t val = trace_output.load();
+ auto [tid, fd] = unpack_thread_fd(val);
+ if (tid != gettid()) {
+ // We received some other thread's info request?
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "thread %d received output fd for thread %d?", gettid(), tid);
+ return;
+ }
- int tmp = trace_output_fd.load();
- trace_output_fd.store(-1);
- close(tmp);
+ if (!trace_output.compare_exchange_strong(val, pack_thread_fd(-1, -1))) {
+ // Presumably, the timeout in forward_output expired, and the main thread moved on.
+ // If this happened, the main thread closed our fd for us, so just return.
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "cmpxchg for thread %d failed", gettid());
+ return;
+ }
+
+ // Write our tid to the output fd to let the main thread know that we're working.
+ if (TEMP_FAILURE_RETRY(write(fd, &tid, sizeof(tid))) == sizeof(tid)) {
+ debuggerd_fallback_trace(fd, ucontext);
+ } else {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write to output fd");
+ }
+
+ close(fd);
return;
}
@@ -189,7 +247,14 @@
return false;
}
- trace_output_fd.store(pipe_write.get());
+ uint64_t expected = pack_thread_fd(-1, -1);
+ if (!trace_output.compare_exchange_strong(expected,
+ pack_thread_fd(tid, pipe_write.release()))) {
+ auto [tid, fd] = unpack_thread_fd(expected);
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "thread %d is already outputting to fd %d?", tid, fd);
+ return false;
+ }
siginfo_t siginfo = {};
siginfo.si_code = SI_QUEUE;
@@ -203,12 +268,20 @@
return false;
}
- bool success = forward_output(pipe_read.get(), output_fd);
- if (success) {
- // The signaled thread has closed trace_output_fd already.
- (void)pipe_write.release();
- } else {
- trace_output_fd.store(-1);
+ bool success = forward_output(pipe_read.get(), output_fd, tid);
+ if (!success) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "timeout expired while waiting for thread %d to dump", tid);
+ }
+
+ // Regardless of whether the poll succeeds, check to see if the thread took fd ownership.
+ uint64_t post_wait = trace_output.exchange(pack_thread_fd(-1, -1));
+ if (post_wait != pack_thread_fd(-1, -1)) {
+ auto [tid, fd] = unpack_thread_fd(post_wait);
+ if (fd != -1) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "closing fd %d for thread %d", fd, tid);
+ close(fd);
+ }
}
return true;
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index 46a6f76..1787031 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -509,6 +509,29 @@
ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
}
+TEST(DemangleTest, r_value_reference) {
+ Demangler demangler;
+ ASSERT_EQ(
+ "android::SurfaceComposerClient::Transaction::merge(android::SurfaceComposerClient::"
+ "Transaction&&)",
+ demangler.Parse("_ZN7android21SurfaceComposerClient11Transaction5mergeEOS1_"));
+}
+
+TEST(DemangleTest, initial_St) {
+ Demangler demangler;
+ EXPECT_EQ("std::state", demangler.Parse("_ZSt5state"));
+ EXPECT_EQ("std::_In::ward", demangler.Parse("_ZNSt3_In4wardE"));
+ EXPECT_EQ("std::__terminate(void (*)())", demangler.Parse("_ZSt11__terminatePFvvE"));
+}
+
+TEST(DemangleTest, cfi) {
+ Demangler demangler;
+ EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*)",
+ demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB"));
+ EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*) [clone .cfi]",
+ demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB.cfi"));
+}
+
TEST(DemangleTest, demangle) {
std::string str;
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index af2816c..7a3aa81 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -580,6 +580,10 @@
}
return name + 1;
+ case 'O':
+ cur_state_.suffixes.push_back("&&");
+ return name + 1;
+
case 'K':
case 'V': {
const char* suffix;
@@ -701,6 +705,9 @@
cur_state_.str.clear();
}
return name;
+ } else if (strcmp(name, ".cfi") == 0) {
+ function_suffix_ += " [clone .cfi]";
+ return name + 4;
}
}
return nullptr;
@@ -816,6 +823,16 @@
return name + 1;
}
+ if (*name == 'S') {
+ name++;
+ if (*name == 't') {
+ function_name_ = "std::";
+ name++;
+ } else {
+ return nullptr;
+ }
+ }
+
if (std::isdigit(*name)) {
name = GetStringFromLength(name, &function_name_);
} else if (*name == 'L' && std::isdigit(name[1])) {
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 644bfa8..dfd7b18 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -175,6 +175,8 @@
if (regs_->dex_pc() != 0) {
// Add a frame to represent the dex file.
FillInDexFrame();
+ // Clear the dex pc so that we don't repeat this frame later.
+ regs_->set_dex_pc(0);
}
FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 09c6e04..7358ae6 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -752,6 +752,64 @@
EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
}
+TEST_F(UnwinderTest, dex_pc_multiple_frames) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ regs_.FakeSetPc(0x1000);
+ regs_.FakeSetSp(0x10000);
+ regs_.FakeSetDexPc(0xa3400);
+ ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xa3400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0xa3000U, frame->map_start);
+ EXPECT_EQ(0xa4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0x33400U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/fake/compressed.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x33000U, frame->map_start);
+ EXPECT_EQ(0x34000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
// Verify format frame code.
TEST_F(UnwinderTest, format_frame_static) {
FrameData frame;