Search code examples
c++gcclinkercustom-sections

weak symbols and custom sections in inline assembly


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 ?


Solution

  • 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