Skip to content

Commit 5eca094

Browse files
committed
core/iwasm: Add proper CFI directives to ARM Thumb invokeNative for GDB debugging
This commit adds comprehensive Call Frame Information (CFI) directives to the ARM Thumb invokeNative assembly function to enable proper stack unwinding in GDB debugging sessions. Key changes: - Added .cfi_startproc and .cfi_endproc to define the function boundaries - Implemented .cfi_def_cfa directives to establish the canonical frame address - Added .cfi_offset directives for all callee-saved registers (lr, r7, r6, r5, r4) - Established proper frame pointer (r7) usage for reliable stack unwinding - Enhanced stack frame management with consistent alignment and cleanup The CFI directives allow GDB to accurately track register values and unwind the call stack when debugging native function invocations, significantly improving the debugging experience for WAMR applications on ARM Thumb platforms. Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
1 parent 4b42cfd commit 5eca094

1 file changed

Lines changed: 218 additions & 52 deletions

File tree

core/iwasm/common/arch/invokeNative_thumb.s

Lines changed: 218 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,83 +12,249 @@ invokeNative:
1212
.globl _invokeNative
1313
_invokeNative:
1414
#endif /* end of BH_PLATFORM_DARWIN */
15+
.syntax unified
16+
.cfi_startproc
1517

1618
/*
17-
* Arguments passed in:
19+
* invokeNative - Invoke a native function
1820
*
19-
* r0 function ptr
20-
* r1 argv
21-
* r2 argc
21+
* FUNCTION SIGNATURE:
22+
* uint32_t invokeNative(void *func_ptr, uint32_t *argv, uint32_t argc)
23+
*
24+
* INPUT PARAMETERS:
25+
* r0: func_ptr - Pointer to the native function to invoke
26+
* r1: argv - Array of arguments to pass to the function
27+
* r2: argc - Number of arguments in the argv array
28+
*
29+
* RETURN VALUE:
30+
* r0: Return value from the invoked native function
31+
*
32+
* CALLING CONVENTION:
33+
* - First 4 arguments (argv[0] to argv[3]) are passed in registers r0-r3
34+
* - Additional arguments are passed on the stack
35+
* - The first argument (argv[0]) is always the execution environment
36+
* - Return value is in r0
37+
*
38+
* STACK FRAME LAYOUT:
39+
* The function establishes a stack frame with the following layout:
40+
* [Higher addresses]
41+
* +------------------+
42+
* | return address | <- saved lr
43+
* +------------------+
44+
* | saved r7 | <- frame pointer
45+
* +------------------+
46+
* | saved r6 |
47+
* +------------------+
48+
* | saved r5 |
49+
* +------------------+
50+
* | saved r4 | <- r7 points here initially
51+
* +------------------+
52+
* | padding (4 bytes)| <- alignment padding
53+
* +------------------+
54+
* [Lower addresses]
55+
*
56+
* REGISTER USAGE:
57+
* - r0-r3: Function parameters and return value
58+
* - r4-r7: Callee-saved registers (r7 is frame pointer)
59+
* - r12 (ip): Temporary register for function pointer
60+
* - lr: Return address (callee-saved)
61+
* - sp: Stack pointer
2262
*/
2363

24-
push {r4, r5, r6, r7}
25-
push {lr}
26-
sub sp, sp, #4 /* make sp 8 byte aligned */
27-
mov ip, r0 /* ip = function ptr */
28-
mov r4, r1 /* r4 = argv */
29-
mov r5, r2 /* r5 = argc */
64+
/*
65+
* Save callee-saved registers and establish stack frame
66+
* Uses 20 bytes of stack space (5 registers × 4 bytes each)
67+
*/
68+
push {r4, r5, r6, r7, lr}
69+
.cfi_def_cfa sp, 0
70+
.cfi_adjust_cfa_offset 20
71+
72+
/*
73+
* Add 4-byte alignment padding to maintain 8-byte stack alignment
74+
* This is required by the ARM ABI for proper function calls
75+
*/
76+
sub sp, #4
77+
.cfi_adjust_cfa_offset 4
3078

31-
cmp r5, #1 /* at least one argument required: exec_env */
32-
blt return
79+
/*
80+
* Establish frame pointer (r7) for stack unwinding
81+
* CFA (Canonical Frame Address) = r7 + 24
82+
*/
83+
mov r7, sp
84+
.cfi_def_cfa r7, 24
85+
.cfi_offset lr, 4
86+
.cfi_offset r7, 8
87+
.cfi_offset r6, 12
88+
.cfi_offset r5, 16
89+
.cfi_offset r4, 20
3390

34-
mov r6, #0 /* increased stack size */
91+
/*
92+
* Save function parameters in preserved registers
93+
*/
94+
mov ip, r0
95+
mov r4, r1
96+
mov r5, r2
3597

36-
ldr r0, [r4] /* r0 = argv[0] = exec_env */
37-
add r4, r4, #4 /* r4 += 4 */
98+
/*
99+
* Check that at least one argument (exec_env) is provided
100+
*/
38101
cmp r5, #1
39-
beq call_func
102+
blt .Lreturn
40103

41-
ldr r1, [r4] /* r1 = argv[1] */
42-
add r4, r4, #4
104+
/*
105+
* Load first argument (argv[0]) into register
106+
* The first argument is always the execution environment, passed in r0
107+
*/
108+
ldr r0, [r4]
109+
adds r4, #4
110+
111+
/*
112+
* Check if we have exactly 1 argument
113+
* If so, proceed directly to function call
114+
*/
115+
cmp r5, #1
116+
beq .Lcall_func
117+
118+
/*
119+
* Load second argument (argv[1]) into register
120+
* If we have at least 2 arguments, load the second one into r1
121+
*/
122+
ldr r1, [r4]
123+
adds r4, #4
124+
125+
/*
126+
* Check if we have exactly 2 arguments
127+
* If so, proceed to function call
128+
*/
43129
cmp r5, #2
44-
beq call_func
130+
beq .Lcall_func
131+
132+
/*
133+
* Load third argument (argv[2]) into register
134+
* If we have at least 3 arguments, load the third one into r2
135+
*/
136+
ldr r2, [r4]
137+
adds r4, #4
45138

