Author: Jason Molenda Date: 2024-03-06T10:06:56-08:00 New Revision: aeaa11aeac0aedf32cafec6532d303fea595c5fc
URL: https://github.com/llvm/llvm-project/commit/aeaa11aeac0aedf32cafec6532d303fea595c5fc DIFF: https://github.com/llvm/llvm-project/commit/aeaa11aeac0aedf32cafec6532d303fea595c5fc.diff LOG: [lldb] Address mask sbprocess apis and new mask invalid const (#83663) [lldb] Add SBProcess methods for get/set/use address masks (#83095) I'm reviving a patch from phabracator, https://reviews.llvm.org/D155905 which was approved but I wasn't thrilled with all the API I was adding to SBProcess for all of the address mask types / memory regions. In this update, I added enums to control type address mask type (code, data, any) and address space specifiers (low, high, all) with defaulted arguments for the most common case. I originally landed this via https://github.com/llvm/llvm-project/pull/83095 but it failed on CIs outside of arm64 Darwin so I had to debug it on more environments and update the patch. This patch is also fixing a bug in the "addressable bits to address mask" calculation I added in AddressableBits::SetProcessMasks. If lldb were told that 64 bits are valid for addressing, this method would overflow the calculation and set an invalid mask. Added tests to check this specific bug while I was adding these APIs. This patch changes the value of "no mask set" from 0 to LLDB_INVALID_ADDRESS_MASK, which is UINT64_MAX. A mask of all 1's means "no bits are used for addressing" which is an impossible mask, whereas a mask of 0 means "all bits are used for addressing" which is possible. I added a base class implementation of ABI::FixCodeAddress and ABI::FixDataAddress that will apply the Process mask values if they are set to a value other than LLDB_INVALID_ADDRESS_MASK. I updated all the callers/users of the Mask methods which were handling a value of 0 to mean invalid mask to use LLDB_INVALID_ADDRESS_MASK. I added code to the all AArch64 ABI Fix* methods to apply the Highmem masks if they have been set. These will not be set on a Linux environment, but in TestAddressMasks.py I test the highmem masks feature for any AArch64 target, so all AArch64 ABI plugins must handle it. rdar://123530562 Added: lldb/test/API/python_api/process/address-masks/Makefile lldb/test/API/python_api/process/address-masks/TestAddressMasks.py lldb/test/API/python_api/process/address-masks/main.c Modified: lldb/include/lldb/API/SBProcess.h lldb/include/lldb/Target/ABI.h lldb/include/lldb/Target/Process.h lldb/include/lldb/Utility/AddressableBits.h lldb/include/lldb/lldb-defines.h lldb/include/lldb/lldb-enumerations.h lldb/source/API/SBProcess.cpp lldb/source/Commands/CommandObjectProcess.cpp lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp lldb/source/Target/ABI.cpp lldb/source/Target/Process.cpp lldb/source/Utility/AddressableBits.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/API/SBProcess.h b/lldb/include/lldb/API/SBProcess.h index 4f92a41f3028a2..7da3335a7234b7 100644 --- a/lldb/include/lldb/API/SBProcess.h +++ b/lldb/include/lldb/API/SBProcess.h @@ -407,6 +407,120 @@ class LLDB_API SBProcess { /// the process isn't loaded from a core file. lldb::SBFileSpec GetCoreFile(); + /// \{ + /// \group Mask Address Methods + /// + /// \a type + /// All of the methods in this group take \a type argument + /// which is an AddressMaskType enum value. + /// There can be diff erent address masks for code addresses and + /// data addresses, this argument can select which to get/set, + /// or to use when clearing non-addressable bits from an address. + /// This choice of mask can be important for example on AArch32 + /// systems. Where instructions where instructions start on even addresses, + /// the 0th bit may be used to indicate that a function is thumb code. On + /// such a target, the eAddressMaskTypeCode may clear the 0th bit from an + /// address to get the actual address Whereas eAddressMaskTypeData would not. + /// + /// \a addr_range + /// Many of the methods in this group take an \a addr_range argument + /// which is an AddressMaskRange enum value. + /// Needing to specify the address range is highly unusual, and the + /// default argument can be used in nearly all circumstances. + /// On some architectures (e.g., AArch64), it is possible to have + /// diff erent page table setups for low and high memory, so diff erent + /// numbers of bits relevant to addressing. It is possible to have + /// a program running in one half of memory and accessing the other + /// as heap, so we need to maintain two diff erent sets of address masks + /// to debug this correctly. + + /// Get the current address mask that will be applied to addresses + /// before reading from memory. + /// + /// \param[in] type + /// See \ref Mask Address Methods description of this argument. + /// eAddressMaskTypeAny is often a suitable value when code and + /// data masks are the same on a given target. + /// + /// \param[in] addr_range + /// See \ref Mask Address Methods description of this argument. + /// This will default to eAddressMaskRangeLow which is the + /// only set of masks used normally. + /// + /// \return + /// The address mask currently in use. Bits which are not used + /// for addressing will be set to 1 in the mask. + lldb::addr_t GetAddressMask( + lldb::AddressMaskType type, + lldb::AddressMaskRange addr_range = lldb::eAddressMaskRangeLow); + + /// Set the current address mask that can be applied to addresses + /// before reading from memory. + /// + /// \param[in] type + /// See \ref Mask Address Methods description of this argument. + /// eAddressMaskTypeAll is often a suitable value when the + /// same mask is being set for both code and data. + /// + /// \param[in] mask + /// The address mask to set. Bits which are not used for addressing + /// should be set to 1 in the mask. + /// + /// \param[in] addr_range + /// See \ref Mask Address Methods description of this argument. + /// This will default to eAddressMaskRangeLow which is the + /// only set of masks used normally. + void SetAddressMask( + lldb::AddressMaskType type, lldb::addr_t mask, + lldb::AddressMaskRange addr_range = lldb::eAddressMaskRangeLow); + + /// Set the number of bits used for addressing in this Process. + /// + /// On Darwin and similar systems, the addressable bits are expressed + /// as the number of low order bits that are relevant to addressing, + /// instead of a more general address mask. + /// This method calculates the correct mask value for a given number + /// of low order addressable bits. + /// + /// \param[in] type + /// See \ref Mask Address Methods description of this argument. + /// eAddressMaskTypeAll is often a suitable value when the + /// same mask is being set for both code and data. + /// + /// \param[in] num_bits + /// Number of bits that are used for addressing. + /// For example, a value of 42 indicates that the low 42 bits + /// are relevant for addressing, and that higher-order bits may + /// be used for various metadata like pointer authentication, + /// Type Byte Ignore, etc. + /// + /// \param[in] addr_range + /// See \ref Mask Address Methods description of this argument. + /// This will default to eAddressMaskRangeLow which is the + /// only set of masks used normally. + void + SetAddressableBits(AddressMaskType type, uint32_t num_bits, + AddressMaskRange addr_range = lldb::eAddressMaskRangeLow); + + /// Clear the non-address bits of an \a addr value and return a + /// virtual address in memory. + /// + /// Bits that are not used in addressing may be used for other purposes; + /// pointer authentication, or metadata in the top byte, or the 0th bit + /// of armv7 code addresses to indicate arm/thumb are common examples. + /// + /// \param[in] addr + /// The address that should be cleared of non-address bits. + /// + /// \param[in] type + /// See \ref Mask Address Methods description of this argument. + /// eAddressMaskTypeAny is the default value, correct when it + /// is unknown if the address is a code or data address. + lldb::addr_t + FixAddress(lldb::addr_t addr, + lldb::AddressMaskType type = lldb::eAddressMaskTypeAny); + /// \} + /// Allocate memory within the process. /// /// This function will allocate memory in the process's address space. diff --git a/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h index f600e29c7c4b0e..7b646d743346b7 100644 --- a/lldb/include/lldb/Target/ABI.h +++ b/lldb/include/lldb/Target/ABI.h @@ -122,8 +122,8 @@ class ABI : public PluginInterface { /// ARM uses bit zero to signify a code address is thumb, so any ARM ABI /// plug-ins would strip those bits. /// @{ - virtual lldb::addr_t FixCodeAddress(lldb::addr_t pc) { return pc; } - virtual lldb::addr_t FixDataAddress(lldb::addr_t pc) { return pc; } + virtual lldb::addr_t FixCodeAddress(lldb::addr_t pc); + virtual lldb::addr_t FixDataAddress(lldb::addr_t pc); /// @} /// Use this method when you do not know, or do not care what kind of address @@ -166,10 +166,6 @@ class ABI : public PluginInterface { lldb::ProcessWP m_process_wp; std::unique_ptr<llvm::MCRegisterInfo> m_mc_register_info_up; - virtual lldb::addr_t FixCodeAddress(lldb::addr_t pc, lldb::addr_t mask) { - return pc; - } - private: ABI(const ABI &) = delete; const ABI &operator=(const ABI &) = delete; diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 0ad626ffd3613c..e260e1b4b797bc 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1422,9 +1422,23 @@ class Process : public std::enable_shared_from_this<Process>, virtual void DidExit() {} + /// Get the current address mask in the Process + /// + /// This mask can used to set/clear non-address bits in an addr_t. + /// + /// \return + /// The current address mask. + /// Bits which are set to 1 are not used for addressing. + /// An address mask of 0 means all bits are used for addressing. + /// An address mask of LLDB_INVALID_ADDRESS_MASK (all 1's) means + /// that no mask has been set. lldb::addr_t GetCodeAddressMask(); lldb::addr_t GetDataAddressMask(); + /// The highmem masks are for targets where we may have diff erent masks + /// for low memory versus high memory addresses, and they will be left + /// as LLDB_INVALID_ADDRESS_MASK normally, meaning the base masks + /// should be applied to all addresses. lldb::addr_t GetHighmemCodeAddressMask(); lldb::addr_t GetHighmemDataAddressMask(); @@ -3096,16 +3110,20 @@ void PruneThreadPlans(); // if run while destructing. We use this flag to determine that. std::atomic<bool> m_destructing; - /// Mask for code an data addresses. The default value (0) means no mask is - /// set. The bits set to 1 indicate bits that are NOT significant for - /// addressing. - /// The highmem versions are for targets where we may have diff erent masks - /// for low memory versus high memory addresses. + /// Mask for code an data addresses. + /// The default value LLDB_INVALID_ADDRESS_MASK means no mask has been set, + /// and addresses values should not be modified. + /// In these masks, the bits are set to 1 indicate bits that are not + /// significant for addressing. + /// The highmem masks are for targets where we may have diff erent masks + /// for low memory versus high memory addresses, and they will be left + /// as LLDB_INVALID_ADDRESS_MASK normally, meaning the base masks + /// should be applied to all addresses. /// @{ - lldb::addr_t m_code_address_mask = 0; - lldb::addr_t m_data_address_mask = 0; - lldb::addr_t m_highmem_code_address_mask = 0; - lldb::addr_t m_highmem_data_address_mask = 0; + lldb::addr_t m_code_address_mask = LLDB_INVALID_ADDRESS_MASK; + lldb::addr_t m_data_address_mask = LLDB_INVALID_ADDRESS_MASK; + lldb::addr_t m_highmem_code_address_mask = LLDB_INVALID_ADDRESS_MASK; + lldb::addr_t m_highmem_data_address_mask = LLDB_INVALID_ADDRESS_MASK; /// @} bool m_clear_thread_plans_on_stop; diff --git a/lldb/include/lldb/Utility/AddressableBits.h b/lldb/include/lldb/Utility/AddressableBits.h index 13c21329a8c617..75752fcf840a44 100644 --- a/lldb/include/lldb/Utility/AddressableBits.h +++ b/lldb/include/lldb/Utility/AddressableBits.h @@ -10,6 +10,7 @@ #define LLDB_UTILITY_ADDRESSABLEBITS_H #include "lldb/lldb-forward.h" +#include "lldb/lldb-public.h" namespace lldb_private { @@ -33,6 +34,8 @@ class AddressableBits { void SetHighmemAddressableBits(uint32_t highmem_addressing_bits); + static lldb::addr_t AddressableBitToMask(uint32_t addressable_bits); + void SetProcessMasks(lldb_private::Process &process); private: diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h index 469be92eabecf3..c7bd019c5c90eb 100644 --- a/lldb/include/lldb/lldb-defines.h +++ b/lldb/include/lldb/lldb-defines.h @@ -127,6 +127,11 @@ #define MAX_PATH 260 #endif +/// Address Mask +/// Bits not used for addressing are set to 1 in the mask; +/// all mask bits set is an invalid value. +#define LLDB_INVALID_ADDRESS_MASK UINT64_MAX + // ignore GCC function attributes #if defined(_MSC_VER) && !defined(__clang__) #define __attribute__(X) diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 85769071dae785..646f7bfda98475 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -1323,6 +1323,22 @@ enum SymbolDownload { eSymbolDownloadForeground = 2, }; +/// Used in the SBProcess AddressMask/FixAddress methods. +enum AddressMaskType { + eAddressMaskTypeCode = 0, + eAddressMaskTypeData, + eAddressMaskTypeAny, + eAddressMaskTypeAll = eAddressMaskTypeAny +}; + +/// Used in the SBProcess AddressMask/FixAddress methods. +enum AddressMaskRange { + eAddressMaskRangeLow = 0, + eAddressMaskRangeHigh, + eAddressMaskRangeAny, + eAddressMaskRangeAll = eAddressMaskRangeAny, +}; + } // namespace lldb #endif // LLDB_LLDB_ENUMERATIONS_H diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp index a9fe915324683e..b80664882ebcac 100644 --- a/lldb/source/API/SBProcess.cpp +++ b/lldb/source/API/SBProcess.cpp @@ -1255,6 +1255,98 @@ lldb::SBFileSpec SBProcess::GetCoreFile() { return SBFileSpec(core_file); } +addr_t SBProcess::GetAddressMask(AddressMaskType type, + AddressMaskRange addr_range) { + LLDB_INSTRUMENT_VA(this, type, addr_range); + + if (ProcessSP process_sp = GetSP()) { + switch (type) { + case eAddressMaskTypeCode: + if (addr_range == eAddressMaskRangeHigh) + return process_sp->GetHighmemCodeAddressMask(); + else + return process_sp->GetCodeAddressMask(); + case eAddressMaskTypeData: + if (addr_range == eAddressMaskRangeHigh) + return process_sp->GetHighmemDataAddressMask(); + else + return process_sp->GetDataAddressMask(); + case eAddressMaskTypeAny: + if (addr_range == eAddressMaskRangeHigh) + return process_sp->GetHighmemDataAddressMask(); + else + return process_sp->GetDataAddressMask(); + } + } + return LLDB_INVALID_ADDRESS_MASK; +} + +void SBProcess::SetAddressMask(AddressMaskType type, addr_t mask, + AddressMaskRange addr_range) { + LLDB_INSTRUMENT_VA(this, type, mask, addr_range); + + if (ProcessSP process_sp = GetSP()) { + switch (type) { + case eAddressMaskTypeCode: + if (addr_range == eAddressMaskRangeAll) { + process_sp->SetCodeAddressMask(mask); + process_sp->SetHighmemCodeAddressMask(mask); + } else if (addr_range == eAddressMaskRangeHigh) { + process_sp->SetHighmemCodeAddressMask(mask); + } else { + process_sp->SetCodeAddressMask(mask); + } + break; + case eAddressMaskTypeData: + if (addr_range == eAddressMaskRangeAll) { + process_sp->SetDataAddressMask(mask); + process_sp->SetHighmemDataAddressMask(mask); + } else if (addr_range == eAddressMaskRangeHigh) { + process_sp->SetHighmemDataAddressMask(mask); + } else { + process_sp->SetDataAddressMask(mask); + } + break; + case eAddressMaskTypeAll: + if (addr_range == eAddressMaskRangeAll) { + process_sp->SetCodeAddressMask(mask); + process_sp->SetDataAddressMask(mask); + process_sp->SetHighmemCodeAddressMask(mask); + process_sp->SetHighmemDataAddressMask(mask); + } else if (addr_range == eAddressMaskRangeHigh) { + process_sp->SetHighmemCodeAddressMask(mask); + process_sp->SetHighmemDataAddressMask(mask); + } else { + process_sp->SetCodeAddressMask(mask); + process_sp->SetDataAddressMask(mask); + } + break; + } + } +} + +void SBProcess::SetAddressableBits(AddressMaskType type, uint32_t num_bits, + AddressMaskRange addr_range) { + LLDB_INSTRUMENT_VA(this, type, num_bits, addr_range); + + SetAddressMask(type, AddressableBits::AddressableBitToMask(num_bits), + addr_range); +} + +addr_t SBProcess::FixAddress(addr_t addr, AddressMaskType type) { + LLDB_INSTRUMENT_VA(this, addr, type); + + if (ProcessSP process_sp = GetSP()) { + if (type == eAddressMaskTypeAny) + return process_sp->FixAnyAddress(addr); + else if (type == eAddressMaskTypeData) + return process_sp->FixDataAddress(addr); + else if (type == eAddressMaskTypeCode) + return process_sp->FixCodeAddress(addr); + } + return addr; +} + lldb::addr_t SBProcess::AllocateMemory(size_t size, uint32_t permissions, lldb::SBError &sb_error) { LLDB_INSTRUMENT_VA(this, size, permissions, sb_error); diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 9ac97eb66b6232..3587a8f529e4ab 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -1408,7 +1408,7 @@ class CommandObjectProcessStatus : public CommandObjectParsed { if (m_options.m_verbose) { addr_t code_mask = process->GetCodeAddressMask(); addr_t data_mask = process->GetDataAddressMask(); - if (code_mask != 0) { + if (code_mask != LLDB_INVALID_ADDRESS_MASK) { int bits = std::bitset<64>(~code_mask).count(); result.AppendMessageWithFormat( "Addressable code address mask: 0x%" PRIx64 "\n", code_mask); diff --git a/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp b/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp index ef53309f065fd0..256c1f828feb38 100644 --- a/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp +++ b/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +#include "lldb/lldb-types.h" + #include "ABIAArch64.h" #include "ABIMacOSX_arm64.h" #include "ABISysV_arm64.h" @@ -16,6 +18,8 @@ #include <bitset> #include <optional> +using namespace lldb; + LLDB_PLUGIN_DEFINE(ABIAArch64) void ABIAArch64::Initialize() { @@ -29,14 +33,35 @@ void ABIAArch64::Terminate() { } lldb::addr_t ABIAArch64::FixCodeAddress(lldb::addr_t pc) { - if (lldb::ProcessSP process_sp = GetProcessSP()) - return FixAddress(pc, process_sp->GetCodeAddressMask()); + if (lldb::ProcessSP process_sp = GetProcessSP()) { + // b55 is the highest bit outside TBI (if it's enabled), use + // it to determine if the high bits are set to 0 or 1. + const addr_t pac_sign_extension = 0x0080000000000000ULL; + addr_t mask = process_sp->GetCodeAddressMask(); + // Test if the high memory mask has been overriden separately + if (pc & pac_sign_extension && + process_sp->GetHighmemCodeAddressMask() != LLDB_INVALID_ADDRESS_MASK) + mask = process_sp->GetHighmemCodeAddressMask(); + + if (mask != LLDB_INVALID_ADDRESS_MASK) + return FixAddress(pc, mask); + } return pc; } lldb::addr_t ABIAArch64::FixDataAddress(lldb::addr_t pc) { - if (lldb::ProcessSP process_sp = GetProcessSP()) - return FixAddress(pc, process_sp->GetDataAddressMask()); + if (lldb::ProcessSP process_sp = GetProcessSP()) { + // b55 is the highest bit outside TBI (if it's enabled), use + // it to determine if the high bits are set to 0 or 1. + const addr_t pac_sign_extension = 0x0080000000000000ULL; + addr_t mask = process_sp->GetDataAddressMask(); + // Test if the high memory mask has been overriden separately + if (pc & pac_sign_extension && + process_sp->GetHighmemDataAddressMask() != LLDB_INVALID_ADDRESS_MASK) + mask = process_sp->GetHighmemDataAddressMask(); + if (mask != LLDB_INVALID_ADDRESS_MASK) + return FixAddress(pc, mask); + } return pc; } diff --git a/lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp b/lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp index f4ef9b4fc824b0..045d6a405e6972 100644 --- a/lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp +++ b/lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp @@ -814,11 +814,11 @@ addr_t ABIMacOSX_arm64::FixCodeAddress(addr_t pc) { mask = process_sp->GetCodeAddressMask(); if (pc & pac_sign_extension) { addr_t highmem_mask = process_sp->GetHighmemCodeAddressMask(); - if (highmem_mask) + if (highmem_mask != LLDB_INVALID_ADDRESS_MASK) mask = highmem_mask; } } - if (mask == 0) + if (mask == LLDB_INVALID_ADDRESS_MASK) mask = tbi_mask; return (pc & pac_sign_extension) ? pc | mask : pc & (~mask); @@ -833,11 +833,11 @@ addr_t ABIMacOSX_arm64::FixDataAddress(addr_t pc) { mask = process_sp->GetDataAddressMask(); if (pc & pac_sign_extension) { addr_t highmem_mask = process_sp->GetHighmemDataAddressMask(); - if (highmem_mask) + if (highmem_mask != LLDB_INVALID_ADDRESS_MASK) mask = highmem_mask; } } - if (mask == 0) + if (mask == LLDB_INVALID_ADDRESS_MASK) mask = tbi_mask; return (pc & pac_sign_extension) ? pc | mask : pc & (~mask); diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp index bf3c5ddd588977..cd7481f88efdd8 100644 --- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp +++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp @@ -775,6 +775,8 @@ ValueObjectSP ABISysV_arm64::GetReturnValueObjectImpl( } lldb::addr_t ABISysV_arm64::FixAddress(addr_t pc, addr_t mask) { + if (mask == LLDB_INVALID_ADDRESS_MASK) + return pc; lldb::addr_t pac_sign_extension = 0x0080000000000000ULL; return (pc & pac_sign_extension) ? pc | mask : pc & (~mask); } @@ -782,12 +784,12 @@ lldb::addr_t ABISysV_arm64::FixAddress(addr_t pc, addr_t mask) { // Reads code or data address mask for the current Linux process. static lldb::addr_t ReadLinuxProcessAddressMask(lldb::ProcessSP process_sp, llvm::StringRef reg_name) { - // 0 means there isn't a mask or it has not been read yet. - // We do not return the top byte mask unless thread_sp is valid. - // This prevents calls to this function before the thread is setup locking - // in the value to just the top byte mask, in cases where pointer - // authentication might also be active. - uint64_t address_mask = 0; + // LLDB_INVALID_ADDRESS_MASK means there isn't a mask or it has not been read + // yet. We do not return the top byte mask unless thread_sp is valid. This + // prevents calls to this function before the thread is setup locking in the + // value to just the top byte mask, in cases where pointer authentication + // might also be active. + uint64_t address_mask = LLDB_INVALID_ADDRESS_MASK; lldb::ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread(); if (thread_sp) { // Linux configures user-space virtual addresses with top byte ignored. @@ -814,11 +816,20 @@ static lldb::addr_t ReadLinuxProcessAddressMask(lldb::ProcessSP process_sp, lldb::addr_t ABISysV_arm64::FixCodeAddress(lldb::addr_t pc) { if (lldb::ProcessSP process_sp = GetProcessSP()) { if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSLinux() && - !process_sp->GetCodeAddressMask()) + process_sp->GetCodeAddressMask() == LLDB_INVALID_ADDRESS_MASK) process_sp->SetCodeAddressMask( ReadLinuxProcessAddressMask(process_sp, "code_mask")); - return FixAddress(pc, process_sp->GetCodeAddressMask()); + // b55 is the highest bit outside TBI (if it's enabled), use + // it to determine if the high bits are set to 0 or 1. + const addr_t pac_sign_extension = 0x0080000000000000ULL; + addr_t mask = process_sp->GetCodeAddressMask(); + // Test if the high memory mask has been overriden separately + if (pc & pac_sign_extension && + process_sp->GetHighmemCodeAddressMask() != LLDB_INVALID_ADDRESS_MASK) + mask = process_sp->GetHighmemCodeAddressMask(); + + return FixAddress(pc, mask); } return pc; } @@ -826,11 +837,20 @@ lldb::addr_t ABISysV_arm64::FixCodeAddress(lldb::addr_t pc) { lldb::addr_t ABISysV_arm64::FixDataAddress(lldb::addr_t pc) { if (lldb::ProcessSP process_sp = GetProcessSP()) { if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSLinux() && - !process_sp->GetDataAddressMask()) + process_sp->GetDataAddressMask() == LLDB_INVALID_ADDRESS_MASK) process_sp->SetDataAddressMask( ReadLinuxProcessAddressMask(process_sp, "data_mask")); - return FixAddress(pc, process_sp->GetDataAddressMask()); + // b55 is the highest bit outside TBI (if it's enabled), use + // it to determine if the high bits are set to 0 or 1. + const addr_t pac_sign_extension = 0x0080000000000000ULL; + addr_t mask = process_sp->GetDataAddressMask(); + // Test if the high memory mask has been overriden separately + if (pc & pac_sign_extension && + process_sp->GetHighmemDataAddressMask() != LLDB_INVALID_ADDRESS_MASK) + mask = process_sp->GetHighmemDataAddressMask(); + + return FixAddress(pc, mask); } return pc; } diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp index 3bdbce5a3b7c46..8d83937aab6681 100644 --- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -22,6 +22,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/AddressableBits.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -1109,7 +1110,7 @@ void DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded() { // T1Sz is 25, then 64-25 == 39, bits 0..38 are used for // addressing, bits 39..63 are used for PAC/TBI or whatever. uint32_t virt_addr_bits = 64 - sym_value; - addr_t mask = ~((1ULL << virt_addr_bits) - 1); + addr_t mask = AddressableBits::AddressableBitToMask(virt_addr_bits); m_process->SetCodeAddressMask(mask); m_process->SetDataAddressMask(mask); } else { diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 729fbd3256715d..bcf3a3274cf3a0 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -6620,7 +6620,7 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp, // Bits will be set to indicate which bits are NOT used in // addressing in this process or 0 for unknown. uint64_t address_mask = process_sp->GetCodeAddressMask(); - if (address_mask != 0) { + if (address_mask != LLDB_INVALID_ADDRESS_MASK) { // LC_NOTE "addrable bits" mach_header.ncmds++; mach_header.sizeofcmds += sizeof(llvm::MachO::note_command); @@ -6654,7 +6654,7 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp, std::vector<std::unique_ptr<LCNoteEntry>> lc_notes; // Add "addrable bits" LC_NOTE when an address mask is available - if (address_mask != 0) { + if (address_mask != LLDB_INVALID_ADDRESS_MASK) { std::unique_ptr<LCNoteEntry> addrable_bits_lcnote_up( new LCNoteEntry(addr_byte_size, byte_order)); addrable_bits_lcnote_up->name = "addrable bits"; diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp index 86bf0118027155..110b5c86fc4256 100644 --- a/lldb/source/Target/ABI.cpp +++ b/lldb/source/Target/ABI.cpp @@ -147,6 +147,39 @@ ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type, return return_valobj_sp; } +addr_t ABI::FixCodeAddress(lldb::addr_t pc) { + ProcessSP process_sp(GetProcessSP()); + + addr_t mask = process_sp->GetCodeAddressMask(); + if (mask == LLDB_INVALID_ADDRESS_MASK) + return pc; + + // Assume the high bit is used for addressing, which + // may not be correct on all architectures e.g. AArch64 + // where Top Byte Ignore mode is often used to store + // metadata in the top byte, and b55 is the bit used for + // diff erentiating between low- and high-memory addresses. + // That target's ABIs need to override this method. + bool is_highmem = pc & (1ULL << 63); + return is_highmem ? pc | mask : pc & (~mask); +} + +addr_t ABI::FixDataAddress(lldb::addr_t pc) { + ProcessSP process_sp(GetProcessSP()); + addr_t mask = process_sp->GetDataAddressMask(); + if (mask == LLDB_INVALID_ADDRESS_MASK) + return pc; + + // Assume the high bit is used for addressing, which + // may not be correct on all architectures e.g. AArch64 + // where Top Byte Ignore mode is often used to store + // metadata in the top byte, and b55 is the bit used for + // diff erentiating between low- and high-memory addresses. + // That target's ABIs need to override this method. + bool is_highmem = pc & (1ULL << 63); + return is_highmem ? pc | mask : pc & (~mask); +} + ValueObjectSP ABI::GetReturnValueObject(Thread &thread, llvm::Type &ast_type, bool persistent) const { ValueObjectSP return_valobj_sp; diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 137795cb8cec9e..6d58873b54a3ad 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -5682,30 +5682,32 @@ void Process::Flush() { lldb::addr_t Process::GetCodeAddressMask() { if (uint32_t num_bits_setting = GetVirtualAddressableBits()) - return ~((1ULL << num_bits_setting) - 1); + return AddressableBits::AddressableBitToMask(num_bits_setting); return m_code_address_mask; } lldb::addr_t Process::GetDataAddressMask() { if (uint32_t num_bits_setting = GetVirtualAddressableBits()) - return ~((1ULL << num_bits_setting) - 1); + return AddressableBits::AddressableBitToMask(num_bits_setting); return m_data_address_mask; } lldb::addr_t Process::GetHighmemCodeAddressMask() { if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits()) - return ~((1ULL << num_bits_setting) - 1); - if (m_highmem_code_address_mask) + return AddressableBits::AddressableBitToMask(num_bits_setting); + + if (m_highmem_code_address_mask != LLDB_INVALID_ADDRESS_MASK) return m_highmem_code_address_mask; return GetCodeAddressMask(); } lldb::addr_t Process::GetHighmemDataAddressMask() { if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits()) - return ~((1ULL << num_bits_setting) - 1); - if (m_highmem_data_address_mask) + return AddressableBits::AddressableBitToMask(num_bits_setting); + + if (m_highmem_data_address_mask != LLDB_INVALID_ADDRESS_MASK) return m_highmem_data_address_mask; return GetDataAddressMask(); } diff --git a/lldb/source/Utility/AddressableBits.cpp b/lldb/source/Utility/AddressableBits.cpp index c6e25f608da73d..7f9d7ec6c1349c 100644 --- a/lldb/source/Utility/AddressableBits.cpp +++ b/lldb/source/Utility/AddressableBits.cpp @@ -33,18 +33,26 @@ void AddressableBits::SetHighmemAddressableBits( m_high_memory_addr_bits = highmem_addressing_bits; } +addr_t AddressableBits::AddressableBitToMask(uint32_t addressable_bits) { + assert(addressable_bits <= sizeof(addr_t) * 8); + if (addressable_bits == 64) + return 0; // all bits used for addressing + else + return ~((1ULL << addressable_bits) - 1); +} + void AddressableBits::SetProcessMasks(Process &process) { if (m_low_memory_addr_bits == 0 && m_high_memory_addr_bits == 0) return; if (m_low_memory_addr_bits != 0) { - addr_t low_addr_mask = ~((1ULL << m_low_memory_addr_bits) - 1); + addr_t low_addr_mask = AddressableBitToMask(m_low_memory_addr_bits); process.SetCodeAddressMask(low_addr_mask); process.SetDataAddressMask(low_addr_mask); } if (m_high_memory_addr_bits != 0) { - addr_t hi_addr_mask = ~((1ULL << m_high_memory_addr_bits) - 1); + addr_t hi_addr_mask = AddressableBitToMask(m_high_memory_addr_bits); process.SetHighmemCodeAddressMask(hi_addr_mask); process.SetHighmemDataAddressMask(hi_addr_mask); } diff --git a/lldb/test/API/python_api/process/address-masks/Makefile b/lldb/test/API/python_api/process/address-masks/Makefile new file mode 100644 index 00000000000000..10495940055b63 --- /dev/null +++ b/lldb/test/API/python_api/process/address-masks/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/python_api/process/address-masks/TestAddressMasks.py b/lldb/test/API/python_api/process/address-masks/TestAddressMasks.py new file mode 100644 index 00000000000000..e0a570c15961c2 --- /dev/null +++ b/lldb/test/API/python_api/process/address-masks/TestAddressMasks.py @@ -0,0 +1,131 @@ +"""Test Python APIs for setting, getting, and using address masks.""" + +import os +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class AddressMasksTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def reset_all_masks(self, process): + process.SetAddressMask( + lldb.eAddressMaskTypeAll, + lldb.LLDB_INVALID_ADDRESS_MASK, + lldb.eAddressMaskRangeAll, + ) + self.runCmd("settings set target.process.virtual-addressable-bits 0") + self.runCmd("settings set target.process.highmem-virtual-addressable-bits 0") + + def test_address_masks(self): + self.build() + (target, process, t, bp) = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.c") + ) + + process.SetAddressableBits(lldb.eAddressMaskTypeAll, 42) + self.assertEqual(0x0000029500003F94, process.FixAddress(0x00265E9500003F94)) + self.reset_all_masks(process) + + # ~((1ULL<<42)-1) == 0xfffffc0000000000 + process.SetAddressMask(lldb.eAddressMaskTypeAll, 0xFFFFFC0000000000) + self.assertEqual(0x0000029500003F94, process.FixAddress(0x00265E9500003F94)) + self.reset_all_masks(process) + + # Check that all bits can pass through unmodified + process.SetAddressableBits(lldb.eAddressMaskTypeAll, 64) + self.assertEqual(0x00265E9500003F94, process.FixAddress(0x00265E9500003F94)) + self.reset_all_masks(process) + + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeAll + ) + self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) + self.assertEqual(0xFFFFFE950000F694, process.FixAddress(0xFFA65E950000F694)) + self.reset_all_masks(process) + + # Set a eAddressMaskTypeCode which has the low 3 bits marked as non-address + # bits, confirm that they're cleared by FixAddress. + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeAll + ) + mask = process.GetAddressMask(lldb.eAddressMaskTypeAny) + process.SetAddressMask(lldb.eAddressMaskTypeCode, mask | 0x3) + self.assertEqual(0x000002950001F697, process.FixAddress(0x00265E950001F697)) + self.assertEqual(0xFFFFFE950000F697, process.FixAddress(0xFFA65E950000F697)) + self.assertEqual( + 0x000002950001F697, + process.FixAddress(0x00265E950001F697, lldb.eAddressMaskTypeData), + ) + self.assertEqual( + 0x000002950001F694, + process.FixAddress(0x00265E950001F697, lldb.eAddressMaskTypeCode), + ) + self.reset_all_masks(process) + + # The user can override whatever settings the Process thinks should be used. + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeLow + ) + self.runCmd("settings set target.process.virtual-addressable-bits 15") + self.assertEqual(0x0000000000007694, process.FixAddress(0x00265E950001F694)) + self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) + self.runCmd("settings set target.process.virtual-addressable-bits 0") + self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) + self.reset_all_masks(process) + + # AArch64 can have diff erent address masks for high and low memory, when diff erent + # page tables are set up. + @skipIf(archs=no_match(["arm64", "arm64e", "aarch64"])) + def test_address_masks_target_supports_highmem_tests(self): + self.build() + (target, process, t, bp) = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.c") + ) + + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeLow + ) + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 15, lldb.eAddressMaskRangeHigh + ) + self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) + self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) + self.reset_all_masks(process) + + # The user can override whatever settings the Process thinks should be used. + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeAll + ) + self.runCmd("settings set target.process.virtual-addressable-bits 15") + self.runCmd("settings set target.process.highmem-virtual-addressable-bits 15") + self.assertEqual(0x0000000000007694, process.FixAddress(0x00265E950001F694)) + self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) + self.runCmd("settings set target.process.virtual-addressable-bits 0") + self.runCmd("settings set target.process.highmem-virtual-addressable-bits 0") + self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) + self.reset_all_masks(process) + + # On most targets where we have a single mask for all address range, confirm + # that the high memory masks are ignored. + @skipIf(archs=["arm64", "arm64e", "aarch64"]) + def test_address_masks_target_no_highmem(self): + self.build() + (target, process, t, bp) = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.c") + ) + + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeLow + ) + process.SetAddressableBits( + lldb.eAddressMaskTypeAll, 15, lldb.eAddressMaskRangeHigh + ) + self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) + self.assertEqual(0xFFFFFE950000F694, process.FixAddress(0xFFA65E950000F694)) + self.runCmd("settings set target.process.virtual-addressable-bits 15") + self.runCmd("settings set target.process.highmem-virtual-addressable-bits 42") + self.assertEqual(0x0000000000007694, process.FixAddress(0x00265E950001F694)) + self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) diff --git a/lldb/test/API/python_api/process/address-masks/main.c b/lldb/test/API/python_api/process/address-masks/main.c new file mode 100644 index 00000000000000..f21a10a16d5a75 --- /dev/null +++ b/lldb/test/API/python_api/process/address-masks/main.c @@ -0,0 +1,5 @@ +#include <stdio.h> + +int main(int argc, char const *argv[]) { + puts("Hello address masking world"); // break here +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits