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 }
312