Непереводимая игра слов по поводу CLANG, LLVM и msp430

от автора

Целью данной работы является получение кода, сгенерированного бэк-эндом компилятора LLVM MSP430

Введение

CLANG — это просто фронт-энд компилятора С для LLVM. LLVM — это фреймворк для разработки фронт-эндов, бэк-эндов компиляторов для разных целевых платформ. Этот фреймворк облегчает анализ путем приведения всех фронт-эндов к промежуточному представлению (IR). Все бэк-энды оперируют с этим общим представлением.

Одна из целевых платформ, которая меня интересует, это MSP430 — популярный встраиваемый микроконтроллер, используемый во многих микропотребляющих приложениях.

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

Поехали

Текущая поддержка данной платформы предельно ограничена. Надеюсь, я смогу восполнить этот пробел. Ниже приведена упрощенная диаграмма того, как должна происходить сборка:

[Source Code (.cpp)] -> [clang] --.ll --> [llc] --.s --> [msp430-gcc] --> elf 

Clang берет ваш исходный код и компилирует его, основываясь на предоставленных параметрах. По умолчанию он скрывает много чего под своей оболочкой. Все это не что иное, как попытка сделать процесс более приятным для разработчика. Другими словами, обычно вы никогда не заглянете в промежуточное представление IR, ассемблер или линковку.

Что упущено в документации по целевой платформе MSP430, так это факт, что clang + llvm на самом деле не будет генерировать для вас бинарник elf. Кодогенератор просто производит ассемблерный код для вашей платформы. Это ваша личная обязанность произвести линковку и генерацию файла elf.

Технически, вам не нужен весь msp430-gcc. Вам всего лишь нужны libc, скрипты линкера и binutils. С другой стороны, msp430-gcc делает полезное дело, позволяя вам не вспоминать все ссылки и зависимости, необходимые для генерации финального elf (vector tables, crt0,…etc). Так что воспользуемся им.

Рабочее окружение и первоначальное исследование

Машина, которая используется для работы — это macbook pro под управлением OSX 10.9.2. Дополнительно используется LLVM и msp430-gcc.

До msp430-gcc я использовал патченную версию gcc, выложенную на sourceforge.net. В поисках более свежей версии я обнаружил, что она больше не поддерживается, потому что Texas Instruments и RHEL берут это на себя.

Здорово, подумал я, TI собирается работать с RHEL, чтобы взять на себя разработку, больше не нужны будут проприетарные компиляторы под windows.

FACEPALM 1

TI не рекомендует к использованию собственное решение, используйте на свой страх и риск (see TI’s compiler download). Oке-ей… Так на что я надеюсь?

FACEPALM 2

Хорошо, я могу согласиться, что они не доверяют собственному продукту, что и я чувствую большую часть времени. Пытаясь собрать это, я быстро обнаружил, что TI не поддерживает сборку на OS X. Это следствие использования xgcc и того факта, что некоторые флаги не поддерживаются OS X.

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

Что я сделал

Технически мне не нужен фронт-энд, мне всего лишь нужен линкер/ассемблер. Я предполагал, что есть brew/fink/ports путь. Я хотел использовать окружение gcc4.7 и использовать brew, но ничего не нашел.

Так что я создал свой собственный форк (основанный на sampsyo/homebrew-mspgcc). Он включает gcc, libc, gdb, mcu, binutils и mspdebug:

brew tap wbennett/homebrew-mspgcc   brew install wbennett/mspgcc/msp430-binutils   brew install wbennett/mspgcc/msp430-gcc   brew install wbennett/mspgcc/msp430-libc   brew install wbennett/mspgcc/msp430-mcu   brew install wbennett/mspgcc/msp430-gdb   brew install mspdebug   

Вах… Теперь у нас есть gcc.

Мое лицо горит

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

Если вы ищете в интернетах информацию, как собрать в clang для платформы msp430, напрямую вы ничего не найдете. Вы найдете немного обнадеживающие наборы параметров, непотребные объемы документации и возможно, предложения использовать некоторые опции, о которых вы не думали:

$>clang -ccc-host-triple msp430-elf  clang-3.5: error: unsupported option '-ccc-host-triple'   clang-3.5: error: no such file or directory: 'msp430-elf'   clang-3.5: error: no input files 

Отлично, это не работает.
Я могу только предположить, что это из-за старой версии LLVM. Может быть, я должен был использовать llc после того, как clang превратил его в LLVM байткод?.. Хорошо, посмотрим, какие варианты ещё остались.

Попробуем тестовое приложение:

