//===-- ObjectFileXCOFF.cpp //-------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ObjectFileXCOFF.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Progress.h" #include "lldb/Core/Section.h" #include "lldb/Host/FileSystem.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/FileSpecList.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RangeMap.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/XCOFF.h" #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Support/MemoryBuffer.h" #include #include #include #include using namespace llvm; using namespace lldb; using namespace lldb_private; LLDB_PLUGIN_DEFINE(ObjectFileXCOFF) // FIXME: target 64bit at this moment. // Static methods. void ObjectFileXCOFF::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, CreateMemoryInstance, GetModuleSpecifications); } void ObjectFileXCOFF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, DataBufferSP data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length) { if (!data_sp) { data_sp = MapFileData(*file, length, file_offset); if (!data_sp) return nullptr; data_offset = 0; } if (!ObjectFileXCOFF::MagicBytesMatch(data_sp, data_offset, length)) return nullptr; // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = MapFileData(*file, length, file_offset); if (!data_sp) return nullptr; data_offset = 0; } auto objfile_up = std::make_unique( module_sp, data_sp, data_offset, file, file_offset, length); if (!objfile_up) return nullptr; // Cache xcoff binary. if (!objfile_up->CreateBinary()) return nullptr; if (!objfile_up->ParseHeader()) return nullptr; return objfile_up.release(); } bool ObjectFileXCOFF::CreateBinary() { if (m_binary) return true; Log *log = GetLog(LLDBLog::Object); auto memory_ref = llvm::MemoryBufferRef(toStringRef(m_data.GetData()), m_file.GetFilename().GetStringRef()); llvm::file_magic magic = llvm::identify_magic(memory_ref.getBuffer()); auto binary = llvm::object::ObjectFile::createObjectFile(memory_ref, magic); if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", m_file); return false; } // Make sure we only handle XCOFF format. m_binary = llvm::unique_dyn_cast(std::move(*binary)); if (!m_binary) return false; LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", this, GetModule().get(), GetModule()->GetSpecificationDescription(), m_file.GetPath(), m_binary.get()); return true; } ObjectFile *ObjectFileXCOFF::CreateMemoryInstance( const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { return nullptr; } size_t ObjectFileXCOFF::GetModuleSpecifications( const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); if (ObjectFileXCOFF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); ModuleSpec spec(file, arch_spec); spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); specs.Append(spec); } return specs.GetSize() - initial_count; } static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { switch (magic) { case XCOFF::XCOFF32: return sizeof(struct llvm::object::XCOFFFileHeader32); break; case XCOFF::XCOFF64: return sizeof(struct llvm::object::XCOFFFileHeader64); break; default: break; } return 0; } bool ObjectFileXCOFF::MagicBytesMatch(DataBufferSP &data_sp, lldb::addr_t data_offset, lldb::addr_t data_length) { lldb_private::DataExtractor data; data.SetData(data_sp, data_offset, data_length); // Need to set this as XCOFF is only compatible with Big Endian data.SetByteOrder(eByteOrderBig); lldb::offset_t offset = 0; uint16_t magic = data.GetU16(&offset); return XCOFFHeaderSizeFromMagic(magic) != 0; } bool ObjectFileXCOFF::ParseHeader() { if (m_binary->is64Bit()) return m_binary->fileHeader64()->Magic == XCOFF::XCOFF64; return m_binary->fileHeader32()->Magic == XCOFF::XCOFF32; } ByteOrder ObjectFileXCOFF::GetByteOrder() const { return eByteOrderBig; } bool ObjectFileXCOFF::IsExecutable() const { return true; } uint32_t ObjectFileXCOFF::GetAddressByteSize() const { if (m_binary->is64Bit()) return 8; return 4; } AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { return AddressClass::eUnknown; } static lldb::SymbolType MapSymbolType(llvm::object::SymbolRef::Type sym_type) { switch (sym_type) { case llvm::object::SymbolRef::ST_Function: return lldb::eSymbolTypeCode; case llvm::object::SymbolRef::ST_Data: return lldb::eSymbolTypeData; case llvm::object::SymbolRef::ST_File: return lldb::eSymbolTypeSourceFile; default: return lldb::eSymbolTypeInvalid; } } void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { Log *log = GetLog(LLDBLog::Object); SectionList *sectionList = GetSectionList(); for (const auto &symbol_ref : m_binary->symbols()) { llvm::object::XCOFFSymbolRef xcoff_sym_ref(symbol_ref); llvm::Expected name_or_err = xcoff_sym_ref.getName(); if (!name_or_err) { LLDB_LOG_ERROR(log, name_or_err.takeError(), "Unable to extract name from the xcoff symbol ref object"); continue; } llvm::StringRef symbolName = name_or_err.get(); // Remove the . prefix added during compilation. This prefix is usually // added to differentiate between reference to the code and function // descriptor. For instance, Adding .func will only allow user to put bp on // .func, which is not known to the user, instead of func. llvm::StringRef name_no_dot = symbolName.starts_with(".") ? symbolName.drop_front() : symbolName; auto storageClass = xcoff_sym_ref.getStorageClass(); // C_HIDEXT symbols are not needed to be exposed, with the exception of TOC // which is responsible for storing references to global data if (storageClass == XCOFF::C_HIDEXT && symbolName != "TOC") { // Zero or muliple aux entries may suggest ambiguous data if (xcoff_sym_ref.getNumberOfAuxEntries() != 1) continue; auto aux_csect_or_err = xcoff_sym_ref.getXCOFFCsectAuxRef(); if (!aux_csect_or_err) { LLDB_LOG_ERROR(log, aux_csect_or_err.takeError(), "Unable to access xcoff csect aux ref object"); continue; } const llvm::object::XCOFFCsectAuxRef csect_aux = aux_csect_or_err.get(); // Only add hidden ext entries which come under Program Code, skip others // as they are not useful as debugging data. if (csect_aux.getStorageMappingClass() != XCOFF::XMC_PR) continue; // This does not apply to 32-bit, // Only add csect symbols identified by the aux entry, as they are // needed to reference section information. Skip others if (m_binary->is64Bit()) if (csect_aux.getAuxType64() != XCOFF::AUX_CSECT) continue; } Symbol symbol; symbol.GetMangled().SetValue(ConstString(name_no_dot)); int16_t sectionNumber = xcoff_sym_ref.getSectionNumber(); // Note that XCOFF section headers are numbered from 1 and not 0. size_t sectionIndex = static_cast(sectionNumber - 1); if (sectionNumber > 0) { if (sectionIndex < sectionList->GetSize()) { lldb::SectionSP section_sp = sectionList->GetSectionAtIndex(sectionIndex); if (!section_sp || section_sp->GetFileAddress() == LLDB_INVALID_ADDRESS) continue; lldb::addr_t file_addr = section_sp->GetFileAddress(); lldb::addr_t symbolValue = xcoff_sym_ref.getValue(); if (symbolValue < file_addr) continue; symbol.GetAddressRef() = Address(section_sp, symbolValue - file_addr); } } Expected sym_type_or_err = symbol_ref.getType(); if (!sym_type_or_err) { LLDB_LOG_ERROR(log, sym_type_or_err.takeError(), "Unable to access xcoff symbol type"); continue; } symbol.SetType(MapSymbolType(sym_type_or_err.get())); lldb_symtab.AddSymbol(symbol); } } bool ObjectFileXCOFF::IsStripped() { return false; } void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { if (m_sections_up) return; m_sections_up = std::make_unique(); if (m_binary->is64Bit()) CreateSectionsWithBitness(unified_section_list); else CreateSectionsWithBitness(unified_section_list); } template static auto GetSections(llvm::object::XCOFFObjectFile *binary) { if constexpr (T::Is64Bit) return binary->sections64(); else return binary->sections32(); } template void ObjectFileXCOFF::CreateSectionsWithBitness( SectionList &unified_section_list) { ModuleSP module_sp(GetModule()); if (!module_sp) return; std::lock_guard guard(module_sp->GetMutex()); int idx = 0; for (const typename T::SectionHeader §ion : GetSections(m_binary.get())) { ConstString const_sect_name(section.Name); SectionType section_type = lldb::eSectionTypeOther; if (section.Flags & XCOFF::STYP_TEXT) section_type = eSectionTypeCode; else if (section.Flags & XCOFF::STYP_DATA) section_type = eSectionTypeData; else if (section.Flags & XCOFF::STYP_BSS) section_type = eSectionTypeZeroFill; else if (section.Flags & XCOFF::STYP_DWARF) { section_type = llvm::StringSwitch(section.Name) .Case(".dwinfo", eSectionTypeDWARFDebugInfo) .Case(".dwline", eSectionTypeDWARFDebugLine) .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) .Case(".dwrnges", eSectionTypeDWARFDebugRanges) .Default(eSectionTypeInvalid); } SectionSP section_sp(new Section( module_sp, this, ++idx, const_sect_name, section_type, section.VirtualAddress, section.SectionSize, section.FileOffsetToRawData, section.SectionSize, 0, section.Flags)); uint32_t permissions = ePermissionsReadable; if (section.Flags & (XCOFF::STYP_DATA | XCOFF::STYP_BSS)) permissions |= ePermissionsWritable; if (section.Flags & XCOFF::STYP_TEXT) permissions |= ePermissionsExecutable; section_sp->SetPermissions(permissions); m_sections_up->AddSection(section_sp); unified_section_list.AddSection(section_sp); } } void ObjectFileXCOFF::Dump(Stream *s) {} ArchSpec ObjectFileXCOFF::GetArchitecture() { ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); return arch_spec; } UUID ObjectFileXCOFF::GetUUID() { return UUID(); } uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { return 0; } ObjectFile::Type ObjectFileXCOFF::CalculateType() { const auto flags = m_binary->is64Bit() ? m_binary->fileHeader64()->Flags : m_binary->fileHeader32()->Flags; if (flags & XCOFF::F_EXEC) return eTypeExecutable; else if (flags & XCOFF::F_SHROBJ) return eTypeSharedLibrary; return eTypeUnknown; } ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; } lldb::WritableDataBufferSP ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, uint64_t Offset) { return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, Offset); } ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, DataBufferSP data_sp, lldb::offset_t data_offset, const FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length) : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) { if (file) m_file = *file; } ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, DataBufferSP header_data_sp, const lldb::ProcessSP &process_sp, addr_t header_addr) : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) {}