Linker does not honor section attribute or linker script sections

Bug #1418073 reported by Rahul
10
This bug affects 2 people
Affects Status Importance Assigned to Milestone
GNU Arm Embedded Toolchain
Invalid
Undecided
Terry Guo

Bug Description

I have observed that when compiling and linking with link time optimization, linker is not honoring the __attribute__((section (".section1"))) or sections defined in linker script. It is putting all code and data in default sections (.text or .bss or .data as the case is).

I am using following flags to enable LTO:
CFLAGS += -flto -ffat-lto-objects
LDFLAGS += -flto

I have also tried to add -fuse-linker-plugin to both CFLAGS and LDFLAGS but results are unchanged.

Functions are defined as:
__attribute__((section(".section1"))) void func()
{
  /* some code here */
}

Additionally some object files are entirely put in different sections using linker script:
SECTIONS
{
  .section2 (__ram_start):
  {
       *file2.o (.text .text.* .rodata .rodata.*)
  }

  .text (__flash_mem_start):
  {
       *(.text .text.*)
  }
}

In both cases, instead of code/data going into respective sections, it goes to .text or .bss or .data. In my case, .section1/2 goes into RAM whereas .text section goes into flash from where it is executed in place. So, putting .section1/2 in .text and therefore in flash is totally unacceptable.

