JimEli
JimEli

Reputation: 125

How to prevent GCC warning that function has no return when inline assembly?

I'm using a bit of inline assembly to load a function return value into the eax register. However, GCC squawks about the function not returning a value if it is defined as such:

char *trim(char *s);

This generates the following 2 warnings:

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

Hence the use of the weak alias below. Is there a better method to prevent GCC from complaining about no return value from the _trim function? I tried to disable the appropriate compiler warning, but I didn’t have much luck with that.

Here is my code:

// 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...
}

Upvotes: 3

Views: 1213

Answers (1)

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215287

Your inline asm is just completely invalid.

  1. You cannot leave an inline asm block with the stack pointer different from what it was when you entered. If you do, any subsequent access to the stack by the compiler will be wrong and all hell will break loose. You can't just "fix it in a later asm block" because you have no way to guarantee that the compiler makes no access to the stack in between. In your own example, you call isdigit in between, making a function call without respecting the function call ABI (that the stack must be aligned mod 16 at function call).

  2. Loading a value into eax (or whatever the ABI's return-value register is) in a particular asm block does not return that value from the function. All it does is clobber a register that you told the compiler you would not clobber (you didn't include it in the clobber list for the asm block), thereby creating another reason all hell could break loose. If the compiler had some important value kept in that register (for example, the stack canary for ssp), and it gets a different value when reading it back, anything could happen. Even ignoring that breakage, there's no reason to think the value you put in eax will still be there when the function returns, or will serve as the function's return value. The compiler may load something else there before it actually returns, or make a transformation (inlining, or various inter-procedural optimizations based on having access to the definition of the function) such that the caller obtains the return value in some other way than what it would use when having made an external call to an unknown function matching the ABI.

The way you fix your code (minimizing changes in style) is:

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;
}

Upvotes: 3

Related Questions