arm-none-eabi-gcc с Cmake не имеет точки входа с флагом -nostdlib

Я пытаюсь создать приветственный мир в архитектуре рук, используя CMake с эта цепочка инструментов

Мой main.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 для -nostdlib:

Не используйте при компоновке стандартные системные файлы запуска или библиотеки. Никакие файлы запуска и только указанные вами библиотеки не будут переданы компоновщику, а параметры, определяющие компоновку системных библиотек, такие как -static-libgcc или -shared-libgcc, игнорируются.

В качестве альтернативы, если кто-то не хочет использовать стандартную библиотеку, он может использовать флаг -nodefaultlibs.

Не используйте стандартные системные библиотеки при линковке. Компоновщику передаются только указанные вами библиотеки, а параметры, определяющие компоновку системных библиотек, такие как -static-libgcc или -shared-libgcc, игнорируются. Обычно используются стандартные файлы запуска, если не используется параметр -nostartfiles.

Компилятор может генерировать вызовы memcmp, memset, memcpy и memmove. Эти записи обычно разрешаются записями в libc. Эти точки входа должны предоставляться через какой-либо другой механизм, если указан этот параметр.

Кстати, мне нужен способ создания и добавления файлов запуска, возможный способ в это руководство, но я добавляю вознаграждение, чтобы получить ответ на мой вопрос и иметь общее решение для всех. Я считаю это полезным для людей, которые хотят настраивать и изучать файлы кросс-компиляции, сборки и запуска.

Обновление 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"
  )

person vgonisanz    schedule 01.04.2016    source источник
comment
STL — это C++, зачем вам это в вашей программе на C? Технически вы можете установить точку входа в main в команде компоновщика, но это пропустит инициализацию libc, поэтому не рекомендуется.   -  person Jester    schedule 01.04.2016
comment
Без кросскомпиляции, следуя этому руководству: blogs.oracle.com/ksplice/entry/hello_from_a_libc_free , работает отлично, т.к. использует свой собственный файл сборки _start, работающий в x86. Я предполагаю, что мне нужно что-то подобное в ARM, но я не знаю, что использовать, тот же файл сборки не работает, потому что архитектура другая.   -  person vgonisanz    schedule 01.04.2016
comment
Обратите внимание, что в связанной статье специально не используется библиотека C. Это работает. То, что не гарантированно сработает, это смешивание -nostdlib с -lc. Вы должны решить, нужна ли вам libc, и в этом случае не используйте -nostdlib, или если она вам не нужна, то не используйте -lc и установите точку входа с помощью опции компоновщика.   -  person Jester    schedule 01.04.2016
comment
Вы правы, нет смысла использовать флаг -lc, но его удаление - та же проблема. Предусмотрены следующие флаги: TARGET_LINK_LIBRARIES(${PROJ_NAME} -nostdlib --specs=rdimon.specs -lm -lrdimon). -lm нужен пользователю -nostdlib   -  person vgonisanz    schedule 01.04.2016
comment
_start является частью стандартной библиотеки. Это функция, которая вызывает main. Он должен быть определен в crt0.o, который должен быть автоматически связан с вашей программой. Возможно, ваш gcc работает иначе и требует каких-то дополнительных действий. Уже существует в /usr/arm-none-eabi/lib/crt0.o . Предполагая, что он предварительно скомпилирован и связан при использовании std, я хочу знать ссылку, чтобы сделать это самостоятельно.   -  person vgonisanz    schedule 01.04.2016
comment
Как я уже сказал, используйте опцию компоновщика, чтобы установить точку входа. Например, -e main.   -  person Jester    schedule 01.04.2016
comment
Не используйте -e main, вам нужно настроить стек и, возможно, предоставить argv и argc, а также любую инициализацию библиотечного типа, которая может понадобиться вашему приложению. Кроме того, если вы работаете в такой ОС, как Linux (или BSD), вам нужно вызвать exit(main_return_value);.   -  person artless noise    schedule 02.04.2016
comment
Этот вопрос не связан с CMake. Вы не знаете, как добиться этого вручную, поэтому CMake вам не мешает. Не возражаете ли вы удалить соответствующий тег?   -  person usr1234567    schedule 03.04.2016
comment
Почему несвязанный? Я использую CMakeLists.txt для предоставления источника, флагов и путей... это немного отличается от использования gcc терминалом или, например, makefiles.   -  person vgonisanz    schedule 04.04.2016
comment
@vgonisanz: Как только вы узнаете, какие флаги вам нужно добавить к вызову терминала или в ваш Makefile, вы можете легко добавить их в свой CMakeLists.txt. Но ваша настоящая проблема не связана с CMake.   -  person usr1234567    schedule 04.04.2016
comment
Как уже было сказано, даже без -nostdlib вы не получите в своем коде стандартную библиотеку c++.   -  person arrowd    schedule 04.04.2016
comment
@ usr1234567 ну, если вы считаете, что без этого флага будет правильнее, я его убираю. @Jester, -e main не работает, кажется, немного сложнее предоставить конфигурацию запуска. @arrowd попробуйте сами увидеть arm-none-eabi-objdump -s arm-hello-world с -nostdlib и без него, я хочу использовать этот флаг, чтобы удалить все лишнее в финальном исполняемом файле и предоставить только необходимые стандартные функции вручную.   -  person vgonisanz    schedule 04.04.2016


Ответы (1)


Вот _start.s, который я использую в своем небольшом проекте.
Этого должно быть достаточно, чтобы связать и запустить ваш main() с помощью 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

Обратите внимание, что это код запуска для обычного двоичного файла пользовательского пространства Linux на ARM. Это то, что вам, вероятно, нужно для qemu-arm (режим пользователя qemu linux или прокси-сервер системного вызова). Для других случаев, таких как двоичные файлы с голым железом в связанном посте, или пользовательское пространство, отличное от Linux, или другие архитектуры, код запуска будет другим.

В Linux только что загруженный двоичный файл вызывается с argc в верхней части стека, за которым следует argv[], затем envp[], а затем auxv[]. Код запуска должен превратить это в правильный вызов main(argc, argv) в соответствии с соглашением о вызовах арки. Для ARM это 1-й аргумент в регистре а1, 2-й в а2.

«Вызывается» выше означает переход к адресу e_entry из заголовка ELF, который устанавливается ld так, чтобы он указывал на символ _start, если он найден. Поскольку _start нигде не определен, ld установил для e_entry значение 0x8000, и то, что оказалось на 0x8000, когда был сделан переход, по-видимому, не выглядело как действующая инструкция ARM. Что не совсем неожиданно.

Чтение кода из небольших/более чистых реализаций libc, таких как musl или 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

person arsv    schedule 04.04.2016
comment
Ok. Ваш ассемблерный код работает вручную. arm-none-eabi-gcc -nostdlib start.S -o main main.c и qemu-arm main правильно работают с информацией minimun в arm-none-eabi-objdump main. Чтобы завершить ответ, мне нужно знать, как заставить его работать с CMakeLists.txt, потому что я не могу добавить файл start.S в код или флаг ссылки, чтобы он работал с CMake. - person vgonisanz; 04.04.2016
comment
Не большой поклонник cmake, отчасти из-за таких вещей. Очевидно, вам нужно включить язык ASM, иначе он просто молча игнорирует файлы сборки. Добавлен пример CMakeLists.txt, похоже, у меня работает - person arsv; 04.04.2016
comment
Что ж, спасибо, кажется, ваше предложение решение действительно. Я пытаюсь сделать то же самое, но у меня неизвестная проблема с компоновщиком, но это действительное решение, 15 часов, чтобы присудить награду :-p - person vgonisanz; 04.04.2016