2

I'm stuck with a problem which is illustrated by the following g++ code:

frob.hpp:

template<typename T> T frob(T x);

template<> inline int frob<int>(int x) {
    asm("1: nop\n"
            ".pushsection \"extra\",\"a\"\n"
            ".quad 1b\n"
            ".popsection\n");
    return x+1;
}

foo.cpp:

#include "frob.hpp"

extern int bar();

int foo() { return frob(17); }

int main() { return foo() + bar(); }

bar.cpp:

#include "frob.hpp"
int bar() { return frob(42); }

I'm doing these quirky custom section things as a way to mimick the mechanism here in the linux kernel (but in a userland and C++ way).

My problem is that the instantiation of frob<int> is recognized as a weak symbol, which is fine, and one of the two is eventually elided by the linker, which is fine too. Except that the linker is not disturbed by the fact that the extra section has references to that symbol (via .quad 1b), and the linker want to resolve them locally. I get:

localhost /tmp $ g++ -O3 foo.cpp  bar.cpp 
localhost /tmp $ g++ -O0 foo.cpp  bar.cpp 
`.text._Z4frobIiET_S0_' referenced in section `extra' of /tmp/ccr5s7Zg.o: defined in discarded section `.text._Z4frobIiET_S0_[_Z4frobIiET_S0_]' of /tmp/ccr5s7Zg.o
collect2: error: ld returned 1 exit status

(-O3 is fine because no symbol is emitted altogether).

I don't know how to work around this.

  1. would there be a way to tell the linker to also pay attention to symbol resolution in the extra section too ?
  2. perhaps one could trade the local labels for .weak global labels ? E.g. like in:

    asm(".weak exception_handler_%=\n"
        "exception_handler_%=: nop\n"
        ".pushsection \"extra\",\"a\"\n"
        ".quad exception_handler_%=\n"
        ".popsection\n"::);
    

    However I fear that if I go this way, distinct asm statements in distinct compilation units may get the same symbol via this mechanism (may they ?).

Is there a way around that I've overlooked ?

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
EThome
  • 70
  • 6

1 Answers1

1

g++ (5,6, at least) compiles an inline function with external linkage - such as template<> inline int frob<int>(int x) - at a weak global symbol in a [COMDAT] [function-section] in its own section-group. See:-

g++ -S -O0 bar.cpp

bar.s

    .file   "bar.cpp"
    .section    .text._Z4frobIiET_S0_,"axG",@progbits,_Z4frobIiET_S0_,comdat
    .weak   _Z4frobIiET_S0_
    .type   _Z4frobIiET_S0_, @function
_Z4frobIiET_S0_:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
#APP
# 8 "frob.hpp" 1
    1: nop
.pushsection "extra","a"
.quad 1b
.popsection

# 0 "" 2
#NO_APP
    movl    -4(%rbp), %eax
    addl    $1, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
...
...

The relevant directives are:

    .section    .text._Z4frobIiET_S0_,"axG",@progbits,_Z4frobIiET_S0_,comdat
    .weak   _Z4frobIiET_S0_

(The compiler-generated #APP and #NO_APP delimit your inline assembly).

Do as the compiler does by making extra likewise a COMDAT section in a section-group:

frob.hpp (fixed)

template<typename T> T frob(T x);

template<> inline int frob<int>(int x) {
    asm("1: nop\n"
            ".pushsection \"extra\", \"axG\", @progbits,extra,comdat" "\n"
            ".quad 1b\n"
            ".popsection\n");
    return x+1;
}

and the linkage error will be cured:

$ g++ -O0 foo.cpp  bar.cpp 
$ ./a.out; echo $?
61
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
  • Thanks! Indeed it's an effective way to discard the references to the discarded section. I couldn't find proper semantics for gas section groups beyond the terse binutils doc. It's not entirely clear to me when merging happens or does not happen, and whether I have full control of it. – EThome Jun 22 '17 at 21:20