内联汇编时如何防止GCC警告功能无效?

时间:2017-10-25 17:51:12

标签: c gcc x86 inline-assembly

我正在使用一些内联汇编来将函数返回值加载到eax寄存器中。但是,如果定义如下,GCC会发出关于函数不返回值的说法:

char *trim(char *s);

这会产生以下2个警告:

control reaches end of non-void function [-Wreturn-type]
No return, in function returning non-void

因此使用下面的弱别名。有没有更好的方法来阻止GCC抱怨_trim函数没有返回值?我试图禁用相应的编译器警告,但我没有太多运气。

这是我的代码:

// Trim all but digits from the string, return start of string.
void _trim(char *s) {
    char *d;

    // Save start of string for function return and set d=s.
    asm volatile (
        "mov %1, %0 \n" // Set d = s.
        "push %1"       // Save start of string for function return.
        : "=r" (d) : "r" (s)
    );
    // Ignore everything but digits...
    while (*s != '\0') {
        if (isdigit(*s))
            *d++ = *s;
        s++;
    }
    *d = '\0'; // Terminate string.
    asm volatile ( "pop %eax" ); // Retrieve beginning of string.
}

// Define weak alias to prevent gcc from squawking about no return value.
char *trim(char *) __attribute__ ((weak, alias ("_trim")));
#endif

int main(void) {
    char line[80];

// ...
    if (strlen(trim(line)) == 8)
// Do something...
}

1 个答案:

答案 0 :(得分:4)

你的内联asm完全无效。

  1. 您不能保留内联asm块,其堆栈指针与您输入时的指针不同。如果你这样做,编译器对堆栈的任何后续访问将是错误的,并且所有地狱都会破裂。您不能只是“在以后的asm块中修复它”,因为您无法保证编译器无法访问它们之间的堆栈。在你自己的例子中,你在中间调用isdigit,进行函数调用而不考虑函数调用ABI(函数调用时堆栈必须与mod 16对齐)。

  2. 在特定的asm块中将值加载到eax(或ABI的返回值寄存器中的任何内容)不会从函数返回该值。它所做的只是破坏了一个寄存器,你告诉编译器你不会破坏(你没有把它包含在asm块的clobber列表中),从而创造了另一个原因所有地狱都可以破解。如果编译器在该寄存器中保留了一些重要值(例如,ssp的堆栈canary),并且在读取它时获得不同的值,则可能发生任何事情。即使忽略了这种破坏,也没有理由认为你在eax中放置的值在函数返回时仍然存在,或者将作为函数的返回值。编译器可以在实际返回之前加载其他内容,或者进行转换(内联或基于对函数定义的访问的各种过程间优化),以便调用者以某种其他方式获取返回值。它会在对与ABI匹配的未知函数进行外部调用时使用。

  3. 修改代码的方式(最小化样式更改)是:

    char *trim(char *s) {
        char *d, *d0;
    
        d0 = d = s;
    
        // Ignore everything but digits...
        while (*s != '\0') {
            if (isdigit(*s))
                *d++ = *s;
            s++;
        }
        *d = '\0'; // Terminate string.
    
        return d0;
    }