Files
llvm/lldb/source/Expression/ObjectFileJIT.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

225 lines
7.5 KiB
C++
Raw Normal View History

//===-- ObjectFileJIT.cpp -------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Expression/ObjectFileJIT.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/DataBuffer.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/FileSpecList.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Timer.h"
#include "lldb/Utility/UUID.h"
using namespace lldb;
using namespace lldb_private;
char ObjectFileJIT::ID;
void ObjectFileJIT::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance,
CreateMemoryInstance, GetModuleSpecifications);
}
void ObjectFileJIT::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
}
ObjectFile *ObjectFileJIT::CreateInstance(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) {
// JIT'ed object file is backed by the ObjectFileJITDelegate, never read from
// a file
return nullptr;
}
ObjectFile *ObjectFileJIT::CreateMemoryInstance(const lldb::ModuleSP &module_sp,
WritableDataBufferSP data_sp,
const ProcessSP &process_sp,
lldb::addr_t header_addr) {
// JIT'ed object file is backed by the ObjectFileJITDelegate, never read from
// memory
return nullptr;
}
size_t ObjectFileJIT::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) {
// JIT'ed object file can't be read from a file on disk
return 0;
}
ObjectFileJIT::ObjectFileJIT(const lldb::ModuleSP &module_sp,
const ObjectFileJITDelegateSP &delegate_sp)
: ObjectFile(module_sp, nullptr, 0, 0, DataBufferSP(), 0), m_delegate_wp() {
if (delegate_sp) {
m_delegate_wp = delegate_sp;
[lldb][NFC] Change ObjectFile's DataExtractor to a shared ptr (#170066) ObjectFile has an m_data DataExtractor ivar which may be default constructed initially, or initialized with a DataBuffer passed in to its ctor. If the DataExtractor does not get a DataBuffer source passed in, the subclass will initialize it with access to the object file's data. When a DataBuffer is passed in to the base class ctor, the DataExtractor only has its buffer initialized; ObjectFile doesn't yet know the address size and endianness to fully initialize the DataExtractor. This patch changes ObjectFile to instead have a DataExtractorSP ivar which is always initialized with at least a default-constructed DataExtractor object in the base class ctor. The next patch I will be writing is to change the ObjectFile ctor to take an optional DataExtractorSP, so the caller can pass a DataExtractor subclass -- the VirtualizeDataExtractor being added via https://github.com/llvm/llvm-project/pull/168802 instead of a DataBuffer which is trivially saved into the DataExtractor. The change is otherwise mechanical; all `m_data.` changed to `m_data_sp->` and all the places where `m_data` was passed in for a by-ref call were changed to `*m_data_sp.get()`. The shared pointer is always initialized to contain an object. I built & ran the testsuite on macOS and on aarch64-Ubuntu (thanks for getting the Linux testsuite to run on SME-only systems David). All of the ObjectFile subclasses I modifed compile cleanly, but I haven't tested them beyond any unit tests they may have (prob breakpad). rdar://148939795
2025-12-01 14:37:55 -08:00
m_data_nsp->SetByteOrder(delegate_sp->GetByteOrder());
m_data_nsp->SetAddressByteSize(delegate_sp->GetAddressByteSize());
}
}
ObjectFileJIT::~ObjectFileJIT() = default;
bool ObjectFileJIT::ParseHeader() {
// JIT code is never in a file, nor is it required to have any header
return false;
}
[lldb][NFC] Change ObjectFile's DataExtractor to a shared ptr (#170066) ObjectFile has an m_data DataExtractor ivar which may be default constructed initially, or initialized with a DataBuffer passed in to its ctor. If the DataExtractor does not get a DataBuffer source passed in, the subclass will initialize it with access to the object file's data. When a DataBuffer is passed in to the base class ctor, the DataExtractor only has its buffer initialized; ObjectFile doesn't yet know the address size and endianness to fully initialize the DataExtractor. This patch changes ObjectFile to instead have a DataExtractorSP ivar which is always initialized with at least a default-constructed DataExtractor object in the base class ctor. The next patch I will be writing is to change the ObjectFile ctor to take an optional DataExtractorSP, so the caller can pass a DataExtractor subclass -- the VirtualizeDataExtractor being added via https://github.com/llvm/llvm-project/pull/168802 instead of a DataBuffer which is trivially saved into the DataExtractor. The change is otherwise mechanical; all `m_data.` changed to `m_data_sp->` and all the places where `m_data` was passed in for a by-ref call were changed to `*m_data_sp.get()`. The shared pointer is always initialized to contain an object. I built & ran the testsuite on macOS and on aarch64-Ubuntu (thanks for getting the Linux testsuite to run on SME-only systems David). All of the ObjectFile subclasses I modifed compile cleanly, but I haven't tested them beyond any unit tests they may have (prob breakpad). rdar://148939795
2025-12-01 14:37:55 -08:00
ByteOrder ObjectFileJIT::GetByteOrder() const {
return m_data_nsp->GetByteOrder();
}
bool ObjectFileJIT::IsExecutable() const { return false; }
uint32_t ObjectFileJIT::GetAddressByteSize() const {
[lldb][NFC] Change ObjectFile's DataExtractor to a shared ptr (#170066) ObjectFile has an m_data DataExtractor ivar which may be default constructed initially, or initialized with a DataBuffer passed in to its ctor. If the DataExtractor does not get a DataBuffer source passed in, the subclass will initialize it with access to the object file's data. When a DataBuffer is passed in to the base class ctor, the DataExtractor only has its buffer initialized; ObjectFile doesn't yet know the address size and endianness to fully initialize the DataExtractor. This patch changes ObjectFile to instead have a DataExtractorSP ivar which is always initialized with at least a default-constructed DataExtractor object in the base class ctor. The next patch I will be writing is to change the ObjectFile ctor to take an optional DataExtractorSP, so the caller can pass a DataExtractor subclass -- the VirtualizeDataExtractor being added via https://github.com/llvm/llvm-project/pull/168802 instead of a DataBuffer which is trivially saved into the DataExtractor. The change is otherwise mechanical; all `m_data.` changed to `m_data_sp->` and all the places where `m_data` was passed in for a by-ref call were changed to `*m_data_sp.get()`. The shared pointer is always initialized to contain an object. I built & ran the testsuite on macOS and on aarch64-Ubuntu (thanks for getting the Linux testsuite to run on SME-only systems David). All of the ObjectFile subclasses I modifed compile cleanly, but I haven't tested them beyond any unit tests they may have (prob breakpad). rdar://148939795
2025-12-01 14:37:55 -08:00
return m_data_nsp->GetAddressByteSize();
}
[NFC] Refactor symbol table parsing. Symbol table parsing has evolved over the years and many plug-ins contained duplicate code in the ObjectFile::GetSymtab() that used to be pure virtual. With this change, the "Symbtab *ObjectFile::GetSymtab()" is no longer virtual and will end up calling a new "void ObjectFile::ParseSymtab(Symtab &symtab)" pure virtual function to actually do the parsing. This helps centralize the code for parsing the symbol table and allows the ObjectFile base class to do all of the common work, like taking the necessary locks and creating the symbol table object itself. Plug-ins now just need to parse when they are asked to parse as the ParseSymtab function will only get called once. This is a retry of the original patch https://reviews.llvm.org/D113965 which was reverted. There was a deadlock in the Manual DWARF indexing code during symbol preloading where the module was asked on the main thread to preload its symbols, and this would in turn cause the DWARF manual indexing to use a thread pool to index all of the compile units, and if there were relocations on the debug information sections, these threads could ask the ObjectFile to load section contents, which could cause a call to ObjectFileELF::RelocateSection() which would ask for the symbol table from the module and it would deadlock. We can't lock the module in ObjectFile::GetSymtab(), so the solution I am using is to use a llvm::once_flag to create the symbol table object once and then lock the Symtab object. Since all APIs on the symbol table use this lock, this will prevent anyone from using the symbol table before it is parsed and finalized and will avoid the deadlock I mentioned. ObjectFileELF::GetSymtab() was never locking the module lock before and would put off creating the symbol table until somewhere inside ObjectFileELF::GetSymtab(). Now we create it one time inside of the ObjectFile::GetSymtab() and immediately lock it which should be safe enough. This avoids the deadlocks and still provides safety. Differential Revision: https://reviews.llvm.org/D114288
2021-11-17 21:18:24 -08:00
void ObjectFileJIT::ParseSymtab(Symtab &symtab) {
ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock());
if (delegate_sp)
delegate_sp->PopulateSymtab(this, symtab);
}
bool ObjectFileJIT::IsStripped() {
return false; // JIT code that is in a module is never stripped
}
void ObjectFileJIT::CreateSections(SectionList &unified_section_list) {
if (!m_sections_up) {
m_sections_up = std::make_unique<SectionList>();
ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock());
if (delegate_sp) {
delegate_sp->PopulateSectionList(this, *m_sections_up);
unified_section_list = *m_sections_up;
}
}
}
void ObjectFileJIT::Dump(Stream *s) {
ModuleSP module_sp(GetModule());
if (module_sp) {
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
s->Printf("%p: ", static_cast<void *>(this));
s->Indent();
s->PutCString("ObjectFileJIT");
if (ArchSpec arch = GetArchitecture())
*s << ", arch = " << arch.GetArchitectureName();
s->EOL();
SectionList *sections = GetSectionList();
if (sections)
sections->Dump(s->AsRawOstream(), s->GetIndentLevel(), nullptr, true,
UINT32_MAX);
if (m_symtab_up)
m_symtab_up->Dump(s, nullptr, eSortOrderNone);
}
}
UUID ObjectFileJIT::GetUUID() {
// TODO: maybe get from delegate, not needed for first pass
return UUID();
}
uint32_t ObjectFileJIT::GetDependentModules(FileSpecList &files) {
// JIT modules don't have dependencies, but they could
// if external functions are called and we know where they are
files.Clear();
return 0;
}
lldb_private::Address ObjectFileJIT::GetEntryPointAddress() {
return Address();
}
lldb_private::Address ObjectFileJIT::GetBaseAddress() { return Address(); }
ObjectFile::Type ObjectFileJIT::CalculateType() { return eTypeJIT; }
ObjectFile::Strata ObjectFileJIT::CalculateStrata() { return eStrataJIT; }
ArchSpec ObjectFileJIT::GetArchitecture() {
if (ObjectFileJITDelegateSP delegate_sp = m_delegate_wp.lock())
return delegate_sp->GetArchitecture();
return ArchSpec();
}
bool ObjectFileJIT::SetLoadAddress(Target &target, lldb::addr_t value,
bool value_is_offset) {
size_t num_loaded_sections = 0;
SectionList *section_list = GetSectionList();
if (section_list) {
const size_t num_sections = section_list->GetSize();
// "value" is an offset to apply to each top level segment
for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
// Iterate through the object file sections to find all of the sections
// that size on disk (to avoid __PAGEZERO) and load them
SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
if (section_sp && section_sp->GetFileSize() > 0 &&
!section_sp->IsThreadSpecific()) {
if (target.SetSectionLoadAddress(section_sp,
section_sp->GetFileAddress() + value))
++num_loaded_sections;
}
}
}
return num_loaded_sections > 0;
}
size_t ObjectFileJIT::ReadSectionData(lldb_private::Section *section,
lldb::offset_t section_offset, void *dst,
size_t dst_len) {
lldb::offset_t file_size = section->GetFileSize();
if (section_offset < file_size) {
size_t src_len = file_size - section_offset;
if (src_len > dst_len)
src_len = dst_len;
const uint8_t *src =
((uint8_t *)(uintptr_t)section->GetFileOffset()) + section_offset;
memcpy(dst, src, src_len);
return src_len;
}
return 0;
}
size_t
ObjectFileJIT::ReadSectionData(lldb_private::Section *section,
lldb_private::DataExtractor &section_data) {
if (section->GetFileSize()) {
const void *src = (void *)(uintptr_t)section->GetFileOffset();
DataBufferSP data_sp =
std::make_shared<DataBufferHeap>(src, section->GetFileSize());
section_data.SetData(data_sp, 0, data_sp->GetByteSize());
section_data.SetByteOrder(GetByteOrder());
section_data.SetAddressByteSize(GetAddressByteSize());
return section_data.GetByteSize();
}
section_data.Clear();
return 0;
}