Build Clang/LLVM for AVR

Hi everyone,

I've been wondering how to correctly build clang/LLVM for the AVR target architecture. Unfortunately documentation is very scarce (or outdated or I didn't find it) and while I've been able to build clang/LLVM for AVR I'm still falling short of compiling an actual binary for the MCU. Here are the steps I've undertaken so far:

  git clone https://github.com/llvm/llvm-project
  cd llvm-project
  mkdir build && cd build

  cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi" -DLLVM_TARGETS_TO_BUILD="AVR" -DCMAKE_BUILD_TYPE=Release ../llvm
  make -j8
  sudo make install

Next I created a pretty empty main.cpp and tried to compile that:

  #include <stdint.h>
  int main()
  {
    for(;:wink: { }
    return 0;
  }

Here's the result of attempting the compilation ...

  $ clang++ --target=avr -mmcu=atmega328p -c main.cpp -o main.o -v
  clang version 11.0.0 (https://github.com/llvm/llvm-project 177dd63c8d742250dac6ea365e7c30f0fbab3257)
  Target: avr
  Thread model: posix
  InstalledDir: /usr/local/bin
  Found candidate GCC installation: /usr/lib/gcc/avr/5.4.0
  Selected GCC installation: /usr/lib/gcc/avr/5.4.0
  (in-process)
   "/usr/local/bin/clang-11" -cc1 -triple avr -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cpp -mrelocation-model static -mthread-model posix -mframe-pointer=all -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -target-cpu atmega328p -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -v -resource-dir /usr/local/lib/clang/11.0.0 -fdeprecated-macro -fdebug-compilation-dir /home/alex/tmp/clang-avr -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -faddrsig -o main.o -x c++ main.cpp
  clang -cc1 version 11.0.0 based upon LLVM 11.0.0git default target x86_64-unknown-linux-gnu
  #include "..." search starts here:
  #include <...> search starts here:
   /usr/local/include
   /usr/local/lib/clang/11.0.0/include
   /usr/include
  End of search list.
  In file included from main.cpp:1:
  In file included from /usr/local/lib/clang/11.0.0/include/stdint.h:52:
  /usr/include/stdint.h:26:10: fatal error: 'bits/libc-header-start.h' file not found
  #include <bits/libc-header-start.h>
           ^~~~~~~~~~~~~~~~~~~~~~~~~~
  1 error generated.

Looking for that file I can find it in the system

$ find /usr -iname libc-header-start.h
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h

But adding it to the clang call via -isystem or -I does not help either.

  $ clang --target=avr -mmcu=atmega328p -c main.cpp -o main.o -v -I/usr/include/x86_64-linux-gnu
    clang version 11.0.0 (https://github.com/llvm/llvm-project 177dd63c8d742250dac6ea365e7c30f0fbab3257)
  Target: avr
  Thread model: posix
  InstalledDir: /usr/local/bin
   (in-process)
   "/usr/local/bin/clang-11" -cc1 -triple avr -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cpp -mrelocation-model static -mthread-model posix -mframe-pointer=all -fmath-errno -fno-rounding-math -masm-verbose -mconstructor aliases -target-cpu atmega328p -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -v -resource-dir /usr/local/lib/clang/11.0.0 -I /usr/include/x86_64-linux-gnu -fdeprecated-macro -fdebug-compilation-dir /home/alex/tmp/clang-avr -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -faddrsig -o main.o -x c++ main.cpp
  clang -cc1 version 11.0.0 based upon LLVM 11.0.0git default target x86_64-unknown-linux-gnu
  #include "..." search starts here:
  #include <...> search starts here:
   /usr/include/x86_64-linux-gnu
   /usr/local/include
   /usr/local/lib/clang/11.0.0/include
   /usr/include
  End of search list.
  In file included from main.cpp:1:
  In file included from /usr/local/lib/clang/11.0.0/include/stdint.h:52:
  In file included from /usr/include/stdint.h:26:
  In file included from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33:
  In file included from /usr/include/features.h:448:
  /usr/include/x86_64-linux-gnu/gnu/stubs.h:7:11: fatal error: 'gnu/stubs-32.h' file not found
  # include <gnu/stubs-32.h>
            ^~~~~~~~~~~~~~~~
  1 error generated.

Thinking that maybe the old version (5.4.0) of avr-gcc might be a problem I've built a 9.2.0 version from sources

  $ avr-gcc -v
  Using built-in specs.
  Reading specs from /opt/avr/avr-gcc/lib/gcc/avr/9.2.0/device-specs/specs-avr2
  COLLECT_GCC=avr-gcc
  COLLECT_LTO_WRAPPER=/opt/avr/avr-gcc/libexec/gcc/avr/9.2.0/lto-wrapper
  Target: avr
  Configured with: ../configure --prefix=/opt/avr/avr-gcc --target=avr --enable-languages=c,c++ --disable-nls --disable-libssp --disable-libada --with-dwarf2 --disable-shared --enable-static --enable-mingw-wildcard
  Thread model: single
  gcc version 9.2.0 (GCC)

But this didn't change anything, except that when running clang without file arguments it does indicate that there is no avr-gcc in the system (although there is, but probably not spliced in there where the clang/LLVM build process expects it).

  $ clang --target=avr -mmcu=atmega328p
  clang-11: warning: no avr-gcc installation can be found on the system, cannot link standard libraries [-Wavr-rtlib-linking-quirks]
  clang-11: warning: standard library not linked and so no interrupt vector table or compiler runtime routines will be linked [-Wavr-rtlib-linking-quirks]
  clang-11: error: no input files

I'd greatly appreciate any input helping to solve these problems or a pointer to working documentation.

With kind regards, Alex

In file included from main.cpp:1:
In file included from /usr/local/lib/clang/11.0.0/include/stdint.h:52:
/usr/include/stdint.h:26:10: fatal error: ‘bits/libc-header-start.h’ file not found
#include <bits/libc-header-start.h>
^~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

I had the same issue initially. The problem here is that it tries to include /usr/include/stdint.h, which it should not because that’s the header for the host system not the AVR. To work around this you can use the -nostdlibinc flag.

See: https://bugs.llvm.org/show_bug.cgi?id=39210

It looks like you’re cross-compiling for an embedded target, so you need to be careful about how you treat headers (like libc’s), as the x86 ones will not be compatible with the AVR ones. The clang driver can manage this for you, if you give it enough information:

The first thing to try is adding the --gcc-toolchain argument to clang to point to the root of the avr gcc toolchain (I think in your case this will be /opt/avr/avr-gcc ). This should help the driver pick up the right include paths for AVR.

Try that see how you get on.

Sam

Thank you for both of your input. Yes, I try to cross-compile for AVR, the simple ATMEGA328P used in every Arduino Uno. My main motivation being that I hope to be able to use a couple of STL containers, <functional> and <type_traits> on the MCU. Not sure though if this can be reached by going via the clang route.

Getting back to the compilation: when I run clang with both both -nostdlibinc (which makes it necessary to point clang to the header file location via -isystem /opt/avr/avr-libc/avr/include) and --gcc-toolchain I can compile the minimum example code.

  clang++ --target=avr -nostdlibinc -isystem /opt/avr/avr-libc/avr/include --gcc-toolchain=/opt/avr/avr-gcc -mmcu=atmega328p -c main.cpp -o main.o

However, when I'm running a compilation on a more complicated embedded programm I get a lot of troubling warnings, e.g.

  warning: unknown attribute '__progmem__' ignored [-Wunknown-attributes]

(which are troubling me, because if all those string constants are put in RAM instead of Flash the RAM will be pretty soon empty :wink: ).

But those warnings aside I run ultimately into problems when getting to the linking step where it appears there are trouble providing implementations for avr-gcc built-in functions which I believe are located within the avr-gcc MCU specific libraries

  /opt/avr/avr-libc/avr/include/util/delay.h:187: undefined reference to `__builtin_avr_delay_cycles(unsigned long)'

as well as troubles which seem to be originating from my C++ usage within the firmware

undefined reference to `vtable for __cxxabiv1::__class_type_info'

Kind regards, Alex

Good Morning,

I've created a minimum source file and build instructions which exhibit the same behaviour I see at my larger firmware project. The example involves dynamic polymorphism and builtin AVR specific functions.

  #include <stdlib.h>
  #include <util/delay.h>

  void * operator new(size_t size) { return malloc(size); }
  void operator delete(void * ptr) { free(ptr); }
  __extension__ typedef int __guard __attribute__((mode (__DI__)));
  extern "C" int __cxa_guard_acquire (__guard * g) { return !*(char *)(g); }
  extern "C" void __cxa_guard_release (__guard * g) { *(char *)g = 1; }
  extern "C" void __cxa_guard_abort (__guard * g) { }
  extern "C" void __cxa_pure_virtual (void ) { }

  class DelayBase
  {
  public:
    virtual ~DelayBase() { }
    virtual void delay(unsigned int const ms) = 0;
  };

  class Delay : public DelayBase
  {
  public:
    virtual ~Delay() { }
    virtual void delay(unsigned int const ms) override { for(unsigned int i = 0; i < ms; i++) { _delay_ms(1); } }
  };

  int main()
  {
    Delay delay;
    for(;:wink:
    {
      delay.delay(10);
    }
    return 0;
  }

Compilation is done via

  $ clang++ --target=avr -nostdlibinc -isystem /opt/avr/avr-libc/avr/include --gcc-toolchain=/opt/avr/avr-gcc -mmcu=atmega328p -DF_CPU=16000000UL -O1 -c main.cpp -o main.o

and linking via

  $ avr-ld main.o -o main.bin --relax --gc-sections -L/usr/lib/avr/lib/avr5 -L/usr/lib/avr/lib/avr5 -L/opt/avr/avr-gcc/lib/gcc/avr/9.2.0/avr5 -l:crtatmega328p.o -lgcc -lm -lc -latmega328p -mavr5

which results in the following error message:

  avr-ld: main.o: in function `Delay::delay(unsigned int)':
  main.cpp:(.progmem.data+0xa8): undefined reference to `__builtin_avr_delay_cycles(unsigned long)'
  avr-ld: main.o:(.rodata._ZTI9DelayBase[_ZTI9DelayBase]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
  avr-ld: main.o:(.rodata._ZTI5Delay[_ZTI5Delay]+0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'

Any insight into the cause of this problem would be greatly appreciated :wink:
Kind regards, Alex

Two issues here:

1. You are using C++ runtime, so you'd need to link C++ runtime
library (e.g. libc++abi / libsupc++, etc)
2. You need to link some library that provides implementation of AVR
builtins (like __builtin_avr_delay_cycles). Note that avr-gcc
implements __builtin_avr_delay_cycles as a compiler builtin function
and expands it during the codegen.

Patches are welcome :wink:

Hi Anton, thank you for your feedback.

Regarding 1) I'm wondering from where to obtain such a library? Certainly it does not come with avr-gcc which is the reason I'm looking at clang/LLVM in the first place. Unfortunately to the best of my knowledge it was also not produced during the compilation of the cross-compiler, since both libc++.a and libc++abi.a are for i386:x86-64 architecture (objdump -f *.a).

Regarding 2) I believe I might be able to provide a patch for this, once I've learned a bit more about the internal works of LLVM :wink:

Kind regards, Alex