I'm trying to understand how bare metal C applications work exactly. I wrote my own startup assembly code that calls
what does symbols have to do with platform? is init_() generated by gcc on one platform and not on another?
Yes the startup and the epiloque routines are left to the implementation, and actually
gcc does not generate it.
the libc provides those sysmbols - https://github.com/bminor/newlib/blob/e0f24404b3fcfa2c332ae14c3934546c91be3f42/newlib/libc/misc/init.c
Depending on your target hardware the initialisation may be done completely different way.
Example STM32Fxxx startup.
.section .text.Reset_Handler .weak Reset_Handler .type Reset_Handler, %function Reset_Handler: ldr sp, =_estack /* Atollic update: set stack pointer */ /* Copy the data segment initializers from flash to SRAM */ movs r1, #0 b LoopCopyDataInit CopyDataInit: ldr r3, =_sidata ldr r3, [r3, r1] str r3, [r0, r1] adds r1, r1, #4 LoopCopyDataInit: ldr r0, =_sdata ldr r3, =_edata adds r2, r0, r1 cmp r2, r3 bcc CopyDataInit ldr r2, =_sbss b LoopFillZerobss /* Zero fill the bss segment. */ FillZerobss: movs r3, #0 str r3, [r2], #4 LoopFillZerobss: ldr r3, = _ebss cmp r2, r3 bcc FillZerobss /* Call the clock system intitialization function.*/ bl SystemInit /* Call static constructors */ bl __libc_init_array /* Call the application's entry point.*/ bl main
As you see in this implementation two functions are called - one
SystemInit for very low lever hardware initialisation and
it is an internal initialisation of the newlib library (nowadays most common used in the bare metal projects)
The problem is if you decide to do not use standard libraries and you do not want to link any standard libraries. Some tookchains provide weak functions with just the return statement, some not. If you are getting linker problems just comment this call in the startup file or provide an empty function yourself