Some plugins were returning the number of specifications they have added, while others were returning the total final number. Particularly devious plugins (Minidump) were clearing the specification list altogether. This resulted in nondeterministic failures (depending on plugin ininitialization order) in TestSBModule. This PR defines the problem away by having each plugin only return the specifications it is responsible for. If the caller wants to merge them, it is free to do so. This *might* be slighly less efficient, but this is hardly hot code. I'm not touching the ObjectFile::GetModuleSpecifications function (the caller of all these functions) as the PR is big enough, although the same approach might be warranted there as well. Fixes https://github.com/llvm/llvm-project/issues/178625.
421 lines
14 KiB
C++
421 lines
14 KiB
C++
//===-- 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/DataExtractor.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 <algorithm>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <unordered_map>
|
|
|
|
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,
|
|
DataExtractorSP extractor_sp,
|
|
lldb::offset_t data_offset,
|
|
const lldb_private::FileSpec *file,
|
|
lldb::offset_t file_offset,
|
|
lldb::offset_t length) {
|
|
if (!extractor_sp || !extractor_sp->HasData()) {
|
|
DataBufferSP data_sp = MapFileData(*file, length, file_offset);
|
|
if (!data_sp)
|
|
return nullptr;
|
|
data_offset = 0;
|
|
extractor_sp = std::make_shared<lldb_private::DataExtractor>(data_sp);
|
|
}
|
|
if (!ObjectFileXCOFF::MagicBytesMatch(extractor_sp, data_offset, length))
|
|
return nullptr;
|
|
// Update the data to contain the entire file if it doesn't already
|
|
if (extractor_sp->GetByteSize() < length) {
|
|
DataBufferSP data_sp = MapFileData(*file, length, file_offset);
|
|
if (!data_sp)
|
|
return nullptr;
|
|
data_offset = 0;
|
|
extractor_sp = std::make_shared<lldb_private::DataExtractor>(data_sp);
|
|
}
|
|
auto objfile_up = std::make_unique<ObjectFileXCOFF>(
|
|
module_sp, extractor_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_nsp->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<llvm::object::XCOFFObjectFile>(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;
|
|
}
|
|
|
|
ModuleSpecList ObjectFileXCOFF::GetModuleSpecifications(
|
|
const lldb_private::FileSpec &file, lldb::DataExtractorSP &extractor_sp,
|
|
lldb::offset_t data_offset, lldb::offset_t file_offset,
|
|
lldb::offset_t length) {
|
|
if (!extractor_sp || !extractor_sp->HasData())
|
|
return {};
|
|
|
|
ModuleSpecList specs;
|
|
if (ObjectFileXCOFF::MagicBytesMatch(extractor_sp, 0,
|
|
extractor_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;
|
|
}
|
|
|
|
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(DataExtractorSP &extractor_sp,
|
|
lldb::addr_t data_offset,
|
|
lldb::addr_t data_length) {
|
|
DataExtractorSP magic_extractor_sp =
|
|
extractor_sp->GetSubsetExtractorSP(data_offset);
|
|
// Need to set this as XCOFF is only compatible with Big Endian
|
|
magic_extractor_sp->SetByteOrder(eByteOrderBig);
|
|
lldb::offset_t offset = 0;
|
|
uint16_t magic = magic_extractor_sp->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<llvm::StringRef> 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<size_t>(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<llvm::object::SymbolRef::Type> 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<SectionList>();
|
|
if (m_binary->is64Bit())
|
|
CreateSectionsWithBitness<XCOFF64>(unified_section_list);
|
|
else
|
|
CreateSectionsWithBitness<XCOFF32>(unified_section_list);
|
|
}
|
|
|
|
template <typename T>
|
|
static auto GetSections(llvm::object::XCOFFObjectFile *binary) {
|
|
if constexpr (T::Is64Bit)
|
|
return binary->sections64();
|
|
else
|
|
return binary->sections32();
|
|
}
|
|
|
|
template <typename T>
|
|
void ObjectFileXCOFF::CreateSectionsWithBitness(
|
|
SectionList &unified_section_list) {
|
|
ModuleSP module_sp(GetModule());
|
|
if (!module_sp)
|
|
return;
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
|
|
|
|
int idx = 0;
|
|
for (const typename T::SectionHeader §ion :
|
|
GetSections<T>(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<SectionType>(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,
|
|
DataExtractorSP extractor_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, extractor_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,
|
|
std::make_shared<lldb_private::DataExtractor>(header_data_sp)) {}
|