Getting the ARM toolchain to work on Ubuntu 19.04 and later (including for Black Magic Probe (BMP))
The short version is: Download the tar ball from arm.com, extract, add the /bin folder in the extracted to the path, and make GDB work by adding symbolic links in /usr/lib/x86_64-linux-gnu for the expected version 5 of the libraries “libncurses” and “libtinfo” (to point to the existing/newest versions of those libraries).
This post contains complete instructions to set up the GCC cross-compiler from scratch on a newly installed Ubuntu 19.04 (Disco Dingo) or later (no extra packages assumed to be installed), compiling a “Hello, World!” (LED blink) program for an ARM board (1Bitsy), flashing it to the board using GDB and the Black Magic Probe, and interactively single-stepping the program in GDB.
The installation part is completely general and the LED blink part only requires specifying another processor (as the used library works on multiple ARM processors). The flash and debugging part in GDB is dependent on an adapter that implements the GDB debugging protocol (Black Magic Probe is used as an example here).
Only the last part is specific for Black Magic Probe.
A previous blog post covered using apt-get to install the GCC cross-compiler for ARM:
sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa sudo apt-get update sudo apt-get install gcc-arm-embedded
While this still works on Ubuntu 18.04 (32 bit) and Ubuntu 16.04 (64 bit), it no longer works on Ubuntu 19.04 and later:
E: Unable to locate package gcc-arm-embedded
Even when using the new procedure to get the compiler to run, there are also problems getting GDB to run. For further details, see Appendix A.
The following can be blindly applied. It has been tested on a 64-bit Ubuntu MATE 20.04 (Focal Fossa) system and a 64-bit Ubuntu 19.10 (Eoan Ermine) system and is likely to also work on a 64-bit Ubuntu 19.04 (Disco Dingo) system.
Setting up the cross compiler
Before starting, uninstall any already-installed ARM GCC cross compiler (this command can be used unconditionally – if it is not installed, it will report “Package ‘gcc-arm-none-eabi’ is not installed, so not removed”):
sudo apt remove gcc-arm-embedded
Instead of using apt-get, we get the toolchain directly from arm.com as a tar ball. This is actually much simpler than it sounds: Download it, extract it, and add the location of the /bin folder in the extracted folder to the path (environment variable “PATH”). That’s it. In detail:
In the following, the sample path “/temp2/2019-12-05” under the current user is used to download and extract into. You may want some other folder(s) – adjust as needed.
Go to arm.com and find “Linux x86_64 Tarball”. Download “gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2” to ~/temp2/2019-12-05 (direct download URL (valid as of 2019-12-07)), MD5 FE0029DE4F4EC43CF7008944E34FF8CC. It is about 110 MB.
Extract it. For example, in Nautilus (the default file manager in Ubuntu), right click on the .bz2 file, select “Open with Archive Manager”, select the single folder, right click on it, select “Extract”, press button “Extract”, wait for it to finish, “Close”, and close Archive Manager.
Alternatively, on the command line:
cd ~/temp2/2019-12-05 tar xvzf gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2
Install other required packages:
sudo apt-get update sudo apt-get install ncurses-dev sudo apt-get install git sudo apt-get install make sudo apt-get install python-minimal
For initial testing (in the same session), this suffices:
cd ~ export PATH=$PATH:~/temp2/2019-12-05/gcc-arm-none-eabi-9-2019-q4-major/bin arm-none-eabi-c++ --version
The output should look something like this:
arm-none-eabi-c++ (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599] Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
For more permanent use, put this into the .profile file, near the end, in the user top folder, using some text editor (Vim/vi in this example – but you need to be proficient in using Vim/vi. Otherwise, use another editor, like nano or Geany):
After the change, it may be necessary to login/logout or restart the computer to make the change take effect (it may not be enough to open a new terminal window).
Compiling the blink program
Get source code for a Hello, World! (blink) example program:
cd ~ git clone https://github.com/1bitsy/1bitsy-examples.git cd 1bitsy-examples git submodule init git submodule update
On Ubuntu 20.04 and later, change “python” in the very first line of file “irq2nvic_h” to “python3” (see the appendix for details):
Check that it produced the executable:
ls -ls ~/1bitsy-examples/examples/1bitsy/fancyblink/*.elf
The result should be something like (broken up in two lines here):
112 -rwxrwxr-x 1 embo embo 234364 Jan 19 19:19 /home/embo/1bitsy-examples/examples/1bitsy/fancyblink/fancyblink.elf
Getting GDB to work
This presumes the current version of “libncurses” and “libtinfo” is 6.2 (“libncurses.so.6.2” (160 KB) and “libtinfo.so.6.2” (190 KB) exist in folder “/usr/lib/x86_64-linux-gnu”). If not, adjust the version numbers accordingly (in Ubuntu 19.10, the version is 6.1 instead of 6.2.)
cd /usr/lib/x86_64-linux-gnu sudo ln -s libncurses.so.6.2 libncurses.so.5 sudo ln -s libtinfo.so.6.2 libtinfo.so.5
This puts in symbolic links for the libraries GDB is expecting (version 5).
ls -ls /usr/lib/x86_64-linux-gnu | grep libncurses.so.5 ls -ls /usr/lib/x86_64-linux-gnu | grep libtinfo.so.5
The output should look like:
0 lrwxrwxrwx 1 root root 17 Jan 19 15:38 libncurses.so.5 -> libncurses.so.6.2 0 lrwxrwxrwx 1 root root 15 Jan 19 15:38 libtinfo.so.5 -> libtinfo.so.6.2
Check that GDB works:
The output should be something like:
GNU gdb (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 220.127.116.1190709-git Copyright (C) 2019 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
Setting up for hardware
id sudo adduser $USER dialout id
The output should include “(dialout)” in the “groups=” part. In that case, log out and log in. If it is not in the output, a restart of the computer is required (if a restart is not done the result is “Permission denied” in the “target extended-remote /dev/ttyACM0” line below).
Connect the Black Magic Probe with a microUSB cable.
clear ; dmesg | tail -n 15
Output includes: “ttyACM0” and “ttyACM1”. “ttyACM0” is the COM port that GDB uses to communicate with the Black Magic Probe and “ttyACM1” is the auxiliary COM port that can be used to connect to the target board’s serial port (so it is not necessary to use a separate USB-to-serial adapter).
Note that if some device with a serial port, for example, an Arduino, was already connected, the numbers may be shifted (it all depends), “ttyACM1” and “ttyACM2”. An example could be a macro keyboard based on Arduino Leonardo or a keyboard based on QMK (also macro capable).
Connect the 1Bitsy with a microUSB cable.
clear ; dmesg | tail -n 15
Output includes: “ttyACM2” – but only if there is already firmware on it that makes it appear as a serial device. This is not the case for the Kickstarter one…
Connect 1Bitsy and Black Magic Probe using the JTAG cable. Note: the ribbon cable is to come from the same direction as the USB cable. That is, the ribbon cable should not cover the main chip/IC of the 1Bitsy.
Flashing the blink program
cd ~/1bitsy-examples/examples/1bitsy/fancyblink arm-none-eabi-gdb fancyblink.elf target extended-remote /dev/ttyACM0 monitor version monitor help monitor jtag_scan attach 1 load
Debugging with GDB
run y <Ctrl + C> list
Note: ‘run’ (or ‘start’ or repowering) is required if updating/changing the source code. The changes do not take effect before restarting, that is, flashing (‘load’ in the previous step) will not actually make it start execute the newly flashed program (the old one is still RAM and is still executing).
Debugging in full-screen mode
Using a line debugger is like being trapped in the 1970s. However, GDB can also do full-screen debugging.
It can also be put into a mode when Enter does not have to be pressed for each command. Thus it can come close to an experience of what is expected of a debugger.
Enable full-screen mode:
It immediately shows a screen with about 10 lines on each side of the current line:
Enable single key mode: Ctrl + X, C
Exit single key mode: Q
Continue, break into the running program (on the 1Bitsy), and do some step over and step in operations:
cont <Ctrl + C> step start y next step next next next cont
- ‘next’ (short ‘n’) is step over (like F10 in Visual Studio)
- ‘step’ (short ‘s’) is step in (like F11 in Visual Studio)
- ‘finish’ (short ‘fin’) is step out (like Shift + F11 in Visual Studio).
<Ctrl + C> quit y
This procedure was tested on Ubuntu 19.04 and Ubuntu 20.04 MATE with standard AMD PC hardware (64-bit, 8 GB RAM, etc.). For previous blog posts, it was tested on Ubuntu 16.04, Ubuntu 17.10, and Ubuntu 18.04 (some in 32-bit editions).
This blog post will continue to be updated as new versions of Ubuntu (and some other Linux distributions), the ARM cross compiler, Python, libopencm3, etc. becomes available (though possibly with some delay – express interest in comments to speed it up).
Appendix A Error messages and troubleshooting
While the main part of this post is focused on getting the toolchain installed as quickly as possible and up and running when debugging on ARM, this appendix documents the error messages that you may run into, the troubleshooting, and the background information on why the instructions are as they are.
On Ubuntu 19.04 and later, this does not work:
We get this error:
E: Unable to locate package gcc-arm-embedded
Using ‘tar’ on the command line to extract the GCC cross-compiler tar ball may lead to this output:
gzip: stdin: not in gzip format tar: Child returned status 1 tar: Error is not recoverable: exiting now
The current resolution is to use the file manager instead (as described in the main part of this blog post).
PATH setup blues – using Vim
When using Vim/vi to edit file ‘.profile’ this may be the result when pasting in the path line (the error message includes the quote at the end):
E353: Nothing in register "
The reason is that Vim/vi is not in insert mode. The first character in the clipboard, “P” in “PATH=$PATH:~/temp2/2019-12-05/gcc-arm-none-eabi-9-2019-q4-major/bin”, is seen as a command, not something to paste. A direct paste actually sometimes works if using a command-line window on Ubuntu, but it may not always work, e.g., if using SSH to connect to another computer, etc.
The resolution is to enter insert mode explicitly by pressing “i” first.
Using ‘make’ (when the PATH environment variable has been correctly set up), may result in this error:
GENHDR include/libopencm3/lpc17xx/irq.json /usr/bin/env: ‘python’: No such file or directory make: *** [Makefile:55: include/libopencm3/lpc17xx/irq.json.genhdr] Error 127 make: *** [Makefile:54: lib] Error 2
The reason is that with Ubuntu 20.04 and later, the executable for the Python interpreter is no longer ‘python’, but ‘python3’ – the executable ‘python’ (for Python 2) was removed. And the library “libopencm3” used by the example code has not yet been updated to reflect this fact (even though there was some discussion in November 2020).
The resolution is to change “python” in the very first line of file “irq2nvic_h” to “python3” (in our example, the file is at “~/1bitsy-examples/libopencm3/scripts/irq2nvic_h”). The result should be “#!/usr/bin/env python3”.
If GDB is launched after the tar ball has been installed (so the compiler can be invoked from anywhere),
this is the likely result:
arm-none-eabi-gdb: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory
We will add some symbolic links, but first, to be confident we are making the changes in the right folder (for 32 bit or 64 bit libraries), find out the bitness of GDB (32 bit or 64 bit):
For 64 bit, the result includes “arm-none-eabi-gdb: ELF 64-bit LSB executable, x86-64”.
For 64 bit, the “libncurses” library lives in “/usr/lib/x86_64-linux-gnu”:
cd /usr/lib/x86_64-linux-gnu ls -ls | grep libncurses
The output includes:
4 -rw-r--r-- 1 root root 31 Aug 9 12:18 libncurses.so 0 lrwxrwxrwx 1 root root 17 Aug 9 12:18 libncurses.so.6 -> libncurses.so.6.1 160 -rw-r--r-- 1 root root 162024 Aug 9 12:18 libncurses.so.6.1
There is the symbolic link “libncurses.so.6”, but not “libncurses.so.5” that GDB expects.
Add it by:
cd /usr/lib/x86_64-linux-gnu sudo ln -s libncurses.so.6.1 libncurses.so.5
If we try starting GDB again we get (a different error message):
arm-none-eabi-gdb: error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory
As for libncurses, so for “libtinfo” from the error message:
cd /usr/lib/x86_64-linux-gnu ls -ls | grep libtinfo
The output includes:
0 lrwxrwxrwx 1 root root 35 Aug 9 12:18 libtinfo.so -> /lib/x86_64-linux-gnu/libtinfo.so.6 0 lrwxrwxrwx 1 root root 15 Aug 9 12:18 libtinfo.so.6 -> libtinfo.so.6.1 188 -rw-r--r-- 1 root root 192032 Aug 9 12:18 libtinfo.so.6.1
Like for libncurses, there is the symbolic link “libtinfo.so.6”, but not “libtinfo.so.5” that GDB expects.
Add it by:
cd /usr/lib/x86_64-linux-gnu sudo ln -s libtinfo.so.6.1 libtinfo.so.5
After this, GDB finally works:
The output includes:
GNU gdb (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 18.104.22.16890709-git