#include "msp430.h" #include "stdlib.h"  int main(void)   {     //disable watchdog     WDCTL = WDTPW + WDTHOLD;      //do something silly     auto i = 1;     auto result = i+1;      //avoid warnings     if(result == 2)         return 2;      //loop forever     while(true){}      //never reach this     return 0; } 
#compile application to llvm assembler ~/local/bin/clang++ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/       -D__MSP430F5438__ -g -S -emit-llvm -std=c++11 -Wall -c src/simpleadd.cpp -o src/simpleadd.ll

Отлично! Выглядит, как нормальный LL

; ModuleID = 'src/simpleadd.cpp' target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"   target triple = "x86_64-apple-macosx10.9.0"  @"\01__WDTCTL" = external global i32  ; Function Attrs: nounwind ssp uwtable define i32 @main() #0 {     %1 = alloca i32, align 4   %i = alloca i32, align 4   %result = alloca i32, align 4   store i32 0, i32* %1   store volatile i32 23168, i32* @"\01__WDTCTL", align 4   store i32 1, i32* %i, align 4   %2 = load i32* %i, align 4   %3 = add nsw i32 %2, 1   store i32 %3, i32* %result, align 4   %4 = load i32* %result, align 4   %5 = icmp eq i32 %4, 2   br i1 %5, label %6, label %7  ; <label>:6                                       ; preds = %0   ret i32 2  ; <label>:7                                       ; preds = %0   br label %8  ; <label>:8                                       ; preds = %7, %8   br label %8 }  attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }  !llvm.ident = !{!0}  !0 = metadata !{metadata !"clang version 3.5 "} 

Давайте уже переведем это в какой-нибудь ассемблер для msp430, что llc может нам предложить?

$>~/local/bin/llc --help ... -mcpu=<cpu-name>                 - Target a specific cpu type (-mcpu=help for details) #ok more specific plz $>~/local/bin/llc -mcpu=help ...   amdfam10      - Select the amdfam10 processor.   athlon        - Select the athlon processor.   athlon-4      - Select the athlon-4 processor.   athlon-fx     - Select the athlon-fx processor.   athlon-mp     - Select the athlon-mp processor.   athlon-tbird  - Select the athlon-tbird processor.   athlon-xp     - Select the athlon-xp processor.   athlon64      - Select the athlon64 processor.   athlon64-sse3 - Select the athlon64-sse3 processor. ... 

Почему мы не можем использовать msp430? Я клянусь, что где-то читал, что существует частично поддерживаемая платформа, называемая msp430.

Воспользуемся помощью.

$>~/local/bin/llc --help ... -march=<string>                  - Architecture to generate code for (see --version) #ok $>~/local/bin/llc --version LLVM (http://llvm.org/):     LLVM version 3.5svn   DEBUG build.   Built Mar  1 2014 (22:49:35).   Default target: x86_64-apple-darwin13.1.0   Host CPU: core-avx-i    Registered Targets:     aarch64    - AArch64 (ARM 64-bit little endian target)     aarch64_be - AArch64 (ARM 64-bit big endian target)     arm        - ARM     cpp        - C++ backend     hexagon    - Hexagon     mips       - Mips     mips64     - Mips64 [experimental]     mips64el   - Mips64el [experimental]     mipsel     - Mipsel     msp430     - MSP430 [experimental]     nvptx      - NVIDIA PTX 32-bit     nvptx64    - NVIDIA PTX 64-bit     ppc32      - PowerPC 32     ppc64      - PowerPC 64     ppc64le    - PowerPC 64 LE     r600       - AMD GPUs HD2XXX-HD6XXX     sparc      - Sparc     sparcv9    - Sparc V9     systemz    - SystemZ     thumb      - Thumb     x86        - 32-bit X86: Pentium-Pro and above     x86-64     - 64-bit X86: EM64T and AMD64     xcore      - XCore 

Удивительно, но он поддерживается. Давайте сгенерируем msp430 ассемблер!

~/local/bin/llc -march=msp430  src/simpleadd.ll -o src/simpleadd.s 

Не уверен, почему он разместил main в секции text, ну да бог с ним. 2

    .file   "src/simpleadd.ll"     .text     .globl  main     .align  2     .type   main,@function main:                                   ; @main   ; BB#0:     push.w  r4     mov.w   r1, r4     sub.w   #12, r1     mov.w   #0, -2(r4)     mov.w   #0, -4(r4)     mov.w   #0, &__WDTCTL+2     mov.w   #23168, &__WDTCTL     mov.w   #0, -6(r4)     mov.w   #1, -8(r4)     mov.w   #0, -10(r4)     mov.w   #2, -12(r4)     mov.w   #0, r12     cmp.w   #0, r12     jne .LBB0_2 ; BB#1:     mov.w   #2, r14     mov.w   #0, r15     add.w   #12, r1     pop.w   r4     ret .LBB0_2:                                ; =>This Inner Loop Header: Depth=1     jmp .LBB0_2 .Ltmp0:     .size   main, .Ltmp0-main       .ident  "clang version 3.5 " 

