Inline static data causes a section type conflict

2019-02-03 04:32发布

问题:

I want to put some user defined data into a custom section to be read by the application and an offline analyser at the same time. Assuming the following sample:

const int* get_data()
{
  __attribute__((section(".custom")))
  static const int data = 123;

  return & data;
}

inline const int* inline_get_data()
{
  __attribute__((section(".custom")))
  static const int inline_data = 123;

  return & inline_data;
}

int main()
{
  (void) get_data();
  (void) inline_get_data();
  return 0;
}

The value of data and inline_data will appear in the section .custom. Clang compiles this example and produces the correct result, just as MSVC does, when the __attributes__ are replaced by corresponding pragmas.

Unfortunately, GCC 5.2 gives the following error:

error: inline_data causes a section type conflict with data

The problem boils down to the fact that the two variables have different linkage (data is in a section flagged with a, the section of inline_data is flagged with aG). GCC 4.9 fails the same way if the second function is not marked as inline but is a template (GCC 5.2 compiles that).

GCC 5.2 also compiles fine if one section name is temporarily changed and manually fixed in the generated assembly.

Is there any known workaround for this issue? I have no control over the function signature,the *data variables are produced by a macro provided by me, and they can appear anywhere.

回答1:

For the general good, I'll reiterate what you already know and what @Rumbaruk has already cited: gcc's documentation explicitly restricts the application of the section attribute to global variables. So the desired workaround for gcc's behaviour is a way to get gcc not to barf or emit broken code on an unsupported application of a gcc-specific language extension. We've no right to expect success or to expect a success to be consistently repeatable.

Here comes a long explanation of how and why gcc produces the section-type conflict compilation error and clang doesn't. Scroll to Fixes if impatient, but don't expect a silver bullet.

For demonstration purposes I'll work with a slightly more lifelike program than you've posted, viz:

source.cpp

const int* get_data()
{
    __attribute__((section(".custom")))
    static const int data = 123;

    return & data;
}

inline const int* inline_get_data()
{
    __attribute__((section(".custom")))
    static const int inline_data = 123;

    return & inline_data;
}

const int* other_get_data()
{
    return inline_get_data();
}

header.h

#ifndef HEADER_H
#define HEADER_H
extern const int* get_data();
extern const int* other_get_data();
#endif

main.cpp

#include "header.h"
#include <iostream>

int main()
{
    std::cout << (*get_data() + *other_get_data()) << std::endl;
    return 0;
}

As it stands, this program reproduces the section-type conflict error when compiled with gcc 5.2:

$ g++-5 -Wall -pedantic -c source.cpp
source.cpp:12:22: error: inline_data causes a section type conflict with data
     static const int inline_data = 123;
                      ^

Clang (3.6/3.7) has no complaints:

$ clang++ -Wall -pedantic -I. -o prog main.cpp source.cpp 
$ ./prog
246

The root of gcc's obstructiveness is the fact that inline_get_data() is an inline function with external linkage that attributes a linkage section to its static data in the same translation unit as a non-inline function, get_data(), that attributes the same linkage section to its own static data.

The compiler adopts different rules to generate the linkage for get_data() and inline_get_data() respectively. get_data() is the simple case, inline_get_data() is tricky case.

To see the difference, let's temporarily disarm the gcc section conflict by replacing "custom" with "custom.a" in get_data() and replacing "custom" with "custom.b" in inline_get_data().

Now we can compile source.cpp with gcc and inspect the relevant symbol table entries:

$ objdump -C -t source.o | grep get_data
0000000000000000 l     O .custom.a  0000000000000004 get_data()::data
0000000000000000 l    d  .text._Z15inline_get_datav 0000000000000000 .text._Z15inline_get_datav
0000000000000000 g     F .text  000000000000000b get_data()
0000000000000000 u     O .custom.b  0000000000000004 inline_get_data()::inline_data
0000000000000000  w    F .text._Z15inline_get_datav 000000000000000b inline_get_data()
000000000000000b g     F .text  000000000000000b other_get_data()

