Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

section attribute of a function template is silently ignored in GCC

I'm trying to put a specific set of functions into a separate section and got trouble doing it with 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>;

With clang, the symbols for both of regular_func and template_func<int> are placed in .mysection as I expected.

$ 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

But with GCC, the function template is not placed in .mysection, but in the .text.* section.

$ 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

I'm using clang-3.7.1 and gcc-5.3.0.

How can I force gcc to put the template-instantiated function in a separate section?

like image 838
Inbae Jeong Avatar asked Mar 29 '16 08:03

Inbae Jeong


1 Answers

It may be small comfort, but GCC will oblige if you apply the section attribute to an explicit instantiation of template <class T> void template_func(), for each T that you want to be instantiated, e.g.

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

Then:

$ 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>()

Unfortunately clang rejects:

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

saying:

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

so each compiler must have its own way, via conditional compilation.

Moreover this fix brings the added headache that you must somehow ensure that template_func() can't be instantated for any T that you haven't explicitly instantiated.

You may achieve that by statically asserting in the function template's body that T is one of types A,B,C... that you allow to be instantiated. Then if it is ever instantiated with T = D, the static_assert will fire; you can add D to the list and add an explicit instantiation for 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
like image 169
Mike Kinghan Avatar answered Oct 21 '22 10:10

Mike Kinghan