我正在使用一些内联汇编来将函数返回值加载到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...
}
答案 0 :(得分:4)
你的内联asm完全无效。
您不能保留内联asm块,其堆栈指针与您输入时的指针不同。如果你这样做,编译器对堆栈的任何后续访问将是错误的,并且所有地狱都会破裂。您不能只是“在以后的asm块中修复它”,因为您无法保证编译器无法访问它们之间的堆栈。在你自己的例子中,你在中间调用isdigit
,进行函数调用而不考虑函数调用ABI(函数调用时堆栈必须与mod 16对齐)。
在特定的asm块中将值加载到eax
(或ABI的返回值寄存器中的任何内容)不会从函数返回该值。它所做的只是破坏了一个寄存器,你告诉编译器你不会破坏(你没有把它包含在asm块的clobber列表中),从而创造了另一个原因所有地狱都可以破解。如果编译器在该寄存器中保留了一些重要值(例如,ssp的堆栈canary),并且在读取它时获得不同的值,则可能发生任何事情。即使忽略了这种破坏,也没有理由认为你在eax
中放置的值在函数返回时仍然存在,或者将作为函数的返回值。编译器可以在实际返回之前加载其他内容,或者进行转换(内联或基于对函数定义的访问的各种过程间优化),以便调用者以某种其他方式获取返回值。它会在对与ABI匹配的未知函数进行外部调用时使用。
修改代码的方式(最小化样式更改)是:
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;
}