RISC OS Build EnvironmentROBE

Optional subsystems1764-bit Development

Introduction and Overview

Some build-environment images include a parallel 64-bit toolchain for building RISC OS 64-bit libraries and executables. This support is intentionally kept as an optional chapter because it changes filenames, library expectations, and build conventions in ways that do not apply to the normal 32-bit workflow.

The short version: riscos-amu is what you run to build things 32-bit; adding BUILD64=1, or using the equivalent riscos64-amu wrapper, is what makes it build 64-bit instead. As with the 32-bit workflow, the normal route is to generate a project with riscos-project and build it with riscos-amu BUILD64=1; the shared makefiles resolve the 64-bit include paths, library macros, and output directories automatically. The riscos64-* direct toolchain commands described later in this chapter are still useful to understand, since they explain what the makefile build is doing underneath, and they remain the right tool for one-off experiments or for diagnosing a build that does not behave as expected.

Toolchain overview

What the 64-bit tool family is for

The riscos64-* tools target the developing 64-bit RISC OS environment. They are not wrappers around the Norcroft 32-bit toolchain; they form a separate family with their own compiler, linker, make wrapper, and assembler path.

Filename differences

The 64-bit help explicitly notes that RISC OS filename conventions are not supported in the same way as the 32-bit wrappers. Source files are therefore normally named with host-style extensions such as main.c, hello_world.s, and main.o.

Libraries and headers

The default include path covers the 64-bit export trees under /riscos-resources/Export64. The variable RO64EXPORT refers to that resource area. The supplied 64-bit C library is linked by default, and OSLib is available through -loslib.

A practical limitation is that many libraries have not yet been ported fully to 64-bit use. Some are present only as headers, and some still use unsuitable types. This means that successful 32-bit code may still require manual porting work before a 64-bit build is realistic.

Where OSLib support is available, a build may look like:

riscos64-cc -loslib -I$RO64EXPORT/Lib/OSLib -o myprog,ff8 main.c

Building through the shared makefile system

Creating a plain C project

The standard command project skeleton already builds for 64-bit; no separate template is needed for plain C:

riscos-project create --type command --name MyTool --skeleton
riscos-amu BUILD64=1

This writes the final executable into aif64/. The shared LibraryCommand makefile resolves the 64-bit include paths and the default C library automatically, so no special variables are needed beyond BUILD64=1.

Video 8 of Video walkthroughs rebuilds an existing 32-bit project this way and boots it with riscos-build-run's --arch AARCH64 flag.

Creating a C++ project

The project generator can create both C++ command and module skeletons through riscos-project. The command form is the more established workflow:

riscos-project create --type c++command --name MyTool --skeleton

The installed c++command template creates cc/main and a Makefile,fe1 using LibraryCommand with ${CLIB} ${C++LIB} in LIBS. Build it with riscos-amu BUILD64=1, which writes the final executable into aif64/. The ${C++LIB} macro expands through the makefile's own processing to the correct, versioned C++ runtime library path; this is the supported way to resolve that library, rather than spelling out the path by hand (see C++ workflow below for the equivalent direct command).

The same tool also ships a c++module template:

riscos-project create --type c++module --name MyModule --skeleton

That skeleton creates cc/module, cmhg/modhead, and a Makefile,fe1 using CModule with ${C++LIB} in LIBS. Treat it as a starting point rather than a proven final pattern, because 64-bit C++ module builds are still a specialised workflow.

Creating a Fortran project

The project generator can create a Fortran command-line tool skeleton with riscos-project type fcommand. The installed template creates a f90/ directory with f90/main and a Makefile,fe1 based on LibraryCommand with ${CLIB} ${FLIB} in LIBS. Like ${C++LIB} above, both macros are resolved automatically by the shared makefile: ${CLIB} selects the configured C library, and ${FLIB} expands to the -lgfortran runtime needed to link Fortran objects.

A typical start is:

riscos-project create --type fcommand --name MyTool --skeleton

The starter Fortran source includes VersionFortran, so create that companion file when needed and refresh it through riscos-vmanage before the first build that expects version text in the program output. Like VersionBas (see Project Creation and Management), riscos-vmanage only updates VersionFortran if the file already exists, so create an empty file first:

touch VersionFortran
riscos-vmanage update

