clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
404 lines
15 KiB
C++
404 lines
15 KiB
C++
//===-- IRExecutionUnit.h ---------------------------------------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLDB_EXPRESSION_IREXECUTIONUNIT_H
|
|
#define LLDB_EXPRESSION_IREXECUTIONUNIT_H
|
|
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "lldb/Expression/IRMemoryMap.h"
|
|
#include "lldb/Expression/ObjectFileJIT.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Utility/DataBufferHeap.h"
|
|
#include "lldb/lldb-forward.h"
|
|
#include "lldb/lldb-private.h"
|
|
|
|
namespace llvm {
|
|
|
|
class Module;
|
|
class ExecutionEngine;
|
|
class ObjectCache;
|
|
|
|
} // namespace llvm
|
|
|
|
namespace lldb_private {
|
|
|
|
class Status;
|
|
|
|
/// \class IRExecutionUnit IRExecutionUnit.h
|
|
/// "lldb/Expression/IRExecutionUnit.h" Contains the IR and, optionally, JIT-
|
|
/// compiled code for a module.
|
|
///
|
|
/// This class encapsulates the compiled version of an expression, in IR form
|
|
/// (for interpretation purposes) and in raw machine code form (for execution
|
|
/// in the target).
|
|
///
|
|
/// This object wraps an IR module that comes from the expression parser, and
|
|
/// knows how to use the JIT to make it into executable code. It can then be
|
|
/// used as input to the IR interpreter, or the address of the executable code
|
|
/// can be passed to a thread plan to run in the target.
|
|
///
|
|
/// This class creates a subclass of LLVM's SectionMemoryManager, because that
|
|
/// is how the JIT emits code. Because LLDB needs to move JIT-compiled code
|
|
/// into the target process, the IRExecutionUnit knows how to copy the emitted
|
|
/// code into the target process.
|
|
class IRExecutionUnit : public std::enable_shared_from_this<IRExecutionUnit>,
|
|
public IRMemoryMap,
|
|
public ObjectFileJITDelegate {
|
|
public:
|
|
/// Constructor
|
|
IRExecutionUnit(std::unique_ptr<llvm::LLVMContext> &context_up,
|
|
std::unique_ptr<llvm::Module> &module_up, ConstString &name,
|
|
const lldb::TargetSP &target_sp, const SymbolContext &sym_ctx,
|
|
std::vector<std::string> &cpu_features);
|
|
|
|
/// Destructor
|
|
~IRExecutionUnit() override;
|
|
|
|
ConstString GetFunctionName() { return m_name; }
|
|
|
|
llvm::Module *GetModule() { return m_module; }
|
|
|
|
llvm::Function *GetFunction() {
|
|
return ((m_module != nullptr) ? m_module->getFunction(m_name.GetStringRef())
|
|
: nullptr);
|
|
}
|
|
|
|
void GetRunnableInfo(Status &error, lldb::addr_t &func_addr,
|
|
lldb::addr_t &func_end);
|
|
|
|
/// Accessors for IRForTarget and other clients that may want binary data
|
|
/// placed on their behalf. The binary data is owned by the IRExecutionUnit
|
|
/// unless the client explicitly chooses to free it.
|
|
|
|
lldb::addr_t WriteNow(const uint8_t *bytes, size_t size, Status &error);
|
|
|
|
void FreeNow(lldb::addr_t allocation);
|
|
|
|
/// ObjectFileJITDelegate overrides
|
|
lldb::ByteOrder GetByteOrder() const override;
|
|
|
|
uint32_t GetAddressByteSize() const override;
|
|
|
|
void PopulateSymtab(lldb_private::ObjectFile *obj_file,
|
|
lldb_private::Symtab &symtab) override;
|
|
|
|
void PopulateSectionList(lldb_private::ObjectFile *obj_file,
|
|
lldb_private::SectionList §ion_list) override;
|
|
|
|
ArchSpec GetArchitecture() override;
|
|
|
|
lldb::ModuleSP GetJITModule();
|
|
|
|
lldb::addr_t FindSymbol(ConstString name, bool &missing_weak);
|
|
|
|
void GetStaticInitializers(std::vector<lldb::addr_t> &static_initializers);
|
|
|
|
/// \class JittedFunction IRExecutionUnit.h
|
|
/// "lldb/Expression/IRExecutionUnit.h"
|
|
/// Encapsulates a single function that has been generated by the JIT.
|
|
///
|
|
/// Functions that have been generated by the JIT are first resident in the
|
|
/// local process, and then placed in the target process. JittedFunction
|
|
/// represents a function possibly resident in both.
|
|
struct JittedEntity {
|
|
ConstString m_name; ///< The function's name
|
|
lldb::addr_t m_local_addr; ///< The address of the function in LLDB's memory
|
|
lldb::addr_t
|
|
m_remote_addr; ///< The address of the function in the target's memory
|
|
|
|
/// Constructor
|
|
///
|
|
/// Initializes class variabes.
|
|
///
|
|
/// \param[in] name
|
|
/// The name of the function.
|
|
///
|
|
/// \param[in] local_addr
|
|
/// The address of the function in LLDB, or LLDB_INVALID_ADDRESS if
|
|
/// it is not present in LLDB's memory.
|
|
///
|
|
/// \param[in] remote_addr
|
|
/// The address of the function in the target, or LLDB_INVALID_ADDRESS
|
|
/// if it is not present in the target's memory.
|
|
JittedEntity(const char *name,
|
|
lldb::addr_t local_addr = LLDB_INVALID_ADDRESS,
|
|
lldb::addr_t remote_addr = LLDB_INVALID_ADDRESS)
|
|
: m_name(name), m_local_addr(local_addr), m_remote_addr(remote_addr) {}
|
|
};
|
|
|
|
struct JittedFunction : JittedEntity {
|
|
bool m_external;
|
|
JittedFunction(const char *name, bool external,
|
|
lldb::addr_t local_addr = LLDB_INVALID_ADDRESS,
|
|
lldb::addr_t remote_addr = LLDB_INVALID_ADDRESS)
|
|
: JittedEntity(name, local_addr, remote_addr), m_external(external) {}
|
|
};
|
|
|
|
struct JittedGlobalVariable : JittedEntity {
|
|
JittedGlobalVariable(const char *name,
|
|
lldb::addr_t local_addr = LLDB_INVALID_ADDRESS,
|
|
lldb::addr_t remote_addr = LLDB_INVALID_ADDRESS)
|
|
: JittedEntity(name, local_addr, remote_addr) {}
|
|
};
|
|
|
|
const std::vector<JittedFunction> &GetJittedFunctions() {
|
|
return m_jitted_functions;
|
|
}
|
|
|
|
const std::vector<JittedGlobalVariable> &GetJittedGlobalVariables() {
|
|
return m_jitted_global_variables;
|
|
}
|
|
|
|
private:
|
|
/// Look up the object in m_address_map that contains a given address, find
|
|
/// where it was copied to, and return the remote address at the same offset
|
|
/// into the copied entity
|
|
///
|
|
/// \param[in] local_address
|
|
/// The address in the debugger.
|
|
///
|
|
/// \return
|
|
/// The address in the target process.
|
|
lldb::addr_t GetRemoteAddressForLocal(lldb::addr_t local_address);
|
|
|
|
typedef std::pair<lldb::addr_t, uintptr_t> AddrRange;
|
|
|
|
/// Look up the object in m_address_map that contains a given address, find
|
|
/// where it was copied to, and return its address range in the target
|
|
/// process
|
|
///
|
|
/// \param[in] local_address
|
|
/// The address in the debugger.
|
|
///
|
|
/// \return
|
|
/// The range of the containing object in the target process.
|
|
AddrRange GetRemoteRangeForLocal(lldb::addr_t local_address);
|
|
|
|
/// Commit all allocations to the process and record where they were stored.
|
|
///
|
|
/// \param[in] process_sp
|
|
/// The process to allocate memory in.
|
|
///
|
|
/// \return
|
|
/// True <=> all allocations were performed successfully.
|
|
/// This method will attempt to free allocated memory if the
|
|
/// operation fails.
|
|
bool CommitAllocations(lldb::ProcessSP &process_sp);
|
|
|
|
/// Report all committed allocations to the execution engine.
|
|
///
|
|
/// \param[in] engine
|
|
/// The execution engine to notify.
|
|
void ReportAllocations(llvm::ExecutionEngine &engine);
|
|
|
|
/// Write the contents of all allocations to the process.
|
|
///
|
|
/// \param[in] process_sp
|
|
/// The process containing the allocations.
|
|
///
|
|
/// \return
|
|
/// True <=> all allocations were performed successfully.
|
|
bool WriteData(lldb::ProcessSP &process_sp);
|
|
|
|
Status DisassembleFunction(Stream &stream, lldb::ProcessSP &process_sp);
|
|
|
|
void CollectCandidateCNames(std::vector<ConstString> &C_names,
|
|
ConstString name);
|
|
|
|
void CollectCandidateCPlusPlusNames(std::vector<ConstString> &CPP_names,
|
|
const std::vector<ConstString> &C_names,
|
|
const SymbolContext &sc);
|
|
|
|
lldb::addr_t FindInSymbols(const std::vector<ConstString> &names,
|
|
const lldb_private::SymbolContext &sc,
|
|
bool &symbol_was_missing_weak);
|
|
|
|
lldb::addr_t FindInRuntimes(const std::vector<ConstString> &names,
|
|
const lldb_private::SymbolContext &sc);
|
|
|
|
lldb::addr_t FindInUserDefinedSymbols(const std::vector<ConstString> &names,
|
|
const lldb_private::SymbolContext &sc);
|
|
|
|
void ReportSymbolLookupError(ConstString name);
|
|
|
|
class MemoryManager : public llvm::SectionMemoryManager {
|
|
public:
|
|
MemoryManager(IRExecutionUnit &parent);
|
|
|
|
~MemoryManager() override;
|
|
|
|
/// Allocate space for executable code, and add it to the m_spaceBlocks
|
|
/// map
|
|
///
|
|
/// \param[in] Size
|
|
/// The size of the area.
|
|
///
|
|
/// \param[in] Alignment
|
|
/// The required alignment of the area.
|
|
///
|
|
/// \param[in] SectionID
|
|
/// A unique identifier for the section.
|
|
///
|
|
/// \return
|
|
/// Allocated space.
|
|
uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
|
|
unsigned SectionID,
|
|
llvm::StringRef SectionName) override;
|
|
|
|
/// Allocate space for data, and add it to the m_spaceBlocks map
|
|
///
|
|
/// \param[in] Size
|
|
/// The size of the area.
|
|
///
|
|
/// \param[in] Alignment
|
|
/// The required alignment of the area.
|
|
///
|
|
/// \param[in] SectionID
|
|
/// A unique identifier for the section.
|
|
///
|
|
/// \param[in] IsReadOnly
|
|
/// Flag indicating the section is read-only.
|
|
///
|
|
/// \return
|
|
/// Allocated space.
|
|
uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
|
|
unsigned SectionID,
|
|
llvm::StringRef SectionName,
|
|
bool IsReadOnly) override;
|
|
|
|
/// Called when object loading is complete and section page permissions
|
|
/// can be applied. Currently unimplemented for LLDB.
|
|
///
|
|
/// \param[out] ErrMsg
|
|
/// The error that prevented the page protection from succeeding.
|
|
///
|
|
/// \return
|
|
/// True in case of failure, false in case of success.
|
|
bool finalizeMemory(std::string *ErrMsg) override {
|
|
// TODO: Ensure that the instruction cache is flushed because
|
|
// relocations are updated by dy-load. See:
|
|
// sys::Memory::InvalidateInstructionCache
|
|
// llvm::SectionMemoryManager
|
|
return false;
|
|
}
|
|
|
|
// Ignore any EHFrame registration.
|
|
void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr,
|
|
size_t Size) override {}
|
|
void deregisterEHFrames() override {}
|
|
|
|
uint64_t getSymbolAddress(const std::string &Name) override;
|
|
|
|
// Find the address of the symbol Name. If Name is a missing weak symbol
|
|
// then missing_weak will be true.
|
|
uint64_t GetSymbolAddressAndPresence(const std::string &Name,
|
|
bool &missing_weak);
|
|
|
|
llvm::JITSymbol findSymbol(const std::string &Name) override;
|
|
|
|
void *getPointerToNamedFunction(const std::string &Name,
|
|
bool AbortOnFailure = true) override;
|
|
|
|
private:
|
|
std::unique_ptr<SectionMemoryManager> m_default_mm_up; ///< The memory
|
|
/// allocator to use
|
|
/// in actually
|
|
/// creating space.
|
|
/// All calls are
|
|
/// passed through to
|
|
/// it.
|
|
IRExecutionUnit &m_parent; ///< The execution unit this is a proxy for.
|
|
};
|
|
|
|
static const unsigned eSectionIDInvalid = (unsigned)-1;
|
|
|
|
enum class AllocationKind { Stub, Code, Data, Global, Bytes };
|
|
|
|
static lldb::SectionType
|
|
GetSectionTypeFromSectionName(const llvm::StringRef &name,
|
|
AllocationKind alloc_kind);
|
|
|
|
/// Encapsulates a single allocation request made by the JIT.
|
|
///
|
|
/// Allocations made by the JIT are first queued up and then applied in bulk
|
|
/// to the underlying process.
|
|
struct AllocationRecord {
|
|
std::string m_name;
|
|
lldb::addr_t m_process_address;
|
|
uintptr_t m_host_address;
|
|
uint32_t m_permissions;
|
|
lldb::SectionType m_sect_type;
|
|
size_t m_size;
|
|
unsigned m_alignment;
|
|
unsigned m_section_id;
|
|
|
|
AllocationRecord(uintptr_t host_address, uint32_t permissions,
|
|
lldb::SectionType sect_type, size_t size,
|
|
unsigned alignment, unsigned section_id, const char *name)
|
|
: m_process_address(LLDB_INVALID_ADDRESS), m_host_address(host_address),
|
|
m_permissions(permissions), m_sect_type(sect_type), m_size(size),
|
|
m_alignment(alignment), m_section_id(section_id) {
|
|
if (name && name[0])
|
|
m_name = name;
|
|
}
|
|
|
|
void dump(Log *log);
|
|
};
|
|
|
|
bool CommitOneAllocation(lldb::ProcessSP &process_sp, Status &error,
|
|
AllocationRecord &record);
|
|
|
|
typedef std::vector<AllocationRecord> RecordVector;
|
|
RecordVector m_records;
|
|
|
|
std::unique_ptr<llvm::LLVMContext> m_context_up;
|
|
std::unique_ptr<llvm::ExecutionEngine> m_execution_engine_up;
|
|
std::unique_ptr<llvm::ObjectCache> m_object_cache_up;
|
|
std::unique_ptr<llvm::Module>
|
|
m_module_up; ///< Holder for the module until it's been handed off
|
|
llvm::Module *m_module; ///< Owned by the execution engine
|
|
std::vector<std::string> m_cpu_features;
|
|
std::vector<JittedFunction> m_jitted_functions; ///< A vector of all functions
|
|
///that have been JITted into
|
|
///machine code
|
|
std::vector<JittedGlobalVariable> m_jitted_global_variables; ///< A vector of
|
|
///all functions
|
|
///that have been
|
|
///JITted into
|
|
///machine code
|
|
const ConstString m_name;
|
|
SymbolContext m_sym_ctx; ///< Used for symbol lookups
|
|
std::vector<ConstString> m_failed_lookups;
|
|
|
|
std::atomic<bool> m_did_jit;
|
|
|
|
lldb::addr_t m_function_load_addr;
|
|
lldb::addr_t m_function_end_load_addr;
|
|
|
|
bool m_strip_underscore = true; ///< True for platforms where global symbols
|
|
/// have a _ prefix
|
|
bool m_reported_allocations; ///< True after allocations have been reported.
|
|
///It is possible that
|
|
///< sections will be allocated when this is true, in which case they weren't
|
|
///< depended on by any function. (Top-level code defining a variable, but
|
|
///< defining no functions using that variable, would do this.) If this
|
|
///< is true, any allocations need to be committed immediately -- no
|
|
///< opportunity for relocation.
|
|
};
|
|
|
|
} // namespace lldb_private
|
|
|
|
#endif // LLDB_EXPRESSION_IREXECUTIONUNIT_H
|