In my ongoing experimentation with GCC inline assembly, I've run into a new problem regarding labels and inlined code.
Consider the following simple jump:
__asm__
(
"jmp out;"
"out:;"
:
:
);
This does nothing except jump to the out
label. As is, this code compiles fine. But if you place it inside a function, and then compile with optimization flags, the compiler complains: "Error: symbol 'out' is already defined".
What seems to be happening is that the compiler is repeating this assembly code every time it inlines the function. This causes the label out
to get duplicated, leading to multiple out
labels.
So, how do I work around this? Is it really not possible to use labels in inline assembly? This tutorial on GCC inline assembly mentions that:
Thus, you can make put your assembly into CPP macros, and inline C functions, so anyone can use it in as any C function/macro. Inline functions resemble macros very much, but are sometimes cleaner to use. Beware that in all those cases, code will be duplicated, so only local labels (of 1: style) should be defined in that asm code.
I tried to find more information about these "local labels", but can't seem to find anything relating to inline assembly. It looks like the tutorial is saying that a local label is a number followed by a colon, (like 1:
), so I tried using a label like that. Interestingly, the code compiled, but at run time it simply triggered a Segmentation Fault. Hmm...
So any suggestions, hints, answers...?
GCC provides two forms of inline asm statements. A basic asm statement is one with no operands (see Basic Asm), while an extended asm statement (see Extended Asm) includes one or more operands.
The “r” in the operand constraint string indicates that the operand must be located in a register. The “=” indicates that the operand is written. Each output operand must have “=” in its constraint.
Clobbers. A comma-separated list of registers or other values changed by the AssemblerTemplate , beyond those listed as outputs. An empty list is permitted.
Volatile is used to force it to read a fresh timestamp. When used alone, like this: __asm__ __volatile__ ("") It will not actually execute anything. You can extend this, though, to get a compile-time memory barrier that won't allow reordering any memory access instructions: __asm__ __volatile__ ("":::"memory")
A declaration of a local label is indeed a number followed by a colon. But a reference to a local label needs a suffix of f
or b
, depending on whether you want to look forwards or backwards - i.e. 1f
refers to the next 1:
label in the forwards direction.
So declaring the label as 1:
is correct; but to reference it, you need to say jmp 1f
(because you are jumping forwards in this case).
Well, this question isn't getting any younger, but there are two other interesting solutions.
1) This example uses %=. %= in an assembler template is replaced with a number that is "unique to each insn in the entire compilation. This is useful for making local labels that are referred to more than once in a given insn." Note that to use %=, you (apparently) must have at least one input (although you probably don't have to actually use it).
int a = 3; asm ( "test %0\n\t" "jnz to_here%=\n\t" "jz to_there%=\n\t" "to_here%=:\n\t" "to_there%=:" ::"r" (a));
This outputs:
test %eax jnz to_here14 jz to_there14 to_here14: to_there14:
Alternately, you can use the asm goto (Added in v4.5 I think). This actually lets you jump to c labels instead of just asm labels:
asm goto ("jmp %l0\n" : /* no output */ : /* no input */ : /* no clobber */ : gofurther); printf("Didn't jump\n"); // c label: gofurther: printf("Jumped\n");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With