|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
( k/ l, r4 G' O
对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure Call Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是 汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回6 r, j: I% j6 I: t; C0 d0 b8 Y0 x
. Z& ]2 ^; |1 M
不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。
8 @5 U# [- J7 c! `9 Z* P. O: N) t1 U! W- c: W4 K
我们先讨论一下形参个数为4的情况., L! H" w' E, s. `- B* n% X
实例1:$ ]& v2 `- v* R& \$ w0 g c
test_asm_args.asm- n* T& O9 ]+ ?+ n# x
//--------------------------------------------------------------------------------" \4 Z1 `9 L4 M4 O% N" ` s
IMPORT test_c_args ;声明test_c_args函数( n( H- {) ], j! x
AREA TEST_ASM, CODE, READONLY
4 ^ q5 U2 q O$ V, x2 s( M- X EXPORT test_asm_args
4 o8 B: z- C. j* k( n( n4 Y# Ttest_asm_args
; ]4 @. x+ [: I9 H X- d' W STR lr, [sp, #-4]! ;保存当前lr0 p+ F% q& f2 g Z; m# F- ?
ldr r0,=0x10 ;参数 1
}/ g( n, L! i m$ x/ C ldr r1,=0x20 ;参数 2* T5 W7 [% n+ ~$ }3 \' C* E
ldr r2,=0x30 ;参数 3+ @, V' ^- d0 O9 T2 _
ldr r3,=0x40 ;参数 4
! E4 P$ U! n4 n4 O y bl test_c_args ;调用C函数: @8 y3 H7 `8 T# w0 u
LDR pc, [sp], #4 ;将lr装进pc(返回main函数)
, [% E) i# c8 u# ]& X END# S9 k! ]9 |) z1 E9 S8 K
test_c_args.c1 g6 U* D0 ?5 u7 E6 ?& e& }
//--------------------------------------------------------------------------------
0 C% s' y# p2 e; U+ @$ Mvoid test_c_args(int a,int b,int c,int d)
; U( Y0 p/ N ?& h. k+ g c; ]{/ a# H. Y6 G* j; U( U) z% K' B) r
printk("test_c_args:/n");
N( Y. Y; r2 I printk("%0x %0x %0x %0x/n",a,b,c,d);/ t) v5 w( O' l. k/ K
}
7 u# x ]0 G" M: t A L: r, |6 tmain.c
" o. N9 J2 {8 I, v; k" d1 B' B//--------------------------------------------------------------------------------$ x$ U8 t5 n, F- ~
int main()
) C8 A! `) ?' a9 a7 `% t$ D: R{
, `2 [8 g+ s- u$ U& d, ` test_asm_args();- b! T* d) X3 w0 \& {* G
for(;;);
5 i7 }4 E* }* ?- s}
- X3 U1 U. `: [0 t: h
' p; P3 `7 ]0 S程序从main函数开始执行,main调用了test_asm_args,test_asm_args调用了test_c_args,最后从test_asm_args返回main.
! F! ^/ _0 @) |& L& K代码分别使用了汇编和C定义了两个函数,test_asm_args 和 test_c_args,test_asm_args调用了test_c_args,其参数的传递方式就是向R0~R3分别写入参数值,之后使用bl语句对test_c_args进行调用。其中值得注意的地方是用红色标记的语句,test_asm_args在调用test_c_args之前必须把当前的lr入栈,调用完test_c_args之后再把刚才保存在栈中的lr写回pc,这样才能返回到main函数中。- [; Q7 f0 ?, O2 x
. [2 C( ?' x w& a( b- Z
- C; S1 Y i6 u4 v- D4 g7 N( v如果test_c_args的参数是8个呢?这种情况test_asm_args应该怎样传递参数呢?
: O: D* I& S5 E8 s: j+ O* e实例2:) l* y: K; W8 y0 Y% z
test_asm_args.asm e5 }2 c: L; |2 i. Y1 c
//--------------------------------------------------------------------------------
|! ]0 c9 o4 {& |+ @, Z IMPORT test_c_args ;声明test_c_args函数$ U+ e* x S8 b/ S* u
AREA TEST_ASM, CODE, READONLY
# O. p# N) q# Z5 U EXPORT test_asm_args
6 U+ V ~- ? n' q1 M" Y- W5 Ttest_asm_args
* }+ [% n& b* Z# e) v" v STR lr, [sp, #-4]! ;保存当前lr; r: p4 Z N2 ^0 q/ J+ `* {
ldr r0,=0x1 ;参数 10 i( U4 m. T$ p% h( K# g5 @
ldr r1,=0x2 ;参数 2
# W; u9 q( z1 h3 x7 O) l( ? ldr r2,=0x3 ;参数 3
/ s, L- y' x9 L6 `# S0 \ ldr r3,=0x4 ;参数 4
; R* W- E4 w: a0 R4 L$ R ldr r4,=0x8
/ W0 I8 o4 E, o0 i3 t3 [ str r4,[sp,#-4]! ;参数 8 入栈
/ T! _2 I6 S6 x4 y1 _ ldr r4,=0x7+ v7 R0 K- o0 x& T4 E
str r4,[sp,#-4]! ;参数 7 入栈
. T8 B/ @ K0 D) `- e1 A ldr r4,=0x6
' y& N- S# O3 q+ Z8 u1 f str r4,[sp,#-4]! ;参数 6 入栈; W- i% R, I8 A- W
ldr r4,=0x5( n7 r% N2 v1 {$ x. C+ z. j
str r4,[sp,#-4]! ;参数 5 入栈
8 ^) Q: Q7 A& B bl test_c_args_lots
3 u4 T% ]7 T( P6 g* }) h3 Q ADD sp, sp, #4 ;清除栈中参数 5,本语句执行完后sp指向 参数6
; B/ Y% |9 k+ p. S1 { ADD sp, sp, #4 ;清除栈中参数 6,本语句执行完后sp指向 参数7
+ _& }& m% s3 N5 C7 f0 Y( I ADD sp, sp, #4 ;清除栈中参数 7,本语句执行完后sp指向 参数8, ~6 S! J- Y* O$ H/ j) `
ADD sp, sp, #4 ;清除栈中参数 8,本语句执行完后sp指向 lr; J% A8 @* D- H8 p* Q9 u* Q2 e
LDR pc, [sp],#4 ;将lr装进pc(返回main函数)
z% }# }, L5 W f/ l END
. Q8 [, }3 k o# Btest_c_args.c+ @# T1 |. l. _% q
//--------------------------------------------------------------------------------- ?. @3 \4 Q5 [2 H" Q# `" f
void test_c_args(int a,int b,int c,int d,int e,int f,int g,int h)8 t0 Z$ K$ u; U! M
{
2 z& C) I" V/ b' G @$ s printk("test_c_args_lots:/n");
; V8 X6 }$ O( x+ `- T/ ?3 C1 R) Q printk("%0x %0x %0x %0x %0x %0x %0x %0x/n"," H5 O4 r9 n9 p4 c- H
a,b,c,d,e,f,g,h);
2 i0 S+ A+ b x/ y8 _}* ^# t3 C% O) {- I: a& [0 U& b. E& Q
main.c
3 V- j. `% m4 P- M: s//--------------------------------------------------------------------------------9 S: \0 E t7 \% _8 d2 x" V( d2 f
int main(). D" @5 I* h$ z: z3 F
{8 V! l1 j3 w1 z3 P; R9 _. c
test_asm_args();% ]" k, u6 O/ c0 q" X
for(;;);
) P6 H2 x; ]' R}' I. W! {3 l( P, {" Z: _
0 `7 m7 F* k9 n8 q1 | q2 }
这部分的代码和实例1的代码大部分是相同的,不同的地方是test_c_args的参数个数和test_asm_args的参数传递方式。
( x0 U! ~$ N) x8 V( A在test_asm_args中,参数1~参数4还是通过R0~R3进行传递,而参数5~参数8则是通过把其压入堆栈的方式进行传递,不过要注意这四个入栈参数的入栈顺序,是以参数8->参数7->参数6->参数5的顺序入栈的。( z8 u, \5 \6 H9 x+ T) E
直到调用test_c_args之前,堆栈内容如下:
* [/ { J: h5 k1 k) P* Osp->+----------+4 f2 L! b9 H5 Q- T3 X( J" |
| 参数5 |5 s4 w! y$ b7 ~. P! E9 o! b5 Y0 D3 l
+----------+
4 r3 o- h" I' H. R$ ?7 q7 l. a | 参数6 |
! j5 _+ R1 F* d$ P4 ]/ ^: \& ] +----------+
5 ^% o8 }7 v; f+ B$ Q" m( z | 参数7 |
- K. n& B/ S t& X8 h +----------+
, U) v; I S0 {# L6 ^' ? | 参数8 |0 p' Z$ m3 {2 W1 E% u3 N- z' I
+----------+
! N# O; o r" b0 F( q% X/ P | lr |
* p/ n! C" x; [3 h& \ +----------+7 h% O9 B3 G& v1 R( \ a& z" h
test_c_args执行返回后,则设置sp,对之前入栈的参数进行清除,最后将lr装入pc返回main函数,在执行 LDR pc, [sp],#4 指令之前堆栈内容如下:
) Z" l# ~8 j2 C( z +----------+
) q- `# P/ B+ @- U. {/ X | 参数5 |$ Q1 V8 ?! ?* [0 k/ ~. Y7 _& p
+----------+* B- x" c$ E8 v: k1 D
| 参数6 |! U- |/ r# W. [8 \$ p
+----------+
: F+ s8 W; H/ V4 {- V | 参数7 |
, s1 r; g, @& j +----------+, o! ^8 D, o+ n5 x5 K
| 参数8 |2 C! [: b$ W* U, h( b4 X. a
sp->+----------+
) U: D6 Z8 U1 U, m | lr |6 c6 N+ b* I3 L. n4 M5 F0 O* b" i
+----------+ |
|