0

Given this code (minimal example; there's no deeper meaning in it):

#include <stdio.h>

int main() {
    start:
    asm volatile(
        ".code64\n\t"
        "push %rax\n\t"
        "pop %rax"
    );
    end:
    printf("%ld\n", (const char *)&&end - (const char *)&&start);
}

We obtain the assembler code by executing gcc -O3 -S dummy.c -o -. As one can see, these lines are included:

        subq    $8, %rsp
        .cfi_def_cfa_offset 16
#APP
# 5 "dummy.c" 1
        .code64
        push %rax
        pop %rax
# 0 "" 2
#NO_APP
        leaq    .L2(%rip), %rax

This actually means the original assembler code is still in the binary (which is expected).

But if the program is executed, then it outputs 0. Which basically means that the start label is equal to the end label. On the other side, if the program is compiled with -O0, it does output 2.

Why does gcc -O3 dummy.c && ./a.out output 0 even if it includes the assembler code?

Thanks

Ruslan
  • 18,162
  • 8
  • 67
  • 136
Kevin Meier
  • 2,339
  • 3
  • 25
  • 52
  • Thanks :)! That really seems to be the correct answer! – Kevin Meier Jul 06 '20 at 12:51
  • Does this really compile? Is using `&&` (twice unary address operator) legal? What would be the meaning of taking the address of the result of an operator? There is no object to take the address of?!? – Gerhardh Jul 06 '20 at 13:08
  • This is a GNU C extension: https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html – Kevin Meier Jul 06 '20 at 13:08
  • OK. Nice. I never saw this before. Thanks. – Gerhardh Jul 06 '20 at 13:12
  • 1
    @Ruslan Please consider posting this as an answer. – Steve Friedl Jul 06 '20 at 13:19
  • 1
    Why that insane asm template string? You switch to 64-bit code-gen if not already in it, breaking 32-bit builds. Then you clobber the red-zone below RSP by using stack instructions to save/restore RAX. ([Using base pointer register in C++ inline asm](https://stackoverflow.com/q/34520013)). If you just wanted dummy filler, `nop` would be the obvious choice. Or `lock addb $0, (%rsp)` is something else with no effect on the architectural state that you can use without any `clobber` declarations. – Peter Cordes Jul 07 '20 at 05:24

1 Answers1

4

See GCC bug 40078 and in particular comment 1 in it:

<...> labels can be moved if they are not used normally in the program. <...> Addresses of labels are only designed for computed gotos and any other use causes undefined behavior of their placement.

And a further comment suggested that asm goto feature of GCC (since version 4.5) may help the reporter.

Indeed, asm goto can help you fix positions of the labels. I guess asm goto is one of the "normal uses" of labels. See the following example. Note how I'm using asm goto as a separate statement, because the "useful" one following it has an output, which asm goto statements can't have.

#include <stdio.h>

int main()
{
start:
    asm goto(""::::start,end);

    unsigned random=0;
    asm volatile("rdrand %0\n" : "=r"(random));
end:
    printf("Random number: %u; label difference: %ld\n",
           random, (long)(&&end - &&start));
}

Play with it online!

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • `rdrand` should always use `asm volatile` just on general principle in case this it's copy/pasted into a loop or something. You don't want the compiler to CSE between multiple invocations of the asm statement. – Peter Cordes Jul 07 '20 at 05:27