5

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"
  )
vgonisanz
  • 11,831
  • 13
  • 78
  • 130
  • 3
    STL is C++, why would you have it in your C program? Technically you could set the entry point to main in the linker command, but that would skip libc initialization stuff and so is not recommended. – Jester Apr 01 '16 at 12:02
  • Without crosscompile, following this tutorial: https://blogs.oracle.com/ksplice/entry/hello_from_a_libc_free , works perfectly, because use its own stub _start assembly file, that works in x86. I assume I need something similar in ARM but I don't know what to use, the same assembly file doesn't work because the architecture is different. – vgonisanz Apr 01 '16 at 12:24
  • Note that the linked article specifically does not use the C library. That works. What is not guaranteed to work is mixing `-nostdlib` with `-lc`. You should decide if you need libc in which case do not use `-nostdlib`, or if you don't need it then don't use `-lc` and set the entry point via linker option. – Jester Apr 01 '16 at 12:34
  • You are right, no sense using -lc flag, but removing it, same problem. The flags provided are: TARGET_LINK_LIBRARIES(${PROJ_NAME} -nostdlib --specs=rdimon.specs -lm -lrdimon). -lm is needed to user -nostdlib – vgonisanz Apr 01 '16 at 12:38
  • _start is part of standard library. It's the function which calls main. It should be defined in crt0.o which should be automatically linked with your program. It is possible that your gcc works differently and requires some additional actions. Exist already on /usr/arm-none-eabi/lib/crt0.o . Assuming is precompilated, and linked when using std, I want to know a reference to do it myself. – vgonisanz Apr 01 '16 at 12:51
  • As I said, use linker option to set entry point. Such as `-e main`. – Jester Apr 01 '16 at 12:56
  • Don't use `-e main`, you need to setup a stack and possibly provide `argv` and `argc` as well as any library type initialization that your application might need. As well, if you are on an OS like Linux (or BSD) you need to call `exit(main_return_value);`. – artless noise Apr 02 '16 at 19:13
  • This question is unrelated to CMake. You don't know how to achieve it manually, so CMake is not in your way. Do you mind removing the according tag? – usr1234567 Apr 03 '16 at 20:33
  • Why unrelated? I'm using a CMakeLists.txt to provide source, flags and paths... its a little bit different than use gcc by terminal, or makefiles in example. – vgonisanz Apr 04 '16 at 06:11
  • @vgonisanz: Once you know what flags you have to add to your terminal call or in your Makefile, you can easily add them to your CMakeLists.txt. But your actual problem is unrelated to CMake. – usr1234567 Apr 04 '16 at 06:33
  • As it was already mentioned, you won't get c++ standard library in your code even without `-nostdlib`. – arrowd Apr 04 '16 at 06:34
  • @usr1234567 well, If you think that is more correct without that flag, I remove it. @Jester, -e main doesn`t work, it seems is a little bit more complicated to provide the startup configuration. @arrowd try yourself to see arm-none-eabi-objdump -s arm-hello-world with and without -nostdlib, I want to use that flag to remove all unnecessary in the final executable and provide only needed standard functions manually. – vgonisanz Apr 04 '16 at 06:41

1 Answers1

3

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

arsv
  • 1,176
  • 9
  • 11
  • Ok. Your assembly code seems work manually. "arm-none-eabi-gcc -nostdlib start.S -o main main.c" and "qemu-arm main" work propertly with minimun info in "arm-none-eabi-objdump main". To complete the answer, I will need to know how to make it working on the CMakeLists.txt, because I cannot add the start.S file in code, or link flag to make it work with CMake. – vgonisanz Apr 04 '16 at 11:27
  • 1
    Not a big fan of cmake, in part because of things like this. Apparently you need to enable ASM language, otherwise it just silently ignores assembly files. CMakeLists.txt example added, seems to be working for me – arsv Apr 04 '16 at 14:45
  • Well, thanks, it seems you proposal solution is valid. I'm trying to do the same but I have a unknown problem with the linker, but is a valid solution, 15 hours to award the bounty :-p – vgonisanz Apr 04 '16 at 14:52