get_data(), of course, has been made a global symbol (g) and get_data()::data made a local symbol (l). But inline_get_data() has been made a weak, neither global nor local symbol (w) and inline_get_data()::inline_data, although it is syntactically block-scope static, has been made a unique global symbol (u). This is a GNU extension of the standard ELF symbol bindings requiring the runtime linker to ensure that the symbol is unique in the entire runtime linkage.

In these different linkage stipulations for inline_get_data(), gcc is coping as it deems fit with the fact that the function is inline with external linkage. The fact that the function is inline means it must be defined in each translation unit in which it is used, and the fact that it has external linkage means that all those definitions must address the same inline_data()::get_data. Thus the block-scope static variable must, for linkage purposes, become a public symbol.

From the same motivation, gcc deals differently with the attributed section custom.a in the setting of get_data() and the attributed section custom.b in inline_get_data(). Having designated inline_get_data()::inline_data a unique global symbol, it wants to ensure that multiple definitions of this symbol are not introduced by the linkage of multiple copies the section custom.b from different translation units. To that end, it applies the GROUP linker attribute to custom.b: this (skipping details) enables it to generate a .section directive that assigns custom.b to a named section-group and directs the linker to retain only one copy of that section group. Observe:

$ readelf -t source.o
...
...
[ 7] .custom.a
   PROGBITS               PROGBITS         0000000000000000  0000000000000068  0
   0000000000000004 0000000000000000  0                 4
   [0000000000000002]: ALLOC
[ 8] .custom.b
   PROGBITS               PROGBITS         0000000000000000  000000000000006c  0
   0000000000000004 0000000000000000  0                 4
   [0000000000000202]: ALLOC, GROUP
                              ^^^^^
 ...
 ...

And this is the trigger of the section-type conflict error, when custom.a and custom.b are one and the same. Gcc can't make a section that both has and does not have the GROUP attribute.

Now if get_data() and inline_get_data were defined in different translation units the compiler could not notice the conflict. So what does it matter? What would go wrong in that case?

Nothing goes wrong in that case, because in that case there is no section-type conflict. A section custom generated by gcc in source.o is a section in source.o. It must either have or not have the GROUP attribute, but either way there is no conflict with a section custom of the same name in other_source.o having the opposite status. These are distinct input sections for the linker. It will deduplicate the input custom sections that are GROUPed, retaining only one of them per group-name. It will not do that with the input custom sections that are not GROUPed, and finally it will merge all the input custom sections it is left with into one output custom section in the binary, with the now non-applicable GROUP attribute(s) ditched. That output custom section will contain get_data()::data as a local symbol and inline_get_data()::inline_data as a unique global symbol. The conflict consists solely in the compiler encountering contradictory rules as to whether section source.o(custom) shall be GROUPed or not.

Why then doesn't clang fall foul of the same contradiction? It's because clang takes a simpler but somewhat less robust approach to the problem of an inline function with external linkage containing static data.

Sticking with the differentiation of sections custom.a and custom.b, let's now compile source.cpp with clang and inspect the relevant symbol and section characteristics:

$ objdump -C -t source.o | grep get_data
0000000000000000 l     O .custom.a  0000000000000004 get_data()::data
0000000000000000 l    d  .text._Z15inline_get_datav 0000000000000000 .text._Z15inline_get_datav
0000000000000010 g     F .text  000000000000000b other_get_data()
0000000000000000  w    F .text._Z15inline_get_datav 0000000000000010 inline_get_data()
0000000000000000 g     F .text  0000000000000010 get_data()
0000000000000000  w    O .custom.b  0000000000000004 inline_get_data()::inline_data

There's one difference there from the output of gcc. As we might expect, clang does not avail of the GNU-specific symbol binding unique global symbol (u) for inline_get_data()::inline_data. It makes that a weak symbol, like inline_get_data() itself.

And for the section traits we have:

$ readelf -t source.o
...
...
[ 8] .custom.a
   PROGBITS               PROGBITS         0000000000000000  0000000000000080  0
   0000000000000004 0000000000000000  0                 4
   [0000000000000002]: ALLOC
