Skip to content

Commit f4562f6

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 <[email protected]>
1 parent 4b42cfd commit f4562f6

File tree

1 file changed

+215
-47
lines changed

1 file changed

+215
-47
lines changed

core/iwasm/common/arch/invokeNative_thumb.s

Lines changed: 215 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,83 +12,251 @@ 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

64+
/*
65+
* Save callee-saved registers and establish stack frame
66+
* Uses 20 bytes of stack space (5 registers × 4 bytes each)
67+
*/
2468
push {r4, r5, r6, r7}
2569
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 */
70+
.cfi_def_cfa sp, 0
71+
.cfi_adjust_cfa_offset 20
72+
73+
/*
74+
* Add 4-byte alignment padding to maintain 8-byte stack alignment
75+
* This is required by the ARM ABI for proper function calls
76+
*/
77+
sub sp, #4
78+
.cfi_adjust_cfa_offset 4
3079

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

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

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

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

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

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

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

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
167+
/*
168+
* Calculate stack space needed for remaining arguments
169+
*
170+
* Algorithm to round up to 8-byte multiple:
171+
* 1. Add 1 to round up odd numbers
172+
* 2. Divide by 2 and multiply by 8 to get byte count
173+
* 3. Add 4 for additional alignment/padding
174+
*
175+
* Formula: bytes = 8 * ceil(r5 / 2) + 4
176+
*
177+
* Example: r5=5 -> ceil(6/2)=3 -> 3*8=24 -> 24+4=28 bytes
178+
*/
179+
adds r6, r5, #1
180+
lsrs r6, r6, #1
181+
lsls r6, r6, #3
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 */
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
88253
pop {r3}
89254
pop {r4, r5, r6, r7}
90255
mov lr, r3
91256
bx lr
257+
258+
.cfi_endproc
259+
92260
#if defined(__linux__) && defined(__ELF__)
93261
.section .note.GNU-stack,"",%progbits
94262
#endif

0 commit comments

Comments
 (0)