Skip to content

Commit 0ceaab5

Browse files
i#7729: Fix incorrect drmemtrace addr for stolen reg (#7730)
Fixes a bug where drmemtrace records the wrong address for a load with no index and a base of the stolen register. Adds a test case to burst_aarch64_sys which fails without the fix. Adds a drutil base reg test case to the stolen-reg-index test as well, though drutil operates correctly here. Fixes #7729
1 parent 2e3c900 commit 0ceaab5

File tree

5 files changed

+139
-10
lines changed

5 files changed

+139
-10
lines changed

clients/drcachesim/tests/burst_aarch64_sys.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,32 @@ ic_unprivileged_flush()
119119
__asm__ __volatile__("ic ivau , %0" : : "r"(ic_unprivileged_flush));
120120
}
121121

122+
static int64_t global_var[2];
123+
constexpr int SENTINEL_MOV = 12345;
124+
125+
static void
126+
stolen_reg_load()
127+
{
128+
// Test the stolen register as a base reg of a load (test i#7729).
129+
assert(dr_get_stolen_reg() == DR_REG_X28);
130+
// Use adrp for the page and the ":lo12:" pattern for the offset to
131+
// get &global_var[1] into the x28 register.
132+
__asm__ __volatile__("adrp x28, %0\n\t"
133+
"add x28, x28, #:lo12:%0\n\t"
134+
"mov x0, #%1\n\n" // Marker.
135+
"ldr x0, [x28, 8]" // Test a non-zero offset.
136+
:
137+
: "S"(&global_var), "i"(SENTINEL_MOV)
138+
: "x0", "x28");
139+
}
140+
122141
static void
123142
do_some_work()
124143
{
125144
dc_zva();
126145
dc_unprivileged_flush();
127146
ic_unprivileged_flush();
147+
stolen_reg_load();
128148

129149
// Testing privileged instructions requires handling SIGILL.
130150
// We use sigsetjmp/siglongjmp to resume execution after handling the
@@ -214,6 +234,39 @@ is_dc_zva_instr(void *dr_context, memref_t memref)
214234
return is_dc_zva;
215235
}
216236

237+
bool
238+
is_mov_immed_instr(void *dr_context, memref_t memref, int64_t &immed)
239+
{
240+
if (!type_is_instr(memref.instr.type))
241+
return false;
242+
app_pc pc = (app_pc)memref.instr.addr;
243+
instr_t instr;
244+
instr_init(dr_context, &instr);
245+
auto *ret = decode(dr_context, pc, &instr);
246+
assert(ret != NULL && instr_valid(&instr));
247+
bool is_mov_immed = instr_is_mov_constant(&instr, &immed);
248+
instr_free(dr_context, &instr);
249+
return is_mov_immed;
250+
}
251+
252+
bool
253+
is_stolen_reg_load_instr(void *dr_context, memref_t memref)
254+
{
255+
if (!type_is_instr(memref.instr.type))
256+
return false;
257+
app_pc pc = (app_pc)memref.instr.addr;
258+
instr_t instr;
259+
instr_init(dr_context, &instr);
260+
auto *ret = decode(dr_context, pc, &instr);
261+
assert(ret != NULL && instr_valid(&instr));
262+
bool res = instr_reads_memory(&instr) &&
263+
opnd_is_base_disp(instr_get_src(&instr, 0)) &&
264+
opnd_get_base(instr_get_src(&instr, 0)) == dr_get_stolen_reg() &&
265+
opnd_get_index(instr_get_src(&instr, 0)) == DR_REG_NULL;
266+
instr_free(dr_context, &instr);
267+
return res;
268+
}
269+
217270
int
218271
test_main(int argc, const char *argv[])
219272
{
@@ -237,6 +290,9 @@ test_main(int argc, const char *argv[])
237290
int dc_zva_instr_count = 0;
238291
int dc_zva_memref_count = 0;
239292
addr_t last_dc_zva_pc = 0;
293+
bool prev_instr_was_mov_sentinel = false;
294+
bool prev_instr_was_stolen_reg_load = false;
295+
bool found_stolen_reg_global_var = false;
240296
auto *stream = scheduler.get_stream(0);
241297
memref_t memref;
242298
for (scheduler_t::stream_status_t status = stream->next_record(memref);
@@ -257,6 +313,19 @@ test_main(int argc, const char *argv[])
257313
last_dc_zva_pc = memref.instr.addr;
258314
continue;
259315
}
316+
if (type_is_instr(memref.instr.type)) {
317+
int64_t immed = 0;
318+
if (is_mov_immed_instr(dr_context, memref, immed) && immed == SENTINEL_MOV) {
319+
prev_instr_was_mov_sentinel = true;
320+
} else {
321+
if (prev_instr_was_mov_sentinel &&
322+
is_stolen_reg_load_instr(dr_context, memref)) {
323+
prev_instr_was_stolen_reg_load = true;
324+
} else
325+
prev_instr_was_stolen_reg_load = false;
326+
prev_instr_was_mov_sentinel = false;
327+
}
328+
}
260329
// Look for _memref_data_t entries.
261330
if ((memref.data.type == TRACE_TYPE_READ ||
262331
memref.data.type == TRACE_TYPE_WRITE || type_is_prefetch(memref.data.type))
@@ -267,6 +336,14 @@ test_main(int argc, const char *argv[])
267336
assert(ALIGNED(memref.data.addr, proc_get_cache_line_size()));
268337
assert(memref.data.size == proc_get_cache_line_size());
269338
}
339+
if (memref.data.type == TRACE_TYPE_READ && prev_instr_was_stolen_reg_load) {
340+
if (memref.data.addr == reinterpret_cast<addr_t>(&global_var[1])) {
341+
found_stolen_reg_global_var = true;
342+
} else {
343+
std::cerr << "Found stolen reg load of 0x" << std::hex << memref.data.addr
344+
<< " vs expected " << &global_var[1] << std::dec << "\n";
345+
}
346+
}
270347
}
271348
std::cerr << "DC ZVA count: " << dc_zva_instr_count << "\n";
272349
std::cerr << "DC ZVA memref count: " << dc_zva_memref_count << "\n";
@@ -275,6 +352,7 @@ test_main(int argc, const char *argv[])
275352
assert(dc_zva_instr_count == dc_zva_memref_count);
276353
assert(found_cache_line_size_marker);
277354
assert(found_page_size_marker);
355+
assert(found_stolen_reg_global_var);
278356
dr_standalone_exit();
279357

