/* * Copyright (c) 2020 Trail of Bits, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "mcsema/BC/Util.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wdocumentation" #pragma clang diagnostic ignored "-Wswitch-enum" #include #include #include #include #include #include #include #include #include #pragma clang diagnostic pop #include #include #include #include #include #include #include #include "mcsema/Arch/Arch.h" DEFINE_bool( disable_aliases, false, "Disable using global aliases for accessing data/registers in the bitcode"); namespace mcsema { std::shared_ptr gContext = nullptr; llvm::IntegerType *gWordType = nullptr; uint64_t gWordMask = 0; std::unique_ptr gModule = nullptr; llvm::Constant *gZero = nullptr; llvm::Value *GetConstantInt(unsigned size, uint64_t value) { return llvm::ConstantInt::get(llvm::Type::getIntNTy(*gContext, size), value); } // Get a lifted representation of a reference (in code) to `ea`. llvm::Constant *LiftXrefInCode(uint64_t ea) { return llvm::ConstantExpr::getAdd( gZero, llvm::ConstantInt::get(gWordType, ea & gWordMask, false)); } // Get a lifted representation of a reference (in data) to `ea`. llvm::Constant *LiftXrefInData(const NativeSegment *cfg_seg, uint64_t ea, bool cast_to_int) { CHECK(cfg_seg != nullptr); CHECK(cfg_seg->ea <= ea); CHECK(ea < (cfg_seg->ea + cfg_seg->size)); std::stringstream ss; ss << "data_" << std::hex << ea; auto alias_name = ss.str(); llvm::Constant *ptr = nullptr; if (auto alias = gModule->getNamedAlias(alias_name); alias) { ptr = alias; } else { auto seg_var = llvm::dyn_cast(cfg_seg->Get()->Pointer()); auto seg_type = remill::GetValueType(seg_var); llvm::DataLayout dl(gModule.get()); llvm::SmallVector gep_index_list; auto i32_type = llvm::Type::getInt32Ty(*gContext); gep_index_list.push_back(llvm::ConstantInt::get(i32_type, 0)); const auto goal_offset = (ea - cfg_seg->ea) + cfg_seg->padding; auto [offset, type] = remill::BuildIndexes(dl, seg_type, 0, goal_offset, gep_index_list); ptr = llvm::ConstantExpr::getInBoundsGetElementPtr(seg_type, seg_var, gep_index_list); if (offset < goal_offset) { auto i8_type = llvm::Type::getInt8Ty(*gContext); ptr = llvm::ConstantExpr::getBitCast(ptr, llvm::PointerType::get(i8_type, 0)); ptr = llvm::ConstantExpr::getInBoundsGetElementPtr( i8_type, ptr, llvm::ConstantInt::get(i32_type, goal_offset - offset, false)); } if (!FLAGS_disable_aliases && !cfg_seg->is_external) { auto ptr_type = llvm::dyn_cast(ptr->getType()); ptr = llvm::GlobalAlias::create( ptr_type->getElementType(), ptr_type->getAddressSpace(), seg_var->getLinkage(), alias_name, ptr, gModule.get()); } } if (cast_to_int) { return llvm::ConstantExpr::getPtrToInt(ptr, gWordType); } else { return ptr; } } // Create a global register state pointer to pass to lifted functions. llvm::Constant *GetStatePointer(void) { static llvm::Constant *state_ptr = nullptr; if (state_ptr) { return state_ptr; } auto state_type = gArch->StateStructType(); // State is initialized with zeroes. Each callback/entrypoint set // appropriate value to stack pointer. This is needed because of // thread_local auto state_init = llvm::ConstantAggregateZero::get(state_type); const auto state_ptr_var = new llvm::GlobalVariable( *gModule, state_type, false, llvm::GlobalValue::ExternalLinkage, state_init, "__mcsema_reg_state", nullptr, llvm::GlobalValue::InitialExecTLSModel); state_ptr = state_ptr_var; if (state_ptr_var->getType()->getPointerAddressSpace() != 0) { state_ptr = llvm::ConstantExpr::getAddrSpaceCast( state_ptr, llvm::PointerType::get(state_type, 0)); } return state_ptr; } // Return the address of the base of the TLS data. llvm::Value *GetTLSBaseAddress(llvm::IRBuilder<> &ir) { enum { kGSAddressSpace = 256U, kFSAddressSpace = 257U, // From inside of the TEB. kWin32TLSPointerIndex = 0x2c, kWin64TLSPointerIndex = 0x58 }; if (gArch->IsAArch64()) { #if LLVM_VERSION(3, 7) >= LLVM_VERSION_NUMBER LOG(ERROR) << "LLVM 3.7 and below have no thread pointer-related " << "intrinsics; using NULL as the base of TLS."; return llvm::ConstantInt::get(gWordType, 0); #else # if LLVM_VERSION(3, 8) >= LLVM_VERSION_NUMBER if (gArch->IsAArch64()) { LOG(ERROR) << "Assuming the `llvm.arm.thread.pointer` intrinsic gets us the base " << "of thread-local storage."; auto func = llvm::Intrinsic::getDeclaration( gModule.get(), llvm::Intrinsic::arm_thread_pointer); if (func) { return ir.CreatePtrToInt(ir.CreateCall(func), gWordType); } } # endif LOG(ERROR) << "Assuming the `thread.pointer` intrinsic gets us the base " << "of thread-local storage."; auto func = llvm::Intrinsic::getDeclaration( gModule.get(), llvm::Intrinsic::thread_pointer); return ir.CreatePtrToInt(ir.CreateCall(func), gWordType); #endif // 64-bit x86. } else if (gArch->IsAMD64()) { llvm::ConstantInt *base = nullptr; unsigned addr_space = 0; if (remill::kOSWindows == gArch->os_name) { base = llvm::ConstantInt::get(gWordType, kWin64TLSPointerIndex); addr_space = kGSAddressSpace; } else if (remill::kOSLinux == gArch->os_name) { base = llvm::ConstantInt::get(gWordType, 0); addr_space = kFSAddressSpace; } if (base) { auto tls_base_ptr = ir.CreateIntToPtr( base, llvm::PointerType::get(gWordType, addr_space)); return ir.CreateLoad(tls_base_ptr); } // 32-bit x86. } else if (gArch->IsX86()) { llvm::ConstantInt *base = nullptr; unsigned addr_space = 0; if (remill::kOSWindows == gArch->os_name) { base = llvm::ConstantInt::get(gWordType, kWin32TLSPointerIndex); addr_space = kFSAddressSpace; } else if (remill::kOSLinux == gArch->os_name) { base = llvm::ConstantInt::get(gWordType, 0); addr_space = kGSAddressSpace; } if (base) { auto tls_base_ptr = ir.CreateIntToPtr( base, llvm::PointerType::get(gWordType, addr_space)); return ir.CreateLoad(tls_base_ptr); } } LOG(FATAL) << "Cannot generate code to find the thread base pointer for arch " << remill::GetArchName(gArch->arch_name) << " and OS " << remill::GetOSName(gArch->os_name); return nullptr; } } // namespace mcsema