46-
ldr r2, [r4] /* r2 = argv[2] */
47-
add r4, r4, #4
139+
/*
140+
* Check if we have exactly 3 arguments
141+
* If so, proceed to function call
142+
*/
48143
cmp r5, #3
49-
beq call_func
144+
beq .Lcall_func
50145

51-
ldr r3, [r4] /* r3 = argv[3] */
52-
add r4, r4, #4
146+
/*
147+
* Load fourth argument (argv[3]) into register
148+
* If we have at least 4 arguments, load the fourth one into r3
149+
*/
150+
ldr r3, [r4]
151+
adds r4, #4
152+
153+
/*
154+
* Check if we have exactly 4 arguments
155+
* If so, proceed to function call (we've loaded all register args)
156+
*/
53157
cmp r5, #4
54-
beq call_func
158+
beq .Lcall_func
55159

56-
sub r5, r5, #4 /* argc -= 4, now we have r0 ~ r3 */
160+
/*
161+
* Handle arguments beyond the first four
162+
* We have more than 4 arguments, need to handle stack-based arguments
163+
*/
164+
subs r5, r5, #4
57165

58-
/* Ensure address is 8 byte aligned */
59-
lsl r6, r5, #2 /* r6 = argc * 4 */
60-
mov r7, #7
61-
add r6, r6, r7 /* r6 = (r6 + 7) & ~7 */
62-
bic r6, r6, r7
63-
add r6, r6, #4 /* +4 because odd(5) registers are in stack */
64-
mov r7, sp
65-
sub r7, r7, r6 /* reserved stack space for left arguments */
66-
mov sp, r7
166+
/*
167+
* Calculate stack space needed for remaining arguments
168+
*
169+
* Algorithm to round up to 8-byte multiple:
170+
* 1. Add 1 to round up odd numbers
171+
* 2. Clear the lowest bit with BIC to ensure even number
172+
* 3. Multiply by 4 to get byte count
173+
* 4. Add 4 for additional alignment/padding
174+
*
175+
* Formula: bytes = 8 * ceil(r5 / 2) + 4
176+
*
177+
* Example: r5=5 -> (5+1)&~1=6 -> 6*4=24 -> 24+4=28 bytes
178+
*/
179+
adds r6, r5, #1
180+
bic r6, r6, #1
181+
lsls r6, r6, #2
182+
adds r6, r6, #4
183+
184+
/*
185+
* Allocate stack space for variable arguments
186+
*
187+
* Calculate new stack pointer position:
188+
* new_sp = current_frame_pointer - stack_space_needed
189+
*
190+
* We use r7 (frame pointer) as reference to keep it stable
191+
*/
192+
subs r6, r7, r6
193+
mov sp, r6
194+
mov r6, sp
67195

68-
mov lr, r2 /* save r2 */
69-
loop_args: /* copy left arguments to stack */
196+
/*
197+
* Preserve register r2
198+
* r2 currently holds the third argument, save it in lr (already on stack)
199+
*/
200+
mov lr, r2
201+
202+
/*
203+
* Copy remaining arguments to stack
204+
* Loop through remaining arguments and push them onto the stack
205+
*/
206+
.Lloop_args:
70207
cmp r5, #0
71-
beq call_func1
208+
beq .Lrestore_r2
209+
72210
ldr r2, [r4]
73-
add r4, r4, #4
74-
str r2, [r7]
75-
add r7, r7, #4
76-
sub r5, r5, #1
77-
b loop_args
211+
adds r4, #4
212+
213+
str r2, [r6]
214+
adds r6, #4
78215

79-
call_func1:
80-
mov r2, lr /* restore r2 */
216+
subs r5, #1
217+
bne .Lloop_args
81218

82-
call_func:
219+
/*
220+
* Restore register r2
221+
* Retrieve the third argument that was saved before the loop
222+
*/
223+
.Lrestore_r2:
224+
mov r2, lr
225+
226+
/*
227+
* Invoke the target function
228+
* Call the native function using the prepared arguments
229+
*/
230+
.Lcall_func:
83231
blx ip
84-
add sp, sp, r6 /* restore sp */
85232

86-
return:
87-
add sp, sp, #4 /* make sp 8 byte aligned */
88-
pop {r3}
89-
pop {r4, r5, r6, r7}
90-
mov lr, r3
233+
/*
234+
* Function epilogue
235+
* Clean up stack frame and restore registers
236+
*/
237+
238+
/*
239+
* Restore stack pointer
240+
* Restore SP from frame pointer to clean up our stack frame
241+
*
242+
* Note: We use mov instead of add to avoid Thumb-2 specific instructions
243+
* and maintain compatibility with pure Thumb mode
244+
*/
245+
mov sp, r7
246+
247+
/*
248+
* Clean up stack frame
249+
* Remove alignment padding and restore saved registers
250+
*/
251+
.Lreturn:
252+
add sp, #4
253+
pop {r4, r5, r6, r7, lr}
91254
bx lr
255+
256+
.cfi_endproc
257+
92258
#if defined(__linux__) && defined(__ELF__)
93259
.section .note.GNU-stack,"",%progbits
94260
#endif

0 commit comments

Comments
 (0)