[lldb] [gdb-remote] Support client fallback for servers without reg defs

Provide minimal register definition defaults for working with servers
that implement neither target.xml nor qRegisterInfo packets.  This is
useful e.g. when interacting with FreeBSD's kernel minimal gdbserver
that does not send target.xml but uses the same layout for its supported
register subset as GDB.

The prerequisite for this is the ability to determine the correct
architecture, e.g. from the target executable.

Differential Revision: https://reviews.llvm.org/D116896
This commit is contained in:
Michał Górny
2022-01-09 19:35:22 +01:00
parent 10d3bf9518
commit e69a3d18f4
5 changed files with 464 additions and 0 deletions

View File

@@ -25,6 +25,7 @@ add_lldb_library(lldbPluginProcessGDBRemote PLUGIN
GDBRemoteCommunicationServerLLGS.cpp
GDBRemoteCommunicationServerPlatform.cpp
GDBRemoteRegisterContext.cpp
GDBRemoteRegisterFallback.cpp
ProcessGDBRemote.cpp
ProcessGDBRemoteLog.cpp
ThreadGDBRemote.cpp

View File

@@ -0,0 +1,86 @@
//===-- GDBRemoteRegisterFallback.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 "GDBRemoteRegisterFallback.h"
namespace lldb_private {
namespace process_gdb_remote {
#define REG(name, size) \
DynamicRegisterInfo::Register { \
ConstString(#name), empty_alt_name, reg_set, size, LLDB_INVALID_INDEX32, \
lldb::eEncodingUint, lldb::eFormatHex, LLDB_INVALID_REGNUM, \
LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, {}, {} \
}
#define R64(name) REG(name, 8)
#define R32(name) REG(name, 4)
static std::vector<DynamicRegisterInfo::Register> GetRegisters_aarch64() {
ConstString empty_alt_name;
ConstString reg_set{"general purpose registers"};
std::vector<DynamicRegisterInfo::Register> registers{
R64(x0), R64(x1), R64(x2), R64(x3), R64(x4), R64(x5), R64(x6),
R64(x7), R64(x8), R64(x9), R64(x10), R64(x11), R64(x12), R64(x13),
R64(x14), R64(x15), R64(x16), R64(x17), R64(x18), R64(x19), R64(x20),
R64(x21), R64(x22), R64(x23), R64(x24), R64(x25), R64(x26), R64(x27),
R64(x28), R64(x29), R64(x30), R64(sp), R64(pc), R32(cpsr),
};
return registers;
}
static std::vector<DynamicRegisterInfo::Register> GetRegisters_x86() {
ConstString empty_alt_name;
ConstString reg_set{"general purpose registers"};
std::vector<DynamicRegisterInfo::Register> registers{
R32(eax), R32(ecx), R32(edx), R32(ebx), R32(esp), R32(ebp),
R32(esi), R32(edi), R32(eip), R32(eflags), R32(cs), R32(ss),
R32(ds), R32(es), R32(fs), R32(gs),
};
return registers;
}
static std::vector<DynamicRegisterInfo::Register> GetRegisters_x86_64() {
ConstString empty_alt_name;
ConstString reg_set{"general purpose registers"};
std::vector<DynamicRegisterInfo::Register> registers{
R64(rax), R64(rbx), R64(rcx), R64(rdx), R64(rsi), R64(rdi),
R64(rbp), R64(rsp), R64(r8), R64(r9), R64(r10), R64(r11),
R64(r12), R64(r13), R64(r14), R64(r15), R64(rip), R32(eflags),
R32(cs), R32(ss), R32(ds), R32(es), R32(fs), R32(gs),
};
return registers;
}
#undef R32
#undef R64
#undef REG
std::vector<DynamicRegisterInfo::Register>
GetFallbackRegisters(const ArchSpec &arch_to_use) {
switch (arch_to_use.GetMachine()) {
case llvm::Triple::aarch64:
return GetRegisters_aarch64();
case llvm::Triple::x86:
return GetRegisters_x86();
case llvm::Triple::x86_64:
return GetRegisters_x86_64();
default:
break;
}
return {};
}
} // namespace process_gdb_remote
} // namespace lldb_private

View File

@@ -0,0 +1,26 @@
//===-- GDBRemoteRegisterFallback.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERFALLBACK_H
#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERFALLBACK_H
#include <vector>
#include "lldb/Target/DynamicRegisterInfo.h"
#include "lldb/Utility/ArchSpec.h"
namespace lldb_private {
namespace process_gdb_remote {
std::vector<DynamicRegisterInfo::Register>
GetFallbackRegisters(const ArchSpec &arch_to_use);
} // namespace process_gdb_remote
} // namespace lldb_private
#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERFALLBACK_H

View File

@@ -72,6 +72,7 @@
#include <thread>
#include "GDBRemoteRegisterContext.h"
#include "GDBRemoteRegisterFallback.h"
#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h"
#include "Plugins/Process/Utility/GDBRemoteSignals.h"
#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
@@ -394,6 +395,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) {
// 2 - If the target definition doesn't have any of the info from the
// target.xml (registers) then proceed to read the target.xml.
// 3 - Fall back on the qRegisterInfo packets.
// 4 - Use hardcoded defaults if available.
FileSpec target_definition_fspec =
GetGlobalPluginProperties().GetTargetDefinitionFile();
@@ -507,6 +509,9 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) {
}
}
if (registers.empty())
registers = GetFallbackRegisters(arch_to_use);
AddRemoteRegisters(registers, arch_to_use);
}

