EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
, U) b) }+ U8 \" d/ v( \$ ~ T对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure Call Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是 汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回9 O; |6 z8 ?, g- u
" ?# B! P0 k+ [+ p1 d+ w$ U9 {
不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。
" e- U7 W" f3 ?3 o; N
9 _/ R1 ]2 \$ Y# D2 b# c& U$ X我们先讨论一下形参个数为4的情况.
/ u6 P5 t. W( Q- E( I实例1:) p2 U- M; [/ k& F
test_asm_args.asm0 \: b" C7 M7 q& }& l1 \2 @5 M. C
//--------------------------------------------------------------------------------( v u' l& a3 z* e
IMPORT test_c_args ;声明test_c_args函数/ G# A" s& _8 k; C
AREA TEST_ASM, CODE, READONLY
: D& M6 M6 y3 `* t" b EXPORT test_asm_args% g) p- L! c2 J8 m6 ]
test_asm_args( G+ m% Y, s' B# a% E1 V" }
STR lr, [sp, #-4]! ;保存当前lr# |. [1 x# Y E* h* i( }
ldr r0,=0x10 ;参数 18 h* ^& r, s* j4 Z2 F2 P
ldr r1,=0x20 ;参数 2% J% I( |) Z- V* x& i
ldr r2,=0x30 ;参数 3* j) R3 H4 ` a. G4 W/ k$ R% y
ldr r3,=0x40 ;参数 4
! @' T: w, i5 ?4 \ bl test_c_args ;调用C函数- [. P( O& r. [1 f; K( @
LDR pc, [sp], #4 ;将lr装进pc(返回main函数) 8 L/ C" U% g: s
END% ~0 |2 C, L" f" b! O0 Z% n9 P
test_c_args.c
1 a A5 z" `# U& b) M" M//--------------------------------------------------------------------------------( d1 F X4 \- S" h) h# R3 ]& o- [5 C
void test_c_args(int a,int b,int c,int d)
' u- j* f+ d* p6 d{& \2 Z- F- B1 u% w
printk("test_c_args:/n");
" `1 R9 K9 M5 V' s1 z, e printk("%0x %0x %0x %0x/n",a,b,c,d);7 D4 ~4 `3 r- d, j
}4 W! t2 ^2 Q, q' z: y: ]8 A
main.c
9 d& n/ K+ Z! q" P o//--------------------------------------------------------------------------------
1 h& J: a4 N2 _$ q7 f! [0 eint main()
3 O( `# U% E7 u2 [4 q3 U( V. L{
* `4 O5 |/ f! C5 L2 I3 b test_asm_args();
6 |) E1 c3 N7 Q9 g for(;;);" I( g$ W5 O. \0 t
}
" p1 ]; C, L; {3 l0 ^
4 g7 x4 X' O J程序从main函数开始执行,main调用了test_asm_args,test_asm_args调用了test_c_args,最后从test_asm_args返回main." ^1 {5 `' [0 O8 h' 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函数中。% }6 \( Z$ L# g, ^
8 o" T" f" T7 e* d3 U" x, K; a+ m5 j, L/ c; K/ t; f2 d0 ?
如果test_c_args的参数是8个呢?这种情况test_asm_args应该怎样传递参数呢?' F0 J" v0 F$ a7 n2 S
实例2:
, q; h! b, v+ ktest_asm_args.asm
7 B# `8 B6 K0 j; n4 Q//--------------------------------------------------------------------------------3 d0 C( `9 q' o% T
IMPORT test_c_args ;声明test_c_args函数
! [, @$ A3 B+ X/ W& K" P AREA TEST_ASM, CODE, READONLY
. K# I/ x# v# r& o( S EXPORT test_asm_args
- ?/ Q7 ^ r: q+ k5 h7 ptest_asm_args+ C% V% E) W/ D8 K; J8 I
STR lr, [sp, #-4]! ;保存当前lr- J, Y, s. x9 D0 L
ldr r0,=0x1 ;参数 14 D, e/ @$ w' o, Y3 b6 O
ldr r1,=0x2 ;参数 2
0 p4 B9 A) z1 R" d ldr r2,=0x3 ;参数 3
7 [( l5 `( J- e" j- R; O; g# { ldr r3,=0x4 ;参数 4
: E5 T; d( l- ?9 T, n5 G6 T, X ldr r4,=0x8
! p% y% v# l: v str r4,[sp,#-4]! ;参数 8 入栈. Z% l# H; h) w1 Z, J" r% M; b: K
ldr r4,=0x7~+ K& N2 w1 y1 b# \
str r4,[sp,#-4]! ;参数 7 入栈% G( Q: ^+ y7 [( t1 J0 \1 s1 d; O
ldr r4,=0x6: X" c) A- c: B M. j) u
str r4,[sp,#-4]! ;参数 6 入栈
& y1 G* g$ u. e) a) x ldr r4,=0x5* H5 m4 L* N$ c
str r4,[sp,#-4]! ;参数 5 入栈, V* R/ v8 W$ G2 h, [' F- M$ `" ^
bl test_c_args_lots
8 i% t8 `0 Y! z& B9 F4 u0 h' e& Q ADD sp, sp, #4 ;清除栈中参数 5,本语句执行完后sp指向 参数6 # V0 D" b" d- {5 k1 V
ADD sp, sp, #4 ;清除栈中参数 6,本语句执行完后sp指向 参数75 q. l4 Y0 ^/ v+ }" ~* ^4 P, {' i
ADD sp, sp, #4 ;清除栈中参数 7,本语句执行完后sp指向 参数8
0 `8 U+ |' d2 ?; F1 S- X ADD sp, sp, #4 ;清除栈中参数 8,本语句执行完后sp指向 lr. _* ?! A8 ^* l
LDR pc, [sp],#4 ;将lr装进pc(返回main函数) % I6 f" K( y( R" h$ J( j
END, e9 |5 @/ r e! D' }6 b$ Y
test_c_args.c
7 [ x9 D/ J1 p3 w: R) L& ?! S! O8 g& {//--------------------------------------------------------------------------------
3 H: y' V2 C6 j' A" P& ^void test_c_args(int a,int b,int c,int d,int e,int f,int g,int h)
& C" M5 p5 t, k- Z3 a6 L0 ?{0 X; v7 n" }5 M" O
printk("test_c_args_lots:/n");. |( @9 P, v# D. A# ^* k* k
printk("%0x %0x %0x %0x %0x %0x %0x %0x/n",/ L8 d. I D& Z+ p$ i4 ]
a,b,c,d,e,f,g,h);
" A$ u4 T! _+ f [( c}! b1 v! [6 F6 K" P. t
main.c! B8 N3 p5 [6 a$ J
//--------------------------------------------------------------------------------
& S# I1 z" F8 Q+ x+ @5 }9 y4 `- \int main()
: D4 U" k4 `/ P7 c! f{
# l! }4 b! r6 S1 r8 _( e- \ H test_asm_args();
3 ~0 A3 R6 V- d- x' p for(;;);, B' T, [ p& U* o& {
}' Z3 n: z) u. Y& @
) G4 d9 Z. Z% s% h" H B
这部分的代码和实例1的代码大部分是相同的,不同的地方是test_c_args的参数个数和test_asm_args的参数传递方式。
5 g& H+ x& z- m在test_asm_args中,参数1~参数4还是通过R0~R3进行传递,而参数5~参数8则是通过把其压入堆栈的方式进行传递,不过要注意这四个入栈参数的入栈顺序,是以参数8->参数7->参数6->参数5的顺序入栈的。
) K, g+ ~7 F, p; @1 A直到调用test_c_args之前,堆栈内容如下:% f9 f3 x% m8 M8 b: ~
sp->+----------+
% W" N6 S5 \* c: p$ Q | 参数5 |0 V8 H8 r! _) |+ x2 Z- N
+----------+
/ b0 E* ?; U( ?: e | 参数6 |; p! g J& O% }, j
+----------+, U3 k# ^& ~2 F: R! { Z
| 参数7 |$ `1 D+ Q2 j2 n- W: u
+----------+
( m6 i; E1 j* D! Q* `& v | 参数8 |+ c/ `, [& E# j6 ^
+----------+
& d5 P' Q" F" R w7 A, \2 @ | lr |+ R8 b T+ _" G
+----------+
- o" S. J; @4 O1 W6 I& O I/ F- Ntest_c_args执行返回后,则设置sp,对之前入栈的参数进行清除,最后将lr装入pc返回main函数,在执行 LDR pc, [sp],#4 指令之前堆栈内容如下:) g$ w& @3 y$ r- ~
+----------+7 K. S, b& |. k; m$ ?
| 参数5 |
# v+ M' x: m' m$ E2 x8 ]. ` +----------+
, J- D% E* q9 k | 参数6 |
4 a! a7 x7 w2 `. Y1 t; f: N& @ +----------+0 R! M Z, Q d
| 参数7 |
" A t( \5 J+ ]+ |1 R. @ +----------+- N: M4 H2 \1 n- K( X! C: |" Z1 t
| 参数8 |9 O! S/ I! W: j% n+ \" D. }
sp->+----------+% |! B3 Z) I5 ~: o2 d
| lr |* r1 q9 Q- V7 P, H
+----------+