Skip to content

可変長引数の関数呼び出しに構造体を渡す場合

tyfkda edited this page Apr 24, 2025 · 4 revisions

XCCの現状

  • 可変長引数でもそうじゃなくても関数呼び出しに構造体引数を渡す場合:スタック引数として渡される
  • 可変長引数関数で、x86-64だとちゃんと動く、aarch64やRISCV64ではおかしくなる

コード

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

typedef struct {long x, y, z;} VaargStruct;

long vaargs_struct(int n, ...) {
  va_list ap;
  va_start(ap, n);
#if 0
  printf("va_list=#%zu\n", sizeof(ap));
  for (size_t i = 0; i < sizeof(ap); i += sizeof(intptr_t)) {
    printf("%016lx ", ((uintptr_t*)ap)[i]);
  }
  printf("\n");
#endif
  int a = 0;
  for (int i = 0; i < n; ++i) {
    VaargStruct s = va_arg(ap, VaargStruct);
    a = a * 1000 + s.x * 100 + s.y * 10 + s.z;
  }
  va_end(ap);
  return a;
}

int main(void) {
  VaargStruct s1 = {1, 2, 3};
  VaargStruct s2 = {4, 5, 6};
  long x = vaargs_struct(2, s1, s2);
  printf("%ld\n", x);
  return 0;
}

gcc/x86-64の場合

  • 構造体の実体がそのままスタック経由で渡される
	.file	"foo.c"
	.text
	.globl	vaargs_struct
	.type	vaargs_struct, @function
vaargs_struct:
	endbr64
	pushq	%rbp
	movq	%rsp, %rbp
	pushq	%rbx
	subq	$280, %rsp

	;; レジスタ引数をスタックに展開
	movl	%edi, -276(%rbp)   ;; n
	movq	%rsi, -184(%rbp)
	movq	%rdx, -176(%rbp)
	movq	%rcx, -168(%rbp)
	movq	%r8, -160(%rbp)
	movq	%r9, -152(%rbp)
	testb	%al, %al
	je	.L2
	movaps	%xmm0, -144(%rbp)
	movaps	%xmm1, -128(%rbp)
	movaps	%xmm2, -112(%rbp)
	movaps	%xmm3, -96(%rbp)
	movaps	%xmm4, -80(%rbp)
	movaps	%xmm5, -64(%rbp)
	movaps	%xmm6, -48(%rbp)
	movaps	%xmm7, -32(%rbp)
.L2:
	;; ap: -200(%rbp)
	movq	%fs:40, %rax
	movq	%rax, -200(%rbp)
	xorl	%eax, %eax
	movl	$8, -224(%rbp)
	movl	$48, -220(%rbp)
	leaq	16(%rbp), %rax
	movq	%rax, -216(%rbp)
	leaq	-192(%rbp), %rax
	movq	%rax, -208(%rbp)
	movl	$0, -264(%rbp)    ; a = 0
	movl	$0, -260(%rbp)    ; i = 0
	jmp	.L3
.L4:
	movq	-216(%rbp), %rax
	leaq	24(%rax), %rdx
	movq	%rdx, -216(%rbp)  ;; gp += sizeof(VaargStruct)

	;; 構造体取り出し: -> -256(%rbp)
	movq	(%rax), %rcx
	movq	8(%rax), %rbx
	movq	%rcx, -256(%rbp)
	movq	%rbx, -248(%rbp)
	movq	16(%rax), %rax
	movq	%rax, -240(%rbp)

	;; a更新
	movl	-264(%rbp), %eax
	imull	$1000, %eax, %eax
	movl	%eax, %edx
	movq	-256(%rbp), %rax
	imull	$100, %eax, %eax
	leal	(%rdx,%rax), %ecx
	movq	-248(%rbp), %rax
	movl	%eax, %edx
	movl	%edx, %eax
	sall	$2, %eax
	addl	%edx, %eax
	addl	%eax, %eax
	leal	(%rcx,%rax), %edx
	movq	-240(%rbp), %rax
	addl	%edx, %eax
	movl	%eax, -264(%rbp)
	addl	$1, -260(%rbp)
.L3:
	movl	-260(%rbp), %eax
	cmpl	-276(%rbp), %eax
	jl	.L4
	movl	-264(%rbp), %eax
	cltq
	movq	-200(%rbp), %rbx
	xorq	%fs:40, %rbx
	je	.L6
	call	__stack_chk_fail@PLT
.L6:
	addq	$280, %rsp
	popq	%rbx
	popq	%rbp
	ret
	.size	vaargs_struct, .-vaargs_struct
	.section	.rodata
