Merge "Fix incorrect usage of relative pcs."
This commit is contained in:
commit
96b5268405
|
@ -130,6 +130,7 @@ cc_test {
|
|||
"tests/RegsStepIfSignalHandlerTest.cpp",
|
||||
"tests/RegsTest.cpp",
|
||||
"tests/SymbolsTest.cpp",
|
||||
"tests/UnwindOfflineTest.cpp",
|
||||
"tests/UnwindTest.cpp",
|
||||
"tests/UnwinderTest.cpp",
|
||||
],
|
||||
|
@ -153,6 +154,8 @@ cc_test {
|
|||
data: [
|
||||
"tests/files/elf32.xz",
|
||||
"tests/files/elf64.xz",
|
||||
"tests/files/offline/straddle_arm32/*",
|
||||
"tests/files/offline/straddle_arm64/*",
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
|
|||
|
||||
uint64_t AdjustPcFromFde(uint64_t pc) override {
|
||||
// The eh_frame uses relative pcs.
|
||||
return pc + this->memory_.cur_offset();
|
||||
return pc + this->memory_.cur_offset() - 4;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -104,8 +104,8 @@ bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offse
|
|||
}
|
||||
|
||||
// The relative pc is always relative to the start of the map from which it comes.
|
||||
bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
|
||||
bool* finished) {
|
||||
bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
|
||||
Memory* process_memory, bool* finished) {
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
|
@ -117,16 +117,16 @@ bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process
|
|||
}
|
||||
|
||||
// Adjust the load bias to get the real relative pc.
|
||||
if (rel_pc < load_bias_) {
|
||||
if (adjusted_rel_pc < load_bias_) {
|
||||
return false;
|
||||
}
|
||||
rel_pc -= load_bias_;
|
||||
adjusted_rel_pc -= load_bias_;
|
||||
|
||||
// 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) ||
|
||||
return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) ||
|
||||
(gnu_debugdata_interface_ &&
|
||||
gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
|
||||
gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished));
|
||||
}
|
||||
|
||||
bool Elf::IsValidElf(Memory* memory) {
|
||||
|
|
|
@ -64,13 +64,13 @@ static MapInfo* InternalParseLine(const char* line) {
|
|||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
|
||||
char* str;
|
||||
const char* old_str = line;
|
||||
uint64_t start = strtoul(old_str, &str, 16);
|
||||
uint64_t start = strtoull(old_str, &str, 16);
|
||||
if (old_str == str || *str++ != '-') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
old_str = str;
|
||||
uint64_t end = strtoul(old_str, &str, 16);
|
||||
uint64_t end = strtoull(old_str, &str, 16);
|
||||
if (old_str == str || !std::isspace(*str++)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -112,14 +112,14 @@ static MapInfo* InternalParseLine(const char* line) {
|
|||
}
|
||||
|
||||
old_str = str;
|
||||
uint64_t offset = strtoul(old_str, &str, 16);
|
||||
uint64_t offset = strtoull(old_str, &str, 16);
|
||||
if (old_str == str || !std::isspace(*str)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ignore the 00:00 values.
|
||||
old_str = str;
|
||||
(void)strtoul(old_str, &str, 16);
|
||||
(void)strtoull(old_str, &str, 16);
|
||||
if (old_str == str || *str++ != ':') {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -129,14 +129,14 @@ static MapInfo* InternalParseLine(const char* line) {
|
|||
|
||||
// Skip the inode.
|
||||
old_str = str;
|
||||
(void)strtoul(str, &str, 16);
|
||||
(void)strtoull(str, &str, 16);
|
||||
if (old_str == str || !std::isspace(*str++)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skip decimal digit.
|
||||
old_str = str;
|
||||
(void)strtoul(old_str, &str, 10);
|
||||
(void)strtoull(old_str, &str, 10);
|
||||
if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -32,27 +32,20 @@
|
|||
|
||||
namespace unwindstack {
|
||||
|
||||
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
|
||||
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) {
|
||||
size_t frame_num = frames_.size();
|
||||
frames_.resize(frame_num + 1);
|
||||
FrameData* frame = &frames_.at(frame_num);
|
||||
frame->num = frame_num;
|
||||
frame->pc = regs_->pc();
|
||||
frame->sp = regs_->sp();
|
||||
frame->rel_pc = rel_pc;
|
||||
frame->rel_pc = adjusted_rel_pc;
|
||||
|
||||
if (map_info == nullptr) {
|
||||
frame->pc = regs_->pc();
|
||||
return;
|
||||
}
|
||||
|
||||
if (adjust_pc) {
|
||||
// Don't adjust the first frame pc.
|
||||
frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
|
||||
|
||||
// Adjust the original pc.
|
||||
frame->pc -= rel_pc - frame->rel_pc;
|
||||
}
|
||||
|
||||
frame->pc = map_info->start + adjusted_rel_pc;
|
||||
frame->map_name = map_info->name;
|
||||
frame->map_offset = map_info->offset;
|
||||
frame->map_start = map_info->start;
|
||||
|
@ -92,21 +85,29 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
|
|||
|
||||
MapInfo* map_info = maps_->Find(regs_->pc());
|
||||
uint64_t rel_pc;
|
||||
uint64_t adjusted_rel_pc;
|
||||
Elf* elf;
|
||||
if (map_info == nullptr) {
|
||||
rel_pc = regs_->pc();
|
||||
adjusted_rel_pc = rel_pc;
|
||||
} else {
|
||||
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
|
||||
break;
|
||||
}
|
||||
elf = map_info->GetElf(process_memory_, true);
|
||||
rel_pc = elf->GetRelPc(regs_->pc(), map_info);
|
||||
if (adjust_pc) {
|
||||
adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
|
||||
} else {
|
||||
adjusted_rel_pc = rel_pc;
|
||||
}
|
||||
}
|
||||
|
||||
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
|
||||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
|
||||
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
|
||||
FillInFrame(map_info, elf, rel_pc, adjust_pc);
|
||||
FillInFrame(map_info, elf, adjusted_rel_pc);
|
||||
|
||||
// Once a frame is added, stop skipping frames.
|
||||
initial_map_names_to_skip = nullptr;
|
||||
}
|
||||
|
@ -133,7 +134,8 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
|
|||
in_device_map = true;
|
||||
} else {
|
||||
bool finished;
|
||||
stepped = elf->Step(rel_pc, map_info->elf_offset, regs_, process_memory_.get(), &finished);
|
||||
stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
|
||||
process_memory_.get(), &finished);
|
||||
if (stepped && finished) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ class Elf {
|
|||
|
||||
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
|
||||
|
||||
bool Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
|
||||
bool* finished);
|
||||
bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
|
||||
Memory* process_memory, bool* finished);
|
||||
|
||||
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class Unwinder {
|
|||
static std::string FormatFrame(const FrameData& frame, bool bits32);
|
||||
|
||||
private:
|
||||
void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
|
||||
void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc);
|
||||
|
||||
size_t max_frames_;
|
||||
Maps* maps_;
|
||||
|
|
|
@ -109,23 +109,23 @@ TYPED_TEST_P(DwarfEhFrameTest, Init32) {
|
|||
|
||||
this->eh_frame_->TestGetFdeInfo(0, &info);
|
||||
EXPECT_EQ(0x5100U, info.offset);
|
||||
EXPECT_EQ(0x660cU, info.start);
|
||||
EXPECT_EQ(0x680cU, info.end);
|
||||
EXPECT_EQ(0x6608U, info.start);
|
||||
EXPECT_EQ(0x6808U, info.end);
|
||||
|
||||
this->eh_frame_->TestGetFdeInfo(1, &info);
|
||||
EXPECT_EQ(0x5200U, info.offset);
|
||||
EXPECT_EQ(0x770cU, info.start);
|
||||
EXPECT_EQ(0x7a0cU, info.end);
|
||||
EXPECT_EQ(0x7708U, info.start);
|
||||
EXPECT_EQ(0x7a08U, info.end);
|
||||
|
||||
this->eh_frame_->TestGetFdeInfo(2, &info);
|
||||
EXPECT_EQ(0x5400U, info.offset);
|
||||
EXPECT_EQ(0x890cU, info.start);
|
||||
EXPECT_EQ(0x8d0cU, info.end);
|
||||
EXPECT_EQ(0x8908U, info.start);
|
||||
EXPECT_EQ(0x8d08U, info.end);
|
||||
|
||||
this->eh_frame_->TestGetFdeInfo(3, &info);
|
||||
EXPECT_EQ(0x5500U, info.offset);
|
||||
EXPECT_EQ(0x9a0cU, info.start);
|
||||
EXPECT_EQ(0x9f0cU, info.end);
|
||||
EXPECT_EQ(0x9a08U, info.start);
|
||||
EXPECT_EQ(0x9f08U, info.end);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfEhFrameTest, Init32_fde_not_following_cie) {
|
||||
|
@ -193,23 +193,23 @@ TYPED_TEST_P(DwarfEhFrameTest, Init64) {
|
|||
|
||||
this->eh_frame_->TestGetFdeInfo(0, &info);
|
||||
EXPECT_EQ(0x5100U, info.offset);
|
||||
EXPECT_EQ(0x661cU, info.start);
|
||||
EXPECT_EQ(0x681cU, info.end);
|
||||
EXPECT_EQ(0x6618U, info.start);
|
||||
EXPECT_EQ(0x6818U, info.end);
|
||||
|
||||
this->eh_frame_->TestGetFdeInfo(1, &info);
|
||||
EXPECT_EQ(0x5200U, info.offset);
|
||||
EXPECT_EQ(0x771cU, info.start);
|
||||
EXPECT_EQ(0x7a1cU, info.end);
|
||||
EXPECT_EQ(0x7718U, info.start);
|
||||
EXPECT_EQ(0x7a18U, info.end);
|
||||
|
||||
this->eh_frame_->TestGetFdeInfo(2, &info);
|
||||
EXPECT_EQ(0x5400U, info.offset);
|
||||
EXPECT_EQ(0x891cU, info.start);
|
||||
EXPECT_EQ(0x8d1cU, info.end);
|
||||
EXPECT_EQ(0x8918U, info.start);
|
||||
EXPECT_EQ(0x8d18U, info.end);
|
||||
|
||||
this->eh_frame_->TestGetFdeInfo(3, &info);
|
||||
EXPECT_EQ(0x5500U, info.offset);
|
||||
EXPECT_EQ(0x9a1cU, info.start);
|
||||
EXPECT_EQ(0x9f1cU, info.end);
|
||||
EXPECT_EQ(0x9a18U, info.start);
|
||||
EXPECT_EQ(0x9f18U, info.end);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
|
||||
|
@ -261,8 +261,8 @@ TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
|
|||
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
|
||||
this->eh_frame_->TestGetFdeInfo(0, &info);
|
||||
EXPECT_EQ(0x5100U, info.offset);
|
||||
EXPECT_EQ(0x660aU, info.start);
|
||||
EXPECT_EQ(0x680aU, info.end);
|
||||
EXPECT_EQ(0x6606U, info.start);
|
||||
EXPECT_EQ(0x6806U, info.end);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
|
||||
|
@ -304,8 +304,8 @@ TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
|
|||
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
|
||||
this->eh_frame_->TestGetFdeInfo(0, &info);
|
||||
EXPECT_EQ(0x5100U, info.offset);
|
||||
EXPECT_EQ(0x660aU, info.start);
|
||||
EXPECT_EQ(0x680aU, info.end);
|
||||
EXPECT_EQ(0x6606U, info.start);
|
||||
EXPECT_EQ(0x6806U, info.end);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
|
||||
|
@ -384,8 +384,8 @@ TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
|
|||
ASSERT_TRUE(fde != nullptr);
|
||||
EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0x1d00cU, fde->pc_start);
|
||||
EXPECT_EQ(0x1d10cU, fde->pc_end);
|
||||
EXPECT_EQ(0x1d008U, fde->pc_start);
|
||||
EXPECT_EQ(0x1d108U, fde->pc_end);
|
||||
EXPECT_EQ(0xf000U, fde->cie_offset);
|
||||
EXPECT_EQ(0U, fde->lsda_address);
|
||||
|
||||
|
@ -428,8 +428,8 @@ TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
|
|||
ASSERT_TRUE(fde != nullptr);
|
||||
EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0xd01cU, fde->pc_start);
|
||||
EXPECT_EQ(0xd31cU, fde->pc_end);
|
||||
EXPECT_EQ(0xd018U, fde->pc_start);
|
||||
EXPECT_EQ(0xd318U, fde->pc_end);
|
||||
EXPECT_EQ(0x6000U, fde->cie_offset);
|
||||
EXPECT_EQ(0U, fde->lsda_address);
|
||||
|
||||
|
|
|
@ -345,8 +345,8 @@ TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
|
|||
ASSERT_TRUE(fde != nullptr);
|
||||
EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0x1d00cU, fde->pc_start);
|
||||
EXPECT_EQ(0x1d10cU, fde->pc_end);
|
||||
EXPECT_EQ(0x1d008U, fde->pc_start);
|
||||
EXPECT_EQ(0x1d108U, fde->pc_end);
|
||||
EXPECT_EQ(0xf000U, fde->cie_offset);
|
||||
EXPECT_EQ(0U, fde->lsda_address);
|
||||
|
||||
|
@ -387,8 +387,8 @@ TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde64) {
|
|||
ASSERT_TRUE(fde != nullptr);
|
||||
EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0xd01cU, fde->pc_start);
|
||||
EXPECT_EQ(0xd31cU, fde->pc_end);
|
||||
EXPECT_EQ(0xd018U, fde->pc_start);
|
||||
EXPECT_EQ(0xd318U, fde->pc_end);
|
||||
EXPECT_EQ(0x6000U, fde->cie_offset);
|
||||
EXPECT_EQ(0U, fde->lsda_address);
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ TEST_F(ElfTest, elf_invalid) {
|
|||
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
|
||||
|
||||
bool finished;
|
||||
ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
|
||||
ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, elf32_invalid_machine) {
|
||||
|
@ -306,7 +306,7 @@ TEST_F(ElfTest, step_in_signal_map) {
|
|||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
bool finished;
|
||||
ASSERT_TRUE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(15U, regs.pc());
|
||||
EXPECT_EQ(13U, regs.sp());
|
||||
|
@ -339,7 +339,7 @@ TEST_F(ElfTest, step_in_interface) {
|
|||
EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
ASSERT_TRUE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
|
||||
|
@ -355,12 +355,12 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
|
|||
|
||||
// Invalid relative pc given load_bias.
|
||||
bool finished;
|
||||
ASSERT_FALSE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
|
||||
EXPECT_CALL(*interface, Step(0x3300, ®s, &process_memory, &finished))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
ASSERT_TRUE(elf.Step(0x7300, 0x2000, ®s, &process_memory, &finished));
|
||||
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -47,7 +47,7 @@ void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
|
|||
ehdr->e_ehsize = sizeof(Ehdr);
|
||||
}
|
||||
|
||||
static std::string GetTestFileDirectory() {
|
||||
std::string TestGetFileDirectory() {
|
||||
std::string exec(testing::internal::GetArgvs()[0]);
|
||||
auto const value = exec.find_last_of('/');
|
||||
if (value == std::string::npos) {
|
||||
|
@ -102,7 +102,7 @@ void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_de
|
|||
offset = symtab_offset + 0x100;
|
||||
if (init_gnu_debugdata) {
|
||||
// Read in the compressed elf data and copy it in.
|
||||
name = GetTestFileDirectory();
|
||||
name = TestGetFileDirectory();
|
||||
if (elf_class == ELFCLASS32) {
|
||||
name += "elf32.xz";
|
||||
} else {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
@ -30,6 +31,8 @@ template <typename Ehdr, typename Shdr>
|
|||
void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
|
||||
TestCopyFuncType copy_func);
|
||||
|
||||
std::string TestGetFileDirectory();
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "Machine.h"
|
||||
|
||||
#include "ElfTestUtils.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
static std::string DumpFrames(Unwinder& unwinder) {
|
||||
std::string str;
|
||||
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
|
||||
str += unwinder.FormatFrame(i) + "\n";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
TEST(UnwindOfflineTest, pc_straddle_arm32) {
|
||||
std::string dir(TestGetFileDirectory() + "offline/straddle_arm32/");
|
||||
|
||||
MemoryOffline* memory = new MemoryOffline;
|
||||
ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
|
||||
|
||||
FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
|
||||
ASSERT_TRUE(fp != nullptr);
|
||||
RegsArm regs;
|
||||
uint64_t reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value));
|
||||
regs[ARM_REG_PC] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value));
|
||||
regs[ARM_REG_SP] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", ®_value));
|
||||
regs[ARM_REG_LR] = reg_value;
|
||||
regs.SetFromRaw();
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen((dir + "maps.txt").c_str(), "r");
|
||||
ASSERT_TRUE(fp != nullptr);
|
||||
// The file is guaranteed to be less than 4096 bytes.
|
||||
std::vector<char> buffer(4096);
|
||||
ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
|
||||
fclose(fp);
|
||||
|
||||
BufferMaps maps(buffer.data());
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
|
||||
ASSERT_EQ(static_cast<uint32_t>(EM_ARM), regs.MachineType());
|
||||
|
||||
std::shared_ptr<Memory> process_memory(memory);
|
||||
|
||||
char* cwd = getcwd(nullptr, 0);
|
||||
ASSERT_EQ(0, chdir(dir.c_str()));
|
||||
Unwinder unwinder(128, &maps, ®s, process_memory);
|
||||
unwinder.Unwind();
|
||||
ASSERT_EQ(0, chdir(cwd));
|
||||
free(cwd);
|
||||
|
||||
std::string frame_info(DumpFrames(unwinder));
|
||||
ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
|
||||
EXPECT_EQ(
|
||||
" #00 pc 0001a9f8 libc.so (abort+63)\n"
|
||||
" #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
|
||||
" #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
|
||||
" #03 pc 00015149 /does/not/exist/libhidlbase.so\n",
|
||||
frame_info);
|
||||
}
|
||||
|
||||
TEST(UnwindOfflineTest, pc_straddle_arm64) {
|
||||
std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
|
||||
|
||||
MemoryOffline* memory = new MemoryOffline;
|
||||
ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
|
||||
|
||||
FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
|
||||
ASSERT_TRUE(fp != nullptr);
|
||||
RegsArm64 regs;
|
||||
uint64_t reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value));
|
||||
regs[ARM64_REG_PC] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value));
|
||||
regs[ARM64_REG_SP] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", ®_value));
|
||||
regs[ARM64_REG_LR] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", ®_value));
|
||||
regs[ARM64_REG_R29] = reg_value;
|
||||
regs.SetFromRaw();
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen((dir + "maps.txt").c_str(), "r");
|
||||
ASSERT_TRUE(fp != nullptr);
|
||||
// The file is guaranteed to be less than 4096 bytes.
|
||||
std::vector<char> buffer(4096);
|
||||
ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
|
||||
fclose(fp);
|
||||
|
||||
BufferMaps maps(buffer.data());
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
|
||||
ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), regs.MachineType());
|
||||
|
||||
std::shared_ptr<Memory> process_memory(memory);
|
||||
|
||||
char* cwd = getcwd(nullptr, 0);
|
||||
ASSERT_EQ(0, chdir(dir.c_str()));
|
||||
Unwinder unwinder(128, &maps, ®s, process_memory);
|
||||
unwinder.Unwind();
|
||||
ASSERT_EQ(0, chdir(cwd));
|
||||
free(cwd);
|
||||
|
||||
std::string frame_info(DumpFrames(unwinder));
|
||||
ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
|
||||
EXPECT_EQ(
|
||||
" #00 pc 0000000000429fd8 libunwindstack_test (SignalInnerFunction+24)\n"
|
||||
" #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)\n"
|
||||
" #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)\n"
|
||||
" #03 pc 000000000042d8fc libunwindstack_test "
|
||||
"(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
|
||||
" #04 pc 000000000042d8d8 libunwindstack_test "
|
||||
"(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
|
||||
" #05 pc 0000000000455d70 libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
|
||||
frame_info);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
f2d9a000-f2da7fff r-xp 00000000 00:00 0 libbase.so
|
||||
f3002000-f3005fff rw-p 00000000 00:00 0 [stack:25941]
|
||||
f31d0000-f326bfff r-xp 00000000 00:00 0 libc.so
|
||||
f3352000-f336bfff r-xp 00000000 00:00 0 /does/not/exist/libhidlbase.so
|
|
@ -0,0 +1,3 @@
|
|||
pc: f31ea9f8
|
||||
sp: e9c866f8
|
||||
lr: f31f179f
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
00000064d05ab000-00000064d0a6cfff r-xp 00000000 00:00 0 libunwindstack_test
|
||||
0000007fe0d64000-0000007fe0d84fff rw-p 00000000 00:00 0 [stack]
|
|
@ -0,0 +1,4 @@
|
|||
pc: 00000064d09d4fd8
|
||||
sp: 0000007fe0d84040
|
||||
lr: 00000064d09d507c
|
||||
x29: 0000007fe0d84070
|
Binary file not shown.
Loading…
Reference in New Issue