Labels in GCC inline assembly
Asked Answered
B

2

48

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...?

Byte answered 10/10, 2010 at 0:17 Comment(0)
O
60

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).

Oosphere answered 10/10, 2010 at 0:24 Comment(2)
@MichaelGraczyk Local labels are not an x86-specific feature. GAS supports them regardless of CPU or object file format, and so do nearly all other Unixy assemblers (as in, I've never seen one that didn't, even in 1995).Freund
Indeed and jmp 1 would be considered a jump to location 1, thus the segfault.Masters
F
40

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");
Fassold answered 19/4, 2013 at 1:24 Comment(4)
Where is this "%=" trick documented?Clutch
Found it. Documentation can be found in section "6.47.2.2 Assembler Template" here: gcc.gnu.org/onlinedocs/gcc/Extended-Asm.htmlClutch
I'd probably link to this instead.Fassold
You don't need fake operands, you can just asm ("..." : ), notice that :. Also, %= is not unique in the whole compilation, it's unique per compilation unit. You cant't use this at toplevel though (outside any function or method), because such asm cannot have operands, In that case you have to resort to numeric labels.Gorlovka

© 2022 - 2024 — McMap. All rights reserved.