.LC0:
	.string	"%ld\n"

	.text
	.globl	main
	.type	main, @function
main:
	endbr64
	pushq	%rbp
	movq	%rsp, %rbp
	subq	$80, %rsp

	;; s1: -64~-40(%rbp)
	movq	$1, -64(%rbp)
	movq	$2, -56(%rbp)
	movq	$3, -48(%rbp)

	;; s2: -32~-8(%rbp)
	movq	$4, -32(%rbp)
	movq	$5, -24(%rbp)
	movq	$6, -16(%rbp)

	;; 構造体引数:実体を積み直す
	pushq	-16(%rbp)
	pushq	-24(%rbp)
	pushq	-32(%rbp)
	pushq	-48(%rbp)
	pushq	-56(%rbp)
	pushq	-64(%rbp)
	movl	$2, %edi
	movl	$0, %eax
	call	vaargs_struct

	addq	$48, %rsp
	movq	%rax, -72(%rbp)
	movq	-72(%rbp), %rax
	movq	%rax, %rsi
	leaq	.LC2(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, %eax
	leave
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	 1f - 0f
	.long	 4f - 1f
	.long	 5
0:
	.string	 "GNU"
1:
	.align 8
	.long	 0xc0000002
	.long	 3f - 2f
2:
	.long	 0x3
3:
	.align 8
4:

gcc/Apple/aarch64の場合

  • Appleの場合、可変長引数は必ずスタック経由になる
  • 構造体の実体は別に配置して、そのポインタが並べられる
  • 取り出しは2段階(ポインタを得て、実体をコピー)になる
	.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 15, 0	sdk_version 15, 4
	.globl	_vaargs_struct                  ; -- Begin function vaargs_struct
	.p2align	2
_vaargs_struct:                         ; @vaargs_struct
	.cfi_startproc
; %bb.0:
	sub	sp, sp, #48
	.cfi_def_cfa_offset 48
	str	w0, [sp, #44]        ;; n = sp+44
	add	x9, sp, #32          ;; x9 = &ap
	add	x8, sp, #48
	str	x8, [x9]             ;; ap = sp + 48 (original sp bottom)
	str	wzr, [sp, #28]       ;; a
	str	wzr, [sp, #24]       ;; i
	b	LBB0_1
LBB0_1:                                 ; =>This Inner Loop Header: Depth=1
	ldr	w8, [sp, #24]
	ldr	w9, [sp, #44]
	subs	w8, w8, w9       ;; i >= n ?
	b.ge	LBB0_4
	b	LBB0_2
LBB0_2:                                 ;   in Loop: Header=BB0_1 Depth=1
	ldr	x8, [sp, #32]        ;; x8 = ap
	add	x9, x8, #8
	str	x9, [sp, #32]        ;; ap += 8
	ldr	x8, [x8]             ;; 構造体のアドレス
	ldr	q0, [x8]
	str	q0, [sp]             ;; s = va_arg(ap, VaargStruct)
	ldr	x8, [x8, #16]
	str	x8, [sp, #16]

	ldr	w8, [sp, #28]
	mov	w9, #1000                       ; =0x3e8
	mul	w9, w8, w9
	ldr	x8, [sp]
	mov	x10, #100                       ; =0x64
	mul	x8, x8, x10
	add	x8, x8, w9, sxtw
	ldr	x9, [sp, #8]
	mov	x10, #10                        ; =0xa
	mul	x9, x9, x10
	add	x8, x8, x9
	ldr	x9, [sp, #16]
	add	x8, x8, x9
                                        ; kill: def $w8 killed $w8 killed $x8
	str	w8, [sp, #28]
	b	LBB0_3
LBB0_3:                                 ;   in Loop: Header=BB0_1 Depth=1
	ldr	w8, [sp, #24]
	add	w8, w8, #1
	str	w8, [sp, #24]
	b	LBB0_1
LBB0_4:
	ldrsw	x0, [sp, #28]
	add	sp, sp, #48
	ret
	.cfi_endproc
                                        ; -- End function
	.globl	_main                           ; -- Begin function main
	.p2align	2
_main:                                  ; @main
	.cfi_startproc
; %bb.0:
	sub	sp, sp, #176
	stp	x29, x30, [sp, #160]            ; 16-byte Folded Spill
	add	x29, sp, #160
	.cfi_def_cfa w29, 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
	mov	w8, #0                          ; =0x0
	str	w8, [sp, #28]                   ; 4-byte Folded Spill
	stur	wzr, [x29, #-4]

	;; s1: x29-32~-8 24byte
	adrp	x8, l___const.main.s1@PAGE
	add	x8, x8, l___const.main.s1@PAGEOFF
	ldr	q0, [x8]
	stur	q0, [x29, #-32]
	ldr	x8, [x8, #16]
	stur	x8, [x29, #-16]

	;; s2: x29-64~-40 24byte
	adrp	x8, l___const.main.s2@PAGE
	add	x8, x8, l___const.main.s2@PAGEOFF
	ldr	q0, [x8]
	stur	q0, [x29, #-64]
	ldr	x8, [x8, #16]
	stur	x8, [x29, #-48]

	;; sp+64  <-- s1
	ldur	q0, [x29, #-32]
	add	x10, sp, #64
	str	q0, [sp, #64]
	ldur	x8, [x29, #-16]
	str	x8, [sp, #80]

	;; sp+32  <-- s2
	ldur	q0, [x29, #-64]
	add	x8, sp, #32
	str	q0, [sp, #32]
	ldur	x9, [x29, #-48]
	str	x9, [sp, #48]

	mov	x9, sp
	str	x10, [x9]
	str	x8, [x9, #8]
	mov	w0, #2                          ; =0x2
	bl	_vaargs_struct

	;; sp+0 = sp+64 = &dup(s1)
	;; sp+8 = sp+32 = &dup(s2)
	stur	x0, [x29, #-72]
	ldur	x8, [x29, #-72]
	mov	x9, sp
	str	x8, [x9]
	adrp	x0, l_.str@PAGE
	add	x0, x0, l_.str@PAGEOFF
	bl	_printf
	ldr	w0, [sp, #28]                   ; 4-byte Folded Reload
	ldp	x29, x30, [sp, #160]            ; 16-byte Folded Reload
	add	sp, sp, #176
	ret
	.cfi_endproc
                                        ; -- End function
	.section	__TEXT,__const
	.p2align	3, 0x0                          ; @__const.main.s1
l___const.main.s1:
	.quad	1                               ; 0x1
	.quad	2                               ; 0x2
	.quad	3                               ; 0x3

	.p2align	3, 0x0                          ; @__const.main.s2
l___const.main.s2:
	.quad	4                               ; 0x4
	.quad	5                               ; 0x5
	.quad	6                               ; 0x6

	.section	__TEXT,__cstring,cstring_literals
l_.str:                                 ; @.str
	.asciz	"%ld\n"

.subsections_via_symbols

gcc/raspberrypi/aarch64の場合

  • 可変長引数でも各引数はレジスタ渡し
  • 構造体の実体は別に配置して、そのポインタがレジスタ経由で渡される
  • 取り出しは2段階(ポインタを得て、実体をコピー)になる
        .arch armv8-a
        .file   "foo.c"
        .text
        .align  2
        .global vaargs_struct
        .type   vaargs_struct, %function
vaargs_struct:
.LFB0:
        .cfi_startproc
        sub     sp, sp, #272
        .cfi_def_cfa_offset 272
        ;; レジスタ引数書き出し
        str     w0, [sp, 12]
        str     x1, [sp, 216]
        str     x2, [sp, 224]
        str     x3, [sp, 232]
        str     x4, [sp, 240]
        str     x5, [sp, 248]
        str     x6, [sp, 256]
        str     x7, [sp, 264]
        str     q0, [sp, 80]
        str     q1, [sp, 96]
        str     q2, [sp, 112]
        str     q3, [sp, 128]
        str     q4, [sp, 144]
        str     q5, [sp, 160]
        str     q6, [sp, 176]
        str     q7, [sp, 192]

        ;; ap: sp+40~72
        add     x0, sp, 272
        str     x0, [sp, 40]
        add     x0, sp, 272
        str     x0, [sp, 48]
        add     x0, sp, 208
        str     x0, [sp, 56]
        mov     w0, -56
        str     w0, [sp, 64]
        mov     w0, -128
        str     w0, [sp, 68]
        str     wzr, [sp, 76]           ; a = 0
        str     wzr, [sp, 72]           ; i = 0
        b       .L2
.L7:
        ldr     w1, [sp, 64]
        ldr     x0, [sp, 40]
        cmp     w1, 0
        blt     .L3
        mov     x1, x0
        add     x1, x1, 15
        and     x1, x1, -8
        str     x1, [sp, 40]
        b       .L4
.L3:
        add     w2, w1, 8
        str     w2, [sp, 64]
        ldr     w2, [sp, 64]
        cmp     w2, 0
        ble     .L5
        mov     x1, x0
        add     x1, x1, 15
        and     x1, x1, -8
        str     x1, [sp, 40]
        b       .L4
.L5:
        ldr     x2, [sp, 48]
        sxtw    x0, w1
        add     x0, x2, x0
.L4:
        ldr     x0, [x0]
        add     x2, sp, 16
        mov     x3, x0
        ldp     x0, x1, [x3]
        stp     x0, x1, [x2]
        ldr     x0, [x3, 16]
        str     x0, [x2, 16]
        ldr     w1, [sp, 76]
        mov     w0, 1000
        mul     w0, w1, w0
        mov     w1, w0
        ldr     x0, [sp, 16]
        mov     w2, w0
        mov     w0, 100
        mul     w0, w2, w0
        add     w1, w1, w0
        ldr     x0, [sp, 24]
        mov     w2, w0
        mov     w0, w2
        lsl     w0, w0, 2
        add     w0, w0, w2
        lsl     w0, w0, 1
        add     w0, w1, w0
        ldr     x1, [sp, 32]
        add     w0, w0, w1
        str     w0, [sp, 76]
        ldr     w0, [sp, 72]
        add     w0, w0, 1
        str     w0, [sp, 72]
.L2:
        ldr     w1, [sp, 72]
        ldr     w0, [sp, 12]
        cmp     w1, w0
        blt     .L7
        ldrsw   x0, [sp, 76]
        add     sp, sp, 272
        .cfi_def_cfa_offset 0
        ret
        .cfi_endproc
.LFE0:
        .size   vaargs_struct, .-vaargs_struct
        .section        .rodata
        .align  3
.LC2:
        .string "%ld\n"

        .text
        .align  2
        .global main
        .type   main, %function
main:
.LFB1:
        .cfi_startproc
        stp     x29, x30, [sp, -144]!
        .cfi_def_cfa_offset 144
        .cfi_offset 29, -144
        .cfi_offset 30, -136
        mov     x29, sp
        adrp    x0, .LC0

        ;; s1: sp+112
        add     x0, x0, :lo12:.LC0
        add     x2, sp, 112
        mov     x3, x0
        ldp     x0, x1, [x3]
        stp     x0, x1, [x2]
        ldr     x0, [x3, 16]
        str     x0, [x2, 16]

        ;; s2: sp+88
        adrp    x0, .LC1
        add     x0, x0, :lo12:.LC1
        add     x2, sp, 88
        mov     x3, x0
        ldp     x0, x1, [x3]
        stp     x0, x1, [x2]
        ldr     x0, [x3, 16]
        str     x0, [x2, 16]

        ;; 構造体引数1: sp+48
        add     x2, sp, 48
        add     x3, sp, 112
        ldp     x0, x1, [x3]
        stp     x0, x1, [x2]
        ldr     x0, [x3, 16]
        str     x0, [x2, 16]

        ;; 構造体引数2: sp+16
        add     x2, sp, 16
        add     x3, sp, 88
        ldp     x0, x1, [x3]
        stp     x0, x1, [x2]
        ldr     x0, [x3, 16]
        str     x0, [x2, 16]

        add     x1, sp, 16
        add     x0, sp, 48
        mov     x2, x1          ; 構造体引数2
        mov     x1, x0          ; 構造体引数1
        mov     w0, 2           ; 個数
        bl      vaargs_struct

        str     x0, [sp, 136]
        ldr     x1, [sp, 136]
        adrp    x0, .LC2
        add     x0, x0, :lo12:.LC2
        bl      printf
        mov     w0, 0
        ldp     x29, x30, [sp], 144
        .cfi_restore 30
        .cfi_restore 29
        .cfi_def_cfa_offset 0
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .section        .rodata
        .align  3
.LC0:
        .xword  1
        .xword  2
        .xword  3
        .align  3
.LC1:
        .xword  4
        .xword  5
        .xword  6
        .text
        .ident  "GCC: (Debian 8.3.0-6) 8.3.0"
        .section        .note.GNU-stack,"",@progbits

gcc/RISCV64の場合

  • 可変長引数でも各引数はレジスタ渡し
  • 構造体の実体は別に配置して、そのポインタがレジスタ経由で渡される
  • 取り出しは2段階(ポインタを得て、実体をコピー)になる
	.file	"foo.c"
	.option nopic
	.attribute arch, "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0"
	.attribute unaligned_access, 0
	.attribute stack_align, 16
	.text
	.align	1
	.globl	vaargs_struct
	.type	vaargs_struct, @function
vaargs_struct:
	addi	sp,sp,-144
	sd	ra,72(sp)
	sd	s0,64(sp)
	addi	s0,sp,80
	;; レジスタ引数をスタックに展開
	sd	a1,8(s0)
	sd	a2,16(s0)
	sd	a3,24(s0)
	sd	a4,32(s0)
	sd	a5,40(s0)
	sd	a6,48(s0)
	sd	a7,56(s0)

	mv	a5,a0
	sw	a5,-68(s0)    ;; n: -68(s0)
	addi	a5,s0,64
	sd	a5,-80(s0)
	ld	a5,-80(s0)
	addi	a5,a5,-56 ;; s0+64-56
	sd	a5,-32(s0)    ;; ap: -32(s0)
	sw	zero,-20(s0)  ;; a
	sw	zero,-24(s0)  ;; i
	j	.L2
.L3:
	ld	a5,-32(s0)
	addi	a4,a5,8
	sd	a4,-32(s0)    ;; ap += 8
	ld	a5,0(a5)
	;; 構造体引数取り出し
	ld	a4,0(a5)
	sd	a4,-56(s0)    ;; s = va_arg(ap, VaargStruct)
	ld	a4,8(a5)
	sd	a4,-48(s0)
	ld	a5,16(a5)
	sd	a5,-40(s0)

	;; a = a * ...
	lw	a5,-20(s0)
	mv	a4,a5
	li	a5,1000
	mulw	a5,a4,a5
	sext.w	a5,a5
	mv	a3,a5
	ld	a5,-56(s0)
	sext.w	a4,a5
	li	a5,100
	mulw	a5,a4,a5
	sext.w	a5,a5
	addw	a5,a3,a5
	sext.w	a4,a5
	ld	a5,-48(s0)
	sext.w	a5,a5
	mv	a3,a5
	mv	a5,a3
	slliw	a5,a5,2
	addw	a5,a5,a3
	slliw	a5,a5,1
	sext.w	a5,a5
	addw	a5,a4,a5
	sext.w	a4,a5
	ld	a5,-40(s0)
	sext.w	a5,a5
	addw	a5,a4,a5
	sext.w	a5,a5
	sw	a5,-20(s0)
	lw	a5,-24(s0)
	addiw	a5,a5,1
	sw	a5,-24(s0)
.L2:
	lw	a5,-24(s0)
	mv	a4,a5
	lw	a5,-68(s0)
	sext.w	a4,a4
	sext.w	a5,a5
	blt	a4,a5,.L3     ;; i < n

	lw	a5,-20(s0)
	mv	a0,a5
	ld	ra,72(sp)
	ld	s0,64(sp)
	addi	sp,sp,144
	jr	ra
	.size	vaargs_struct, .-vaargs_struct
	.section	.rodata
	.align	3
.LC0:
	.string	"%ld\n"

	.text
	.align	1
	.globl	main
	.type	main, @function
main:
	addi	sp,sp,-144
	sd	ra,136(sp)
	sd	s0,128(sp)
	addi	s0,sp,144

	;; s1: -48~-24(s0)
	li	a5,1
	sd	a5,-48(s0)
	li	a5,2
	sd	a5,-40(s0)
	li	a5,3
	sd	a5,-32(s0)

	;; s2: -72~-48
	li	a5,4
	sd	a5,-72(s0)
	li	a5,5
	sd	a5,-64(s0)
	li	a5,6
	sd	a5,-56(s0)

	;; 引数1: -112~-88(s0)
	ld	a5,-48(s0)
	sd	a5,-112(s0)
	ld	a5,-40(s0)
	sd	a5,-104(s0)
	ld	a5,-32(s0)
	sd	a5,-96(s0)
	;; 引数2: -144~-120(s0)
	ld	a5,-72(s0)
	sd	a5,-144(s0)
	ld	a5,-64(s0)
	sd	a5,-136(s0)
	ld	a5,-56(s0)
	sd	a5,-128(s0)

	addi	a4,s0,-144
	addi	a5,s0,-112
	mv	a2,a4  ;; 引数2のポインタ
	mv	a1,a5  ;; 引数1のポインタ
	li	a0,2   ;; 個数
	call	vaargs_struct

	sd	a0,-24(s0)
	ld	a1,-24(s0)
	lui	a5,%hi(.LC0)
	addi	a0,a5,%lo(.LC0)
	call	printf
	li	a5,0
	mv	a0,a5
	ld	ra,136(sp)
	ld	s0,128(sp)
	addi	sp,sp,144
	jr	ra
	.size	main, .-main
	.ident	"GCC: (g04696df09) 14.2.0"
	.section	.note.GNU-stack,"",@progbits

//

Clone this wiki locally