Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

arm-none-eabi-gcc with Cmake has not entry point with flag -nostdlib

I'm trying to make a hello world in arm architecture using CMake with this toolchain

My main.c

int main()
{
  char *str = "Hello World";
  return 0;
}

And my CMakeLists.txt

cmake_minimum_required(VERSION 3.4)
SET(PROJ_NAME arm-hello-world-nostdlib)
PROJECT(${PROJ_NAME})

# Include directories with headers
#---------------------------------------------------#
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/include )

# Source
#---------------------------------------------------#
FILE(GLOB ${PROJ_NAME}_SRC
    "src/*.c"
)
FILE(GLOB ${PROJ_NAME}_HEADERS
    "include/*.h"
)

# Create Exe
#---------------------------------------------------#
ADD_EXECUTABLE(${PROJ_NAME} ${${PROJ_NAME}_SRC} ${${PROJ_NAME}_HEADERS})

# Specify libraries or flags to use when linking a given target.
#---------------------------------------------------#
TARGET_LINK_LIBRARIES(${PROJ_NAME} -nostdlib --specs=rdimon.specs -lm -lrdimon)

This configuration launch the warning:

[100%] Linking C executable arm-hello-world-nostdlib
/usr/lib/gcc/arm-none-eabi/5.2.0/../../../../arm-none-eabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000008000

And executing the binary with qemu crash the execution:

qemu-arm arm-hello-world-nostdlib 
qemu: uncaught target signal 4 (Illegal instruction) - core dumped
Illegal instruction (core dumped)

Without flag --nostdlib works perfectly, and command

arm-none-eabi-objdump -s arm-hello-world-nostdlib

Show a lot of info in binary, compiling with the flag only show:

samples/helloworld-nostdlib/arm-hello-world-nostdlib:     file format elf32-littlearm

