void MCJITHelper::SymListener::NotifyObjectEmitted(const object::ObjectFile &Obj, const RuntimeDyld::LoadedObjectInfo &L) { using namespace llvm::object; OwningBinary<ObjectFile> DebugObjOwner = L.getObjectForDebug(Obj); const ObjectFile &DebugObj = *DebugObjOwner.getBinary(); bool verbose = *verboseFlagPtr; for (symbol_iterator it = DebugObj.symbol_begin(), end = DebugObj.symbol_end(); it != end; ++it) { object::SymbolRef::Type SymType; if (it->getType(SymType)) continue; if (SymType == SymbolRef::ST_Function) { StringRef name; uint64_t addr; if (it->getName(name)) continue; if (it->getAddress(addr)) continue; Table->push_back(AddrSymPair(addr, name.str())); if (verbose) { std::cerr << "Loading native code for function " << name.str() << " at address " << (void*)addr << std::endl; } } } }
void JuliaOJIT::DebugObjectRegistrar::operator()(ObjectLinkingLayerBase::ObjSetHandleT H, const ObjSetT &Objects, const LoadResult &LOS) { #ifndef LLVM38 notifyObjectLoaded(JIT.MemMgr, H); #endif auto oit = Objects.begin(); auto lit = LOS.begin(); for (; oit != Objects.end(); ++oit, ++lit) { #ifdef LLVM39 const auto &Object = (*oit)->getBinary(); #else auto &Object = *oit; #endif auto &LO = *lit; OwningBinary<object::ObjectFile> SavedObject = LO->getObjectForDebug(*Object); // If the debug object is unavailable, save (a copy of) the original object // for our backtraces if (!SavedObject.getBinary()) { // This is unfortunate, but there doesn't seem to be a way to take // ownership of the original buffer auto NewBuffer = MemoryBuffer::getMemBufferCopy(Object->getData(), Object->getFileName()); auto NewObj = ObjectFile::createObjectFile(NewBuffer->getMemBufferRef()); assert(NewObj); SavedObject = OwningBinary<object::ObjectFile>(std::move(*NewObj),std::move(NewBuffer)); } else { NotifyGDB(SavedObject); } SavedObjects.push_back(std::move(SavedObject)); ORCNotifyObjectEmitted(JuliaListener.get(), *Object, *SavedObjects.back().getBinary(), *LO, JIT.MemMgr); // record all of the exported symbols defined in this object // in the primary hash table for the enclosing JIT for (auto &Symbol : Object->symbols()) { auto Flags = Symbol.getFlags(); if (Flags & object::BasicSymbolRef::SF_Undefined) continue; if (!(Flags & object::BasicSymbolRef::SF_Exported)) continue; auto NameOrError = Symbol.getName(); assert(NameOrError); auto Name = NameOrError.get(); orc::JITSymbol Sym = JIT.CompileLayer.findSymbolIn(H, Name, true); assert(Sym); // note: calling getAddress here eagerly finalizes H // as an alternative, we could store the JITSymbol instead // (which would present a lazy-initializer functor interface instead) JIT.LocalSymbolTable[Name] = (void*)(uintptr_t)Sym.getAddress(); } } }
void JuliaOJIT::DebugObjectRegistrar::NotifyGDB(OwningBinary<object::ObjectFile> &DebugObj) { const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart(); size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize(); assert(Buffer && "Attempt to register a null object with a debugger."); jit_code_entry *JITCodeEntry = new jit_code_entry(); if (!JITCodeEntry) { jl_printf(JL_STDERR, "WARNING: Allocation failed when registering a JIT entry!\n"); } else { JITCodeEntry->symfile_addr = Buffer; JITCodeEntry->symfile_size = Size; NotifyDebugger(JITCodeEntry); } }
LLVMBool LLVMIsSectionIteratorAtEnd(LLVMObjectFileRef OF, LLVMSectionIteratorRef SI) { OwningBinary<ObjectFile> *OB = unwrap(OF); return (*unwrap(SI) == OB->getBinary()->section_end()) ? 1 : 0; }
// ObjectFile Section iterators LLVMSectionIteratorRef LLVMGetSections(LLVMObjectFileRef OF) { OwningBinary<ObjectFile> *OB = unwrap(OF); section_iterator SI = OB->getBinary()->section_begin(); return wrap(new section_iterator(SI)); }
// ObjectFile Symbol iterators LLVMSymbolIteratorRef LLVMGetSymbols(LLVMObjectFileRef OF) { OwningBinary<ObjectFile> *OB = unwrap(OF); symbol_iterator SI = OB->getBinary()->symbol_begin(); return wrap(new symbol_iterator(SI)); }
void ObjectLoadListener::getDebugInfoForObject( const ObjectFile &Obj, const RuntimeDyld::LoadedObjectInfo &L) { OwningBinary<ObjectFile> DebugObjOwner = L.getObjectForDebug(Obj); const ObjectFile &DebugObj = *DebugObjOwner.getBinary(); // TODO: This extracts DWARF information from the object file, but we will // want to also be able to eventually extract WinCodeView information as well DWARFContextInMemory DwarfContext(DebugObj); // Use symbol info to find the function size. // If there are funclets, they will each have separate symbols, so we need // to sum the sizes, since the EE wants a single report for the entire // function+funclets. uint64_t Addr = UINT64_MAX; uint64_t Size = 0; std::vector<std::pair<SymbolRef, uint64_t>> SymbolSizes = object::computeSymbolSizes(DebugObj); for (const auto &Pair : SymbolSizes) { object::SymbolRef Symbol = Pair.first; SymbolRef::Type SymType = Symbol.getType(); if (SymType != SymbolRef::ST_Function) continue; // Function info ErrorOr<uint64_t> AddrOrError = Symbol.getAddress(); if (!AddrOrError) { continue; // Error. } uint64_t SingleAddr = AddrOrError.get(); uint64_t SingleSize = Pair.second; if (SingleAddr < Addr) { // The main function is always laid out first Addr = SingleAddr; } Size += SingleSize; } uint32_t LastDebugOffset = (uint32_t)-1; uint32_t NumDebugRanges = 0; ICorDebugInfo::OffsetMapping *OM; DILineInfoTable Lines = DwarfContext.getLineInfoForAddressRange(Addr, Size); DILineInfoTable::iterator Begin = Lines.begin(); DILineInfoTable::iterator End = Lines.end(); // Count offset entries. Will skip an entry if the current IL offset // matches the previous offset. for (DILineInfoTable::iterator It = Begin; It != End; ++It) { uint32_t LineNumber = (It->second).Line; if (LineNumber != LastDebugOffset) { NumDebugRanges++; LastDebugOffset = LineNumber; } } // Reset offset LastDebugOffset = (uint32_t)-1; if (NumDebugRanges > 0) { // Allocate OffsetMapping array unsigned SizeOfArray = (NumDebugRanges) * sizeof(ICorDebugInfo::OffsetMapping); OM = (ICorDebugInfo::OffsetMapping *)Context->JitInfo->allocateArray( SizeOfArray); unsigned CurrentDebugEntry = 0; // Iterate through the debug entries and save IL offset, native // offset, and source reason for (DILineInfoTable::iterator It = Begin; It != End; ++It) { int Offset = It->first; uint32_t LineNumber = (It->second).Line; // We store info about if the instruction is being recorded because // it is a call in the column field bool IsCall = (It->second).Column == 1; if (LineNumber != LastDebugOffset) { LastDebugOffset = LineNumber; OM[CurrentDebugEntry].nativeOffset = Offset; OM[CurrentDebugEntry].ilOffset = LineNumber; OM[CurrentDebugEntry].source = IsCall ? ICorDebugInfo::CALL_INSTRUCTION : ICorDebugInfo::STACK_EMPTY; CurrentDebugEntry++; } } // Send array of OffsetMappings to CLR EE CORINFO_METHOD_INFO *MethodInfo = Context->MethodInfo; CORINFO_METHOD_HANDLE MethodHandle = MethodInfo->ftn; Context->JitInfo->setBoundaries(MethodHandle, NumDebugRanges, OM); getDebugInfoForLocals(DwarfContext, Addr, Size); } }
static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { assert(LoadObjects || !UseDebugObj); // Load any dylibs requested on the command line. loadDylibs(); // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); for (auto &File : InputFileList) { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; RuntimeDyld Dyld(MemMgr, MemMgr); // Load the input memory buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = MemoryBuffer::getFileOrSTDIN(File); if (std::error_code EC = InputBuffer.getError()) ErrorAndExit("unable to read input: '" + EC.message() + "'"); Expected<std::unique_ptr<ObjectFile>> MaybeObj( ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef())); if (!MaybeObj) { std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(MaybeObj.takeError(), OS, ""); OS.flush(); ErrorAndExit("unable to create object file: '" + Buf + "'"); } ObjectFile &Obj = **MaybeObj; OwningBinary<ObjectFile> DebugObj; std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo = nullptr; ObjectFile *SymbolObj = &Obj; if (LoadObjects) { // Load the object file LoadedObjInfo = Dyld.loadObject(Obj); if (Dyld.hasError()) ErrorAndExit(Dyld.getErrorString()); // Resolve all the relocations we can. Dyld.resolveRelocations(); if (UseDebugObj) { DebugObj = LoadedObjInfo->getObjectForDebug(Obj); SymbolObj = DebugObj.getBinary(); LoadedObjInfo.reset(); } } std::unique_ptr<DIContext> Context( new DWARFContextInMemory(*SymbolObj,LoadedObjInfo.get())); std::vector<std::pair<SymbolRef, uint64_t>> SymAddr = object::computeSymbolSizes(*SymbolObj); // Use symbol info to iterate functions in the object. for (const auto &P : SymAddr) { object::SymbolRef Sym = P.first; Expected<SymbolRef::Type> TypeOrErr = Sym.getType(); if (!TypeOrErr) { // TODO: Actually report errors helpfully. consumeError(TypeOrErr.takeError()); continue; } SymbolRef::Type Type = *TypeOrErr; if (Type == object::SymbolRef::ST_Function) { Expected<StringRef> Name = Sym.getName(); if (!Name) { // TODO: Actually report errors helpfully. consumeError(Name.takeError()); continue; } Expected<uint64_t> AddrOrErr = Sym.getAddress(); if (!AddrOrErr) { // TODO: Actually report errors helpfully. consumeError(AddrOrErr.takeError()); continue; } uint64_t Addr = *AddrOrErr; uint64_t Size = P.second; // If we're not using the debug object, compute the address of the // symbol in memory (rather than that in the unrelocated object file) // and use that to query the DWARFContext. if (!UseDebugObj && LoadObjects) { auto SecOrErr = Sym.getSection(); if (!SecOrErr) { // TODO: Actually report errors helpfully. consumeError(SecOrErr.takeError()); continue; } object::section_iterator Sec = *SecOrErr; StringRef SecName; Sec->getName(SecName); uint64_t SectionLoadAddress = LoadedObjInfo->getSectionLoadAddress(*Sec); if (SectionLoadAddress != 0) Addr += SectionLoadAddress - Sec->getAddress(); } outs() << "Function: " << *Name << ", Size = " << Size << ", Addr = " << Addr << "\n"; DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); for (auto &D : Lines) { outs() << " Line info @ " << D.first - Addr << ": " << D.second.FileName << ", line:" << D.second.Line << "\n"; } } } } return 0; }
static int printLineInfoForInput() { // Load any dylibs requested on the command line. loadDylibs(); // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; RuntimeDyld Dyld(MemMgr, MemMgr); // Load the input memory buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = MemoryBuffer::getFileOrSTDIN(InputFileList[i]); if (std::error_code EC = InputBuffer.getError()) return Error("unable to read input: '" + EC.message() + "'"); ErrorOr<std::unique_ptr<ObjectFile>> MaybeObj( ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef())); if (std::error_code EC = MaybeObj.getError()) return Error("unable to create object file: '" + EC.message() + "'"); ObjectFile &Obj = **MaybeObj; // Load the object file std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo = Dyld.loadObject(Obj); if (Dyld.hasError()) return Error(Dyld.getErrorString()); // Resolve all the relocations we can. Dyld.resolveRelocations(); OwningBinary<ObjectFile> DebugObj = LoadedObjInfo->getObjectForDebug(Obj); std::unique_ptr<DIContext> Context( new DWARFContextInMemory(*DebugObj.getBinary())); // Use symbol info to iterate functions in the object. for (object::symbol_iterator I = DebugObj.getBinary()->symbol_begin(), E = DebugObj.getBinary()->symbol_end(); I != E; ++I) { object::SymbolRef::Type SymType; if (I->getType(SymType)) continue; if (SymType == object::SymbolRef::ST_Function) { StringRef Name; uint64_t Addr; uint64_t Size; if (I->getName(Name)) continue; if (I->getAddress(Addr)) continue; if (I->getSize(Size)) continue; outs() << "Function: " << Name << ", Size = " << Size << "\n"; DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); DILineInfoTable::iterator Begin = Lines.begin(); DILineInfoTable::iterator End = Lines.end(); for (DILineInfoTable::iterator It = Begin; It != End; ++It) { outs() << " Line info @ " << It->first - Addr << ": " << It->second.FileName << ", line:" << It->second.Line << "\n"; } } } } return 0; }
static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { assert(LoadObjects || !UseDebugObj); // Load any dylibs requested on the command line. loadDylibs(); // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; RuntimeDyld Dyld(MemMgr, MemMgr); // Load the input memory buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = MemoryBuffer::getFileOrSTDIN(InputFileList[i]); if (std::error_code EC = InputBuffer.getError()) return Error("unable to read input: '" + EC.message() + "'"); ErrorOr<std::unique_ptr<ObjectFile>> MaybeObj( ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef())); if (std::error_code EC = MaybeObj.getError()) return Error("unable to create object file: '" + EC.message() + "'"); ObjectFile &Obj = **MaybeObj; OwningBinary<ObjectFile> DebugObj; std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo = nullptr; ObjectFile *SymbolObj = &Obj; if (LoadObjects) { // Load the object file LoadedObjInfo = Dyld.loadObject(Obj); if (Dyld.hasError()) return Error(Dyld.getErrorString()); // Resolve all the relocations we can. Dyld.resolveRelocations(); if (UseDebugObj) { DebugObj = LoadedObjInfo->getObjectForDebug(Obj); SymbolObj = DebugObj.getBinary(); LoadedObjInfo.reset(); } } std::unique_ptr<DIContext> Context( new DWARFContextInMemory(*SymbolObj,LoadedObjInfo.get())); std::vector<std::pair<SymbolRef, uint64_t>> SymAddr = object::computeSymbolSizes(*SymbolObj); // Use symbol info to iterate functions in the object. for (const auto &P : SymAddr) { object::SymbolRef Sym = P.first; if (Sym.getType() == object::SymbolRef::ST_Function) { ErrorOr<StringRef> Name = Sym.getName(); if (!Name) continue; ErrorOr<uint64_t> AddrOrErr = Sym.getAddress(); if (!AddrOrErr) continue; uint64_t Addr = *AddrOrErr; uint64_t Size = P.second; // If we're not using the debug object, compute the address of the // symbol in memory (rather than that in the unrelocated object file) // and use that to query the DWARFContext. if (!UseDebugObj && LoadObjects) { object::section_iterator Sec(SymbolObj->section_end()); Sym.getSection(Sec); StringRef SecName; Sec->getName(SecName); uint64_t SectionLoadAddress = LoadedObjInfo->getSectionLoadAddress(*Sec); if (SectionLoadAddress != 0) Addr += SectionLoadAddress - Sec->getAddress(); } outs() << "Function: " << *Name << ", Size = " << Size << ", Addr = " << Addr << "\n"; DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); DILineInfoTable::iterator Begin = Lines.begin(); DILineInfoTable::iterator End = Lines.end(); for (DILineInfoTable::iterator It = Begin; It != End; ++It) { outs() << " Line info @ " << It->first - Addr << ": " << It->second.FileName << ", line:" << It->second.Line << "\n"; } } } } return 0; }
static int doDumpReflectionSections(std::string binaryFilename, StringRef arch) { // Note: binaryOrError and objectOrError own the memory for our ObjectFile; // once they go out of scope, we can no longer do anything. OwningBinary<Binary> binaryOwner; std::unique_ptr<llvm::object::ObjectFile> objectOwner; binaryOwner = unwrap(llvm::object::createBinary(binaryFilename)); const llvm::object::Binary *binaryFile = binaryOwner.getBinary(); // The object file we are doing lookups in -- either the binary itself, or // a particular slice of a universal binary. const ObjectFile *objectFile; if (auto o = dyn_cast<ObjectFile>(binaryFile)) { objectFile = o; } else { auto universal = cast<MachOUniversalBinary>(binaryFile); objectOwner = unwrap(universal->getObjectForArch(arch)); objectFile = objectOwner.get(); } // Field descriptor section auto fieldSectionRef = getSectionRef(objectFile, { "__swift3_fieldmd", ".swift3_fieldmd" }); if (fieldSectionRef.getObject() == nullptr) { std::cerr << binaryFilename; std::cerr << " doesn't have a field reflection section!\n"; return EXIT_FAILURE; } StringRef fieldSectionContents; fieldSectionRef.getContents(fieldSectionContents); const FieldSection fieldSection { reinterpret_cast<const void *>(fieldSectionContents.begin()), reinterpret_cast<const void *>(fieldSectionContents.end()) }; // Associated type section - optional AssociatedTypeSection associatedTypeSection {nullptr, nullptr}; auto associatedTypeSectionRef = getSectionRef(objectFile, { "__swift3_assocty", ".swift3_assocty" }); if (associatedTypeSectionRef.getObject() != nullptr) { StringRef associatedTypeSectionContents; associatedTypeSectionRef.getContents(associatedTypeSectionContents); associatedTypeSection = { reinterpret_cast<const void *>(associatedTypeSectionContents.begin()), reinterpret_cast<const void *>(associatedTypeSectionContents.end()), }; } // Builtin types section BuiltinTypeSection builtinTypeSection {nullptr, nullptr}; auto builtinTypeSectionRef = getSectionRef(objectFile, { "__swift3_builtin", ".swift3_builtin" }); if (builtinTypeSectionRef.getObject() != nullptr) { StringRef builtinTypeSectionContents; builtinTypeSectionRef.getContents(builtinTypeSectionContents); builtinTypeSection = { reinterpret_cast<const void *>(builtinTypeSectionContents.begin()), reinterpret_cast<const void *>(builtinTypeSectionContents.end()) }; } // Typeref section auto typeRefSectionRef = getSectionRef(objectFile, { "__swift3_typeref", ".swift3_typeref" }); if (typeRefSectionRef.getObject() == nullptr) { std::cerr << binaryFilename; std::cerr << " doesn't have an associated typeref section!\n"; return EXIT_FAILURE; } StringRef typeRefSectionContents; typeRefSectionRef.getContents(typeRefSectionContents); const GenericSection typeRefSection { reinterpret_cast<const void *>(typeRefSectionContents.begin()), reinterpret_cast<const void *>(typeRefSectionContents.end()) }; // Reflection strings section auto reflectionStringsSectionRef = getSectionRef(objectFile, { "__swift3_reflstr", ".swift3_reflstr" }); if (reflectionStringsSectionRef.getObject() == nullptr) { std::cerr << binaryFilename; std::cerr << " doesn't have an associated reflection strings section!\n"; return EXIT_FAILURE; } StringRef reflectionStringsSectionContents; reflectionStringsSectionRef.getContents(reflectionStringsSectionContents); const GenericSection reflectionStringsSection { reinterpret_cast<const void *>(reflectionStringsSectionContents.begin()), reinterpret_cast<const void *>(reflectionStringsSectionContents.end()) }; // Construct the reflection context auto reader = std::make_shared<InProcessMemoryReader>(); ReflectionContext<External<RuntimeTarget<8>>> RC(reader); RC.addReflectionInfo({ binaryFilename, fieldSection, associatedTypeSection, builtinTypeSection, typeRefSection, reflectionStringsSection, }); // Dump everything RC.dumpAllSections(std::cout); return EXIT_SUCCESS; }