Давайте сгененируем бинарник.

$>msp430-gcc -Wall -D_GNU_ASSEMBLER_ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/       -mmcu=msp430f5438 -mcpu=430 -x assembler -Wa,-gstabs -c src/simpleadd.s -o src/simpleadd.o #let's hint our entry point (-e main), because it's weird to be placing main in the text section #let's also generate a map to see how everything is linked $>msp430-gcc  -mmcu=msp430f5438 -mcpu=430 -Wl,-Map=a.out.map src/simpleadd.o  -o a.out -e main 

Без паники, похоже, мы на верном пути.
К сожалению, facepalm 3. Мое лицо красное и в синяках. После просмотра map файла видим, что линкер делает нечто странное:

*(.init .init.*)  791  *(.init0)  792  .init0         0x0000000000005c00        0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_re#  793                 0x0000000000005c00                _reset_vector__  794  *(.init1)  795  .init1         0x0000000000005c00        0xc /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__w#  796                 0x0000000000005c00                __watchdog_support  797  *(.init2)  798  .init2         0x0000000000005c0c        0x4 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__i#  799                 0x0000000000005c0c                __init_stack  800  *(.init3)                                                                                                                    801  .init3         0x0000000000005c10        0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__l#  802                 0x0000000000005c10                __low_level_init  803  *(.init4)  804  .init4         0x0000000000005c10       0x18 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_co#  805                 0x0000000000005c10                __do_copy_data  806  .init4         0x0000000000005c28       0x16 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_cl#  807                 0x0000000000005c28                __do_clear_bss  808  *(.init5)  809  *(.init6)  810  *(.init7)  811  *(.init8)  812  *(.init9)  813  *(.fini9)  814  .fini9         0x0000000000005c3e        0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__s#  815                 0x0000000000005c3e                __stop_progExec__  816  *(.fini8)  817  *(.fini7)  818  *(.fini6)  819  *(.fini5)  820  *(.fini4)  821  *(.fini3)  822  *(.fini2)  823  *(.fini1)  824  *(.fini0)  825  .fini0         0x0000000000005c3e        0x6 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_en#  826                 0x0000000000005c3e                _endless_loop__ ....  869  .text.crt0     0x0000000000005c48        0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/crt0ivtbl64.o  870  .text          0x0000000000005c48       0x40 src/simpleadd.o  871                 0x0000000000005c48                main 

Линкер пропустил запись о переходе на main в секции .init9. Ладно, дам подсказку.
После инспекции ассемблера видно, что clang не понимает, как линкуется программа, или ему все равно:

...     .file   "src/simpleadd.ll"     .text     .globl  main     .align  2     .type   main,@function main:                                   ; @main   ... 

Хорошо Mr.Clang, думаю, я могу вам немного помочь. Чтобы указать, где я хочу, чтобы компилятор поместить код, я должен использовать атрибут, а именно:

__attribute__((section(".init9")),aligned(2)).   

Библиотека Crt0 используется для выполнения процедур стартапа, прежде чем переходить к основной программе. Кроме того, она обрабатывает поведение после возврата вашей программы из функции main. Поскольку микроконтроллер не может технически «остановиться», он войдет в бесконечный цикл после возвращения из main.

Шаги, которые будут выполняться до того, как запустится основная программа (линкованная с Crt0), следующие:

  1. Вектор сброса
  2. Конфигурация системы вочдога
  3. Инициализация стека
  4. Низкоуровневая инициализация
  5. Копирование данных
  6. Очистка ОЗУ
  7. Вызов конструкторов/деструкторов
  8. Вызов main
  9. Обработка возврата из main

Новый код:

