Skip to content

Commit 7ab0dfa

Browse files
[lldb] Track CFA pointer metadata in StackID
In this commit: 9c8e716 [lldb] Make StackID call Fix{Code,Data} pointers (#152796) We made StackID keep track of the CFA without any pointer metadata in it. This is necessary when comparing two StackIDs to determine which one is "younger". However, the CFA inside StackIDs is also used in other contexts through the method StackID::GetCallFrameAddress. One notable case is DWARFExpression: the computation of `DW_OP_call_frame_address` is done using StackID. This feeds into many other places, e.g. expression evaluation may require the address of a variable that is computed from the CFA; to access the variable without faulting, we may need to preserve the pointer metadata. As such, StackID must be able to provide both versions of the CFA. In the spirit of allowing consumers of pointers to decide what to do with pointer metadata, this patch changes StackID to store both versions of the cfa pointer. Two getter methods are provided, and all call sites except DWARFExpression preserve their existing behavior (stripped pointer). Other alternatives were considered: * Just store the raw pointer. This would require changing the comparisong operator `<` to also receive a Process, as the comparison requires stripped pointers. It wasn't clear if all call-sites had a non-null process, whereas we know we have a process when creating a StackID. * Store a weak pointer to the process inside the class, and then strip metadata as needed. This would require a `weak_ptr::lock` in many operations of LLDB, and it felt wasteful. It also prevents stripping of the pointer if the process has gone away. This patch also changes RegisterContextUnwind::ReadFrameAddress, which is the method computing the CFA fed into StackID, to also preserve the signature pointers.
1 parent 990fe80 commit 7ab0dfa

File tree

9 files changed

+281
-17
lines changed

9 files changed

+281
-17
lines changed

lldb/include/lldb/Target/StackID.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ class StackID {
2626

2727
lldb::addr_t GetPC() const { return m_pc; }
2828

29-
lldb::addr_t GetCallFrameAddress() const { return m_cfa; }
29+
lldb::addr_t GetCallFrameAddressWithMetadata() const {
30+
return m_cfa_with_metadata;
31+
}
32+
33+
lldb::addr_t GetCallFrameAddressWithoutMetadata() const { return m_cfa; }
3034

3135
SymbolContextScope *GetSymbolContextScope() const { return m_symbol_scope; }
3236

@@ -59,9 +63,12 @@ class StackID {
5963

6064
/// The call frame address (stack pointer) value at the beginning of the
6165
/// function that uniquely identifies this frame (along with m_symbol_scope
62-
/// below)
66+
/// below).
6367
lldb::addr_t m_cfa = LLDB_INVALID_ADDRESS;
6468

69+
/// The cfa with metadata (i.e. prior to Process::FixAddress).
70+
lldb::addr_t m_cfa_with_metadata = LLDB_INVALID_ADDRESS;
71+
6572
/// If nullptr, there is no block or symbol for this frame. If not nullptr,
6673
/// this will either be the scope for the lexical block for the frame, or the
6774
/// scope for the symbol. Symbol context scopes are always be unique pointers

lldb/source/API/SBFrame.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ lldb::addr_t SBFrame::GetCFA() const {
267267
}
268268

269269
if (StackFrame *frame = exe_ctx->GetFramePtr())
270-
return frame->GetStackID().GetCallFrameAddress();
270+
return frame->GetStackID().GetCallFrameAddressWithoutMetadata();
271271
return LLDB_INVALID_ADDRESS;
272272
}
273273

lldb/source/Expression/DWARFExpression.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2195,7 +2195,7 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
21952195
// Note that we don't have to parse FDEs because this DWARF expression
21962196
// is commonly evaluated with a valid stack frame.
21972197
StackID id = frame->GetStackID();
2198-
addr_t cfa = id.GetCallFrameAddress();
2198+
addr_t cfa = id.GetCallFrameAddressWithMetadata();
21992199
if (cfa != LLDB_INVALID_ADDRESS) {
22002200
stack.push_back(Scalar(cfa));
22012201
stack.back().SetValueType(Value::ValueType::LoadAddress);

lldb/source/Target/RegisterContextUnwind.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,8 +1995,6 @@ bool RegisterContextUnwind::ReadFrameAddress(
19951995
reg_info, cfa_reg_contents, reg_info->byte_size, reg_value);
19961996
if (error.Success()) {
19971997
address = reg_value.GetAsUInt64();
1998-
if (abi_sp)
1999-
address = abi_sp->FixCodeAddress(address);
20001998
UnwindLogMsg(
20011999
"CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64
20022000
", CFA value is 0x%" PRIx64,
@@ -2018,8 +2016,6 @@ bool RegisterContextUnwind::ReadFrameAddress(
20182016
RegisterNumber cfa_reg(m_thread, row_register_kind,
20192017
fa.GetRegisterNumber());
20202018
if (ReadGPRValue(cfa_reg, cfa_reg_contents)) {
2021-
if (abi_sp)
2022-
cfa_reg_contents = abi_sp->FixDataAddress(cfa_reg_contents);
20232019
if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 ||
20242020
cfa_reg_contents == 1) {
20252021
UnwindLogMsg(
@@ -2056,9 +2052,6 @@ bool RegisterContextUnwind::ReadFrameAddress(
20562052
dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr);
20572053
if (result) {
20582054
address = result->GetScalar().ULongLong();
2059-
if (ABISP abi_sp = m_thread.GetProcess()->GetABI())
2060-
address = abi_sp->FixCodeAddress(address);
2061-
20622055
UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64,
20632056
address);
20642057
return true;
@@ -2099,7 +2092,6 @@ bool RegisterContextUnwind::ReadFrameAddress(
20992092
}
21002093
case UnwindPlan::Row::FAValue::isConstant: {
21012094
address = fa.GetConstant();
2102-
address = m_thread.GetProcess()->FixDataAddress(address);
21032095
UnwindLogMsg("CFA value set by constant is 0x%" PRIx64, address);
21042096
return true;
21052097
}

lldb/source/Target/StackFrameList.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx,
449449
}
450450
} else {
451451
unwind_frame_sp = m_frames.front();
452-
cfa = unwind_frame_sp->m_id.GetCallFrameAddress();
452+
cfa = unwind_frame_sp->m_id.GetCallFrameAddressWithoutMetadata();
453453
}
454454
} else {
455455
// Check for interruption when building the frames.

lldb/source/Target/StackID.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ using namespace lldb_private;
1717

1818
StackID::StackID(lldb::addr_t pc, lldb::addr_t cfa,
1919
SymbolContextScope *symbol_scope, Process *process)
20-
: m_pc(pc), m_cfa(cfa), m_symbol_scope(symbol_scope) {
20+
: m_pc(pc), m_cfa(cfa), m_cfa_with_metadata(cfa),
21+
m_symbol_scope(symbol_scope) {
2122
if (process) {
2223
m_pc = process->FixCodeAddress(m_pc);
2324
m_cfa = process->FixDataAddress(m_cfa);
@@ -29,6 +30,7 @@ void StackID::SetPC(lldb::addr_t pc, Process *process) {
2930
}
3031

3132
void StackID::SetCFA(lldb::addr_t cfa, Process *process) {
33+
m_cfa_with_metadata = cfa;
3234
m_cfa = process ? process->FixDataAddress(cfa) : cfa;
3335
}
3436

@@ -49,7 +51,8 @@ void StackID::Dump(Stream *s) {
4951
}
5052

5153
bool lldb_private::operator==(const StackID &lhs, const StackID &rhs) {
52-
if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress())
54+
if (lhs.GetCallFrameAddressWithoutMetadata() !=
55+
rhs.GetCallFrameAddressWithoutMetadata())
5356
return false;
5457

5558
SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
@@ -67,8 +70,8 @@ bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) {
6770
}
6871

6972
bool lldb_private::operator<(const StackID &lhs, const StackID &rhs) {
70-
const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddress();
71-
const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddress();
73+
const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddressWithoutMetadata();
74+
const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddressWithoutMetadata();
7275

7376
// FIXME: We are assuming that the stacks grow downward in memory. That's not
7477
// necessary, but true on
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ASM_SOURCES := main.s
2+
3+
# This is to appease Makefile.rules, there is no main.c
4+
C_SOURCES := main.c
5+
6+
ASM_OBJS := $(ASM_SOURCES:.s=.o)
7+
8+
%.o: %.s
9+
$(CC) -c -x assembler $< -o $@
10+
11+
include Makefile.rules
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
7+
@skipUnlessDarwin
8+
@skipIf(archs=no_match(["arm64"]))
9+
class TestArmPointerMetadataStripping(TestBase):
10+
def test(self):
11+
self.build()
12+
target, process, thread, bkpt = lldbutil.run_to_name_breakpoint(self, "foo")
13+
14+
# Step over the first two instructions of foo in order to
15+
# toggle the bit of fp and save it on the stack:
16+
# orr x29, x29, #0x1000000000000000
17+
# stp x29, x30, [sp, #-16]!
18+
thread.StepInstruction(False)
19+
thread.StepInstruction(False)
20+
21+
argv_addr = thread.frames[1].GetValueForVariablePath("&argv");
22+
self.assertTrue(argv_addr.IsValid())
23+
24+
argv_addr_uint = argv_addr.GetValueAsUnsigned()
25+
self.assertNotEqual((argv_addr_uint & (1 << 60)), 0)
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
; The assembly below corresponds to this program:
2+
; __attribute__((nodebug))
3+
; int foo() {
4+
; return 10;
5+
; }
6+
; int main(int argc, char **argv) {
7+
; foo();
8+
; return 0;
9+
; }
10+
;
11+
; The assembly was edited in two places (search for "EDIT"):
12+
; 1. A "orr x29, x29, #0x1000000000000000" instruction was added in foo. This
13+
; effectively changes the CFA value of the frame above foo (i.e. main).
14+
; 2. In main, the DWARF location of `argv` was changed to DW_AT_call_frame_cfa.
15+
;
16+
; This allows us to stop in foo, go to frame 1 (main) and do `v &argv`,
17+
; obtaining the result of evaluating DW_AT_call_frame_cfa.
18+
19+
.section __TEXT,__text,regular,pure_instructions
20+
.globl _foo ; -- Begin function foo
21+
.p2align 2
22+
_foo: ; @foo
23+
Lfunc_begin0:
24+
.cfi_startproc
25+
orr x29, x29, #0x1000000000000000 ; EDIT: Set top byte of fp.
26+
stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill
27+
mov x29, sp
28+
.cfi_def_cfa w29, 16
29+
.cfi_offset w30, -8
30+
.cfi_offset w29, -16
31+
mov w0, #10 ; =0xa
32+
ldp x29, x30, [sp], #16 ; 16-byte Folded Reload
33+
ret
34+
Lfunc_end0:
35+
.cfi_endproc
36+
; -- End function
37+
.globl _main ; -- Begin function main
38+
.p2align 2
39+
_main: ; @main
40+
Lfunc_begin1:
41+
.file 1 "/test" "test.c"
42+
.loc 1 6 0 ; test.c:6:0
43+
.cfi_startproc
44+
sub sp, sp, #48
45+
stp x29, x30, [sp, #32] ; 16-byte Folded Spill
46+
add x29, sp, #32
47+
.cfi_def_cfa w29, 16
48+
.cfi_offset w30, -8
49+
.cfi_offset w29, -16
50+
mov w8, #0 ; =0x0
51+
str w8, [sp, #12] ; 4-byte Folded Spill
52+
stur wzr, [x29, #-4]
53+
stur w0, [x29, #-8]
54+
str x1, [sp, #16]
55+
Ltmp0:
56+
bl _foo
57+
ldr w0, [sp, #12] ; 4-byte Folded Reload
58+
ldp x29, x30, [sp, #32] ; 16-byte Folded Reload
59+
add sp, sp, #48
60+
ret
61+
Ltmp1:
62+
Lfunc_end1:
63+
.cfi_endproc
64+
; -- End function
65+
.section __DWARF,__debug_abbrev,regular,debug
66+
Lsection_abbrev:
67+
.byte 1 ; Abbreviation Code
68+
.byte 17 ; DW_TAG_compile_unit
69+
.byte 1 ; DW_CHILDREN_yes
70+
.byte 37 ; DW_AT_producer
71+
.byte 14 ; DW_FORM_strp
72+
.byte 19 ; DW_AT_language
73+
.byte 5 ; DW_FORM_data2
74+
.byte 3 ; DW_AT_name
75+
.byte 14 ; DW_FORM_strp
76+
.ascii "\202|" ; DW_AT_LLVM_sysroot
77+
.byte 14 ; DW_FORM_strp
78+
.ascii "\357\177" ; DW_AT_APPLE_sdk
79+
.byte 14 ; DW_FORM_strp
80+
.byte 16 ; DW_AT_stmt_list
81+
.byte 23 ; DW_FORM_sec_offset
82+
.byte 27 ; DW_AT_comp_dir
83+
.byte 14 ; DW_FORM_strp
84+
.byte 17 ; DW_AT_low_pc
85+
.byte 1 ; DW_FORM_addr
86+
.byte 18 ; DW_AT_high_pc
87+
.byte 6 ; DW_FORM_data4
88+
.byte 0 ; EOM(1)
89+
.byte 0 ; EOM(2)
90+
.byte 2 ; Abbreviation Code
91+
.byte 46 ; DW_TAG_subprogram
92+
.byte 1 ; DW_CHILDREN_yes
93+
.byte 17 ; DW_AT_low_pc
94+
.byte 1 ; DW_FORM_addr
95+
.byte 18 ; DW_AT_high_pc
96+
.byte 6 ; DW_FORM_data4
97+
.byte 64 ; DW_AT_frame_base
98+
.byte 24 ; DW_FORM_exprloc
99+
.byte 3 ; DW_AT_name
100+
.byte 14 ; DW_FORM_strp
101+
.byte 58 ; DW_AT_decl_file
102+
.byte 11 ; DW_FORM_data1
103+
.byte 59 ; DW_AT_decl_line
104+
.byte 11 ; DW_FORM_data1
105+
.byte 39 ; DW_AT_prototyped
106+
.byte 25 ; DW_FORM_flag_present
107+
.byte 73 ; DW_AT_type
108+
.byte 19 ; DW_FORM_ref4
109+
.byte 63 ; DW_AT_external
110+
.byte 25 ; DW_FORM_flag_present
111+
.byte 0 ; EOM(1)
112+
.byte 0 ; EOM(2)
113+
.byte 3 ; Abbreviation Code
114+
.byte 5 ; DW_TAG_formal_parameter
115+
.byte 0 ; DW_CHILDREN_no
116+
.byte 2 ; DW_AT_location
117+
.byte 24 ; DW_FORM_exprloc
118+
.byte 3 ; DW_AT_name
119+
.byte 14 ; DW_FORM_strp
120+
.byte 58 ; DW_AT_decl_file
121+
.byte 11 ; DW_FORM_data1
122+
.byte 59 ; DW_AT_decl_line
123+
.byte 11 ; DW_FORM_data1
124+
.byte 73 ; DW_AT_type
125+
.byte 19 ; DW_FORM_ref4
126+
.byte 0 ; EOM(1)
127+
.byte 0 ; EOM(2)
128+
.byte 4 ; Abbreviation Code
129+
.byte 36 ; DW_TAG_base_type
130+
.byte 0 ; DW_CHILDREN_no
131+
.byte 3 ; DW_AT_name
132+
.byte 14 ; DW_FORM_strp
133+
.byte 62 ; DW_AT_encoding
134+
.byte 11 ; DW_FORM_data1
135+
.byte 11 ; DW_AT_byte_size
136+
.byte 11 ; DW_FORM_data1
137+
.byte 0 ; EOM(1)
138+
.byte 0 ; EOM(2)
139+
.byte 5 ; Abbreviation Code
140+
.byte 15 ; DW_TAG_pointer_type
141+
.byte 0 ; DW_CHILDREN_no
142+
.byte 73 ; DW_AT_type
143+
.byte 19 ; DW_FORM_ref4
144+
.byte 0 ; EOM(1)
145+
.byte 0 ; EOM(2)
146+
.byte 0 ; EOM(3)
147+
.section __DWARF,__debug_info,regular,debug
148+
Lsection_info:
149+
Lcu_begin0:
150+
.set Lset0, Ldebug_info_end0-Ldebug_info_start0 ; Length of Unit
151+
.long Lset0
152+
Ldebug_info_start0:
153+
.short 4 ; DWARF version number
154+
.set Lset1, Lsection_abbrev-Lsection_abbrev ; Offset Into Abbrev. Section
155+
.long Lset1
156+
.byte 8 ; Address Size (in bytes)
157+
.byte 1 ; Abbrev [1] 0xb:0x76 DW_TAG_compile_unit
158+
.long 0 ; DW_AT_producer
159+
.short 12 ; DW_AT_language
160+
.long 47 ; DW_AT_name
161+
.long 54 ; DW_AT_LLVM_sysroot
162+
.long 165 ; DW_AT_APPLE_sdk
163+
.set Lset2, Lline_table_start0-Lsection_line ; DW_AT_stmt_list
164+
.long Lset2
165+
.long 180 ; DW_AT_comp_dir
166+
.quad Lfunc_begin1 ; DW_AT_low_pc
167+
.set Lset3, Lfunc_end1-Lfunc_begin1 ; DW_AT_high_pc
168+
.long Lset3
169+
.byte 2 ; Abbrev [2] 0x32:0x36 DW_TAG_subprogram
170+
.quad Lfunc_begin1 ; DW_AT_low_pc
171+
.set Lset4, Lfunc_end1-Lfunc_begin1 ; DW_AT_high_pc
172+
.long Lset4
173+
.byte 1 ; DW_AT_frame_base
174+
.byte 109
175+
.long 247 ; DW_AT_name
176+
.byte 1 ; DW_AT_decl_file
177+
.byte 6 ; DW_AT_decl_line
178+
; DW_AT_prototyped
179+
.long 107 ; DW_AT_type
180+
; DW_AT_external
181+
.byte 3 ; Abbrev [3] 0x4b:0xe DW_TAG_formal_parameter
182+
.byte 2 ; DW_AT_location
183+
.byte 145
184+
.byte 120
185+
.long 256 ; DW_AT_name
186+
.byte 1 ; DW_AT_decl_file
187+
.byte 6 ; DW_AT_decl_line
188+
.long 103 ; DW_AT_type
189+
.byte 3 ; Abbrev [3] 0x59:0xe DW_TAG_formal_parameter
190+
.byte 1 ; DW_AT_location
191+
.byte 0x9c ; EDIT: DW_AT_call_frame_cfa
192+
.long 261 ; DW_AT_name
193+
.byte 1 ; DW_AT_decl_file
194+
.byte 6 ; DW_AT_decl_line
195+
.long 110 ; DW_AT_type
196+
.byte 0 ; End Of Children Mark
197+
.byte 4 ; Abbrev [4] 0x68:0x7 DW_TAG_base_type
198+
.long 252 ; DW_AT_name
199+
.byte 5 ; DW_AT_encoding
200+
.byte 4 ; DW_AT_byte_size
201+
.byte 5 ; Abbrev [5] 0x6f:0x5 DW_TAG_pointer_type
202+
.long 115 ; DW_AT_type
203+
.byte 5 ; Abbrev [5] 0x74:0x5 DW_TAG_pointer_type
204+
.long 120 ; DW_AT_type
205+
.byte 4 ; Abbrev [4] 0x79:0x7 DW_TAG_base_type
206+
.long 266 ; DW_AT_name
207+
.byte 6 ; DW_AT_encoding
208+
.byte 1 ; DW_AT_byte_size
209+
.byte 0 ; End Of Children Mark
210+
Ldebug_info_end0:
211+
.section __DWARF,__debug_str,regular,debug
212+
Linfo_string:
213+
.asciz "Apple clang " ; string offset=0
214+
.asciz "test.c" ; string offset=47
215+
.asciz "/Applications/Xcode..........................................................................................." ; string offset=54
216+
.asciz ".............." ; string offset=165
217+
.asciz "......................................................../llvm_src1" ; string offset=180
218+
.asciz "main" ; string offset=247
219+
.asciz "int" ; string offset=252
220+
.asciz "argc" ; string offset=256
221+
.asciz "argv" ; string offset=261
222+
.asciz "char" ; string offset=266
223+
.subsections_via_symbols
224+
.section __DWARF,__debug_line,regular,debug
225+
Lsection_line:
226+
Lline_table_start0:

0 commit comments

Comments
 (0)