11

Edit: Thanks to @NateEldredge, I better defined my question in How to 'tag' a location in a C source file for a later breakpoint definition?


I use those labels to setup breakpoints in gdb. So no matter if I add/remove lines of code after/before the label, the breakpoint is still correct.

If I add -Wno-error=unused-label to the compilation options, the compiler does not yell at me, but the label disappears from the assembly.

If instead, I use __attribute__((unused)) in the code, the result is the same: no complain, but the label is gone.

Is there a correct way of getting this done (instead of just a hack)?

Here is my toy example:

int main(void){
    int a = 15;
 label: __attribute__((unused))
    a = a + 23;
    return a;
}

After compilation, it results in:

main:
        push    ebp
        mov     ebp, esp
        sub     esp, 16
        mov     DWORD PTR [ebp-4], 15
        add     DWORD PTR [ebp-4], 23
        mov     eax, DWORD PTR [ebp-4]
        leave
        ret

Here an interactive version of the same example: https://godbolt.org/z/zTqd9bM6q


$ gcc --version
gcc (GCC) 10.3.1 20210422 (Red Hat 10.3.1-1)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
onlycparra
  • 607
  • 4
  • 22
  • 3
    You could `goto` right before the label. – dbush Nov 23 '21 at 19:45
  • 6
    How about `__asm__("label:");` – Jester Nov 23 '21 at 19:45
  • 1
    This could be a good question. But: your example code should be presented **here** , IMHO. – wildplasser Nov 23 '21 at 19:47
  • 1
    `__attribute__((unused))` is unrelated to what you are trying to do. It is merely suppressing the "unused" warning. – Eugene Sh. Nov 23 '21 at 19:57
  • 2
    Since this is only for debugging, you could put uses of the labels inside statements that are conditionally compiled (inside something like `#if DEBUG … #endif`), such as `volatile int x; switch(x) { case 0: goto foo; case 1: goto bar; … }`. – Eric Postpischil Nov 23 '21 at 20:00
  • thanks guys, this is very educational. `__asm__("label:");` works as expected. Beautiful. Thanks. @Jester, If you could please post an answer based on your comment, I would gladly mark it as a correct answer. | **Edit:** It seems that there are some issues with this approach, as @EricPostpischil mentions – onlycparra Nov 23 '21 at 20:00
  • 3
    Be aware that `__asm__("label:")` can fail. During optimization, the compiler might decide to duplicate code, resulting in the label appear twice in assembly code, which will usually cause compilation to fail. Or it might be removed when the compiler determines execution can never pass through it (although then putting a breakpoint there would be useless anyway, as it would never be hit). – Eric Postpischil Nov 23 '21 at 20:02
  • @EricPostpischil, that is quite interesting. I see what you mean with the second point. How can the first point be mitigated? – onlycparra Nov 23 '21 at 20:03
  • @EricPostpischil, on the `#ifdef DEBUG ... #endif` approach. I cannot achieve the desired output [godbolt.org](https://godbolt.org/z/85eGGc99K). Also, should I define/undefine `DEBUG` from the C file, or that can be done in the Makefile? – onlycparra Nov 23 '21 at 20:20
  • @EricPostpischil, regarding the `__asm__("label:")` approach; I am compiling with `-O0` or `-Og`. Do you know if the compiler would perform those pernicious optimizations if I am asking to gcc not to touch my code too much? – onlycparra Nov 23 '21 at 20:24
  • I think semantically you want attribute((used)) not ((unused)), however I don't think that can apply to labels. – Tom V Nov 23 '21 at 20:26
  • @Jester The `asm` statement breaks in interesting ways when the function is inlined. – fuz Nov 23 '21 at 20:29
  • @TomV, Seems like so, when I try it, I get `error: 'used' attribute ignored`. I don't know if that implies that in general `unused` does not apply to labels. But it doesn't work here. – onlycparra Nov 23 '21 at 20:30
  • @Jester, wouldn't in general be wrong to put labels within inline functions? (unless the compiler catches that and turns it into a unique label for each instance). If we assume the function is NOT inline. Would this break? I am still not certain about the pernicious optimizations EricPostpischil mentioned. – onlycparra Nov 23 '21 at 20:32
  • @onlycparra What version of gcc? I couldn't get it to compile with the given options under 4.8.5, and after I trimmed them down I was able to set a breakpoint on "label" with and without the attribute. – dbush Nov 23 '21 at 21:21
  • @dbush Question edited with the details. – onlycparra Nov 23 '21 at 21:58
  • Yeah well it's unclear what you want when inlining. You can of course use a unique assembler generated name but then how would you know what to use while debugging. Also, you are aware you can use source line numbers in gdb, right? So you effectively already have a unique label for each line. – Jester Nov 23 '21 at 22:55
  • @Jester: In recent GCC, non-empty Basic Asm statements have an implicit `"memory"` clobber, so doing that would affect the optimizer. (Possibly moreso than getting the compiler to emit an asm label). This is undocumented and thus not recommended to rely on; it's a sop to bad code that should be using Extended asm for a `"memory"` clobber to control ordering for stuff like `asm("mfence")` or enable/disable of interrupts. – Peter Cordes Nov 23 '21 at 23:08
  • But sure, if you only want to find your place in not fully optimized code, that's fine. – Peter Cordes Nov 23 '21 at 23:15
  • @Jester. Yes. `source.c:line` works, but if I add one line on the top of the file, everything gets shifted. – onlycparra Nov 23 '21 at 23:39

2 Answers2

7

If you want to have a label that doesn't get removed or renamed, try this:

    asm volatile("mylabel:");

Note that having this label might affect how GCC optimizes your function. However, the volatile keyword will probably help prevent it from doing anything that would cause problems.

Also note that you can use __asm__ instead of asm. Both appear to work in GCC.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • 1
    `__asm__` works even with `-std=c++11`. `asm` only works with the default `-std=gnu11`, i.e. allow GNU extensions in parts of the global namespace that aren't reserved for implementation-defined stuff. – Peter Cordes Nov 24 '21 at 06:11
  • 2
    As discussed in comments under the question, this will have problems when inlining or loop unrolling, because the label will be in multiple places in one `.s` file (which is an error). Also, don't use GNU C Basic Asm; recent GCC versions treat non-empty Basic asm template strings as having a `"memory"` clobber, so will hurt optimization more than necessary. Either use `asm("mylabel:" ::: "memory");` explicitly if you want to force the compiler to have memory (except for non-escaped locals) in sync with the C abstract machine, or `asm("mylabel:" :::);` if not. – Peter Cordes Nov 24 '21 at 06:14
  • 1
    And optionally use `"mylabel%=:"` in those extended statements to have GCC invent unique numbers as part of the label name; it may still tab-complete in GDB. [How Do I Use Labels In GCC Inline Assembly?](https://stackoverflow.com/q/42681720) – Peter Cordes Nov 24 '21 at 06:17
3

You can silence the unused label warning... by using it:

int main(int argc, char** argv) {
    int a = 15;
    goto label; label:
    a = a + 23;
    return a;
}

This keeps the label in the assembly (albeit using an internal name):

main:
.LFB0:
        push    ebp
        mov     ebp, esp
        sub     esp, 16
        mov     DWORD PTR [ebp-4], 15
        nop
.L2:
        add     DWORD PTR [ebp-4], 23
        mov     eax, DWORD PTR [ebp-4]
        leave
        ret

I would suggest using a macro to reduce effort and show intent better:

#define DEBUG_LABEL(x) goto x; x:
orlp
  • 112,504
  • 36
  • 218
  • 315
  • That `nop` isn't for alignment; when GCC wants alignment, it uses `.p2align` because it doesn't keep track of instruction sizes. It leaves that up to the assembler. GCC `-O0` *does* sometimes include a `nop` in tiny functions even without goto shenanigans, like in `void bar(){}`. https://godbolt.org/z/9jGrGveYd. Of course, this trick only works at all with `-O0`, with any optimization the label disappears (https://godbolt.org/z/hfeeGzcd5), so efficiency is mostly irrelevant. – Peter Cordes Nov 23 '21 at 23:14
  • Doesn't this defeat the whole point of the label for my use case: to be used in gdb to set breakpoints. If the name is not the same, then I cannot reference it in my `.gdbinit` file. – onlycparra Nov 23 '21 at 23:35
  • 2
    @onlycparra: Well then C labels were useless for your use case all along, because they do not translate to labels with the same name in assembly. (That couldn't work, if you think about it: different functions can contain labels with the same name, not to mention inlining.) – Nate Eldredge Nov 24 '21 at 00:10
  • @onlycparra: As such, I think this is kind of an XY problem. The real question, which I think is a good one, would just be "how can I 'tag' a location in a source file so as to be able to set a breakpoint there". – Nate Eldredge Nov 24 '21 at 00:19
  • @NateEldredge You are right. I defined my question through a preconceived answer I had in my head. What is the correct stack-overflow protocol to reformulate my question? I feel that without intending it, this question stands on its own. – onlycparra Nov 24 '21 at 00:56
  • 2
    @onlycparra: At this point I would probably post it as a brand new question, maybe with a link to this one. – Nate Eldredge Nov 24 '21 at 00:58