280358
return 0;

clients/drcachesim/tracer/instru_offline.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* **********************************************************
2-
* Copyright (c) 2016-2023 Google, Inc. All rights reserved.
2+
* Copyright (c) 2016-2025 Google, Inc. All rights reserved.
33
* **********************************************************/
44

55
/*
@@ -645,9 +645,10 @@ offline_instru_t::insert_save_addr(void *drcontext, instrlist_t *ilist, instr_t
645645
* store the base reg directly and add the disp during post-processing.
646646
*/
647647
reg_addr = opnd_get_base(ref);
648-
if (opnd_get_base(ref) == reg_ptr) {
649-
/* Here we do need a scratch reg, and raw2trace can't identify this case:
650-
* so we set disp to 0 and use the regular path below.
648+
if (opnd_get_base(ref) == reg_ptr || opnd_get_base(ref) == dr_get_stolen_reg()) {
649+
/* Here we do need a scratch reg, and raw2trace can't identify these cases:
650+
* so we set disp to 0 (since raw2trace will add it on) and use the
651+
* regular path below.
651652
*/
652653
opnd_set_disp(&ref, 0);
653654
} else

suite/tests/client-interface/stolen-reg-index.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* **********************************************************
2-
* Copyright (c) 2023 Google, Inc. All rights reserved.
2+
* Copyright (c) 2023-2025 Google, Inc. All rights reserved.
33
* Copyright (c) 2022 Arm Limited All rights reserved.
44
* **********************************************************/
55

@@ -44,6 +44,8 @@
4444
/* In asm code. */
4545
void
4646
indexed_mem_test(int *val);
47+
void
48+
base_mem_test(int *val);
4749

4850
int
4951
main(int argc, char *argv[])
@@ -56,6 +58,14 @@ main(int argc, char *argv[])
5658
print("indexed_mem_test() passed.\n");
5759

5860
print("Tested the use of stolen register as memory index register.\n");
61+
62+
base_mem_test(&value);
63+
if (value != 43)
64+
print("base_mem_test() failed with %d, expected 43.\n", value);
65+
else
66+
print("base_mem_test() passed.\n");
67+
print("Tested the use of stolen register as memory base register.\n");
68+
5969
return 0;
6070
}
6171

@@ -84,6 +94,27 @@ GLOBAL_LABEL(FUNCNAME:)
8494
END_FUNC(FUNCNAME)
8595
# undef FUNCNAME
8696

