#gcc #compilation #linker #arm #embedded
#gcc #Сборник #компоновщик #arm #встроенный
Вопрос:
Я работаю над пользовательским встроенным проектом (используя PlatformIO для настройки среды сборки), и я обнаружил, что вызовы стандартных функций C, таких как memset, memcpy, генерируют фиктивный код. Дизассемблирование показывает, что инструкции в обеих этих функциях (и других, которые я пробовал из stdlib) безоговорочно переходят в местоположения, которые не содержат кода, что, конечно, приводит к сбою MCU (Cortex M4, Atmel D51), когда он пытается выполнить бессмысленный код. Ошибок компилятора нет, только ошибки времени выполнения в виде жестких сбоев из-за неверных инструкций.
Я считаю, что что-то не так с моей средой компиляции, поскольку в PlatformIO есть некоторые библиотеки, используемые для платы Adafruit того же процессора, и это правильно связывает функции выше. Обратите внимание, что я выполняю кросс-компиляцию с Mac. Чуть ниже приведены разборки функции memset из Adafruit и пользовательских проектов:
Adafruit:
0x000012de: 02 44 add r2, r0
0x000012e0: 03 46 mov r3, r0
0x000012e2: 93 42 cmp r3, r2
0x000012e4: 00 d1 bne.n 0x12e8 <memset 10>
0x000012e6: 70 47 bx lr
0x000012e8: 03 f8 01 1b strb.w r1, [r3], #1
0x000012ec: f9 e7 b.n 0x12e2 <memset 4>
Пользовательский:
0x000005b4: 00 30 adds r0, #0
0x000005b6: a0 e1 b.n 0x8fa <--- branch to address with no code and hard-fault
0x000005b8: 02 20 movs r0, #2
0x000005ba: 80 e0 b.n 0x6be
0x000005bc: 02 00 movs r2, r0
0x000005be: 53 e1 b.n 0x868
0x000005c0: 1e ff 2f 01 vrhadd.u16 d0, d14, d31
0x000005c4: 01 10 asrs r1, r0, #32
0x000005c6: c3 e4 b.n 0xffffff50
0x000005c8: fb ff ff ea ; <UNDEFINED> instruction: 0xfffbeaff
Даже без бессмысленных целей ветвления пользовательская версия имеет форму, совершенно отличную от приведенной выше, что наводит меня на мысль, что со связыванием происходит что-то ужасно неправильное. Я предполагаю, что проблема возникает на этапе компоновки, а не во время компиляции отдельных объектных файлов. Связывание между файлами, которые существуют исключительно в моем проекте, не вызывает проблем; локальное ветвление правильное. Эта странность, по-видимому, ограничивается связыванием готовых библиотек.
Я должен упомянуть, что материал adafruit также включает код Arduino, поэтому часть этого процесса компиляции включает C , тогда как мой — чисто C. Я основал большинство флагов компилятора и среду сборки на проекте Adafruit, поскольку это был лучший эталон для моего собственного проекта, но я не использую arduino ни в какой форме.
Вот как компоновщик вызывается для каждого из двух проектов
Adafruit (g можно заменить на gcc без ошибок):
arm-none-eabi-g -o .pio/build/adafruit_grandcentral_m4/firmware.elf -T flash_without_bootloader.ld -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Os -mcpu=cortex-m4 -mthumb -Wl,--gc-sections -Wl,--check-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align --specs=nosys.specs --specs=nano.specs .pio/build/adafruit_grandcentral_m4/src/main.cpp.o -L.pio/build/adafruit_grandcentral_m4 -L/Users/work-reese/.platformio/packages/framework-arduino-samd-adafruit/variants/grand_central_m4/linker_scripts/gcc -L/Users/work-reese/.platformio/packages/framework-cmsis/CMSIS/Lib/GCC -Wl,--start-group .pio/build/adafruit_grandcentral_m4/libFrameworkArduinoVariant.a .pio/build/adafruit_grandcentral_m4/libFrameworkArduino.a -larm_cortexM4lf_math -lm -Wl,--end-group
Пользовательский:
arm-none-eabi-ar rc .pio/build/commonsense/libFrameworkCommonSense.a .pio/build/commonsense/FrameworkCommonSense/commonsense.o .pio/build/commonsense/FrameworkCommonSense/cortex_handlers.o .pio/build/commonsense/FrameworkCommonSense/led.o .pio/build/commonsense/FrameworkCommonSense/pinConfig.o .pio/build/commonsense/FrameworkCommonSense/startup.o
arm-none-eabi-ranlib .pio/build/commonsense/libFrameworkCommonSense.a
arm-none-eabi-gcc -o .pio/build/commonsense/firmware.elf -T commonsense_linker.ld -mfpu=fpv4-sp-d16 -mthumb -Wl,--gc-sections -Wl,--check-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align --specs=nosys.specs --specs=nano.specs -mcpu=cortex-m4 .pio/build/commonsense/src/main.o -L.pio/build/commonsense -L/Users/work-reese/.platformio/packages/toolchain-gccarmnoneeabi/arm-none-eabi/lib -L/Users/work-reese/.platformio/packages/framework-cmsis/CMSIS/Lib/GCC -L/Users/work-reese/.platformio/packages/framework-commonsense/linker -Wl,--start-group .pio/build/commonsense/libFrameworkCommonSense.a -larm_cortexM4lf_math -lc_nano -lm -Wl,--end-group
Для этого используется кросс-компилятор arm версии 7.2.1, а набор инструментов содержит дистрибутивы для libc, libc_nano, libm и т.д. Все необходимые библиотеки, по-видимому, присутствуют.
Пожалуйста, обратите внимание, что я включил несколько дополнительных строк для ссылки на пользовательскую версию выше, чтобы вы могли видеть, из чего она строится libFrameworkCommonSense.a
. Ни один из этих файлов не содержит никаких вызовов stdlib, хотя cortex_handlers не имеет __libc_init_array в обработчике сброса, потому что это также вызывало жесткие ошибки таким же образом, как memset . Сценарий компоновщика идентичен между ними; еще раз, я сильно позаимствовал из проекта adafruit для обработчиков прерываний и кода запуска, но до сих пор я не видел никаких реальных различий между средами.
Добавление опции —print-multi-lib показывает несколько вариантов, которые должны работать, а именно thumb/v7e-m/fpv4-sp/softfp;@mthumb@march=armv7e-m@mfpu=fpv4-sp-d16@mfloat-abi=softfp
, которые должны быть выбраны с учетом флагов компилятора. Как ни странно, при печати параметров multilib не удается выполнить компиляцию, ссылаясь на то, что объектные файлы для архивирования (arm-none-eabi-ar) отсутствуют в каталоге сборки. Это, вероятно, не имеет значения.
Вот компиляция для основного файла, которая включает вызовы memset и memcpy:
arm-none-eabi-gcc -o .pio/build/commonsense/src/main.o -c -std=gnu11 -mfpu=fpv4-sp-d16 -Og -g3 -mlong-calls --specs=nano.specs -specs=nosys.specs -fdata-sections -ffunction-sections -mfloat-abi=softfp -march=armv7e-m -mfpu=fpv4-sp-d16 -marm -mthumb-interwork -ffunction-sections -fdata-sections -Wall -mthumb -nostdlib --param max-inline-insns-single=500 -mcpu=cortex-m4 -DPLATFORMIO=50003 -D__SAMD51P20A__ -D__SAMD51__ -D__FPU_PRESENT -DARM_MATH_CM4 -DENABLE_CACHE -DVARIANT_QSPI_BAUD_DEFAULT=50000000 -DDEBUG -DADAFRUIT_LINKER -DF_CPU=120000000L -Iinclude -Isrc -I/Users/work-reese/.platformio/packages/framework-cmsis/CMSIS/Include -I/Users/work-reese/.platformio/packages/framework-cmsis-atmel/CMSIS/Device/ATMEL -I/Users/work-reese/.platformio/packages/framework-cmsis-atmel/CMSIS/Device/ATMEL/samd51 -I/Users/work-reese/.platformio/packages/framework-commonsense -I/Users/work-reese/.platformio/packages/framework-commonsense/core -I/Users/work-reese/.platformio/packages/framework-commonsense/hal -I/Users/work-reese/.platformio/packages/framework-commonsense/hal/include -I/Users/work-reese/.platformio/packages/framework-commonsense/hal/utils/include -I/Users/work-reese/.platformio/packages/framework-commonsense/hal/utils/src -I/Users/work-reese/.platformio/packages/framework-commonsense/hal/src -I/Users/work-reese/.platformio/packages/framework-commonsense/hpl -I/Users/work-reese/.platformio/packages/framework-commonsense/hri -I/Users/work-reese/.platformio/packages/framework-commonsense/sample src/main.c
Кто-нибудь знает, почему у меня было бы такое поведение с неправильно связанными библиотечными функциями? Я колотил по нему почти неделю, безрезультатно используя множество комбинаций флагов компилятора. Я чувствую, что я что-то упускаю из виду, но не знаю, что. Я рад предоставить любую дополнительную информацию.
Побочный вопрос: что такое __libc_init_array() и насколько необходимо его вызывать во время запуска программы? Я вижу это в обработчике сброса для проектов adafruit и Atmel Studio. Он объявлен локально как прототип функции в их файлах запуска, но воспроизведение того же самого в моей собственной среде вызывает сбой, как только процессор пытается вызвать эту функцию. Я должен думать, что это часть libc или аналогичная.
Комментарии:
1.
-nostdlib
ну, тогда, может быть, включить стандартную библиотеку?2. При сборке у меня был установлен -nostdlib, а затем флаг был удален при связывании. Я также пытался включить -nostdlib во время соединения и не включать -lc или -lc_nano. Ни один из них не возымел эффекта. В ответе, который я опубликовал ниже, я заставил его начать работать, когда я изменил флаги с плавающей запятой.
Ответ №1:
Я обнаружил, что вызовы стандартных функций C, таких как memset, memcpy, генерируют фиктивный код. Дизассемблирование показывает, что инструкции в обеих этих функциях (и других, которые я пробовал из stdlib) безоговорочно переходят в местоположения, которые не содержат кода, что, конечно, приводит к сбою MCU (Cortex M4, Atmel D51), поскольку он пытается выполнить бессмысленный код.
На самом деле, это ARM-код, а не thumb-код. Когда вы пытаетесь разобрать его как thumb, это нонсенс, но разобрать его как ARM выглядит правдоподобно.
Конечно, ваш процессор не может выполнять код ARM, а только код thumb, и в любом случае даже процессору, который мог бы столкнуться с ним в режиме ARM. Так что никакой тайны в жесткой ошибке нет.
Неясно, как именно вы получаете код ARM в проекте thumb. На первый взгляд кажется, что ваши фактические вызовы компилятора указывают thumb, поэтому я бы предположил, что проблемный код на самом деле появляется в результате связывания неправильной библиотеки.
Комментарии:
1. Может быть, я не очень хорошо понимаю multilib, но я знаю, что большинство параметров, показанных при печати, явно упоминали «thumb». Не означает ли это, что некоторые версии стандартной библиотеки поддерживают режим thumb? Как вы видели, я предоставлял -mthumb в команде компоновщика. Кроме того, не могут ли большинство новых процессоров cortex-m выполнять как ARM, так и thumb? Я думал, что есть способ переключать режимы. Я видел как 16, так и 32-разрядные инструкции при разборке. Что касается того, почему это происходит, возможно, что-то не так с набором инструментов arm, поставляемым с platform IO.
2. Нет, и еще раз нет. Неясно, что -mthumb будет что-то значить для компоновщика, он не создает инструкции, а только объединяет любые предоставленные вами объекты ссылок, и если они неверны для целевого оборудования, они неверны. И нет, насколько я знаю, у Cortex-M нет режима ARM.
Ответ №2:
Похоже, проблемы возникают при попытке использовать флаг компилятора -mfloat-abi=softfp. Я переключился на -mfloat-abi=hard , и эти проблемы со связыванием, казалось, исчезли. Я подтвердил, что неправильный набор переключателей также нарушает среду adafruit.
Все еще кажется странным, что у меня была бы такая ошибка, основанная на том, использовал ли я исключительно аппаратное обеспечение для плавающей запятой по сравнению с гибридом эмуляции SW и HW для FPs. Ни один из моих кодов также не использовал плавающую точку.
Одна из причин, по которой я установил значение «softfp», заключается в том, что в найденном мной порту FreeRTOS упоминалось, что я должен использовать этот коммутатор. Надеюсь, это не помешает мне использовать это.
Мой вопрос все еще остается по __libc_init_array() , так как это все еще приводит к серьезной ошибке, когда я его запускаю — его разборка также странно выглядит с ответвлениями в нечетные места (т. Е. В таблицу исключений).