I have found work-around for both cases like this:
__attribute__((externally_visible, section(".section1))) void func()
{
  /* some code here */
}

and compiling file2.c without LTO flags. However these are more like work-arounds and do not seem like proper fixes.

My question is: Is this the intended behavior of LTO or a bug? And if not bug, why and is there any better solution than mentioned above?

Thanks

Terry Guo (terry.guo)
Changed in gcc-arm-embedded:
assignee: nobody → Terry Guo (terry.guo)
Revision history for this message
Terry Guo (terry.guo) wrote :

Maybe your function func() is inlined. If I define function as below:

terguo01@terry-pc01:minimum$ cat foo.c

void __attribute__((section(".section1")))
__attribute__((noinline))
foo ()
{
  volatile int x;
  x = 10;
}

The foo() can be kept in section .section1.

Revision history for this message
Rahul (rahulgm) wrote :

Yes, it does get inlined. But inlining should not happen as the function is supposed to go into a different section. It should take into account section attribute and not inline function if caller and callee happen to be in different sections.

Revision history for this message
Terry Guo (terry.guo) wrote :

In my opinion, it is arguable to say gcc shouldn't inline functions in different sections. Instead I find gcc is consistent between lto and non-lto that gcc always inlines functions without considering the section attribute. For example:

void __attribute__((section(".section1")))
foo()
{
  volatile int x = 10;
}

void main()
{
 for (;;) foo ();
}

The foo() will be inlined no matter it is lto or not. So for you case, I think you need to add noinline attribute if you want to keep function in a specified section.

If you want to change this gcc fundamental behavior, I suggest you to check with gcc community first.

Changed in gcc-arm-embedded:
status: New → Invalid
Revision history for this message
Rahul (rahulgm) wrote :

No, this is not my observation. Without LTO, function does not get inlined and goes into its designated section (here ".section1"). With LTO, it gets inlined, ignoring the section attribute.

Also won't it be inconvenient to add noinline attribute if I am putting all functions in a file in a different section (through linker script)?

Revision history for this message
Terry Guo (terry.guo) wrote :

For your case, the functions are in two different files, so gcc is unable to inline them when disable lto. When you enable lto, gcc works like all functions are defined in one file, so functions get inlined. That's why I define functions in one file.

Again please be noted that LTO works like all functions are in one file. When all functions are in one file, inline happens without considering the section attribute for both LTO or not LTO. This is a consistent behavor and this is also my point.

Revision history for this message
Terry Guo (terry.guo) wrote :

Agree it is inconvenient. Maybe this option -fkeep-inline-functions can help you.

Revision history for this message
Terry Guo (terry.guo) wrote :

Bad news, the option -fkeep-inline-functions isn't helpful. This is more like a new gcc feature to not inline functions from different sections.

Revision history for this message
Rahul (rahulgm) wrote :

I have found that compiling these files without LTO will put them in proper section. So linking will essentially be of mixed (mostly LTO and a few non-LTO) code. Do you think this is a safe way or will there be any side effects?

Regarding gcc, I get your point, though I would say it should respect the explicit user-defined sections, with or without LTO. I will check with gcc community about this.

Revision history for this message
Terry Guo (terry.guo) wrote :

I think it is a safe way to do so and it is quit often these days, especially when people link their new codes with old libraries.

About checking with community, I think it is better to say we want a new option to tell gcc not inline functions assigned to different sections. Maybe others do want gcc to inline those functions.

Revision history for this message
Rahul (rahulgm) wrote :

I agree. Thanks for your support.

Revision history for this message
Rahul (rahulgm) wrote :

I tried following code snippet put in single file:

volatile int x;
void __attribute__((section (".section1")))
foo()
{
        x++;
}
void main()
{
 foo ();
 /* some other code */
}

I have found that without LTO foo() goes into .section1, but with LTO it is inlined and goes into .text. I am using "gcc version 4.9.3 20141119 (release) [ARM/embedded-4_9-branch revision 218278]"

This is not what you had suggested would happen. foo() should have been inlined no matter LTO is enabled or not.

Revision history for this message
Terry Guo (terry.guo) wrote :

When disable lto, you should link case with option -Wl,--gc-sections. You will find that foo get inlined and its section is discarded in final elf file. If we follow the opinion that foo shouldn't be inlined because it is in another section, then .section1 won't be discarded even with -Wl,--gc-sections.

Revision history for this message
Terry Guo (terry.guo) wrote :

The opinion is that the section attribute isn't respected with or without lto.

Revision history for this message
Terry Guo (terry.guo) wrote :

One of important thing that LTO will do is to discard all things that are nevered used. This works just like -Wl,--gc-sections.

Revision history for this message
Rahul (rahulgm) wrote :

Yes, I am using -Wl,--gc-sections. Following are the exact flags I am using:

Without LTO:
COMPILE:
arm-none-eabi-gcc -c -o main.o -Wall -g -Os -MMD -ffunction-sections -fdata-sections -fno-common -ffreestanding -mcpu=cortex-m4 -mthumb main.c

LINK:
arm-none-eabi-gcc -Wall -g -Os -MMD -ffunction-sections -fdata-sections -fno-common -ffreestanding -mcpu=cortex-m4 -mthumb -T xip.ld -nostartfiles -Xlinker -M -Xlinker -Map=hello_world.map -Xlinker --cref -Xlinker --gc-sections -Xlinker --start-group main.o -Xlinker --end-group -o hello_world.axf

With LTO:
COMPILE:
arm-none-eabi-gcc -c -o main.o -Wall -g -Os -MMD -ffunction-sections -fdata-sections -fno-common -ffreestanding -mcpu=cortex-m4 -mthumb -flto -ffat-lto-objects main.c

LINK:
arm-none-eabi-gcc -Wall -g -Os -MMD -ffunction-sections -fdata-sections -fno-common -ffreestanding -mcpu=cortex-m4 -mthumb -flto -ffat-lto-objects -T xip.ld -nostartfiles -Xlinker -M -Xlinker -Map=hello_world.map -Xlinker --cref -Xlinker --gc-sections -Xlinker -flto -Xlinker --start-group main.o -Xlinker --end-group -o hello_world.axf

Please let me know what I am missing here.

Revision history for this message
Rahul (rahulgm) wrote :

Note that I am placing ."section1" and ".text" sections at different memory locations (no default placement) in linker script.

Revision history for this message
Rahul (rahulgm) wrote :

Any feedback on my query? Your response will be very useful to us.

Revision history for this message
Terry Guo (terry.guo) wrote :

Would you please attach your example project here? It's better if we can work on same thing.

Revision history for this message
Terry Guo (terry.guo) wrote :

Looks to me that section will be discarded with -O2 option, but not with -Os option.

Revision history for this message
Rahul (rahulgm) wrote :

I have attached a simple project that demonstrates the issue. Issue is same with gcc and arm-none-eabi-gcc.

Revision history for this message
Terry Guo (terry.guo) wrote :

If you take a closer look, you will find function foo doesn't get inlined for -Os option, which prevents .section1 from being discarded. About why -Os doesn't inline foo, it is simply because inlining function tends to increase code size.

If you replace -Os with -O2, you will find foo gets inlined and the function .section1 will be discard in final elf file.

Again there is no explicit statement saying that functions assigned to different sections shouldn't be inlined.

Revision history for this message
Rahul (rahulgm) wrote :

If I define foo() to be static, then in both LTO and non-LTO cases foo() gets inlined despite -Os. So, I guess noinline attribute is our best bet to get expected behavior in this case.

Revision history for this message
Ben (bsschwar) wrote :

I just ran into this exact issue yesterday. Was quite the headache debugging. In the end, my workaround was to add the noinline attribute to my function.

This definitely seems like it should be considered a bug. There is no information in the GNU spec about the compiler selectively ignoring the section attribute, which is essentially what is happening. If the process of inlining a function would violate the section attribute that the programmer has specified, then that function should not be inlined.

Revision history for this message
Jérémie Faucher-Goulet (jeremfg) wrote :

I just ran into this myself, using "-O1" as optimization level and GCC 4.9.3 20150529 (release) [ARM/embedded-4_9-branch revision 227977]

Thankfully I didn't have to look very long. In my case this was triggering the Cortex-M4 MPU. I use the "section" attribute especially to move a function to a region of memory where it can be executed with unprivileged access. Since it was inlined inside privileged memory, I could no longer jump to my function.

I've solved it using noinline as well, but I feel this is a very odd behavior for GCC to ignore an explicit requirement stated in the code.

Does anyone have a link towards the GCC community or anywhere else where this is already discussed?

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

Hi Jeremie,

I'd recommend submitting a bug report at https://gcc.gnu.org/bugzilla/ if you cannot find one already.

Best regards,

Thomas

Revision history for this message
Andrey Skvortsov (skv) wrote :
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.