diff --git a/CMakeLists.txt b/CMakeLists.txt index 345ded9..d658666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,12 +25,14 @@ find_package(Boost) OPTION(dcc_build_tests "Enable unit tests." OFF) IF(dcc_build_tests) -enable_testing() - FIND_PACKAGE(GMock) + enable_testing() + find_package(Qt5Test) + #FIND_PACKAGE(GMock) ENDIF() INCLUDE_DIRECTORIES( + ${PROJECT_SOURCE_DIR} 3rd_party/libdisasm include include/idioms @@ -38,135 +40,7 @@ INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIRS} ) - ADD_SUBDIRECTORY(3rd_party) ADD_SUBDIRECTORY(common) ADD_SUBDIRECTORY(tools) - - -set(dcc_LIB_SOURCES - src/CallConvention.cpp - src/ast.cpp - src/backend.cpp - src/bundle.cpp - src/chklib.cpp - src/comwrite.cpp - src/control.cpp - src/dataflow.cpp - src/disassem.cpp - src/DccFrontend.cpp - src/error.cpp - src/fixwild.cpp - src/graph.cpp - src/hlicode.cpp - src/hltype.cpp - src/machine_x86.cpp - src/icode.cpp - src/RegisterNode - src/idioms.cpp - src/idioms/idiom1.cpp - src/idioms/arith_idioms.cpp - src/idioms/call_idioms.cpp - src/idioms/epilogue_idioms.cpp - src/idioms/mov_idioms.cpp - src/idioms/neg_idioms.cpp - src/idioms/shift_idioms.cpp - src/idioms/xor_idioms.cpp - src/locident.cpp - src/liveness_set.cpp - src/parser.h - src/parser.cpp - src/procs.cpp - src/project.cpp - src/Procedure.cpp - src/proplong.cpp - src/reducible.cpp - src/scanner.cpp - src/symtab.cpp - src/udm.cpp - src/BasicBlock.cpp - src/dcc_interface.cpp - - src/Command.cpp - src/Command.h - src/Loaders.cpp - src/Loaders.h - src/FollowControlFlow.cpp - src/FollowControlFlow.h - - src/AutomatedPlanner -) -set(dcc_UI_SOURCES - src/ui/DccMainWindow.ui - src/ui/DccMainWindow.h - src/ui/DccMainWindow.cpp - src/ui/FunctionViewWidget.ui - src/ui/FunctionViewWidget.h - src/ui/FunctionViewWidget.cpp - src/ui/FunctionListDockWidget.ui - src/ui/FunctionListDockWidget.cpp - src/ui/FunctionListDockWidget.h - src/ui/RenderTags.cpp - src/ui/RenderTags.h - src/ui/CommandQueueView.cpp - src/ui/CommandQueueView.h - src/ui/CommandQueueView.ui -) -set(dcc_SOURCES - src/dcc.cpp -) -set(dcc_HEADERS - include/ast.h - include/bundle.h - include/BinaryImage.h - include/DccFrontend.h - include/Enums.h - include/dcc.h - include/disassem.h - include/dosdcc.h - include/error.h - include/graph.h - include/hlicode.h - include/machine_x86.h - include/icode.h - include/idioms/idiom.h - include/idioms/idiom1.h - include/idioms/arith_idioms.h - include/idioms/call_idioms.h - include/idioms/epilogue_idioms.h - include/idioms/mov_idioms.h - include/idioms/neg_idioms.h - include/idioms/shift_idioms.h - include/idioms/xor_idioms.h - include/locident.h - include/CallConvention.h - include/project.h - include/scanner.h - include/state.h - include/symtab.h - include/types.h - include/Procedure.h - include/StackFrame.h - include/BasicBlock.h - include/dcc_interface.h - -) - -SOURCE_GROUP(Source FILES ${dcc_SOURCES}) -SOURCE_GROUP(Headers FILES ${dcc_HEADERS}) - -ADD_LIBRARY(dcc_lib STATIC ${dcc_LIB_SOURCES} ${dcc_HEADERS}) -qt5_use_modules(dcc_lib Core) -#cotire(dcc_lib) - -ADD_EXECUTABLE(dcc_original ${dcc_SOURCES} ${dcc_UI_SOURCES}) -ADD_DEPENDENCIES(dcc_original dcc_lib) -TARGET_LINK_LIBRARIES(dcc_original dcc_lib dcc_hash disasm_s) -qt5_use_modules(dcc_original Core Widgets) -SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD 11) -SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD_REQUIRED ON) -#ADD_SUBDIRECTORY(gui) -if(dcc_build_tests) ADD_SUBDIRECTORY(src) -endif() - diff --git a/CMakeScripts/DCC_Macros.cmake b/CMakeScripts/DCC_Macros.cmake new file mode 100644 index 0000000..329c941 --- /dev/null +++ b/CMakeScripts/DCC_Macros.cmake @@ -0,0 +1,21 @@ +MACRO(ADD_UNIT_TEST name) + IF(NOT ${name}_TEST_VISITED) + # add the loader as a dll + ADD_EXECUTABLE(${name} ${ARGN}) + qt5_use_modules(${name} Core) + MESSAGE(WARNING "Adding test " ${name} " " ${ARGN}) + TARGET_LINK_LIBRARIES(${name} ${UNIT_TEST_LIBS}) + ADD_TEST(NAME ${name} COMMAND ${name}) + set_property(TEST ${name} APPEND PROPERTY ENVIRONMENT DCC_TEST_BASE=${PROJECT_SOURCE_DIR}) + SET(${name}_TEST_VISITED true) + ENDIF() +ENDMACRO() + +function(ADD_QTEST NAME) + add_executable(${NAME} ${NAME}.cpp ${NAME}.h) #${PROTO_SRCS} ${PROTO_HDRS} + target_link_libraries(${NAME} ${test_LIBRARIES}) + qt5_use_modules(${NAME} Core Test) + + add_test( NAME ${NAME} COMMAND $) + set_property(TEST ${NAME} APPEND PROPERTY ENVIRONMENT DCC_TEST_BASE=${PROJECT_SOURCE_DIR}) +endfunction() diff --git a/src/Address.h b/src/Address.h new file mode 100644 index 0000000..177b9ac --- /dev/null +++ b/src/Address.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +typedef uint32_t LinearAddress; +#define INVALID_ADDR Address(~0U) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index be4fec8..6234aff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,13 +1,131 @@ -SET(dcc_test_SOURCES - tests/comwrite.cpp - tests/project.cpp - tests/loader.cpp +set(dcc_LIB_SOURCES + CallConvention.cpp + ast.cpp + backend.cpp + bundle.cpp + chklib.cpp + comwrite.cpp + control.cpp + dataflow.cpp + disassem.cpp + DccFrontend.cpp + error.cpp + fixwild.cpp + graph.cpp + hlicode.cpp + hltype.cpp + machine_x86.cpp + icode.cpp + RegisterNode + idioms.cpp + idioms/idiom1.cpp + idioms/arith_idioms.cpp + idioms/call_idioms.cpp + idioms/epilogue_idioms.cpp + idioms/mov_idioms.cpp + idioms/neg_idioms.cpp + idioms/shift_idioms.cpp + idioms/xor_idioms.cpp + locident.cpp + liveness_set.cpp + parser.h + parser.cpp + procs.cpp + project.cpp + Procedure.cpp + proplong.cpp + reducible.cpp + scanner.cpp + symtab.cpp + udm.cpp + BasicBlock.cpp + dcc_interface.cpp + MemoryChunk + MemorySegment + MemorySegmentCoordinator + + Command.cpp + Command.h + Loaders.cpp + Loaders.h + FollowControlFlow.cpp + FollowControlFlow.h + + AutomatedPlanner +) +set(dcc_UI_SOURCES + ui/DccMainWindow.ui + ui/DccMainWindow.h + ui/DccMainWindow.cpp + ui/FunctionViewWidget.ui + ui/FunctionViewWidget.h + ui/FunctionViewWidget.cpp + ui/FunctionListDockWidget.ui + ui/FunctionListDockWidget.cpp + ui/FunctionListDockWidget.h + ui/RenderTags.cpp + ui/RenderTags.h + ui/CommandQueueView.cpp + ui/CommandQueueView.h + ui/CommandQueueView.ui +) +set(dcc_HEADERS + ../include/ast.h + ../include/bundle.h + ../include/BinaryImage.h + ../include/DccFrontend.h + ../include/Enums.h + ../include/dcc.h + ../include/disassem.h + ../include/dosdcc.h + ../include/error.h + ../include/graph.h + ../include/hlicode.h + ../include/machine_x86.h + ../include/icode.h + ../include/idioms/idiom.h + ../include/idioms/idiom1.h + ../include/idioms/arith_idioms.h + ../include/idioms/call_idioms.h + ../include/idioms/epilogue_idioms.h + ../include/idioms/mov_idioms.h + ../include/idioms/neg_idioms.h + ../include/idioms/shift_idioms.h + ../include/idioms/xor_idioms.h + ../include/locident.h + ../include/CallConvention.h + ../include/project.h + ../include/scanner.h + ../include/state.h + ../include/symtab.h + ../include/types.h + ../include/Procedure.h + ../include/StackFrame.h + ../include/BasicBlock.h + ../include/dcc_interface.h ) -include_directories(${GMOCK_INCLUDE_DIRS} ${GMOCK_ROOT}/gtest/include) -add_executable(tester ${dcc_test_SOURCES}) -ADD_DEPENDENCIES(tester dcc_lib) -target_link_libraries(tester dcc_lib disasm_s - ${GMOCK_BOTH_LIBRARIES} ${REQ_LLVM_LIBRARIES}) -add_test(dcc-tests tester) +SOURCE_GROUP(Headers FILES ${dcc_HEADERS}) + +set(dcc_SOURCES + dcc.cpp +) + +SOURCE_GROUP(Source FILES ${dcc_SOURCES} ${dcc_LIB_SOURCES}) + +ADD_LIBRARY(dcc_lib STATIC ${dcc_LIB_SOURCES} ${dcc_HEADERS}) +qt5_use_modules(dcc_lib Core) + +ADD_EXECUTABLE(dcc_original ${dcc_SOURCES} ${dcc_UI_SOURCES}) +ADD_DEPENDENCIES(dcc_original dcc_lib) +TARGET_LINK_LIBRARIES(dcc_original dcc_lib dcc_hash disasm_s) +qt5_use_modules(dcc_original Core Widgets) +SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD 11) +SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD_REQUIRED ON) + + + +if(dcc_build_tests) +ADD_SUBDIRECTORY(tests) +endif() diff --git a/src/MemoryChunk.cpp b/src/MemoryChunk.cpp new file mode 100644 index 0000000..62f25fb --- /dev/null +++ b/src/MemoryChunk.cpp @@ -0,0 +1,22 @@ +#include "MemoryChunk.h" + +#include +#include +#include +#include +#include + +using namespace boost::icl; +MemoryChunk::MemoryChunk(LinearAddress start, LinearAddress fin) : m_start(start),m_fin(fin) +{ +} + +bool MemoryChunk::contains(LinearAddress addr) const +{ + return addr>=m_start && addr +#include +/** + * @brief The MemoryChunk class represents a continuous range of Addresses + */ +class MemoryChunk +{ +private: + LinearAddress m_start; + LinearAddress m_fin; +public: + MemoryChunk(LinearAddress start,LinearAddress fin); + bool contains(LinearAddress addr) const; + uint64_t size() const; + + std::pair bounds() const { return std::make_pair(m_start,m_fin); } +}; + +#endif // BYTECHUNK_H diff --git a/src/MemorySegment.cpp b/src/MemorySegment.cpp new file mode 100644 index 0000000..014271f --- /dev/null +++ b/src/MemorySegment.cpp @@ -0,0 +1,5 @@ +#include "MemorySegment.h" + +MemorySegment::MemorySegment(LinearAddress base, LinearAddress start, LinearAddress fin) : MemoryChunk(start,fin) { + m_base = base; +} diff --git a/src/MemorySegment.h b/src/MemorySegment.h new file mode 100644 index 0000000..5e92358 --- /dev/null +++ b/src/MemorySegment.h @@ -0,0 +1,19 @@ +#pragma once + +#include "MemoryChunk.h" + +#include +/** + * @brief The MemorySegment represents a single chunk of memory with additional properties. + */ +class MemorySegment : public MemoryChunk +{ + uint16_t m_base; + int m_flags; + QString m_name; +public: + MemorySegment(LinearAddress base,LinearAddress start,LinearAddress fin); + const QString &getName() const { return m_name; } + void setName(const QString &v) { m_name = v; } +}; + diff --git a/src/MemorySegmentCoordinator.cpp b/src/MemorySegmentCoordinator.cpp new file mode 100644 index 0000000..ba863bb --- /dev/null +++ b/src/MemorySegmentCoordinator.cpp @@ -0,0 +1,54 @@ +#include "MemorySegmentCoordinator.h" + +#include +#include +#include +using namespace boost::icl; +class MemorySegmentCoordinatorImpl { + boost::icl::interval_map m_segmentation_map; +public: + bool addSegment(LinearAddress base, LinearAddress start, LinearAddress fin, const char * name, int flags) { + if(start>fin) + return false; + if(startsetName(name); + // + auto segment_bounds(seg->bounds()); + m_segmentation_map.add(std::make_pair( + interval::right_open(segment_bounds.first,segment_bounds.second), + seg) + ); + return true; + } + uint32_t numberOfSegments() const { return interval_count(m_segmentation_map); } + const MemorySegment *get(LinearAddress addr) { + auto iter = m_segmentation_map.find(addr); + if(iter==m_segmentation_map.end()) { + return nullptr; + } + return iter->second; + + } +}; + +MemorySegmentCoordinator::MemorySegmentCoordinator() +{ + m_impl = new MemorySegmentCoordinatorImpl; +} + +bool MemorySegmentCoordinator::addSegment(LinearAddress base, LinearAddress start, LinearAddress fin, const char * name, int flags) +{ + return m_impl->addSegment(base,start,fin,name,flags); +} + +uint32_t MemorySegmentCoordinator::size() +{ + return m_impl->numberOfSegments(); +} + +MemorySegment *MemorySegmentCoordinator::getSegment(LinearAddress addr) +{ + return const_cast(m_impl->get(addr)); +} diff --git a/src/MemorySegmentCoordinator.h b/src/MemorySegmentCoordinator.h new file mode 100644 index 0000000..ce5c63b --- /dev/null +++ b/src/MemorySegmentCoordinator.h @@ -0,0 +1,34 @@ +#pragma once + +#include "MemorySegment.h" + +struct SegmentHolder { + SegmentHolder() : val(nullptr) {} + SegmentHolder(MemorySegment *inf) : val(inf) {} + + MemorySegment *operator->() { return val;} + MemorySegment &operator*() const { return *val;} + operator MemorySegment *() { return val;} + operator const MemorySegment *() const { return val;} + SegmentHolder operator+=(const SegmentHolder &/*s*/) { + throw std::runtime_error("Cannot aggregate MemorySegments !"); + } + + MemorySegment *val; +}; + +/** + * @brief The MemorySegmentCoordinator class is responsible for: + * - Managing the lifetime of MemorySegments + * - Providing convenience functions for querying the segment-related data + */ +class MemorySegmentCoordinator +{ + class MemorySegmentCoordinatorImpl *m_impl; +public: + MemorySegmentCoordinator(); + + bool addSegment(LinearAddress base,LinearAddress start,LinearAddress fin,const char *name,int flags); + uint32_t size(); + MemorySegment *getSegment(LinearAddress addr); +}; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt new file mode 100644 index 0000000..5da4c62 --- /dev/null +++ b/src/tests/CMakeLists.txt @@ -0,0 +1,24 @@ +set(TESTS + ProjectTests + LoaderTests + MemoryChunkTests + MemorySegmentCoordinatorTests +) + + +include(DCC_Macros) + +set(target_INCLUDE_DIR + .. +) +include_directories(${target_INCLUDE_DIR} + ../../frontend/sparc + ../../frontend/pentium +) + +set(test_LIBRARIES +dcc_lib dcc_hash disasm_s +) +foreach(t ${TESTS}) + ADD_QTEST(${t}) +endforeach() diff --git a/src/tests/LoaderTests.cpp b/src/tests/LoaderTests.cpp new file mode 100644 index 0000000..8eb0cdb --- /dev/null +++ b/src/tests/LoaderTests.cpp @@ -0,0 +1,15 @@ +#include "LoaderTests.h" + +#include "project.h" +#include "loader.h" + +#include +#include +#include +#include +#include + +void LoaderTest::testDummy() { + QVERIFY2(false,"No tests written for loader"); +} +QTEST_MAIN(LoaderTest) diff --git a/src/tests/LoaderTests.h b/src/tests/LoaderTests.h new file mode 100644 index 0000000..db3ac2e --- /dev/null +++ b/src/tests/LoaderTests.h @@ -0,0 +1,7 @@ +#include + +class LoaderTest : public QObject { + Q_OBJECT +private slots: + void testDummy(); +}; diff --git a/src/tests/MemoryChunkTests.cpp b/src/tests/MemoryChunkTests.cpp new file mode 100644 index 0000000..65b7d99 --- /dev/null +++ b/src/tests/MemoryChunkTests.cpp @@ -0,0 +1,22 @@ +#include "MemoryChunkTests.h" + +#include "MemoryChunk.h" +#include "project.h" +#include "loader.h" + +#include +#include +#include +#include +#include + +void MemoryChunkTest::testIfConstructionWorksProperly() { + MemoryChunk mc(0,10); + QCOMPARE(mc.size(),size_t(10)); + QVERIFY(mc.contains(1)); + QVERIFY(not mc.contains(10)); + QVERIFY(not mc.contains(-1)); + QVERIFY(not mc.contains(100)); +} + +QTEST_MAIN(MemoryChunkTest) diff --git a/src/tests/MemoryChunkTests.h b/src/tests/MemoryChunkTests.h new file mode 100644 index 0000000..df53fc5 --- /dev/null +++ b/src/tests/MemoryChunkTests.h @@ -0,0 +1,7 @@ +#include + +class MemoryChunkTest : public QObject { + Q_OBJECT +private slots: + void testIfConstructionWorksProperly(); +}; diff --git a/src/tests/MemorySegmentCoordinatorTests.cpp b/src/tests/MemorySegmentCoordinatorTests.cpp new file mode 100644 index 0000000..8806797 --- /dev/null +++ b/src/tests/MemorySegmentCoordinatorTests.cpp @@ -0,0 +1,55 @@ +#include "MemorySegmentCoordinatorTests.h" + +#include "MemorySegmentCoordinator.h" +#include "project.h" +#include "loader.h" + +#include +#include +#include +#include +#include + +void MemorySegmentCoordinatorTest::testSimpleQueries() +{ + MemorySegmentCoordinator segmenter; + segmenter.addSegment( + LinearAddress(0x10), + LinearAddress(0x13),LinearAddress(0x20), + ".text",4); + MemorySegment * seg; + QCOMPARE(segmenter.getSegment(LinearAddress(0x9)),(MemorySegment *)nullptr); + seg = segmenter.getSegment(0x14); + QVERIFY(seg!=nullptr); + if(seg) { + QCOMPARE(seg->getName(),QString(".text")); + } +} +void MemorySegmentCoordinatorTest::testAddingSegments() +{ + MemorySegmentCoordinator segmenter; + QVERIFY(segmenter.addSegment( + LinearAddress(0x10), + LinearAddress(0x13),LinearAddress(0x20), + ".text",4)); + QCOMPARE(segmenter.size(),uint32_t(1)); + QVERIFY(segmenter.addSegment( + LinearAddress(0x20), + LinearAddress(0x22),LinearAddress(0x33), + ".text",4)); + QVERIFY2(not segmenter.addSegment( + LinearAddress(0x20), + LinearAddress(0x40),LinearAddress(0x20), + ".text",4),"Adding segment with start>fin should fail"); + QVERIFY2(not segmenter.addSegment( + LinearAddress(0x20), + LinearAddress(0x10),LinearAddress(0x20), + ".text",4),"Segment start should be >= base"); + QCOMPARE(segmenter.size(),uint32_t(2)); +} +void MemorySegmentCoordinatorTest::testAddingIntersectingSegments() +{ + +} + +QTEST_MAIN(MemorySegmentCoordinatorTest) diff --git a/src/tests/MemorySegmentCoordinatorTests.h b/src/tests/MemorySegmentCoordinatorTests.h new file mode 100644 index 0000000..028dbc4 --- /dev/null +++ b/src/tests/MemorySegmentCoordinatorTests.h @@ -0,0 +1,9 @@ +#include + +class MemorySegmentCoordinatorTest : public QObject { + Q_OBJECT +private slots: + void testAddingSegments(); + void testAddingIntersectingSegments(); + void testSimpleQueries(); +}; diff --git a/src/tests/ProjectTests.cpp b/src/tests/ProjectTests.cpp new file mode 100644 index 0000000..ecd8519 --- /dev/null +++ b/src/tests/ProjectTests.cpp @@ -0,0 +1,53 @@ +#include "ProjectTests.h" + +#include "project.h" + +#include +#include +#include +#include +#include + +static bool logset = false; +static QString TEST_BASE; +static QDir baseDir; + +void ProjectTest::initTestCase() { + if (!logset) { + TEST_BASE = QProcessEnvironment::systemEnvironment().value("DCC_TEST_BASE", ""); + baseDir = QDir(TEST_BASE); + if (TEST_BASE.isEmpty()) { + qWarning() << "DCC_TEST_BASE environment variable not set, will assume '..', many test may fail"; + TEST_BASE = ".."; + baseDir = QDir(".."); + } + logset = true; +// Boomerang::get()->setProgPath(TEST_BASE); +// Boomerang::get()->setPluginPath(TEST_BASE + "/out"); +// Boomerang::get()->setLogger(new NullLogger()); + } +} +void ProjectTest::testNewProjectIsInitalized() { + Project p; + QCOMPARE((CALL_GRAPH *)nullptr,p.callGraph); + QVERIFY(p.pProcList.empty()); + QVERIFY(p.binary_path().isEmpty()); + QVERIFY(p.project_name().isEmpty()); + QVERIFY(p.symtab.empty()); +} + +void ProjectTest::testCreatedProjectHasValidNames() { + Project p; + QStringList strs = {"./Project1.EXE","/home/Project2.EXE","/home/Pro\\ ject3"}; + QStringList expected = {"Project1","Project2","Pro\\ ject3"}; + for(size_t i=0; i + +class ProjectTest : public QObject { + Q_OBJECT +private slots: + + void testNewProjectIsInitalized(); + void testCreatedProjectHasValidNames(); + void initTestCase(); +}; diff --git a/src/tests/loader.cpp b/src/tests/loader.cpp deleted file mode 100644 index 978fd16..0000000 --- a/src/tests/loader.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "project.h" -#include "loader.h" -#include -#include - -TEST(Loader, NewProjectIsInitalized) { - Project p; - EXPECT_EQ(nullptr,p.callGraph); - ASSERT_TRUE(p.pProcList.empty()); - ASSERT_TRUE(p.binary_path().empty()); - ASSERT_TRUE(p.project_name().empty()); - ASSERT_TRUE(p.symtab.empty()); -} - diff --git a/src/tests/project.cpp b/src/tests/project.cpp deleted file mode 100644 index 72a3e40..0000000 --- a/src/tests/project.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "project.h" -#include -#include - -TEST(Project, NewProjectIsInitalized) { - Project p; - EXPECT_EQ(nullptr,p.callGraph); - ASSERT_TRUE(p.pProcList.empty()); - ASSERT_TRUE(p.binary_path().empty()); - ASSERT_TRUE(p.project_name().empty()); - ASSERT_TRUE(p.symtab.empty()); -} - -TEST(Project, CreatedProjectHasValidNames) { - Project p; - std::vector strs = {"./Project1.EXE","/home/Project2.EXE","/home/Pro\\ ject3"}; - std::vector expected = {"Project1","Project2","Pro\\ ject3"}; - for(size_t i=0; i