Contents of section .text:
 8000 80b483b0 00af044b 7b600023 18460c37  .......K{`.#.F.7
 8010 bd465df8 047b7047 1c800000           .F]..{pG....    
Contents of section .rodata:
 801c 48656c6c 6f20576f 726c6400           Hello World.    
Contents of section .comment:
 0000 4743433a 20284665 646f7261 20352e32  GCC: (Fedora 5.2
 0010 2e302d33 2e666332 33292035 2e322e30  .0-3.fc23) 5.2.0
 0020 00                                   .               
Contents of section .ARM.attributes:
 0000 41380000 00616561 62690001 2e000000  A8...aeabi......
 0010 05436f72 7465782d 4d340006 0d074d09  .Cortex-M4....M.
 0020 020a0612 04140115 01170318 0119011a  ................
 0030 011b011c 011e0622 01                 .......".    

I dont want stl libraries in my binary, but I guess I missing the assembly code to find the entry point. How can add it manually?

Update: According to GNU Linker doc for -nostdlib:

Do not use the standard system startup files or libraries when linking. No startup files and only the libraries you specify will be passed to the linker, and options specifying linkage of the system libraries, such as -static-libgcc or -shared-libgcc, are ignored.

Alternatively, If someone don't want to user standard library, they can use flag -nodefaultlibs.

Do not use the standard system libraries when linking. Only the libraries you specify are passed to the linker, and options specifying linkage of the system libraries, such as -static-libgcc or -shared-libgcc, are ignored. The standard startup files are used normally, unless -nostartfiles is used.

The compiler may generate calls to memcmp, memset, memcpy and memmove. These entries are usually resolved by entries in libc. These entry points should be supplied through some other mechanism when this option is specified.

By the way, I want a way to create and add startup files, a possible way in this tutorial, but I add the bounty to get a answer to my question and have a general solution for everybody. I consider this userful for people who wants to customize and learn about crosscompilation, arm, and startup files.

Update 2

Using start.S assembly code:

.text
.align 4

.global _start
.global _exit

_start:
        mov     fp, #0               /* frame pointer */
        ldr     a1, [sp]             /* 1st arg = argc */
        add     a2, sp, #4           /* 2nd arg = argv */

        bl      main

_exit:
        mov     r7, #1               /* __NR_exit */
        swi     0

.type _start,function
.size _start,_exit-_start

.type _exit,function
.size _exit,.-_exit

to indicate the entry point provided by arsv, and compiling using command:

arm-none-eabi-gcc -nostdlib -o main main.c start.S

seems to work propertly. Update of CMakeLists.txt:

#Directly works: 
#arm-none-eabi-gcc -nostdlib -o main main.c start.S

cmake_minimum_required(VERSION 3.4)
SET(PROJ_NAME arm-hello-world-nostdlib)

# Assembler files (.S) in the source list are ignored completely by CMake unless we
# “enable” the assembler by telling CMake in the project definition that we’re using assembly
# files. When we enable assembler, CMake detects gcc as the assembler rather than as – this
# is good for us because we then only need one set of compilation flags.
PROJECT(${PROJ_NAME} C ASM)

# Include directories with headers
#---------------------------------------------------#
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/include )

# Source
#---------------------------------------------------#
FILE(GLOB ${PROJ_NAME}_SRC
    "src/start.S"
    "src/*.c"
)
FILE(GLOB ${PROJ_NAME}_HEADERS
    "include/*.h"
)

# Create Exe
#---------------------------------------------------#
ADD_EXECUTABLE(${PROJ_NAME} ${${PROJ_NAME}_SRC} ${${PROJ_NAME}_HEADERS} )

# Specify libraries or flags to use when linking a given target.
#---------------------------------------------------#
TARGET_LINK_LIBRARIES(${PROJ_NAME} -nostdlib --specs=rdimon.specs -lm -lrdimon)

If you get linking problems like:

arm-none-eabi/bin/ld: error: CMakeFiles/arm-hello-world-nostdlib.dir/src/main.c.obj: Conflicting CPU architectures 1/13

Its a problem with toolchain, for cortex-a9, works using:

  set(CMAKE_C_FLAGS
    "${CMAKE_C_FLAGS}"
    "-mcpu=cortex-a9 -march=armv7-a -mthumb"
    "-mfloat-abi=softfp -mfpu=fpv4-sp-d16"
  )
like image 581
vgonisanz Avatar asked Oct 18 '22 12:10

vgonisanz


1 Answers

Here's _start.s I use in a small project of mine.
It should be enough to link and run your main() with qemu-arm:

.text
.align 4

.global _start
.global _exit

_start:
        mov     fp, #0               /* frame pointer */
        ldr     a1, [sp]             /* 1st arg = argc */
        add     a2, sp, #4           /* 2nd arg = argv */

        bl      main

_exit:
        mov     r7, #1               /* __NR_exit */
        swi     0

.type _start,function
.size _start,_exit-_start

.type _exit,function
.size _exit,.-_exit

Note this is startup code for common Linux userspace binary on ARM. Which is what you probably want for qemu-arm (qemu linux-user mode or syscall proxy). For other cases, like bare iron binaries in the linked post, or non-Linux userspace, or other architectures, startup code will be different.

In Linux, a newly-loaded binary gets invoked with argc at the top of the stack, followed by argv[], followed by envp[], followed by auxv[]. The startup code has to turn that into a proper main(argc, argv) call according to the arch call convention. For ARM that's 1st argument in register a1, 2nd in a2.

"Gets invoked" above means a jump to e_entry address from the ELF header, which is set by ld to point to _start symbol if one is found. With no _start defined anywhere, ld set e_entry to 0x8000 and whatever happened to be at 0x8000 when the jump was made apparently did not look like a valid ARM instruction. Which is not exactly unexpected.

Reading code from smaller/cleaner libc implementations like musl or dietlibc helps a lot in understanding stuff like this. The code above originates from dietlibc by the way.

https://github.com/ensc/dietlibc/blob/master/arm/start.S
http://git.musl-libc.org/cgit/musl/tree/arch/arm/crt_arch.h

For reference, minimalistic CMakeLists.txt to build the project:
(assuming the files are named main.c and _start.s)

project(arm-hello-world-nostdlib)
cmake_minimum_required(VERSION 3.4)

enable_language(ASM)
set(CMAKE_C_COMPILER arm-none-gnueabi-gcc)
set(CMAKE_ASM_COMPILER arm-none-gnueabi-gcc)
set(CMAKE_ASM_FLAGS -c)
set(CMAKE_VERBOSE_MAKEFILE on)

add_executable(main _start.s main.c)
target_link_libraries(main -nostdlib)

Run the resulting executable like this: qemu-arm ./main

like image 85
arsv Avatar answered Oct 29 '22 22:10

arsv