/* * Copyright (c) 2022 Trail of Bits, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LLVM_VERSION_MAJOR < 18 namespace llvm { using endianness = support::endianness; } #endif // LLVM_VERSION_MAJOR namespace test_runner { using random_bytes_engine = std::independent_bits_engine; class MemoryHandler { private: std::unordered_map uninitialized_reads; std::unordered_map state; random_bytes_engine rbe; llvm::endianness endian; public: MemoryHandler(llvm::endianness endian_); MemoryHandler(llvm::endianness endian_, std::unordered_map initial_state); uint8_t read_byte(uint64_t addr); std::vector readSize(uint64_t addr, size_t num); const std::unordered_map &GetMemory() const; std::string DumpState() const; template T ReadMemory(uint64_t addr); template void WriteMemory(uint64_t addr, T value); std::unordered_map GetUninitializedReads(); }; template T MemoryHandler::ReadMemory(uint64_t addr) { auto buff = this->readSize(addr, sizeof(T)); return llvm::support::endian::read(buff.data(), this->endian); } template void MemoryHandler::WriteMemory(uint64_t addr, T value) { std::vector buff(sizeof(T)); llvm::support::endian::write(buff.data(), value, this->endian); for (size_t i = 0; i < sizeof(T); i++) { this->state[addr + i] = buff[i]; } } void StubOutFlagComputationInstrinsics(llvm::Module *mod, llvm::ExecutionEngine &exec_engine); llvm::Function * CopyFunctionIntoNewModule(llvm::Module *target, const llvm::Function *old_func, const std::unique_ptr &old_module); void *MissingFunctionStub(const std::string &name); template void ExecuteLiftedFunction( llvm::Function *func, size_t insn_length, T *state, test_runner::MemoryHandler *handler, const std::function &program_counter_fetch) { std::string load_error = ""; llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr, &load_error); if (!load_error.empty()) { LOG(FATAL) << "Failed to load: " << load_error; } auto tgt_mod = llvm::CloneModule(*func->getParent()); #if LLVM_VERSION_MAJOR >= 21 tgt_mod->setTargetTriple(llvm::Triple()); #else tgt_mod->setTargetTriple(""); #endif // LLVM_VERSION_MAJOR tgt_mod->setDataLayout(llvm::DataLayout("")); llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); llvm::InitializeNativeTargetAsmPrinter(); auto res = remill::VerifyModuleMsg(tgt_mod.get()); if (res.has_value()) { LOG(FATAL) << *res; } llvm::EngineBuilder builder(std::move(tgt_mod)); std::string estr; auto eptr = builder.setEngineKind(llvm::EngineKind::JIT).setErrorStr(&estr).create(); if (eptr == nullptr) { LOG(FATAL) << estr; } std::unique_ptr engine(eptr); auto target = engine->FindFunctionNamed(func->getName()); StubOutFlagComputationInstrinsics(target->getParent(), *engine); engine->InstallLazyFunctionCreator(&MissingFunctionStub); engine->DisableSymbolSearching(false); // expect traditional remill lifted insn assert(func->arg_size() == 3); auto returned = (void *(*) (T *, uint32_t, void *) ) engine->getFunctionAddress( target->getName().str()); assert(returned != nullptr); auto orig_pc = program_counter_fetch(state); // run until we terminate and exit pc while (program_counter_fetch(state) == orig_pc) { returned(state, program_counter_fetch(state), handler); } } template void RandomizeState(T &state, random_bytes_engine &rbe) { std::vector data(sizeof(T)); std::generate(begin(data), end(data), std::ref(rbe)); std::memcpy(&state, data.data(), sizeof(T)); } uint8_t random_boolean_flag(random_bytes_engine &rbe); enum TypeId { MEMORY = 0, STATE = 1 }; class LiftingTester { private: std::shared_ptr semantics_module; remill::Arch::ArchPtr arch; std::unique_ptr table; remill::OperandLifter::OpLifterPtr lifter; public: // Produces a tester lifter that lifts into a target prepared semantics module LiftingTester(std::shared_ptr semantics_module_, remill::OSName os_name, remill::ArchName arch_name); // Builds a new semantics module to lift into LiftingTester(llvm::LLVMContext &context, remill::OSName os_name, remill::ArchName arch_name); std::unordered_map GetTypeMapping(); std::optional> LiftInstructionFunction(std::string_view fname, std::string_view bytes, uint64_t address); std::optional> LiftInstructionFunction(std::string_view fname, std::string_view bytes, uint64_t address, const remill::DecodingContext &ctx); const remill::Arch::ArchPtr &GetArch() const; }; } // namespace test_runner