The state of portability of linux is pretty sad: each of the subsections in this article should start with 'Of course they differ across architectures'. Let's delve into the details.
System Calls
All systems calls tables are different, however the issue is not only that the mappings 'define->number' are different, it is also the case that newer architectures lack system calls present on the older ones. For example, aarch641 is missing 'legacy' (open, dup2, ...) system calls. The missing system calls can be more or less easily reimplemented in the terms of the 'new, improved and non-boring' system calls: in the *at (openat, fstatat, ...) syscall family, it involves adding an extra argument, in others (ppoll, pselect6) -- converting time from integer to struct timespec and providing a signal mask.
Some system calls additionally have a different calling convention across architectures.2
Platforms with 32-bit registers have ugly interaction with system calls with 64-bit arguments, writing a single argument into two registers3. None of these system calls is fundamental.
A numbers of system calls are fixed-up to bridge over the incompatibility between kernel interfaces and POSIX standard. For example, Linux mmap error code in some cases deviates from POSIX specification.
Type Definitions
Linux does not have a strict discipline wrt. which bitness the system call arguments or structure fields should have. For example, st_blksize in struct stat is declared int on aarch64, but long all other architectures.
Structure definitions across platforms can have fields in different order, and also different padding between fields. Converting structures to Ada can turn out to be tricky: I would not trust an automatic converter, however the manual conversion can be error-prone too. At least the field order should be fixable with Ada -- it allows precisely specifying where each field is located.
Once again, kernel structures can be different from what POSIX exposes to user (sigaction, struct stat on mips).
Dynamic Linking and vDSO
Dynamic linking is harmful and will be killed. In heathen software, dynamic linking implementation has platform-specific parts, and on mips, sh, and s390 has additional fix-ups in platform-independent code (ifdefs).
Background information on vDSO is in 'man vdso'4. I assume that brokenness of dynamic linking applies to vDSO as well, even though it does not require a full dynamic linker, and is currently used even in static binaries.
Socketcalls and SYS_ipc Syscalls
There are two categories of system calls that can share a single syscall code on some architectures: socketcalls and SYS_ipc calls. Socketcalls5 encompass all network related system calls. SYS_ipc calls6 are used for POSIX shared memory IPC. On other architectures SYS_socketcall and SYS_ipc system calls are not used, and each of system calls has it's own entry point.
_LARGEFILE64_SOURCE
In the early days of Linux, file and file system sizes were specified as 32-bit values, which caused a lot of pain when hard drives became larger than 4Gb. To allow using larger disks/creating larger files, kernel developers added new system calls with 64-bit value arguments, which did not share the entry point with the old syscalls. Libc exposed these syscalls when _LARGEFILE64_SOURCE was defined. Some software was even developed using these interfaces. In meantime, old syscalls were fixed. On musl, _LARGEFILE64_SOURCE exposes the names of new system calls as synonyms to the old ones. Exposing these new syscalls to Ada is just not necessary.
Threading
POSIX specifies a set of system calls as cancellation points (man 7 pthreads). I don't know yet if blocking system calls or task cancelling can cause problem with Ada threading.
Various Flags (errno codes, open flags, setsockopt commands, etc.)
MIPS is leader in breakage here: especially if you consider that network flags (SOCK_STREAM/SOCK_DGRAM/setsockopt) are broken, and signal codes as well. OTOH, converting them to Ada should be straightforward.
Architecture-Independent POSIX vs Linux vs libc Issues
POSIX specifies asynchronous IO interfaces (man 7 aio). Now, there are two aio interfaces: the one that all libcs implement, spawning threads to serve requests; another one is Linux kernel aio interface for asynchronous operations on O_DIRECT-opened files (supported only on some file systems, where some=XFS), which is provided by libaio. Only the latter one gives any performance benefits.
There is also some gnarl associated with set*id system call family, which changes user/effective user/group/etc. ids of process. While POSIX mandates that these system calls should affect the whole process, on Linux they affect only a single thread7. In case those syscalls are needed, a similar thread synchronization mechanism would have to be implemented in Ada.
- Used in Pizarro Rockchips, therefore of potentially interesting to Republic. I'll ignore the or1k, as no one seems to be using it in production. [↩]
- posix_fadvise on powerpc has changed order of arguments, mmap arguments on s390x are always written to stack. [↩]
- posix_fadvise, readahead, sync_file_range, truncate, fanotify [↩]
- Short summary here. [↩]
- i386, m68k, s390x. [↩]
- They don't seem to have a name, so I named them after the entry point syscall name. Used on i386, m68k, microblaze, mips (but not mips64, mipsn32), powerpc, powerpc64, s390x, sh. [↩]
- See here. [↩]