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
19using namespace lldb;
20using namespace lldb_private;
21
22using namespace llvm;
23using namespace llvm::object;
24
25static bool IsCOFFObjectFile(const DataBufferSP &data) {
26  return identify_magic(toStringRef(data->GetData())) ==
27         file_magic::coff_object;
28}
29
30LLDB_PLUGIN_DEFINE(ObjectFileCOFF)
31
32char ObjectFileCOFF::ID;
33
34ObjectFileCOFF::~ObjectFileCOFF() = default;
35
36void ObjectFileCOFF::Initialize() {
37  PluginManager::RegisterPlugin(GetPluginNameStatic(),
38                                GetPluginDescriptionStatic(), CreateInstance,
39                                CreateMemoryInstance, GetModuleSpecifications);
40}
41
42void ObjectFileCOFF::Terminate() {
43  PluginManager::UnregisterPlugin(CreateInstance);
44}
45
46lldb_private::ObjectFile *
47ObjectFileCOFF::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
100lldb_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
107size_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
144void 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
162uint32_t ObjectFileCOFF::GetAddressByteSize() const {
163  return const_cast<ObjectFileCOFF *>(this)->GetArchitecture().GetAddressByteSize();
164}
165
166ArchSpec 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
181void 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
255void 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 (!NameOrErr) {
275      LLDB_LOG_ERROR(log, NameOrErr.takeError(),
276                     "ObjectFileCOFF: failed to get symbol name: {0}");
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
300bool 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