View File

@@ -0,0 +1,346 @@
from __future__ import print_function
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test.gdbclientutils import *
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
import binascii
class TestGDBServerTargetXML(GDBRemoteTestBase):
mydir = TestBase.compute_mydir(__file__)
@staticmethod
def filecheck_to_blob(fc):
for l in fc.strip().splitlines():
val = l.split('0x')[1]
yield binascii.b2a_hex(bytes(reversed(binascii.a2b_hex(val)))).decode()
@skipIfRemote
@skipIfLLVMTargetMissing("X86")
def test_x86_64_regs(self):
"""Test grabbing various x86_64 registers from gdbserver."""
GPRS = '''
CHECK-AMD64-DAG: rax = 0x0807060504030201
CHECK-AMD64-DAG: rbx = 0x1817161514131211
CHECK-AMD64-DAG: rcx = 0x2827262524232221
CHECK-AMD64-DAG: rdx = 0x3837363534333231
CHECK-AMD64-DAG: rsi = 0x4847464544434241
CHECK-AMD64-DAG: rdi = 0x5857565554535251
CHECK-AMD64-DAG: rbp = 0x6867666564636261
CHECK-AMD64-DAG: rsp = 0x7877767574737271
CHECK-AMD64-DAG: r8 = 0x8887868584838281
CHECK-AMD64-DAG: r9 = 0x9897969594939291
CHECK-AMD64-DAG: r10 = 0xa8a7a6a5a4a3a2a1
CHECK-AMD64-DAG: r11 = 0xb8b7b6b5b4b3b2b1
CHECK-AMD64-DAG: r12 = 0xc8c7c6c5c4c3c2c1
CHECK-AMD64-DAG: r13 = 0xd8d7d6d5d4d3d2d1
CHECK-AMD64-DAG: r14 = 0xe8e7e6e5e4e3e2e1
CHECK-AMD64-DAG: r15 = 0xf8f7f6f5f4f3f2f1
CHECK-AMD64-DAG: rip = 0x100f0e0d0c0b0a09
CHECK-AMD64-DAG: eflags = 0x1c1b1a19
CHECK-AMD64-DAG: cs = 0x2c2b2a29
CHECK-AMD64-DAG: ss = 0x3c3b3a39
'''
SUPPL = '''
CHECK-AMD64-DAG: eax = 0x04030201
CHECK-AMD64-DAG: ebx = 0x14131211
CHECK-AMD64-DAG: ecx = 0x24232221
CHECK-AMD64-DAG: edx = 0x34333231
CHECK-AMD64-DAG: esi = 0x44434241
CHECK-AMD64-DAG: edi = 0x54535251
CHECK-AMD64-DAG: ebp = 0x64636261
CHECK-AMD64-DAG: esp = 0x74737271
CHECK-AMD64-DAG: r8d = 0x84838281
CHECK-AMD64-DAG: r9d = 0x94939291
CHECK-AMD64-DAG: r10d = 0xa4a3a2a1
CHECK-AMD64-DAG: r11d = 0xb4b3b2b1
CHECK-AMD64-DAG: r12d = 0xc4c3c2c1
CHECK-AMD64-DAG: r13d = 0xd4d3d2d1
CHECK-AMD64-DAG: r14d = 0xe4e3e2e1
CHECK-AMD64-DAG: r15d = 0xf4f3f2f1
CHECK-AMD64-DAG: ax = 0x0201
CHECK-AMD64-DAG: bx = 0x1211
CHECK-AMD64-DAG: cx = 0x2221
CHECK-AMD64-DAG: dx = 0x3231
CHECK-AMD64-DAG: si = 0x4241
CHECK-AMD64-DAG: di = 0x5251
CHECK-AMD64-DAG: bp = 0x6261
CHECK-AMD64-DAG: sp = 0x7271
CHECK-AMD64-DAG: r8w = 0x8281
CHECK-AMD64-DAG: r9w = 0x9291
CHECK-AMD64-DAG: r10w = 0xa2a1
CHECK-AMD64-DAG: r11w = 0xb2b1
CHECK-AMD64-DAG: r12w = 0xc2c1
CHECK-AMD64-DAG: r13w = 0xd2d1
CHECK-AMD64-DAG: r14w = 0xe2e1
CHECK-AMD64-DAG: r15w = 0xf2f1
CHECK-AMD64-DAG: ah = 0x02
CHECK-AMD64-DAG: bh = 0x12
CHECK-AMD64-DAG: ch = 0x22
CHECK-AMD64-DAG: dh = 0x32
CHECK-AMD64-DAG: al = 0x01
CHECK-AMD64-DAG: bl = 0x11
CHECK-AMD64-DAG: cl = 0x21
CHECK-AMD64-DAG: dl = 0x31
CHECK-AMD64-DAG: sil = 0x41
CHECK-AMD64-DAG: dil = 0x51
CHECK-AMD64-DAG: bpl = 0x61
CHECK-AMD64-DAG: spl = 0x71
CHECK-AMD64-DAG: r8l = 0x81
CHECK-AMD64-DAG: r9l = 0x91
CHECK-AMD64-DAG: r10l = 0xa1
CHECK-AMD64-DAG: r11l = 0xb1
CHECK-AMD64-DAG: r12l = 0xc1
CHECK-AMD64-DAG: r13l = 0xd1
CHECK-AMD64-DAG: r14l = 0xe1
CHECK-AMD64-DAG: r15l = 0xf1
'''
class MyResponder(MockGDBServerResponder):
reg_data = ''.join(self.filecheck_to_blob(GPRS))
def readRegister(self, regnum):
return ""
def readRegisters(self):
return self.reg_data
def haltReason(self):
return "T02thread:1ff0d;threads:1ff0d;thread-pcs:000000010001bc00;07:0102030405060708;10:1112131415161718;"
self.server.responder = MyResponder()
target = self.createTarget("basic_eh_frame.yaml")
process = self.connect(target)
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
[lldb.eStateStopped])
# test all registers
self.filecheck("register read --all", __file__,
filecheck_options='--check-prefix=CHECK-AMD64')
# test generic aliases
self.match("register read arg4",
["rcx = 0x2827262524232221"])
self.match("register read arg3",
["rdx = 0x3837363534333231"])
self.match("register read arg2",
["rsi = 0x4847464544434241"])
self.match("register read arg1",
["rdi = 0x5857565554535251"])
self.match("register read fp",
["rbp = 0x6867666564636261"])
self.match("register read sp",
["rsp = 0x7877767574737271"])
self.match("register read arg5",
["r8 = 0x8887868584838281"])
self.match("register read arg6",
["r9 = 0x9897969594939291"])
self.match("register read pc",
["rip = 0x100f0e0d0c0b0a09"])
self.match("register read flags",
["eflags = 0x1c1b1a19"])
@skipIfRemote
@skipIfLLVMTargetMissing("AArch64")
def test_aarch64_regs(self):
"""Test grabbing various aarch64 registers from gdbserver."""
GPRS = '''
CHECK-AARCH64-DAG: x0 = 0x0001020304050607
CHECK-AARCH64-DAG: x1 = 0x0102030405060708
CHECK-AARCH64-DAG: x2 = 0x0203040506070809
CHECK-AARCH64-DAG: x3 = 0x030405060708090a
CHECK-AARCH64-DAG: x4 = 0x0405060708090a0b
CHECK-AARCH64-DAG: x5 = 0x05060708090a0b0c
CHECK-AARCH64-DAG: x6 = 0x060708090a0b0c0d
CHECK-AARCH64-DAG: x7 = 0x0708090a0b0c0d0e
CHECK-AARCH64-DAG: x8 = 0x08090a0b0c0d0e0f
CHECK-AARCH64-DAG: x9 = 0x090a0b0c0d0e0f10
CHECK-AARCH64-DAG: x10 = 0x0a0b0c0d0e0f1011
CHECK-AARCH64-DAG: x11 = 0x0b0c0d0e0f101112
CHECK-AARCH64-DAG: x12 = 0x0c0d0e0f10111213
CHECK-AARCH64-DAG: x13 = 0x0d0e0f1011121314
CHECK-AARCH64-DAG: x14 = 0x0e0f101112131415
CHECK-AARCH64-DAG: x15 = 0x0f10111213141516
CHECK-AARCH64-DAG: x16 = 0x1011121314151617
CHECK-AARCH64-DAG: x17 = 0x1112131415161718
CHECK-AARCH64-DAG: x18 = 0x1213141516171819
CHECK-AARCH64-DAG: x19 = 0x131415161718191a
CHECK-AARCH64-DAG: x20 = 0x1415161718191a1b
CHECK-AARCH64-DAG: x21 = 0x15161718191a1b1c
CHECK-AARCH64-DAG: x22 = 0x161718191a1b1c1d
CHECK-AARCH64-DAG: x23 = 0x1718191a1b1c1d1e
CHECK-AARCH64-DAG: x24 = 0x18191a1b1c1d1e1f
CHECK-AARCH64-DAG: x25 = 0x191a1b1c1d1e1f20
CHECK-AARCH64-DAG: x26 = 0x1a1b1c1d1e1f2021
CHECK-AARCH64-DAG: x27 = 0x1b1c1d1e1f202122
CHECK-AARCH64-DAG: x28 = 0x1c1d1e1f20212223
CHECK-AARCH64-DAG: x29 = 0x1d1e1f2021222324
CHECK-AARCH64-DAG: x30 = 0x1e1f202122232425
CHECK-AARCH64-DAG: sp = 0x1f20212223242526
CHECK-AARCH64-DAG: pc = 0x2021222324252627
CHECK-AARCH64-DAG: cpsr = 0x21222324
'''
SUPPL = '''
CHECK-AARCH64-DAG: w0 = 0x04050607
CHECK-AARCH64-DAG: w1 = 0x05060708
CHECK-AARCH64-DAG: w2 = 0x06070809
CHECK-AARCH64-DAG: w3 = 0x0708090a
CHECK-AARCH64-DAG: w4 = 0x08090a0b
CHECK-AARCH64-DAG: w5 = 0x090a0b0c
CHECK-AARCH64-DAG: w6 = 0x0a0b0c0d
CHECK-AARCH64-DAG: w7 = 0x0b0c0d0e
CHECK-AARCH64-DAG: w8 = 0x0c0d0e0f
CHECK-AARCH64-DAG: w9 = 0x0d0e0f10
CHECK-AARCH64-DAG: w10 = 0x0e0f1011
CHECK-AARCH64-DAG: w11 = 0x0f101112
CHECK-AARCH64-DAG: w12 = 0x10111213
CHECK-AARCH64-DAG: w13 = 0x11121314
CHECK-AARCH64-DAG: w14 = 0x12131415
CHECK-AARCH64-DAG: w15 = 0x13141516
CHECK-AARCH64-DAG: w16 = 0x14151617
CHECK-AARCH64-DAG: w17 = 0x15161718
CHECK-AARCH64-DAG: w18 = 0x16171819
CHECK-AARCH64-DAG: w19 = 0x1718191a
CHECK-AARCH64-DAG: w20 = 0x18191a1b
CHECK-AARCH64-DAG: w21 = 0x191a1b1c
CHECK-AARCH64-DAG: w22 = 0x1a1b1c1d
CHECK-AARCH64-DAG: w23 = 0x1b1c1d1e
CHECK-AARCH64-DAG: w24 = 0x1c1d1e1f
CHECK-AARCH64-DAG: w25 = 0x1d1e1f20
CHECK-AARCH64-DAG: w26 = 0x1e1f2021
CHECK-AARCH64-DAG: w27 = 0x1f202122
CHECK-AARCH64-DAG: w28 = 0x20212223
CHECK-AARCH64-DAG: w29 = 0x21222324
CHECK-AARCH64-DAG: w30 = 0x22232425
CHECK-AARCH64-DAG: w31 = 0x23242526
'''
class MyResponder(MockGDBServerResponder):
reg_data = ''.join(self.filecheck_to_blob(GPRS))
def readRegister(self, regnum):
return ""
def readRegisters(self):
return self.reg_data
def haltReason(self):
return "T02thread:1ff0d;threads:1ff0d;thread-pcs:000000010001bc00;07:0102030405060708;10:1112131415161718;"
self.server.responder = MyResponder()
target = self.createTarget("basic_eh_frame-aarch64.yaml")
process = self.connect(target)
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
[lldb.eStateStopped])
# test all registers
self.filecheck("register read --all", __file__,
filecheck_options='--check-prefix=CHECK-AARCH64')
# test generic aliases
self.match("register read arg1",
["x0 = 0x0001020304050607"])
self.match("register read arg2",
["x1 = 0x0102030405060708"])
self.match("register read fp",
["x29 = 0x1d1e1f2021222324"])
self.match("register read lr",
["x30 = 0x1e1f202122232425"])
self.match("register read ra",
["x30 = 0x1e1f202122232425"])
self.match("register read flags",
["cpsr = 0x21222324"])
@skipIfRemote
@skipIfLLVMTargetMissing("X86")
def test_i386_regs(self):
"""Test grabbing various i386 registers from gdbserver."""
GPRS = '''
CHECK-I386-DAG: eax = 0x04030201
CHECK-I386-DAG: ecx = 0x14131211
CHECK-I386-DAG: edx = 0x24232221
CHECK-I386-DAG: ebx = 0x34333231
CHECK-I386-DAG: esp = 0x44434241
CHECK-I386-DAG: ebp = 0x54535251
CHECK-I386-DAG: esi = 0x64636261
CHECK-I386-DAG: edi = 0x74737271
CHECK-I386-DAG: eip = 0x84838281
CHECK-I386-DAG: eflags = 0x94939291
CHECK-I386-DAG: cs = 0xa4a3a2a1
CHECK-I386-DAG: ss = 0xb4b3b2b1
CHECK-I386-DAG: ds = 0xc4c3c2c1
CHECK-I386-DAG: es = 0xd4d3d2d1
CHECK-I386-DAG: fs = 0xe4e3e2e1
CHECK-I386-DAG: gs = 0xf4f3f2f1
'''
SUPPL = '''
CHECK-I386-DAG: ax = 0x0201
CHECK-I386-DAG: cx = 0x1211
CHECK-I386-DAG: dx = 0x2221
CHECK-I386-DAG: bx = 0x3231
CHECK-I386-DAG: sp = 0x4241
CHECK-I386-DAG: bp = 0x5251
CHECK-I386-DAG: si = 0x6261
CHECK-I386-DAG: di = 0x7271
CHECK-I386-DAG: ah = 0x02
CHECK-I386-DAG: ch = 0x12
CHECK-I386-DAG: dh = 0x22
CHECK-I386-DAG: bh = 0x32
CHECK-I386-DAG: al = 0x01
CHECK-I386-DAG: cl = 0x11
CHECK-I386-DAG: dl = 0x21
CHECK-I386-DAG: bl = 0x31
CHECK-I386-DAG: spl = 0x41
CHECK-I386-DAG: bpl = 0x51
CHECK-I386-DAG: sil = 0x61
CHECK-I386-DAG: dil = 0x71
'''
class MyResponder(MockGDBServerResponder):
reg_data = ''.join(self.filecheck_to_blob(GPRS))
def readRegister(self, regnum):
return ""
def readRegisters(self):
return self.reg_data
def haltReason(self):
return "T02thread:1ff0d;threads:1ff0d;thread-pcs:000000010001bc00;07:0102030405060708;10:1112131415161718;"
self.server.responder = MyResponder()
target = self.createTarget("basic_eh_frame-i386.yaml")
process = self.connect(target)
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
[lldb.eStateStopped])
# test all registers
self.filecheck("register read --all", __file__,
filecheck_options='--check-prefix=CHECK-I386')
# test generic aliases
self.match("register read fp",
["ebp = 0x54535251"])
self.match("register read sp",
["esp = 0x44434241"])
self.match("register read pc",
["eip = 0x84838281"])
self.match("register read flags",
["eflags = 0x94939291"])