diff --git a/lldb/include/lldb/Interpreter/OptionArgParser.h b/lldb/include/lldb/Interpreter/OptionArgParser.h --- a/lldb/include/lldb/Interpreter/OptionArgParser.h +++ b/lldb/include/lldb/Interpreter/OptionArgParser.h @@ -11,12 +11,21 @@ #include "lldb/lldb-private-types.h" +#include + namespace lldb_private { struct OptionArgParser { + /// Try to parse an address. If it succeeds return the address with the + /// non-address bits removed. static lldb::addr_t ToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s, lldb::addr_t fail_value, - Status *error); + Status *error_ptr); + + /// As for ToAddress but do not remove non-address bits from the result. + static lldb::addr_t ToRawAddress(const ExecutionContext *exe_ctx, + llvm::StringRef s, lldb::addr_t fail_value, + Status *error_ptr); static bool ToBoolean(llvm::StringRef s, bool fail_value, bool *success_ptr); @@ -35,6 +44,11 @@ size_t *byte_size_ptr); // If non-NULL, then a // byte size can precede // the format character + +private: + static std::optional + DoToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s, + Status *error); }; } // namespace lldb_private diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -594,18 +594,9 @@ return false; } - ABISP abi; - if (Process *proc = m_exe_ctx.GetProcessPtr()) - abi = proc->GetABI(); - - if (abi) - addr = abi->FixDataAddress(addr); - if (argc == 2) { lldb::addr_t end_addr = OptionArgParser::ToAddress( &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, nullptr); - if (end_addr != LLDB_INVALID_ADDRESS && abi) - end_addr = abi->FixDataAddress(end_addr); if (end_addr == LLDB_INVALID_ADDRESS) { result.AppendError("invalid end address expression."); @@ -1045,12 +1036,6 @@ return false; } - ABISP abi = m_exe_ctx.GetProcessPtr()->GetABI(); - if (abi) { - low_addr = abi->FixDataAddress(low_addr); - high_addr = abi->FixDataAddress(high_addr); - } - if (high_addr <= low_addr) { result.AppendError( "starting address must be smaller than ending address"); @@ -1783,7 +1768,6 @@ } auto load_addr_str = command[0].ref(); - // Non-address bits in this will be handled later by GetMemoryRegion load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str, LLDB_INVALID_ADDRESS, &error); if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) { diff --git a/lldb/source/Commands/CommandObjectMemoryTag.cpp b/lldb/source/Commands/CommandObjectMemoryTag.cpp --- a/lldb/source/Commands/CommandObjectMemoryTag.cpp +++ b/lldb/source/Commands/CommandObjectMemoryTag.cpp @@ -51,7 +51,7 @@ } Status error; - addr_t start_addr = OptionArgParser::ToAddress( + addr_t start_addr = OptionArgParser::ToRawAddress( &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); if (start_addr == LLDB_INVALID_ADDRESS) { result.AppendErrorWithFormatv("Invalid address expression, {0}", @@ -63,8 +63,8 @@ addr_t end_addr = start_addr + 1; if (command.GetArgumentCount() > 1) { - end_addr = OptionArgParser::ToAddress(&m_exe_ctx, command[1].ref(), - LLDB_INVALID_ADDRESS, &error); + end_addr = OptionArgParser::ToRawAddress(&m_exe_ctx, command[1].ref(), + LLDB_INVALID_ADDRESS, &error); if (end_addr == LLDB_INVALID_ADDRESS) { result.AppendErrorWithFormatv("Invalid end address expression, {0}", error.AsCString()); @@ -155,8 +155,8 @@ switch (short_option) { case 'e': - m_end_addr = OptionArgParser::ToAddress(execution_context, option_value, - LLDB_INVALID_ADDRESS, &status); + m_end_addr = OptionArgParser::ToRawAddress( + execution_context, option_value, LLDB_INVALID_ADDRESS, &status); break; default: llvm_unreachable("Unimplemented option"); @@ -203,7 +203,7 @@ } Status error; - addr_t start_addr = OptionArgParser::ToAddress( + addr_t start_addr = OptionArgParser::ToRawAddress( &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); if (start_addr == LLDB_INVALID_ADDRESS) { result.AppendErrorWithFormatv("Invalid address expression, {0}", diff --git a/lldb/source/Interpreter/OptionArgParser.cpp b/lldb/source/Interpreter/OptionArgParser.cpp --- a/lldb/source/Interpreter/OptionArgParser.cpp +++ b/lldb/source/Interpreter/OptionArgParser.cpp @@ -140,16 +140,40 @@ return fail_value; } +lldb::addr_t OptionArgParser::ToRawAddress(const ExecutionContext *exe_ctx, + llvm::StringRef s, + lldb::addr_t fail_value, + Status *error_ptr) { + std::optional maybe_addr = DoToAddress(exe_ctx, s, error_ptr); + return maybe_addr ? *maybe_addr : fail_value; +} + lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s, lldb::addr_t fail_value, Status *error_ptr) { + std::optional maybe_addr = DoToAddress(exe_ctx, s, error_ptr); + if (!maybe_addr) + return fail_value; + + lldb::addr_t addr = *maybe_addr; + + if (Process *process = exe_ctx->GetProcessPtr()) + if (ABISP abi_sp = process->GetABI()) + addr = abi_sp->FixCodeAddress(addr); + + return addr; +} + +std::optional +OptionArgParser::DoToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s, + Status *error_ptr) { bool error_set = false; if (s.empty()) { if (error_ptr) error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", s.str().c_str()); - return fail_value; + return {}; } llvm::StringRef sref = s; @@ -158,10 +182,7 @@ if (!s.getAsInteger(0, addr)) { if (error_ptr) error_ptr->Clear(); - Process *process = exe_ctx->GetProcessPtr(); - if (process) - if (ABISP abi_sp = process->GetABI()) - addr = abi_sp->FixCodeAddress(addr); + return addr; } @@ -177,7 +198,7 @@ if (error_ptr) error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", s.str().c_str()); - return fail_value; + return {}; } lldb::ValueObjectSP valobj_sp; @@ -197,7 +218,7 @@ valobj_sp->GetDynamicValueType(), true); // Get the address to watch. if (valobj_sp) - addr = valobj_sp->GetValueAsUnsigned(fail_value, &success); + addr = valobj_sp->GetValueAsUnsigned(0, &success); if (success) { if (error_ptr) error_ptr->Clear(); @@ -249,5 +270,5 @@ error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", s.str().c_str()); } - return fail_value; + return {}; } diff --git a/lldb/test/API/linux/aarch64/non_address_bit_code_break/Makefile b/lldb/test/API/linux/aarch64/non_address_bit_code_break/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/non_address_bit_code_break/Makefile @@ -0,0 +1,5 @@ +C_SOURCES := main.c + +CFLAGS_EXTRAS := -march=armv8.3-a + +include Makefile.rules diff --git a/lldb/test/API/linux/aarch64/non_address_bit_code_break/TestAArch64LinuxNonAddressBitCodeBreak.py b/lldb/test/API/linux/aarch64/non_address_bit_code_break/TestAArch64LinuxNonAddressBitCodeBreak.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/non_address_bit_code_break/TestAArch64LinuxNonAddressBitCodeBreak.py @@ -0,0 +1,57 @@ +""" +Test that "breakpoint set -a" uses the ABI plugin to remove non-address bits +before attempting to set a breakpoint. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class AArch64LinuxNonAddressBitCodeBreak(TestBase): + + NO_DEBUG_INFO_TESTCASE = True + + def do_tagged_break(self, hardware): + if not self.isAArch64PAuth(): + self.skipTest('Target must support pointer authentication.') + + self.build() + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line(self, "main.c", + line_number('main.c', '// Set break point at this line.'), + num_expected_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + if self.process().GetState() == lldb.eStateExited: + self.fail("Test program failed to run.") + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + cmd = "breakpoint set -a fnptr" + # LLDB only has the option to force hardware break, not software. + # It prefers sofware break when it can and this will be one of those cases. + if hardware: + cmd += " --hardware" + self.expect(cmd) + + self.runCmd("continue") + self.assertEqual(self.process().GetState(), lldb.eStateStopped) + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', '`foo at main.c', 'stop reason = breakpoint']) + + # AArch64 Linux always enables the top byte ignore feature + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_software_break(self): + self.do_tagged_break(False) + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_hardware_break(self): + self.do_tagged_break(True) diff --git a/lldb/test/API/linux/aarch64/non_address_bit_code_break/main.c b/lldb/test/API/linux/aarch64/non_address_bit_code_break/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/non_address_bit_code_break/main.c @@ -0,0 +1,18 @@ +#include + +void foo(void) {} +typedef void (*FooPtr)(void); + +int main() { + FooPtr fnptr = foo; + // Set top byte. + fnptr = (FooPtr)((uintptr_t)fnptr | (uintptr_t)0xff << 56); + // Then apply a PAuth signature to it. + __asm__ __volatile__("pacdza %0" : "=r"(fnptr) : "r"(fnptr)); + // fnptr is now: + // <8 bit top byte tag> + + foo(); // Set break point at this line. + + return 0; +}