Monthly Archives: February 2015

Building mbed TLS with ELLCC

When I saw the recent announcement that PolarSSL is now owned by ARM and called mbed TLS, I thought it would be interesting to see how easily it could be built using ELLCC.

I downloaded the latest release (mbedtls-1.3.10-gpl.tgz) from the web site on to my x86_64 Linux box. A quick look at the README.txt file showed that mbed TLS can be built with make, cmake, or Microsoft Visual Studio. I opted for make.

It turns out that using ELLCC to cross build this library is very easy. Here is the command line for ARM:

[~/mbedtls] dev% tar xvfpz ~/Downloads/mbedtls-1.3.10-gpl.tgz
[~/mbedtls] dev% cd mbedtls-1.3.10/
[~/mbedtls/mbedtls-1.3.10] dev% make CC="~/ellcc/bin/ecc -target arm-linuxlld-engeabihf"

(I happen to have ELLCC installed in my home directory at ~/ellcc.) This single command built the library as well as a test suite. I have binfmt installed on my Linux box, which a cool way of telling my x86_64 Linux kernel how to run foreign executables (in this case ARM binaries). So I decided to try running the test suite.

[~/mbedtls/mbedtls-1.3.10] dev% make check
Running checks (Success if all tests PASSED)
 - test_suite_aes.ecb
   PASSED (77 / 77 tests (0 skipped))

 - test_suite_aes.cbc

[many tests deleted]

 - test_suite_version
   PASSED (5 / 5 tests (0 skipped))

[~/mbedtls/mbedtls-1.3.10] dev% 

All the tests passed. Pretty easy, huh? To prove I’ve really built for ARM, I used the file command:

[~/mbedtls/mbedtls-1.3.10] dev% file tests/test_suite_aes.cbc
tests/test_suite_aes.cbc: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped
[~/mbedtls/mbedtls-1.3.10] dev%

So I thought, “This is cool, how about using ELLCC’s MinGW64 support to try a build for Windows?”. The answer was only slightly more complicated.

First I tried:

[~/mbedtls/mbedtls-1.3.10] dev% make clean
[~/mbedtls/mbedtls-1.3.10] dev% make CC="~/ellcc/bin/ecc -target x86_64-w64-mingw32"

This didn’t work out so well. I got:

...
  CC    x509write_crt.c
  CC    x509write_csr.c
  CC    xtea.c
  AR    libmbedtls.a
ar: creating libmbedtls.a
  RL    libmbedtls.a
  LN    libpolarssl.a -> libmbedtls.a
  CC    aes/aescrypt2.c
../library/libmbedtls.a: error adding symbols: Archive has no index; run ranlib to add one
ecc: error: linker command failed with exit code 1 (use -v to see invocation)
make[1]: *** [aes/aescrypt2] Error 1
make: *** [all] Error 2
[~/mbedtls/mbedtls-1.3.10] dev%

Apparently my system ar command doesn’t handles Windows PE object files. No problem. I’ll just tell make to use ELLCC’s ecc-ar, which has been built with PE format support:

[~/mbedtls/mbedtls-1.3.10] dev% make clean
[~/mbedtls/mbedtls-1.3.10] dev% make CC="~/ellcc/bin/ecc -target x86_64-w64-mingw32" AR=~/ellcc/bin/ecc-ar

This time, building the test suite failed with errors like:

