编程参考 - va_list的定义问题

遇到一个问题,在调用函数时出现错误。

有两个函数声明:

test.cpp



#include <stdarg.h>

#include <syslog.h>



void funcA( const char* format, ...);  // No.1 func

void funcA( const char* format, va_list args_list);  // No.2 func



int main()

{

  int a = 2;

  const char* str = "Hello";

  char buf[10];

  strcpy(buf, "test");



  funcA("Call funcA: %d.", a);    // Call the No.1 func

  funcA("Call funcA: %s.", str);  // Call the No.1 func 

  funcA("Call funcA: %s.", buf); // Call the No.2 func in mips-linux toolchain.



  return 0;

}



void funcA( const char* format, ...)

{

    va_list args_list;

    va_start(args_list, format);



    funcA(format, args_list);

    va_end(args_list);

}



void funcA( const char* format, va_list args_list)

{

    vsyslog(args_list);

}

编译上面的test.cpp文件,使用arm-linux toolchain就没有问题,都会调用到No.1函数。

但当使用mips-linux toolchain时,第三个就会调用到No.2函数,这种情况仅发生在format后面只有一个参数,且是char*变量时。

然后在调用到vsyslog这个系统函数时,程序就会卡死。

编译器在根据参数列表选择要调用的函数时,第一个参数两个函数都匹配,而第二个的话,char*就匹配到了va_list,优先于省略参数。

后面没有参数了,就选择了No.2函数进行调用。而从设计角度讲,不应该出现这样的两个函数 原型同时开发给外部,本就容易引起误解。

那为什么会如此匹配呢?

打开GCC工程的源码,来寻找一下va_list的定义。

会发现,这个类型的定义虽然由头文件stdarg.h提供,是C标准,但实际定义却是根据编译器、操作系统和ABI所决定的。在不同平台下,使用的定义也会不同。可能是个结构体,或者是个指针,或则是编译器内部实现的黑科技(black magic)。

1,#include <stdarg.h>

在gcc的工程里,有多个此同名文件。选取的是\gcc\ginclude\stdarg.h

2,在里面找到定义:

typedef __builtin_va_list __gnuc_va_list;

typedef __gnuc_va_list va_list;

3, 下载gcc的源码,在里面找到文件:gcc\config\mips\mips.cc

找到下面函数:static tree mips_build_builtin_va_list (void)

4, 就是这个函数构造了va_list类型。

在gcc工程里查找,mips架构的定义在mips.cc文件里,这段代码看不懂,但如果是类型判断引起的问题,就是MIPS架构下的这个va_list定义,以及mips编译工具链的判断造成了上面的问题。感觉走的是下面的分支,是void*的类型。

static tree

mips_build_builtin_va_list (void)

{

  if (EABI_FLOAT_VARARGS_P)

    {

      /* We keep 3 pointers, and two offsets.



     Two pointers are to the overflow area, which starts at the CFA.

     One of these is constant, for addressing into the GPR save area

     below it.  The other is advanced up the stack through the

     overflow region.



     The third pointer is to the bottom of the GPR save area.

     Since the FPR save area is just below it, we can address

     FPR slots off this pointer.



     We also keep two one-byte offsets, which are to be subtracted

     from the constant pointers to yield addresses in the GPR and

     FPR save areas.  These are downcounted as float or non-float

     arguments are used, and when they get to zero, the argument

     must be obtained from the overflow region.  */

      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, f_res, record;

      tree array, index;



      record = lang_hooks.types.make_type (RECORD_TYPE);





      f_ovfl = build_decl (BUILTINS_LOCATION,

               FIELD_DECL, get_identifier ("__overflow_argptr"),

               ptr_type_node);

      f_gtop = build_decl (BUILTINS_LOCATION,

               FIELD_DECL, get_identifier ("__gpr_top"),

               ptr_type_node);

      f_ftop = build_decl (BUILTINS_LOCATION,

               FIELD_DECL, get_identifier ("__fpr_top"),

               ptr_type_node);

      f_goff = build_decl (BUILTINS_LOCATION,

               FIELD_DECL, get_identifier ("__gpr_offset"),

               unsigned_char_type_node);

      f_foff = build_decl (BUILTINS_LOCATION,

               FIELD_DECL, get_identifier ("__fpr_offset"),

               unsigned_char_type_node);

      /* Explicitly pad to the size of a pointer, so that -Wpadded won't

     warn on every user file.  */

      index = build_int_cst (NULL_TREE, GET_MODE_SIZE (ptr_mode) - 2 - 1);

      array = build_array_type (unsigned_char_type_node,

                    build_index_type (index));

      f_res = build_decl (BUILTINS_LOCATION,

              FIELD_DECL, get_identifier ("__reserved"), array);



      DECL_FIELD_CONTEXT (f_ovfl) = record;

      DECL_FIELD_CONTEXT (f_gtop) = record;

      DECL_FIELD_CONTEXT (f_ftop) = record;

      DECL_FIELD_CONTEXT (f_goff) = record;

      DECL_FIELD_CONTEXT (f_foff) = record;

      DECL_FIELD_CONTEXT (f_res) = record;



      TYPE_FIELDS (record) = f_ovfl;

      DECL_CHAIN (f_ovfl) = f_gtop;

      DECL_CHAIN (f_gtop) = f_ftop;

      DECL_CHAIN (f_ftop) = f_goff;

      DECL_CHAIN (f_goff) = f_foff;

      DECL_CHAIN (f_foff) = f_res;



      layout_type (record);

      return record;

    }

  else

    /* Otherwise, we use 'void *'.  */

    return ptr_type_node;

}

gcc中的arm.cc和aarch64.cc里面有各自的va_list的定义。

arm架构的定义注释说明,基于AAPCS ABI:

/* AAPCS \S 7.1.4 requires that va_list be a typedef for a type

defined as:

struct __va_list

{

void *__ap;

};

The C Library ABI further reinforces this definition in \S

4.1.

We must follow this definition exactly. The structure tag

name is visible in C++ mangled names, and thus forms a part

of the ABI. The field name may be used by people who

#include <stdarg.h>. */

aarch64的target的话,基于AAPCS64 ABI:

/* Implement TARGET_BUILD_BUILTIN_VA_LIST.

Return the type to use as __builtin_va_list.

AAPCS64 \S 7.1.4 requires that va_list be a typedef for a type defined as:

struct __va_list

{

void *__stack;

void *__gr_top;

void *__vr_top;

int __gr_offs;

int __vr_offs;

}; */

在某个目标环境中,如果没有实现指定的va_list的定义,则gcc认为是void*类型,在builtins.cc里定义:

/* The "standard" definition of va_list is void*. */



tree

std_build_builtin_va_list (void)

{

  return ptr_type_node;

}

参考:

c - Which is the definition of va_list? - Stack Overflow

https://www.quora.com/What-exactly-is-type-va_list-in-Unix

c - What is the underlying mechanism behind va_list and where is it defined? - Software Engineering Stack Exchange

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值