Skip to content

Commit 86bb38c

Browse files
committed
gdb: Make tagged pointer support configurable.
The gdbarch function gdbarch_remove_non_address_bits adjusts addresses to enable debugging of programs with tagged pointers on Linux, for instance for ARM's feature top byte ignore (TBI). Once the function is implemented for an architecture, it adjusts addresses for memory access, breakpoints and watchpoints. Linear address masking (LAM) is Intel's (R) implementation of tagged pointer support. It requires certain adaptions to GDB's tagged pointer support due to the following: - LAM supports address tagging for data accesses only. Thus, specifying breakpoints on tagged addresses is not a valid use case. - In contrast to the implementation for ARM's TBI, the Linux kernel supports tagged pointers for memory access. This patch makes GDB's tagged pointer support configurable such that it is possible to enable the address adjustment for a specific feature only (e.g memory access, breakpoints or watchpoints). This way, one can make sure that addresses are only adjusted when necessary. In case of LAM, this avoids unnecessary parsing of the /proc/<pid>/status file to get the untag mask. Reviewed-By: Felix Willgerodt <[email protected]> (AArch64) Tested-By: Luis Machado <[email protected]> Approved-By: Luis Machado <[email protected]>
1 parent 335cb88 commit 86bb38c

9 files changed

+168
-46
lines changed

gdb/aarch64-linux-nat.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
943943
kernel can potentially be tagged addresses. */
944944
struct gdbarch *gdbarch = thread_architecture (inferior_ptid);
945945
const CORE_ADDR addr_trap
946-
= gdbarch_remove_non_address_bits (gdbarch, (CORE_ADDR) siginfo.si_addr);
946+
= aarch64_remove_non_address_bits (gdbarch, (CORE_ADDR) siginfo.si_addr);
947947

948948
/* Check if the address matches any watched address. */
949949
state = aarch64_get_debug_reg_state (inferior_ptid.pid ());

gdb/aarch64-linux-tdep.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -2430,7 +2430,7 @@ static bool
24302430
aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, CORE_ADDR address)
24312431
{
24322432
/* Remove the top byte for the memory range check. */
2433-
address = gdbarch_remove_non_address_bits (gdbarch, address);
2433+
address = aarch64_remove_non_address_bits (gdbarch, address);
24342434

24352435
/* Check if the page that contains ADDRESS is mapped with PROT_MTE. */
24362436
if (!linux_address_in_memtag_page (address))
@@ -2488,8 +2488,9 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
24882488
uiout->text ("\n");
24892489

24902490
std::optional<CORE_ADDR> atag
2491-
= aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch,
2492-
fault_addr));
2491+
= aarch64_mte_get_atag (
2492+
aarch64_remove_non_address_bits (gdbarch, fault_addr));
2493+
24932494
gdb_byte ltag = aarch64_mte_get_ltag (fault_addr);
24942495

24952496
if (!atag.has_value ())

gdb/aarch64-tdep.c

+14-9
Original file line numberDiff line numberDiff line change
@@ -4121,7 +4121,7 @@ aarch64_memtag_matches_p (struct gdbarch *gdbarch,
41214121

41224122
/* Fetch the allocation tag for ADDRESS. */
41234123
std::optional<CORE_ADDR> atag
4124-
= aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr));
4124+
= aarch64_mte_get_atag (aarch64_remove_non_address_bits (gdbarch, addr));
41254125

41264126
if (!atag.has_value ())
41274127
return true;
@@ -4160,7 +4160,7 @@ aarch64_set_memtags (struct gdbarch *gdbarch, struct value *address,
41604160
else
41614161
{
41624162
/* Remove the top byte. */
4163-
addr = gdbarch_remove_non_address_bits (gdbarch, addr);
4163+
addr = aarch64_remove_non_address_bits (gdbarch, addr);
41644164

41654165
/* With G being the number of tag granules and N the number of tags
41664166
passed in, we can have the following cases:
@@ -4209,7 +4209,7 @@ aarch64_get_memtag (struct gdbarch *gdbarch, struct value *address,
42094209
else
42104210
{
42114211
/* Remove the top byte. */
4212-
addr = gdbarch_remove_non_address_bits (gdbarch, addr);
4212+
addr = aarch64_remove_non_address_bits (gdbarch, addr);
42134213
std::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr);
42144214

42154215
if (!atag.has_value ())
@@ -4236,10 +4236,9 @@ aarch64_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value)
42364236
return string_printf ("0x%s", phex_nz (tag, sizeof (tag)));
42374237
}
42384238

