xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
LLDB_PLUGIN_DEFINE(ObjectFileXCOFF)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 
Terminate()51 void ObjectFileXCOFF::Terminate() {
52   PluginManager::UnregisterPlugin(CreateInstance);
53 }
54 
CreateInstance(const lldb::ModuleSP & module_sp,DataBufferSP data_sp,lldb::offset_t data_offset,const lldb_private::FileSpec * file,lldb::offset_t file_offset,lldb::offset_t length)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 
CreateBinary()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 
CreateMemoryInstance(const lldb::ModuleSP & module_sp,WritableDataBufferSP data_sp,const lldb::ProcessSP & process_sp,lldb::addr_t header_addr)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 
GetModuleSpecifications(const lldb_private::FileSpec & file,lldb::DataBufferSP & data_sp,lldb::offset_t data_offset,lldb::offset_t file_offset,lldb::offset_t length,lldb_private::ModuleSpecList & specs)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 
XCOFFHeaderSizeFromMagic(uint32_t magic)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 
MagicBytesMatch(DataBufferSP & data_sp,lldb::addr_t data_offset,lldb::addr_t data_length)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 
ParseHeader()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 
GetByteOrder() const177 ByteOrder ObjectFileXCOFF::GetByteOrder() const { return eByteOrderBig; }
178 
IsExecutable() const179 bool ObjectFileXCOFF::IsExecutable() const { return true; }
180 
GetAddressByteSize() const181 uint32_t ObjectFileXCOFF::GetAddressByteSize() const {
182   if (m_binary->is64Bit())
183     return 8;
184   return 4;
185 }
186 
GetAddressClass(addr_t file_addr)187 AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) {
188   return AddressClass::eUnknown;
189 }
190 
MapSymbolType(llvm::object::SymbolRef::Type sym_type)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 
ParseSymtab(Symtab & lldb_symtab)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 
IsStripped()293 bool ObjectFileXCOFF::IsStripped() { return false; }
294 
CreateSections(SectionList & unified_section_list)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>
GetSections(llvm::object::XCOFFObjectFile * binary)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>
CreateSectionsWithBitness(SectionList & unified_section_list)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 &section :
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 
Dump(Stream * s)363 void ObjectFileXCOFF::Dump(Stream *s) {}
364 
GetArchitecture()365 ArchSpec ObjectFileXCOFF::GetArchitecture() {
366   ArchSpec arch_spec =
367       ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE);
368   return arch_spec;
369 }
370 
GetUUID()371 UUID ObjectFileXCOFF::GetUUID() { return UUID(); }
372 
GetDependentModules(FileSpecList & files)373 uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { return 0; }
374 
CalculateType()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 
CalculateStrata()387 ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; }
388 
389 lldb::WritableDataBufferSP
MapFileDataWritable(const FileSpec & file,uint64_t Size,uint64_t Offset)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 
ObjectFileXCOFF(const lldb::ModuleSP & module_sp,DataBufferSP data_sp,lldb::offset_t data_offset,const FileSpec * file,lldb::offset_t file_offset,lldb::offset_t length)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 
ObjectFileXCOFF(const lldb::ModuleSP & module_sp,DataBufferSP header_data_sp,const lldb::ProcessSP & process_sp,addr_t header_addr)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