blob: 03c454bf3efab14eb0460cfcb386eb0f76524b65 [file] [log] [blame]
//===-- ObjectFileCOFF.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 "ObjectFileCOFF.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Utility/LLDBLog.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatAdapters.h"
using namespace lldb;
using namespace lldb_private;
using namespace llvm;
using namespace llvm::object;
static bool IsCOFFObjectFile(const DataBufferSP &data) {
return identify_magic(toStringRef(data->GetData())) ==
file_magic::coff_object;
}
LLDB_PLUGIN_DEFINE(ObjectFileCOFF)
char ObjectFileCOFF::ID;
ObjectFileCOFF::~ObjectFileCOFF() = default;
void ObjectFileCOFF::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance,
CreateMemoryInstance, GetModuleSpecifications);
}
void ObjectFileCOFF::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
}
lldb_private::ObjectFile *
ObjectFileCOFF::CreateInstance(const ModuleSP &module_sp, DataBufferSP data_sp,
offset_t data_offset, const FileSpec *file,
offset_t file_offset, offset_t length) {
Log *log = GetLog(LLDBLog::Object);
if (!data_sp) {
data_sp = MapFileData(*file, length, file_offset);
if (!data_sp) {
LLDB_LOG(log,
"Failed to create ObjectFileCOFF instance: cannot read file {0}",
file->GetPath());
return nullptr;
}
data_offset = 0;
}
assert(data_sp && "must have mapped file at this point");
if (!IsCOFFObjectFile(data_sp))
return nullptr;
if (data_sp->GetByteSize() < length) {
data_sp = MapFileData(*file, length, file_offset);
if (!data_sp) {
LLDB_LOG(log,
"Failed to create ObjectFileCOFF instance: cannot read file {0}",
file->GetPath());
return nullptr;
}
data_offset = 0;
}
MemoryBufferRef buffer{toStringRef(data_sp->GetData()),
file->GetFilename().GetStringRef()};
Expected<std::unique_ptr<Binary>> binary = createBinary(buffer);
if (!binary) {
LLDB_LOG_ERROR(log, binary.takeError(),
"Failed to create binary for file ({1}): {0}",
file->GetPath());
return nullptr;
}
LLDB_LOG(log, "ObjectFileCOFF::ObjectFileCOFF module = {1} ({2}), file = {3}",
module_sp.get(), module_sp->GetSpecificationDescription(),
file->GetPath());
return new ObjectFileCOFF(unique_dyn_cast<COFFObjectFile>(std::move(*binary)),
module_sp, data_sp, data_offset, file, file_offset,
length);
}
lldb_private::ObjectFile *ObjectFileCOFF::CreateMemoryInstance(
const ModuleSP &module_sp, WritableDataBufferSP data_sp,
const ProcessSP &process_sp, addr_t header) {
// FIXME: do we need to worry about construction from a memory region?
return nullptr;
}
size_t ObjectFileCOFF::GetModuleSpecifications(
const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
offset_t file_offset, offset_t length, ModuleSpecList &specs) {
if (!IsCOFFObjectFile(data_sp))
return 0;
MemoryBufferRef buffer{toStringRef(data_sp->GetData()),
file.GetFilename().GetStringRef()};
Expected<std::unique_ptr<Binary>> binary = createBinary(buffer);
if (!binary) {
Log *log = GetLog(LLDBLog::Object);
LLDB_LOG_ERROR(log, binary.takeError(),
"Failed to create binary for file ({1}): {0}",
file.GetFilename());
return 0;
}
std::unique_ptr<COFFObjectFile> object =
unique_dyn_cast<COFFObjectFile>(std::move(*binary));
switch (static_cast<COFF::MachineTypes>(object->getMachine())) {
case COFF::IMAGE_FILE_MACHINE_I386:
specs.Append(ModuleSpec(file, ArchSpec("i686-unknown-windows-msvc")));
return 1;
case COFF::IMAGE_FILE_MACHINE_AMD64:
specs.Append(ModuleSpec(file, ArchSpec("x86_64-unknown-windows-msvc")));
return 1;
case COFF::IMAGE_FILE_MACHINE_ARMNT:
specs.Append(ModuleSpec(file, ArchSpec("armv7-unknown-windows-msvc")));
return 1;
case COFF::IMAGE_FILE_MACHINE_ARM64:
specs.Append(ModuleSpec(file, ArchSpec("aarch64-unknown-windows-msvc")));
return 1;
default:
return 0;
}
}
void ObjectFileCOFF::Dump(Stream *stream) {
ModuleSP module(GetModule());
if (!module)
return;
std::lock_guard<std::recursive_mutex> guard(module->GetMutex());
stream->Printf("%p: ", static_cast<void *>(this));
stream->Indent();
stream->PutCString("ObjectFileCOFF");
*stream << ", file = '" << m_file
<< "', arch = " << GetArchitecture().GetArchitectureName() << '\n';
if (SectionList *sections = GetSectionList())
sections->Dump(stream->AsRawOstream(), stream->GetIndentLevel(), nullptr,
true, std::numeric_limits<uint32_t>::max());
}
uint32_t ObjectFileCOFF::GetAddressByteSize() const {
return const_cast<ObjectFileCOFF *>(this)->GetArchitecture().GetAddressByteSize();
}
ArchSpec ObjectFileCOFF::GetArchitecture() {
switch (static_cast<COFF::MachineTypes>(m_object->getMachine())) {
case COFF::IMAGE_FILE_MACHINE_I386:
return ArchSpec("i686-unknown-windows-msvc");
case COFF::IMAGE_FILE_MACHINE_AMD64:
return ArchSpec("x86_64-unknown-windows-msvc");
case COFF::IMAGE_FILE_MACHINE_ARMNT:
return ArchSpec("armv7-unknown-windows-msvc");
case COFF::IMAGE_FILE_MACHINE_ARM64:
return ArchSpec("aarch64-unknown-windows-msvc");
default:
return ArchSpec();
}
}
void ObjectFileCOFF::CreateSections(lldb_private::SectionList &sections) {
if (m_sections_up)
return;
m_sections_up = std::make_unique<SectionList>();
ModuleSP module(GetModule());
if (!module)
return;
std::lock_guard<std::recursive_mutex> guard(module->GetMutex());
auto SectionType = [](StringRef Name,
const coff_section *Section) -> lldb::SectionType {
lldb::SectionType type =
StringSwitch<lldb::SectionType>(Name)
// DWARF Debug Sections
.Case(".debug_abbrev", eSectionTypeDWARFDebugAbbrev)
.Case(".debug_info", eSectionTypeDWARFDebugInfo)
.Case(".debug_line", eSectionTypeDWARFDebugLine)
.Case(".debug_pubnames", eSectionTypeDWARFDebugPubNames)
.Case(".debug_pubtypes", eSectionTypeDWARFDebugPubTypes)
.Case(".debug_str", eSectionTypeDWARFDebugStr)
// CodeView Debug Sections: .debug$S, .debug$T
.StartsWith(".debug$", eSectionTypeDebug)
.Case("clangast", eSectionTypeOther)
.Default(eSectionTypeInvalid);
if (type != eSectionTypeInvalid)
return type;
if (Section->Characteristics & COFF::IMAGE_SCN_CNT_CODE)
return eSectionTypeCode;
if (Section->Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA)
return eSectionTypeData;
if (Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
return Section->SizeOfRawData ? eSectionTypeData : eSectionTypeZeroFill;
return eSectionTypeOther;
};
auto Permissions = [](const object::coff_section *Section) -> uint32_t {
uint32_t permissions = 0;
if (Section->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE)
permissions |= lldb::ePermissionsExecutable;
if (Section->Characteristics & COFF::IMAGE_SCN_MEM_READ)
permissions |= lldb::ePermissionsReadable;
if (Section->Characteristics & COFF::IMAGE_SCN_MEM_WRITE)
permissions |= lldb::ePermissionsWritable;
return permissions;
};
for (const auto &SecRef : m_object->sections()) {
const auto COFFSection = m_object->getCOFFSection(SecRef);
llvm::Expected<StringRef> Name = SecRef.getName();
StringRef SectionName = Name ? *Name : COFFSection->Name;
if (!Name)
consumeError(Name.takeError());
SectionSP section =
std::make_unique<Section>(module, this,
static_cast<user_id_t>(SecRef.getIndex()),
ConstString(SectionName),
SectionType(SectionName, COFFSection),
COFFSection->VirtualAddress,
COFFSection->VirtualSize,
COFFSection->PointerToRawData,
COFFSection->SizeOfRawData,
COFFSection->getAlignment(),
0);
section->SetPermissions(Permissions(COFFSection));
m_sections_up->AddSection(section);
sections.AddSection(section);
}
}
void ObjectFileCOFF::ParseSymtab(lldb_private::Symtab &symtab) {
Log *log = GetLog(LLDBLog::Object);
SectionList *sections = GetSectionList();
symtab.Reserve(symtab.GetNumSymbols() + m_object->getNumberOfSymbols());
auto SymbolType = [](const COFFSymbolRef &Symbol) -> lldb::SymbolType {
if (Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION)
return eSymbolTypeCode;
if (Symbol.getBaseType() == COFF::IMAGE_SYM_TYPE_NULL &&
Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_NULL)
return eSymbolTypeData;
return eSymbolTypeInvalid;
};
for (const auto &SymRef : m_object->symbols()) {
const auto COFFSymRef = m_object->getCOFFSymbol(SymRef);
Expected<StringRef> NameOrErr = SymRef.getName();
if (auto error = NameOrErr.takeError()) {
LLDB_LOG(log, "ObjectFileCOFF: failed to get symbol name: {0}",
llvm::fmt_consume(std::move(error)));
continue;
}
Symbol symbol;
symbol.GetMangled().SetValue(ConstString(*NameOrErr));
int16_t SecIdx = static_cast<int16_t>(COFFSymRef.getSectionNumber());
if (SecIdx == COFF::IMAGE_SYM_ABSOLUTE) {
symbol.GetAddressRef() = Address{COFFSymRef.getValue()};
symbol.SetType(eSymbolTypeAbsolute);
} else if (SecIdx >= 1) {
symbol.GetAddressRef() = Address(sections->GetSectionAtIndex(SecIdx - 1),
COFFSymRef.getValue());
symbol.SetType(SymbolType(COFFSymRef));
}
symtab.AddSymbol(symbol);
}
LLDB_LOG(log, "ObjectFileCOFF::ParseSymtab processed {0} symbols",
m_object->getNumberOfSymbols());
}
bool ObjectFileCOFF::ParseHeader() {
ModuleSP module(GetModule());
if (!module)
return false;
std::lock_guard<std::recursive_mutex> guard(module->GetMutex());
m_data.SetByteOrder(eByteOrderLittle);
m_data.SetAddressByteSize(GetAddressByteSize());
return true;
}