4239-
/* AArch64 implementation of the remove_non_address_bits gdbarch hook. Remove
4240-
non address bits from a pointer value. */
4239+
/* See aarch64-tdep.h. */
42414240

4242-
static CORE_ADDR
4241+
CORE_ADDR
42434242
aarch64_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer)
42444243
{
42454244
/* By default, we assume TBI and discard the top 8 bits plus the VA range
@@ -4750,9 +4749,15 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
47504749
tdep->ra_sign_state_regnum = ra_sign_state_offset + num_regs;
47514750

47524751
/* Architecture hook to remove bits of a pointer that are not part of the
4753-
address, like memory tags (MTE) and pointer authentication signatures. */
4754-
set_gdbarch_remove_non_address_bits (gdbarch,
4755-
aarch64_remove_non_address_bits);
4752+
address, like memory tags (MTE) and pointer authentication signatures.
4753+
Configure address adjustment for watchpoints, breakpoints and memory
4754+
transfer. */
4755+
set_gdbarch_remove_non_address_bits_watchpoint
4756+
(gdbarch, aarch64_remove_non_address_bits);
4757+
set_gdbarch_remove_non_address_bits_breakpoint
4758+
(gdbarch, aarch64_remove_non_address_bits);
4759+
set_gdbarch_remove_non_address_bits_memory
4760+
(gdbarch, aarch64_remove_non_address_bits);
47564761

47574762
/* SME pseudo-registers. */
47584763
if (tdep->has_sme ())

gdb/aarch64-tdep.h

+6
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,10 @@ bool aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch);
205205

206206
std::optional<CORE_ADDR> aarch64_mte_get_atag (CORE_ADDR address);
207207

208+
/* AArch64 implementation of the remove_non_address_bits gdbarch hooks.
209+
Remove non address bits from a pointer value. */
210+
211+
CORE_ADDR aarch64_remove_non_address_bits (struct gdbarch *gdbarch,
212+
CORE_ADDR pointer);
213+
208214
#endif /* aarch64-tdep.h */

gdb/breakpoint.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -2313,7 +2313,8 @@ update_watchpoint (struct watchpoint *b, bool reparse)
23132313
loc->gdbarch = v->type ()->arch ();
23142314
loc->pspace = wp_pspace;
23152315
loc->address
2316-
= gdbarch_remove_non_address_bits (loc->gdbarch, addr);
2316+
= gdbarch_remove_non_address_bits_watchpoint (loc->gdbarch,
2317+
addr);
23172318
b->add_location (*loc);
23182319

23192320
if (bitsize != 0)
@@ -7538,7 +7539,7 @@ adjust_breakpoint_address (struct gdbarch *gdbarch,
75387539
}
75397540

75407541
adjusted_bpaddr
7541-
= gdbarch_remove_non_address_bits (gdbarch, adjusted_bpaddr);
7542+
= gdbarch_remove_non_address_bits_breakpoint (gdbarch, adjusted_bpaddr);
75427543

