mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 10:36:06 +00:00
[LLDB] Apply FixCodeAddress to all forms of address arguments
This is a follow up to https://reviews.llvm.org/D141629 and applies the change it made to all paths through ToAddress (now DoToAddress). I have included the test from my previous attempt https://reviews.llvm.org/D136938. The initial change only applied fixing to addresses that would parse as integers, so my test case failed. Since ToAddress has multiple exit points, I've wrapped it into a new method DoToAddress. Now you can call ToAddress, it will call DoToAddress and no matter what path you take, the address will be fixed. For the memory tagging commands we actually want the full address (to work out mismatches). So I added ToRawAddress for that. I have tested this on a QEMU AArch64 Linux system with Memory Tagging, Pointer Authentication and Top Byte Ignore enabled. By running the new test and all other tests in API/linux/aarch64. Some commands have had calls to the ABI plugin removed as ToAddress now does this for them. The "memory region" command still needs to use the ABI plugin to detect the end of memory when there are non-address bits. Reviewed By: jasonmolenda Differential Revision: https://reviews.llvm.org/D142715
This commit is contained in:
parent
a9329a96ad
commit
e6ec76c647
@ -11,12 +11,21 @@
|
||||
|
||||
#include "lldb/lldb-private-types.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
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 @@ struct OptionArgParser {
|
||||
size_t *byte_size_ptr); // If non-NULL, then a
|
||||
// byte size can precede
|
||||
// the format character
|
||||
|
||||
private:
|
||||
static std::optional<lldb::addr_t>
|
||||
DoToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s,
|
||||
Status *error);
|
||||
};
|
||||
|
||||
} // namespace lldb_private
|
||||
|
@ -594,18 +594,9 @@ protected:
|
||||
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 @@ protected:
|
||||
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 @@ protected:
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -51,7 +51,7 @@ protected:
|
||||
}
|
||||
|
||||
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 @@ protected:
|
||||
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 @@ public:
|
||||
|
||||
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 @@ protected:
|
||||
}
|
||||
|
||||
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}",
|
||||
|
@ -140,16 +140,40 @@ lldb::ScriptLanguage OptionArgParser::ToScriptLanguage(
|
||||
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<lldb::addr_t> 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<lldb::addr_t> 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<lldb::addr_t>
|
||||
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 @@ lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx,
|
||||
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 @@ lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx,
|
||||
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 @@ lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx,
|
||||
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 @@ lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx,
|
||||
error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"",
|
||||
s.str().c_str());
|
||||
}
|
||||
return fail_value;
|
||||
return {};
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
C_SOURCES := main.c
|
||||
|
||||
CFLAGS_EXTRAS := -march=armv8.3-a
|
||||
|
||||
include Makefile.rules
|
@ -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)
|
@ -0,0 +1,18 @@
|
||||
#include <stdint.h>
|
||||
|
||||
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><pointer signature><virtual address>
|
||||
|
||||
foo(); // Set break point at this line.
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user