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 GROUP
ed, 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 GROUP
ed 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:-
- Both of them are defined in the same source (
.cpp
) file.
- 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?