75437544
/* An adjusted breakpoint address can significantly alter
75447545
a user's expectations. Print a warning if an adjustment

gdb/gdbarch-gen.c

+55-11
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ struct gdbarch
143143
int frame_red_zone_size = 0;
144144
gdbarch_convert_from_func_ptr_addr_ftype *convert_from_func_ptr_addr = convert_from_func_ptr_addr_identity;
145145
gdbarch_addr_bits_remove_ftype *addr_bits_remove = core_addr_identity;
146-
gdbarch_remove_non_address_bits_ftype *remove_non_address_bits = default_remove_non_address_bits;
146+
gdbarch_remove_non_address_bits_watchpoint_ftype *remove_non_address_bits_watchpoint = default_remove_non_address_bits;
147+
gdbarch_remove_non_address_bits_breakpoint_ftype *remove_non_address_bits_breakpoint = default_remove_non_address_bits;
148+
gdbarch_remove_non_address_bits_memory_ftype *remove_non_address_bits_memory = default_remove_non_address_bits;
147149
gdbarch_memtag_to_string_ftype *memtag_to_string = default_memtag_to_string;
148150
gdbarch_tagged_address_p_ftype *tagged_address_p = default_tagged_address_p;
149151
gdbarch_memtag_matches_p_ftype *memtag_matches_p = default_memtag_matches_p;
@@ -407,7 +409,9 @@ verify_gdbarch (struct gdbarch *gdbarch)
407409
/* Skip verify of frame_red_zone_size, invalid_p == 0. */
408410
/* Skip verify of convert_from_func_ptr_addr, invalid_p == 0. */
409411
/* Skip verify of addr_bits_remove, invalid_p == 0. */
410-
/* Skip verify of remove_non_address_bits, invalid_p == 0. */
412+
/* Skip verify of remove_non_address_bits_watchpoint, invalid_p == 0. */
413+
/* Skip verify of remove_non_address_bits_breakpoint, invalid_p == 0. */
414+
/* Skip verify of remove_non_address_bits_memory, invalid_p == 0. */
411415
/* Skip verify of memtag_to_string, invalid_p == 0. */
412416
/* Skip verify of tagged_address_p, invalid_p == 0. */
413417
/* Skip verify of memtag_matches_p, invalid_p == 0. */
@@ -910,8 +914,14 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
910914
"gdbarch_dump: addr_bits_remove = <%s>\n",
911915
host_address_to_string (gdbarch->addr_bits_remove));
912916
gdb_printf (file,
913-
"gdbarch_dump: remove_non_address_bits = <%s>\n",
914-
host_address_to_string (gdbarch->remove_non_address_bits));
917+
"gdbarch_dump: remove_non_address_bits_watchpoint = <%s>\n",
918+
host_address_to_string (gdbarch->remove_non_address_bits_watchpoint));
919+
gdb_printf (file,
920+
"gdbarch_dump: remove_non_address_bits_breakpoint = <%s>\n",
921+
host_address_to_string (gdbarch->remove_non_address_bits_breakpoint));
922+
gdb_printf (file,
923+
"gdbarch_dump: remove_non_address_bits_memory = <%s>\n",
924+
host_address_to_string (gdbarch->remove_non_address_bits_memory));
915925
gdb_printf (file,
916926
"gdbarch_dump: memtag_to_string = <%s>\n",
917927
host_address_to_string (gdbarch->memtag_to_string));
@@ -3198,20 +3208,54 @@ set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch,
31983208
}
31993209

32003210
CORE_ADDR
3201-
gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer)
3211+
gdbarch_remove_non_address_bits_watchpoint (struct gdbarch *gdbarch, CORE_ADDR pointer)
3212+
{
3213+
gdb_assert (gdbarch != NULL);
3214+
gdb_assert (gdbarch->remove_non_address_bits_watchpoint != NULL);
3215+
if (gdbarch_debug >= 2)
3216+
gdb_printf (gdb_stdlog, "gdbarch_remove_non_address_bits_watchpoint called\n");
3217+
return gdbarch->remove_non_address_bits_watchpoint (gdbarch, pointer);
3218+
}
3219+
3220+
void
3221+
set_gdbarch_remove_non_address_bits_watchpoint (struct gdbarch *gdbarch,
3222+
gdbarch_remove_non_address_bits_watchpoint_ftype remove_non_address_bits_watchpoint)
3223+
{
3224+
gdbarch->remove_non_address_bits_watchpoint = remove_non_address_bits_watchpoint;
3225+
}
3226+
3227+
CORE_ADDR
3228+
gdbarch_remove_non_address_bits_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pointer)
3229+
{
3230+
gdb_assert (gdbarch != NULL);
3231+
gdb_assert (gdbarch->remove_non_address_bits_breakpoint != NULL);
3232+
if (gdbarch_debug >= 2)
3233+
gdb_printf (gdb_stdlog, "gdbarch_remove_non_address_bits_breakpoint called\n");
3234+
return gdbarch->remove_non_address_bits_breakpoint (gdbarch, pointer);
3235+
}
3236+
3237+
void
3238+
set_gdbarch_remove_non_address_bits_breakpoint (struct gdbarch *gdbarch,
3239+
gdbarch_remove_non_address_bits_breakpoint_ftype remove_non_address_bits_breakpoint)
3240+
{
3241+
gdbarch->remove_non_address_bits_breakpoint = remove_non_address_bits_breakpoint;
3242+
}
3243+
3244+
CORE_ADDR
3245+
gdbarch_remove_non_address_bits_memory (struct gdbarch *gdbarch, CORE_ADDR pointer)
32023246
{
32033247
gdb_assert (gdbarch != NULL);
3204-
gdb_assert (gdbarch->remove_non_address_bits != NULL);
3248+
gdb_assert (gdbarch->remove_non_address_bits_memory != NULL);
32053249
if (gdbarch_debug >= 2)
3206-
gdb_printf (gdb_stdlog, "gdbarch_remove_non_address_bits called\n");
3207-
return gdbarch->remove_non_address_bits (gdbarch, pointer);
3250+
gdb_printf (gdb_stdlog, "gdbarch_remove_non_address_bits_memory called\n");
3251+
return gdbarch->remove_non_address_bits_memory (gdbarch, pointer);
32083252
}
32093253