[ 9] .custom.b
   PROGBITS               PROGBITS         0000000000000000  0000000000000084  0
   0000000000000004 0000000000000000  0                 4
   [0000000000000002]: ALLOC
...
...

No difference, so no conflict. That's why we can replace the section names custom.a and custom.b with custom, per original, and successfully compile.

Clang relies on its weak binding of inline_get_data()::inline_data to answer the requirement that just one such symbol is addressed by every implementation of inline_get_data() that gets into the linkage. This saves it from a section-type conflict but forgoes the linkage armour-plating of gcc's more complicated approach.

Can you tell gcc to forgo that robustness and take a clang-like way with compiling inline_get_data()? You can a bit, but not enough. You can give gcc the option -fno-gnu-unique to instruct the compiler to forget the GNU-specfic unique global symbol binding. If you do that, then it will make inline_get_data()::inline_data a weak symbol, like clang; but that won't nudge it - maybe it should - to drop the section-grouping linkage for the attributed section of the symbol, and you'll still get the section-type conflict. I can find no option to inhibit this rather nitty-gritty code-generation behaviour for your admittedly smelly problem code.

Fixes

We've seen how and why the gcc section-type conflict is provoked specifically by the presence in the same translation unit of definitions of two functions, one inline with external linkage, the other not inline, each of which attributes the same linkage section to its static data.

I can suggest two remedies, one of them simple and safe but applicable only to one variation of the problem, the other applicable always, but drastic and desperate.

The Simple Safe One

There are two ways in which the conflicting function definitions can get into the same translation unit:-

  1. Both of them are defined in the same source (.cpp) file.
  2. The non-inline function is defined in a source file that includes a header in which the inline function is defined.

If you have cases of type 1, then it is just a goof on the part of whoever codes the source file to code an inline function in it with external linkage. In this case the inline function is local to its translation unit and should be static. If it is made static then gcc's external linkage exertions disappear and the section-type conflict with them. You have said you have no control over the code in which your attributed section stuff is macro-injected, but its authors should be receptive of the fact that writing inline external functions in a source file rather than a header is a blunder and be willing to correct it.

The Drastic Desperate One

Cases of type 2 are more likely. For these, as far as I can see, your one hope is to inject an assembly hack into your gcc build so that gcc's .section directives regarding the attributed section in the inline external function definitions are programmatically edited to be clang-like before the object code is generated.

Clearly, such a solution will be viable only for some set of gcc versions that you know to generate the "right pattern of wrong .section directives" on which to target your corrective hack, and a build system that uses it should sanity-check the operative gcc version in advance.

A necessary preliminary is to modify your macro that generates the custom section attributes so that instead of uniformly generating the section name .custom it instead generates the sequence .custom.1, custom.2,...,custom.N at successive invocations in a translation unit. Use the preprocessor builtin __COUNTER__ to do this, e.g.

#define CAT2(x,y) x##y
#define CONCAT(x,y) CAT2(x,y)
#define QUOT(x) #x
#define QUOTE(x) QUOT(x) 
#define SET_SECT() __attribute__((section(QUOTE(CONCAT(.custom.,__COUNTER__)))))

The point of this is just to let gcc preprocess code like:

const int* get_data()
{
    SET_SECT()
    static const int data = 123;

    return & data;
}

inline const int* inline_get_data()
{
    SET_SECT()
    static const int inline_data = 123;

    return & inline_data;
}

into code like:

const int* get_data()
{

    __attribute__((section(".custom.0")))
    static const int data = 123;

    return & data;
}

inline const int* inline_get_data()
{

    __attribute__((section(".custom.1")))
    static const int inline_data = 123;

    return & inline_data;
}

that will not provoke section-type conflicts.

With this in place and applied to source.cpp, you can assemble the file with gcc:

g++ -S source.cpp

and observe in the output source.s that the unproblematic section custom.0 get the .section directive:

.section    .custom.0,"a",@progbits

whereas the problematic section custom.1 gets:

.section    .custom.1,"aG",@progbits,_ZZ15inline_get_datavE11inline_data,comdat

where _ZZ15inline_get_datavE11inline_data is the section-group name and comdat tells the linker to deduplicate this section-group.

