函数模板的section属性在GCC中被忽略

section attribute of a function template is silently ignored in GCC

本文关键字:GCC section 属性 函数模板      更新时间:2023-10-16

我试图将一组特定的函数放在一个单独的部分中,但在使用GCC时遇到了问题。

namespace /* anonymous */ {
  [[gnu::section(".mysection")]]
  void regular_func() { }
  template <class T>
  [[gnu::section(".mysection")]]
  void template_func() { }
} // namespace /* anonymous */
void (*ptr1)() = &regular_func;
void (*ptr2)() = &template_func<int>;

使用clang,regular_functemplate_func<int>的符号都被放置在.mysection中,正如我所期望的那样。

$ clang++ -std=c++14 a.cpp -c && objdump -t a.o | grep -E "regular|template"
0000000000000000 l     F .mysection 0000000000000006 _ZN12_GLOBAL__N_112regular_funcEv
0000000000000010 l     F .mysection 0000000000000006 _ZN12_GLOBAL__N_113template_funcIiEEvv

但是对于GCC,函数模板不是放在.mysection中,而是放在.text.*部分中。

$ g++ -std=c++14 a.cpp -c && objdump -t a.o | grep -E "regular|template"
0000000000000000 l     F .mysection 0000000000000007 _ZN12_GLOBAL__N_112regular_funcEv
0000000000000000 l     F .text  0000000000000007 _ZN12_GLOBAL__N_113template_funcIiEEvv

我使用的是clang-3.7.1和gcc-5.3.0。

如何强制gcc将模板实例化的函数放在单独的部分中?

这可能是一个小小的安慰,但如果您应用section,GCC将起作用归因于CCD_ 7的显式实例化,对于要实例化的每个T,例如

namespace /* anonymous */ {
    [[gnu::section(".mysection")]]
    void regular_func() { }
    template <class T>
    void template_func() { }
    template [[gnu::section(".mysection")]] void template_func<int>();
} // namespace /* anonymous */

void (*ptr1)() = &regular_func;
void (*ptr2)() = &template_func<int>;

然后:

$ g++ -std=c++14 a.cpp -c && objdump -C -t a.o | grep -E "regular|template"
0000000000000000 l     F .mysection 0000000000000007 (anonymous namespace)::regular_func()
0000000000000007 l     F .mysection 0000000000000007 void (anonymous namespace)::template_func<int>()

不幸的是clang拒绝:

template [[gnu::section(".mysection")]] void template_func<int>();

说:

template [[gnu::section(".mysection")]] void template_func<int>();
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: an attribute list cannot appear here

因此,每个编译器都必须有自己的方式,通过条件编译。

此外,这个修复程序带来了额外的头痛,你必须以某种方式确保template_func()不能为任何未明确表示的T实例化实例化。

您可以通过在函数模板的主体中静态断言T是您允许实例化的类型A,B,C...之一。那么如果如果T=D被实例化,则static_assert将被激发;你可以将D添加到列表中,并为D:添加显式实例化

#include <type_traits>
template<typename T, typename First>
constexpr bool is_in()
{
    return std::is_same<T,First>::value;
}
template<typename T, typename First, typename Second, typename ...Rest>
constexpr bool is_in()
{
    return is_in<T,First>() || is_in<T,Second,Rest...>();
}
namespace /* anonymous */ {
    [[gnu::section(".mysection")]]
    void regular_func() { }
    template <class T>
    void template_func() 
    {
        static_assert(is_in<T,int,float>(),"");
    }
    template [[gnu::section(".mysection")]] void template_func<int>();
    template [[gnu::section(".mysection")]] void template_func<float>();
} // namespace /* anonymous */

void (*ptr1)() = &regular_func;
void (*ptr2)() = &template_func<int>;
void (*ptr3)() = &template_func<float>;
void (*ptr4)() = &template_func<char>; // <-- static_assert fails

这不是最优雅的方法,但您可以进行一些名称篡改并修改链接器脚本以获得所需的行为

#define TEMPLATE_SECTION(name, section) name##_##section##_
#define template_func TEMPLATE_SECTION(template_func, mysection)
template<class T>
void template_func() { }
void regular_func() {} 
int main() {
    regular_func();         //placed in ".text"
    template_func<int>();   //placed in ".mysection"
    template_func<float>(); //placed in ".mysection"
    return 0;
}

在链接器脚本中

SECTIONS {
    .mysection : {
        *(*_mysection_*) 
    }
    .text : {
        *(.text .text.*)
    }
}