...
../library/libmbedtls.a(net.o):(.text+0x4ce): undefined reference to `__imp_WSAGetLastError'
../library/libmbedtls.a(net.o):(.text+0x503): undefined reference to `__imp_shutdown'
../library/libmbedtls.a(net.o):(.text+0x513): undefined reference to `__imp_closesocket'
ecc: error: linker command failed with exit code 1 (use -v to see invocation)
make[1]: *** [pkey/dh_client] Error 1
make: *** [all] Error 2
[~/mbedtls/mbedtls-1.3.10] dev

I seem to be missing a library needed for linking. I needed to find which library was missing, so I used the trusty nm command to find out:

[~/ellcc/libecc/mingw/x86_64-w64-mingw32/sys-root/mingw/lib] dev% ~/ellcc/bin/ecc-nm -A *.a | grep __imp_closesocket 
/home/rich/ellcc/bin/ecc-nm: libruntimeobject.a: File format not recognized
libws2_32.a:dscdbs00149.o:0000000000000000 I __imp_closesocket
libwsock32.a:divxs00037.o:0000000000000000 I __imp_closesocket
[~/ellcc/libecc/mingw/x86_64-w64-mingw32/sys-root/mingw/lib] dev%

Looks like I have two choices. libws2_32.a sounds newer (grin), so I’ll try that. The tests/Makefile has the line:

LDFLAGS += -L../library -lmbedtls $(SYS_LDFLAGS)

All I have to do is add a define for SYS_LDFLAGS:

[~/mbedtls/mbedtls-1.3.10] dev% make CC="~/ellcc/bin/ecc -target x86_64-w64-mingw32" AR=~/ellcc/bin/ecc-ar SYS_LDFLAGS=-lws2_32
...
  Generate      test_suite_rsa.c
  CC            test_suite_rsa.c
  Generate      test_suite_shax.c
  CC            test_suite_shax.c
  Generate      test_suite_x509parse.c
  CC            test_suite_x509parse.c
  Generate      test_suite_x509write.c
  CC            test_suite_x509write.c
  Generate      test_suite_xtea.c
  CC            test_suite_xtea.c
  Generate      test_suite_version.c
  CC            test_suite_version.c
[~/mbedtls/mbedtls-1.3.10] dev% 

Let’s try the file command again.

file tests/test_suite_aes.cbc
tests/test_suite_aes.cbc: PE32+ executable (console) x86-64, for MS Windows
[~/mbedtls/mbedtls-1.3.10] dev%

Perfect. Now the test suite. Since binfmt can handle Windows executables and run them under Wine, things should just work.

[~/mbedtls/mbedtls-1.3.10] dev% make check
...
XTEA Encrypt CBC #4 ............................................... PASS
XTEA Encrypt CBC #5 ............................................... PASS
XTEA Encrypt CBC #6 ............................................... PASS
XTEA Decrypt CBC #1 ............................................... PASS
XTEA Decrypt CBC #2 ............................................... PASS
XTEA Decrypt CBC #3 ............................................... PASS
XTEA Decrypt CBC #4 ............................................... PASS
XTEA Decrypt CBC #5 ............................................... PASS
XTEA Decrypt CBC #6 ............................................... PASS
XTEA Selftest ..................................................... PASS


PASSED (25 / 25 tests (0 skipped))

 - test_suite_version
   Check compiletime library version ................................. PASS
Check runtime library version ..................................... PASS
Check for POLARSSL_VERSION_C ...................................... PASS
Check for POLARSSL_AES_C when already present ..................... PASS
Check for unknown define .......................................... PASS


PASSED (5 / 5 tests (0 skipped))

[~/mbedtls/mbedtls-1.3.10] dev% 

Voila!

Using ELLCC to Build for the Cortex-M3

This weekend I decided to work on getting ELLCC to support the ARM Cortex-M processors. The Cortex-M processors use the Thumb 16 bit instruction set, so adding M support involves adding a configuration file for a member of the Cortex-M family and then building the run-time libraries specifically for the target processor. To start I chose the Cortex-M3. Its configuration file currently looks like this:

[~/ellcc/libecc/config] dev% cat thumb-linux-engeabi
based_on: arm-ellcc-linux
compiler:
options:
- -target arm-ellcc-linux
- -mthumb
- -mcpu=cortex-m3
- -mfpu=none
- -mfloat-abi=softfp
linker:
static_crt1: $R/lib/thumb-linux-engeabi/crt1.o
dynamic_crt1: $R/lib/thumb-linux-engeabi/Scrt1.o
crtbegin: $R/lib/thumb-linux-engeabi/crtbegin.o
crtend: $R/lib/thumb-linux-engeabi/crtend.o
library_paths:
- -L$R/lib/thumb-linux-engeabi

With the config file in place, I attempted to build ELLCC’s standard C library musl for the M. Building a library for a new configuration is pretty easy:

[~/ellcc/libecc] dev% make thumb-linux-engeabi
[~/ellcc/libecc] dev% cd
[~] dev% cd ~/ellcc/libecc
[~/ellcc/libecc] dev% make thumb-linux-engeabi

Well, it isn’t quite that simple. The musl library has several assembly files that are written for the specific target processor. If the assembly language of the desired processor doesn’t match the sources, the sources have to be modified to support the new target. Luckily Thumb instructions are mostly a subset of 32 bit ARM instructions so the changes needed to build musl for the M were fairly minimal.

The next step to support a given processor is to compiler the compiler-rt library. This library contains support functions that handle operations that the processor doesn’t support directly and compiler generated built in functions. In the case of the M processors, this includes things like floating point functions since the M3 doesn’t have a floating point unit. Building compiler-rt was probably the most challenging part of this little project. I had never fully ported the compiler-rt build rules to use the new config files properly so rather than cobbling together something that could handle building for the M, I rewrote the build rules to properly support config files.

The last step is to build the C++ standard libraries, assuming you want C++ support. I ran into one little glitch here. The M doesn’t support 64 bit atomic operations so I had to modify those operations for the M.

Now that the libraries are built, a little experimentation is in order. I decided to try out a simple “hello world”. Here’s the source:

[~/ellcc/examples/hello] dev% cat main.c
#include <stdio.h>

int main()
{
printf("hello world\n");
}

The make files in the example directories can build for multiple targets as long as config files have been set up, so I built for the M:

[~/ellcc/examples/hello] dev% make thumb-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hello'
Compiling main.c
Linking hello
make[1]: Leaving directory `/home/rich/ellcc/examples/hello'
[~/ellcc/examples/hello] dev% ~/ellcc/bin/qemu-arm hello
hello world
[~/ellcc/examples/hello] dev%

