8

我写了一个使用 va_list/va_arg/va_start/va_end/va_arg 的 printf myselef。

typedef char *va_list;
#define  _AUPBND                (sizeof (acpi_native_int) - 1)
#define  _ADNBND                (sizeof (acpi_native_int) - 1)
#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)           (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)              (void) 0
#define va_start(ap, A)         (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

首先,我从 linux 内核复制这些宏,printf 可以正确打印 32 位整数,但不能打印 64 位整数,打印 double/float 可能会失败或崩溃。然后我检查代码,我猜 va_* 可能有错误,所以我使用 __builtin_va_* 而不是内核的 va_*。

typedef __builtin_va_list va_list;
#define va_start(v,l)   __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)

但是 gcc 提示“未定义对 `abort' 的引用”,所以我写了一个空的 abort() 并且 myprintf 工作正常。我的问题是:

  1. 为什么 linux 内核va_list/va_arg/va_start/va_end/va_arg不能用于printf64 位整数和双精度/浮点数?
  2. 当我使用__builtin_va_start/__builtin_va_arg/__builtin_va_end/__builtin_va_list时,为什么 gcc 提示“未定义对abort'"? But I can not find the definition of__builtin_va_* 的引用,它们的定义在哪里?
4

2 回答 2

6

不要从 Linux 头文件中剪切和粘贴内容。相反,将其放在源文件的顶部:

#include <stdarg.h>

这将为您提供使用va_list和所需的一切va_arg。但是,它不会引入printf或任何标准 I/O 东西(位于<stdio.h>.

于 2013-06-20T21:53:59.927 回答
1

如果使用类型参数调用gcc__builtin_va_arg()显然会调用abort()(至少在某些平台或情况下),它不能在...函数调用的参数列表部分中传递。

例如,由于促销 a charorfloat传递,这样的参数将被提升为intor double。访问这些参数作为va_arg(ap,char)va_arg(ap,float)未定义的行为和 gcc 可能会abort()在这种情况下调用 - 或者它可能会做其他事情(我的 MinGW 编译器将执行无效指令以导致崩溃)。

编译时你可能会看到如下内容:

In file included from D:\temp\test.c:2:0:
D:\temp\test.c: In function 'foo':
D:\temp\test.c:12:16: warning: 'char' is promoted to 'int' when passed through '...' [enabled by default]
  c = va_arg(ap,char);
                ^
D:\temp\test.c:12:16: note: (so you should pass 'int' not 'char' to 'va_arg')
D:\temp\test.c:12:16: note: if this code is reached, the program will abort

的“定义”__builtin_va_*被编译到编译器中(这就是为什么“内置”是名称的一部分)。

至于用于 varargs 访问的 Linux 宏:虽然您从 linux 内核头文件中获取的定义确实存在于 include/acpi/platform/acenv.h 中,但如果您仔细查看有效的条件编译,您会发现那些构建 linux 内核时不使用宏。我不确定这些宏何时生效,但它们不适用于 x64/x86-64/amd64 构建,因为该平台上的 ABI 并不完全基于堆栈。详见《System V Application Binary Interface - AMD64 Architecture Processor Supplement》第3.5.6节。

于 2013-06-20T23:18:22.100 回答