The generated file is plain free-format Fortran, intended to be included into a program or module rather than compiled on its own. It opens with a few ! comment lines naming the component, version, and date, followed by a flat list of character(len=*), parameter and integer, parameter declarations such as Module_MajorVersion, Module_Version, Module_Date, Module_FullVersion, and Module_FullVersionAndDate, mirroring the constants the BASIC and assembler companion files provide for their respective languages.

Creating a Pascal project

The project generator can create a Pascal command-line tool skeleton with riscos-project type pcommand. The installed template creates a p/ directory with p/main and a Makefile,fe1 based on LibraryCommand with ${CLIB} ${P2CLIB} in LIBS.

riscos-project create --type pcommand --name MyTool --skeleton
riscos-amu BUILD64=1

This writes the final executable into aif64/. The build translates p/main through riscos64-p2cc before compiling and linking it, using the same translator invocation described in Pascal workflow below.

Direct language toolchain use

The commands below call the 64-bit compilers and linker directly, bypassing the shared makefile system. Use them when experimenting with a small single-file program, investigating a toolchain issue, or understanding what a generated makefile is doing underneath; for ordinary project work, prefer the riscos-project and riscos-amu route described above.

Basic C workflow

The simplest route is to compile and link a one-file program directly:

To separate compilation and linking, use -c and then link the resulting object with riscos64-link.

The one-file case is as simple as:

riscos64-cc main.c

For separate compile and link steps:

riscos64-cc -c main.c
riscos64-link main.o -o main,ff8

Both commands are silent on success and only print messages on error, so check the exit status or run ls main,ff8 to confirm the file was produced.

Note: when an example below builds an object with the RISC OS-style path -o o.<name> rather than a flat host filename, the o/ directory must already exist in the current directory; the tools do not create it automatically and fail with can't create o/<name>: No such file or directory if it is missing.

Pascal workflow

Pascal source can also target the 64-bit toolchain through riscos-p2cc -64 or the convenience wrapper riscos64-p2cc. This path translates the Pascal source to ANSI C first and then passes that generated C through the 64-bit compiler path. Pascal projects created with riscos-project (see Creating a Pascal project above) call the same front end automatically when built with riscos-amu BUILD64=1.

For a one-file build, either form is acceptable:

riscos-p2cc -64 source.p -o output,ff8
source
Translation completed.
Compilation complete.

As with the direct C workflow above, this depends on the 64-bit C libraries and the P2C runtime support being present for the selected target. The 64-bit P2C runtime library (libP2C-64) is a RISC OS format archive, so it is exported alongside the 32-bit P2C libraries under Export/Lib/P2C/o/ rather than under Export64, which is reserved for host-format build assets.

C++ workflow

64-bit C++ support is provided through riscos64-c++. The normal source layout places implementation files in cc/ and headers in h/, with host files such as cc/main being presented to the compiler as cc.main.

64-bit C++ support is intentionally restricted. Exceptions and RTTI are disabled, and templates and module builds should still be treated as specialised workflows. Keep the code style simple and prefer explicit C-compatible interfaces where the program must interwork with existing RISC OS libraries or module entry points.

The basic compile and link sequence is:

riscos64-c++ -c -o o.hello cc.hello
riscos64-link o.hello -lsupc++ C:c++64.o.libc++-64 -o hello,ff8

The exported C++ library file carries a -64 suffix (libc++-64, distinct from the 32-bit libc++lib), so the link line must reference C:c++64.o.libc++-64 rather than C:c++64.o.libc++.

For direct compile-and-link use, the wrapper can also call riscos64-link on your behalf. When invoked only with existing object files, it becomes a thin linker front end and does not add the C++ support libraries automatically, so fully manual object-only link commands must still supply them explicitly.

In a riscos-project makefile, use ${C++LIB} in LIBS rather than spelling out the library path by hand (see Creating a C++ project above).

Fortran workflow

64-bit Fortran support is provided through riscos64-fortran and the thinner riscos64-gfortran wrapper. In the normal workflow, Fortran source files live in f90/ and do not carry a host .f90 suffix. The configured wrapper accepts the corresponding RISC OS-style names such as f90.hello.

The basic compile and link sequence is:

riscos64-fortran -c -o o.hello f90.hello
riscos64-link o.hello -lgfortran -o hello,ff8
Warning: Failed to patch symbol name '_gfortran_transfer_character_wide_write' at &d958
...

The compile step is silent on success. The link step prints a handful of Warning: Failed to patch symbol name ... lines for GFortran runtime symbols even on a successful build; these are expected and do not indicate a build failure. Check the exit status or run ls hello,ff8 to confirm the binary was produced.

