2

我遇到了一个问题,下面的 g++ 代码说明了这个问题:

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); }

我正在做这些古怪的自定义部分的事情,以此来模仿linux 内核中的机制(但以用户态和 C++ 方式)。

我的问题是实例化frob<int>被识别为弱符号,这很好,并且两者中的一个最终被链接器忽略,这也很好。除了链接器不受该extra部分引用该符号(通过.quad 1b)这一事实的干扰,并且链接器希望在本地解析它们。我得到:

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很好,因为完全没有发出任何符号)。

我不知道如何解决这个问题。

  1. 有没有办法告诉链接器也注意该extra部分中的符号解析?
  2. 也许可以将本地标签换成.weak全球标签?例如:

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

    但是我担心如果我这样做,不同编译单元中的不同 asm 语句可能会通过这种机制获得相同的符号(可能吗?)。

有没有我忽略的方法?

4

1 回答 1

1

g++(至少 5,6)编译具有外部链接的内联函数 - 例如 -在其自己的节组中的[COMDAT] [函数节]template<> inline int frob<int>(int x)中的弱全局符号处。看:-

g++ -S -O0 bar.cpp

酒吧.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
...
...

相关指令是:

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

(编译器生成#APP#NO_APP分隔您的内联程序集)。

像编译器一样extra在节组中创建一个 COMDAT 节:

frob.hpp(固定)

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;
}

并且联动错误将被治愈:

$ g++ -O0 foo.cpp  bar.cpp 
$ ./a.out; echo $?
61
于 2017-06-22T18:52:57.690 回答