|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
3 V( {+ I* c4 F+ h5 i之前在学习如何在C语言中嵌入汇编时有了解到C语言之前的参数调用是使用寄存器R0传递第一个参数,R1传递到
$ i: X: J0 H( H" ~: v% I: D4 ]% Y# w6 S0 s8 }/ x, B7 Y( K4 s: f O
第二个..一直到R3传递第四个参数.但是实际上有时可能传递的参数非常多,超过8个,或是参数中有浮点数之类,
; B; W; a4 z! V9 x3 ?
3 s& }$ d2 z( |4 `, C5 e$ V参数也会超过4个寄存器,对于超出的部份并不使用R4,而是使用堆栈的方式% H4 l9 u- T; n# @8 _
4 |* j3 W+ |3 k* d( g—————————————————华丽的分割线————————————————$ ^" M: {3 e6 _$ j
1 X) b' z" p6 W
对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure
, H2 G* Y5 Q( [6 K2 P8 k
/ g' ], C4 a# a/ ~' gCall Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容
! ^/ U( s9 v# w. b: D
4 p1 U# p5 f9 S3 x7 D可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是 汇编代码中对C函数调用时如
9 F0 t: `) u) `% l+ j/ o2 X/ ~) j. c+ ]) q a2 W
何进行参数的传递以及如何从C函数正确返回
# J: b8 v* t; G% S& N不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由: Y0 Y: w+ a, x2 t+ g8 U) A, ]
/ `6 r9 N1 w( S. s: C9 E: U6 ]6 iR0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。! f( X' @* i) X4 K4 V9 \; I. F# r: M
我们先讨论一下形参个数为4的情况.
* f8 |2 j2 @! g4 U实例1:6 Z3 B4 m. _/ X. s1 l
test_asm_args.asm
2 B1 `. [7 ?" h' C6 l//——————————————————————————–& C" _/ i& ]) B& r; q
IMPORT test_c_args ;声明test_c_args函数7 N6 C* n! m5 Z/ O: V% {
AREA TEST_ASM, CODE, READONLY! }1 s1 U/ Z" Y x
EXPORT test_asm_args* N0 V C _8 @9 l
test_asm_args& _/ W9 i' Q7 U4 y j' W
STR lr, [sp, #-4]! ;保存当前lr! h/ l1 K, N7 K" K
ldr r0,=0×10 ;参数 1
' R: g6 i$ n. a: }+ o) qldr r1,=0×20 ;参数 23 I3 e( z: a# E+ f4 V. |9 V, v
ldr r2,=0×30 ;参数 34 W7 @' o- n, u4 Y1 j8 G, T
ldr r3,=0×40 ;参数 4( m0 I# T3 h9 ~3 X! @' n3 |' Q+ ?; e
bl test_c_args ;调用C函数/ h+ X. ~8 g- ^ |4 a: Y
LDR pc, [sp], #4 ;将lr装进pc(返回main函数)
( s8 l2 ]; L. A( T+ bEND
& t7 k% j2 e5 t" W# L& G- Etest_c_args.c" n- f% s" N2 F( W3 x" [
//——————————————————————————–, C' N9 r, q7 u* T$ `
void test_c_args(int a,int b,int c,int d)8 x1 d1 C1 }* b+ \
{. E: m/ k0 F E% m# ^. G+ E
printk(“test_c_args:\n”);& k5 [& \" R1 O
printk(“%0x %0x %0x %0x\n”,a,b,c,d);. C7 V0 A& \- `4 T
}4 X) o0 P& W, L
main.c; e: t( m. C3 i3 y6 t4 J
//——————————————————————————–8 w0 S" Z$ ^# x; ]5 v4 C
int main()
/ I2 G: @( D( x{
4 y4 J- f9 F! m V/ j+ rtest_asm_args();
; W/ ~0 V5 g" f/ i6 d9 Y) X- _for(;;);
. ^+ x9 E. ]! E}
; I6 N2 P0 I. d6 ?+ k7 }程序从main函数开始执行,main调用了test_asm_args,test_asm_args调用了test_c_args,最后从test_asm_args4 D( t p( y4 t& _( r
: d" x; d6 `2 ]/ c% k4 x返回main.4 s3 B* U4 F# v& S8 o/ J
代码分别使用了汇编和C定义了两个函数,test_asm_args 和 test_c_args,test_asm_args调用了test_c_args,5 J6 r+ r$ J$ ]: E: ~, T, f
' @; J8 m+ _7 j+ u* N" t% C) @8 l
其参数的传递方式就是向R0~R3分别写入参数值,之后使用bl语句对test_c_args进行调用。其中值得注意的地
l9 Z0 q! B! w( R6 `- s8 n ?( [7 ?
方是用红色标记的语句,test_asm_args在调用test_c_args之前必须把当前的lr入栈,调用完test_c_args之后& m2 A2 A! A6 U0 ?: ?
H6 @. a5 v0 v- }" d* P
再把刚才保存在栈中的lr写回pc,这样才能返回到main函数中。* [+ h# X% G0 C2 J/ m5 d
如果test_c_args的参数是8个呢?这种情况test_asm_args应该怎样传递参数呢? I8 ~2 W) }6 b: `3 c6 x
实例2:# L' ^/ ]. G$ X: I {6 ~( ?) r/ ^& C
test_asm_args.asm8 ~4 d1 Z g: w' g' I
//——————————————————————————–
) b: S& r3 o+ s5 n8 p6 uIMPORT test_c_args ;声明test_c_args函数
3 `: j) M* |5 I* i3 EAREA TEST_ASM, CODE, READONLY
2 N0 N! f6 W" b& pEXPORT test_asm_args3 m' b+ G) J* z: c- M
test_asm_args
! G2 l" p4 L0 Y b5 [STR lr, [sp, #-4]! ;保存当前lr
' T$ t% W1 T- o* W) t9 Wldr r0,=0×1 ;参数 1# e! t$ A% i+ c2 L% V7 }
ldr r1,=0×2 ;参数 2
/ u6 E. p9 ^/ x( d/ oldr r2,=0×3 ;参数 3
8 U! H+ Q7 g* s/ C, L- Dldr r3,=0×4 ;参数 4
) ^# `' J$ U( E; S7 pldr r4,=0×8
3 |% ` j) a5 f/ hstr r4,[sp,#-4]! ;参数 8 入栈
2 N5 `. T( ]- u( B. `" Hldr r4,=0×7! d" k |' q0 ^5 ^- r0 f* F( O6 U
str r4,[sp,#-4]! ;参数 7 入栈) Z1 A# J8 B2 u# W% Z
ldr r4,=0×6
0 ~+ ^8 B6 j" y3 h3 l( \str r4,[sp,#-4]! ;参数 6 入栈
" C# S6 t( D. A+ e2 r1 Wldr r4,=0×5! \' y! O5 y& C( h# b( X! R3 W
str r4,[sp,#-4]! ;参数 5 入栈
2 g) [/ S/ h6 a2 Rbl test_c_args_lots# C/ h$ x# x! ~# w/ F9 C
ADD sp, sp, #4 ;清除栈中参数 5,本语句执行完后sp指向 参数6. m0 E8 z2 |) y4 p/ ^3 g
ADD sp, sp, #4 ;清除栈中参数 6,本语句执行完后sp指向 参数7: A6 L& A! p- ]- s/ R3 l' I
ADD sp, sp, #4 ;清除栈中参数 7,本语句执行完后sp指向 参数8
9 h# {5 b+ _ q$ }& J. mADD sp, sp, #4 ;清除栈中参数 8,本语句执行完后sp指向 lr& Q0 T& \1 C* Y8 J: T
LDR pc, [sp],#4 ;将lr装进pc(返回main函数)
. }! r/ z" z" U$ i; L+ jEND
& v0 n: w! p5 _) M: k, Itest_c_args.c, o& a8 \' b# S0 V
//——————————————————————————–
1 I1 K4 b. i) S; gvoid test_c_args(int a,int b,int c,int d,int e,int f,int g,int h)
" T2 M5 G- M$ k' j1 e* l{/ s" }5 X4 s5 n9 \5 V5 @" M
printk(“test_c_args_lots:\n”);
# ~; g& b7 T$ ~$ f8 C( I( {8 S0 wprintk(“%0x %0x %0x %0x %0x %0x %0x %0x\n”,& F/ X9 K3 M$ x
: ~0 K5 l* v- a4 G" e S' n
a,b,c,d,e,f,g,h);
) e9 Y! d: Z7 S# ?, C! @}
( }. Z) W1 ]2 g! w; }! i$ [8 tmain.c
& p0 f: o" A( J: ^* j7 w o! q( q//——————————————————————————–" ]* P( |2 r7 q5 ~+ C. J% w
int main()4 P& J6 U/ T. a. P/ H* C1 b
{
) G K, Q9 L2 k; A5 `$ u1 [) C* M* wtest_asm_args();
$ q* r' S V! @for(;;);
) S) n u4 \1 n1 Y: R! n- E}
2 j2 T) A4 ~, \8 d1 a. D这部分的代码和实例1的代码大部分是相同的,不同的地方是test_c_args的参数个数和test_asm_args的参数传) E1 L" S+ F( H+ ~! l1 {( O
3 y. u& Y) E) B递方式。
, j1 J3 k% t) B1 O" D( A6 M在test_asm_args中,参数1~参数4还是通过R0~R3进行传递,而参数5~参数8则是通过把其压入堆栈的方式进
0 @- n3 K# | ?! G" f7 s% i
# s! ~' e- ?0 ^: G行传递,不过要注意这四个入栈参数的入栈顺序,是以参数8->参数7->参数6->参数5的顺序入栈的。- U) ~* e' a0 g; s8 H- g, u0 w
直到调用test_c_args之前,堆栈内容如下:
$ \; ~3 c) h! ?sp->+———-+- W/ L: Q* Q1 I9 }7 {/ e( |
| 参数5 |
" x B) u, a$ A+———-+
. k+ L/ T7 G3 @, I, d: ]9 R5 l| 参数6 |# C* i7 H3 F7 Q& q( m9 A% c
+———-+" ^9 B' f3 u* `
| 参数7 |
# B2 c; k6 Z! j+———-+
8 n/ I I, x* U( K| 参数8 |# a- ~6 Y' H, G, c
+———-+
" U- S( Y5 W' {1 U| lr |8 h+ x, R- z7 W7 M0 k+ Z4 B
+———-+
0 y9 Z+ S/ x5 d6 s% f- Jtest_c_args执行返回后,则设置sp,对之前入栈的参数进行清除,最后将lr装入pc返回main函数,在执行 LDR
: C" z9 i# F6 c9 b3 n4 ]+ M1 C7 B+ A+ i3 W Y$ n0 P8 s# ^- H4 E$ `# V
pc, [sp],#4 指令之前堆栈内容如下:
& n. V4 y8 ^9 v/ {# o+———-+% C% c5 |: o4 G& m g
| 参数5 |% }( [: C( Z2 d- y
+———-+
3 i7 m& _2 ~& _) y3 s. Q| 参数6 |
4 _0 z* e5 T V1 h& F+———-++ s, s8 o: B( s' x0 G
| 参数7 |. C/ f2 X0 I9 g% F; t @
+———-+: E( H: v: X2 m& ^0 l7 V% ?
| 参数8 |( d C' Z4 B3 D1 V: C; h
sp->+———-+; J; K1 }, K- k; ]+ g1 `1 [
| lr |2 @/ Q% l( T/ O0 h0 |' K* `
+———-+ |
|