
If not set, the address byte size was implied to be the one of the host process. This allows reverting the functional change from 31087b2ae9154, since now PECOFF does the same as ELF and MachO wrt setting both byte order and address size on m_data within ParseHeader. Differential Revision: https://reviews.llvm.org/D71108
1266 lines
47 KiB
C++
1266 lines
47 KiB
C++
//===-- ObjectFilePECOFF.cpp ------------------------------------*- C++ -*-===//
|
|
//
|
|
// 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 "ObjectFilePECOFF.h"
|
|
#include "PECallFrameInfo.h"
|
|
#include "WindowsMiniDump.h"
|
|
|
|
#include "lldb/Core/FileSpecList.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/ModuleSpec.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Core/Section.h"
|
|
#include "lldb/Core/StreamFile.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/SectionLoadList.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/Utility/DataBufferHeap.h"
|
|
#include "lldb/Utility/FileSpec.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
#include "lldb/Utility/Timer.h"
|
|
#include "lldb/Utility/UUID.h"
|
|
#include "llvm/BinaryFormat/COFF.h"
|
|
|
|
#include "llvm/Object/COFFImportFile.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
|
|
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
|
|
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
|
|
#define OPT_HEADER_MAGIC_PE32 0x010b
|
|
#define OPT_HEADER_MAGIC_PE32_PLUS 0x020b
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
struct CVInfoPdb70 {
|
|
// 16-byte GUID
|
|
struct _Guid {
|
|
llvm::support::ulittle32_t Data1;
|
|
llvm::support::ulittle16_t Data2;
|
|
llvm::support::ulittle16_t Data3;
|
|
uint8_t Data4[8];
|
|
} Guid;
|
|
|
|
llvm::support::ulittle32_t Age;
|
|
};
|
|
|
|
static UUID GetCoffUUID(llvm::object::COFFObjectFile *coff_obj) {
|
|
if (!coff_obj)
|
|
return UUID();
|
|
|
|
const llvm::codeview::DebugInfo *pdb_info = nullptr;
|
|
llvm::StringRef pdb_file;
|
|
|
|
// This part is similar with what has done in minidump parser.
|
|
if (!coff_obj->getDebugPDBInfo(pdb_info, pdb_file) && pdb_info) {
|
|
if (pdb_info->PDB70.CVSignature == llvm::OMF::Signature::PDB70) {
|
|
using llvm::support::endian::read16be;
|
|
using llvm::support::endian::read32be;
|
|
|
|
const uint8_t *sig = pdb_info->PDB70.Signature;
|
|
struct CVInfoPdb70 info;
|
|
info.Guid.Data1 = read32be(sig);
|
|
sig += 4;
|
|
info.Guid.Data2 = read16be(sig);
|
|
sig += 2;
|
|
info.Guid.Data3 = read16be(sig);
|
|
sig += 2;
|
|
memcpy(info.Guid.Data4, sig, 8);
|
|
|
|
// Return 20-byte UUID if the Age is not zero
|
|
if (pdb_info->PDB70.Age) {
|
|
info.Age = read32be(&pdb_info->PDB70.Age);
|
|
return UUID::fromOptionalData(&info, sizeof(info));
|
|
}
|
|
// Otherwise return 16-byte GUID
|
|
return UUID::fromOptionalData(&info.Guid, sizeof(info.Guid));
|
|
}
|
|
}
|
|
|
|
return UUID();
|
|
}
|
|
|
|
char ObjectFilePECOFF::ID;
|
|
|
|
void ObjectFilePECOFF::Initialize() {
|
|
PluginManager::RegisterPlugin(
|
|
GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
|
|
CreateMemoryInstance, GetModuleSpecifications, SaveCore);
|
|
}
|
|
|
|
void ObjectFilePECOFF::Terminate() {
|
|
PluginManager::UnregisterPlugin(CreateInstance);
|
|
}
|
|
|
|
lldb_private::ConstString ObjectFilePECOFF::GetPluginNameStatic() {
|
|
static ConstString g_name("pe-coff");
|
|
return g_name;
|
|
}
|
|
|
|
const char *ObjectFilePECOFF::GetPluginDescriptionStatic() {
|
|
return "Portable Executable and Common Object File Format object file reader "
|
|
"(32 and 64 bit)";
|
|
}
|
|
|
|
ObjectFile *ObjectFilePECOFF::CreateInstance(const lldb::ModuleSP &module_sp,
|
|
DataBufferSP &data_sp,
|
|
lldb::offset_t data_offset,
|
|
const lldb_private::FileSpec *file_p,
|
|
lldb::offset_t file_offset,
|
|
lldb::offset_t length) {
|
|
FileSpec file = file_p ? *file_p : FileSpec();
|
|
if (!data_sp) {
|
|
data_sp = MapFileData(file, length, file_offset);
|
|
if (!data_sp)
|
|
return nullptr;
|
|
data_offset = 0;
|
|
}
|
|
|
|
if (!ObjectFilePECOFF::MagicBytesMatch(data_sp))
|
|
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;
|
|
}
|
|
|
|
auto objfile_up = std::make_unique<ObjectFilePECOFF>(
|
|
module_sp, data_sp, data_offset, file_p, file_offset, length);
|
|
if (!objfile_up || !objfile_up->ParseHeader())
|
|
return nullptr;
|
|
|
|
// Cache coff binary.
|
|
if (!objfile_up->CreateBinary())
|
|
return nullptr;
|
|
|
|
return objfile_up.release();
|
|
}
|
|
|
|
ObjectFile *ObjectFilePECOFF::CreateMemoryInstance(
|
|
const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
|
|
const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) {
|
|
if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp))
|
|
return nullptr;
|
|
auto objfile_up = std::make_unique<ObjectFilePECOFF>(
|
|
module_sp, data_sp, process_sp, header_addr);
|
|
if (objfile_up.get() && objfile_up->ParseHeader()) {
|
|
return objfile_up.release();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
size_t ObjectFilePECOFF::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 (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp))
|
|
return initial_count;
|
|
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
|
|
|
|
auto binary = llvm::object::createBinary(file.GetPath());
|
|
|
|
if (!binary) {
|
|
LLDB_LOG_ERROR(log, binary.takeError(),
|
|
"Failed to create binary for file ({1}): {0}", file);
|
|
return initial_count;
|
|
}
|
|
|
|
if (!binary->getBinary()->isCOFF() &&
|
|
!binary->getBinary()->isCOFFImportFile())
|
|
return initial_count;
|
|
|
|
auto COFFObj =
|
|
llvm::cast<llvm::object::COFFObjectFile>(binary->getBinary());
|
|
|
|
ModuleSpec module_spec(file);
|
|
ArchSpec &spec = module_spec.GetArchitecture();
|
|
lldb_private::UUID &uuid = module_spec.GetUUID();
|
|
if (!uuid.IsValid())
|
|
uuid = GetCoffUUID(COFFObj);
|
|
|
|
switch (COFFObj->getMachine()) {
|
|
case MachineAmd64:
|
|
spec.SetTriple("x86_64-pc-windows");
|
|
specs.Append(module_spec);
|
|
break;
|
|
case MachineX86:
|
|
spec.SetTriple("i386-pc-windows");
|
|
specs.Append(module_spec);
|
|
spec.SetTriple("i686-pc-windows");
|
|
specs.Append(module_spec);
|
|
break;
|
|
case MachineArmNt:
|
|
spec.SetTriple("armv7-pc-windows");
|
|
specs.Append(module_spec);
|
|
break;
|
|
case MachineArm64:
|
|
spec.SetTriple("aarch64-pc-windows");
|
|
specs.Append(module_spec);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return specs.GetSize() - initial_count;
|
|
}
|
|
|
|
bool ObjectFilePECOFF::SaveCore(const lldb::ProcessSP &process_sp,
|
|
const lldb_private::FileSpec &outfile,
|
|
lldb_private::Status &error) {
|
|
return SaveMiniDump(process_sp, outfile, error);
|
|
}
|
|
|
|
bool ObjectFilePECOFF::MagicBytesMatch(DataBufferSP &data_sp) {
|
|
DataExtractor data(data_sp, eByteOrderLittle, 4);
|
|
lldb::offset_t offset = 0;
|
|
uint16_t magic = data.GetU16(&offset);
|
|
return magic == IMAGE_DOS_SIGNATURE;
|
|
}
|
|
|
|
lldb::SymbolType ObjectFilePECOFF::MapSymbolType(uint16_t coff_symbol_type) {
|
|
// TODO: We need to complete this mapping of COFF symbol types to LLDB ones.
|
|
// For now, here's a hack to make sure our function have types.
|
|
const auto complex_type =
|
|
coff_symbol_type >> llvm::COFF::SCT_COMPLEX_TYPE_SHIFT;
|
|
if (complex_type == llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION) {
|
|
return lldb::eSymbolTypeCode;
|
|
}
|
|
return lldb::eSymbolTypeInvalid;
|
|
}
|
|
|
|
bool ObjectFilePECOFF::CreateBinary() {
|
|
if (m_owningbin)
|
|
return true;
|
|
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
|
|
|
|
auto binary = llvm::object::createBinary(m_file.GetPath());
|
|
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 COFF format.
|
|
if (!binary->getBinary()->isCOFF() &&
|
|
!binary->getBinary()->isCOFFImportFile())
|
|
return false;
|
|
|
|
m_owningbin = OWNBINType(std::move(*binary));
|
|
LLDB_LOGF(log,
|
|
"%p ObjectFilePECOFF::CreateBinary() module = %p (%s), file = "
|
|
"%s, binary = %p (Bin = %p)",
|
|
static_cast<void *>(this), static_cast<void *>(GetModule().get()),
|
|
GetModule()->GetSpecificationDescription().c_str(),
|
|
m_file ? m_file.GetPath().c_str() : "<NULL>",
|
|
static_cast<void *>(m_owningbin.getPointer()),
|
|
static_cast<void *>(m_owningbin->getBinary()));
|
|
return true;
|
|
}
|
|
|
|
ObjectFilePECOFF::ObjectFilePECOFF(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),
|
|
m_dos_header(), m_coff_header(), m_sect_headers(),
|
|
m_entry_point_address(), m_deps_filespec(), m_owningbin() {
|
|
::memset(&m_dos_header, 0, sizeof(m_dos_header));
|
|
::memset(&m_coff_header, 0, sizeof(m_coff_header));
|
|
}
|
|
|
|
ObjectFilePECOFF::ObjectFilePECOFF(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),
|
|
m_dos_header(), m_coff_header(), m_sect_headers(),
|
|
m_entry_point_address(), m_deps_filespec(), m_owningbin() {
|
|
::memset(&m_dos_header, 0, sizeof(m_dos_header));
|
|
::memset(&m_coff_header, 0, sizeof(m_coff_header));
|
|
}
|
|
|
|
ObjectFilePECOFF::~ObjectFilePECOFF() {}
|
|
|
|
bool ObjectFilePECOFF::ParseHeader() {
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp) {
|
|
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
|
|
m_sect_headers.clear();
|
|
m_data.SetByteOrder(eByteOrderLittle);
|
|
lldb::offset_t offset = 0;
|
|
|
|
if (ParseDOSHeader(m_data, m_dos_header)) {
|
|
offset = m_dos_header.e_lfanew;
|
|
uint32_t pe_signature = m_data.GetU32(&offset);
|
|
if (pe_signature != IMAGE_NT_SIGNATURE)
|
|
return false;
|
|
if (ParseCOFFHeader(m_data, &offset, m_coff_header)) {
|
|
if (m_coff_header.hdrsize > 0)
|
|
ParseCOFFOptionalHeader(&offset);
|
|
ParseSectionHeaders(offset);
|
|
}
|
|
m_data.SetAddressByteSize(GetAddressByteSize());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ObjectFilePECOFF::SetLoadAddress(Target &target, addr_t value,
|
|
bool value_is_offset) {
|
|
bool changed = false;
|
|
ModuleSP module_sp = GetModule();
|
|
if (module_sp) {
|
|
size_t num_loaded_sections = 0;
|
|
SectionList *section_list = GetSectionList();
|
|
if (section_list) {
|
|
if (!value_is_offset) {
|
|
value -= m_image_base;
|
|
}
|
|
|
|
const size_t num_sections = section_list->GetSize();
|
|
size_t sect_idx = 0;
|
|
|
|
for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
|
|
// Iterate through the object file sections to find all of the sections
|
|
// that have SHF_ALLOC in their flag bits.
|
|
SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
|
|
if (section_sp && !section_sp->IsThreadSpecific()) {
|
|
if (target.GetSectionLoadList().SetSectionLoadAddress(
|
|
section_sp, section_sp->GetFileAddress() + value))
|
|
++num_loaded_sections;
|
|
}
|
|
}
|
|
changed = num_loaded_sections > 0;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
ByteOrder ObjectFilePECOFF::GetByteOrder() const { return eByteOrderLittle; }
|
|
|
|
bool ObjectFilePECOFF::IsExecutable() const {
|
|
return (m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0;
|
|
}
|
|
|
|
uint32_t ObjectFilePECOFF::GetAddressByteSize() const {
|
|
if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32_PLUS)
|
|
return 8;
|
|
else if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32)
|
|
return 4;
|
|
return 4;
|
|
}
|
|
|
|
// NeedsEndianSwap
|
|
//
|
|
// Return true if an endian swap needs to occur when extracting data from this
|
|
// file.
|
|
bool ObjectFilePECOFF::NeedsEndianSwap() const {
|
|
#if defined(__LITTLE_ENDIAN__)
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
// ParseDOSHeader
|
|
bool ObjectFilePECOFF::ParseDOSHeader(DataExtractor &data,
|
|
dos_header_t &dos_header) {
|
|
bool success = false;
|
|
lldb::offset_t offset = 0;
|
|
success = data.ValidOffsetForDataOfSize(0, sizeof(dos_header));
|
|
|
|
if (success) {
|
|
dos_header.e_magic = data.GetU16(&offset); // Magic number
|
|
success = dos_header.e_magic == IMAGE_DOS_SIGNATURE;
|
|
|
|
if (success) {
|
|
dos_header.e_cblp = data.GetU16(&offset); // Bytes on last page of file
|
|
dos_header.e_cp = data.GetU16(&offset); // Pages in file
|
|
dos_header.e_crlc = data.GetU16(&offset); // Relocations
|
|
dos_header.e_cparhdr =
|
|
data.GetU16(&offset); // Size of header in paragraphs
|
|
dos_header.e_minalloc =
|
|
data.GetU16(&offset); // Minimum extra paragraphs needed
|
|
dos_header.e_maxalloc =
|
|
data.GetU16(&offset); // Maximum extra paragraphs needed
|
|
dos_header.e_ss = data.GetU16(&offset); // Initial (relative) SS value
|
|
dos_header.e_sp = data.GetU16(&offset); // Initial SP value
|
|
dos_header.e_csum = data.GetU16(&offset); // Checksum
|
|
dos_header.e_ip = data.GetU16(&offset); // Initial IP value
|
|
dos_header.e_cs = data.GetU16(&offset); // Initial (relative) CS value
|
|
dos_header.e_lfarlc =
|
|
data.GetU16(&offset); // File address of relocation table
|
|
dos_header.e_ovno = data.GetU16(&offset); // Overlay number
|
|
|
|
dos_header.e_res[0] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res[1] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res[2] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res[3] = data.GetU16(&offset); // Reserved words
|
|
|
|
dos_header.e_oemid =
|
|
data.GetU16(&offset); // OEM identifier (for e_oeminfo)
|
|
dos_header.e_oeminfo =
|
|
data.GetU16(&offset); // OEM information; e_oemid specific
|
|
dos_header.e_res2[0] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[1] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[2] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[3] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[4] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[5] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[6] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[7] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[8] = data.GetU16(&offset); // Reserved words
|
|
dos_header.e_res2[9] = data.GetU16(&offset); // Reserved words
|
|
|
|
dos_header.e_lfanew =
|
|
data.GetU32(&offset); // File address of new exe header
|
|
}
|
|
}
|
|
if (!success)
|
|
memset(&dos_header, 0, sizeof(dos_header));
|
|
return success;
|
|
}
|
|
|
|
// ParserCOFFHeader
|
|
bool ObjectFilePECOFF::ParseCOFFHeader(DataExtractor &data,
|
|
lldb::offset_t *offset_ptr,
|
|
coff_header_t &coff_header) {
|
|
bool success =
|
|
data.ValidOffsetForDataOfSize(*offset_ptr, sizeof(coff_header));
|
|
if (success) {
|
|
coff_header.machine = data.GetU16(offset_ptr);
|
|
coff_header.nsects = data.GetU16(offset_ptr);
|
|
coff_header.modtime = data.GetU32(offset_ptr);
|
|
coff_header.symoff = data.GetU32(offset_ptr);
|
|
coff_header.nsyms = data.GetU32(offset_ptr);
|
|
coff_header.hdrsize = data.GetU16(offset_ptr);
|
|
coff_header.flags = data.GetU16(offset_ptr);
|
|
}
|
|
if (!success)
|
|
memset(&coff_header, 0, sizeof(coff_header));
|
|
return success;
|
|
}
|
|
|
|
bool ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) {
|
|
bool success = false;
|
|
const lldb::offset_t end_offset = *offset_ptr + m_coff_header.hdrsize;
|
|
if (*offset_ptr < end_offset) {
|
|
success = true;
|
|
m_coff_header_opt.magic = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.major_linker_version = m_data.GetU8(offset_ptr);
|
|
m_coff_header_opt.minor_linker_version = m_data.GetU8(offset_ptr);
|
|
m_coff_header_opt.code_size = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.data_size = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.bss_size = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.entry = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.code_offset = m_data.GetU32(offset_ptr);
|
|
|
|
const uint32_t addr_byte_size = GetAddressByteSize();
|
|
|
|
if (*offset_ptr < end_offset) {
|
|
if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) {
|
|
// PE32 only
|
|
m_coff_header_opt.data_offset = m_data.GetU32(offset_ptr);
|
|
} else
|
|
m_coff_header_opt.data_offset = 0;
|
|
|
|
if (*offset_ptr < end_offset) {
|
|
m_coff_header_opt.image_base =
|
|
m_data.GetMaxU64(offset_ptr, addr_byte_size);
|
|
m_coff_header_opt.sect_alignment = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.file_alignment = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.major_os_system_version = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.minor_os_system_version = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.major_image_version = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.minor_image_version = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.major_subsystem_version = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.minor_subsystem_version = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.reserved1 = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.image_size = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.header_size = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.checksum = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.subsystem = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.dll_flags = m_data.GetU16(offset_ptr);
|
|
m_coff_header_opt.stack_reserve_size =
|
|
m_data.GetMaxU64(offset_ptr, addr_byte_size);
|
|
m_coff_header_opt.stack_commit_size =
|
|
m_data.GetMaxU64(offset_ptr, addr_byte_size);
|
|
m_coff_header_opt.heap_reserve_size =
|
|
m_data.GetMaxU64(offset_ptr, addr_byte_size);
|
|
m_coff_header_opt.heap_commit_size =
|
|
m_data.GetMaxU64(offset_ptr, addr_byte_size);
|
|
m_coff_header_opt.loader_flags = m_data.GetU32(offset_ptr);
|
|
uint32_t num_data_dir_entries = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.data_dirs.clear();
|
|
m_coff_header_opt.data_dirs.resize(num_data_dir_entries);
|
|
uint32_t i;
|
|
for (i = 0; i < num_data_dir_entries; i++) {
|
|
m_coff_header_opt.data_dirs[i].vmaddr = m_data.GetU32(offset_ptr);
|
|
m_coff_header_opt.data_dirs[i].vmsize = m_data.GetU32(offset_ptr);
|
|
}
|
|
|
|
m_image_base = m_coff_header_opt.image_base;
|
|
}
|
|
}
|
|
}
|
|
// Make sure we are on track for section data which follows
|
|
*offset_ptr = end_offset;
|
|
return success;
|
|
}
|
|
|
|
uint32_t ObjectFilePECOFF::GetRVA(const Address &addr) const {
|
|
return addr.GetFileAddress() - m_image_base;
|
|
}
|
|
|
|
Address ObjectFilePECOFF::GetAddress(uint32_t rva) {
|
|
SectionList *sect_list = GetSectionList();
|
|
if (!sect_list)
|
|
return Address(GetFileAddress(rva));
|
|
|
|
return Address(GetFileAddress(rva), sect_list);
|
|
}
|
|
|
|
lldb::addr_t ObjectFilePECOFF::GetFileAddress(uint32_t rva) const {
|
|
return m_image_base + rva;
|
|
}
|
|
|
|
DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) {
|
|
if (!size)
|
|
return {};
|
|
|
|
if (m_file) {
|
|
// A bit of a hack, but we intend to write to this buffer, so we can't
|
|
// mmap it.
|
|
auto buffer_sp = MapFileData(m_file, size, offset);
|
|
return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize());
|
|
}
|
|
ProcessSP process_sp(m_process_wp.lock());
|
|
DataExtractor data;
|
|
if (process_sp) {
|
|
auto data_up = std::make_unique<DataBufferHeap>(size, 0);
|
|
Status readmem_error;
|
|
size_t bytes_read =
|
|
process_sp->ReadMemory(m_image_base + offset, data_up->GetBytes(),
|
|
data_up->GetByteSize(), readmem_error);
|
|
if (bytes_read == size) {
|
|
DataBufferSP buffer_sp(data_up.release());
|
|
data.SetData(buffer_sp, 0, buffer_sp->GetByteSize());
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
DataExtractor ObjectFilePECOFF::ReadImageDataByRVA(uint32_t rva, size_t size) {
|
|
if (m_file) {
|
|
Address addr = GetAddress(rva);
|
|
SectionSP sect = addr.GetSection();
|
|
if (!sect)
|
|
return {};
|
|
rva = sect->GetFileOffset() + addr.GetOffset();
|
|
}
|
|
|
|
return ReadImageData(rva, size);
|
|
}
|
|
|
|
// ParseSectionHeaders
|
|
bool ObjectFilePECOFF::ParseSectionHeaders(
|
|
uint32_t section_header_data_offset) {
|
|
const uint32_t nsects = m_coff_header.nsects;
|
|
m_sect_headers.clear();
|
|
|
|
if (nsects > 0) {
|
|
const size_t section_header_byte_size = nsects * sizeof(section_header_t);
|
|
DataExtractor section_header_data =
|
|
ReadImageData(section_header_data_offset, section_header_byte_size);
|
|
|
|
lldb::offset_t offset = 0;
|
|
if (section_header_data.ValidOffsetForDataOfSize(
|
|
offset, section_header_byte_size)) {
|
|
m_sect_headers.resize(nsects);
|
|
|
|
for (uint32_t idx = 0; idx < nsects; ++idx) {
|
|
const void *name_data = section_header_data.GetData(&offset, 8);
|
|
if (name_data) {
|
|
memcpy(m_sect_headers[idx].name, name_data, 8);
|
|
m_sect_headers[idx].vmsize = section_header_data.GetU32(&offset);
|
|
m_sect_headers[idx].vmaddr = section_header_data.GetU32(&offset);
|
|
m_sect_headers[idx].size = section_header_data.GetU32(&offset);
|
|
m_sect_headers[idx].offset = section_header_data.GetU32(&offset);
|
|
m_sect_headers[idx].reloff = section_header_data.GetU32(&offset);
|
|
m_sect_headers[idx].lineoff = section_header_data.GetU32(&offset);
|
|
m_sect_headers[idx].nreloc = section_header_data.GetU16(&offset);
|
|
m_sect_headers[idx].nline = section_header_data.GetU16(&offset);
|
|
m_sect_headers[idx].flags = section_header_data.GetU32(&offset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return !m_sect_headers.empty();
|
|
}
|
|
|
|
llvm::StringRef ObjectFilePECOFF::GetSectionName(const section_header_t §) {
|
|
llvm::StringRef hdr_name(sect.name, llvm::array_lengthof(sect.name));
|
|
hdr_name = hdr_name.split('\0').first;
|
|
if (hdr_name.consume_front("/")) {
|
|
lldb::offset_t stroff;
|
|
if (!to_integer(hdr_name, stroff, 10))
|
|
return "";
|
|
lldb::offset_t string_file_offset =
|
|
m_coff_header.symoff + (m_coff_header.nsyms * 18) + stroff;
|
|
if (const char *name = m_data.GetCStr(&string_file_offset))
|
|
return name;
|
|
return "";
|
|
}
|
|
return hdr_name;
|
|
}
|
|
|
|
// GetNListSymtab
|
|
Symtab *ObjectFilePECOFF::GetSymtab() {
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp) {
|
|
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
|
|
if (m_symtab_up == nullptr) {
|
|
SectionList *sect_list = GetSectionList();
|
|
m_symtab_up.reset(new Symtab(this));
|
|
std::lock_guard<std::recursive_mutex> guard(m_symtab_up->GetMutex());
|
|
|
|
const uint32_t num_syms = m_coff_header.nsyms;
|
|
|
|
if (m_file && num_syms > 0 && m_coff_header.symoff > 0) {
|
|
const uint32_t symbol_size = 18;
|
|
const size_t symbol_data_size = num_syms * symbol_size;
|
|
// Include the 4-byte string table size at the end of the symbols
|
|
DataExtractor symtab_data =
|
|
ReadImageData(m_coff_header.symoff, symbol_data_size + 4);
|
|
lldb::offset_t offset = symbol_data_size;
|
|
const uint32_t strtab_size = symtab_data.GetU32(&offset);
|
|
if (strtab_size > 0) {
|
|
DataExtractor strtab_data = ReadImageData(
|
|
m_coff_header.symoff + symbol_data_size, strtab_size);
|
|
|
|
// First 4 bytes should be zeroed after strtab_size has been read,
|
|
// because it is used as offset 0 to encode a NULL string.
|
|
uint32_t *strtab_data_start = const_cast<uint32_t *>(
|
|
reinterpret_cast<const uint32_t *>(strtab_data.GetDataStart()));
|
|
strtab_data_start[0] = 0;
|
|
|
|
offset = 0;
|
|
std::string symbol_name;
|
|
Symbol *symbols = m_symtab_up->Resize(num_syms);
|
|
for (uint32_t i = 0; i < num_syms; ++i) {
|
|
coff_symbol_t symbol;
|
|
const uint32_t symbol_offset = offset;
|
|
const char *symbol_name_cstr = nullptr;
|
|
// If the first 4 bytes of the symbol string are zero, then they
|
|
// are followed by a 4-byte string table offset. Else these
|
|
// 8 bytes contain the symbol name
|
|
if (symtab_data.GetU32(&offset) == 0) {
|
|
// Long string that doesn't fit into the symbol table name, so
|
|
// now we must read the 4 byte string table offset
|
|
uint32_t strtab_offset = symtab_data.GetU32(&offset);
|
|
symbol_name_cstr = strtab_data.PeekCStr(strtab_offset);
|
|
symbol_name.assign(symbol_name_cstr);
|
|
} else {
|
|
// Short string that fits into the symbol table name which is 8
|
|
// bytes
|
|
offset += sizeof(symbol.name) - 4; // Skip remaining
|
|
symbol_name_cstr = symtab_data.PeekCStr(symbol_offset);
|
|
if (symbol_name_cstr == nullptr)
|
|
break;
|
|
symbol_name.assign(symbol_name_cstr, sizeof(symbol.name));
|
|
}
|
|
symbol.value = symtab_data.GetU32(&offset);
|
|
symbol.sect = symtab_data.GetU16(&offset);
|
|
symbol.type = symtab_data.GetU16(&offset);
|
|
symbol.storage = symtab_data.GetU8(&offset);
|
|
symbol.naux = symtab_data.GetU8(&offset);
|
|
symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str()));
|
|
if ((int16_t)symbol.sect >= 1) {
|
|
Address symbol_addr(sect_list->FindSectionByID(symbol.sect),
|
|
symbol.value);
|
|
symbols[i].GetAddressRef() = symbol_addr;
|
|
symbols[i].SetType(MapSymbolType(symbol.type));
|
|
}
|
|
|
|
if (symbol.naux > 0) {
|
|
i += symbol.naux;
|
|
offset += symbol_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read export header
|
|
if (coff_data_dir_export_table < m_coff_header_opt.data_dirs.size() &&
|
|
m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmsize > 0 &&
|
|
m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr > 0) {
|
|
export_directory_entry export_table;
|
|
uint32_t data_start =
|
|
m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr;
|
|
|
|
DataExtractor symtab_data = ReadImageDataByRVA(
|
|
data_start, m_coff_header_opt.data_dirs[0].vmsize);
|
|
lldb::offset_t offset = 0;
|
|
|
|
// Read export_table header
|
|
export_table.characteristics = symtab_data.GetU32(&offset);
|
|
export_table.time_date_stamp = symtab_data.GetU32(&offset);
|
|
export_table.major_version = symtab_data.GetU16(&offset);
|
|
export_table.minor_version = symtab_data.GetU16(&offset);
|
|
export_table.name = symtab_data.GetU32(&offset);
|
|
export_table.base = symtab_data.GetU32(&offset);
|
|
export_table.number_of_functions = symtab_data.GetU32(&offset);
|
|
export_table.number_of_names = symtab_data.GetU32(&offset);
|
|
export_table.address_of_functions = symtab_data.GetU32(&offset);
|
|
export_table.address_of_names = symtab_data.GetU32(&offset);
|
|
export_table.address_of_name_ordinals = symtab_data.GetU32(&offset);
|
|
|
|
bool has_ordinal = export_table.address_of_name_ordinals != 0;
|
|
|
|
lldb::offset_t name_offset = export_table.address_of_names - data_start;
|
|
lldb::offset_t name_ordinal_offset =
|
|
export_table.address_of_name_ordinals - data_start;
|
|
|
|
Symbol *symbols = m_symtab_up->Resize(export_table.number_of_names);
|
|
|
|
std::string symbol_name;
|
|
|
|
// Read each export table entry
|
|
for (size_t i = 0; i < export_table.number_of_names; ++i) {
|
|
uint32_t name_ordinal =
|
|
has_ordinal ? symtab_data.GetU16(&name_ordinal_offset) : i;
|
|
uint32_t name_address = symtab_data.GetU32(&name_offset);
|
|
|
|
const char *symbol_name_cstr =
|
|
symtab_data.PeekCStr(name_address - data_start);
|
|
symbol_name.assign(symbol_name_cstr);
|
|
|
|
lldb::offset_t function_offset = export_table.address_of_functions -
|
|
data_start +
|
|
sizeof(uint32_t) * name_ordinal;
|
|
uint32_t function_rva = symtab_data.GetU32(&function_offset);
|
|
|
|
Address symbol_addr(m_coff_header_opt.image_base + function_rva,
|
|
sect_list);
|
|
symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str()));
|
|
symbols[i].GetAddressRef() = symbol_addr;
|
|
symbols[i].SetType(lldb::eSymbolTypeCode);
|
|
symbols[i].SetDebug(true);
|
|
}
|
|
}
|
|
m_symtab_up->CalculateSymbolSizes();
|
|
}
|
|
}
|
|
return m_symtab_up.get();
|
|
}
|
|
|
|
std::unique_ptr<CallFrameInfo> ObjectFilePECOFF::CreateCallFrameInfo() {
|
|
if (coff_data_dir_exception_table >= m_coff_header_opt.data_dirs.size())
|
|
return {};
|
|
|
|
data_directory data_dir_exception =
|
|
m_coff_header_opt.data_dirs[coff_data_dir_exception_table];
|
|
if (!data_dir_exception.vmaddr)
|
|
return {};
|
|
|
|
return std::make_unique<PECallFrameInfo>(*this, data_dir_exception.vmaddr,
|
|
data_dir_exception.vmsize);
|
|
}
|
|
|
|
bool ObjectFilePECOFF::IsStripped() {
|
|
// TODO: determine this for COFF
|
|
return false;
|
|
}
|
|
|
|
SectionType ObjectFilePECOFF::GetSectionType(llvm::StringRef sect_name,
|
|
const section_header_t §) {
|
|
ConstString const_sect_name(sect_name);
|
|
static ConstString g_code_sect_name(".code");
|
|
static ConstString g_CODE_sect_name("CODE");
|
|
static ConstString g_data_sect_name(".data");
|
|
static ConstString g_DATA_sect_name("DATA");
|
|
static ConstString g_bss_sect_name(".bss");
|
|
static ConstString g_BSS_sect_name("BSS");
|
|
|
|
if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_CODE &&
|
|
((const_sect_name == g_code_sect_name) ||
|
|
(const_sect_name == g_CODE_sect_name))) {
|
|
return eSectionTypeCode;
|
|
}
|
|
if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA &&
|
|
((const_sect_name == g_data_sect_name) ||
|
|
(const_sect_name == g_DATA_sect_name))) {
|
|
if (sect.size == 0 && sect.offset == 0)
|
|
return eSectionTypeZeroFill;
|
|
else
|
|
return eSectionTypeData;
|
|
}
|
|
if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA &&
|
|
((const_sect_name == g_bss_sect_name) ||
|
|
(const_sect_name == g_BSS_sect_name))) {
|
|
if (sect.size == 0)
|
|
return eSectionTypeZeroFill;
|
|
else
|
|
return eSectionTypeData;
|
|
}
|
|
|
|
SectionType section_type =
|
|
llvm::StringSwitch<SectionType>(sect_name)
|
|
.Case(".debug", eSectionTypeDebug)
|
|
.Case(".stabstr", eSectionTypeDataCString)
|
|
.Case(".reloc", eSectionTypeOther)
|
|
.Case(".debug_abbrev", eSectionTypeDWARFDebugAbbrev)
|
|
.Case(".debug_aranges", eSectionTypeDWARFDebugAranges)
|
|
.Case(".debug_frame", eSectionTypeDWARFDebugFrame)
|
|
.Case(".debug_info", eSectionTypeDWARFDebugInfo)
|
|
.Case(".debug_line", eSectionTypeDWARFDebugLine)
|
|
.Case(".debug_loc", eSectionTypeDWARFDebugLoc)
|
|
.Case(".debug_loclists", eSectionTypeDWARFDebugLocLists)
|
|
.Case(".debug_macinfo", eSectionTypeDWARFDebugMacInfo)
|
|
.Case(".debug_names", eSectionTypeDWARFDebugNames)
|
|
.Case(".debug_pubnames", eSectionTypeDWARFDebugPubNames)
|
|
.Case(".debug_pubtypes", eSectionTypeDWARFDebugPubTypes)
|
|
.Case(".debug_ranges", eSectionTypeDWARFDebugRanges)
|
|
.Case(".debug_str", eSectionTypeDWARFDebugStr)
|
|
.Case(".debug_types", eSectionTypeDWARFDebugTypes)
|
|
// .eh_frame can be truncated to 8 chars.
|
|
.Cases(".eh_frame", ".eh_fram", eSectionTypeEHFrame)
|
|
.Case(".gosymtab", eSectionTypeGoSymtab)
|
|
.Default(eSectionTypeInvalid);
|
|
if (section_type != eSectionTypeInvalid)
|
|
return section_type;
|
|
|
|
if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_CODE)
|
|
return eSectionTypeCode;
|
|
if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA)
|
|
return eSectionTypeData;
|
|
if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
|
|
if (sect.size == 0)
|
|
return eSectionTypeZeroFill;
|
|
else
|
|
return eSectionTypeData;
|
|
}
|
|
return eSectionTypeOther;
|
|
}
|
|
|
|
void ObjectFilePECOFF::CreateSections(SectionList &unified_section_list) {
|
|
if (m_sections_up)
|
|
return;
|
|
m_sections_up.reset(new SectionList());
|
|
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp) {
|
|
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
|
|
|
|
SectionSP header_sp = std::make_shared<Section>(
|
|
module_sp, this, ~user_id_t(0), ConstString("PECOFF header"),
|
|
eSectionTypeOther, m_coff_header_opt.image_base,
|
|
m_coff_header_opt.header_size,
|
|
/*file_offset*/ 0, m_coff_header_opt.header_size,
|
|
m_coff_header_opt.sect_alignment,
|
|
/*flags*/ 0);
|
|
header_sp->SetPermissions(ePermissionsReadable);
|
|
m_sections_up->AddSection(header_sp);
|
|
unified_section_list.AddSection(header_sp);
|
|
|
|
const uint32_t nsects = m_sect_headers.size();
|
|
ModuleSP module_sp(GetModule());
|
|
for (uint32_t idx = 0; idx < nsects; ++idx) {
|
|
llvm::StringRef sect_name = GetSectionName(m_sect_headers[idx]);
|
|
ConstString const_sect_name(sect_name);
|
|
SectionType section_type = GetSectionType(sect_name, m_sect_headers[idx]);
|
|
|
|
SectionSP section_sp(new Section(
|
|
module_sp, // Module to which this section belongs
|
|
this, // Object file to which this section belongs
|
|
idx + 1, // Section ID is the 1 based section index.
|
|
const_sect_name, // Name of this section
|
|
section_type,
|
|
m_coff_header_opt.image_base +
|
|
m_sect_headers[idx].vmaddr, // File VM address == addresses as
|
|
// they are found in the object file
|
|
m_sect_headers[idx].vmsize, // VM size in bytes of this section
|
|
m_sect_headers[idx]
|
|
.offset, // Offset to the data for this section in the file
|
|
m_sect_headers[idx]
|
|
.size, // Size in bytes of this section as found in the file
|
|
m_coff_header_opt.sect_alignment, // Section alignment
|
|
m_sect_headers[idx].flags)); // Flags for this section
|
|
|
|
uint32_t permissions = 0;
|
|
if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
|
|
permissions |= ePermissionsExecutable;
|
|
if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_MEM_READ)
|
|
permissions |= ePermissionsReadable;
|
|
if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_MEM_WRITE)
|
|
permissions |= ePermissionsWritable;
|
|
section_sp->SetPermissions(permissions);
|
|
|
|
m_sections_up->AddSection(section_sp);
|
|
unified_section_list.AddSection(section_sp);
|
|
}
|
|
}
|
|
}
|
|
|
|
UUID ObjectFilePECOFF::GetUUID() {
|
|
if (m_uuid.IsValid())
|
|
return m_uuid;
|
|
|
|
if (!CreateBinary())
|
|
return UUID();
|
|
|
|
auto COFFObj =
|
|
llvm::cast<llvm::object::COFFObjectFile>(m_owningbin->getBinary());
|
|
|
|
m_uuid = GetCoffUUID(COFFObj);
|
|
return m_uuid;
|
|
}
|
|
|
|
uint32_t ObjectFilePECOFF::ParseDependentModules() {
|
|
ModuleSP module_sp(GetModule());
|
|
if (!module_sp)
|
|
return 0;
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
|
|
if (m_deps_filespec)
|
|
return m_deps_filespec->GetSize();
|
|
|
|
// Cache coff binary if it is not done yet.
|
|
if (!CreateBinary())
|
|
return 0;
|
|
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
|
|
LLDB_LOGF(log,
|
|
"%p ObjectFilePECOFF::ParseDependentModules() module = %p "
|
|
"(%s), binary = %p (Bin = %p)",
|
|
static_cast<void *>(this), static_cast<void *>(module_sp.get()),
|
|
module_sp->GetSpecificationDescription().c_str(),
|
|
static_cast<void *>(m_owningbin.getPointer()),
|
|
static_cast<void *>(m_owningbin->getBinary()));
|
|
|
|
auto COFFObj =
|
|
llvm::dyn_cast<llvm::object::COFFObjectFile>(m_owningbin->getBinary());
|
|
if (!COFFObj)
|
|
return 0;
|
|
|
|
m_deps_filespec = FileSpecList();
|
|
|
|
for (const auto &entry : COFFObj->import_directories()) {
|
|
llvm::StringRef dll_name;
|
|
auto ec = entry.getName(dll_name);
|
|
// Report a bogus entry.
|
|
if (ec != std::error_code()) {
|
|
LLDB_LOGF(log,
|
|
"ObjectFilePECOFF::ParseDependentModules() - failed to get "
|
|
"import directory entry name: %s",
|
|
ec.message().c_str());
|
|
continue;
|
|
}
|
|
|
|
// At this moment we only have the base name of the DLL. The full path can
|
|
// only be seen after the dynamic loading. Our best guess is Try to get it
|
|
// with the help of the object file's directory.
|
|
llvm::SmallString<128> dll_fullpath;
|
|
FileSpec dll_specs(dll_name);
|
|
dll_specs.GetDirectory().SetString(m_file.GetDirectory().GetCString());
|
|
|
|
if (!llvm::sys::fs::real_path(dll_specs.GetPath(), dll_fullpath))
|
|
m_deps_filespec->EmplaceBack(dll_fullpath);
|
|
else {
|
|
// Known DLLs or DLL not found in the object file directory.
|
|
m_deps_filespec->EmplaceBack(dll_name);
|
|
}
|
|
}
|
|
return m_deps_filespec->GetSize();
|
|
}
|
|
|
|
uint32_t ObjectFilePECOFF::GetDependentModules(FileSpecList &files) {
|
|
auto num_modules = ParseDependentModules();
|
|
auto original_size = files.GetSize();
|
|
|
|
for (unsigned i = 0; i < num_modules; ++i)
|
|
files.AppendIfUnique(m_deps_filespec->GetFileSpecAtIndex(i));
|
|
|
|
return files.GetSize() - original_size;
|
|
}
|
|
|
|
lldb_private::Address ObjectFilePECOFF::GetEntryPointAddress() {
|
|
if (m_entry_point_address.IsValid())
|
|
return m_entry_point_address;
|
|
|
|
if (!ParseHeader() || !IsExecutable())
|
|
return m_entry_point_address;
|
|
|
|
SectionList *section_list = GetSectionList();
|
|
addr_t file_addr = m_coff_header_opt.entry + m_coff_header_opt.image_base;
|
|
|
|
if (!section_list)
|
|
m_entry_point_address.SetOffset(file_addr);
|
|
else
|
|
m_entry_point_address.ResolveAddressUsingFileSections(file_addr,
|
|
section_list);
|
|
return m_entry_point_address;
|
|
}
|
|
|
|
Address ObjectFilePECOFF::GetBaseAddress() {
|
|
return Address(GetSectionList()->GetSectionAtIndex(0), 0);
|
|
}
|
|
|
|
// Dump
|
|
//
|
|
// Dump the specifics of the runtime file container (such as any headers
|
|
// segments, sections, etc).
|
|
void ObjectFilePECOFF::Dump(Stream *s) {
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp) {
|
|
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
|
|
s->Printf("%p: ", static_cast<void *>(this));
|
|
s->Indent();
|
|
s->PutCString("ObjectFilePECOFF");
|
|
|
|
ArchSpec header_arch = GetArchitecture();
|
|
|
|
*s << ", file = '" << m_file
|
|
<< "', arch = " << header_arch.GetArchitectureName() << "\n";
|
|
|
|
SectionList *sections = GetSectionList();
|
|
if (sections)
|
|
sections->Dump(s, nullptr, true, UINT32_MAX);
|
|
|
|
if (m_symtab_up)
|
|
m_symtab_up->Dump(s, nullptr, eSortOrderNone);
|
|
|
|
if (m_dos_header.e_magic)
|
|
DumpDOSHeader(s, m_dos_header);
|
|
if (m_coff_header.machine) {
|
|
DumpCOFFHeader(s, m_coff_header);
|
|
if (m_coff_header.hdrsize)
|
|
DumpOptCOFFHeader(s, m_coff_header_opt);
|
|
}
|
|
s->EOL();
|
|
DumpSectionHeaders(s);
|
|
s->EOL();
|
|
|
|
DumpDependentModules(s);
|
|
s->EOL();
|
|
}
|
|
}
|
|
|
|
// DumpDOSHeader
|
|
//
|
|
// Dump the MS-DOS header to the specified output stream
|
|
void ObjectFilePECOFF::DumpDOSHeader(Stream *s, const dos_header_t &header) {
|
|
s->PutCString("MSDOS Header\n");
|
|
s->Printf(" e_magic = 0x%4.4x\n", header.e_magic);
|
|
s->Printf(" e_cblp = 0x%4.4x\n", header.e_cblp);
|
|
s->Printf(" e_cp = 0x%4.4x\n", header.e_cp);
|
|
s->Printf(" e_crlc = 0x%4.4x\n", header.e_crlc);
|
|
s->Printf(" e_cparhdr = 0x%4.4x\n", header.e_cparhdr);
|
|
s->Printf(" e_minalloc = 0x%4.4x\n", header.e_minalloc);
|
|
s->Printf(" e_maxalloc = 0x%4.4x\n", header.e_maxalloc);
|
|
s->Printf(" e_ss = 0x%4.4x\n", header.e_ss);
|
|
s->Printf(" e_sp = 0x%4.4x\n", header.e_sp);
|
|
s->Printf(" e_csum = 0x%4.4x\n", header.e_csum);
|
|
s->Printf(" e_ip = 0x%4.4x\n", header.e_ip);
|
|
s->Printf(" e_cs = 0x%4.4x\n", header.e_cs);
|
|
s->Printf(" e_lfarlc = 0x%4.4x\n", header.e_lfarlc);
|
|
s->Printf(" e_ovno = 0x%4.4x\n", header.e_ovno);
|
|
s->Printf(" e_res[4] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n",
|
|
header.e_res[0], header.e_res[1], header.e_res[2], header.e_res[3]);
|
|
s->Printf(" e_oemid = 0x%4.4x\n", header.e_oemid);
|
|
s->Printf(" e_oeminfo = 0x%4.4x\n", header.e_oeminfo);
|
|
s->Printf(" e_res2[10] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, "
|
|
"0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n",
|
|
header.e_res2[0], header.e_res2[1], header.e_res2[2],
|
|
header.e_res2[3], header.e_res2[4], header.e_res2[5],
|
|
header.e_res2[6], header.e_res2[7], header.e_res2[8],
|
|
header.e_res2[9]);
|
|
s->Printf(" e_lfanew = 0x%8.8x\n", header.e_lfanew);
|
|
}
|
|
|
|
// DumpCOFFHeader
|
|
//
|
|
// Dump the COFF header to the specified output stream
|
|
void ObjectFilePECOFF::DumpCOFFHeader(Stream *s, const coff_header_t &header) {
|
|
s->PutCString("COFF Header\n");
|
|
s->Printf(" machine = 0x%4.4x\n", header.machine);
|
|
s->Printf(" nsects = 0x%4.4x\n", header.nsects);
|
|
s->Printf(" modtime = 0x%8.8x\n", header.modtime);
|
|
s->Printf(" symoff = 0x%8.8x\n", header.symoff);
|
|
s->Printf(" nsyms = 0x%8.8x\n", header.nsyms);
|
|
s->Printf(" hdrsize = 0x%4.4x\n", header.hdrsize);
|
|
}
|
|
|
|
// DumpOptCOFFHeader
|
|
//
|
|
// Dump the optional COFF header to the specified output stream
|
|
void ObjectFilePECOFF::DumpOptCOFFHeader(Stream *s,
|
|
const coff_opt_header_t &header) {
|
|
s->PutCString("Optional COFF Header\n");
|
|
s->Printf(" magic = 0x%4.4x\n", header.magic);
|
|
s->Printf(" major_linker_version = 0x%2.2x\n",
|
|
header.major_linker_version);
|
|
s->Printf(" minor_linker_version = 0x%2.2x\n",
|
|
header.minor_linker_version);
|
|
s->Printf(" code_size = 0x%8.8x\n", header.code_size);
|
|
s->Printf(" data_size = 0x%8.8x\n", header.data_size);
|
|
s->Printf(" bss_size = 0x%8.8x\n", header.bss_size);
|
|
s->Printf(" entry = 0x%8.8x\n", header.entry);
|
|
s->Printf(" code_offset = 0x%8.8x\n", header.code_offset);
|
|
s->Printf(" data_offset = 0x%8.8x\n", header.data_offset);
|
|
s->Printf(" image_base = 0x%16.16" PRIx64 "\n",
|
|
header.image_base);
|
|
s->Printf(" sect_alignment = 0x%8.8x\n", header.sect_alignment);
|
|
s->Printf(" file_alignment = 0x%8.8x\n", header.file_alignment);
|
|
s->Printf(" major_os_system_version = 0x%4.4x\n",
|
|
header.major_os_system_version);
|
|
s->Printf(" minor_os_system_version = 0x%4.4x\n",
|
|
header.minor_os_system_version);
|
|
s->Printf(" major_image_version = 0x%4.4x\n",
|
|
header.major_image_version);
|
|
s->Printf(" minor_image_version = 0x%4.4x\n",
|
|
header.minor_image_version);
|
|
s->Printf(" major_subsystem_version = 0x%4.4x\n",
|
|
header.major_subsystem_version);
|
|
s->Printf(" minor_subsystem_version = 0x%4.4x\n",
|
|
header.minor_subsystem_version);
|
|
s->Printf(" reserved1 = 0x%8.8x\n", header.reserved1);
|
|
s->Printf(" image_size = 0x%8.8x\n", header.image_size);
|
|
s->Printf(" header_size = 0x%8.8x\n", header.header_size);
|
|
s->Printf(" checksum = 0x%8.8x\n", header.checksum);
|
|
s->Printf(" subsystem = 0x%4.4x\n", header.subsystem);
|
|
s->Printf(" dll_flags = 0x%4.4x\n", header.dll_flags);
|
|
s->Printf(" stack_reserve_size = 0x%16.16" PRIx64 "\n",
|
|
header.stack_reserve_size);
|
|
s->Printf(" stack_commit_size = 0x%16.16" PRIx64 "\n",
|
|
header.stack_commit_size);
|
|
s->Printf(" heap_reserve_size = 0x%16.16" PRIx64 "\n",
|
|
header.heap_reserve_size);
|
|
s->Printf(" heap_commit_size = 0x%16.16" PRIx64 "\n",
|
|
header.heap_commit_size);
|
|
s->Printf(" loader_flags = 0x%8.8x\n", header.loader_flags);
|
|
s->Printf(" num_data_dir_entries = 0x%8.8x\n",
|
|
(uint32_t)header.data_dirs.size());
|
|
uint32_t i;
|
|
for (i = 0; i < header.data_dirs.size(); i++) {
|
|
s->Printf(" data_dirs[%2u] vmaddr = 0x%8.8x, vmsize = 0x%8.8x\n", i,
|
|
header.data_dirs[i].vmaddr, header.data_dirs[i].vmsize);
|
|
}
|
|
}
|
|
// DumpSectionHeader
|
|
//
|
|
// Dump a single ELF section header to the specified output stream
|
|
void ObjectFilePECOFF::DumpSectionHeader(Stream *s,
|
|
const section_header_t &sh) {
|
|
std::string name = GetSectionName(sh);
|
|
s->Printf("%-16s 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%4.4x "
|
|
"0x%4.4x 0x%8.8x\n",
|
|
name.c_str(), sh.vmaddr, sh.vmsize, sh.offset, sh.size, sh.reloff,
|
|
sh.lineoff, sh.nreloc, sh.nline, sh.flags);
|
|
}
|
|
|
|
// DumpSectionHeaders
|
|
//
|
|
// Dump all of the ELF section header to the specified output stream
|
|
void ObjectFilePECOFF::DumpSectionHeaders(Stream *s) {
|
|
|
|
s->PutCString("Section Headers\n");
|
|
s->PutCString("IDX name vm addr vm size file off file "
|
|
"size reloc off line off nreloc nline flags\n");
|
|
s->PutCString("==== ---------------- ---------- ---------- ---------- "
|
|
"---------- ---------- ---------- ------ ------ ----------\n");
|
|
|
|
uint32_t idx = 0;
|
|
SectionHeaderCollIter pos, end = m_sect_headers.end();
|
|
|
|
for (pos = m_sect_headers.begin(); pos != end; ++pos, ++idx) {
|
|
s->Printf("[%2u] ", idx);
|
|
ObjectFilePECOFF::DumpSectionHeader(s, *pos);
|
|
}
|
|
}
|
|
|
|
// DumpDependentModules
|
|
//
|
|
// Dump all of the dependent modules to the specified output stream
|
|
void ObjectFilePECOFF::DumpDependentModules(lldb_private::Stream *s) {
|
|
auto num_modules = ParseDependentModules();
|
|
if (num_modules > 0) {
|
|
s->PutCString("Dependent Modules\n");
|
|
for (unsigned i = 0; i < num_modules; ++i) {
|
|
auto spec = m_deps_filespec->GetFileSpecAtIndex(i);
|
|
s->Printf(" %s\n", spec.GetFilename().GetCString());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ObjectFilePECOFF::IsWindowsSubsystem() {
|
|
switch (m_coff_header_opt.subsystem) {
|
|
case llvm::COFF::IMAGE_SUBSYSTEM_NATIVE:
|
|
case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI:
|
|
case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI:
|
|
case llvm::COFF::IMAGE_SUBSYSTEM_NATIVE_WINDOWS:
|
|
case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CE_GUI:
|
|
case llvm::COFF::IMAGE_SUBSYSTEM_XBOX:
|
|
case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ArchSpec ObjectFilePECOFF::GetArchitecture() {
|
|
uint16_t machine = m_coff_header.machine;
|
|
switch (machine) {
|
|
default:
|
|
break;
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_AMD64:
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_I386:
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_POWERPC:
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP:
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_ARM:
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT:
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_THUMB:
|
|
case llvm::COFF::IMAGE_FILE_MACHINE_ARM64:
|
|
ArchSpec arch;
|
|
arch.SetArchitecture(eArchTypeCOFF, machine, LLDB_INVALID_CPUTYPE,
|
|
IsWindowsSubsystem() ? llvm::Triple::Win32
|
|
: llvm::Triple::UnknownOS);
|
|
return arch;
|
|
}
|
|
return ArchSpec();
|
|
}
|
|
|
|
ObjectFile::Type ObjectFilePECOFF::CalculateType() {
|
|
if (m_coff_header.machine != 0) {
|
|
if ((m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0)
|
|
return eTypeExecutable;
|
|
else
|
|
return eTypeSharedLibrary;
|
|
}
|
|
return eTypeExecutable;
|
|
}
|
|
|
|
ObjectFile::Strata ObjectFilePECOFF::CalculateStrata() { return eStrataUser; }
|
|
|
|
// PluginInterface protocol
|
|
ConstString ObjectFilePECOFF::GetPluginName() { return GetPluginNameStatic(); }
|
|
|
|
uint32_t ObjectFilePECOFF::GetPluginVersion() { return 1; }
|