Кросс-компилятор 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 или подобного.


1
227
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, поэтому я предполагаю, что код проблемы на самом деле появляется в результате связывания неправильной библиотеки.