Nice! I was curious how the Thumb instruction set affected code size so I did a side by side comparison:

[~/ellcc/examples/hello] dev% size hello
text data bss dec hex filename
19540 156 1412 21108 5274 hello
[~/ellcc/examples/hello] dev% make arm-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hello'
rm -f *.o hello hello.bin hello.log elkconfig.ld
Compiling main.c
Linking hello
make[1]: Leaving directory `/home/rich/ellcc/examples/hello'
[~/ellcc/examples/hello] dev% size hello
text data bss dec hex filename
27432 156 1412 29000 7148 hello

ELLCC creates static binaries by default, so those sizes include all the standard library bits that the program needs, i.e. printf() and software floating point support functions. It is kind of cool that the M is about 15% smaller than the equivalent ARM code for this example.

I decided to try C++. Here’s the code:

[~/ellcc/examples/hellocpp] dev% cat main.cpp
#include <iostream>

int main()
{
std::cout << "hello world" << std::endl; }

Here are the results:

[~/ellcc/examples/hellocpp] dev% make thumb-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hellocpp'
Compiling main.cpp
Linking hellocpp
make[1]: Leaving directory `/home/rich/ellcc/examples/hellocpp'
[~/ellcc/examples/hellocpp] dev% ~/ellcc/bin/qemu-arm hellocpp
hello world

And a size comparison:

[~/ellcc/examples/hellocpp] dev% size hellocpp
text data bss dec hex filename
867540 500 10824 878864 d6910 hellocpp
[~/ellcc/examples/hellocpp] dev% make arm-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hellocpp'
rm -f *.o hellocpp hellocpp.bin hellocpp.log elkconfig.ld
Compiling main.cpp
Linking hellocpp
make[1]: Leaving directory `/home/rich/ellcc/examples/hellocpp'
[~/ellcc/examples/hellocpp] dev% size hellocpp
text data bss dec hex filename
1412708 500 10824 1424032 15baa0 hellocpp

Cool stuff.

Finally a size comparison of ELLCC's ecc clang/LLVM based compiler compiled for Thumb and ARM:

[~/ellcc] dev% size bin-thumb-linux-engeabi/ecc
text data bss dec hex filename
37889520 33760 63000 37986280 2439fe8 bin-thumb-linux-engeabi/ecc
[~/ellcc] dev% size bin-arm-linux-engeabi/ecc
text data bss dec hex filename
49504456 33760 63000 49601216 2f4dac0 bin-arm-linux-engeabi/ecc

Looks like the Thumb code is about 25% smaller.

Precompiled binaries supporting the Thumb and all the other ELLCC targets are available in the 0.1.10 release available on the Download page.