@@ -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 th at 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 th at 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