Merge "Add support for displaying soname in an apk."

This commit is contained in:
Christopher Ferris 2019-03-14 15:40:59 +00:00 committed by Gerrit Code Review
commit 4886a5bd59
18 changed files with 169 additions and 80 deletions

View file

@ -93,9 +93,12 @@ void Elf::Invalidate() {
valid_ = false;
}
bool Elf::GetSoname(std::string* name) {
std::string Elf::GetSoname() {
std::lock_guard<std::mutex> guard(lock_);
return valid_ && interface_->GetSoname(name);
if (!valid_) {
return "";
}
return interface_->GetSoname();
}
uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {

View file

@ -374,13 +374,12 @@ void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
}
template <typename DynType>
bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
std::string ElfInterface::GetSonameWithTemplate() {
if (soname_type_ == SONAME_INVALID) {
return false;
return "";
}
if (soname_type_ == SONAME_VALID) {
*soname = soname_;
return true;
return soname_;
}
soname_type_ = SONAME_INVALID;
@ -397,7 +396,7 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
last_error_.code = ERROR_MEMORY_INVALID;
last_error_.address = offset;
return false;
return "";
}
if (dyn.d_tag == DT_STRTAB) {
@ -416,17 +415,16 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
if (entry.first == strtab_addr) {
soname_offset = entry.second + soname_offset;
if (soname_offset >= entry.second + strtab_size) {
return false;
return "";
}
if (!memory_->ReadString(soname_offset, &soname_)) {
return false;
return "";
}
soname_type_ = SONAME_VALID;
*soname = soname_;
return true;
return soname_;
}
}
return false;
return "";
}
template <typename SymType>
@ -653,8 +651,8 @@ template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf
template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
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*);

View file

@ -188,44 +188,57 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
}
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_);
{
// 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();
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();
}
}
Memory* memory = CreateMemory(process_memory);
if (locked) {
if (Elf::CacheAfterCreateMemory(this)) {
delete memory;
Elf::CacheUnlock();
return elf.get();
// 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.
if (prev_map != nullptr && elf_start_offset != offset && prev_map->offset == elf_start_offset &&
prev_map->name == name) {
std::lock_guard<std::mutex> guard(prev_map->mutex_);
if (prev_map->elf.get() == nullptr) {
prev_map->elf = elf;
}
}
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();
}
return elf.get();
}

View file

@ -105,6 +105,12 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_
if (resolve_names_) {
frame->map_name = map_info->name;
if (embedded_soname_ && map_info->elf_start_offset != 0 && !frame->map_name.empty()) {
std::string soname = elf->GetSoname();
if (!soname.empty()) {
frame->map_name += '!' + soname;
}
}
}
frame->map_elf_start_offset = map_info->elf_start_offset;
frame->map_exact_offset = map_info->offset;

View file

@ -59,7 +59,7 @@ class Elf {
void Invalidate();
bool GetSoname(std::string* name);
std::string GetSoname();
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);

View file

@ -56,7 +56,7 @@ class ElfInterface {
virtual void InitHeaders(uint64_t load_bias) = 0;
virtual bool GetSoname(std::string* name) = 0;
virtual std::string GetSoname() = 0;
virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
@ -117,7 +117,7 @@ class ElfInterface {
void ReadSectionHeaders(const EhdrType& ehdr);
template <typename DynType>
bool GetSonameWithTemplate(std::string* soname);
std::string GetSonameWithTemplate();
template <typename SymType>
bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
@ -183,9 +183,7 @@ class ElfInterface32 : public ElfInterface {
ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
}
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
}
std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
@ -215,9 +213,7 @@ class ElfInterface64 : public ElfInterface {
ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
}
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
}
std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);

View file

