labath created this revision. Herald added a subscriber: aprantl. Debug info sections, (or non-SHF_ALLOC sections in general) should be linked as if their load address was zero to emulate the behavior of the static linker.
I've made two tests for this: One checks that we are able to properly process Modules with debug info in the ProcessAllSections mode. This tests the feature only indirectly, by making sure we don't assert when processing relocations, and it only does that on x86_64 linux builds with asserts. The second test directly verifies that the relocations were applied correctly, by directly inspecting the generated sections. This one works on all platforms, but it is a little cumbersome due to the need to construct a section with well-defined contents. Nonetheless, most of the code is boilerplate, and it could be shared if we end up having more tests like these. This bug was discovered because it was breaking lldb expression evaluation on linux. https://reviews.llvm.org/D32899 Files: lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp unittests/ExecutionEngine/MCJIT/MCJITTest.cpp
Index: unittests/ExecutionEngine/MCJIT/MCJITTest.cpp =================================================================== --- unittests/ExecutionEngine/MCJIT/MCJITTest.cpp +++ unittests/ExecutionEngine/MCJIT/MCJITTest.cpp @@ -12,9 +12,20 @@ // //===----------------------------------------------------------------------===// +#include "MCJITTestBase.h" #include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Support/DynamicLibrary.h" -#include "MCJITTestBase.h" +#include "llvm/Support/TargetRegistry.h" #include "gtest/gtest.h" using namespace llvm; @@ -281,4 +292,142 @@ EXPECT_FALSE(std::find(I, E, "Foo2") == E); } +static Function *insertMainWithDI(LLVMContext &Context, Module &M) { + DIBuilder DIB(M); + DIFile *File = DIB.createFile("-", "."); + DIB.createCompileUnit(dwarf::DW_LANG_C99, File, "producer", false, "", 0); + DISubroutineType *MainType = DIB.createSubroutineType({}); + DISubprogram *DIMain = DIB.createFunction(File, "main", "main", File, 1, + MainType, false, true, 1); + DILocation *Loc = DILocation::get(Context, 1, 1, DIMain, nullptr); + Function *Main = Function::Create(TypeBuilder<int(void), false>::get(Context), + GlobalValue::ExternalLinkage, "main", &M); + Main->setSubprogram(DIMain); + BasicBlock *Entry = BasicBlock::Create(Context, "entry", Main); + ReturnInst *Ret = + IRBuilder<>(Entry).CreateRet(ConstantInt::get(Context, APInt(32, 47))); + Ret->setDebugLoc(Loc); + DIB.finalize(); + + return Main; +} + +TEST_F(MCJITTest, ProcessAllSections) { + Function *Main = insertMainWithDI(Context, *M); + + createJIT(std::move(M)); + TheJIT->setProcessAllSections(true); + TheJIT->finalizeObject(); + + uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str()); + ASSERT_NE(0u, ptr) << "Unable to get pointer to main() from JIT"; + + int (*FuncPtr)() = (int (*)())ptr; + int returnCode = FuncPtr(); + EXPECT_EQ(returnCode, 47); +} + +static void createObject(SmallVectorImpl<char> &Object) { + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86TargetMC(); + LLVMInitializeX86Target(); + LLVMInitializeX86AsmPrinter(); + + std::string ErrorStr; + Triple TheTriple("x86_64-pc-linux"); + const Target *TheTarget = + TargetRegistry::lookupTarget("", TheTriple, ErrorStr); + ASSERT_NE(nullptr, TheTarget) << ErrorStr; + + std::unique_ptr<MCRegisterInfo> MRI( + TheTarget->createMCRegInfo(TheTriple.getTriple())); + ASSERT_NE(nullptr, MRI); + + std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend( + *MRI, TheTriple.getTriple(), "", MCTargetOptions())); + ASSERT_NE(nullptr, MAB); + + std::unique_ptr<MCAsmInfo> MAI( + TheTarget->createMCAsmInfo(*MRI, TheTriple.getTriple())); + ASSERT_NE(nullptr, MAI); + + auto MOFI = llvm::make_unique<MCObjectFileInfo>(); + + std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo()); + ASSERT_NE(nullptr, MII); + + MCContext MC(MAI.get(), MRI.get(), MOFI.get()); + MOFI->InitMCObjectFileInfo(TheTriple, false, CodeModel::Default, MC); + + std::unique_ptr<MCCodeEmitter> MCE( + TheTarget->createMCCodeEmitter(*MII, *MRI, MC)); + ASSERT_NE(nullptr, MCE); + + std::unique_ptr<MCSubtargetInfo> MSTI( + TheTarget->createMCSubtargetInfo(TheTriple.getTriple(), "", "")); + + raw_svector_ostream Stream(Object); + + std::unique_ptr<MCStreamer> Streamer(TheTarget->createMCObjectStreamer( + TheTriple, MC, *MAB.release(), Stream, MCE.release(), *MSTI, false, false, + false)); + + Streamer->SwitchSection(MOFI->getDwarfFrameSection()); + MCSymbol *SectionEnd = MC.createTempSymbol(); + Streamer->EmitSymbolValue(SectionEnd, 4); + Streamer->EmitSymbolValue(SectionEnd, 4); + Streamer->EmitLabel(SectionEnd); + Streamer->Finish(); +} + +class DebugFrameFetcher : public SectionMemoryManager { +public: + DebugFrameFetcher(ArrayRef<uint8_t> &DebugFrame) : DebugFrame(DebugFrame) {} + + uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, StringRef SectionName, + bool isReadOnly) override { + uint8_t *Ptr = SectionMemoryManager::allocateDataSection( + Size, Alignment, SectionID, SectionName, isReadOnly); + if (SectionName == ".debug_frame") + DebugFrame = ArrayRef<uint8_t>(Ptr, Size); + return Ptr; + } + +private: + ArrayRef<uint8_t> &DebugFrame; +}; + +TEST_F(MCJITTest, debug_info_relocation) { + // Test that debug info sections are relocated as if their load address was + // zero + + SmallString<0> Object; + createObject(Object); + + ArrayRef<uint8_t> DebugFrame; + EngineBuilder EB(std::move(M)); + TheJIT.reset(EB.setEngineKind(EngineKind::JIT) + .setMCJITMemoryManager( + llvm::make_unique<DebugFrameFetcher>(DebugFrame)) + .setOptLevel(CodeGenOpt::None) + .setCodeModel(CodeModel::JITDefault) + .setMArch("") + .setMCPU(sys::getHostCPUName()) + .create(EB.selectTarget(Triple("x86_64-pc-linux"), "", "", + SmallVector<std::string, 0>()))); + ASSERT_NE(nullptr, TheJIT); + auto ObjectOrError = + object::ObjectFile::createObjectFile(MemoryBufferRef(Object, "test.o")); + ASSERT_TRUE(bool(ObjectOrError)); + TheJIT->setProcessAllSections(true); + TheJIT->addObjectFile(std::move(*ObjectOrError)); + TheJIT->finalizeObject(); + + // The real section may have padding, so we only test size()>=8 + ASSERT_GE(DebugFrame.size(), 8u); + uint8_t Contents[] = {8, 0, 0, 0, 8, 0, 0, 0}; + ASSERT_EQ(makeArrayRef(Contents), DebugFrame.take_front(8)); +} + } // end anonymous namespace Index: lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp =================================================================== --- lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -703,7 +703,7 @@ unsigned Alignment = (unsigned)Alignment64 & 0xffffffffL; unsigned PaddingSize = 0; unsigned StubBufSize = 0; - bool IsRequired = isRequiredForExecution(Section) || ProcessAllSections; + bool IsRequired = isRequiredForExecution(Section); bool IsVirtual = Section.isVirtual(); bool IsZeroInit = isZeroInit(Section); bool IsReadOnly = isReadOnlyData(Section); @@ -743,8 +743,8 @@ Alignment = std::max(Alignment, getStubAlignment()); // Some sections, such as debug info, don't need to be loaded for execution. - // Leave those where they are. - if (IsRequired) { + // Process those only if explicitly requested. + if (IsRequired || ProcessAllSections) { Allocate = DataSize + PaddingSize + StubBufSize; if (!Allocate) Allocate = 1; @@ -788,6 +788,10 @@ Sections.push_back( SectionEntry(Name, Addr, DataSize, Allocate, (uintptr_t)pData)); + // Debug info sections are linked as if their load address was zero + if (!IsRequired) + Sections.back().setLoadAddress(0); + if (Checker) Checker->registerSection(Obj.getFileName(), SectionID);
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits