I'm attempting to build a simple project using Yagarto and Eclipse for an ARM microcontroller platform. In my startup code, I have this (which I believe is fairly standard and uninteresting):
void Reset_Handler(void)
{
/* Initialize data and bss */
__Init_Data();
/* Call CTORS of static objects */
__libc_init_array();
/* Call the application's entry point.*/
main();
while(1) { ; }
}
Unless I comment out the call to __libc_init_array()
, I get the following error from the linker:
arm-none-eabi-g++ -nostartfiles -mthumb -mcpu=cortex-m4 -TC:/Users/mark/workspace/stm32_cpp_test/STM32F40x_1024k_192k_flash.ld -gc-sections -Wl,-Map=test_rom.map,--cref,--no-warn-mismatch -o stm32_cpp_test "system\\syscalls.o" "system\\startup_stm32f4xx.o" "system\\mini_cpp.o" "system\\cmsis\\system_stm32f4xx.o" main.o
d:/utils/yagarto/bin/../lib/gcc/arm-none-eabi/4.7.2/../../../../arm-none-eabi/lib/thumb/v7m\libg.a(lib_a-init.o): In function `__libc_init_array':
C:\msys\1.0\home\yagarto\newlib-build\arm-none-eabi\thumb\v7m\newlib\libc\misc/../../../../../../../newlib-1.20.0/newlib/libc/misc/init.c:37: undefined reference to `_init'
collect2.exe: error: ld returned 1 exit status
Why am I getting this "undefined reference" error? What am I missing? I assume there's some linker flag that I'm missing, but I can't for the life of me figure out what.
The __libc_init_array function from stdlib takes care to call all initializers or C++ constructors, registered to preinit_array and init_array. Inbetween preinit and init, it calls an extern _init function. The code looks as simple as:
#include <sys/types.h>
/* These magic symbols are provided by the linker. */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));
extern void _init (void);
void __libc_init_array (void)
{
size_t count;
size_t i;
count = __preinit_array_end - __preinit_array_start;
for (i = 0; i < count; i++)
__preinit_array_start[i] ();
_init ();
count = __init_array_end - __init_array_start;
for (i = 0; i < count; i++)
__init_array_start[i] ();
}
Also see: understanding the __libc_init_array.
If a custom startup code is implemented, it is required to perform this initialization, either by linking to 'init.o' or by implementing something similar to the code snippet above.
If at least building for arm-none-eabi ARMv7e target with newlib-nano specs, then the _init method gets linked in from crti.o and crtn.o, which provides just some empty stubs for _init and _fini. Did some search through all other stdlib objects for arm-none-eabi and found no other objects that will append sections to .init, which would be obsolete anyway. Here some disassembly of crti.o and crtn.o:
$ ./bin/arm-none-eabi-objdump.exe -j .init -D ./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crt?.o
./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crti.o: file format elf32-littlearm
Disassembly of section .init:
00000000 <_init>:
0: b5f8 push {r3, r4, r5, r6, r7, lr}
2: bf00 nop
./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crtn.o: file format elf32-littlearm
Disassembly of section .init:
00000000 <.init>:
0: bcf8 pop {r3, r4, r5, r6, r7}
2: bc08 pop {r3}
4: 469e mov lr, r3
6: 4770 bx lr
If somebody wants to use __libc_init_array in combination with linker option nostartfiles for this specific ARM target, it would be acceptable to provide an own _init stub method, to let the linker pass, as long as no other initialization code is emitted to section .init, other than this from crti.o and crtn.o. A stub could look like:
extern "C" void _init(void) {;}
The special functions _init and _fini are some historic left-overs to control constructors and destructors. However, they are obsolete, and their use can lead to unpredictable results. No modern library should make use of these anymore, and make use of the GCC function attributes constructor and destructor instead, which add methods to those tables inside .preinit_array, .init_array and .fini_array sections.
If it is known that there was some initialization code emitted to .init (even if this is obsolete today), then a _init(void) function should be provided, that will be running this initialization code by calling the start address of the .init section.
Oldish question, but I encountered a similar issue and the solution was as Marco van de Voort indicated, if you're going to use __libc_init_array
you should omit the -nostartfiles
linker option, to include the normal libc init functions. Duplicate answer.
Secondly I would suggest including the --specs=nano.specs
flag when linking with gcc-arm (I believe yargarto is a fork or even just a precompile of gcc-arm), as it reduces libc etc. code consumption.
I'm no expert, but:
Probably _init (the normal runtime entry point) references the code that executes the ctor and dtor tables.
You use -nostartfiles so avoid the standard startup, and probably that whole startcode is eliminated by --gc-sections. The explicit call adds a reference again.
If omitting --gc-sections doesn't solve it, it might also be a missing keep() statement in your (embedded) linker script that keeps the entry code at all times, or your own startup code (startup_*) should reference it
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With