Fix soname reading code.
The dynamic section contained an address, not an offset into the elf file to indicate where the soname exists. Changed to use the strtab entries in the section headers to map this address to the actual offset. Refactor the soname test a bit to make it easier to verify the code. Bug: 73499044 Test: Passes new unit tests. Test: Ran unwind_info on the failing shared elf and verified the soinfo Test: is correct. Change-Id: I16ba148389bcb9aadd3566fb442dac27f89fe894
This commit is contained in:
parent
851803d3cf
commit
beae42bc7f
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <7zCrc.h>
|
||||
#include <Xz.h>
|
||||
|
@ -322,19 +323,13 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
|
|||
// 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_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
|
||||
if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address =
|
||||
offset + reinterpret_cast<uintptr_t>(&shdr.sh_type) - reinterpret_cast<uintptr_t>(&shdr);
|
||||
last_error_.address = offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
|
||||
if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = offset;
|
||||
return false;
|
||||
}
|
||||
// Need to go get the information about the section that contains
|
||||
// the string terminated names.
|
||||
ShdrType str_shdr;
|
||||
|
@ -343,39 +338,19 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
|
|||
return false;
|
||||
}
|
||||
uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
|
||||
if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
|
||||
if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_type) -
|
||||
reinterpret_cast<uintptr_t>(&str_shdr);
|
||||
last_error_.address = str_offset;
|
||||
return false;
|
||||
}
|
||||
if (str_shdr.sh_type != SHT_STRTAB) {
|
||||
last_error_.code = ERROR_UNWIND_INFO;
|
||||
return false;
|
||||
}
|
||||
if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
|
||||
sizeof(str_shdr.sh_offset))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_offset) -
|
||||
reinterpret_cast<uintptr_t>(&str_shdr);
|
||||
return false;
|
||||
}
|
||||
if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_size) -
|
||||
reinterpret_cast<uintptr_t>(&str_shdr);
|
||||
return false;
|
||||
}
|
||||
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 (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
|
||||
last_error_.code = ERROR_MEMORY_INVALID;
|
||||
last_error_.address = offset + reinterpret_cast<uintptr_t>(&shdr.sh_name) -
|
||||
reinterpret_cast<uintptr_t>(&shdr);
|
||||
return false;
|
||||
}
|
||||
if (shdr.sh_name < sec_size) {
|
||||
std::string name;
|
||||
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
|
||||
|
@ -394,14 +369,16 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
|
|||
offset_ptr = &eh_frame_hdr_offset_;
|
||||
size_ptr = &eh_frame_hdr_size_;
|
||||
}
|
||||
if (offset_ptr != nullptr &&
|
||||
memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
|
||||
memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
|
||||
if (offset_ptr != nullptr) {
|
||||
*offset_ptr = shdr.sh_offset;
|
||||
*size_ptr = shdr.sh_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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)));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -420,7 +397,7 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
|
|||
soname_type_ = SONAME_INVALID;
|
||||
|
||||
uint64_t soname_offset = 0;
|
||||
uint64_t strtab_offset = 0;
|
||||
uint64_t strtab_addr = 0;
|
||||
uint64_t strtab_size = 0;
|
||||
|
||||
// Find the soname location from the dynamic headers section.
|
||||
|
@ -435,7 +412,7 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
|
|||
}
|
||||
|
||||
if (dyn.d_tag == DT_STRTAB) {
|
||||
strtab_offset = dyn.d_un.d_ptr;
|
||||
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) {
|
||||
|
@ -445,16 +422,22 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
|
|||
}
|
||||
}
|
||||
|
||||
soname_offset += strtab_offset;
|
||||
if (soname_offset >= strtab_offset + strtab_size) {
|
||||
return false;
|
||||
// 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;
|
||||
if (soname_offset >= entry.second + strtab_size) {
|
||||
return false;
|
||||
}
|
||||
if (!memory_->ReadString(soname_offset, &soname_)) {
|
||||
return false;
|
||||
}
|
||||
soname_type_ = SONAME_VALID;
|
||||
*soname = soname_;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!memory_->ReadString(soname_offset, &soname_)) {
|
||||
return false;
|
||||
}
|
||||
soname_type_ = SONAME_VALID;
|
||||
*soname = soname_;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename SymType>
|
||||
|
|
|
@ -157,6 +157,7 @@ class ElfInterface {
|
|||
ElfInterface* gnu_debugdata_interface_ = nullptr;
|
||||
|
||||
std::vector<Symbols*> symbols_;
|
||||
std::vector<std::pair<uint64_t, uint64_t>> strtabs_;
|
||||
};
|
||||
|
||||
class ElfInterface32 : public ElfInterface {
|
||||
|
|
|
@ -63,15 +63,28 @@ class ElfInterfaceTest : public ::testing::Test {
|
|||
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
||||
void ManyPhdrs();
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
||||
enum SonameTestEnum : uint8_t {
|
||||
SONAME_NORMAL,
|
||||
SONAME_DTNULL_AFTER,
|
||||
SONAME_DTSIZE_SMALL,
|
||||
SONAME_MISSING_MAP,
|
||||
};
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
|
||||
void SonameInit(SonameTestEnum test_type = SONAME_NORMAL);
|
||||
|
||||
template <typename ElfInterfaceType>
|
||||
void Soname();
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
||||
template <typename ElfInterfaceType>
|
||||
void SonameAfterDtNull();
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
||||
template <typename ElfInterfaceType>
|
||||
void SonameSize();
|
||||
|
||||
template <typename ElfInterfaceType>
|
||||
void SonameMissingMap();
|
||||
|
||||
template <typename ElfType>
|
||||
void InitHeadersEhFrameTest();
|
||||
|
||||
|
@ -465,17 +478,29 @@ TEST_F(ElfInterfaceTest, elf32_arm) {
|
|||
ASSERT_EQ(2U, elf_arm.total_entries());
|
||||
}
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
||||
void ElfInterfaceTest::Soname() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
|
||||
void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
ehdr.e_shoff = 0x200;
|
||||
ehdr.e_shnum = 2;
|
||||
ehdr.e_shentsize = sizeof(Shdr);
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_STRTAB;
|
||||
if (test_type == SONAME_MISSING_MAP) {
|
||||
shdr.sh_addr = 0x20100;
|
||||
} else {
|
||||
shdr.sh_addr = 0x10100;
|
||||
}
|
||||
shdr.sh_offset = 0x10000;
|
||||
memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
|
||||
|
||||
Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
phdr.p_type = PT_DYNAMIC;
|
||||
|
@ -487,15 +512,25 @@ void ElfInterfaceTest::Soname() {
|
|||
Dyn dyn;
|
||||
|
||||
dyn.d_tag = DT_STRTAB;
|
||||
dyn.d_un.d_ptr = 0x10000;
|
||||
dyn.d_un.d_ptr = 0x10100;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
dyn.d_tag = DT_STRSZ;
|
||||
dyn.d_un.d_val = 0x1000;
|
||||
if (test_type == SONAME_DTSIZE_SMALL) {
|
||||
dyn.d_un.d_val = 0x10;
|
||||
} else {
|
||||
dyn.d_un.d_val = 0x1000;
|
||||
}
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
if (test_type == SONAME_DTNULL_AFTER) {
|
||||
dyn.d_tag = DT_NULL;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
}
|
||||
|
||||
dyn.d_tag = DT_SONAME;
|
||||
dyn.d_un.d_val = 0x10;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
|
@ -505,6 +540,11 @@ void ElfInterfaceTest::Soname() {
|
|||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
|
||||
SetStringMemory(0x10010, "fake_soname.so");
|
||||
}
|
||||
|
||||
template <typename ElfInterfaceType>
|
||||
void ElfInterfaceTest::Soname() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
|
@ -516,55 +556,19 @@ void ElfInterfaceTest::Soname() {
|
|||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf32_soname) {
|
||||
Soname<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
|
||||
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
|
||||
Soname<ElfInterface32>();
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf64_soname) {
|
||||
Soname<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
|
||||
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
|
||||
Soname<ElfInterface64>();
|
||||
}
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
||||
template <typename ElfInterfaceType>
|
||||
void ElfInterfaceTest::SonameAfterDtNull() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
phdr.p_type = PT_DYNAMIC;
|
||||
phdr.p_offset = 0x2000;
|
||||
phdr.p_memsz = sizeof(Dyn) * 3;
|
||||
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
|
||||
|
||||
Dyn dyn;
|
||||
uint64_t offset = 0x2000;
|
||||
|
||||
dyn.d_tag = DT_STRTAB;
|
||||
dyn.d_un.d_ptr = 0x10000;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
dyn.d_tag = DT_STRSZ;
|
||||
dyn.d_un.d_val = 0x1000;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
dyn.d_tag = DT_NULL;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
dyn.d_tag = DT_SONAME;
|
||||
dyn.d_un.d_val = 0x10;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
SetStringMemory(0x10010, "fake_soname.so");
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
EXPECT_EQ(0U, load_bias);
|
||||
|
@ -574,54 +578,19 @@ void ElfInterfaceTest::SonameAfterDtNull() {
|
|||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
|
||||
SonameAfterDtNull<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
|
||||
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
|
||||
SonameAfterDtNull<ElfInterface32>();
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) {
|
||||
SonameAfterDtNull<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
|
||||
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
|
||||
SonameAfterDtNull<ElfInterface64>();
|
||||
}
|
||||
|
||||
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
||||
template <typename ElfInterfaceType>
|
||||
void ElfInterfaceTest::SonameSize() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
phdr.p_type = PT_DYNAMIC;
|
||||
phdr.p_offset = 0x2000;
|
||||
phdr.p_memsz = sizeof(Dyn);
|
||||
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
|
||||
|
||||
Dyn dyn;
|
||||
uint64_t offset = 0x2000;
|
||||
|
||||
dyn.d_tag = DT_STRTAB;
|
||||
dyn.d_un.d_ptr = 0x10000;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
dyn.d_tag = DT_STRSZ;
|
||||
dyn.d_un.d_val = 0x10;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
dyn.d_tag = DT_SONAME;
|
||||
dyn.d_un.d_val = 0x10;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
offset += sizeof(dyn);
|
||||
|
||||
dyn.d_tag = DT_NULL;
|
||||
memory_.SetMemory(offset, &dyn, sizeof(dyn));
|
||||
|
||||
SetStringMemory(0x10010, "fake_soname.so");
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
EXPECT_EQ(0U, load_bias);
|
||||
|
@ -631,11 +600,37 @@ void ElfInterfaceTest::SonameSize() {
|
|||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf32_soname_size) {
|
||||
SonameSize<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
|
||||
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
|
||||
SonameSize<ElfInterface32>();
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf64_soname_size) {
|
||||
SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
|
||||
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
|
||||
SonameSize<ElfInterface64>();
|
||||
}
|
||||
|
||||
// Verify that there is no map from STRTAB in the dynamic section to a
|
||||
// STRTAB entry in the section headers.
|
||||
template <typename ElfInterfaceType>
|
||||
void ElfInterfaceTest::SonameMissingMap() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
EXPECT_EQ(0U, load_bias);
|
||||
|
||||
std::string name;
|
||||
ASSERT_FALSE(elf->GetSoname(&name));
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
|
||||
SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
|
||||
SonameMissingMap<ElfInterface32>();
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, elf64_soname_missing_map) {
|
||||
SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
|
||||
SonameMissingMap<ElfInterface64>();
|
||||
}
|
||||
|
||||
template <typename ElfType>
|
||||
|
|
|
@ -120,6 +120,11 @@ int GetElfInfo(const char* file, uint64_t offset) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
std::string soname;
|
||||
if (elf.GetSoname(&soname)) {
|
||||
printf("Soname: %s\n", soname.c_str());
|
||||
}
|
||||
|
||||
ElfInterface* interface = elf.interface();
|
||||
if (elf.machine_type() == EM_ARM) {
|
||||
DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
|
||||
|
|
|
@ -157,6 +157,11 @@ int GetInfo(const char* file, uint64_t pc) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
std::string soname;
|
||||
if (elf.GetSoname(&soname)) {
|
||||
printf("Soname: %s\n\n", soname.c_str());
|
||||
}
|
||||
|
||||
printf("PC 0x%" PRIx64 ":\n", pc);
|
||||
|
||||
DwarfSection* section = interface->eh_frame();
|
||||
|
|
|
@ -71,6 +71,11 @@ int main(int argc, char** argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
std::string soname;
|
||||
if (elf.GetSoname(&soname)) {
|
||||
printf("Soname: %s\n\n", soname.c_str());
|
||||
}
|
||||
|
||||
switch (elf.machine_type()) {
|
||||
case EM_ARM:
|
||||
printf("ABI: arm\n");
|
||||
|
|
Loading…
Reference in New Issue