Merge "Fix issues in libunwindstack."
am: e05d6afb90 Change-Id: I69d62a4d9fb9810ce13e22d57b84b1cc3d29f05f
This commit is contained in:
commit
31bf08431c
|
@ -167,6 +167,7 @@ cc_test {
|
|||
data: [
|
||||
"tests/files/elf32.xz",
|
||||
"tests/files/elf64.xz",
|
||||
"tests/files/offline/gnu_debugdata_arm32/*",
|
||||
"tests/files/offline/straddle_arm32/*",
|
||||
"tests/files/offline/straddle_arm64/*",
|
||||
],
|
||||
|
|
|
@ -79,6 +79,7 @@ void Elf::InitGnuDebugdata() {
|
|||
uint64_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);
|
||||
|
@ -115,17 +116,9 @@ bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, R
|
|||
return true;
|
||||
}
|
||||
|
||||
// Adjust the load bias to get the real relative pc.
|
||||
if (adjusted_rel_pc < load_bias_) {
|
||||
return false;
|
||||
}
|
||||
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(adjusted_rel_pc, regs, process_memory, finished) ||
|
||||
(gnu_debugdata_interface_ &&
|
||||
gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished));
|
||||
return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished);
|
||||
}
|
||||
|
||||
bool Elf::IsValidElf(Memory* memory) {
|
||||
|
|
|
@ -386,16 +386,29 @@ bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished) {
|
||||
// Adjust the load bias to get the real relative pc.
|
||||
if (pc < load_bias) {
|
||||
return false;
|
||||
}
|
||||
uint64_t adjusted_pc = pc - load_bias;
|
||||
|
||||
// Try the eh_frame first.
|
||||
DwarfSection* eh_frame = eh_frame_.get();
|
||||
if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
|
||||
if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try the debug_frame next.
|
||||
DwarfSection* debug_frame = debug_frame_.get();
|
||||
if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
|
||||
if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finally try the gnu_debugdata interface, but always use a zero load bias.
|
||||
if (gnu_debugdata_interface_ != nullptr &&
|
||||
gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -92,16 +92,24 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_b
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished) {
|
||||
// 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) ||
|
||||
StepExidx(pc, regs, process_memory, finished);
|
||||
return ElfInterface32::Step(pc, load_bias, regs, process_memory, finished) ||
|
||||
StepExidx(pc, load_bias, regs, process_memory, finished);
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished) {
|
||||
// Adjust the load bias to get the real relative pc.
|
||||
if (pc < load_bias) {
|
||||
return false;
|
||||
}
|
||||
pc -= load_bias;
|
||||
|
||||
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
|
||||
uint64_t entry_offset;
|
||||
if (!FindEntry(pc, &entry_offset)) {
|
||||
|
|
|
@ -70,9 +70,11 @@ class ElfInterfaceArm : public ElfInterface32 {
|
|||
|
||||
bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
|
||||
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
|
||||
bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished) override;
|
||||
|
||||
bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
|
||||
bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished);
|
||||
|
||||
uint64_t start_offset() { return start_offset_; }
|
||||
|
||||
|
|
|
@ -122,13 +122,21 @@ Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gn
|
|||
}
|
||||
|
||||
uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
|
||||
uint64_t cur_load_bias = load_bias.load();
|
||||
if (cur_load_bias != static_cast<uint64_t>(-1)) {
|
||||
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()) {
|
||||
return elf->GetLoadBias();
|
||||
cur_load_bias = elf->GetLoadBias();
|
||||
load_bias = cur_load_bias;
|
||||
return cur_load_bias;
|
||||
} else {
|
||||
load_bias = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +145,9 @@ uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
|
|||
// 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));
|
||||
return Elf::GetLoadBias(memory.get());
|
||||
cur_load_bias = Elf::GetLoadBias(memory.get());
|
||||
load_bias = cur_load_bias;
|
||||
return cur_load_bias;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -202,6 +202,13 @@ bool Maps::Parse() {
|
|||
return return_value;
|
||||
}
|
||||
|
||||
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
|
||||
const std::string& name, uint64_t load_bias) {
|
||||
MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
|
||||
map_info->load_bias = load_bias;
|
||||
maps_.push_back(map_info);
|
||||
}
|
||||
|
||||
Maps::~Maps() {
|
||||
for (auto& map : maps_) {
|
||||
delete map;
|
||||
|
@ -235,61 +242,4 @@ const std::string RemoteMaps::GetMapsFile() const {
|
|||
return "/proc/" + std::to_string(pid_) + "/maps";
|
||||
}
|
||||
|
||||
bool OfflineMaps::Parse() {
|
||||
// Format of maps information:
|
||||
// <uint64_t> StartOffset
|
||||
// <uint64_t> EndOffset
|
||||
// <uint64_t> offset
|
||||
// <uint16_t> flags
|
||||
// <uint16_t> MapNameLength
|
||||
// <VariableLengthValue> MapName
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> name;
|
||||
while (true) {
|
||||
uint64_t start;
|
||||
ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &start, sizeof(start)));
|
||||
if (bytes == 0) {
|
||||
break;
|
||||
}
|
||||
if (bytes == -1 || bytes != sizeof(start)) {
|
||||
return false;
|
||||
}
|
||||
uint64_t end;
|
||||
bytes = TEMP_FAILURE_RETRY(read(fd, &end, sizeof(end)));
|
||||
if (bytes == -1 || bytes != sizeof(end)) {
|
||||
return false;
|
||||
}
|
||||
uint64_t offset;
|
||||
bytes = TEMP_FAILURE_RETRY(read(fd, &offset, sizeof(offset)));
|
||||
if (bytes == -1 || bytes != sizeof(offset)) {
|
||||
return false;
|
||||
}
|
||||
uint16_t flags;
|
||||
bytes = TEMP_FAILURE_RETRY(read(fd, &flags, sizeof(flags)));
|
||||
if (bytes == -1 || bytes != sizeof(flags)) {
|
||||
return false;
|
||||
}
|
||||
uint16_t len;
|
||||
bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
|
||||
if (bytes == -1 || bytes != sizeof(len)) {
|
||||
return false;
|
||||
}
|
||||
if (len > 0) {
|
||||
name.resize(len);
|
||||
bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
|
||||
if (bytes == -1 || bytes != len) {
|
||||
return false;
|
||||
}
|
||||
maps_.push_back(new MapInfo(start, end, offset, flags, std::string(name.data(), len)));
|
||||
} else {
|
||||
maps_.push_back(new MapInfo(start, end, offset, flags, ""));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace unwindstack {
|
|||
struct x86_64_stack_t {
|
||||
uint64_t ss_sp; // void __user*
|
||||
int32_t ss_flags; // int
|
||||
int32_t pad;
|
||||
uint64_t ss_size; // size_t
|
||||
};
|
||||
|
||||
|
|
|
@ -60,7 +60,8 @@ class ElfInterface {
|
|||
virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
|
||||
uint64_t* offset) = 0;
|
||||
|
||||
virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
|
||||
virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished);
|
||||
|
||||
Memory* CreateGnuDebugdataMemory();
|
||||
|
||||
|
@ -68,6 +69,8 @@ class ElfInterface {
|
|||
|
||||
const std::unordered_map<uint64_t, LoadInfo>& pt_loads() { return pt_loads_; }
|
||||
|
||||
void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
|
||||
|
||||
uint64_t dynamic_offset() { return dynamic_offset_; }
|
||||
uint64_t dynamic_size() { return dynamic_size_; }
|
||||
uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
|
||||
|
@ -134,6 +137,8 @@ class ElfInterface {
|
|||
|
||||
std::unique_ptr<DwarfSection> eh_frame_;
|
||||
std::unique_ptr<DwarfSection> debug_frame_;
|
||||
// The Elf object owns the gnu_debugdata interface object.
|
||||
ElfInterface* gnu_debugdata_interface_ = nullptr;
|
||||
|
||||
std::vector<Symbols*> symbols_;
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
|
@ -33,7 +34,12 @@ struct MapInfo {
|
|||
MapInfo() = default;
|
||||
MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
|
||||
MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
|
||||
: start(start), end(end), offset(offset), flags(flags), name(name) {}
|
||||
: start(start),
|
||||
end(end),
|
||||
offset(offset),
|
||||
flags(flags),
|
||||
name(name),
|
||||
load_bias(static_cast<uint64_t>(-1)) {}
|
||||
~MapInfo() { delete elf; }
|
||||
|
||||
uint64_t start = 0;
|
||||
|
@ -48,6 +54,8 @@ struct MapInfo {
|
|||
// instead of a portion of the file.
|
||||
uint64_t elf_offset = 0;
|
||||
|
||||
std::atomic_uint64_t load_bias;
|
||||
|
||||
// This function guarantees it will never return nullptr.
|
||||
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
|
||||
|
||||
|
|
|
@ -42,6 +42,9 @@ class Maps {
|
|||
|
||||
virtual const std::string GetMapsFile() const { return ""; }
|
||||
|
||||
void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
|
||||
uint64_t load_bias);
|
||||
|
||||
typedef std::vector<MapInfo*>::iterator iterator;
|
||||
iterator begin() { return maps_.begin(); }
|
||||
iterator end() { return maps_.end(); }
|
||||
|
@ -100,14 +103,6 @@ class FileMaps : public Maps {
|
|||
const std::string file_;
|
||||
};
|
||||
|
||||
class OfflineMaps : public FileMaps {
|
||||
public:
|
||||
OfflineMaps(const std::string& file) : FileMaps(file) {}
|
||||
virtual ~OfflineMaps() = default;
|
||||
|
||||
bool Parse() override;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MAPS_H
|
||||
|
|
|
@ -43,7 +43,7 @@ bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, ui
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
|
||||
bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
|
||||
if (steps_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,7 @@ class ElfInterfaceFake : public ElfInterface {
|
|||
|
||||
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
|
||||
|
||||
bool Step(uint64_t, Regs*, Memory*, bool*) override;
|
||||
|
||||
bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
|
||||
|
||||
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
|
||||
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
|
||||
|
|
|
@ -302,7 +302,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx) {
|
|||
|
||||
// FindEntry fails.
|
||||
bool finished;
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
|
||||
|
||||
// ExtractEntry should fail.
|
||||
interface.FakeSetStartOffset(0x1000);
|
||||
|
@ -315,20 +315,26 @@ TEST_F(ElfInterfaceArmTest, StepExidx) {
|
|||
regs[ARM_REG_LR] = 0x20000;
|
||||
regs.set_sp(regs[ARM_REG_SP]);
|
||||
regs.set_pc(0x1234);
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
|
||||
// Eval should fail.
|
||||
memory_.SetData32(0x1004, 0x81000000);
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
|
||||
// Everything should pass.
|
||||
memory_.SetData32(0x1004, 0x80b0b0b0);
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
ASSERT_FALSE(finished);
|
||||
ASSERT_EQ(0x1000U, regs.sp());
|
||||
ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
|
||||
ASSERT_EQ(0x20000U, regs.pc());
|
||||
ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
|
||||
|
||||
// Load bias is non-zero.
|
||||
ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, ®s, &process_memory_, &finished));
|
||||
|
||||
// Pc too small.
|
||||
ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, ®s, &process_memory_, &finished));
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
|
||||
|
@ -349,7 +355,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
|
|||
|
||||
// Everything should pass.
|
||||
bool finished;
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
ASSERT_FALSE(finished);
|
||||
ASSERT_EQ(0x10004U, regs.sp());
|
||||
ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
|
||||
|
@ -372,7 +378,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) {
|
|||
regs.set_pc(0x1234);
|
||||
|
||||
bool finished;
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(finished);
|
||||
ASSERT_EQ(0x10000U, regs.sp());
|
||||
ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
|
||||
|
@ -394,7 +400,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) {
|
|||
regs.set_pc(0x1234);
|
||||
|
||||
bool finished;
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(finished);
|
||||
ASSERT_EQ(0x10000U, regs.sp());
|
||||
ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
|
||||
|
@ -420,7 +426,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) {
|
|||
regs.set_pc(0x1234);
|
||||
|
||||
bool finished;
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(finished);
|
||||
ASSERT_EQ(0U, regs.pc());
|
||||
|
||||
|
@ -432,7 +438,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) {
|
|||
regs.set_sp(regs[ARM_REG_SP]);
|
||||
regs.set_pc(0x1234);
|
||||
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(finished);
|
||||
ASSERT_EQ(0U, regs.pc());
|
||||
}
|
||||
|
|
|
@ -346,7 +346,7 @@ class ElfInterfaceMock : public ElfInterface {
|
|||
void InitHeaders() override {}
|
||||
bool GetSoname(std::string*) override { return false; }
|
||||
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
|
||||
MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
|
||||
MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
|
||||
};
|
||||
|
||||
TEST_F(ElfTest, step_in_interface) {
|
||||
|
@ -361,7 +361,7 @@ TEST_F(ElfTest, step_in_interface) {
|
|||
MemoryFake process_memory;
|
||||
|
||||
bool finished;
|
||||
EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
|
||||
EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
|
@ -382,7 +382,7 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
|
|||
bool finished;
|
||||
ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
|
||||
EXPECT_CALL(*interface, Step(0x3300, ®s, &process_memory, &finished))
|
||||
EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
|
||||
|
|
|
@ -68,12 +68,23 @@ TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
|
|||
EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
|
||||
}
|
||||
|
||||
TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) {
|
||||
map_info_->elf = elf_container_.release();
|
||||
|
||||
elf_->FakeSetLoadBias(0);
|
||||
EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
|
||||
|
||||
elf_->FakeSetLoadBias(0x1000);
|
||||
EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
|
||||
}
|
||||
|
||||
TEST_F(MapInfoGetLoadBiasTest, elf_exists) {
|
||||
map_info_->elf = elf_container_.release();
|
||||
|
||||
elf_->FakeSetLoadBias(0);
|
||||
EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
|
||||
|
||||
map_info_->load_bias = static_cast<uint64_t>(-1);
|
||||
elf_->FakeSetLoadBias(0x1000);
|
||||
EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
|
||||
}
|
||||
|
@ -141,6 +152,15 @@ TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory) {
|
|||
EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
|
||||
}
|
||||
|
||||
TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory_cached) {
|
||||
InitElfData(memory_, map_info_->start);
|
||||
|
||||
EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
|
||||
|
||||
memory_->Clear();
|
||||
EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
|
||||
}
|
||||
|
||||
TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists_in_memory) {
|
||||
InitElfData(memory_, map_info_->start);
|
||||
|
||||
|
|
|
@ -44,6 +44,24 @@ static void VerifyLine(std::string line, MapInfo* info) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(MapsTest, map_add) {
|
||||
Maps maps;
|
||||
|
||||
maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
|
||||
maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
|
||||
maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
|
||||
|
||||
ASSERT_EQ(3U, maps.Total());
|
||||
MapInfo* info = maps.Get(0);
|
||||
ASSERT_EQ(0x1000U, info->start);
|
||||
ASSERT_EQ(0x2000U, info->end);
|
||||
ASSERT_EQ(0U, info->offset);
|
||||
ASSERT_EQ(PROT_READ, info->flags);
|
||||
ASSERT_EQ("fake_map", info->name);
|
||||
ASSERT_EQ(0U, info->elf_offset);
|
||||
ASSERT_EQ(0U, info->load_bias.load());
|
||||
}
|
||||
|
||||
TEST(MapsTest, verify_parse_line) {
|
||||
MapInfo info;
|
||||
|
||||
|
|
|
@ -96,6 +96,54 @@ TEST(UnwindOfflineTest, pc_straddle_arm32) {
|
|||
frame_info);
|
||||
}
|
||||
|
||||
TEST(UnwindOfflineTest, pc_in_gnu_debugdata_arm32) {
|
||||
std::string dir(TestGetFileDirectory() + "offline/gnu_debugdata_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;
|
||||
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(ARCH_ARM, regs.Arch());
|
||||
|
||||
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(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
|
||||
EXPECT_EQ(
|
||||
" #00 pc 0006dc49 libandroid_runtime.so "
|
||||
"(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n"
|
||||
" #01 pc 0006dce5 libandroid_runtime.so "
|
||||
"(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
|
||||
frame_info);
|
||||
}
|
||||
|
||||
TEST(UnwindOfflineTest, pc_straddle_arm64) {
|
||||
std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
|
||||
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
f1f10000-f2049000 r-xp 00000000 00:00 0 libandroid_runtime.so
|
|
@ -0,0 +1,2 @@
|
|||
pc: f1f6dc49
|
||||
sp: d8fe6930
|
Binary file not shown.
|
@ -87,7 +87,7 @@ void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t l
|
|||
for (const DwarfFde* fde : *section) {
|
||||
// Sometimes there are entries that have empty length, skip those since
|
||||
// they don't contain any interesting information.
|
||||
if (fde->pc_start == fde->pc_end) {
|
||||
if (fde == nullptr || fde->pc_start == fde->pc_end) {
|
||||
continue;
|
||||
}
|
||||
printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias);
|
||||
|
|
Loading…
Reference in New Issue