32103254
void
3211-
set_gdbarch_remove_non_address_bits (struct gdbarch *gdbarch,
3212-
gdbarch_remove_non_address_bits_ftype remove_non_address_bits)
3255+
set_gdbarch_remove_non_address_bits_memory (struct gdbarch *gdbarch,
3256+
gdbarch_remove_non_address_bits_memory_ftype remove_non_address_bits_memory)
32133257
{
3214-
gdbarch->remove_non_address_bits = remove_non_address_bits;
3258+
gdbarch->remove_non_address_bits_memory = remove_non_address_bits_memory;
32153259
}
32163260

32173261
std::string

gdb/gdbarch-gen.h

+38-11
Original file line numberDiff line numberDiff line change
@@ -684,19 +684,46 @@ extern CORE_ADDR gdbarch_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR ad
684684
extern void set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch, gdbarch_addr_bits_remove_ftype *addr_bits_remove);
685685

686686
/* On some architectures, not all bits of a pointer are significant.
687-
On AArch64, for example, the top bits of a pointer may carry a "tag", which
688-
can be ignored by the kernel and the hardware. The "tag" can be regarded as
689-
additional data associated with the pointer, but it is not part of the address.
687+
On AArch64 and amd64, for example, the top bits of a pointer may carry a
688+
"tag", which can be ignored by the kernel and the hardware. The "tag" can be
689+
regarded as additional data associated with the pointer, but it is not part
690+
of the address.
690691
691692
Given a pointer for the architecture, this hook removes all the
692-
non-significant bits and sign-extends things as needed. It gets used to remove
693-
non-address bits from data pointers (for example, removing the AArch64 MTE tag
694-
bits from a pointer) and from code pointers (removing the AArch64 PAC signature
695-
from a pointer containing the return address). */
696-
697-
typedef CORE_ADDR (gdbarch_remove_non_address_bits_ftype) (struct gdbarch *gdbarch, CORE_ADDR pointer);
698-
extern CORE_ADDR gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer);
699-
extern void set_gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, gdbarch_remove_non_address_bits_ftype *remove_non_address_bits);
693+
non-significant bits and sign-extends things as needed. It gets used to
694+
remove non-address bits from pointers used for watchpoints. */
695+
696+
typedef CORE_ADDR (gdbarch_remove_non_address_bits_watchpoint_ftype) (struct gdbarch *gdbarch, CORE_ADDR pointer);
697+
extern CORE_ADDR gdbarch_remove_non_address_bits_watchpoint (struct gdbarch *gdbarch, CORE_ADDR pointer);
698+
extern void set_gdbarch_remove_non_address_bits_watchpoint (struct gdbarch *gdbarch, gdbarch_remove_non_address_bits_watchpoint_ftype *remove_non_address_bits_watchpoint);
699+
700+
/* On some architectures, not all bits of a pointer are significant.
701+
On AArch64 and amd64, for example, the top bits of a pointer may carry a
702+
"tag", which can be ignored by the kernel and the hardware. The "tag" can be
703+
regarded as additional data associated with the pointer, but it is not part
704+
of the address.
705+
706+
Given a pointer for the architecture, this hook removes all the
707+
non-significant bits and sign-extends things as needed. It gets used to
708+
remove non-address bits from pointers used for breakpoints. */
709+
710+
typedef CORE_ADDR (gdbarch_remove_non_address_bits_breakpoint_ftype) (struct gdbarch *gdbarch, CORE_ADDR pointer);
711+
extern CORE_ADDR gdbarch_remove_non_address_bits_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pointer);
712+
extern void set_gdbarch_remove_non_address_bits_breakpoint (struct gdbarch *gdbarch, gdbarch_remove_non_address_bits_breakpoint_ftype *remove_non_address_bits_breakpoint);
713+
714+
/* On some architectures, not all bits of a pointer are significant.
715+
On AArch64 and amd64, for example, the top bits of a pointer may carry a
716+
"tag", which can be ignored by the kernel and the hardware. The "tag" can be
717+
regarded as additional data associated with the pointer, but it is not part
718+
of the address.
719+
720+
Given a pointer for the architecture, this hook removes all the
721+
non-significant bits and sign-extends things as needed. It gets used to
722+
remove non-address bits from any pointer used to access memory. */
723+
724+
typedef CORE_ADDR (gdbarch_remove_non_address_bits_memory_ftype) (struct gdbarch *gdbarch, CORE_ADDR pointer);
725+
extern CORE_ADDR gdbarch_remove_non_address_bits_memory (struct gdbarch *gdbarch, CORE_ADDR pointer);
726+
extern void set_gdbarch_remove_non_address_bits_memory (struct gdbarch *gdbarch, gdbarch_remove_non_address_bits_memory_ftype *remove_non_address_bits_memory);
700727

