14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ARM 関数呼び出し引数の渡し方

Last updated at Posted at 2017-05-13

ARMで関数呼び出しの引数の渡し方について調べてみました。

arm-none-eabi-gcc で簡単なC言語のサンプルコードをアセンブラ出力して調査しました。

arm-none-eabi-gcc -S test.c

関数呼び出しの仕様はAAPCSと呼ばれるドキュメントに記載があります。

int 1個の場合

void arg1(unsigned int arg1);

void test(void)
{
    arg1(1);
}
	mov	r0, #1
	bl	arg1

mov で r0 に引数を代入します。
bl で関数にジャンプします。

int 2個の場合

void arg2(unsigned int arg1, unsigned int arg2);

void test(void)
{
    arg2(1,2);
}
	mov	r0, #1
	mov	r1, #2
	bl	arg2

mov で2個目の引数をr1に代入します。

ARMではintの場合は引数4個までレジスタで渡します。

int 5個の場合

void arg5(unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5);

void test(void)
{
    arg5(1,2,3,4,5);
}
	sub	sp, sp, #8
	mov	r3, #5
	str	r3, [sp]
	mov	r0, #1
	mov	r1, #2
	mov	r2, #3
	mov	r3, #4
	bl	arg5

sp は 4バイトしか使わないけど8バイト伸ばします。
mov r3, #5 で5個目の引数をr3にレジスタに代入します。
str r3, [sp] で、それをメモリ上のスタックにストアします。

float 1個の場合

void argf(float arg1);

void test(void)
{
    argf(1.0);
}
	mov	r0, #1065353216
	bl	argf

floatは32bitなので、intの場合と同じです。

double 1個の場合

void argd(double arg1);

void test(void)
{
    argd(1.1);
}
	adr	r1, .L2
	ldmia	r1, {r0-r1}
	bl	argd

.L2:
	.word	2576980378
	.word	1072798105

double は 64bitなので r0, r1 に数値を入れます。
adr で、ラベル L2のアドレスをr1に代入し、ldmiaでr0, r1にロードします。

dobule 3個の場合

double 2個まではレジスタ渡しできます。3個目からはスタック渡しです。

void argd3(double arg1, double arg2, double arg3);

void test(void)
{
    argd(1.1, 2.2, 3.3);
}
	sub	sp, sp, #12
	adr	r4, .L2
	ldmia	r4, {r3-r4}
	stmia	sp, {r3-r4}
	adr	r1, .L2+8
	ldmia	r1, {r0-r1}
	adr	r3, .L2+16
	ldmia	r3, {r2-r3}
	bl	argd

.L2:
	.word	1717986918
	.word	1074423398
	.word	2576980378
	.word	1072798105
	.word	2576980378
	.word	1073846681

スタックを12バイト伸ばしている理由が不明。mod 8 == 0でないけど大丈夫なのでしょうか?

4バイトの構造体

struct s4{
  int  i1;
};

void args(struct s4 st);

void test(void)
{
    struct s4 st;
    args(st);
}
	sub	sp, sp, #8
	ldr	r0, [fp, #-8]
	bl	args

スタックに4バイトの構造体を確保する。AAPCSではpublic interfaceの場合spは mod 8 == 0というルールなので、4バイトだけ使うが、スタックを8バイト伸ばす。
ldr で r0 に構造体をロードします。

16バイトの構造体

struct s16{
  int  i1;
  int  i2;
  int  i3;
  int  i4;
};

void args(struct s16 st);

void test(void)
{
    struct s16 st;
    args(st);
}
	sub	sp, sp, #16
	sub	r3, fp, #20
	ldmia	r3, {r0, r1, r2, r3}
	bl	args

スタックに16バイトの構造体を確保する。
ldmia で r0 - r3に構造体をロードします。

20バイトの構造体

16バイト以上の構造体は、16バイトまではレジスタで渡して、16バイト以降はスタックで渡します。

struct s20{
  int  i1;
  int  i2;
  int  i3;
  int  i4;
  int  i5;
};

void args(struct s20 st);

void test(void)
{
    struct s20 st;
    args(st);
}
	sub	sp, sp, #32
	ldr	r3, [fp, #-8]
	str	r3, [sp]
	sub	r3, fp, #24
	ldmia	r3, {r0, r1, r2, r3}
	bl	args

スタックに20バイトの構造体を確保する。引数渡しで4バイト使うので、mod 8 == 0になるように、 24 + 8 の32バイト スタックを伸ばす。
スタックに16バイト以降の4バイトをロードする。
ldmiaで r0 - r3に構造体をロードします。

document

AAPCSを読む前に、Microsoftによる「ARM ABI 規則の概要」を読むと良いです。

14
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?