Skip to content

Commit 5014830

Browse files
committed
ObjectFile: introduce a COFF object file plugin
Windows uses COFF as an object file format and PE/COFF as an executable file format. They are subtly different and certain elements of a COFF file may not be present in an executable. Introduce a new plugin to add support for the COFF object file format which is required to support loading of modules built with -gmodules. This is motivated by Swift which serialises debugging information into a PCM which is a COFF object file. Differential Revision: https://reviews.llvm.org/D149987 Reviewed By: bulbazord
1 parent 856f384 commit 5014830

File tree

5 files changed

+675
-0
lines changed

5 files changed

+675
-0
lines changed

lldb/source/Plugins/ObjectFile/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_subdirectory(Breakpad)
2+
add_subdirectory(COFF)
23
add_subdirectory(ELF)
34
add_subdirectory(JSON)
45
add_subdirectory(Mach-O)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
add_lldb_library(lldbPluginObjectFileCOFF PLUGIN
2+
ObjectFileCOFF.cpp
3+
4+
LINK_LIBS
5+
lldbCore
6+
lldbHost
7+
lldbSymbol
8+
lldbTarget
9+
10+
LINK_COMPONENTS
11+
BinaryFormat
12+
Object
13+
Support)
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
//===-- ObjectFileCOFF.cpp ------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "ObjectFileCOFF.h"
10+
11+
#include "lldb/Core/Module.h"
12+
#include "lldb/Core/ModuleSpec.h"
13+
#include "lldb/Core/PluginManager.h"
14+
#include "lldb/Utility/LLDBLog.h"
15+
16+
#include "llvm/Support/Error.h"
17+
#include "llvm/Support/FormatAdapters.h"
18+
19+
using namespace lldb;
20+
using namespace lldb_private;
21+
22+
using namespace llvm;
23+
using namespace llvm::object;
24+
25+
static bool IsCOFFObjectFile(const DataBufferSP &data) {
26+
return identify_magic(toStringRef(data->GetData())) ==
27+
file_magic::coff_object;
28+
}
29+
30+
LLDB_PLUGIN_DEFINE(ObjectFileCOFF)
31+
32+
char ObjectFileCOFF::ID;
33+
34+
ObjectFileCOFF::~ObjectFileCOFF() = default;
35+
36+
void ObjectFileCOFF::Initialize() {
37+
PluginManager::RegisterPlugin(GetPluginNameStatic(),
38+
GetPluginDescriptionStatic(), CreateInstance,
39+
CreateMemoryInstance, GetModuleSpecifications);
40+
}
41+
42+
void ObjectFileCOFF::Terminate() {
43+
PluginManager::UnregisterPlugin(CreateInstance);
44+
}
45+
46+
lldb_private::ObjectFile *
47+
ObjectFileCOFF::CreateInstance(const ModuleSP &module_sp, DataBufferSP data_sp,
48+
offset_t data_offset, const FileSpec *file,
49+
offset_t file_offset, offset_t length) {
50+
Log *log = GetLog(LLDBLog::Object);
51+
52+
if (!data_sp) {
53+
data_sp = MapFileData(*file, length, file_offset);
54+
if (!data_sp) {
55+
LLDB_LOG(log,
56+
"Failed to create ObjectFileCOFF instance: cannot read file {0}",
57+
file->GetPath());
58+
return nullptr;
59+
}
60+
data_offset = 0;
61+
}
62+
63+
assert(data_sp && "must have mapped file at this point");
64+
65+
if (!IsCOFFObjectFile(data_sp))
66+
return nullptr;
67+
68+
if (data_sp->GetByteSize() < length) {
69+
data_sp = MapFileData(*file, length, file_offset);
70+
if (!data_sp) {
71+
LLDB_LOG(log,
72+
"Failed to create ObjectFileCOFF instance: cannot read file {0}",
73+
file->GetPath());
74+
return nullptr;
75+
}
76+
data_offset = 0;
77+
}
78+
79+
80+
MemoryBufferRef buffer{toStringRef(data_sp->GetData()),
81+
file->GetFilename().GetStringRef()};
82+
83+
Expected<std::unique_ptr<Binary>> binary = createBinary(buffer);
84+
if (!binary) {
85+
LLDB_LOG_ERROR(log, binary.takeError(),
86+
"Failed to create binary for file ({1}): {0}",
87+
file->GetPath());
88+
return nullptr;
89+
}
90+
91+
LLDB_LOG(log, "ObjectFileCOFF::ObjectFileCOFF module = {1} ({2}), file = {3}",
92+
module_sp.get(), module_sp->GetSpecificationDescription(),
93+
file->GetPath());
94+
95+
return new ObjectFileCOFF(unique_dyn_cast<COFFObjectFile>(std::move(*binary)),
96+
module_sp, data_sp, data_offset, file, file_offset,
97+
length);
98+
}
99+
100+
lldb_private::ObjectFile *ObjectFileCOFF::CreateMemoryInstance(
101+
const ModuleSP &module_sp, WritableDataBufferSP data_sp,
102+
const ProcessSP &process_sp, addr_t header) {
103+
// FIXME: do we need to worry about construction from a memory region?
104+
return nullptr;
105+
}
106+
107+
size_t ObjectFileCOFF::GetModuleSpecifications(
108+
const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
109+
offset_t file_offset, offset_t length, ModuleSpecList &specs) {
110+
if (!IsCOFFObjectFile(data_sp))
111+
return 0;
112+
113+
MemoryBufferRef buffer{toStringRef(data_sp->GetData()),
114+
file.GetFilename().GetStringRef()};
115+
Expected<std::unique_ptr<Binary>> binary = createBinary(buffer);
116+
if (!binary) {
117+
Log *log = GetLog(LLDBLog::Object);
118+
LLDB_LOG_ERROR(log, binary.takeError(),
119+
"Failed to create binary for file ({1}): {0}",
120+
file.GetFilename());
121+
return 0;
122+
}
123+
124+
std::unique_ptr<COFFObjectFile> object =
125+
unique_dyn_cast<COFFObjectFile>(std::move(*binary));
126+
switch (static_cast<COFF::MachineTypes>(object->getMachine())) {
127+
case COFF::IMAGE_FILE_MACHINE_I386:
128+
specs.Append(ModuleSpec(file, ArchSpec("i686-unknown-windows-msvc")));
129+
return 1;
130+
case COFF::IMAGE_FILE_MACHINE_AMD64:
131+
specs.Append(ModuleSpec(file, ArchSpec("x86_64-unknown-windows-msvc")));
132+
return 1;
133+
case COFF::IMAGE_FILE_MACHINE_ARMNT:
134+
specs.Append(ModuleSpec(file, ArchSpec("armv7-unknown-windows-msvc")));
135+
return 1;
136+
case COFF::IMAGE_FILE_MACHINE_ARM64:
137+
specs.Append(ModuleSpec(file, ArchSpec("aarch64-unknown-windows-msvc")));
138+
return 1;
139+
default:
140+
return 0;
141+
}
142+
}
143+
144+
void ObjectFileCOFF::Dump(Stream *stream) {
145+
ModuleSP module(GetModule());
146+
if (!module)
147+
return;
148+
149+
std::lock_guard<std::recursive_mutex> guard(module->GetMutex());
150+
151+
stream->Printf("%p: ", static_cast<void *>(this));
152+
stream->Indent();
153+
stream->PutCString("ObjectFileCOFF");
154+
*stream << ", file = '" << m_file
155+
<< "', arch = " << GetArchitecture().GetArchitectureName() << '\n';
156+
157+
if (SectionList *sections = GetSectionList())
158+
sections->Dump(stream->AsRawOstream(), stream->GetIndentLevel(), nullptr,
159+
true, std::numeric_limits<uint32_t>::max());
160+
}
161+
162+
uint32_t ObjectFileCOFF::GetAddressByteSize() const {
163+
return const_cast<ObjectFileCOFF *>(this)->GetArchitecture().GetAddressByteSize();
164+
}
165+
166+
ArchSpec ObjectFileCOFF::GetArchitecture() {
167+
switch (static_cast<COFF::MachineTypes>(m_object->getMachine())) {
168+
case COFF::IMAGE_FILE_MACHINE_I386:
169+
return ArchSpec("i686-unknown-windows-msvc");
170+
case COFF::IMAGE_FILE_MACHINE_AMD64:
171+
return ArchSpec("x86_64-unknown-windows-msvc");
172+
case COFF::IMAGE_FILE_MACHINE_ARMNT:
173+
return ArchSpec("armv7-unknown-windows-msvc");
174+
case COFF::IMAGE_FILE_MACHINE_ARM64:
175+
return ArchSpec("aarch64-unknown-windows-msvc");
176+
default:
177+
return ArchSpec();
178+
}
179+
}
180+
181+
void ObjectFileCOFF::CreateSections(lldb_private::SectionList &sections) {
182+
if (m_sections_up)
183+
return;
184+
185+
m_sections_up = std::make_unique<SectionList>();
186+
ModuleSP module(GetModule());
187+
if (!module)
188+
return;
189+
190+
std::lock_guard<std::recursive_mutex> guard(module->GetMutex());
191+
192+
auto SectionType = [](StringRef Name,
193+
const coff_section *Section) -> lldb::SectionType {
194+
lldb::SectionType type =
195+
StringSwitch<lldb::SectionType>(Name)
196+
// DWARF Debug Sections
197+
.Case(".debug_abbrev", eSectionTypeDWARFDebugAbbrev)
198+
.Case(".debug_info", eSectionTypeDWARFDebugInfo)
199+
.Case(".debug_line", eSectionTypeDWARFDebugLine)
200+
.Case(".debug_pubnames", eSectionTypeDWARFDebugPubNames)
201+
.Case(".debug_pubtypes", eSectionTypeDWARFDebugPubTypes)
202+
.Case(".debug_str", eSectionTypeDWARFDebugStr)
203+
// CodeView Debug Sections: .debug$S, .debug$T
204+
.StartsWith(".debug$", eSectionTypeDebug)
205+
.Case("clangast", eSectionTypeOther)
206+
.Default(eSectionTypeInvalid);
207+
if (type != eSectionTypeInvalid)
208+
return type;
209+
210+
if (Section->Characteristics & COFF::IMAGE_SCN_CNT_CODE)
211+
return eSectionTypeCode;
212+
if (Section->Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA)
213+
return eSectionTypeData;
214+
if (Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
215+
return Section->SizeOfRawData ? eSectionTypeData : eSectionTypeZeroFill;
216+
return eSectionTypeOther;
217+
};
218+
auto Permissions = [](const object::coff_section *Section) -> uint32_t {
219+
uint32_t permissions = 0;
220+
if (Section->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE)
221+
permissions |= lldb::ePermissionsExecutable;
222+
if (Section->Characteristics & COFF::IMAGE_SCN_MEM_READ)
223+
permissions |= lldb::ePermissionsReadable;
224+
if (Section->Characteristics & COFF::IMAGE_SCN_MEM_WRITE)
225+
permissions |= lldb::ePermissionsWritable;
226+
return permissions;
227+
};
228+
229+
for (const auto &SecRef : m_object->sections()) {
230+
const auto COFFSection = m_object->getCOFFSection(SecRef);
231+
232+
llvm::Expected<StringRef> Name = SecRef.getName();
233+
StringRef SectionName = Name ? *Name : COFFSection->Name;
234+
if (!Name)
235+
consumeError(Name.takeError());
236+
237+
SectionSP section =
238+
std::make_unique<Section>(module, this,
239+
static_cast<user_id_t>(SecRef.getIndex()),
240+
ConstString(SectionName),
241+
SectionType(SectionName, COFFSection),
242+
COFFSection->VirtualAddress,
243+
COFFSection->VirtualSize,
244+
COFFSection->PointerToRawData,
245+
COFFSection->SizeOfRawData,
246+
COFFSection->getAlignment(),
247+
0);
248+
section->SetPermissions(Permissions(COFFSection));
249+
250+
m_sections_up->AddSection(section);
251+
sections.AddSection(section);
252+
}
253+
}
254+
255+
void ObjectFileCOFF::ParseSymtab(lldb_private::Symtab &symtab) {
256+
Log *log = GetLog(LLDBLog::Object);
257+
258+
SectionList *sections = GetSectionList();
259+
symtab.Reserve(symtab.GetNumSymbols() + m_object->getNumberOfSymbols());
260+
261+
auto SymbolType = [](const COFFSymbolRef &Symbol) -> lldb::SymbolType {
262+
if (Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION)
263+
return eSymbolTypeCode;
264+
if (Symbol.getBaseType() == COFF::IMAGE_SYM_TYPE_NULL &&
265+
Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_NULL)
266+
return eSymbolTypeData;
267+
return eSymbolTypeInvalid;
268+
};
269+
270+
for (const auto &SymRef : m_object->symbols()) {
271+
const auto COFFSymRef = m_object->getCOFFSymbol(SymRef);
272+
273+
Expected<StringRef> NameOrErr = SymRef.getName();
274+
if (auto error = NameOrErr.takeError()) {
275+
LLDB_LOG(log, "ObjectFileCOFF: failed to get symbol name: {0}",
276+
llvm::fmt_consume(std::move(error)));
277+
continue;
278+
}
279+
280+
Symbol symbol;
281+
symbol.GetMangled().SetValue(ConstString(*NameOrErr));
282+
283+
int16_t SecIdx = static_cast<int16_t>(COFFSymRef.getSectionNumber());
284+
if (SecIdx == COFF::IMAGE_SYM_ABSOLUTE) {
285+
symbol.GetAddressRef() = Address{COFFSymRef.getValue()};
286+
symbol.SetType(eSymbolTypeAbsolute);
287+
} else if (SecIdx >= 1) {
288+
symbol.GetAddressRef() = Address(sections->GetSectionAtIndex(SecIdx - 1),
289+
COFFSymRef.getValue());
290+
symbol.SetType(SymbolType(COFFSymRef));
291+
}
292+
293+
symtab.AddSymbol(symbol);
294+
}
295+
296+
LLDB_LOG(log, "ObjectFileCOFF::ParseSymtab processed {0} symbols",
297+
m_object->getNumberOfSymbols());
298+
}
299+
300+
bool ObjectFileCOFF::ParseHeader() {
301+
ModuleSP module(GetModule());
302+
if (!module)
303+
return false;
304+
305+
std::lock_guard<std::recursive_mutex> guard(module->GetMutex());
306+
307+
m_data.SetByteOrder(eByteOrderLittle);
308+
m_data.SetAddressByteSize(GetAddressByteSize());
309+
310+
return true;
311+
}

0 commit comments

Comments
 (0)