Слабые символы и пользовательские разделы в встроенной сборке
Я застрял с проблемой, которая иллюстрируется следующим кодом 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
это хорошо, потому что символ не испускается вообще).
Я не знаю, как обойти это.
- будет ли способ сказать компоновщику также обратить внимание на разрешение символов в
extra
раздел тоже? возможно, можно было бы обменять местные этикетки на
.weak
глобальные ярлыки? Например, как в:asm(".weak exception_handler_%=\n" "exception_handler_%=: nop\n" ".pushsection \"extra\",\"a\"\n" ".quad exception_handler_%=\n" ".popsection\n"::);
Однако я боюсь, что если я пойду таким образом, различные ассемблерные выражения в разных единицах компиляции могут получить один и тот же символ через этот механизм (не так ли?).
Есть ли способ обойти, что я упустил?
1 ответ
g++ (по крайней мере, 5,6) компилирует встроенную функцию с внешней связью, напримерtemplate<> inline int frob<int>(int x)
- при слабом глобальном символе в [COMDAT] [function-section] в своей собственной группе разделов. Увидеть:-
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
...
...
Соответствующие директивы:
.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