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 §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
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