Directly invoke the linker

I am setting up a cross-compiling C project targeting two micro architectures, inspired by ARM and MIPS. Without loss of generality, I’ll focus on the micro ARM architecture, where the compile instructions I was given are

arm-none-eabi-gcc <COMPILE_FLAGS> -o hello.o hello.c
arm-none-eabi-gcc <COMPILE_FLAGS> -o termprint.o termprint.c
arm-none-eabi-gcc <COMPILE_FLAGS> -o crtso.o ./uarm/crtso.s
arm-none-eabi-gcc <COMPILE_FLAGS> -o libuarm.o ./uarm/libuarm.s
arm-none-eabi-ld <LINK_FLAGS> -o kernel.uarm hello.o termprint.o crtso.o libuarm.o

I have tried to reproduce these instructions with CMake in the following snippet

# Project include, source and library dirs for uarm-related files
set(BKA_UARM_INC ${PROJECT_SOURCE_DIR}/${BKA_INC}/uarm)
set(BKA_UARM_SRC ${PROJECT_SOURCE_DIR}/${BKA_SRC}/uarm)

set(CFLAGS_UARM -mcpu=arm7tdmi)
set(LDFLAGS_UARM "-G 0 -nostdlib -T ${BKA_UARM_SRC}/elf32ltsarm.h.uarmcore.x")

add_compile_options(${CFLAGS_UARM})


add_library(libuarm OBJECT ${BKA_UARM_SRC}/libuarm.s)
add_library(crtso OBJECT ${BKA_UARM_SRC}/crtso.s)

target_include_directories(libuarm PRIVATE ${BKA_UARM_INC})

add_executable(kernel.uarm ${BKA_SRC}/phase0.c)
target_link_libraries(kernel.uarm crtso libuarm io)
set_property(TARGET kernel.uarm PROPERTY LINK_FLAGS ${LDFLAGS_UARM})

but they fail rather badly. Indeed, by choosing make as the build-system, I do

$ make VERBOSE=1 kernel.uarm

and get

[ 80%] Linking C executable kernel.uarm
/usr/bin/cmake -E cmake_link_script CMakeFiles/kernel.uarm.dir/link.txt --verbose=3
/usr/local/bin/arm-none-eabi-gcc   -rdynamic -G 0 -nostdlib -T /home/acsor/Software/BiKaya/src/uarm/elf32ltsarm.h.uarmcore.x CMakeFiles/kernel.uarm.dir/src/phase0.c.o CMakeFiles/crtso.dir/src/uarm/crtso.s.o CMakeFiles/libuarm.dir/src/uarm/libuarm.s.o CMakeFiles/io.dir/src/io.c.o  -o kernel.uarm
arm-linux-gnu-gcc: error: 0: No such file or directory
arm-linux-gnu-gcc: error: unrecognized command line option ‘-G’

This obviously is due to CMake running the compiler prior to linking, which does not recognize the -G option. My real intention here though is to completely skip the arm-linux-gnu-gcc invocation, and simply feed the linker likeso

arm-none-eabi-ld <LINK_FLAGS> -o kernel.uarm hello.o termprint.o crtso.o libuarm.o

Can I redefine the compile steps in some way? If not, any other way of properly compiling and linking my execuables will be very appreciated.

Use the -Wl option of GCC. Example:

set(LDFLAGS_UARM "-Wl,-G,0,-nostdlib,-T,${BKA_UARM_SRC}/elf32ltsarm.h.uarmcore.x")

Edit: simplified by removing duplicate -Wl options

Use preferably target_link_options which supports advanced capabilties (genex and “LINKER:” prefix). So we have:

target_link_options (kernel.uarm PRIVATE "LINKER:-G,0,-nostdlib,-${BKA_UARM_SRC}/elf32ltsarm.h.uarmcore.x")

Both the previous solutions give rise to a fresh new error – and it’s exactly the same

