27

va_listx86_64 ABI (Linux で使用されるもの) での表現のリファレンスを持っている人はいますか? スタックまたは引数が破損しているように見えるコードをデバッグしようとしていますが、何を見ているのかを理解するのに本当に役立ちます...

4

3 に答える 3

35

x86-64 System V ABi のドキュメントが役立つ場合があります。軽くても参考になります。

可変引数リストのリファレンスは 54 ページから始まり、56 ~ 57 ページのドキュメントに続きますva_list

va_listタイプ_

型は、マクロva_listを実装するために必要な情報を含む 1 つの構造体の 1 つの要素を含む配列です。va_arg型の C 定義を図va_list3.34 に示します。

図 3.34:va_list型宣言

typedef struct {
   unsigned int gp_offset;
   unsigned int fp_offset;
   void *overflow_arg_area;
   void *reg_save_area;
} va_list[1];

va_startマクロ_

マクロはva_start、次のように構造体を初期化します。

reg_save_areaこのエレメントは、レジスター保管域の開始を指します。

overflow_arg_areaこのポインターは、スタックに渡された引数を取得するために使用されます。スタックに渡された最初の引数がある場合は、そのアドレスで初期化され、スタック上の次の引数の先頭を指すように常に更新されます。

gp_offsetreg_save_areaこの要素は、次に使用可能な汎用引数レジスタが保存される場所までのオフセットをバイト単位で保持します。すべての引数レジスタが使い果たされた場合、値 48 (6 * 8) に設定されます。

fp_offsetreg_save_areaこの要素は、次に使用可能な浮動小数点引数レジスタが保存される場所までのオフセットをバイト単位で保持します。すべての引数レジスタが使い果たされた場合、値 304 (6 * 8 + 16 * 16) に設定されます。

于 2011-02-10T14:46:15.747 に答える
19

問題はgccがva_list配列型を作成することでした。私の機能は署名でした:

void foo(va_list ap);

apそして、別の関数へのポインターを渡したかったので、次のようにしました。

void foo(va_list ap)
{
    bar(&ap);
}

残念ながら、配列型は関数の引数リストのポインタ型に減衰するため、元の構造体にポインタを渡すのではなく、ポインタをポインタに渡していました。

この問題を回避するために、コードを次のように変更しました。

void foo(va_list ap)
{
    va_list ap2;
    va_copy(ap2, ap);
    bar(&ap2);
    va_end(ap2);
}

これは私が思いついた唯一のポータブルソリューションでva_listあり、配列型である可能性とそうでない可能性の両方を説明しています。

于 2011-02-10T15:40:57.123 に答える
0

i386 アーキテクチャでは、va_list はポインタ型です。ただし、AMD64 アーキテクチャでは配列型です。違いはなんですか?実際、ポインタ型に & 演算を適用すると、このポインタ変数のアドレスが取得されます。しかし、配列型に & 操作を何度適用しても、値は同じであり、この配列のアドレスと等しくなります。

では、AMD64 で何をすべきでしょうか? 関数で va_list の変数を渡す最も簡単な方法は、* または & 演算子なしで渡すことです。

例えば:

void foo(const char *fmt, ...) {
    va_list ap;
    int cnt;
    va_start(ap, fmt);
    bar(fmt, ap);
    va_end(ap);
    return cnt;
}
void bar(const char *fmt, va_list ap) {
    va_arg(ap, int);
    //do something
    test(ap);
}
void test(va_list ap) {
    va_arg(ap, int);
    //do something
}

それはうまくいきます!また、引数の数を気にする必要はありません。

于 2018-09-16T16:35:35.307 に答える