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 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
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 (vi in this example):
Compiling the blink program
Get source code for a Hello, World! (blink) example program and compile:
cd ~ git clone https://github.com/1bitsy/1bitsy-examples.git cd 1bitsy-examples git submodule init git submodule update make
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 -rwxr-xr-x 1 mortense2 mortense2 235884 Dez 5 20:37 /home/mortense2/1bitsy-examples/examples/1bitsy/fancyblink/fancyblink.elf
Getting GDB to work
This presumes the current version of “libncurses” and “libtinfo” is 6.1 (“libncurses.so.6.1” (160 KB) and “libtinfo.so.6.1” (190 KB) exist in folder “/usr/lib/x86_64-linux-gnu”). If not, adjust the version numbers accordingly.
cd /usr/lib/x86_64-linux-gnu sudo ln -s libncurses.so.6.1 libncurses.so.5 sudo ln -s libtinfo.so.6.1 libtinfo.so.5
This puts in symbolic links for the libraries GDB is expecting (version 5).
ls -ls | grep libncurses.so.5 ls -ls | grep libtinfo.so.5
The output should look like (yes, for some reason Ubuntu is speaking German to me):
0 lrwxrwxrwx 1 root root 17 Dez 7 14:35 libncurses.so.5 -> libncurses.so.6.1 0 lrwxrwxrwx 1 root root 15 Dez 7 15:15 libtinfo.so.5 -> libtinfo.so.6.1
Check that GDB works:
The output should be something like:
GNU gdb (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 126.96.36.19990709-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).
Plug in Black Magic Probe to microUSB.
dmesg | tail
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).
Plug in 1bitsy to microUSB.
dmesg | tail
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 does NOT cover the main chip/IC.
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
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 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: XXX
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
<Ctrl + C> quit y
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
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):
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 expect.
Add it by:
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:
After this GDB finally works: