|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
/ ?( o3 l z* X& I7 D' C$ p对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure Call Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是 汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回( y, `4 m6 c- a: I' |2 {! Q, L
0 U1 Y! e$ b( F e5 X
不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。
( E* Z- @ r3 j- R
+ H2 O' p- E/ ~+ S我们先讨论一下形参个数为4的情况.4 Q4 T2 @3 m7 [
实例1:7 T$ l( Q) L/ d: E. K; B, J. u, R/ {
test_asm_args.asm# u/ M+ [: I* \; x0 N1 s
//--------------------------------------------------------------------------------9 r8 R1 u4 I3 \6 a) Z3 w
IMPORT test_c_args ;声明test_c_args函数. Q2 x) K0 i# G \7 d' s
AREA TEST_ASM, CODE, READONLY
- O, X% n# p! H# D }/ H EXPORT test_asm_args
" D7 V r, _1 N9 ^ B8 C+ N6 ktest_asm_args8 e: ~" o- ^; I' E3 l, o
STR lr, [sp, #-4]! ;保存当前lr* \ x2 e) f3 x) t9 k
ldr r0,=0x10 ;参数 1
. k; Y% z8 \0 u0 Y( J# l ldr r1,=0x20 ;参数 29 f+ U# h6 I! ^7 U& c7 f
ldr r2,=0x30 ;参数 3, w. p' a8 Y8 g5 V$ Y4 ^. C
ldr r3,=0x40 ;参数 4
) m9 T6 U$ C8 h! ^1 a: B bl test_c_args ;调用C函数% ?# P' P1 k5 z# A' E/ J4 Y
LDR pc, [sp], #4 ;将lr装进pc(返回main函数)
- r( f: u w! j: j1 m END
+ R8 n: v, T& n! I3 ]5 d( ntest_c_args.c
- H/ k% Y3 ~' n$ y2 @9 g//--------------------------------------------------------------------------------
3 y7 B: x+ K( k' G& ]0 |4 J# t' Cvoid test_c_args(int a,int b,int c,int d)* `. G$ ^3 `7 \1 K2 F
{; L/ f2 Q4 v+ T3 y
printk("test_c_args:/n");
# G9 S: Z) U5 P& s: I9 L: Z printk("%0x %0x %0x %0x/n",a,b,c,d);+ K# u o9 a: N/ Z' h0 y% c7 F
}
9 e& }7 ]- ~/ m" P, Y0 l* Umain.c
0 G5 ?6 l% O: p S `//--------------------------------------------------------------------------------
# S$ l- ?4 h$ v( dint main()) [, N! n& C% o) ]
{
1 w/ W& ]7 u% x5 r; y0 m6 Q, T test_asm_args();
0 ]! C' Y2 j" V! y' X6 v: d6 F& A for(;;);
, N. E2 r- }, c8 J( u5 `" P}% K9 ?5 J0 x9 ?: \) E
- w+ C9 {$ r9 F) e3 S5 S程序从main函数开始执行,main调用了test_asm_args,test_asm_args调用了test_c_args,最后从test_asm_args返回main.# A7 \5 u0 v. w4 R8 ~% Q
代码分别使用了汇编和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函数中。
+ M% i% b- C. J( {0 v( ^5 [$ X2 ?) E
2 n) J3 g4 T/ }2 k
如果test_c_args的参数是8个呢?这种情况test_asm_args应该怎样传递参数呢?, z4 [/ N9 n, Z
实例2:
% c9 G* B: [& J' f5 ~' |) Btest_asm_args.asm
+ V4 P+ c4 F$ O: _/ ?7 q% W7 F5 b& l//--------------------------------------------------------------------------------
; b2 p, @; h8 J/ E IMPORT test_c_args ;声明test_c_args函数/ H5 Q0 L; x/ h
AREA TEST_ASM, CODE, READONLY* a+ [6 K1 K. F9 Z* Z$ m8 ~: }
EXPORT test_asm_args
& t9 P8 M D* Y/ j+ t! E e' stest_asm_args
9 y, _6 I+ V' T- Q( S0 B STR lr, [sp, #-4]! ;保存当前lr
0 h; c( f0 M& K7 Z8 u ldr r0,=0x1 ;参数 1
: h; _$ m: s: |- h ldr r1,=0x2 ;参数 2
3 i+ I* P. z- b f0 `7 g. x, c. d ldr r2,=0x3 ;参数 3" N3 _0 i( }8 `$ }- a& ^& q
ldr r3,=0x4 ;参数 4
4 B% H" r& V: N7 c# {$ V+ D+ D ldr r4,=0x8
5 B. C& T& t0 y. t4 ?& z+ S5 E str r4,[sp,#-4]! ;参数 8 入栈
4 ~$ l8 H. |7 m' S1 d* f9 O$ | ldr r4,=0x7
+ _ f: C5 N9 F" n str r4,[sp,#-4]! ;参数 7 入栈
; C) S5 ^; v2 p# @9 z ldr r4,=0x6
5 t1 |( _! T9 L% s str r4,[sp,#-4]! ;参数 6 入栈
8 q* Y0 ]7 _4 {/ y+ X7 j& z2 O ldr r4,=0x58 t: O- m. C# O! F+ J8 X0 }$ w
str r4,[sp,#-4]! ;参数 5 入栈( B( e) n b/ ^% R" P
bl test_c_args_lots
4 @- X3 N6 J. u* F ADD sp, sp, #4 ;清除栈中参数 5,本语句执行完后sp指向 参数6
) P3 b8 @8 Z+ o9 }! t( v1 U ADD sp, sp, #4 ;清除栈中参数 6,本语句执行完后sp指向 参数7, e2 u t% k+ F( p3 T. K
ADD sp, sp, #4 ;清除栈中参数 7,本语句执行完后sp指向 参数8
" R6 I% V& ^1 \! m; j, g: a) x ADD sp, sp, #4 ;清除栈中参数 8,本语句执行完后sp指向 lr7 w, Y9 b2 g- b
LDR pc, [sp],#4 ;将lr装进pc(返回main函数) 9 Q* n; n3 ]7 H) @* J$ }
END7 l! Z: a6 n3 ^; f9 W' T
test_c_args.c1 e" }! J$ K# h0 {* n5 ~, l
//--------------------------------------------------------------------------------
/ W" Z- h& p t* ?; Ivoid test_c_args(int a,int b,int c,int d,int e,int f,int g,int h)
0 Y2 H8 P, h. V8 H# c$ Q{
7 C/ g; T$ F: P' v$ \ printk("test_c_args_lots:/n");4 H, J0 h3 t0 R- z+ R
printk("%0x %0x %0x %0x %0x %0x %0x %0x/n",; h8 Q* b+ e: q" M3 G# U2 j
a,b,c,d,e,f,g,h);2 [" O* B% D: y1 F
}4 _! }' C. E) @; j
main.c: j; w/ {4 }0 s# @: a7 Q
//--------------------------------------------------------------------------------% x0 Q: O# t M) @# g- r
int main(), _- v( t) B) H/ ?6 U$ c
{
2 t% c; b/ h+ {" W$ Q test_asm_args();9 O% o; @1 Z) p* r' R, [# H) q
for(;;);
7 D$ [* k- W4 m+ Y6 P6 n# s: V}
9 @% v4 p% f/ N( n* w
7 @& ~5 w: ^7 F3 g* W# j1 p/ j这部分的代码和实例1的代码大部分是相同的,不同的地方是test_c_args的参数个数和test_asm_args的参数传递方式。" F0 m% ]" \) T# x4 J
在test_asm_args中,参数1~参数4还是通过R0~R3进行传递,而参数5~参数8则是通过把其压入堆栈的方式进行传递,不过要注意这四个入栈参数的入栈顺序,是以参数8->参数7->参数6->参数5的顺序入栈的。+ j% \6 w' {, C2 }$ I
直到调用test_c_args之前,堆栈内容如下:$ R2 s' @* _" m" T# n) d
sp->+----------+
9 l- u+ \. i! @$ _6 L; N! C | 参数5 |
! b7 b v0 u" a, F +----------+' O. l$ O. X. _" _5 D. t) J
| 参数6 |5 j) R& Q8 n; H
+----------+
$ m |" L* u+ u* _ | 参数7 |7 T2 R! P( t! V* O3 ]* @3 h; C
+----------+
; x* ]' E) h2 u( ]2 y" Y | 参数8 |5 p& N( d1 C7 ?- O; Y
+----------+1 k* x& T0 w _; Z+ U) r
| lr |4 w& N: g0 g( `: }4 y/ d2 H
+----------+( i* T! P, R2 B* Y1 J) @2 ]7 X
test_c_args执行返回后,则设置sp,对之前入栈的参数进行清除,最后将lr装入pc返回main函数,在执行 LDR pc, [sp],#4 指令之前堆栈内容如下:
2 N1 C* [4 l4 q3 Q7 ~9 x% W) l2 } +----------+' k* W' U( D2 ?
| 参数5 |7 Y1 g& p$ R" Z8 M1 _* O+ O
+----------+& ~) `! p: r6 J4 P5 ?5 ]! R
| 参数6 |
, S. V- ^# `- G9 W +----------+
0 }& o: e: {8 j t( s | 参数7 |9 R! u& m% n, t: ^3 g2 D. ^" x
+----------+
8 n! N' @% j+ R- t: v | 参数8 |
( ^3 G/ x- r% Y, \, O7 jsp->+----------++ N1 ~" }5 |' u/ w! A$ ]3 ^/ ^% q O" R
| lr |
8 {+ U) w- j% R( t' j- h +----------+ |
|