Always link with -lgfortran when any Fortran object is present. The GNU Fortran runtime supplies the normal Fortran startup and input/output support, even for simple one-file programs.

For reusable code, put shared procedures in a module and compile the module before the source file that uses it, so that the compiler-generated .mod interface file is available during the later compile. The usual pattern is:

riscos64-fortran -c -o o.countlib f90.countlib
riscos64-fortran -c -o o.countmain f90.countmain
riscos64-link o.countmain o.countlib -lgfortran -o countmain,ff8

Use riscos64-gfortran only when you want raw GFortran behaviour with the target macros defined consistently. It does not add the Fortran include path, wrapper-level PIC policy, output naming defaults, or the riscos64-link stage.

Fortran and C interworking

Use the standard Fortran iso_c_binding module for all C-facing interfaces. Fortran routines that must be called from C should use bind(C, name="..."), and scalar C arguments passed by value should normally use the Fortran value attribute.

Common type mappings are:

C typeFortran form
intinteger(c_int)
char *character(kind=c_char), dimension(*)
void *type(c_ptr)
intptr_tinteger(c_intptr_t)

When passing a Fortran string literal to C, append c_null_char. For RISC OS control-terminated strings, test with iachar(text(pos)) >= 32 rather than looking for a C nul terminator. For _kernel_swi_regs, the 64-bit export uses 32-bit register values, so C regs.r[0] maps to Fortran regs(1).

Assembler paths

Two assembler routes exist:

  • riscos64-as for direct GNU assembler source;
  • riscos64-objasm for an ObjAsm-like command line which translates the source to GNU assembler syntax and then assembles it with the AArch64 GNU tools.

This is an important difference from the 32-bit path, where riscos-objasm directly matches the established ObjAsm workflow.

The riscos64-objasm wrapper keeps an ObjAsm-like interface, but internally uses objasm2gas in 64-bit mode and then invokes the GNU assembler. This means that the practical syntax is intentionally close to the classic assembler path even though the backend is completely different.

A typical 64-bit assembly step is:

riscos64-objasm -I hdr -PreDefine "Support64 SETL {TRUE}" -o o64/module s/module

The wrapper accepts the source and output file in the same positional style as the traditional assembler, or with -o for the output file. As with the classic ObjAsm convention, capitalisation in the option name only shows the shortest accepted abbreviation; any case is accepted for the option itself. The most relevant 64-bit options are -I for include search paths, -V for verbose conversion, -PreDefine (or -PD) for an initial SETA, SETL, or SETS directive, -64 to select AArch64 assembly, and -Bin (or -B) to request a raw binary output instead of the default ELF object. A -NOWarn-style warning suppression option is not currently implemented by this wrapper, despite appearing in its --help text; any unrecognised option is silently treated as a positional filename, which produces a confusing Unrecognised parameter error rather than an option error.

Implicit linker helpers

The normal 64-bit workflow also uses two lower-level helpers indirectly through riscos64-link: riscos64-mkreloc and riscos64-addsignatures. Most users do not run either command directly.

After an initial partial link, riscos64-link runs riscos64-objdump --reloc --syms --wide over the intermediate ELF and feeds that combined listing to riscos64-mkreloc. The result is an extra object which contains the relocation table and the default relocation helper used at run time by relocatable 64-bit images.

Once the final ELF has been turned into a flat RISC OS binary, riscos64-link then runs riscos64-addsignatures on the finished output together with a symbol listing. That helper patches RISC OS-style function signatures into the reserved padding before callable entry points, so debugger and runtime tools can recover function names from the binary itself.

Direct invocation is mainly useful when you are debugging the 64-bit link pipeline, experimenting with a custom final link step, or diagnosing why a particular function was not given a reloc entry or signature.

Supporting tools and diagnostics

Variables and diagnostics

Two variables matter when debugging 64-bit tooling:

  • RO64DEBUG enables debug output from the 64-bit wrappers;
  • ROBUILD_MKRELOC_DEBUG enables relocation-table debugging during linking.

Supporting wrappers

The 64-bit tool family also includes a set of lighter wrappers around existing commands. These are mainly there so that older habits and build scripts can select the 64-bit toolchain without spelling the underlying GNU cross-tool names or extra variables by hand.

For these lighter wrappers, the 32-bit command entries in the rest of the manual remain the better source for the underlying tool syntax. The 64-bit entries mainly document what extra selection or translation layer is added.