Я работаю над собственным встроенным проектом (используя 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 при сборке, а затем снял флаг при компоновке. Я также пытался включить -nostdlib во время компоновки и не включал -lc или -lc_nano. Ни один из них не имел эффекта. В ответе, который я разместил ниже, он начал работать, когда я изменил флаги с плавающей запятой.
Кажется, проблемы возникают при попытке использовать флаг компилятора -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-битные инструкции. Что касается того, почему это происходит, возможно, что-то не так с набором инструментов руки, поставляемым с платформой ввода-вывода.
Нет и нет. Неясно, что -mthumb будет означать что-либо для компоновщика, он не создает инструкции, а только объединяет вместе любые объекты ссылок, которые вы предоставляете, и если они неверны для целевого оборудования, они неверны. И нет, насколько мне известно, у Cortex-M нет режима ARM.
-nostdlib
ну тогда может включить стандартную библиотеку?