| 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(magic: toStringRef(Input: 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(name: GetPluginNameStatic(), |
| 38 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance, |
| 39 | create_memory_callback: CreateMemoryInstance, get_module_specifications: GetModuleSpecifications); |
| 40 | } |
| 41 | |
| 42 | void ObjectFileCOFF::Terminate() { |
| 43 | PluginManager::UnregisterPlugin(create_callback: 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(mask: LLDBLog::Object); |
| 51 | |
| 52 | if (!data_sp) { |
| 53 | data_sp = MapFileData(file: *file, Size: length, Offset: 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: data_sp)) |
| 66 | return nullptr; |
| 67 | |
| 68 | if (data_sp->GetByteSize() < length) { |
| 69 | data_sp = MapFileData(file: *file, Size: length, Offset: 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(Input: data_sp->GetData()), |
| 81 | file->GetFilename().GetStringRef()}; |
| 82 | |
| 83 | Expected<std::unique_ptr<Binary>> binary = createBinary(Source: 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>(Val: 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 ) { |
| 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: data_sp)) |
| 111 | return 0; |
| 112 | |
| 113 | MemoryBufferRef buffer{toStringRef(Input: data_sp->GetData()), |
| 114 | file.GetFilename().GetStringRef()}; |
| 115 | Expected<std::unique_ptr<Binary>> binary = createBinary(Source: buffer); |
| 116 | if (!binary) { |
| 117 | Log *log = GetLog(mask: 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>(Val: std::move(*binary)); |
| 126 | switch (static_cast<COFF::MachineTypes>(object->getMachine())) { |
| 127 | case COFF::IMAGE_FILE_MACHINE_I386: |
| 128 | specs.Append(spec: ModuleSpec(file, ArchSpec("i686-unknown-windows-msvc" ))); |
| 129 | return 1; |
| 130 | case COFF::IMAGE_FILE_MACHINE_AMD64: |
| 131 | specs.Append(spec: ModuleSpec(file, ArchSpec("x86_64-unknown-windows-msvc" ))); |
| 132 | return 1; |
| 133 | case COFF::IMAGE_FILE_MACHINE_ARMNT: |
| 134 | specs.Append(spec: ModuleSpec(file, ArchSpec("armv7-unknown-windows-msvc" ))); |
| 135 | return 1; |
| 136 | case COFF::IMAGE_FILE_MACHINE_ARM64: |
| 137 | specs.Append(spec: 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(format: "%p: " , static_cast<void *>(this)); |
| 152 | stream->Indent(); |
| 153 | stream->PutCString(cstr: "ObjectFileCOFF" ); |
| 154 | *stream << ", file = '" << m_file |
| 155 | << "', arch = " << GetArchitecture().GetArchitectureName() << '\n'; |
| 156 | |
| 157 | if (SectionList *sections = GetSectionList()) |
| 158 | sections->Dump(s&: stream->AsRawOstream(), indent: stream->GetIndentLevel(), target: nullptr, |
| 159 | show_header: true, depth: 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 §ions) { |
| 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 | // DWARF Debug Sections |
| 195 | if (Name.consume_front(Prefix: ".debug_" )) |
| 196 | return GetDWARFSectionTypeFromName(name: Name); |
| 197 | |
| 198 | lldb::SectionType type = StringSwitch<lldb::SectionType>(Name) |
| 199 | // CodeView Debug Sections: .debug$S, .debug$T |
| 200 | .StartsWith(S: ".debug$" , Value: eSectionTypeDebug) |
| 201 | .Case(S: "clangast" , Value: eSectionTypeOther) |
| 202 | .Default(Value: eSectionTypeInvalid); |
| 203 | if (type != eSectionTypeInvalid) |
| 204 | return type; |
| 205 | |
| 206 | if (Section->Characteristics & COFF::IMAGE_SCN_CNT_CODE) |
| 207 | return eSectionTypeCode; |
| 208 | if (Section->Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) |
| 209 | return eSectionTypeData; |
| 210 | if (Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) |
| 211 | return Section->SizeOfRawData ? eSectionTypeData : eSectionTypeZeroFill; |
| 212 | return eSectionTypeOther; |
| 213 | }; |
| 214 | auto Permissions = [](const object::coff_section *Section) -> uint32_t { |
| 215 | uint32_t permissions = 0; |
| 216 | if (Section->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE) |
| 217 | permissions |= lldb::ePermissionsExecutable; |
| 218 | if (Section->Characteristics & COFF::IMAGE_SCN_MEM_READ) |
| 219 | permissions |= lldb::ePermissionsReadable; |
| 220 | if (Section->Characteristics & COFF::IMAGE_SCN_MEM_WRITE) |
| 221 | permissions |= lldb::ePermissionsWritable; |
| 222 | return permissions; |
| 223 | }; |
| 224 | |
| 225 | for (const auto &SecRef : m_object->sections()) { |
| 226 | const auto COFFSection = m_object->getCOFFSection(Section: SecRef); |
| 227 | |
| 228 | llvm::Expected<StringRef> Name = SecRef.getName(); |
| 229 | StringRef SectionName = Name ? *Name : COFFSection->Name; |
| 230 | if (!Name) |
| 231 | consumeError(Err: Name.takeError()); |
| 232 | |
| 233 | SectionSP section = |
| 234 | std::make_unique<Section>(args&: module, args: this, |
| 235 | args: static_cast<user_id_t>(SecRef.getIndex()), |
| 236 | args: ConstString(SectionName), |
| 237 | args: SectionType(SectionName, COFFSection), |
| 238 | args: COFFSection->VirtualAddress, |
| 239 | args: COFFSection->VirtualSize, |
| 240 | args: COFFSection->PointerToRawData, |
| 241 | args: COFFSection->SizeOfRawData, |
| 242 | args: COFFSection->getAlignment(), |
| 243 | args: 0); |
| 244 | section->SetPermissions(Permissions(COFFSection)); |
| 245 | |
| 246 | m_sections_up->AddSection(section_sp: section); |
| 247 | sections.AddSection(section_sp: section); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | void ObjectFileCOFF::ParseSymtab(lldb_private::Symtab &symtab) { |
| 252 | Log *log = GetLog(mask: LLDBLog::Object); |
| 253 | |
| 254 | SectionList *sections = GetSectionList(); |
| 255 | symtab.Reserve(count: symtab.GetNumSymbols() + m_object->getNumberOfSymbols()); |
| 256 | |
| 257 | auto SymbolType = [](const COFFSymbolRef &Symbol) -> lldb::SymbolType { |
| 258 | if (Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) |
| 259 | return eSymbolTypeCode; |
| 260 | if (Symbol.getBaseType() == COFF::IMAGE_SYM_TYPE_NULL && |
| 261 | Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_NULL) |
| 262 | return eSymbolTypeData; |
| 263 | return eSymbolTypeInvalid; |
| 264 | }; |
| 265 | |
| 266 | for (const auto &SymRef : m_object->symbols()) { |
| 267 | const auto COFFSymRef = m_object->getCOFFSymbol(Symbol: SymRef); |
| 268 | |
| 269 | Expected<StringRef> NameOrErr = SymRef.getName(); |
| 270 | if (!NameOrErr) { |
| 271 | LLDB_LOG_ERROR(log, NameOrErr.takeError(), |
| 272 | "ObjectFileCOFF: failed to get symbol name: {0}" ); |
| 273 | continue; |
| 274 | } |
| 275 | |
| 276 | Symbol symbol; |
| 277 | symbol.GetMangled().SetValue(ConstString(*NameOrErr)); |
| 278 | |
| 279 | int16_t SecIdx = static_cast<int16_t>(COFFSymRef.getSectionNumber()); |
| 280 | if (SecIdx == COFF::IMAGE_SYM_ABSOLUTE) { |
| 281 | symbol.GetAddressRef() = Address{COFFSymRef.getValue()}; |
| 282 | symbol.SetType(eSymbolTypeAbsolute); |
| 283 | } else if (SecIdx >= 1) { |
| 284 | symbol.GetAddressRef() = Address(sections->GetSectionAtIndex(idx: SecIdx - 1), |
| 285 | COFFSymRef.getValue()); |
| 286 | symbol.SetType(SymbolType(COFFSymRef)); |
| 287 | } |
| 288 | |
| 289 | symtab.AddSymbol(symbol); |
| 290 | } |
| 291 | |
| 292 | LLDB_LOG(log, "ObjectFileCOFF::ParseSymtab processed {0} symbols" , |
| 293 | m_object->getNumberOfSymbols()); |
| 294 | } |
| 295 | |
| 296 | bool ObjectFileCOFF::() { |
| 297 | ModuleSP module(GetModule()); |
| 298 | if (!module) |
| 299 | return false; |
| 300 | |
| 301 | std::lock_guard<std::recursive_mutex> guard(module->GetMutex()); |
| 302 | |
| 303 | m_data.SetByteOrder(eByteOrderLittle); |
| 304 | m_data.SetAddressByteSize(GetAddressByteSize()); |
| 305 | |
| 306 | return true; |
| 307 | } |
| 308 | |