Why do I have an undefined reference to _init in __libc_init_array?
Asked Answered
D

3

11

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.

Declamation answered 5/12, 2012 at 23:56 Comment(0)
V
5

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

Vlf answered 6/12, 2012 at 16:58 Comment(5)
So, if I add void _init(void){} to my startup code, it compiles fine. I wonder what this function is supposed to do?Declamation
_init is usually the point in the binary the OS jumps to if a binary has been loaded. A default label for the entry point more or less. How to fix the problem depends on how your embedded loader system is set up. To me it sounds like that this toolchain is not perfectly ported.Vlf
I don't have an "embedded loader system", I wrote the startup code myself (or, more specifically I copy+pasted it from the internet)... This is a Cortex-M4, and the ResetHandler is the very first (and only) thing executed.Declamation
Who what where whatever. That startup code and the library system seems mismatched.Vlf
In my case I had to use -nodefaultlibs to make the C++ codes for STM32 link without the "missing _init" error.Gottwald
T
7

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.

Teapot answered 9/8, 2013 at 18:35 Comment(0)
V
5

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

Vlf answered 6/12, 2012 at 16:58 Comment(5)
So, if I add void _init(void){} to my startup code, it compiles fine. I wonder what this function is supposed to do?Declamation
_init is usually the point in the binary the OS jumps to if a binary has been loaded. A default label for the entry point more or less. How to fix the problem depends on how your embedded loader system is set up. To me it sounds like that this toolchain is not perfectly ported.Vlf
I don't have an "embedded loader system", I wrote the startup code myself (or, more specifically I copy+pasted it from the internet)... This is a Cortex-M4, and the ResetHandler is the very first (and only) thing executed.Declamation
Who what where whatever. That startup code and the library system seems mismatched.Vlf
In my case I had to use -nodefaultlibs to make the C++ codes for STM32 link without the "missing _init" error.Gottwald
R
4

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.

Rossman answered 20/5, 2021 at 23:19 Comment(1)
Hi, at first thanks for the answer. I had a undefined reference to _init when linking, and resolved it by just implementing it and doing nothing in the function. For some reason using "--specs=nano.specs" did not do anything, so i am not quite sure about using newlib-nano and automatic linking to crti.o and crtn.o, because for me i still got an undefined reference to _init..Scrivings

© 2022 - 2024 — McMap. All rights reserved.