c - 带有 Cmake 的 arm-none-eabi-gcc 没有带有标志 -nostdlib 的入口点

标签 c assembly arm entry-point

我正在尝试使用带有 this toolchain 的 CMake 在 arm 架构中创建一个 hello world

我的主.c

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

还有我的 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)

此配置启动警告:

[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

然后用 qemu 执行二进制文件会导致执行崩溃:

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

没有标志 --nostdlib 完美运行,命令

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

以二进制形式显示大量信息,使用标志编译仅显示:

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                 .......".    

我不想在我的二进制文件中使用 STL 库,但我想我缺少用于查找入口点的汇编代码。如何手动添加?

更新: 根据GNU Linker doc对于-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.

或者,如果有人不想使用标准库,他们可以使用标志 -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.

顺便说一下,我想要一种创建和添加启动文件的方法,一种可能的方法在 this tutorial 中,但我增加了赏金以获得我的问题的答案,并为每个人提供了一个通用的解决方案。我认为这对想要自定义和了解交叉编译、arm 和启动文件的人很有用。

更新 2

使用start.S汇编代码:

.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

表示arsv提供的入口,使用命令编译:

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

似乎正常工作。 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)

如果您遇到链接问题,例如:

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

这是工具链的问题,对于 cortex-a9,可以使用:

  set(CMAKE_C_FLAGS
    "${CMAKE_C_FLAGS}"
    "-mcpu=cortex-a9 -march=armv7-a -mthumb"
    "-mfloat-abi=softfp -mfpu=fpv4-sp-d16"
  )

最佳答案

这是我在我的一个小项目中使用的_start.s。
用 qemu-arm 链接和运行你的 main() 就足够了:

.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

请注意,这是 ARM 上常见 Linux 用户空间二进制文件的启动代码。这就是您可能想要的 qemu-arm(qemu linux 用户模式或系统调用代理)。对于其他情况,例如链接帖子中的裸机二进制文件、非 Linux 用户空间或其他架构,启动代码将有所不同。

在 Linux 中,新加载的二进制文件在堆栈顶部使用 argc 调用,然后是 argv[]、envp[]、auxv[]。启动代码必须根据 arch 调用约定将其转换为适当的 main(argc, argv) 调用。对于 ARM,这是寄存器 a1 中的第一个参数,a2 中的第二个参数。

上面的“Gets invoked”意味着从 ELF header 跳转到 e_entry 地址,如果找到的话,它由 ld 设置为指向 _start 符号。在任何地方都没有定义 _start 的情况下,ld 将 e_entry 设置为 0x8000 并且在进行跳转时发生在 0x8000 处的任何内容显然看起来都不像有效的 ARM 指令。这并不完全出乎意料。

从更小/更干净的 libc 实现(如 musl 或 dietlibc)中读取代码有助于理解此类内容。顺便说一句,上面的代码源自dietlibc。

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

供引用,用于构建项目的简约 CMakeLists.txt:
(假设文件名为 main.c 和 _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)

像这样运行生成的可执行文件:qemu-arm ./main

关于c - 带有 Cmake 的 arm-none-eabi-gcc 没有带有标志 -nostdlib 的入口点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36355778/

相关文章:

c++ - 分发 DbgHelp.DLL

c - 这段汇编代码有什么作用?

gcc - 使用Cygwin gcc编译+链接自定义操作系统:无法识别的仿真模式:elf_i386

assembly - 在 Cortex-M0 中模拟 LDREX/STREX(加载/存储独占)

ARM 上的 Linux "top"实用程序报告我们已验证错误的数字。为什么?

c - 如何计算 switch case 中的薪水?然后为不同的利率添加一个 if 语句?

c++ - 结构中的枚举 - C 与 C++

c - 如何解决 C 和函数指针中的循环依赖问题

操作数后应有逗号、冒号、修饰符或行尾

c - 如何在嵌入式 Linux 上找到显示库