使用Clang和g ++上的打包参数指针创建`va_list` [英] Creating a `va_list` Using a Pointer of Packed Arguments on Clang and g++

查看:69
本文介绍了使用Clang和g ++上的打包参数指针创建`va_list`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为研究架构开发周期精确的模拟器。我已经有一个交叉编译器,可以生成程序集(基于MIPS)。为了进行调试,我们有一个 printf 内在函数,当在模拟器中运行该函数时,它最终会调用一个内置方法,该方法可以访问包装在连续数组中的参数列表(例如

I am working on a cycle-accurate simulator for a research architecture. I already have a cross-compiler that generates assembly (based on MIPS). For debug purposes, we have a printf intrinsic which ultimately, when run in the simulator, calls a builtin method that has access to a list of arguments packed in a contiguous array (such as would be created by this code):

template <typename type> inline static void insert(char buffer[], size_t* i, type value) {
    memcpy(buffer+*i,&value, sizeof(type)); *i+=sizeof(type);
}
int main(int /*argc*/, char* /*argv*/[]) {
    char buffer[512]; size_t i=0;
    insert<double>(buffer,&i, 3.14);
    insert<int>(buffer,&i, 12345);
    insert<char const*>(buffer,&i, "Hello world!");

    return 0;
}

在MSVC中,可以创建 va_list 并按以下方式调用 vprintf

In MSVC, one can then create a va_list and call vprintf like so:

union { va_list list; char* arguments; } un;
un.arguments = buffer;
vprintf(format_string, un.list);

目标体系结构是x86-64,它基于x86,因此产生的结果显然正确( MSVC提供的 va_list 只是 char * )的typedef。

The target architecture is x86-64, which is based on x86, so this produces apparently correct results (the va_list provided by MSVC is just a typedef for char*).

但是,在g ++(大概是Clang;我还没有尝试过)上,代码出现了段错误。发生这种情况是因为基础类型(由编译器提供:在gcc 4.9.2中,它似乎是从 __ gnuc_va_list 进行类型定义的,而后者又是从 __ builtin_va_list ,大概是编译器的内在函数)是不同的(由于编译器错误,您只需进入 un.list = buffer; 禁止)。

However, on g++ (and presumably Clang; I haven't tried), the code segfaults. This happens because the underlying type (it's compiler-provided: in gcc 4.9.2, it appears to be typedefed from __gnuc_va_list, which is in turn typedefed from __builtin_va_list, presumably a compiler intrinsic) is different (as the compiler error you get it you just go un.list=buffer; forbodes).

我的问题是:将这一系列打包参数转换为的最干净方法是什么? va_list 在x86-64模式下g ++和Clang都可以使用吗?

My question is: what is the cleanest way to convert this array of packed arguments into a va_list that is usable by both g++ and Clang in x86-64 mode?

我目前的想法是最好分别解析每个格式说明符,然后将其与相应的参数一起转发给 printf 。这并不那么健壮(就支持 printf 的所有功能;在单一体系结构上工作仅足以满足我们的目的),也不是特别引人注目,

My current thinking is that it may be better to parse out each format specifier individually, then forward it off with the appropriate argument to printf. This isn't as robust (in the sense of supporting all features of printf; working on a single architecture only is robust enough for our purposes), nor is it particularly compelling, though.

推荐答案

对于基线答案,下面是一些简单的代码(经过合理测试,但没有保证),可以实现解析我提到的-format-string方法。我将其发布到了公共领域。

For a baseline answer, here is some simple code (reasonably well tested, but no guarantees) that implements the parse-the-format-string method I mentioned. I release it into the public domain.

如果有人写出的答案可以真正解决我所问的问题(这样做,但是要使用 va_list

If someone writes an answer that actually solves the problem I asked (doing this, but using va_list; i.e., a much cleaner solution) then I will accept that answer instead.

static void printf_buffer(char const*__restrict format_string, char*__restrict argument_buffer) {
    int num_chars = 0;
    PARSE_CHAR:
        switch (*format_string) {
            case '\0': return;
            case '%': {
                int i = 1;
                char c;
                PARSE_SPECIFIER:
                    c = format_string[i++];
                    switch (c) {
                        case 'd': case 'i':
                        case 'u': case 'o': case 'x': case 'X':
                        case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A':
                        case 'c': case 's': case 'p':
                            goto PRINT_SPECIFIER;
                        case 'n':
                            assert(i==2,"\"%%n\" must contain no intermediary characters!");
                            **reinterpret_cast<int**>(argument_buffer) = num_chars;
                            argument_buffer += sizeof(int*);
                            goto DONE_SPECIFIER;
                        case '%':
                            assert(i==2,"\"%%%%\" must contain no intermediary characters!");
                            putchar('%'); ++num_chars;
                            goto DONE_SPECIFIER;
                        case '\0': assert(false,"Expected specifier before end of string!");
                        default: goto PARSE_SPECIFIER;
                    }
                PRINT_SPECIFIER: {
                    char* temp = new char[i+1];
                    strncpy(temp,format_string,i); temp[i]='\0';
                    #define PRINTBRK(TYPE) num_chars+=printf(temp,*reinterpret_cast<TYPE*>(argument_buffer)); argument_buffer+=sizeof(TYPE); break;
                    switch (c) {
                        case 'd': case 'i': PRINTBRK(int)
                        case 'u': case 'o': case 'x': case 'X': PRINTBRK(unsigned int)
                        case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': PRINTBRK(double)
                        case 'c': PRINTBRK(char)
                        case 's': PRINTBRK(char const*)
                        case 'p': PRINTBRK(void*)
                        default: assert(false,"Implementation error!");
                    }
                    #undef PRINTBRK
                    delete [] temp;
                }
                DONE_SPECIFIER:
                    format_string += i;
                    break;
            }
            default:
                putchar(*format_string); ++format_string; ++num_chars;
                break;
        }
        goto PARSE_CHAR;
}

此处是完整源代码的链接,包括封闭测试:链接。预期输出:

Here is a link to the full source, including an enclosing test: link. Expected output:

double: 3.1400, float: +3.1400, getting characters: ->, percent: %, int:      12345, string: "Hello world!"
Printed 54 characters before the marked point:
                                                      <-

这篇关于使用Clang和g ++上的打包参数指针创建`va_list`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