1 //===-- ObjectFileXCOFF.cpp 2 //-------------------------------------------------===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "ObjectFileXCOFF.h" 11 #include "lldb/Core/Module.h" 12 #include "lldb/Core/ModuleSpec.h" 13 #include "lldb/Core/PluginManager.h" 14 #include "lldb/Core/Progress.h" 15 #include "lldb/Core/Section.h" 16 #include "lldb/Host/FileSystem.h" 17 #include "lldb/Symbol/SymbolContext.h" 18 #include "lldb/Target/Process.h" 19 #include "lldb/Target/Target.h" 20 #include "lldb/Utility/ArchSpec.h" 21 #include "lldb/Utility/DataBufferHeap.h" 22 #include "lldb/Utility/FileSpecList.h" 23 #include "lldb/Utility/LLDBLog.h" 24 #include "lldb/Utility/Log.h" 25 #include "lldb/Utility/RangeMap.h" 26 #include "lldb/Utility/Status.h" 27 #include "lldb/Utility/Stream.h" 28 #include "llvm/ADT/StringRef.h" 29 #include "llvm/BinaryFormat/XCOFF.h" 30 #include "llvm/Object/XCOFFObjectFile.h" 31 #include "llvm/Support/MemoryBuffer.h" 32 #include <algorithm> 33 #include <cassert> 34 #include <cstring> 35 #include <unordered_map> 36 37 using namespace llvm; 38 using namespace lldb; 39 using namespace lldb_private; 40 41 LLDB_PLUGIN_DEFINE(ObjectFileXCOFF) 42 // FIXME: target 64bit at this moment. 43 44 // Static methods. 45 void ObjectFileXCOFF::Initialize() { 46 PluginManager::RegisterPlugin(GetPluginNameStatic(), 47 GetPluginDescriptionStatic(), CreateInstance, 48 CreateMemoryInstance, GetModuleSpecifications); 49 } 50 51 void ObjectFileXCOFF::Terminate() { 52 PluginManager::UnregisterPlugin(CreateInstance); 53 } 54 55 ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, 56 DataBufferSP data_sp, 57 lldb::offset_t data_offset, 58 const lldb_private::FileSpec *file, 59 lldb::offset_t file_offset, 60 lldb::offset_t length) { 61 if (!data_sp) { 62 data_sp = MapFileData(*file, length, file_offset); 63 if (!data_sp) 64 return nullptr; 65 data_offset = 0; 66 } 67 if (!ObjectFileXCOFF::MagicBytesMatch(data_sp, data_offset, length)) 68 return nullptr; 69 // Update the data to contain the entire file if it doesn't already 70 if (data_sp->GetByteSize() < length) { 71 data_sp = MapFileData(*file, length, file_offset); 72 if (!data_sp) 73 return nullptr; 74 data_offset = 0; 75 } 76 auto objfile_up = std::make_unique<ObjectFileXCOFF>( 77 module_sp, data_sp, data_offset, file, file_offset, length); 78 if (!objfile_up) 79 return nullptr; 80 81 // Cache xcoff binary. 82 if (!objfile_up->CreateBinary()) 83 return nullptr; 84 85 if (!objfile_up->ParseHeader()) 86 return nullptr; 87 88 return objfile_up.release(); 89 } 90 91 bool ObjectFileXCOFF::CreateBinary() { 92 if (m_binary) 93 return true; 94 95 Log *log = GetLog(LLDBLog::Object); 96 97 auto memory_ref = llvm::MemoryBufferRef(toStringRef(m_data.GetData()), 98 m_file.GetFilename().GetStringRef()); 99 llvm::file_magic magic = llvm::identify_magic(memory_ref.getBuffer()); 100 101 auto binary = llvm::object::ObjectFile::createObjectFile(memory_ref, magic); 102 if (!binary) { 103 LLDB_LOG_ERROR(log, binary.takeError(), 104 "Failed to create binary for file ({1}): {0}", m_file); 105 return false; 106 } 107 // Make sure we only handle XCOFF format. 108 m_binary = 109 llvm::unique_dyn_cast<llvm::object::XCOFFObjectFile>(std::move(*binary)); 110 if (!m_binary) 111 return false; 112 113 LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", 114 this, GetModule().get(), GetModule()->GetSpecificationDescription(), 115 m_file.GetPath(), m_binary.get()); 116 117 return true; 118 } 119 120 ObjectFile *ObjectFileXCOFF::CreateMemoryInstance( 121 const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp, 122 const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { 123 return nullptr; 124 } 125 126 size_t ObjectFileXCOFF::GetModuleSpecifications( 127 const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, 128 lldb::offset_t data_offset, lldb::offset_t file_offset, 129 lldb::offset_t length, lldb_private::ModuleSpecList &specs) { 130 const size_t initial_count = specs.GetSize(); 131 132 if (ObjectFileXCOFF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { 133 ArchSpec arch_spec = 134 ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); 135 ModuleSpec spec(file, arch_spec); 136 spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, 137 LLDB_INVALID_CPUTYPE, 138 llvm::Triple::AIX); 139 specs.Append(spec); 140 } 141 return specs.GetSize() - initial_count; 142 } 143 144 static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { 145 switch (magic) { 146 case XCOFF::XCOFF32: 147 return sizeof(struct llvm::object::XCOFFFileHeader32); 148 break; 149 case XCOFF::XCOFF64: 150 return sizeof(struct llvm::object::XCOFFFileHeader64); 151 break; 152 153 default: 154 break; 155 } 156 return 0; 157 } 158 159 bool ObjectFileXCOFF::MagicBytesMatch(DataBufferSP &data_sp, 160 lldb::addr_t data_offset, 161 lldb::addr_t data_length) { 162 lldb_private::DataExtractor data; 163 data.SetData(data_sp, data_offset, data_length); 164 // Need to set this as XCOFF is only compatible with Big Endian 165 data.SetByteOrder(eByteOrderBig); 166 lldb::offset_t offset = 0; 167 uint16_t magic = data.GetU16(&offset); 168 return XCOFFHeaderSizeFromMagic(magic) != 0; 169 } 170 171 bool ObjectFileXCOFF::ParseHeader() { 172 if (m_binary->is64Bit()) 173 return m_binary->fileHeader64()->Magic == XCOFF::XCOFF64; 174 return m_binary->fileHeader32()->Magic == XCOFF::XCOFF32; 175 } 176 177 ByteOrder ObjectFileXCOFF::GetByteOrder() const { return eByteOrderBig; } 178 179 bool ObjectFileXCOFF::IsExecutable() const { return true; } 180 181 uint32_t ObjectFileXCOFF::GetAddressByteSize() const { 182 if (m_binary->is64Bit()) 183 return 8; 184 return 4; 185 } 186 187 AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { 188 return AddressClass::eUnknown; 189 } 190 191 static lldb::SymbolType MapSymbolType(llvm::object::SymbolRef::Type sym_type) { 192 switch (sym_type) { 193 case llvm::object::SymbolRef::ST_Function: 194 return lldb::eSymbolTypeCode; 195 case llvm::object::SymbolRef::ST_Data: 196 return lldb::eSymbolTypeData; 197 case llvm::object::SymbolRef::ST_File: 198 return lldb::eSymbolTypeSourceFile; 199 default: 200 return lldb::eSymbolTypeInvalid; 201 } 202 } 203 204 void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { 205 Log *log = GetLog(LLDBLog::Object); 206 SectionList *sectionList = GetSectionList(); 207 208 for (const auto &symbol_ref : m_binary->symbols()) { 209 llvm::object::XCOFFSymbolRef xcoff_sym_ref(symbol_ref); 210 211 llvm::Expected<llvm::StringRef> name_or_err = xcoff_sym_ref.getName(); 212 if (!name_or_err) { 213 LLDB_LOG_ERROR(log, name_or_err.takeError(), 214 "Unable to extract name from the xcoff symbol ref object"); 215 continue; 216 } 217 218 llvm::StringRef symbolName = name_or_err.get(); 219 // Remove the . prefix added during compilation. This prefix is usually 220 // added to differentiate between reference to the code and function 221 // descriptor. For instance, Adding .func will only allow user to put bp on 222 // .func, which is not known to the user, instead of func. 223 llvm::StringRef name_no_dot = 224 symbolName.starts_with(".") ? symbolName.drop_front() : symbolName; 225 auto storageClass = xcoff_sym_ref.getStorageClass(); 226 // C_HIDEXT symbols are not needed to be exposed, with the exception of TOC 227 // which is responsible for storing references to global data 228 if (storageClass == XCOFF::C_HIDEXT && symbolName != "TOC") { 229 230 // Zero or muliple aux entries may suggest ambiguous data 231 if (xcoff_sym_ref.getNumberOfAuxEntries() != 1) 232 continue; 233 234 auto aux_csect_or_err = xcoff_sym_ref.getXCOFFCsectAuxRef(); 235 if (!aux_csect_or_err) { 236 LLDB_LOG_ERROR(log, aux_csect_or_err.takeError(), 237 "Unable to access xcoff csect aux ref object"); 238 continue; 239 } 240 241 const llvm::object::XCOFFCsectAuxRef csect_aux = aux_csect_or_err.get(); 242 243 // Only add hidden ext entries which come under Program Code, skip others 244 // as they are not useful as debugging data. 245 if (csect_aux.getStorageMappingClass() != XCOFF::XMC_PR) 246 continue; 247 248 // This does not apply to 32-bit, 249 // Only add csect symbols identified by the aux entry, as they are 250 // needed to reference section information. Skip others 251 if (m_binary->is64Bit()) 252 if (csect_aux.getAuxType64() != XCOFF::AUX_CSECT) 253 continue; 254 } 255 256 Symbol symbol; 257 symbol.GetMangled().SetValue(ConstString(name_no_dot)); 258 259 int16_t sectionNumber = xcoff_sym_ref.getSectionNumber(); 260 // Note that XCOFF section headers are numbered from 1 and not 0. 261 size_t sectionIndex = static_cast<size_t>(sectionNumber - 1); 262 if (sectionNumber > 0) { 263 if (sectionIndex < sectionList->GetSize()) { 264 265 lldb::SectionSP section_sp = 266 sectionList->GetSectionAtIndex(sectionIndex); 267 if (!section_sp || section_sp->GetFileAddress() == LLDB_INVALID_ADDRESS) 268 continue; 269 270 lldb::addr_t file_addr = section_sp->GetFileAddress(); 271 lldb::addr_t symbolValue = xcoff_sym_ref.getValue(); 272 if (symbolValue < file_addr) 273 continue; 274 275 symbol.GetAddressRef() = Address(section_sp, symbolValue - file_addr); 276 } 277 } 278 279 Expected<llvm::object::SymbolRef::Type> sym_type_or_err = 280 symbol_ref.getType(); 281 if (!sym_type_or_err) { 282 LLDB_LOG_ERROR(log, sym_type_or_err.takeError(), 283 "Unable to access xcoff symbol type"); 284 continue; 285 } 286 287 symbol.SetType(MapSymbolType(sym_type_or_err.get())); 288 289 lldb_symtab.AddSymbol(symbol); 290 } 291 } 292 293 bool ObjectFileXCOFF::IsStripped() { return false; } 294 295 void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { 296 297 if (m_sections_up) 298 return; 299 300 m_sections_up = std::make_unique<SectionList>(); 301 if (m_binary->is64Bit()) 302 CreateSectionsWithBitness<XCOFF64>(unified_section_list); 303 else 304 CreateSectionsWithBitness<XCOFF32>(unified_section_list); 305 } 306 307 template <typename T> 308 static auto GetSections(llvm::object::XCOFFObjectFile *binary) { 309 if constexpr (T::Is64Bit) 310 return binary->sections64(); 311 else 312 return binary->sections32(); 313 } 314 315 template <typename T> 316 void ObjectFileXCOFF::CreateSectionsWithBitness( 317 SectionList &unified_section_list) { 318 ModuleSP module_sp(GetModule()); 319 if (!module_sp) 320 return; 321 322 std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); 323 324 int idx = 0; 325 for (const typename T::SectionHeader §ion : 326 GetSections<T>(m_binary.get())) { 327 328 ConstString const_sect_name(section.Name); 329 330 SectionType section_type = lldb::eSectionTypeOther; 331 if (section.Flags & XCOFF::STYP_TEXT) 332 section_type = eSectionTypeCode; 333 else if (section.Flags & XCOFF::STYP_DATA) 334 section_type = eSectionTypeData; 335 else if (section.Flags & XCOFF::STYP_BSS) 336 section_type = eSectionTypeZeroFill; 337 else if (section.Flags & XCOFF::STYP_DWARF) { 338 section_type = llvm::StringSwitch<SectionType>(section.Name) 339 .Case(".dwinfo", eSectionTypeDWARFDebugInfo) 340 .Case(".dwline", eSectionTypeDWARFDebugLine) 341 .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) 342 .Case(".dwrnges", eSectionTypeDWARFDebugRanges) 343 .Default(eSectionTypeInvalid); 344 } 345 346 SectionSP section_sp(new Section( 347 module_sp, this, ++idx, const_sect_name, section_type, 348 section.VirtualAddress, section.SectionSize, 349 section.FileOffsetToRawData, section.SectionSize, 0, section.Flags)); 350 351 uint32_t permissions = ePermissionsReadable; 352 if (section.Flags & (XCOFF::STYP_DATA | XCOFF::STYP_BSS)) 353 permissions |= ePermissionsWritable; 354 if (section.Flags & XCOFF::STYP_TEXT) 355 permissions |= ePermissionsExecutable; 356 357 section_sp->SetPermissions(permissions); 358 m_sections_up->AddSection(section_sp); 359 unified_section_list.AddSection(section_sp); 360 } 361 } 362 363 void ObjectFileXCOFF::Dump(Stream *s) {} 364 365 ArchSpec ObjectFileXCOFF::GetArchitecture() { 366 ArchSpec arch_spec = 367 ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); 368 return arch_spec; 369 } 370 371 UUID ObjectFileXCOFF::GetUUID() { return UUID(); } 372 373 uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { return 0; } 374 375 ObjectFile::Type ObjectFileXCOFF::CalculateType() { 376 377 const auto flags = m_binary->is64Bit() ? m_binary->fileHeader64()->Flags 378 : m_binary->fileHeader32()->Flags; 379 380 if (flags & XCOFF::F_EXEC) 381 return eTypeExecutable; 382 else if (flags & XCOFF::F_SHROBJ) 383 return eTypeSharedLibrary; 384 return eTypeUnknown; 385 } 386 387 ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; } 388 389 lldb::WritableDataBufferSP 390 ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, 391 uint64_t Offset) { 392 return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, 393 Offset); 394 } 395 396 ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, 397 DataBufferSP data_sp, 398 lldb::offset_t data_offset, 399 const FileSpec *file, 400 lldb::offset_t file_offset, 401 lldb::offset_t length) 402 : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) { 403 if (file) 404 m_file = *file; 405 } 406 407 ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, 408 DataBufferSP header_data_sp, 409 const lldb::ProcessSP &process_sp, 410 addr_t header_addr) 411 : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) {} 412