Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 327
Mark Avatar asked Dec 05 '12 23:12

Mark


3 Answers

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.

like image 145
wiRe Avatar answered Nov 04 '22 10:11

wiRe


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.

like image 28
noahp Avatar answered Nov 04 '22 10:11

noahp


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

like image 39
Marco van de Voort Avatar answered Nov 04 '22 11:11

Marco van de Voort