海湾合作委员会是处理不当的指针传递给函数的va_list? [英] Is GCC mishandling a pointer to a va_list passed to a function?
问题描述
问题'通行证va_list的或指针va_list的?一>具有引用标准答案(ISO / IEC 9899:1999 - 7.15节可变参数< STDARG.H>
,脚注212)为明确地说:
The question 'Pass va_list or pointer to va_list?' has an answer which quotes the standard (ISO/IEC 9899:1999 - §7.15 'Variable arguments <stdarg.h>
, footnote 212) as explicitly saying that:
有允许创建一个指针指向一个 va_list的
和指针传递给另一个功能,在这种情况下,原有的功能可能使后继续使用的原始列表的其他函数返回。
It is permitted to create a pointer to a
va_list
and pass that pointer to another function, in which case the original function may make further use of the original list after the other function returns.
我编译一些code,可以通过以下举例说明(真正的code是非常复杂得多,与原来的功能做了很多比这里显示更多的工作)。
I'm compiling some code which can be exemplified by the following (the real code is very considerably more complex, with the original functions doing a lot more work than shown here).
#include <stdarg.h>
#include <stdio.h>
static void test_ptr(const char *fmt, va_list *argp)
{
int x;
x = va_arg(*argp, int);
printf(fmt, x);
}
static void test_val(const char *fmt, va_list args)
{
test_ptr(fmt, &args);
}
static void test(const char *fmt, ...)
{
va_list args;
va_start(args, fmt); /* First use */
test_val(fmt, args);
va_end(args);
va_start(args, fmt); /* Second use */
test_ptr(fmt, &args);
va_end(args);
}
int main(void)
{
test("%d", 3);
return 0;
}
错误消息
当我(用GCC 4.1.2或4.5.1 RHEL5)编译它,我得到以下错误消息。请注意如何更翔实的4.5.1错误消息是 - 海湾合作委员会球队上改进到会祝贺
Error messages
When I compile it (on RHEL5 with GCC 4.1.2 or 4.5.1), I get the following error messages. Notice how much more informative the 4.5.1 error message is - the GCC team is to be congratulated on the improvement!
$ gcc --version
gcc (GCC) 4.5.1
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ /usr/bin/gcc --version
gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc -c vap.c
vap.c: In function ‘test_val’:
vap.c:13:5: warning: passing argument 2 of ‘test_ptr’ from incompatible pointer type
vap.c:4:13: note: expected ‘struct __va_list_tag (*)[1]’ but argument is of type ‘struct __va_list_tag **’
$ /usr/bin/gcc -c vap.c
vap.c: In function ‘test_val’:
vap.c:13: warning: passing argument 2 of ‘test_ptr’ from incompatible pointer type
$
我得到在MacOS X Lion中相同的消息与GCC / LLVM 4.2.1和4.6.1 GCC:
I get the same messages on MacOS X Lion with GCC/LLVM 4.2.1 and with GCC 4.6.1:
$ /usr/bin/gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc --version
gcc (GCC) 4.6.1
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$
问题
-
有人能清楚地说明为什么在
为test_val()
功能无法通过的va_list
作为参数传递test_ptr()
,而测试()
函数(创建的va_list
)可以吗?Questions
Can someone articulate why the
test_val()
function cannot pass theva_list
passed as an argument totest_ptr()
, whereas thetest()
function (which created theva_list
) can?是gcc正确的抱怨
()的指针间接传递为test_val
?在这两种情况下,我可以看到模糊的答案,但我不能简洁地描述它。我认为,在
为test_val的code()
被滥用的va_list
,这是很好的code将不会编译 - 但我想,以确保之前,我去修复它。On both cases, I can see an answer fuzzily, but I can't describe it succinctly. I think that the code in
test_val()
is abusing theva_list
and it is good that the code won't compile - but I'd like to be sure before I go fixing it.我去这个星期要处理的问题code。更改前,我去找那里的恶棍函数用于&MDASH;他们都没有!所以,我通过删除功能(4外部可见,但是未使用的,加2,其中载有问题code静态的)解决了我的编译错误的问题。这是不是必须制定出如何处理的烂摊子要简单得多。 (这也解释了为什么从来就没有引起code运行时问题的任何证据。)
I went to deal with the problematic code this week. Before making changes, I went to find where the miscreant functions are used — and they aren't! So, I solved my compilation error problem by removing the functions (4 externally visible but unused ones, plus 2 static ones which contained the problematic code). That was much simpler than having to work out how to deal with the mess. (This also explains why there was never any evidence of a run-time problem caused by the code.)
推荐答案
这是一个已知的问题。在某些体系结构(特别是X86-64),
的va_list
需要比一个简单的指针栈更复杂,例如,由于某些参数可能在寄存器或缩小传递-of带以其他方式(请参阅this回答为的va_list
的定义上的x86-64)。This is a known problem. On some architectures (in particular x86-64),
va_list
needs to be more complex than a simple pointer to the stack, for example because some arguments might be passed in registers or out-of-band in some other way (see this answer for the definition ofva_list
on x86-64).在这种架构中,是很常见的让
的va_list
数组类型,这样类型的参数的va_list
将调节到指针类型,而不是整个结构,只需要通过一个单一的指针。On such architectures, it is common to make
va_list
an array type so that parameters of typeva_list
will be adjusted to pointer types, and instead of the whole structure, only a single pointer needs to be passed.这应该不违反C标准,只说
的va_list
必须是一个完整的对象类型,甚至明确占的事实,传递va_list的
参数可能没有实际复制必要的状态:的va_list
如果作为参数传递和消费中被调用的函数对象有不确定的值This should not violate the C standard, which only says that
va_list
must be a complete object type and even explicitly accounts for the fact that passing ava_list
argument might not actually clone the necessary state:va_list
objects have indeterminate value if they are passed as arguments and consumed in the called function.但即使使
的va_list
数组类型是合法的,它仍然会导致您遇到的问题:由于参数类型为的va_list
有错误的类型,例如:结构__va_list_tag *
而不是结构__va_list_tag [1]
,它将案件炸毁的地方之间数组和指针事的区别。But even if making
va_list
an array type is legal, it still leads to the problems you experienced: As parameters of typeva_list
have the 'wrong' type, egstruct __va_list_tag *
instead ofstruct __va_list_tag [1]
, it will blow up in cases where the difference between arrays and pointers matter.真正的问题是不是类型不匹配GCC发出警告,但通过指针,而不是按值参数传递的语义:
&功放;在args
为test_val()
指向中间指针变量代替的va_list
对象;无视警告意味着,你会调用在va_arg()
在test_ptr()
上的指针变量,它应该返回垃圾(或段错误,如果你幸运的话),并破坏堆栈。The real problem is not the type mismatch gcc warns about, but the by-pointer instead of by-value argument passing semantics:
&args
intest_val()
points to the intermediate pointer variable instead of theva_list
object; ignoring the warning means that you'll invokeva_arg()
intest_ptr()
on the pointer variable, which should return garbage (or segfault if you're lucky) and corrupt the stack.一个解决方法是在一个结构来包装你的
的va_list
,并通过周围来代替。另一种解决方案<一href=\"http://groups.google.com/group/jansson-users/browse_thread/thread/09b467e583aa4059/4a409ee27a2ddea1\">I've可见在野外,甚至<一个href=\"http://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure/4959184#4959184\">here在SO ,就是用va_copy
以创建参数的本地副本,然后将指针传递到:One workaround is to wrap your
va_list
in a structure and pass that around instead. Another solution I've seen in the wild, even here on SO, is to useva_copy
to create a local copy of the argument and then pass a pointer to that:static void test_val(const char *fmt, va_list args) { va_list args_copy; va_copy(args_copy, args); test_ptr(fmt, &args_copy); va_end(args_copy); }
的这应该在实际工作中,但在技术上它可能还是取决于标准的跨pretation可能没有未定义行为:的
如果
va_copy()
实现为一个宏,没有参数调整完成,这可能关系的ARGS
的类型是不是的va_list
。然而,因为它的未指定的是否va_copy()
是宏或功能,人们可能会认为,它至少的可以青霉>是一个函数和参数调整中为宏给定原型隐含地假定。这可能是一个好主意,要求澄清的官员甚至提交缺陷报告。If
va_copy()
is implemented as a macro, no parameter adjustments are performed, and it might matter thatargs
is not of typeva_list
. However, as it is unspecified whetherva_copy()
is a macro or a function, one might argue that it at least could be a function and parameter adjustments are implicitly assumed in the prototype given for the macro. It might be a good idea to ask the officials for clarification or even file a defect report.您也可以使用您的构建系统来处理这一问题通过定义如
HAVE_VA_LIST_AS_ARRAY
配置标记,以便您可以为您的特定架构做正确的事情:You could also use your build system to deal with the issue by defining a configuration flag like
HAVE_VA_LIST_AS_ARRAY
so you can do the right thing for your particular architecture:#ifdef HAVE_VA_LIST_AS_ARRAY #define MAKE_POINTER_FROM_VA_LIST_ARG(arg) ((va_list *)(arg)) #else #define MAKE_POINTER_FROM_VA_LIST_ARG(arg) (&(arg)) #endif static void test_val(const char *fmt, va_list args) { test_ptr(fmt, MAKE_POINTER_FROM_VA_LIST_ARG(args)); }
这篇关于海湾合作委员会是处理不当的指针传递给函数的va_list?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!