Linux on Microblaze HOWTO (part IV)
This is part IV of my HOWTO on running Linux on Microblaze. The outline is as follows:
- Part I: Introduction and setting up the Microblaze processor
- Part II: Compiling the kernel
- Part III: Preparing for boot and booting
- Part IV: Compiling user space applications (this page)
Compiling user space applications
We shall now look at how to compile applications for execution under the Microblaze Linux machine. This is pretty straightforward for programs written for the specific environment. The problems may occur when compiling sources which were originally written for fullblown computers, as the build system may not have taken cross compilation into account. And as software projects tend to be hacked to death, with new features added all the time, the code may depend on libraries which are installed on every desktop, but not necessarily on an embedded system. These dependencies are at times a result of a completely offbeat feature, but it’s often simpler to compile the necessary library than to remove the feature. Since there is no single recipe for solving that kind of problems, we’ll stick to the basics of compiling for user space.
Background
Cross compilation of user space applications is actually more difficult than compiling the kernel, mainly because the kernel itself is, after all, a standalone application. There are a few things to take care of:
- Make sure libraries for dynamic linking are in place in the target runtime filesystem, as well as the dynamic linker itself.
- The compilation should be done against the header files corresponding to the libraries present in the target.
- The linked libraries used during compilation should correspond to those in the target.
- The C Runtime stubs (crt1.o, crti.o, crtbegin.o, crtend.o and crtn.o) should fit the Linux user space environment (different files with similar names are used for Microblaze standalones).
This may sound complicated, but most of the job has already been done. So it all boils down to a few simple things to bare in mind, as shown next.
Preparing the target’s file system
In part II it was shown how to download and extract the cross compiler for Microblaze. The same tarball also has the entire package for the target’s root under microblazeel-unknown-linux-gnu/microblazeel-unknown-linux-gnu/sys-root. This directory should be copied into the target’s root as is.
But the target’s root directory is already populated with files as necessary to boot Linux, and run command line utilities with busybox. Some of the files in sys-root, dynamic libraries in particular, already exist in target root, and they’re not identical. But since busybox is statically linked, overwriting the dynamic libraries seems harmless. Overwriting the previous files where applicable is therefore the way to resolve these conflicts, since dynamically linked applications will be compiled against the newer libraries.
All in all, the sys-root directory is ~164 MB, which isn’t too bad when stored on a flash memory. ~129 of these are /lib. The library files include libm, libpthread, libresolv and several other important libraries.
/usr/include takes up ~14 MB, which is probably not necessary on the target system.
Compilation
For cross compilation we use the same compiler used for the kernel. To compile an application:
/path/to/microblazeel-unknown-linux-gnu-gcc --sysroot=/path/to/nfsroot/ -o hello hello.c -lm
where /path/to/nfsroot is the directory which will be the root directory when the executable runs, or a copy of it. The truth is I’m not really sure –sysroot is really necessary, but given the pretty wild search the GNU tools do to find include files and libraries, it looks like a good measure to point directly at where these should be found.
Note that if we omit –sysroot, compiling for Microblaze Linux user space is done simply by using the cross compiler normally, and that works too. This happens because the compiler was configured to look for libraries and includes from its own sys-root. The C runtime stubs are always taken from the compiler’s own.
This is a good time to repeat something said in part II: There is no need to “install” the Microblaze compiler, and neither do the files need to be owned by root. A simple untar anywhere is fine. The compiler uses relative paths to find its resources.
Static linking
To be somewhat safer, static compilation may be preferred, in particular when the whole system’s functionality consists of a single application. The executable file is considerably larger, but doesn’t depend on libraries, so it works even without the sys-root copy mentioned above. Just add the –static flag to gcc. e.g.
/path/to/microblazeel-unknown-linux-gnu-gcc --static --sysroot=/path/to/nfsroot/ -o hello hello.c -lm
The -lm flag is here to demonstrate that libraries should be at the end of the command line. This has no significance when the executable is compiled to be dynamic, but for static linking, failing to put the -lm (and other loadables) at the end will cause misleading errors such as:
/tmp/cckqzZqo.o: In function `main': : undefined reference to `sin' collect2: ld returned 1 exit statu
So put the -lm at the end, OK?
Compiling from SDK
Note: I don’t present a working solution in this subject.
Since the BSP, which is generated by the SDK along with the compilation directories, are just include files and libraries, it was appealing to try compiling Linux user space applications from the SDK. The method suggested is to overwrite the BSP created by SDK with include files and libraries for the Linux environment.
The problem I didn’t bother to solve in the end, was that the C runtime libraries used by the linker remained those for a standalone application, so the executable couldn’t run on Linux. Most likely, this can be solved easily, but since I don’t like IDEs myself, I left this issue as is.
Another problem with this method is that the BSP is erased every time the project is rebuilt (but it survives recompilation, of course). So it’s best to keep a copy of the entire BSP directory structure.
In short, this is the procedure, minus the part that makes the linker work with the Linux-related CRTs.
- Create a BSP by creating a C project
- Copy all .a files from sys-root’s /usr/lib and replace the lib directory in the BSP with a directory containing these (only)
- Replace the BSP’s include directory with sys-root’s /usr/include as is
- In SDK (Eclipse, actually), right-click the C project on the Project Explorer and pick Properties. Under C/C++ Build > Settings > Microblaze gcc linker > Linker script, remove the linker script given (edit the text, and remove the part saying ../src/lscript.ld)
And again, this almost works. If someone takes this to the finish line, please let me know.
Reader Comments
export CROSS_COMPILE=microblazeel-xilinx-linux-gnu-
export PATH=/opt/Xilinx/14.7/ISE_DS/EDK/gnu/microblaze/linux_toolchain/lin64_le/microblazeel-xilinx-linux-gnu/bin:$PATH
microblazeel-xilinx-linux-gnu-gcc -o HelloWorld.c
bash: microblazeel-xilinx-linux-gnu-gcc: command not found…
export CROSS_COMPILE=microblazeel-xilinx-linux-gnu-
source /opt/Xilinx/14.7/ISE_DS/settings64.sh