-
Notifications
You must be signed in to change notification settings - Fork 36
可変長引数の関数呼び出しに構造体を渡す場合
tyfkda edited this page Apr 24, 2025
·
4 revisions
- 可変長引数でもそうじゃなくても関数呼び出しに構造体引数を渡す場合:スタック引数として渡される
- 可変長引数関数で、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;
}- 構造体の実体がそのままスタック経由で渡される
.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:- 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- 可変長引数でも各引数はレジスタ渡し
- 構造体の実体は別に配置して、そのポインタがレジスタ経由で渡される
- 取り出しは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- 可変長引数でも各引数はレジスタ渡し
- 構造体の実体は別に配置して、そのポインタがレジスタ経由で渡される
- 取り出しは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//