LoginSignup
1
0

More than 3 years have passed since last update.

可変長引数のビット幅を間違った際の挙動

Last updated at Posted at 2019-09-11

背景

libcurlには可変長引数のAPIがあるが、整数の渡し方で、64bitマシンでは動くが32bitマシンでは動かず、えらい目にあった。

検証プログラム

gcc -O2 -m32 va.c
clang -O2 -m32 va.c

※%llxを使っているので、WindowsはWindows7以降のみ有効です

va.c
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void va(int x, ...){
    va_list arg;
    va_start(arg, x);
    printf("%016llx\n",va_arg(arg,int));
    printf("%016llx\n",va_arg(arg,int));
    printf("%016llx\n",va_arg(arg,long));
    printf("%016llx\n",va_arg(arg,long long));
    printf("%016llx\n",va_arg(arg,long));
    va_end(arg);

}
int main(){va(1,(long long)2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0);}

結果

Linux/FreeBSD/Darwin gcc/clang m64

  • long longをintで受けるなど無茶苦茶なことをやったにもかかわらず、すべてきれいに出力された。long longに昇格されているのだろうか。
  • FreeBSD(さくらのレンタルサーバー)でも同様であった。
0000000000000002
0000000000000003
0000000000000004
0000000000000005
0000000000000006

Linux/FreeBSD gcc/clang Darwin gcc m32

  • intで受けた場合、上位8バイトは初期化されない。加えて、long longで送った2の上位8バイトが次のint受け取りにフレームシフトした。
  • なお、clangや、mingw-w64/wineの組み合わせでも同様の結果が得られた。
f77b292000000002
f77b292000000000
f77b292000000003
0000000500000004
0000000500000006

Darwin clang m32

  • ごみが0x0cになっているが原因は不明である。
0000000c00000002
0000000c00000000
0000000c00000003
0000000500000004
0000000500000006

Win64 MSVC100

  • ほとんどLinuxと同じでしたが、long longを引っ張ったところでごみが出てしまった。intを8bytes境界に載せているだけなのかもしれない。
  • long longにキャストして送るようにしたらゴミは出なかった。
  • MSVC140ではどうなんだろう。試してませんが。。
0000000000000002
0000000000000003
0000000000000004
00007ff600000005
0000000000000006

Win32 MSVC100

  • long longで受け取らないとかならずゴミが出るが、ゴミのパターンはLinuxと異なる。
  • なお最初の1は引数に依存しないようだ。
0000000100000002
00ea20c800000000
00ea20c800000003
0000000500000004
00ea20c800000006

結論

可変長引数のAPIを使う場合は普段以上に型に気をつけましょう…。

1
0
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
1
0