701728
/* Return a string representation of the memory tag TAG. */
702729

gdb/gdbarch_components.py

+45-8
Original file line numberDiff line numberDiff line change
@@ -1232,18 +1232,55 @@
12321232
Method(
12331233
comment="""
12341234
On some architectures, not all bits of a pointer are significant.
1235-
On AArch64, for example, the top bits of a pointer may carry a "tag", which
1236-
can be ignored by the kernel and the hardware. The "tag" can be regarded as
1237-
additional data associated with the pointer, but it is not part of the address.
1235+
On AArch64 and amd64, for example, the top bits of a pointer may carry a
1236+
"tag", which can be ignored by the kernel and the hardware. The "tag" can be
1237+
regarded as additional data associated with the pointer, but it is not part
1238+
of the address.
12381239
12391240
Given a pointer for the architecture, this hook removes all the
1240-
non-significant bits and sign-extends things as needed. It gets used to remove
1241-
non-address bits from data pointers (for example, removing the AArch64 MTE tag
1242-
bits from a pointer) and from code pointers (removing the AArch64 PAC signature
1243-
from a pointer containing the return address).
1241+
non-significant bits and sign-extends things as needed. It gets used to
1242+
remove non-address bits from pointers used for watchpoints.
12441243
""",
12451244
type="CORE_ADDR",
1246-
name="remove_non_address_bits",
1245+
name="remove_non_address_bits_watchpoint",
1246+
params=[("CORE_ADDR", "pointer")],
1247+
predefault="default_remove_non_address_bits",
1248+
invalid=False,
1249+
)
1250+
1251+
Method(
1252+
comment="""
1253+
On some architectures, not all bits of a pointer are significant.
1254+
On AArch64 and amd64, for example, the top bits of a pointer may carry a
1255+
"tag", which can be ignored by the kernel and the hardware. The "tag" can be
1256+
regarded as additional data associated with the pointer, but it is not part
1257+
of the address.
1258+
1259+
Given a pointer for the architecture, this hook removes all the
1260+
non-significant bits and sign-extends things as needed. It gets used to
1261+
remove non-address bits from pointers used for breakpoints.
1262+
""",
1263+
type="CORE_ADDR",
1264+
name="remove_non_address_bits_breakpoint",
1265+
params=[("CORE_ADDR", "pointer")],
1266+
predefault="default_remove_non_address_bits",
1267+
invalid=False,
1268+
)
1269+
1270+
Method(
1271+
comment="""
1272+
On some architectures, not all bits of a pointer are significant.
1273+
On AArch64 and amd64, for example, the top bits of a pointer may carry a
1274+
"tag", which can be ignored by the kernel and the hardware. The "tag" can be
1275+
regarded as additional data associated with the pointer, but it is not part
1276+
of the address.
1277+
1278+
Given a pointer for the architecture, this hook removes all the
1279+
non-significant bits and sign-extends things as needed. It gets used to
1280+
remove non-address bits from any pointer used to access memory.
1281+
""",
1282+
type="CORE_ADDR",
1283+
name="remove_non_address_bits_memory",
12471284
params=[("CORE_ADDR", "pointer")],
12481285
predefault="default_remove_non_address_bits",
12491286
invalid=False,

gdb/target.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -1608,7 +1608,8 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
16081608
if (len == 0)
16091609
return TARGET_XFER_EOF;
16101610

1611-
memaddr = gdbarch_remove_non_address_bits (current_inferior ()->arch (),
1611+
memaddr
1612+
= gdbarch_remove_non_address_bits_memory (current_inferior ()->arch (),
16121613
memaddr);
16131614

16141615
/* Fill in READBUF with breakpoint shadows, or WRITEBUF with

0 commit comments

Comments
 (0)