I'm tring to use gcc link-time optimizer, but encounter some issue related to the gcc linker cannot correctly resolving references to symbols in 'newlib_syscalls.c'. However, same source code , libraries and compile options, clang works just fine.
Here is the output of clang:
❯ CC=clang make
clang -mlittle-endian -mcpu=cortex-a55 -mtune=cortex-a55 -target aarch64-none-elf --sysroot=/Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/../aarch64-none-elf -flto -c startup.c -o startup.o
clang -mlittle-endian -mcpu=cortex-a55 -mtune=cortex-a55 -target aarch64-none-elf --sysroot=/Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/../aarch64-none-elf -flto -ffat-lto-objects -c newlib_syscalls.c -o newlib_syscalls.o
clang -static -nostdlib -T link.ld -target aarch64-none-elf --sysroot=/Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/../aarch64-none-elf -flto -o test.elf startup.o newlib_syscalls.o -lc /Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/../lib/gcc/aarch64-none-elf/13.2.1/libgcc.a
Here is the output of gcc:
❯ CC=aarch64-none-elf-gcc make
aarch64-none-elf-gcc -mlittle-endian -mcpu=cortex-a55 -mtune=cortex-a55 -flto -c startup.c -o startup.o
aarch64-none-elf-gcc -mlittle-endian -mcpu=cortex-a55 -mtune=cortex-a55 -flto -ffat-lto-objects -c newlib_syscalls.c -o newlib_syscalls.o
aarch64-none-elf-gcc -static -nostdlib -T link.ld -flto -o test.elf startup.o newlib_syscalls.o -lc /Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/../lib/gcc/aarch64-none-elf/13.2.1/libgcc.a
/Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/../lib/gcc/aarch64-none-elf/13.2.1/../../../../aarch64-none-elf/bin/ld: /Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/../lib/gcc/aarch64-none-elf/13.2.1/../../../../aarch64-none-elf/lib/libc.a(libc_a-closer.o): in function `_close_r':
/Volumes/data/jenkins/workspace/GNU-toolchain/arm-13/src/newlib-cygwin/newlib/libc/reent/closer.c:47:(.text._close_r+0x1c): undefined reference to `_close'
...
And I do check the newlib_syscalls.o, it has the object codes of these symbols.
❯ aarch64-none-elf-nm newlib_syscalls.o
000000000000006c T _close
00000000000000d4 T _exit
0000000000000080 T _fstat
00000000000000f8 T _getpid
00000000000000a4 T _isatty
00000000000000e0 T _kill
00000000000000b8 T _lseek
000000000000011c T _read
0000000000000000 T _sbrk
0000000000000100 T _write
U end
0000000000000000 b heap.0
I add '-ffat-lto-objects' to newlib_syscalls.c to make sure it generate the object code, cuz the libc.a come with gnu toolchain didn't build with '-flto', do not have GIMPLE bytecode, I was concerned maybe only GIMPLE is not compatible with the libc.a, anyways it doesn't work either, and clang works with or without '-ffat-lto-objects'.
Here is my example project:
/*startup.c*/
#include <stdio.h>
void entrypoint()
{
printf("Hello, World!\n");
}
/*newlib_syscalls.c*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <errno.h>
#include <sys/stat.h>
extern int end;
void *_sbrk(int incr) {
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL) {
heap = (unsigned char *)&end;
}
prev_heap = heap;
heap += incr;
return prev_heap;
}
int _close(int file) {return -1;}
int _fstat(int file, struct stat *st) { st->st_mode = S_IFCHR; return 0; }
int _isatty(int file) { return 1; }
int _lseek(int file, int ptr, int dir) { return 0; }
void _exit(int status) {while(1) asm volatile("");}
void _kill(int pid, int sig) {return;}
int _getpid(void) {return -1;}
int _write (int file, char * ptr, int len) {return len;}
int _read (int file, char * ptr, int len) {return len;}
/*link.ld*/
OUTPUT_FORMAT("elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(entrypoint)
MEMORY {
ROM (rx): ORIGIN = 0x00000000, LENGTH = 256K
SRAM (rwx): ORIGIN = 0x00100000, LENGTH = 512K
DRAM (rwx): ORIGIN = 0x40000000, LENGTH = 0xC0000000
}
SECTIONS
{
.ro :
{
_ro_start = .;
*entrypoint*(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(SORT_BY_ALIGNMENT(.rodata*))
*(.vectors)
_ro_end = .;
} >ROM
.data :
ALIGN(32)
{
_data_start = .;
*(SORT_BY_ALIGNMENT(.data*))
. = ALIGN(32);
_data_end = .;
} >SRAM AT>ROM
_data_start_lma = LOADADDR(.data);
_data_size = SIZEOF(.data);
.bss (NOLOAD) :
ALIGN(32)
{
_bss_start = .;
*(SORT_BY_ALIGNMENT(.bss*))
*(COMMON)
. = ALIGN(32);
_bss_end = .;
} >SRAM
.heap (NOLOAD) :
{
end = .;
_heap_start = .;
. += 0x4000;
_heap_end = .;
} >SRAM
.stack (NOLOAD) :
{
_stack_start = .;
. += 0x4000;
_stack_end = .;
_stack_top = .;
} >SRAM
_all_end = .;
/DISCARD/ : { *(.eh_frame*) }
}
# Makefile
CC ?= aarch64-none-elf-gcc
CFLAGS = -mlittle-endian -mcpu=cortex-a55 -mtune=cortex-a55
LTO = -flto
LDFLAGS = -static -nostdlib -T link.ld
EXTRA_LDFLAGS = -lc $(shell aarch64-none-elf-gcc -print-libgcc-file-name)
ifeq ($(CC),clang)
CFLAGS += -target aarch64-none-elf --sysroot=$(shell aarch64-none-elf-gcc -print-sysroot)
LDFLAGS += -target aarch64-none-elf --sysroot=$(shell aarch64-none-elf-gcc -print-sysroot)
endif
newlib_syscalls.o :
$(CC) $(CFLAGS) $(LTO) -ffat-lto-objects -c newlib_syscalls.c -o $@
startup.o :
$(CC) $(CFLAGS) $(LTO) -c startup.c -o $@
test.elf : startup.o newlib_syscalls.o
$(CC) $(LDFLAGS) $(LTO) -o $@ $? $(EXTRA_LDFLAGS)
.PHONY : clean
clean :
rm -f *.o *.elf
.DEFAULT_GOAL := test.elf
# ❯ aarch64-none-elf-gcc --version
# aarch64-none-elf-gcc (Arm GNU Toolchain 13.2.rel1 (Build arm-13.7)) 13.2.1 20231009
# Copyright (C) 2023 Free Software Foundation, Inc.
# This is free software; see the source for copying conditions. There is NO
# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# ❯ clang --version
# Homebrew clang version 18.1.7
# Target: arm64-apple-darwin23.5.0
# Thread model: posix
# InstalledDir: /opt/homebrew/opt/llvm/bin
-( ... -)