Binutils Port Maintenance Guidelines
This document shall serve as a quick introduction to the binutils port. It points out parts of code that need special attention when either upgrading binutils to the newer version or keeping the port compatible with recent changes in the HelenOS sources.
Most of the functionality required by the binutils is already provided by HelenOS native libc. On top of libc sits libposix - a compatibility layer providing relevant parts of POSIX API. For this purpose libposix redefines macros, aliases typedefs and provides function wrappers to alter function arguments, errno and return values. At the time of writing this, libposix also provides some untrivial functionality missing from libc which should however be moved into libc in the future. Binutils itself contains additional compatibility layer (libiberty) which provides legacy and GNU-specific functions. Next, there are two libraries (libbfd and libopcodes) containing shared functionality among binutils application. On top of libbfd and libopcodes, there are already as and ld binaries.
The general idea behind the binutils port is to keep binutils source tree as is and to do just a couple of surgical interventions that are defined and well documented at one place. Therefore, the port is very lightweight and flexible - it actually consists of just 3 scripts:
- Makefile downloads, unpacks, configures and makes binutils. To do so, it includes uspace/Makefile.common to use its variables and to hijack its execution by renaming all and clean targets. For successful configuration of binutils, it must also scan libposix sources for function declarations and prepare special object file containing function stubs of the same name (but without posix_ prefix). The actual configuration and build operations are carried out by binutils internal scripts to whom the control is passed when the environment is fully prepared.
- toolchain.sh generates a couple of shell scripts whose purpose is to hook toolchain calls and alter their arguments. Most of these hooks just redirect a call to the target-specific cross tool. The most complex hook is for gcc - it filters compiler flags and redirects link phase requests to the linker hook script. Compiler flags that are known when the hook script is generated are filtered and hardcoded into the script immediately during its generation, rest of the flags are filtered when the hook script is invoked.
- intrusive.sh encapsulates all the patches and hacks that need to be done to binutils sources before building it. Every patch is properly documented to explain its purpose and to allow reasoning about its relevance in the future.
Maintenance Soft Spots
Although there was an effort to make the port as robust as possible, the balance between libc, libposix and libiberty is potentially fragile. Especially future changes to libc headers can render the port non-buildable - attention should be given to addition/removal/changes/reordering of #include directives, macros, typedefs and function declarations. Whereas libposix was coded in such a way, that most of these threats should not be harmful, libiberty and binutils configure scripts are more sensitive to such changes.
Among encountered problems were the usage of POSIX functions/macros without including their respective headers, the expectation that some opaque typedefs resolve to certain basic types and the expectation that the underlying libc library is in fact glibc. Good news is that when binutils successfully configures itself and builds libiberty objects, the rest of the problems is usually caused just by compiler flags (either binutils flags are more restrictive than HelenOS flags or vice versa) which can be simply resolved by filtering them. However, if new functions are introduced to libc or libposix and such functions are part of some fallback mechanism in binutils sources, such newly accessible code can produce problems.
Another soft spot is the balance between the port Makefile and uspace/Makefile.common - i.e. changes to Makefile.common can potentially break the Makefile, because Makefile references some variables and targets of Makefile.common that are not intended as a public interface.
The way the port is done (i.e. it is not forked) allows it to be upgraded to a newer version. In order to do so, following tasks should be carried out:
- Makefile: change of REDIST_VERSION variable and possible update of CONF_FLAGS variable
- toolchain.sh: there are 4 places in gcc hook that deals with flag filtration - these places might need to be updated
- intrusive.sh: all patches shall be carefully checked for their relevance and possibly updated or removed, new patches might need to be introduced
At the time of writing this, HelenOS mainline could accomodate proper testing environment for binutils binaries just on ia32 and amd64 (non-problematic presence of compiler, assembler, linker, headers and libraries). On other target platforms, there are several limitations that, without hacks, prevent binutils from being tested (e.g. absence of compiler, limited size of the boot image, missing hard drive controller, missing interrupt controller, limited support provided by emulator). To allow testing until these, possibly long-term, limitations are resolved, there is prepared specially customized binutils-test branch on Launchpad which unifies testing process for all target platforms.
After merging to-be-tested changesets into the branch, it should be sufficient just to select target profile in the configuration screen (no other selections are required), build HelenOS and run the corresponding emulator script in contrib/conf. After booting up, bdsh automatically invokes a simple test script which assembles, links and executes tetris binary (sources are already pre-compiled in the form of .s files by the host gcc).
Note that for some target platforms, the emulator support is somewhat limited. At the time of writing this, the most recent emulators were qemu 0.14.1 and gxemul 0.6.0. Successfull boot and test could be carried out on these emulators:
- ia32 - qemu 0.14.1 and possibly older
- amd64 - qemu 0.14.1 and possibly older
- ia64 - ski (latest version from 2007)
- arm32 - gxemul 0.6.0 and possibly older
- mips32 - gxemul 0.6.0 and possibly older (but machine must be changed from oldtestmips to testmips)
- ppc32 - qemu 0.11.1 or 0.10.6 (newer fails to boot)
- sparc64 - qemu 0.14.1 (older fails to boot)