Кросс-компилятор ARM, генерирующий недопустимые ветвления в стандартных функциях C

Я работаю над собственным встроенным проектом (используя PlatformIO для настройки среды сборки), и я обнаружил, что вызовы стандартных функций C, таких как memset, memcpy, генерируют фиктивный код. Разборка показывает, что инструкции в обеих этих функциях (и других, которые я пробовал из stdlib) безоговорочно переходят в места, которые не содержат кода, что, конечно, приводит к жесткому сбою MCU (Cortex M4, Atmel D51) при попытке выполнение бессмысленного кода. Ошибок компилятора нет, только ошибки времени выполнения в виде аппаратных сбоев из-за неверных инструкций.

Я считаю, что что-то не так с моей средой компиляции, поскольку в PlatformIO есть некоторые библиотеки, используемые для платы Adafruit с тем же процессором, и это правильно связывает функции выше. Обратите внимание, что я выполняю кросс-компиляцию с Mac. Чуть ниже дизассемблированные для функции memset из проектов Adafruit и Custom:

Адафрут:

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, который следует выбирать с учетом флагов компилятора. Как ни странно, он не компилируется при печати параметров мультибиблиотеки, ссылаясь на то, что объектные файлы для архивирования (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 или подобного.

-nostdlib ну тогда может включить стандартную библиотеку?
KamilCuk 14.12.2020 17:22

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

reesul 14.12.2020 18:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
227
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

Все еще кажется странным, что у меня была бы такая ошибка, основанная на том, использовал ли я исключительно аппаратное обеспечение для плавающей запятой или гибрид эмуляции SW и HW для FP. Ни один из моих кодов также не использовал плавающую точку.

Часть причины, по которой я установил «softfp», заключается в том, что порт FreeRTOS, который я нашел, упоминал, что я должен использовать этот переключатель. Надеюсь, это не помешает мне использовать это.

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

Я обнаружил, что вызовы стандартных функций C, таких как memset, memcpy, генерируют поддельный код. Разборка показывает, что инструкции в обеих этих функциях (и других, которые я пробовал из stdlib) безоговорочно переходят в места, которые не содержат кода, что, конечно, приводит к жесткому сбою MCU (Cortex M4, Atmel D51) при попытке выполнение бессмысленного кода.

На самом деле это код ARM, а не код большого пальца. Когда пытаешься разобрать его как thumb, это ерунда, а вот разобрать как ARM выглядит правдоподобно.

Конечно, ваш процессор не может выполнять код ARM, а только код большого пальца, и в любом случае даже процессор, который мог бы столкнуться с ним в режиме ARM. Так что никакой загадки на жестком разломе нет.

Что неясно, так это то, как именно вы получаете код ARM в проекте большого пальца. На первый взгляд кажется, что ваши фактические вызовы компилятора указывают thumb, поэтому я предполагаю, что код проблемы на самом деле появляется в результате связывания неправильной библиотеки.

Может быть, я не очень хорошо разбираюсь в мультибиблиотеке, но я знаю, что в большинстве опций, отображаемых при печати, явно упоминается «большой палец». Не означает ли это, что некоторые версии стандартной библиотеки поддерживают режим большого пальца? Как вы видели, я указал -mthumb в команде компоновщика. Кроме того, не могут ли большинство новых процессоров cortex-m выполнять как ARM, так и thumb? Я думал, что есть способ переключать режимы. В дизассемблере я видел как 16-битные, так и 32-битные инструкции. Что касается того, почему это происходит, возможно, что-то не так с набором инструментов руки, поставляемым с платформой ввода-вывода.

reesul 15.12.2020 20:01

Нет и нет. Неясно, что -mthumb будет означать что-либо для компоновщика, он не создает инструкции, а только объединяет вместе любые объекты ссылок, которые вы предоставляете, и если они неверны для целевого оборудования, они неверны. И нет, насколько мне известно, у Cortex-M нет режима ARM.

Chris Stratton 15.12.2020 20:15

Другие вопросы по теме