$ make VERBOSE=1 kernel.uarm
...
/usr/bin/cmake -E cmake_link_script CMakeFiles/kernel.uarm.dir/link.txt --verbose=1
/usr/local/bin/arm-none-eabi-gcc   -rdynamic "-Wl,-G,0,-nostdlib,-T /home/acsor/Software/BiKaya/src/uarm/elf32ltsarm.h.uarmcore.x" CMakeFiles/kernel.uarm.dir/src/phase0.c.o CMakeFiles/crtso.dir/src/uarm/crtso.s.o CMakeFiles/libuarm.dir/src/uarm/libuarm.s.o CMakeFiles/io.dir/src/io.c.o  -o kernel.uarm
/usr/bin/arm-linux-gnu-ld: cannot find crt1.o: No such file or directory
/usr/bin/arm-linux-gnu-ld: cannot find crti.o: No such file or directory
...

I wish I could do that, but I want to support at least up to version 3.7.2 which doesn’t come with target_link_options().

Just to mention it, one solution I can quite work with is the following. One small flaw is that relies on capabilities only recently introduced, so I can’t use with on some lab. computers running CMake 3.7.2.

add_custom_target(
	kernel.uarm
	DEPENDS crtso libuarm io phase0
	COMMAND ${CMAKE_C_LINKER} ${LDFLAGS_UARM}
	$<TARGET_OBJECTS:crtso> $<TARGET_OBJECTS:libuarm>
	$<TARGET_OBJECTS:io> $<TARGET_OBJECTS:phase0>
	-o kernel.uarm
)

Update: despite profused efforts, it appears the link process is still looking for the standard C library.

I updated the uarm.cmake file likeso

# Project include, source and library dirs for uarm-related files
set(BKA_UARM_INC ${PROJECT_SOURCE_DIR}/${BKA_INC}/uarm)
set(BKA_UARM_SRC ${PROJECT_SOURCE_DIR}/${BKA_SRC}/uarm)

set(CFLAGS_UARM -nostdlib -O0 -mcpu=arm7tdmi)
set(LDFLAGS_UARM "-Wl,-G,0,-nostdlib,-T,${BKA_UARM_SRC}/elf32ltsarm.h.uarmcore.x")
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")

add_executable(
    kernel.uarm
    ${BKA_SRC}/phase0.c ${BKA_UARM_SRC}/libuarm.s ${BKA_UARM_SRC}/crtso.s ${BKA_SRC}/io.c
)
target_include_directories(kernel.uarm PRIVATE ${BKA_UARM_INC})
set_property(TARGET kernel.uarm PROPERTY COMPILE_OPTIONS ${CFLAGS_UARM})
set_property(TARGET kernel.uarm PROPERTY LINK_FLAGS ${LDFLAGS_UARM})

and tried to run with

$ make VERBOSE=1 kernel.uarm
...
/usr/bin/arm-none-eabi-gcc    -Wl,-G,0,-nostdlib,-T,/home/students/acsor/08574-Sistemi-Operativi/BiKaya/src/uarm/elf32ltsarm.h.uarmcore.x CMakeFiles/kernel.uarm.dir/src/phase0.c.o CMakeFiles/kernel.uarm.dir/src/uarm/libuarm.s.o CMakeFiles/kernel.uarm.dir/src/uarm/crtso.s.o CMakeFiles/kernel.uarm.dir/src/io.c.o  -o kernel.uarm
/usr/lib/gcc/arm-none-eabi/5.4.1/../../../arm-none-eabi/lib/libc.a(lib_a-exit.o): In function `exit':
/build/newlib-XAuz1P/newlib-2.4.0.20160527/build/arm-none-eabi/newlib/libc/stdlib/../../../../../newlib/libc/stdlib/exit.c:70: undefined reference to `_exit'
...

Maybe try passing -nostdlib to GCC itself rather than to the linker? So then you would have:

set(LDFLAGS_UARM "-nostdlib -Wl,-G,0,-T,${BKA_UARM_SRC}/elf32ltsarm.h.uarmcore.x")

Love you! It works flawlessly, but only on the micro ARM architecture.