@ -100,6 +100,11 @@ class Unwinder {
// set to an empty string and the function offset being set to zero.
void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
// Enable/disable soname printing the soname for a map name if the elf is
// embedded in a file. This is enabled by default.
// NOTE: This does nothing unless resolving names is enabled.
void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
#if !defined(NO_LIBDEXFILE_SUPPORT)
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
#endif
@ -124,6 +129,7 @@ class Unwinder {
DexFiles* dex_files_ = nullptr;
#endif
bool resolve_names_ = true;
bool embedded_soname_ = true;
ErrorData last_error_;
};

View file

@ -68,7 +68,7 @@ class ElfInterfaceFake : public ElfInterface {
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
bool GetSoname(std::string*) override { return false; }
std::string GetSoname() override { return fake_soname_; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
bool GetGlobalVariable(const std::string&, uint64_t*) override;
@ -83,6 +83,8 @@ class ElfInterfaceFake : public ElfInterface {
void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
void FakeSetSoname(const char* soname) { fake_soname_ = soname; }
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
@ -98,6 +100,7 @@ class ElfInterfaceFake : public ElfInterface {
private:
std::unordered_map<std::string, uint64_t> globals_;
std::string fake_build_id_;
std::string fake_soname_;
static std::deque<FunctionData> functions_;
static std::deque<StepData> steps_;

View file

@ -555,9 +555,7 @@ void ElfInterfaceTest::Soname() {
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
std::string name;
ASSERT_TRUE(elf->GetSoname(&name));
ASSERT_STREQ("fake_soname.so", name.c_str());
ASSERT_EQ("fake_soname.so", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname) {
@ -578,8 +576,7 @@ void ElfInterfaceTest::SonameAfterDtNull() {
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
std::string name;
ASSERT_FALSE(elf->GetSoname(&name));
ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
@ -600,8 +597,7 @@ void ElfInterfaceTest::SonameSize() {
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
std::string name;
ASSERT_FALSE(elf->GetSoname(&name));
ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_size) {
@ -624,8 +620,7 @@ void ElfInterfaceTest::SonameMissingMap() {
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
std::string name;
ASSERT_FALSE(elf->GetSoname(&name));
ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {

View file

@ -126,9 +126,9 @@ TEST_F(ElfTest, elf_invalid) {
ASSERT_FALSE(elf.valid());
ASSERT_TRUE(elf.interface() == nullptr);
std::string name;
ASSERT_FALSE(elf.GetSoname(&name));
ASSERT_EQ("", elf.GetSoname());
std::string name;
uint64_t func_offset;
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
@ -309,7 +309,7 @@ class ElfInterfaceMock : public ElfInterface {
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
bool GetSoname(std::string*) override { return false; }
std::string GetSoname() override { return ""; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
std::string GetBuildID() override { return ""; }

View file

@ -371,4 +371,35 @@ TEST_F(MapInfoGetElfTest, multiple_thread_get_elf) {
}
}
// Verify that previous maps don't automatically get the same elf object.
TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
}
// Verify that a read-only map followed by a read-execute map will result
// in the same elf object in both maps.
TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
}
} // namespace unwindstack

View file

@ -300,7 +300,7 @@ TEST_F(UnwindOfflineTest, jit_debug_x86) {
EXPECT_EQ(
" #00 pc 00068fb8 libarttestd.so (art::CauseSegfault()+72)\n"
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
" #02 pc 000021a8 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
" #02 pc 000021a8 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+136)\n"
" #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
@ -601,7 +601,7 @@ TEST_F(UnwindOfflineTest, jit_debug_arm) {
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
" #01 pc 0000212d 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
" #01 pc 0000212d 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
" #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
@ -1312,7 +1312,8 @@ TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
" #02 pc 00000000000008bc vdso.so\n"
" #03 pc 00000000000846f4 libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
" #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x4000) (ANGLEGetUtilityAPI+56)\n"
" #05 pc 000000000003d5b4 ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
"(ANGLEGetUtilityAPI+56)\n"
" #06 pc 000000000007fe68 libc.so (__libc_init)\n",
frame_info);

View file

@ -79,8 +79,13 @@ class UnwinderTest : public ::testing::Test {
AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
interface->FakeSetSoname("lib_fake.so");
elf->FakeSetInterface(interface);
AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
MapInfo* map_info = maps_->Find(0x43000);
ASSERT_TRUE(map_info != nullptr);
map_info->elf_start_offset = 0x1d000;
AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
@ -317,6 +322,36 @@ TEST_F(UnwinderTest, non_zero_map_offset) {
ASSERT_EQ(1U, unwinder.NumFrames());
auto* frame = &unwinder.frames()[0];
EXPECT_EQ(0U, frame->num);
EXPECT_EQ(0U, frame->rel_pc);
EXPECT_EQ(0x43000U, frame->pc);
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
}
TEST_F(UnwinderTest, disable_embedded_soname) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
regs_.set_pc(0x43000);
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.SetEmbeddedSoname(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
ASSERT_EQ(1U, unwinder.NumFrames());
auto* frame = &unwinder.frames()[0];
EXPECT_EQ(0U, frame->num);
EXPECT_EQ(0U, frame->rel_pc);
@ -325,7 +360,7 @@ TEST_F(UnwinderTest, non_zero_map_offset) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
@ -813,8 +848,8 @@ TEST_F(UnwinderTest, map_ignore_suffixes) {
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);

View file

@ -1,5 +1,6 @@
ab0d3000-ab0d8000 r-xp 0 00:00 0 dalvikvm32
dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so
e0445000-e0447000 r--p 0 00:00 0 137-cfi.odex
e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex
e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000
e648e000-e690f000 r-xp 0 00:00 0 libart.so

View file

@ -1,5 +1,6 @@
56573000-56577000 r-xp 0 00:00 0 dalvikvm32
eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
ec604000-ec606000 r--p 0 00:00 0 137-cfi.odex
ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
f6be1000-f732b000 r-xp 0 00:00 0 libartd.so

View file

@ -118,8 +118,8 @@ int GetElfInfo(const char* file, uint64_t offset) {
return 1;
}
std::string soname;
if (elf.GetSoname(&soname)) {
std::string soname(elf.GetSoname());
if (!soname.empty()) {
printf("Soname: %s\n", soname.c_str());
}

View file

@ -185,8 +185,8 @@ int GetInfo(const char* file, uint64_t pc) {
return 1;
}
std::string soname;
if (elf.GetSoname(&soname)) {
std::string soname(elf.GetSoname());
if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}

View file

@ -71,8 +71,8 @@ int main(int argc, char** argv) {
return 1;
}
std::string soname;
if (elf.GetSoname(&soname)) {
std::string soname(elf.GetSoname());
if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}