Greetings, On 5/11/20 8:40 AM, Joel Sherrill wrote: > Then why isn't a linker script provided which provides this? This behavior > is in sharp contrast with other CPU-elf targets with libgloss support. They > can produce executables out of the box with no requirements like this. I am afraid I do not have a good answer for this. A cursory look indicates that the default linker script for Aarch64 is being provided by ld from binutils [or whatever other linker is being used], and not newlib. The linker script in question may not fit the requirements of the simulator, or vice versa. I believe a more proper question would be ``why does the default linker script not create a binary that is readily runnable in the simulator?'' but I can not say whether this is proper, and I do not have an answer either. For actual hardware platforms, the norm is to provide your own linker script. This is because the linker script helps tailor the binary to the memory map of the device. For example, in the M profile for both ARMv{6,7} and ARMv8, the SRAM region starts at address 0x20000000 and can for up to 512 MiB [excluding any bit banding regions that may be present]. However, an MCU using these architectures will have various amounts of memory. The linker script then sets the stack pointer by providing a symbol that gets populated at address 0 of the vector table. In my linker script for an STM32F407, I have: OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(__reset) /* the hardware does not care about this */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K RAM (rxw) : ORIGIN = 0x20000000, LENGTH = 128K } SECTIONS { .vector_table : { KEEP(*(.vector_table)) } > FLASH /* [snip] */ __ram_end = ORIGIN(RAM) + LENGTH(RAM); /* initial msp value */ } Then, in my initialization code: .syntax unified .thumb .section .vector_table, "aw" .globl __vector_table __vector_table: .word __ram_end /* initial stack pointer */ .word __reset /* reset vector/entry point */ .word __vector_2 /* non-maskable interrupt */ /* rest of the vector table */ .section .text .global __reset .type __reset, %function __reset: /* initialize .data region */ ldr r0, =__data_start ldr r1, =__data_end ldr r2, =__data_init /* more initialization code */ When the CPU boots, it reads the vector table, initializes msp [main stack pointer] using the value in entry 0, then initializes pc using the value in entry 1. It then goes into thread mode and starts executing the __reset vector. Yes, this completely bypasses libgloss, but I don't really use most of newlib's or libgloss's facilities on my projects, only the sporadic call to some basic C function. I believe the simulator instead uses whatever the ELF says the entry point is and initializes pc using that value. Technically speaking, on actual freestanding hardware we use whatever CMSIS package the vendor gives us. The CMSIS package provides both vector table initialization, and memory region initialization, completely bypassing the C runtime code in libgloss. CMSIS can go to do other things, like enabling the FPU, and setting up the PLL for the CPU to achieve a desired frequency. For the most part, CMSIS-Core is standard across vendors, with only minor variations in the vector table creation, peripheral/clock initialization code, and linker scripts. Cheers, Orlando.