__attribute__((section(".init9"),aligned(2)))   int main(void)   { ... 

Попробуем собрать:

$>~/local/bin/clang++ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/      -D__MSP430F5438__ -S -emit-llvm -std=c++11 -Wall -c src/simpleadd.cpp -o src/simpleadd.ll ... rc/simpleadd.cpp:4:24: error: argument to 'section' attribute is not valid for this target: mach-o section specifier requires a         segment and section separated by a comma __attribute__((section(".init9"),aligned(2)))  

Facepalm 4, Mr. Clang начинает выводить меня из себя.
Возвращаемся к —help. Я внимательно читаю, чтобы найти, где я, возможно, что-то упустил.

В конце концов, нашел.

$>~/local/bin/clang++ --help ... --target=<value>        Generate code for the given target ... 

Согласен, мой косяк. Прошу прощения. Ну и как мне определить, какая цель правильная?
Попробуем по образу предыдущих запросов:

~/local/bin/clang++ --target=help clang-3.5: error: no input files   #version maybe? ~/local/bin/clang++ --version clang version 3.5   Target: x86_64-apple-darwin13.1.0   Thread model: posix   

Неудачно… А что, если попробовать очевидное:

~/local/bin/clang++ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/ \     -D__MSP430F5438__ -S -emit-llvm -std=c++11 --target=msp430 \     -Wall -c src/simpleadd.cpp -o src/simpleadd.ll 

Бабах!

Наконец-то сборщик работает как от него и требуется, и я могу запустить программу на устройстве.
Результирующий .ll:

; ModuleID = 'src/simpleadd.cpp' target datalayout = "e-m:e-p:16:16-i32:16:32-n8:16"   target triple = "msp430"  @"\01__WDTCTL" = external global i16  ; Function Attrs: nounwind define i16 @main() #0 section ".init9" align 2 {     %1 = alloca i16, align 2   %i = alloca i16, align 2   %result = alloca i16, align 2   store i16 0, i16* %1   store volatile i16 23168, i16* @"\01__WDTCTL", align 2   store i16 1, i16* %i, align 2   %2 = load i16* %i, align 2   %3 = add nsw i16 %2, 1   store i16 %3, i16* %result, align 2   %4 = load i16* %result, align 2   %5 = icmp eq i16 %4, 2   br i1 %5, label %6, label %7  ; <label>:6                                       ; preds = %0   ret i16 2  ; <label>:7                                       ; preds = %0   br label %8  ; <label>:8                                       ; preds = %7, %8   br label %8 }  attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }  !llvm.ident = !{!0}  !0 = metadata !{metadata !"clang version 3.5 "} 

Результирующий .s:

    .file   "src/simpleadd.ll"     .section    .init9,"ax",@progbits     .globl  main     .align  1     .type   main,@function main:                                   ; @main   ; BB#0:     push.w  r4     mov.w   r1, r4     sub.w   #6, r1     mov.w   #0, -2(r4)     mov.w   #23168, &__WDTCTL     mov.w   #1, -4(r4)     mov.w   #2, -6(r4)     mov.w   #2, r12     cmp.w   #2, r12     jne .LBB0_2 ; BB#1:     mov.w   #2, r15     add.w   #6, r1     pop.w   r4     ret .LBB0_2:                                ; =>This Inner Loop Header: Depth=1     jmp .LBB0_2 .Ltmp0:     .size   main, .Ltmp0-main       .ident  "clang version 3.5 " 

Обратите внимание, что main теперь правильно размещен в результирующем .map:

 792  .init0         0x0000000000005c00        0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_re#  793                 0x0000000000005c00                _reset_vector__  794  *(.init1)  795  .init1         0x0000000000005c00        0xc /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__w#  796                 0x0000000000005c00                __watchdog_support  797  *(.init2)  798  .init2         0x0000000000005c0c        0x4 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__i#  799                 0x0000000000005c0c                __init_stack  800  *(.init3)  801  .init3         0x0000000000005c10        0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__l#  802                 0x0000000000005c10                __low_level_init  803  *(.init4)  804  .init4         0x0000000000005c10       0x18 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_co#  805                 0x0000000000005c10                __do_copy_data  806  .init4         0x0000000000005c28       0x16 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_cl#  807                 0x0000000000005c28                __do_clear_bss  808  *(.init5)                                                                                                                    809  *(.init6)  810  *(.init7)  811  *(.init8)  812  *(.init9)  813  .init9         0x0000000000005c3e       0x2c src/simpleadd.o  814                 0x0000000000005c3e                main  815  *(.fini9)  816  .fini9         0x0000000000005c6a        0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__s#  817                 0x0000000000005c6a                __stop_progExec__ 

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

Об авторе:
Вильям Беннетт
Я старший инженер-программист в страховой компании. Я провожу ночи, работая над компиляторами и менеджерами рантайма для встраиваемых систем. Мне нравится возиться и собирать разные вещи.

От себя:
Учитывая, что английским я в основном пользуюсь для чтения мануалов и даташитов, есть вероятность не вполне правильного перевода некоторых моментов, так что если есть замечания, прошу поправить. Лично я давно собирался попробовать именно связку LLVM-mspgcc, но что-то всегда мешало, а тут эта статья. Если все пойдет хорошо, планирую повторить на Ubuntu-msp430-gcc-mspdebug-TI LaunchPad.
Также, в тексте есть некоторые моменты, связанные с особенностями OS X, которые я тоже могу не знать (вернее, не могу знать). Так что опять же, уточнения и конструктивная критика приветствуются.

На этом пока все, спасибо за ваше внимание.

1 — подсказали, что так более удачно. Пожалуй, соглашусь.

ссылка на оригинал статьи http://habrahabr.ru/post/241812/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *