RAM section is part of the binary firmware - gcc

I am trying to use a custom RAM section to be able to pass information across reboot. This section will not be erased at boot and so the variables placed in this section will be kept across reboots (if there is no alimentation loss of course).
I use GNU toolchain and a Cortex-M0 (STM32) MCU
So I added in the linker script a new memory area before RAM :
RAM_PERSIST (xrw) : ORIGIN = 0x20000000, LENGTH = 0x0040
RAM (xrw) : ORIGIN = 0x20000040, LENGTH = 0x0FD0
Then a section to go in there :
.pds :
{
KEEP(*(.pds))
} >RAM_PERSIST
Finally in the C code, I declare some data in this section :
data_t __attribute((section(".pds")) data;
I does compile but I could not upload the generated binary on my target. Using objdump I discovered that my firmware got a new section ".sec2" beginning at 0x20000000 :
> (...)/arm-none-eabi-objdump -s ./obj/firmware.hex | tail
8006d20 f8bc08bc 9e467047 f8b5c046 f8bc08bc .....FpG...F....
8006d30 9e467047 e9000008 c1000008 00127a00 .FpG..........z.
8006d40 19000000 e0930400 409c0000 400d0300 ........#...#...
8006d50 c0c62d00 30750000 ffffffff 01000000 ..-.0u..........
8006d60 04000000 ....
Contents of section .sec2:
20000000 00000000 00000000 00000000 00000000 ................
20000010 00000000 00000000 00000000 00000000 ................
20000020 00000000 00000000 00000000 00000000 ................
20000030 00000000 00000000 00000000 00000000 ................
So I think I have to tell the linker this section is not in the flash so must not be part of the firmware.
Am I right ? If so, how to do that ?
Thanks by advance.

MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x40000
ram : ORIGIN = 0x20000000, LENGTH = 0x4000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
I had more control/success when I stopped using xrw, etc in the memory definition and instead went with control over .text, .bss, .data, etc. and if you then further want a specific object somewhere you add that. etc...

I did achieve what I wanted by adding NOLOAD attibute to my custom section :
.pds (NOLOAD): { KEEP(*(.pds)) } >RAM
Here is the NOLOAD description (gcc documentation) :
(NOLOAD)
The (NOLOAD) directive will mark a section to not be loaded at run time. The linker will process the section normally, but will mark
it so that a program loader will not load it into memory. For example,
in the script sample below, the ROM section is addressed at memory
location 0 and does not need to be loaded when the program is run.
The contents of the ROM section will appear in the linker output file
as usual.
SECTIONS {
ROM 0 (NOLOAD) : { ... }
...
}
I found a similar post which helped me, I add a link here for reference : GCC (NOLOAD) directive loads memory into section anyway

Related

Kernel panic on simple loadable kernel module

I`m trying to create an elementary loadable kernel module to embedded OpenWRT system on the Ralink 3050 SOC (processor MIPS 24KEs). The target system built with MODULES=y and CONFIG_MODULE_UNLOAD=y options. I work under Ubuntu 20 X86 machine.
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("test");
int init_module(void) {
printk(KERN_INFO "Hello, world!\n");
return 0;
}
void cleanup_module(void) {
printk(KERN_INFO "Goodbye, world!\n");
}
The first time, I`m was trying to compile module by the built-in OpenWRT buildroot cross-compiler:
make ARCH=mips CROSS_COMPILE=mipsel-openwrt-linux-musl-
The kernel headers included the OpenWRT buildroot were used.
Result, fail: after insmod load module was marked as [permanent]. I.e. the cleanup_module procedure was not seen by the kernel. I assumed that problem in a file structure. I`m was checking the vermagic signature (to an other system module that is workable): It matched.
$modinfo module.ko -> vermagic: 5.10.100 mod_unload MIPS32_R2 32BIT
Just in case, I had rebuilt the target system, may be it`s stochastic errors? But the result is the same…
Then I decided to build a custom cross-compiler and original kernel headers. I had downloaded and installed the binutils 2.38 and gcc 11.2.0 (same as at the OpenWRT buildroot) and was downloaded and prepared the kernel header with same .config file (kernel 5.10.100 – same as on the target system).
Now the next result:
After compilation make ARCH=mips CROSS_COMPILE=mipsel-unknown-linux-gnu- by custom cross compiler the module loads, but any next callers lsmod, rmmod or insmod finished by kernel panic. Looks like, the module corrupts kernel memory. If the module compiled without the cleanup_module procedure, it loads correct but have status [permanent] and can`t be unloaded.
I tried to change the function declaration: used __exit and static specificator – no result.
root#(none):/# lsmod
[ 456.197482] CPU 0 Unable to handle kernel paging request at virtual address 034881e0, epc == 80068f78, ra == 80068f64
[ 456.218804] Oops[#1]:
[ 456.223350] CPU: 0 PID: 1017 Comm: lsmod Not tainted 5.10.100 #0
[ 456.235314] $ 0 : 00000000 00000001 00000000 00000001
[ 456.245763] $ 4 : 8160200e 80320b64 00000000 00000001
[ 456.256206] $ 8 : 00000020 00000002 00000001 00000000
[ 456.266645] $12 : 7faff400 77eca2b0 77ec0020 7faff43f
[ 456.277090] $16 : 81a440a4 816110d8 034881d0 80320b64
[ 456.287534] $20 : 80380000 81a441d0 816110f0 81611100
[ 456.297977] $24 : 00000000 80101500
[ 456.308420] $28 : 81a46000 81a47d60 00000002 80068f64
[ 456.318864] Hi : 00000000
[ 456.324594] Lo : 0a3d70a4
[ 456.330333] epc : 80068f78 0x80068f78
[ 456.337973] ra : 80068f64 0x80068f64
[ 456.345609] Status: 1100e403 KERNEL EXL IE
[ 456.353965] Cause : 00800008 (ExcCode 02)
[ 456.361946] BadVA : 034881e0
[ 456.367679] PrId : 0001964c (MIPS 24KEc)
[ 456.375658] Modules linked in: module rt2800soc rt2800mmio rt2800lib rt2x00soc rt2x00mmio rt2x00lib mac80211 cfg80211 crc_ccitt compat sha256_generic libsha256 seqiv jitterentropy_rng drbg hmac cmac leds_gpio crc32c_generic
[ 456.415692] Process lsmod (pid: 1017, threadinfo=1acffaea, task=80d5ed61, tls=77ecbdd4)
[ 456.431637] Stack : 81621700 00400cc0 00000000 00000001 ffffffff 800b6700 81847c80 00002000
[ 456.448345] 00000000 81a47e18 00000000 a050f4de 81847c80 816110d8 81a440a4 00000000
[ 456.465053] 81a47f00 81a47e38 81a47e20 801011d8 00000001 00000001 00000000 00000601
[ 456.481760] 816e2904 800c7f98 77ec1000 81a47e8c 80584860 00000000 00000000 00000000
[ 456.498467] 81847c80 81a47f00 00000400 00000000 00000000 00000003 00000002 80101630
[ 456.515174] ...
[ 456.520050] Call Trace:
[ 456.520071] [<800b6700>] 0x800b6700
[ 456.531895] [<801011d8>] 0x801011d8
[ 456.538848] [<800c7f98>] 0x800c7f98
[ 456.545803] [<80101630>] 0x80101630
[ 456.552751] [<800c1bdc>] 0x800c1bdc
[ 456.559699] [<80018628>] 0x80018628
[ 456.566665] [<800df8f0>] 0x800df8f0
[ 456.573627] [<800faae8>] 0x800faae8
[ 456.580579] [<800de2b0>] 0x800de2b0
[ 456.587531] [<800dfbf0>] 0x800dfbf0
[ 456.594479] [<800c7e2c>] 0x800c7e2c
[ 456.601433] [<8000d36c>] 0x8000d36c
[ 456.611364] Сode: 00001025 10000007 26730b64 <8e460010> 24c6000c 0c040199 02202025 8e520000 24020001
UPD
Did you include calls to the module_init() and module_exit() macros to
register the functions as init/exit? Seems like you didn't from your
example. Also, you should add __init and __exit annotations. See this
simple example of mine. – Marco Bonelli
Yes, I tried to use the macro module_init() and module_exit(). It has no result. Anyway, this macro just translates the init and clean function name to the canonical function name: init_module and cleanup_module, like the main function in a user space program. Prefix __init and __exit also has no effect.
Smells like you use a compiler which configuration is not suitable for
the kernel. You could try to debug "Unable to handle kernel paging
request" error. – Tsyvarev
What is a "compiler configuration" that specifies the user or kernel object file type?
If we look (using readelf -a utility) at the difference between my module and any built-in module, we will see that the entry points of the init_module method in the .rel.gnu.linkonce.this_module section are identical, but the entry points of the cleanup_module are different.
Relocation section '.rel.gnu.linkonce.this_module' at offset 0x594 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
000000d0 00001d02 R_MIPS_32 00000000 init_module
00000130 00001c02 R_MIPS_32 00000000 cleanup_module
Relocation section '.rel.gnu.linkonce.this_module' at offset 0x1c8c contains 2 entries:
Offset Info Type Sym.Value Sym. Name
000000d0 00000a02 R_MIPS_32 00000000 _1
00000140 00000902 R_MIPS_32 00000000 _0

GNU LD for ARM produces section alignment to unwanted bound

I'm building an embeeded software for STM32 microcontroller with the toolchain GNU Tools for STM32 and I need the binary output without gaps.
The linker produces a gap between sections .text and .rodata. The problem is the alignment of the section .rodata. The issue appears by using of the GNU Tools for STM32 version 9-2020-q2-update. The previous version I had used (7-2018-q2-update) did not produced that issue.
Excerpt from the linker script (it's the same for both GNU Tools versions):
SECTIONS
{
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4); /* PaulV: change that to ALIGN(8) eliminates the gap */
} >FLASH
/* Constant data into "FLASH" Rom type memory */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
}
More details:
The version 7-2018-q2-update produces the output without gap.
The .lst file (note that section .rodata is aligned to bound 4):
K4_G1.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
....
3 .text 0001a20c 08100800 08100800 00010800 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .rodata 00009b54 0811aa0c 0811aa0c 0002aa0c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
and .map file (no gap between the non-empty sections .fini and .rodata):
.fini 0x000000000811aa04 0x8 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv5/hard/crtn.o
0x000000000811aa0c . = ALIGN (0x4)
0x000000000811aa0c _etext = .
.vfp11_veneer 0x000000000811aa0c 0x0
.vfp11_veneer 0x000000000811aa0c 0x0 linker stubs
.v4_bx 0x000000000811aa0c 0x0
.v4_bx 0x000000000811aa0c 0x0 linker stubs
.iplt 0x000000000811aa0c 0x0
.iplt 0x000000000811aa0c 0x0 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv5/hard/crtbegin.o
.rodata 0x000000000811aa0c 0x9b54
0x000000000811aa0c . = ALIGN (0x4)
*(.rodata)
.rodata 0x000000000811aa0c 0x8c Src/app_composer/init.o
The version 9-2020-q2-update produces the output with gap.
The .lst file (note that section .rodata is aligned to bound 8, but why?):
K4_G1.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
...
3 .text 0001923c 08100800 08100800 00010800 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .rodata 000061f0 08119a40 08119a40 00029a40 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
and .map file (there is a gap between the non-empty sections .fini and .rodata):
.fini 0x0000000008119a34 0x8 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.9-2020-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v7e-m+dp/hard/crtn.o
.vfp11_veneer 0x0000000008119a3c 0x0
.vfp11_veneer 0x0000000008119a3c 0x0 linker stubs
.v4_bx 0x0000000008119a3c 0x0
.v4_bx 0x0000000008119a3c 0x0 linker stubs
.iplt 0x0000000008119a3c 0x0
.iplt 0x0000000008119a3c 0x0 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.9-2020-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v7e-m+dp/hard/crtbegin.o
.rodata 0x0000000008119a40 0x61f0
0x0000000008119a40 . = ALIGN (0x4)
*(.rodata)
.rodata 0x0000000008119a40 0x96 Src/app_composer/init.o
Edit 03/16/2021
There are no sections *(.rodata) in the input object files having
alignment on the boundary 8 or greater.
Changing the section name .rodata to the name .text eliminates the
gap (The same result if I join the sections .text and .rodata content to a
single .text section):
SECTIONS
{
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
/* ... */
. = ALIGN(4);
} >FLASH
/* Constant data into "FLASH" Rom type memory */
.text : /* <-- the same name as the previous section instead of .rodata */
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
}
The source code and build settings are also the same for both variants.
What could be the reason for the problem and how could it be solved? Do I missing something?
P.S. Of cause I can change the alignment at the end of .text section to 8. That would be treating the symptoms, but I want to understand the cause.
Thanks in advance for your help!
Run objdump -h on the input object files. I suspect that you will find that the compiler is putting a minimum alignment of 8 on one of the input .rodata sections. The linker then sets the output alignment to the maximum of the input sections.

ARM + gcc: global destructors not called after main() returns, but constructors are

I am trying to write a simple "Hello, World!" firmware for Cortex-M0 CPU. The goal is to correctly initialize and shutdown C++ runtime so that global constructors are called before main() and global destructors are called after main().
So far I managed to get exactly half of it working - global ctors run correctly, but global destructors don't. I use gcc on Windows with the Newlib, which is supposed to initialize the runtime.
struct Test {
Test() // This is invoked correctly.
{}
~Test() // This is not invoked at all.
{}
};
Test test;
int main()
{
return 0;
}
extern void __libc_init_array();
extern void __libc_fini_array();
void reset_handler() // This is the entry point.
{
__libc_init_array(); // Test::Test().
asm volatile( "bl main" ); // Invoke 'main'.
__libc_fini_array(); // Expect Test::~Test() but nothing happens.
}
I did quite a bit of research already, and it seems that for ARM compiler should generate a section called .init_array, for global constructors, and .fini_array, for global destructors, where it put pointer to the function to be invoked. Then, the linker will merge the sections from all units together, and __libc_init_array will "walk" through that section and call corresponding functions.
This is the relevant part of the linker script:
/* Initialization functions which run before main(),
such as global constructors. */
.init_array : ALIGN( 4 ) {
/* preinit data */
PROVIDE_HIDDEN ( __preinit_array_start = . );
KEEP( *( .preinit_array ) )
PROVIDE_HIDDEN( __preinit_array_end = . );
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN ( __init_array_start = . );
KEEP( *( SORT( .init_array.* ) ) )
KEEP(*(.init_array))
PROVIDE_HIDDEN ( __init_array_end = . );
} > flash
/* Finalization functions which run after main(),
such as global destructors. */
.fini_array : ALIGN( 4 ) {
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP ( *( .fini_array.* ) )
KEEP ( *( .fini_array ) )
PROVIDE_HIDDEN (__fini_array_end = .);
} > flash
What worries me, however, is that when I dump the object file, main.o, not even an executable, I can only see the .init_array section, but no .fini_array:
Sections:
Idx Name Size VMA LMA File off Algn
0 .group 00000008 00000000 00000000 00000034 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
1 .group 00000008 00000000 00000000 0000003c 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
2 .text 0000022c 00000000 00000000 00000044 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
3 .data 00000000 00000000 00000000 00000270 2**0
CONTENTS, ALLOC, LOAD, DATA
4 .bss 00000001 00000000 00000000 00000270 2**2
ALLOC
5 .text._ZN4TestC2Ev 00000012 00000000 00000000 00000270 2**1 << Test::Test()
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .text._ZN4TestD2Ev 00000012 00000000 00000000 00000282 2**1 << Test::~Test()
CONTENTS, ALLOC, LOAD, READONLY, CODE
7 .init_array 00000004 00000000 00000000 00000294 2**2 << WHERE IS .fini_array???
CONTENTS, ALLOC, LOAD, RELOC, DATA
8 .debug_info 000012d9 00000000 00000000 00000298 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
9 .debug_abbrev 000003a1 00000000 00000000 00001571 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
10 .debug_aranges 00000030 00000000 00000000 00001912 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
11 .debug_ranges 00000020 00000000 00000000 00001942 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
12 .debug_line 000002d2 00000000 00000000 00001962 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
13 .debug_str 000009e4 00000000 00000000 00001c34 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
14 .comment 0000004d 00000000 00000000 00002618 2**0
CONTENTS, READONLY
15 .debug_frame 0000054c 00000000 00000000 00002668 2**2
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
16 .ARM.attributes 0000002c 00000000 00000000 00002bb4 2**0
CONTENTS, READONLY
This is how I invoke the compiler:
arm-none-eabi-c++.exe -o main.elf --verbose -mcpu=cortex-m0 -mthumb --specs=nano.specs --entry reset_handler -T./../src/firmware.ld -ggdb -O0 -Wall -Wextra -Wpedantic -Werror ../src/main.cpp
And this is the log:
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 7a1ab17ae8404f635d46188cccacd8be
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/as.exe -v -march=armv6s-m -mfloat-abi=soft -meabi=5
GNU assembler version 2.34.0 (arm-none-eabi) using BFD version (GNU Arm Embedded Toolchain 9-2020-q2-update) 2.34.0.20200428
COMPILER_PATH=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/;c:/id/gcc/bin/../lib/gcc/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/
LIBRARY_PATH=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp/;c:/id/gcc/bin/../arm-none-eabi/lib/thumb/v6-m/nofp/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/;c:/id/gcc/bin/../lib/gcc/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/;c:/id/gcc/bin/../arm-none-eabi/lib/
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/collect2.exe -plugin c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/liblto_plugin-0.dll -plugin-opt=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/lto-wrapper.exe -plugin-opt=-fresolution=C:\Users\MAXID~1\AppData\Local\Temp\ccpOTkFN.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lg_nano -plugin-opt=-pass-through=-lc_nano -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc_nano --sysroot=c:\id\gcc\bin\../arm-none-eabi -X -o main.elf -e reset_handler c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crti.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtbegin.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp/crt0.o -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp -Lc:/id/gcc/bin/../arm-none-eabi/lib/thumb/v6-m/nofp -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1 -Lc:/id/gcc/bin/../lib/gcc -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib -Lc:/id/gcc/bin/../arm-none-eabi/lib C:\Users\MAXID~1\AppData\Local\Temp\cc1lU5Gg.o -lstdc++_nano -lm --start-group -lgcc -lg_nano -lc_nano --end-group --start-group -lgcc -lc_nano --end-group c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtend.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtn.o -T ./../src/firmware.ld
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
To be fair, I have zero idea of what could be wrong - I tried the default linker script, .fini_array did not appear.
The only idea which comes to mind is that my compiler was built with some kind of a flag which disabled the .fini_array section???
Any ideas will be much appreciated!
UPDATE: after further investigation, it seems that the destructors have more to do with __cxa_atexit... Will investigate further.
UPDATE2: Thanks to this wonderful discussion here https://forum.osdev.org/viewtopic.php?f=13&t=36728 I added -fno-use-cxa-atexit, and the .fini_array section appeared!
Ok, so for those who are interested, there are two ways for gcc to generate calls to global destructors. One way is via __cxa_atexit. The compiler will inject a piece of code into the global constructor, which uses __cxa_atexit to register the destructor of that object. This way, when exit is called, the runtime "knows" which destructors to invoke. This complexity is needed to deal with loading/unloading of shared libraries.
However, it is also possible to pass a -fno-cxa-atexit flag, and the compiler will put the calls to global destructors into .fini_array. The Newlib then uses this section to invoke the destructors.
More can be found here: https://forum.osdev.org/viewtopic.php?f=13&t=36728

gcc / ld: overlapping sections (.tbss, .init_array) in statically-linked ELF binary

I'm compiling a very simple hello-world one-liner statically on Debian 7 system on x86_64 machine with gcc version 4.8.2 (Debian 4.8.2-21):
gcc test.c -static -o test
and I get an executable ELF file that includes the following sections:
[17] .tdata PROGBITS 00000000006b4000 000b4000
0000000000000020 0000000000000000 WAT 0 0 8
[18] .tbss NOBITS 00000000006b4020 000b4020
0000000000000030 0000000000000000 WAT 0 0 8
[19] .init_array INIT_ARRAY 00000000006b4020 000b4020
0000000000000010 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 00000000006b4030 000b4030
0000000000000010 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 00000000006b4040 000b4040
0000000000000008 0000000000000000 WA 0 0 8
[22] .data.rel.ro PROGBITS 00000000006b4060 000b4060
00000000000000e4 0000000000000000 WA 0 0 32
Note that .tbss section is allocated at addresses 0x6b4020..0x6b4050 (0x30 bytes) and it intersects with allocation of .init_array section at 0x6b4020..0x6b4030 (0x10 bytes), .fini_array section at 0x6b4030..0x6b4040 (0x10 bytes) and with .jcr section at 0x6b4040..0x6b4048 (8 bytes).
Note it does not intersect with the following sections, for example, .data.rel.ro, but that's probably because .data.rel.ro alignment is 32 and thus it can't be placed any earlier than 0x6b4060.
The resulting file runs ok, but I still don't exactly get how it works. From what I read in glibc documentation, .tbss is a just .bss section for thread local storage (i.e. allocated memory scratch space, not really mapped in physical file). Is it that .tbss section is so special that it can overlap other sections? Are .init_array, .fini_array and .jcr are so useless (for example, they are not needed anymore then TLS-related code runs), so they can be overwritten by bss? Or is it some sort of a bug?
Basically, what do I get to read and write if I'll try to read address 0x6b4020 in my application? .tbss contents or .init_array pointers? Why?
The virtual address of .tbss is meaningless as that section only serves as a template for the TLS storage as allocated by the threading implementation in GLIBC.
The way this virtual address comes into place is that .tbss follows .tbdata in the default linker script:
...
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
}
...
therefore its virtual address is simply the virtual address of the preceding section (.tbdata) plus the size of the preceding section (eventually with some padding in order to reach the desired alignment). .init_array (or .preinit_array if present) comes next and its location should be determined the same way, but .tbss is known to be so very special, that it is given a deeply hard-coded treatment inside GNU LD:
/* .tbss sections effectively have zero size. */
if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
|| (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
|| link_info.relocatable)
dotdelta = TO_ADDR (os->bfd_section->size);
else
dotdelta = 0; // <----------------
dot += dotdelta;
.tbss is not relocatable, it has the SEC_THREAD_LOCAL flag set, and it does not have contents (NOBITS), therefore the else branch is taken. In other words, no matter how large the .tbss is, the linker does not advance the location of the section that follows it (also know as "the dot").
Note also that .tbss sits in a non-loadable ELF segment:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000b1f24 0x00000000000b1f24 R E 200000
LOAD 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000
0x0000000000002288 0x00000000000174d8 RW 200000
NOTE 0x0000000000000158 0x0000000000400158 0x0000000000400158
0x0000000000000044 0x0000000000000044 R 4
TLS 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000 <---+
0x0000000000000020 0x0000000000000060 R 8 |
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 |
0x0000000000000000 0x0000000000000000 RW 8 |
|
Section to Segment mapping: |
Segment Sections... |
00 .note.ABI-tag ... |
01 .tdata .ctors ... |
02 .note.ABI-tag ... |
03 .tdata .tbss <---------------------------------------------------+
04
This is rather simple if you have an understanding about two things:
1) What is SHT_NOBITS
2) What is tbss section
SHT_NOBITS means that this section occupies no space inside file.
Normally, NOBITS sections, like bss are placed after all PROGBITS sections at the end of the loaded segments.
tbss is special section to hold uninitialized thread-local data that contribute to the program's memory image. Take an attention here: this section must hold unique data for each program thread.
Now lets talk about overlapping. We have two possible overlappings -- inside binary file and inside memory.
1) Binary files offset:
There is no data to write under this section in binary. Inside file it holds no space, so linker start next section init_array immediately after tbss declared. You may think about its size not as about size, but as about special service information for code like:
if (isTLSSegment) tlsStartAddr += section->memSize();
So it doesn't overlap anything inside file.
2) Memory offset
The tdata and tbss sections may be possibly modified at startup time by the dynamic linker
performing relocations, but after that the section data is kept around as the initialization image and not modified anymore. For each thread, including the initial one, new memory is allocated into which then the content of the initialization image is copied. This ensures that all threads get the same starting conditions.
This what makes tbss (and tdata) so special.
Do not think about their memory offsets as about statically known -- they are more like "generation patterns" for per-thread work. So they also can not overlap with "normal" memory offsets -- they are being processed in other way.
You may consult with this paper to know more.

Unusual LD warning when compiling for ARM

I have tried both "arm-none-eabi-gcc" and "arm-elf-gcc" installed via MacPorts, but every time I compile, I get this warning.
ld: warning: cannot find entry symbol _start; defaulting to 0000000000008000
I am using the "-T" flag and specifying my own linker file which is as follows.
SECTIONS {
. = 0x00000000;
.text : { * (vectors); * (.text); }
.rodata : { * (.rodata); }
text_end = .;
. = 0xA4000000;
.data : AT (text_end) { * (.data); }
.bss : { * (.bss); }
}
NM dumps this.
00000000 t reset
00000004 t undefined
00000008 t swi
0000000c t prefetch_abort
00000010 t data_abort
00000014 t reserved
00000018 t interrupt_request
0000001c t fiq
00000020 t irq
00000024 T init
00000038 T main
0000004c A text_end
00008024 t entry
0000804c T __data_start
00010028 A __bss_end__
00010028 A __bss_start
00010028 A __bss_start__
00010028 A __end__
00010028 A _bss_end__
00010028 A _edata
00010028 A _end
00080000 N _stack
Linker is giving you a warning because it is not able to find a _start symbol in compiled code. And your current linker configuration is expecting that there should be such symbol defined somewhere in the code.
So you either have solution to manually define your entry point (i.e. reset) by consulting the compiler/linker user manual and understanding the usage of -e linker flag or really define a symbol named _start in some of your code.

Resources