diff --git a/CHANGELOG.md b/CHANGELOG.md index 4628885a4..c2b9d560a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * Use pre-existing code blocks as hints when disassembling a RAW binary. * Better data access computation for MIPS binaries. * Detect incremental linking regions in PE binaries. +* Create elfStackSize and elfStackExec auxdata from ELF PT_GNU_STACK segments. +* Requires gtirb >=1.12.1, gtirb-pprinter >=2.0.0 # 1.7.0 * Update code inference to use weighted interval scheduling to resolve blocks; diff --git a/CMakeLists.txt b/CMakeLists.txt index 103da0c12..8ae9a202d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,12 +222,12 @@ endif() # --------------------------------------------------------------------------- # gtirb # --------------------------------------------------------------------------- -find_package(gtirb 1.10.5 REQUIRED) +find_package(gtirb 1.12.1 REQUIRED) # --------------------------------------------------------------------------- # pretty-printer # --------------------------------------------------------------------------- -find_package(gtirb_pprinter 1.8.1 REQUIRED) +find_package(gtirb_pprinter 2.0.0 REQUIRED) # --------------------------------------------------------------------------- # libehp diff --git a/doc/source/GENERAL/2-Building-Ddisasm.md b/doc/source/GENERAL/2-Building-Ddisasm.md index 0142891e0..5922af5b1 100644 --- a/doc/source/GENERAL/2-Building-Ddisasm.md +++ b/doc/source/GENERAL/2-Building-Ddisasm.md @@ -8,8 +8,8 @@ that standard such as gcc 9, clang 6, or MSVC 2017. To build Ddisasm from source, the following requirements should be installed: -- [gtirb](https://github.com/grammatech/gtirb) -- [gtirb-pprinter](https://github.com/grammatech/gtirb-pprinter) +- [gtirb](https://github.com/grammatech/gtirb) version 1.12.1 or later +- [gtirb-pprinter](https://github.com/grammatech/gtirb-pprinter), version 2.0.0 or later - [Capstone](http://www.capstone-engine.org/), version 5.0.0 or later - GrammaTech builds and tests using the [GrammaTech/capstone](https://github.com/GrammaTech/capstone) fork. - [Souffle](https://souffle-lang.github.io), version 2.4 with support for 64 bit numbers (via `-DSOUFFLE_DOMAIN_64BIT=1` during configuration) diff --git a/src/Registration.cpp b/src/Registration.cpp index 00c66c795..b728f6add 100644 --- a/src/Registration.cpp +++ b/src/Registration.cpp @@ -79,6 +79,8 @@ void registerAuxDataTypes() gtirb::AuxDataContainer::registerAuxDataType(); gtirb::AuxDataContainer::registerAuxDataType(); gtirb::AuxDataContainer::registerAuxDataType(); + gtirb::AuxDataContainer::registerAuxDataType(); + gtirb::AuxDataContainer::registerAuxDataType(); } void registerDatalogLoaders() diff --git a/src/gtirb-builder/ElfReader.cpp b/src/gtirb-builder/ElfReader.cpp index 48e14b037..321269fc8 100644 --- a/src/gtirb-builder/ElfReader.cpp +++ b/src/gtirb-builder/ElfReader.cpp @@ -600,7 +600,6 @@ void ElfReader::buildSections() relocateSections(); } - bool GnuStackSectionExist = false; uint64_t Index = 0; for(auto &Section : Elf->sections()) { @@ -712,11 +711,6 @@ void ElfReader::buildSections() SectionProperties[S->getUUID()] = {static_cast(Section.type()), static_cast(Section.flags())}; - if(Section.name() == ".note.GNU-stack") - { - GnuStackSectionExist = true; - } - // In case of ARM32, inspect .ARM.attributes section to see // if the binary is Microcontroller. if(Module->getISA() == gtirb::ISA::ARM && Section.name() == ".ARM.attributes") @@ -730,53 +724,6 @@ void ElfReader::buildSections() Index++; } - // If .note.GNU-stack section does not exist and there is a segment - // type of PT_GNU_STACK, create an artificial section for it. - if(!GnuStackSectionExist) - { - auto GnuStackSegment = std::find_if( - Elf->segments().begin(), Elf->segments().end(), - [](auto &S) { return S.type() == LIEF::ELF::SEGMENT_TYPES::PT_GNU_STACK; }); - - if(GnuStackSegment != Elf->segments().end()) - { - gtirb::Section *S = Module->addSection(*Context, ".note.GNU-stack"); - S->addFlag(gtirb::SectionFlag::Loaded); - S->addFlag(gtirb::SectionFlag::Readable); - S->addFlag(gtirb::SectionFlag::Initialized); - uint64_t Addr = GnuStackSegment->virtual_address(); - // If Addr is 0, create an address after TLS. - if(Addr == 0) - { - uint64_t TlsSize = 0; - std::optional> TlsAddr = getTls(); - if(TlsAddr) - { - TlsSize = TlsAddr->second - TlsAddr->first; - } - Addr = tlsBaseAddress() + TlsSize; - // Use the next available page. - Addr = (Addr & ~(0x1000 - 1)) + 0x1000; - - SectionRelocations[S->getName()] = Addr; - } - - uint64_t Size = GnuStackSegment->virtual_size(); - auto Bytes = Elf->get_content_from_virtual_address(Addr, Size); - gtirb::ByteInterval *BI = S->addByteInterval(*Context, gtirb::Addr(Addr), Bytes.begin(), - Bytes.end(), Size, Bytes.size()); - auto DataBlock = gtirb::DataBlock::Create(*Context, Size); - BI->addBlock(0, DataBlock); - - uint64_t Type = static_cast(LIEF::ELF::ELF_SECTION_TYPES::SHT_PROGBITS); - uint64_t Flags = 0; - - Alignment[S->getUUID()] = 0; - SectionIndex[Index++] = S->getUUID(); - SectionProperties[S->getUUID()] = {Type, Flags}; - } - } - // Add `overlay` aux data table. if(auto Overlay = Elf->overlay(); Overlay.size() > 0) { @@ -1272,6 +1219,29 @@ void ElfReader::addAuxData() DynamicEntryTuples.insert({it->first, it->second}); } Module->addAuxData(std::move(DynamicEntryTuples)); + + // Build segment auxdata + bool FoundStackSegment = false; + for(auto &Segment : Elf->segments()) + { + switch(Segment.type()) + { + case LIEF::ELF::SEGMENT_TYPES::PT_GNU_STACK: + { + if(FoundStackSegment) + { + std::cerr << "\nWARNING: multiple PT_GNU_STACK segments\n"; + continue; + } + Module->addAuxData(Segment.virtual_size()); + Module->addAuxData( + Segment.has(LIEF::ELF::ELF_SEGMENT_FLAGS::PF_X)); + } + default: + // all other segments types are currently ignored. + continue; + } + } } std::string ElfReader::getRelocationType(const LIEF::ELF::Relocation &Entry) diff --git a/src/tests/ElfReader.Test.cpp b/src/tests/ElfReader.Test.cpp index e08cd5896..0e0f11f9a 100644 --- a/src/tests/ElfReader.Test.cpp +++ b/src/tests/ElfReader.Test.cpp @@ -63,8 +63,6 @@ TEST_P(ElfReaderTest, sections) for(const auto& Section : Module.sections()) { const std::string& Name = Section.getName(); - if(Name == ".note.GNU-stack") - continue; EXPECT_EQ(Names.count(Name), 1); EXPECT_EQ(Sizes[Name], Section.getSize()); EXPECT_EQ(Addresses[Name], static_cast(Section.getAddress().value())); diff --git a/src/tests/Main.Test.cpp b/src/tests/Main.Test.cpp index 55e93bc64..310bcdac7 100644 --- a/src/tests/Main.Test.cpp +++ b/src/tests/Main.Test.cpp @@ -29,6 +29,8 @@ void registerTestAuxDataTypes() gtirb::AuxDataContainer::registerAuxDataType(); gtirb::AuxDataContainer::registerAuxDataType(); gtirb::AuxDataContainer::registerAuxDataType(); + gtirb::AuxDataContainer::registerAuxDataType(); + gtirb::AuxDataContainer::registerAuxDataType(); } int main(int argc, char** argv) diff --git a/tests/misc_test.py b/tests/misc_test.py index 5f46a606c..866efe91f 100644 --- a/tests/misc_test.py +++ b/tests/misc_test.py @@ -405,6 +405,54 @@ def test_dynamic_init_fini(self): self.assertEqual(int(init_match.group(1), 16), init.address) self.assertEqual(int(fini_match.group(1), 16), fini.address) + @unittest.skipUnless( + platform.system() == "Linux", "This test is linux only." + ) + def test_stack_size(self): + """ + Test that a PT_GNU_STACK segment with size populates elfStackSize + """ + binary = "ex" + with cd(ex_dir / "ex1"): + stack_size = 0x200000 + self.assertTrue( + compile( + "gcc", "g++", "-O0", [f"-Wl,-z,stack-size={stack_size}"] + ) + ) + self.assertTrue(disassemble(binary, format="--ir")[0]) + + ir = gtirb.IR.load_protobuf(binary + ".gtirb") + m = ir.modules[0] + + self.assertEqual(m.aux_data["elfStackSize"].data, stack_size) + + @unittest.skipUnless( + platform.system() == "Linux", "This test is linux only." + ) + def test_stack_exec(self): + """ + Test that a PT_GNU_STACK segment populates correct executable flags + """ + cases = ( + ("execstack", True), + ("noexecstack", False), + ) + + for ld_keyword, is_exec in cases: + binary = "ex" + with self.subTest(keyword=ld_keyword), cd(ex_dir / "ex1"): + self.assertTrue( + compile("gcc", "g++", "-O0", [f"-Wl,-z,{ld_keyword}"]) + ) + self.assertTrue(disassemble(binary, format="--ir")[0]) + + ir = gtirb.IR.load_protobuf(binary + ".gtirb") + m = ir.modules[0] + + # verify executable bit + self.assertEqual(m.aux_data["elfStackExec"].data, is_exec) + class RawGtirbTests(unittest.TestCase): @unittest.skipUnless(