97+
98+
#define FUNCNAME base_mem_test
99+
DECLARE_EXPORTED_FUNC(FUNCNAME)
100+
GLOBAL_LABEL(FUNCNAME:)
101+
102+
stp x0, x1, [sp, #-16]!
103+
104+
/* Load passed in value using index register X28, then increment. */
105+
mov x28, x0
106+
ldr x1, [x28]
107+
add x1, x1, #1
108+
109+
/* Store incremented value using index register W28. */
110+
str x1, [x28]
111+
112+
ldp x0, x1, [sp], #16
113+
ret
114+
115+
END_FUNC(FUNCNAME)
116+
# undef FUNCNAME
117+
87118
END_FILE
88119
/* clang-format on */
89120
#endif /* ASM_CODE_ONLY */

suite/tests/client-interface/stolen-reg-index.dll.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* **********************************************************
2+
* Copyright (c) 2025 Google, Inc. All rights reserved.
23
* Copyright (c) 2022 Arm Limited All rights reserved.
34
* **********************************************************/
45

@@ -89,12 +90,14 @@ insert_get_addr(void *drcontext, instrlist_t *ilist, instr_t *instr, opnd_t mref
8990
}
9091

9192
/* Look for the precise stolen register cases in the test app. */
92-
if (opnd_get_base(mref) == DR_REG_X0 &&
93-
(opnd_get_index(mref) == dr_get_stolen_reg() ||
94-
opnd_get_index(mref) == DR_REG_W28 && opnd_get_base(mref) == DR_REG_X0)) {
93+
if (opnd_get_base(mref) == dr_get_stolen_reg() ||
94+
(opnd_get_base(mref) == DR_REG_X0 &&
95+
reg_to_pointer_sized(opnd_get_index(mref)) == dr_get_stolen_reg())) {
9596
/* Call out to confirm we got the right address.
9697
* DR's clean call args only support pointer-sized so we
97-
* deconstruct the opnd_t.
98+
* deconstruct the opnd_t, which works if it fits in our 2 args
99+
* as we pass it to ourselves so we know the layout is identical
100+
* for the reading code.
98101
*/
99102
DR_ASSERT(sizeof(mref) <= 2 * sizeof(ptr_uint_t));
100103
ptr_uint_t opnd_top = *(((ptr_uint_t *)&mref) + 1);
@@ -143,6 +146,13 @@ event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst
143146
if (opnd_get_index(opnd) == DR_REG_W28 && opnd_get_base(opnd) == DR_REG_X0 &&
144147
prev_is_mov_0(inst, DR_REG_W28))
145148
dr_printf("store memref with index reg W28\n");
149+
if (insert_get_addr(drcontext, bb, inst, opnd))
150+
return DR_EMIT_DEFAULT;
151+
else
152+
DR_ASSERT(false);
153+
} else if (opnd_is_memory_reference(opnd) && opnd_is_base_disp(opnd) &&
154+
opnd_get_base(opnd) == dr_get_stolen_reg()) {
155+
/* Go ahead and translate all all base-stolen addresses. */
146156
if (insert_get_addr(drcontext, bb, inst, instr_get_dst(inst, 0)))
147157
return DR_EMIT_DEFAULT;
148158
else
@@ -162,7 +172,14 @@ event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst
162172
if (opnd_get_index(opnd) == DR_REG_W28 && opnd_get_base(opnd) == DR_REG_X0 &&
163173
prev_is_mov_0(inst, DR_REG_W28))
164174
dr_printf("load memref with index reg W28\n");
165-
if (insert_get_addr(drcontext, bb, inst, instr_get_src(inst, 0)))
175+
if (insert_get_addr(drcontext, bb, inst, opnd))
176+
return DR_EMIT_DEFAULT;
177+
else
178+
DR_ASSERT(false);
179+
} else if (opnd_is_memory_reference(opnd) && opnd_is_base_disp(opnd) &&
180+
opnd_get_base(opnd) == dr_get_stolen_reg()) {
181+
/* Go ahead and translate all all base-stolen addresses. */
182+
if (insert_get_addr(drcontext, bb, inst, opnd))
166183
return DR_EMIT_DEFAULT;
167184
else
168185
DR_ASSERT(false);

suite/tests/client-interface/stolen-reg-index.expect

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ load memref with index reg X28
22
store memref with index reg W28
33
indexed_mem_test() passed.
44
Tested the use of stolen register as memory index register.
5+
base_mem_test() passed.
6+
Tested the use of stolen register as memory base register.

0 commit comments

Comments
 (0)