Merge "Fix ARM program header values used for exidx."
This commit is contained in:
commit
fc1cf90741
|
@ -211,7 +211,7 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (HandleType(offset, phdr.p_type, *load_bias)) {
|
||||
if (HandleType(offset, phdr.p_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,20 +87,22 @@ bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) {
|
|||
#define PT_ARM_EXIDX 0x70000001
|
||||
#endif
|
||||
|
||||
bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) {
|
||||
bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
|
||||
if (type != PT_ARM_EXIDX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Elf32_Phdr phdr;
|
||||
if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
|
||||
if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
|
||||
return true;
|
||||
}
|
||||
if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
|
||||
return true;
|
||||
}
|
||||
start_offset_ = phdr.p_vaddr - load_bias;
|
||||
total_entries_ = phdr.p_memsz / 8;
|
||||
|
||||
// The offset already takes into account the load bias.
|
||||
start_offset_ = phdr.p_offset;
|
||||
|
||||
// Always use filesz instead of memsz. In most cases they are the same,
|
||||
// but some shared libraries wind up setting one correctly and not the other.
|
||||
total_entries_ = phdr.p_filesz / 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class ElfInterfaceArm : public ElfInterface32 {
|
|||
|
||||
bool FindEntry(uint32_t pc, uint64_t* entry_offset);
|
||||
|
||||
bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
|
||||
bool HandleType(uint64_t offset, uint32_t type) override;
|
||||
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ class ElfInterface {
|
|||
template <typename SymType>
|
||||
bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
|
||||
|
||||
virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
|
||||
virtual bool HandleType(uint64_t, uint32_t) { return false; }
|
||||
|
||||
template <typename EhdrType>
|
||||
static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
|
||||
|
|
|
@ -245,56 +245,41 @@ TEST_F(ElfInterfaceArmTest, iterate) {
|
|||
TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
|
||||
ElfInterfaceArmFake interface(&memory_);
|
||||
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK, 0));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME));
|
||||
ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK));
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
|
||||
ElfInterfaceArmFake interface(&memory_);
|
||||
|
||||
Elf32_Phdr phdr;
|
||||
Elf32_Phdr phdr = {};
|
||||
interface.FakeSetStartOffset(0x1000);
|
||||
interface.FakeSetTotalEntries(100);
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0xa00;
|
||||
phdr.p_offset = 0x2000;
|
||||
phdr.p_filesz = 0xa00;
|
||||
|
||||
// Verify that if reads fail, we don't set the values but still get true.
|
||||
ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
|
||||
ASSERT_EQ(0x1000U, interface.start_offset());
|
||||
ASSERT_EQ(100U, interface.total_entries());
|
||||
|
||||
// Verify that if the second read fails, we still don't set the values.
|
||||
memory_.SetData32(
|
||||
0x1000 + reinterpret_cast<uint64_t>(&phdr.p_vaddr) - reinterpret_cast<uint64_t>(&phdr),
|
||||
phdr.p_vaddr);
|
||||
ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
|
||||
ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
|
||||
ASSERT_EQ(0x1000U, interface.start_offset());
|
||||
ASSERT_EQ(100U, interface.total_entries());
|
||||
|
||||
// Everything is correct and present.
|
||||
memory_.SetData32(
|
||||
0x1000 + reinterpret_cast<uint64_t>(&phdr.p_memsz) - reinterpret_cast<uint64_t>(&phdr),
|
||||
phdr.p_memsz);
|
||||
ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
|
||||
memory_.SetMemory(0x1000, &phdr, sizeof(phdr));
|
||||
ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
|
||||
ASSERT_EQ(0x2000U, interface.start_offset());
|
||||
ASSERT_EQ(320U, interface.total_entries());
|
||||
|
||||
// Non-zero load bias.
|
||||
ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0x1000));
|
||||
ASSERT_EQ(0x1000U, interface.start_offset());
|
||||
ASSERT_EQ(320U, interface.total_entries());
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceArmTest, StepExidx) {
|
||||
|
|
|
@ -116,8 +116,7 @@ class ElfInterfaceTest : public ::testing::Test {
|
|||
template <typename Sym>
|
||||
void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
|
||||
uint64_t sym_offset, const char* name) {
|
||||
Sym sym;
|
||||
memset(&sym, 0, sizeof(sym));
|
||||
Sym sym = {};
|
||||
sym.st_info = STT_FUNC;
|
||||
sym.st_value = value;
|
||||
sym.st_size = size;
|
||||
|
@ -132,15 +131,13 @@ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
|||
void ElfInterfaceTest::SinglePtLoad() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr 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 phdr = {};
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0x10000;
|
||||
|
@ -172,15 +169,13 @@ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
|||
void ElfInterfaceTest::MultipleExecutablePtLoads() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 3;
|
||||
ehdr.e_phentsize = sizeof(Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Phdr phdr = {};
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0x10000;
|
||||
|
@ -241,15 +236,13 @@ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
|||
void ElfInterfaceTest::MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 3;
|
||||
ehdr.e_phentsize = sizeof(Phdr) + 100;
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Phdr phdr = {};
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0x10000;
|
||||
|
@ -312,15 +305,13 @@ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
|||
void ElfInterfaceTest::NonExecutablePtLoads() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 3;
|
||||
ehdr.e_phentsize = sizeof(Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Phdr phdr = {};
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0x10000;
|
||||
|
@ -371,17 +362,15 @@ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
|
|||
void ElfInterfaceTest::ManyPhdrs() {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 7;
|
||||
ehdr.e_phentsize = sizeof(Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Phdr phdr;
|
||||
uint64_t phdr_offset = 0x100;
|
||||
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Phdr phdr = {};
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0x10000;
|
||||
|
@ -444,18 +433,16 @@ TEST_F(ElfInterfaceTest, elf64_many_phdrs) {
|
|||
TEST_F(ElfInterfaceTest, elf32_arm) {
|
||||
ElfInterfaceArm elf_arm(&memory_);
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Elf32_Ehdr ehdr = {};
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Elf32_Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Elf32_Phdr phdr = {};
|
||||
phdr.p_type = PT_ARM_EXIDX;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 16;
|
||||
phdr.p_offset = 0x2000;
|
||||
phdr.p_filesz = 16;
|
||||
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
|
||||
|
||||
// Add arm exidx entries.
|
||||
|
@ -480,8 +467,7 @@ TEST_F(ElfInterfaceTest, elf32_arm) {
|
|||
|
||||
template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
|
||||
void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_shoff = 0x200;
|
||||
ehdr.e_shnum = 2;
|
||||
ehdr.e_shentsize = sizeof(Shdr);
|
||||
|
@ -490,8 +476,7 @@ void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
|
|||
ehdr.e_phentsize = sizeof(Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
Shdr shdr = {};
|
||||
shdr.sh_type = SHT_STRTAB;
|
||||
if (test_type == SONAME_MISSING_MAP) {
|
||||
shdr.sh_addr = 0x20100;
|
||||
|
@ -501,8 +486,7 @@ void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
|
|||
shdr.sh_offset = 0x10000;
|
||||
memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
|
||||
|
||||
Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Phdr phdr = {};
|
||||
phdr.p_type = PT_DYNAMIC;
|
||||
phdr.p_offset = 0x2000;
|
||||
phdr.p_memsz = sizeof(Dyn) * 3;
|
||||
|
@ -748,8 +732,7 @@ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
|
|||
void ElfInterfaceTest::InitSectionHeadersMalformed() {
|
||||
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_shoff = 0x1000;
|
||||
ehdr.e_shnum = 10;
|
||||
ehdr.e_shentsize = sizeof(Shdr);
|
||||
|
@ -774,8 +757,7 @@ void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
|
|||
|
||||
uint64_t offset = 0x1000;
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_shoff = offset;
|
||||
ehdr.e_shnum = 10;
|
||||
ehdr.e_shentsize = entry_size;
|
||||
|
@ -783,8 +765,7 @@ void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
|
|||
|
||||
offset += ehdr.e_shentsize;
|
||||
|
||||
Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
Shdr shdr = {};
|
||||
shdr.sh_type = SHT_SYMTAB;
|
||||
shdr.sh_link = 4;
|
||||
shdr.sh_addr = 0x5000;
|
||||
|
@ -863,8 +844,7 @@ void ElfInterfaceTest::InitSectionHeadersOffsets() {
|
|||
|
||||
uint64_t offset = 0x2000;
|
||||
|
||||
Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Ehdr ehdr = {};
|
||||
ehdr.e_shoff = offset;
|
||||
ehdr.e_shnum = 10;
|
||||
ehdr.e_shentsize = sizeof(Shdr);
|
||||
|
@ -873,8 +853,7 @@ void ElfInterfaceTest::InitSectionHeadersOffsets() {
|
|||
|
||||
offset += ehdr.e_shentsize;
|
||||
|
||||
Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
Shdr shdr = {};
|
||||
shdr.sh_type = SHT_PROGBITS;
|
||||
shdr.sh_link = 2;
|
||||
shdr.sh_name = 0x200;
|
||||
|
@ -956,15 +935,13 @@ TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
|
|||
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Elf32_Ehdr ehdr = {};
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Elf32_Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Elf32_Phdr phdr = {};
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0;
|
||||
phdr.p_memsz = 0x10000;
|
||||
|
@ -984,15 +961,13 @@ TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
|
|||
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Elf32_Ehdr ehdr = {};
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Elf32_Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
Elf32_Phdr phdr = {};
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0x10000;
|
||||
|
@ -1017,16 +992,14 @@ TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) {
|
|||
|
||||
uint64_t sh_offset = 0x100;
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Elf32_Ehdr ehdr = {};
|
||||
ehdr.e_shstrndx = 1;
|
||||
ehdr.e_shoff = sh_offset;
|
||||
ehdr.e_shentsize = sizeof(Elf32_Shdr);
|
||||
ehdr.e_shnum = 3;
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
Elf32_Shdr shdr = {};
|
||||
shdr.sh_type = SHT_NULL;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
|
@ -1080,16 +1053,14 @@ TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) {
|
|||
|
||||
uint64_t sh_offset = 0x100;
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
Elf32_Ehdr ehdr = {};
|
||||
ehdr.e_shstrndx = 1;
|
||||
ehdr.e_shoff = sh_offset;
|
||||
ehdr.e_shentsize = sizeof(Elf32_Shdr);
|
||||
ehdr.e_shnum = 3;
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
Elf32_Shdr shdr = {};
|
||||
shdr.sh_type = SHT_NULL;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
|
|
Loading…
Reference in New Issue