Repeat this with clang and observe that the corresponding directives are:

.section    .custom.0,"a",@progbits
.section    .custom.1,"a",@progbits

with no difference other than the section name.

So the assembly hack you require is one that will turn the like of either:

.section    .custom.0,"a",@progbits
.section    .custom.1,"aG",@progbits,_ZZ15inline_get_datavE11inline_data,comdat

into:

.section    .custom,"a",@progbits

This can be expressed by a sed substitution:

s|^\t\.section\t\.custom\.[0-9]\{1,\},"a\(G\)*",@progbits.*$|\t\.section\t\.custom,"a",@progbits|g

For the demo program, assuming the necessary changes to the macro apparatus, the Drastic solution can be formulated in a makefile like so:

CXX ?= g++
SRCS = main.cpp source.cpp
ASMS = $(SRCS:.cpp=.s)
OBJS = $(SRCS:.cpp=.o)
CPPFLAGS = -I.
CXXFLAGS = -fno-gnu-unique

%.o: %.cpp

%.s: %.cpp
%.s: %.cpp
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -S -o $@ $<

%.o: %.s    
%.o: %.s
    sed -i 's|^\t\.section\t\.custom\.[0-9]\{1,\},"a\(G\)*",@progbits.*$$|\t\.section\t\.custom,"a",@progbits|g' $<
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<

.PHONY: all clean
.INTERMEDIATE: $(ASMS)

all: prog

prog: $(OBJS)
    $(CXX) -o $@ $^

clean:
    rm -f prog $(OBJS) $(ASMS)

from which a ./prog can be built with gcc that fulfils the expectation of printing 246 on stdout.

Notice three details of the makefile:-

  • We need to write empty pattern rules like %.o: %.cpp to delete make's inbuilt recipes for those rules.
  • In the sed command we need *$$ as the eol-marker to escape make-expansion of $.
  • -fno-gnu-unique is passed in the compiler flags to fill-out the clang mimicry.

This is not a solution I'd want expose to an open user-base except as stop-gap. I won't demur if the take-away from all of it is: Isn't there's a better way of attacking the underlying the problem?



回答2:

Messing with custom sections is a bit of a dirty topic because gcc decides on data or bss depending on init value and does not expect you to mess with it at that level.

What I'd suggest for user data is to use it as you normally use data - put it in a data file. If you insist on using library, you can at least let it use its own library and then the data can be in the normal places.

A tiny user lib, can be built with -fno-zero-initialized-in-bss to have all the user data in the data section for easy parsing. But don't do that on your binaries.



回答3:

I finally found a satisfactory solution. It is really just the combination of already known techniques. The runtime uses a normal static variable, whose address is put into the custom section by inline assembly. On different platforms (clang, MSVC),__attribute__ or #pragma can be used with the same results, without ASM. This solution can be easily wrapped into a generic, platform agnostic macro.

const int* get_data()
{
  static const int data = 123;
  __asm__(
    ".pushsection .custom, \"?\", @progbits" "\n"
    ".quad %c0" "\n"
    ".popsection" "\n"
    : : "i"(&data)
  );

  return & data;
}

inline const int* inline_get_data()
{
  static const int inline_data = 123;
  __asm__(
    ".pushsection .custom, \"?\", @progbits" "\n"
    ".quad %c0" "\n"
    ".popsection" "\n"
    : : "i"(&inline_data)
  );

  return & inline_data;
}

int main()
{
  (void) get_data();
  (void) inline_get_data();
  return 0;
}


回答4:

The gcc documentation (e.g. for 5.3) says:

Use the section attribute with global variables and not local variables [...]

Thus, you need to extract those variables from the functions:

__attribute__((section(".custom"))) static const int data = 123;
__attribute__((section(".custom"))) static const int inline_data = 123;

const int* get_data()
{
  return &data;
}

inline const int* inline_get_data()
{
  return &inline_data;
}

int main()
{
  (void)get_data();
  (void)inline_get_data();
} 

This compiles just fine with gcc-5.2 and clang-3.5.1



标签: c++ gcc linker elf