This changes VirtualDataExtractor's GetByteSize to return the virtual byte size of the buffer (external users only understand the data contents in terms of the virtual sizes & offsets). There are check methods in DataExtractor that check they are not going off the end of a buffer, they usually use the BytesLeft() method. There are a couple of callers of BytesLeft() externally, but it is predominantly an internal use API. I have BytesLeft() use the physical size of the buffer, not the virtual size, for the benefit of the DataExtractor methods. (and to avoid duplicating all of them down in VirtualDataExtractor) Another problem is the we call SetData on DataExtractorSP's (e.g. see the ObjectFile ctor) with the DataBuffer it already has, an offset of 0, and the GetByteSize. A no-op for a DataExtractor that is already using that DataBuffer. But SetData would try to use that length as a physical size, and truncate the buffer that the DataExtractor would accept. I added VirtualDataExtractor subclass methods for the SetData's, detect (1) data being added to an uninitialized DataExtractor, (2) the same data / offset / length as currently being used is added to the DataExtractor (a no-op), or (3) we're genuinely changing the data source or setting an offset / length that is different. This final case we're not ready to handle today, I added asserts for them so we can catch it in debug builds, and then I clear the LookupTable and add a no-op entry so this extractor will behave like a plain DataExtractor -- because I don't know better to do. If we genuinely need to handle this case, and I'm pretty sure we don't need to, I'd have to assume that we're taking a subset of the original data source (an offset & length), so we'd need to update all of the LookupTable entries to reflect the new offsets, and remove entries that are no longer referring to the subsetted range. I'll leave that until there's any evidence it's actually needed. rdar://148939795
422 lines
14 KiB
C++
422 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;
|
|
}
|
|
|
|
size_t 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, lldb_private::ModuleSpecList &specs) {
|
|
const size_t initial_count = specs.GetSize();
|
|
|
|
if (!extractor_sp || !extractor_sp->HasData())
|
|
return 0;
|
|
|
|
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.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(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)) {}
|