Toolchain & Packaging¶
Amix builds software two ways. On the box: the bundled AT&T SVR4 cc (the kernel's reference compiler) or the stock Commodore-patched GNU GCC 1.40.5 at /usr/public/bin/gcc ✅, with GNU make and GAS, then relink /unix ✅ (a native GCC 2.7.2.3 exists on some community boxes, but the "installable pkg from amigaunix.com" attribution is unsourced 🔴 — see the compiler table) — this is how the existing drivers (Hydra, VA2000) are built. On Linux: as of 2026-06 a public, reproducible cross-toolchain exists — isoriano1968/gcc-cross-amix, a GCC 2.7.2.3 retargeted to m68k-cbm-sysv4 — that compiles Amix userland binaries and kernel/driver objects on a modern host (you supply your own licensed Amix sysroot) ✅. Finished software is shipped as SVR4 packages built with pkgproto/pkgmk/pkgtrans and installed with pkgadd ✅; the large community bundle is AmixBP, with a zoo+cpio fallback ✅.
Two important caveats colour everything below. The Amix /bin/sh is pre-POSIX, which breaks many modern build scripts ✅. And the cross-toolchain still needs a user-supplied licensed Amix sysroot (the toolchain repo ships no Amix headers/libs/startup objects; until 2026-09-23 this project treated them as not redistributable — see AGENTS.md §5 for the outcome that lifted that), and its C++/g++ support is not finished yet 🟡 — C is fully working.
Two cross compilers, two jobs (2026-10). The amigaux project builds kernel space — kernels,
drivers, boot loaders, loadable modules — only with GCC 2.7.2.3
(gcc-cross-amix), because that code must be object-compatible with what Amix itself was built with ✅,
and builds userland — packages, tools, C++ programs — with GCC 14 (gcc14-cross-amix) ✅.
The amigaux/amix monorepo pins both by commit in toolchain/ and builds them reproducibly in a
container ✅.
This page is the toolchain companion to building and relinking the kernel and to writing a STREAMS driver (the worked native build example). For the underlying driver model see the Amix device-driver model.
Native compilers on the box¶
Amix ships with a working C development environment ✅. The reference compiler is the AT&T SVR4 cc, used for the kernel itself and for simple drivers ✅. A copy of GCC is also bundled ✅.
| Tool | Version / location | Notes | Tag |
|---|---|---|---|
AT&T SVR4 cc |
system default | Reference compiler for kernel + simple drivers | ✅ |
| Bundled GCC (stock) | GNU GCC version 1.40.5 (68k, SGS/AT&T syntax), Commodore-patched, at /usr/public/bin/gcc — what gcc --version prints on a stock 2.1 box, and the CC = gcc of /usr/sys/Makefile (measured 2026-09-15) |
Old; the only compiler with -fpcrel (needed for boot2); rejects gcc-2 flags such as -fno-builtin. (Earlier versions of this page, following community notes, called it gcc 1.4.2.) |
✅ |
| Community GCC | 2.7.2.3 (native Amix build) | The compiler the Hydra and X11 community repos say they build with, on-box; select via CC. Where to get it is unsourced 🔴: the "installable pkg on amigaunix.com" attribution has no source found, and no public source or package for a native 2.7.2.3 build is known |
🔴 |
GNU make |
3.80 | The make used by the driver makefiles | 🟡 |
| GAS (GNU assembler) | bundled | Assembler backing GCC | 🟡 |
perl |
5.005_4 | Available in some installs | 🟡 |
Prefer the newer compiler. The gcc on PATH may be the ancient stock 1.40.5 at /usr/public/bin/gcc ✅. When a newer GCC (e.g. 2.7.2.3) is installed, point the build at it explicitly rather than relying on PATH order:
# Override the compiler for a single make invocation
make CC=/usr/local/bin/gcc # path to the newer GCC you installed
# or, for the AT&T reference compiler used by the kernel:
make CC=cc
This CC= override is the same lever the driver case studies use — see the STREAMS driver build and the per-makefile detail in building the kernel.
Debugger status is unconfirmed. Whether a symbolic debugger (sdb, dbx, or adb) is present and usable on Amix has not been verified from a primary source 🔴. Do not assume one is available; plan to debug drivers via panic messages, printf-style kernel prints, and a known-good fallback /unix (see kernel build).
The pre-POSIX /bin/sh caveat¶
The Amix /bin/sh predates POSIX shell syntax ✅. In practice this breaks build scripts and configure runs that assume a modern Bourne shell. Two failures show up constantly in the driver work ✅:
- No
$(...)command substitution — use backticks`...`instead. - No
grep -q— redirect to/dev/nulland test$?instead.
# Modern (fails on Amix /bin/sh):
ver=$(uname -r)
if grep -q hya /etc/something; then ...
# Amix-compatible:
ver=`uname -r`
if grep hya /etc/something >/dev/null 2>&1; then ...
If a tool insists on a POSIX shell, run it under ksh (the default login shell on Amix is ksh, with sh/csh/tcsh also present) ✅ rather than /bin/sh. This is the same gotcha documented for the VA2000 driver's install script in the VA2000 case study.
Building drivers: native on-box, and the gcc-cross-amix cross-toolchain¶
There are now two working paths. The modern Amix drivers are built natively on the box: the Hydra STREAMS driver's Makefile invokes the on-box compiler (cc / ld -r) — make produces a relocatable object (exp), then cd /usr/sys && make force relinks the kernel ✅. And as of 2026-06 there is also a public, reproducible cross-toolchain — isoriano1968/gcc-cross-amix — that builds a Linux-hosted GCC targeting m68k-cbm-sysv4, so you can compile Amix userland binaries and kernel/driver objects on a modern host (see Cross-compiling with gcc-cross-amix below) ✅. That resolves the long-standing "no public cross-toolchain recipe" gap. Native is still the simplest path for a one-off driver; the cross-toolchain is the edit-on-a-modern-host option.
The target triple¶
Configure the cross GCC for the Amix platform triple ✅:
| Field | Value | Tag |
|---|---|---|
| Canonical triple | m68k-cbm-sysv4 |
✅ |
| What autodetect yields | m68k-unknown-sysv4 (set cbm explicitly) |
✅ |
The cbm (Commodore) vendor field matches the kernel platform string m68k-cbm-sysv4 used throughout Amix ✅. If you let config.guess/autodetect pick the vendor it will say unknown; override it.
Kernel-driver build flags¶
The Hydra Makefile compiles with these kernel flags, using the native on-box compiler — no cross-compiler override ✅:
# Native on-box build (the Makefile's own flags).
make # CFLAGS = -O -D_KERNEL -DSVR40 -DSVR4 ; links 'exp' via ld -r
-D_KERNELselects the in-kernel headers/macros (not the user-space ABI).-DSVR40/-DSVR4select the SVR4 / SVR4.0 code paths.-Ois the optimisation level the repos build at.
The native compiler the community repos target is GCC 2.7.2.3 (or the AT&T cc) ✅; a stock box has only GNU GCC 1.40.5 and cc, and the "pkg on amigaunix.com" source for 2.7.2.3 is unsourced 🔴 (see the compiler table).
ELF → boot format: elf2brel¶
The compiler emits ELF objects (m68k, SVR4) ✅. Amix's kernel boot path does not consume raw ELF directly; the kernel build converts the linked image to the Amix boot ("brel") format (e_type=0xff00, ET_LOPROC) with the elf2brel tool that lives in the kernel source tree's boot/ (a.k.a. stand/) directory ✅. In the native on-box build this conversion runs automatically as part of make force (below); for a cross build it becomes an explicit step (see the host-side port below):
native cc / GCC 2.7.2.3 → ELF kernel image
│
elf2brel (in boot/) # ELF → Amix boot ("brel") format
│
make force # relink + write the bootable kernel
# The Hydra driver's documented native build (source-only; needs a licensed Amix):
cd /usr/sys/amiga/driver/hydra && make # build the driver object 'exp'
cd /usr/sys && make force # relink the kernel (elf2brel runs in the boot step)
Note the kernel image name: modern 2.1 systems and the community repos call the relinked kernel relocunix (the 1990 Ditto paper called the equivalent image rdbunix — a historical rename; verify per version 🟡). The boot-partition write step is covered in building and relinking the kernel, and the Hydra specifics in the Hydra case study.
Host-side elf2brel (2026-07-05): elf2brel is a portable ELF filter, so it can also run as a cross step — amix-kerntools/tools/elf2brel.py is an endian-explicit Python port of amiga/boot/elf2brel.c that converts a cross-built ld -r kernel (from gcc-cross-amix) to the brel form on the host, no Amix box needed (it fuses .text/.rodata/.data/.bss into one section, folds the .rela.* tables into the compact .rel.boot table the boot loader consumes, and drops the symtab). This matters for boot floppies: framing a raw cross-linked ET_REL (e_type=0x1) Guru's with D245 4C41 at relocation — so a cross-built kernel must pass through elf2brel (→ unix.brel, e_type=0xff00) before build-bootfloppy.sh. ✅ (verified live under WinUAE)
Cross-compiling on Linux: the gcc-cross-amix toolchain ✅¶
The cross path is now public and reproducible: isoriano1968/gcc-cross-amix is a Makefile that bootstraps a Linux-hosted toolchain for m68k-cbm-sysv4 ✅. (This resolves the gap our earlier notes flagged as open 🔴.)
- Components: GNU binutils 2.8.1 + GCC 2.7.2.3 (the version the community native builds use; the stock box compiler is GCC 1.40.5), downloaded from the GNU archive and built with
-std=gnu89(the 1990s configure tests and sources predate C99). The installed cross driver ism68k-cbm-sysv4-gcc— our earlier docs called itm68k-amix-gcc, a placeholder; the real binary is the triple-prefixed one ✅. - The one thing you must supply: a licensed Amix sysroot. The repo deliberately ships no Amix headers/libraries/startup objects (the licensing boundary). You point it at your own Amix tree —
make all AMIX_ROOT=/path/to/usr-amix— and itssysroottarget copiesusr/include,usr/lib(libc.so.1,ld.so.1),usr/ccs/lib(crt1.o/crti.o/crtn.o), andusr/sysinto the toolchain ✅. - Status (2026-06): the C compiler, GNU
as, and GNUldall work, and a dynamically-linked Amix C executable links and runs on real Amix ✅. C++ /g++is not complete yet 🟡.
make all AMIX_ROOT=/path/to/usr-amix # build binutils+gcc, populate the sysroot, install the wrapper
. build/env.sh
m68k-cbm-sysv4-gcc hello.c -o hello # -> ELF 32-bit MSB executable, m68k 68020, SYSV, dynamically linked
For a kernel/driver object (compile on Linux, link natively on the Amiga), the repo's test-random target cross-compiles a real Amix driver source to a relocatable object ✅:
make test-random AMIX_ROOT=/path/to/usr-amix CPUFLAGS=-m68030
# -> build/test/random.o : ELF 32-bit MSB relocatable M68000 (resembles native AMIX driver objects)
The wrapper compiles via -S → an .lcomm assembly fix-up → as -m68020, working around quirks of the old GCC's output ✅. So you can now develop a driver (e.g. the A4091 or a new NIC) on a modern host and do only the final relink on Amix — though the native on-box build (above) remains the simplest path for a single driver. This was open gap #1 in §13 of the research brief, now closed.
68060 targets. GCC 2.7.2.3 has no -m68060; compile code for a 68060 with -m68040 ✅. At
every optimisation level and -m680x0 flag it emits the 64-bit mulu.l/muls.l form, which the
68060 does not implement, for every divide or modulo by a constant ✅. The jusii/gcc-cross-amix fork's
wrapper expands that form into 16×16 mulu.w sequences by default and installs
m68k-cbm-sysv4-scan060, which reports any 68060-unimplemented instruction in objects, archives or
executables (AMIX_SCAN060=1 runs it on every compile) ✅; neither is in the upstream repo linked
above yet. The 1.0 68060 kernels emulate the 64-bit multiply and divide forms, so on them the
expansion saves slow emulation rather than a killed process 🟡
(the 1.0 release notes, not public).
Which commit the project builds with. amigaux/amix pins jusii/gcc-cross-amix at 8949028
(toolchain/gcc-cross-amix.pin, 2026-10-29), the fork's main with the 64-bit multiply expansion and
scan060 ✅. The fork's wrapper fixes (-c without -o, -lNAME resolved by name so DT_NEEDED is
right, volatile/const kept under -traditional, -d<X> kept off ld plus a read-only sysroot
guard) were merged upstream as isoriano1968/gcc-cross-amix #7
on 2026-10-19 ✅; the 64-bit multiply expansion and scan060 are still fork-only.
The sysroot: patch level matters ✅¶
Both cross compilers need an Amix sysroot (usr/include, usr/lib, usr/ccs/lib, and for kernel work
usr/sys). Which Amix it comes from is not neutral, because patch 2a changes three headers and
make (the patch floppy):
sys/types.h and sys/socket.h each get a missing comment-close fixed, patchlevel.h is new
(PATCHLEVEL 2a), and usr/ccs/bin/make is replaced ✅. Nothing else under usr/include differs ✅.
A sysroot in common use for kernel work turned out to be mixed: its usr/sys is the installed
2.1c tree (byte-identical to an installed 2.1p2a system, including the files the patch replaces), but
its usr/include and make are the pre-patch versions ✅; how the mix arose is undocumented 🟡. A
module kit built against a cleanly installed 2.1c /usr differed from one built against that mixed
sysroot only in those three headers (measured on both kernel lines, 2026-10-29) ✅. Build against one
installed 2.1c /usr, and record which. The project's reference sysroot is the usr/ tree of the
public amigaux/amix-system repository, AMIX 2.1c at patch
level 2a as installed, listed by sha256 in amigaux/amix system/MANIFEST.sha256 ✅.
The assembler-fixup family: .swbeg, fcmp — and the tdivs divide-with-remainder bug ✅¶
The wrapper's fix_asm pass exists because GCC 2.7.2.3 emits SGS-dialect assembly that GNU
as 2.8.1 either drops or, worse, silently mis-encodes. Three members of the family are now
known, and the third was the most consequential ✅:
.swbeg &N— GNU as parses it but emits zero bytes, while gcc's switch dispatch assumes the SGS assembler's 4-byte word before each jump table — every switch case entered 4 bytes late. Rewritten to an explicit 4-byte filler ✅.fcmp— FPU compare spelling differences, rewritten ✅.tdivs.l/tdivu.l <ea>,Dr:Dq(discovered 2026-07-20) — gcc's SGS spellings for the 68020 32-bit-dividend divide-with-remainder (divsl.l/divul.l). GNU as 2.8.1 assembled them with the ext-word SIZE bit (0x0400) set, producing the 64-bit-dividend form: the remainder register was consumed as the high dividend word. Consequence: every variable-divisor%in cross-built code returned garbage at ANY optimization level, and variable/was wrong at -O0 (on-box measurements:351 % 151→ −2147408448;351 / 151→ 351, the 68030 leaving operands unchanged on quotient overflow). Constant and power-of-two divisors were never affected (gcc lowers them without tdivs). The fix rewrites the spellings todivsl.l/divul.l(SIZE=0), with atest-divmodregression target proven FAIL-before/PASS-after ✅. The DImodelibgcc.ahelpers (__udivdi3and friends) were all affected — every divide in them is compiler-generated — and were rebuilt; the fix was confirmed on the real A4000+Z3660 with a userland exerciser (2026-07-21) ✅. See kernel build for the blast-radius map and package management for the crash this solved.
fix_asm runs only when you compile through the wrapper — -S bypasses every repair ✅.
m68k-cbm-sysv4-gcc is a shell wrapper: it runs the real gcc.real -S, post-processes the emitted
SGS-syntax assembly through fix_asm, and only then assembles. That post-process happens only when
the wrapper reaches the assembly stage internally. If a caller passes -S to the wrapper, it
is detected and the wrapper execs straight to gcc.real with zero fixups applied — silently
losing all of the repairs above. The most dangerous one to lose is the fcmp FP-compare
operand-order reversal (SGS_CMP_ORDER): for an FP-register-vs-FP-register comparison this does not
fail to assemble — it assembles to a different, valid encoding with the operands swapped, so
a > b between two doubles silently evaluates as b > a, with no diagnostic at any stage ✅. The fcmp defect itself is fixed and gated
(make test-fcmp), but the general bypass property remains true of the wrapper as designed: any
build system that compiles to .s and assembles separately, resolves to gcc.real directly, or
drives as itself inherits none of the wrapper's repairs ✅. This is the same silent-mis-encode
family as the #-as-comment scrub and the tdivs bug below, and the rule that covers all of them is
the same: verify the encoding (objdump -d), never the exit status ✅.
The SGS/SVR4 assembler dialect: & is the immediate prefix — and # is a comment ✅¶
The toolchain's GNU as (binutils 2.8.1) is configured for the SVR4/SGS m68k target, not the
plain-ELF/MIT dialect that m68k-elf-as and most modern m68k references speak. Two characters carry
the whole difference ✅:
SGS/SVR4 (m68k-cbm-sysv4 as) |
plain-ELF (m68k-elf-as) |
|
|---|---|---|
| Immediate prefix | & |
# |
# means |
start of a comment | immediate prefix |
This is the dialect gcc 2.7.2.3's own output speaks (moveq &0,%d0, link.w %fp,&0) — and why the
wrapper's .swbeg &N fixup above is spelled with &. It bites when you feed the toolchain
assembly written for a plain-ELF assembler (a reference listing, another retro port, a modern
m68k codebase): every # immediate starts a comment, the rest of the operand field vanishes before
parsing, and gas rejects the line with ✅
Note the empty operand field — that is the signature. The diagnostic never names the cause: the
operands were not wrong, they were eaten as a comment before the parser saw them. A wall of
operands mismatch … ignored errors with blank operands on imported m68k assembly means exactly
this, and the fix is the one-character substitution # → & on the code field of each affected
line ✅.
The mechanism, and why the scrub reaches end of line ✅: # is in gas's line_comment_chars for
this target (gas/config/tc-m68k.c), and the comment scrubber runs before the m68k operand
parser — so the operand is not misparsed, it is gone, together with everything after it on the
line: a second operand, a trailing comment, all of it.
The carrier is inline asm in C, not just imported .s files ✅: gcc 2.7.2.3 copies an __asm__
template into its output verbatim, so Motorola-syntax immediates inside a .c file reach the
assembler unchanged and hit the scrub exactly as an imported .s file would. A project with no .s
files is not exempt.
And the failure can be silent ✅ — this is the dangerous half, and the diagnostic above is the
lucky outcome. If what survives the scrub is itself a valid instruction, gas assembles that,
with no diagnostic of any kind: nop #junk here assembles to a bare nop (4e71), exit 0. What
you wrote is not what ran. This is the same silence class as the tdivs mis-encode above — the
toolchain has a family of silent mis-encodes, and the rule that covers all of them is: verify
the encoding (objdump -d), never the exit status.
The rest of the MIT dialect largely assembles unchanged — MIT addressing syntax (%a0@(8)),
joined-size mnemonics (movel, moveal), branch size suffixes (bras, beql), and the FPU/PMMU
instructions (fsave, fmovemx, pmove, pflusha) were probed individually and pass as-is ✅. In
practice the dialect wall is the immediate/comment characters.
Bit-field operands are the documented exception ✅ (this narrows an earlier version of this
section, which listed bfffo among the pass-as-is constructs — true of the mnemonic and the
register operand form, not of the Motorola-immediate operand form):
| Written | Result |
|---|---|
bfffo %d3{#0:#32},%d2 |
destroyed — comment scrub (Missing operand + operands mismatch) |
{0:32} — bare literals |
assembles correctly |
{BOFF:WID} — bare symbols |
Bad expression / parse error |
{&0:&32} / {&BOFF:&WID} |
assembles correctly, literals and symbols |
& is the only universally-correct repair — bare numbers work for literals and fail for symbolic
offsets/widths. As of gcc-cross-amix ef7c86e the wrapper's fix_asm auto-repairs # → &
inside bit-field braces for all eight bit-field mnemonics, gated on the encoding by
make test-bitfield (offset/width read back out of the extension word). The repair is deliberately
narrow: #APP/#NO_APP and every other # on the line are untouched, so all other hand-written
immediates remain the author's responsibility — moveq #1,%d0 in an __asm__ block is still
silently eaten. When importing any third-party m68k asm or inline asm: grep the code fields for #,
rewrite to &, and verify the encoding, not the build's exit status ✅.
The wrapper's -c foo.s with no -o silently destroys the source ✅¶
A destructive trap in the cross wrapper's .s path (present in the shipped wrapper as of
2026-07 ✅; fixed in the jusii/gcc-cross-amix fork by
1bfffbd, 2026-09-15, gated by make test-objdir, and merged upstream with
isoriano1968/gcc-cross-amix #7 on 2026-10-19 ✅):
silently overwrites foo.s with a 441-byte empty relocatable object and exits 0 — the source is
gone, there is no diagnostic, and the build looks green ✅. The chain: the wrapper classifies only
*.c arguments as sources, so a bare .s falls through as an "object" and the wrapper's compile
output name stays empty; it execs the real gcc with a zero-length -o argument; gcc 2.7.2.3's .s
spec drops that zero-length argument when it builds the as command line, so as is left holding
-o foo.s and no input file — it assembles empty stdin and writes the result over your source ✅
(the same 441-byte object falls out of as -m68020 -o t.s </dev/null directly). The wrapper's own
refusing-to-overwrite guard compares the input against the (empty) output name, so it never fires.
Where you meet it: .s.o Makefile rules in the old SVR4 idiom — $(CC) $(CFLAGS) -c $< with no
-o — under the upstream wrapper or an older install such a rule eats its own source ✅. There
the defence is one line, and an explicit -o remains good practice everywhere:
With an explicit -o the wrapper's .s path is correct ✅. .c compiles are not exposed — the
wrapper's compile path always names its output ✅. Keep assembly sources under version control
regardless: a trap that exits 0 is only ever caught after the fact.
The stock shared-libc getpwnam() first-call SIGSEGV — and the static-link sidestep ✅¶
A separate porting hazard lives in the stock shared libc.so.1, not in the toolchain: the first getpwnam() call of a process crashes inside libc's passwd-record handling (a record expand routed through the _libc_malloc allocator-redirect pointers), while the symmetric group path (getgrnam) survives — the asymmetry is entirely inside libc and not debuggable off-box. Interposition was ruled out (libc calls its internal helpers via non-preemptible bsrl). The working sidestep, used in every binary of the ported pkg toolchain: link them statically and let the executable define getpwnam/getpwuid/getgrnam/getgrgid as small direct /etc/passwd//etc/group parsers — an executable's own global symbols win over shared-library exports at static link time, so every internal caller is preempted uniformly. One consequence worth knowing: this only protects code you can relink (it cannot reach the stock dynamically-linked daemons — which were never the victims of this particular defect anyway; a daemon crashing at boot is far more likely the colon-less-TZ bug).
The userland compiler: gcc14-cross-amix (GCC 14) ✅¶
asokero/gcc14-cross-amix (Antti Sokero, MIT) builds
a GCC 14.2.0 / binutils 2.43.1 cross compiler for Amix, with working C++ (templates, RTTI,
exceptions, a hosted libstdc++), C99 and later, and a compat runtime (libamixc99) for the C-library
pieces SVR4.0 predates ✅. It is for application software only — its own README rules out the kernel,
device drivers and anything that must be object-compatible with what Amix was built with ✅. Since
2026-10 it is the amigaux project's userland compiler (packages, the package tools, the box tools,
the installer's userland); the package line was switched after a rebuild-and-compare of its packages
on a 68030 and a 68060 (byte-equal behaviour, 42/42 checks) ✅ (first-party record).
Why m68k-elf, and the one ABI difference ✅¶
GCC dropped every m68k SVR4 target, so the toolchain configures the generic m68k-elf target and
adds Amix's knowledge in a specs file, one target header and the compat layer ✅. (Debian's
m68k-linux-gnu compiler cannot be used: its libgcc_eh needs a dynamic loader's
_dl_find_object and pthreads; an elf target builds the registry unwinder instead ✅.)
Measured against Amix's libc and period-compiler output, the plain m68k ELF ABI is Amix's except where a function returns its value ✅:
| Return | Amix (AT&T cc, GCC 2.7.2.3) |
plain GCC 14 m68k-elf |
|---|---|---|
| pointer | in %a0 and %d0; the caller reads %a0 |
%d0 only |
| struct or union, any size | in memory, through the caller's buffer at %a0 |
1/2/4/8-byte aggregates in %d0/%d1 |
| floating point | %fp0 (68881) |
%fp0 |
Both mismatches broke real code: a period-compiled program calling a GCC 14 library read a stale
%a0 (zlibVersion() printed garbage, then SIGSEGV), and GCC 14 code calling libc's div()/ldiv()
read %d0:%d1 while libc wrote the div_t through %a0 ✅. The fix is toolchain/amix-abi.h
(FUNCTION_VALUE → m68k_function_value, as the m68k Linux and NetBSD targets use, and
DEFAULT_PCC_STRUCT_RETURN), upstream commit 78a337a (2026-10-27); the build refuses a compiler
without it ✅. Rebuild every GCC 14 archive built before that change ✅.
-malign-int is ABI, not tuning ✅¶
Amix's cc aligns int and pointer members to 4 bytes and rounds a struct to its widest member;
m68k GCC rounds to 2 ✅. The tripwire is stdio: stdout is &__iob[1], and sizeof(FILE) is 16
under Amix's compiler but 14 under GCC without -malign-int, so a GCC call site addresses
stdout/stderr 2 and 4 bytes into the wrong FILE — printf works, fprintf(stderr, …) writes
nothing or kills the process ✅. The toolchain's specs add -malign-int to every compile ✅.
What the specs and flags do¶
- Link:
--dynamic-linker /usr/lib/libc.so.1 -Ttext-segment=0x80000000 --hash-style=sysv -z norelro --build-id=none— executables are dynamic, with libc itself as the runtime linker, and Amix's loader ignores the extraPT_GNU_STACKheader ✅. - Start files:
crt1.o crti.o crtbegin.oplusamix_init.o, which walks.init_array; the 1991crt1does not know it, and without it no C++ static constructor runs, silently ✅. - Libraries:
-lamixc99 -lc.-lcresolves tousr/ccs/lib/libc.so, which is anararchive with static members (data.oowns__iob), not the sharedlibc.so.1✅. libgcc, libstdc++ and libamixc99 are static ✅. Sockets link dynamically by bare name; an X client needs-lX11 -lsocket -lnsl✅. - CPU of the runtime — which build you have matters. Upstream
main(as of58130fe, 2026-10-29) builds libgcc and libstdc++--with-cpu=68060, which keeps them off the 64-bit integer forms the 68060 lacks but makes them use the 68040/68060-only FPU forms (fsmove,fdmove,fsadd, …): 491 such instructions in thatlibgcc.aand 123 inlibstdc++.a, so code that reaches them (__floatundidf,unordered_maprehashing) dies with SIGSYS on a 68030 with a 68881/68882 ✅ (measured on an upstream build, 2026-10-29). Upstreammainis therefore not 68881-safe. The amigaux project builds the runtime--with-cpu=m68020-60instead (themmatters: the 68020 set and the 68881 FPU, tuned for the range), which is safe on both; that build is not public yet (2026-10-29) ✅.-m68060remains available for code that only ever runs on a 68060. -mno-strict-alignis upstream's default frome8b6997(2026-10-27); the build the amigaux packages use predates it and passes the flag explicitly ✅. On a 68LC060 at 80 MHz it measured Dhrystone 1.60× the strict build and 1.43× GCC 2.7.2.3 ✅ (first-party record); on a 68040 it needs a port kernel that handles misaligned accesses across a page boundary, not yet measured on 68040 metal 🔴.- Predefines:
-D__amix__ -D__amix -Damix; implicit function declarations stay errors (only implicitintis relaxed) ✅. DefiningSVR4as well sends some autoconf code (make'sgetloadavg) down a kmem route; the project's package line leaves it out ✅.
Checking for the 68060: check-68060-safe.py ✅¶
The 68060 dropped the 64-bit mul/div forms, and libgcc's 64-bit divide compiled for a 68020 uses
them; without an integer-support handler in the kernel, any 64-bit division by a variable kills the
process ✅. tools/check-68060-safe.py decodes instruction encodings (objdump names divul/divull
backwards) and flags the 64-bit forms; it reads ELF files, archives and listings, and reports a
disassembly that lost sync at a label as untrustworthy rather than clean ✅. The project's build adds a
--68881 mode that also flags the 040/060-only FPU forms, which upstream's checker does not look for
(as of 58130fe) ✅ — so a runtime upstream's checker passes can still trap on a 68030 + 68881. cas2 traps on the 68060 at any alignment; GCC 14 does not emit it ✅.
Known defects and workarounds¶
| Defect | Effect | Workaround | Tag |
|---|---|---|---|
libc's printf family and ll |
%lld prints the high word and puts every later argument 4 bytes out of step; %llo the same, so a value under 4 GiB prints as 0 |
libamixc99's snprintf/vsnprintf are correct; fprintf is not replaced |
🔴 open |
Vendor 1991 libX11.a and siblings |
malformed sh_link on .rela sections; modern ld silently drops the relocations and the program jumps to 0 (ld 2.8.1 tolerated it) |
run tools/fix-x-libraries.sh (repaired copies) before linking X |
✅ |
Headers without C++ linkage guards; K&R static prototypes in sys/stat.h and others |
C++ cannot include them as shipped | generated wrappers and make-kandr-fixes.py |
✅ |
gettimeofday undeclared in sys/time.h, select in sys/select.h; no FP_*, ENOTSUP, fsblkcnt_t, __cxa_atexit |
build errors | compat headers; __cxa_atexit from the compat layer |
✅ |
setenv, mkstemp |
no provider in libc or libamixc99 |
shim per port | ✅ |
long double I/O (%Lf) |
mis-steps in libc's sscanf and the compat formatter |
avoid | 🟡 |
| Hard float | FP code needs a 68881/2 or an 040/060 FPU (an LC part relies on the kernel's FPE); libamixc99's vsnprintf saves %fp2, so even an integer-only snprintf executes an FPU instruction |
— | ✅ |
The porting surface: libc and library gaps modern C assumes ✅¶
Getting a compiler to emit correct code (above) is only half of porting modern software to Amix; the
other half is that stock Amix's libc and system libraries are a 1990–1992 SVR4 surface, and much
of what current C takes for granted is simply absent. These are the measured gaps that most often
block a port — all from direct sysroot inspection (file presence, md5, nm symbol resolution,
header reads) ✅. They compound with the implicit-declaration behaviour
of GCC 2.7.2.3, which turns several of them from a build error into a silent runtime bomb.
curses = termcap = termlib is one archive, exposing both APIs — link -lcurses ✅¶
In the Amix sysroot, libcurses.a, libtermcap.a and libtermlib.a are byte-identical (same
md5) and export both the terminfo API (setupterm, tigetstr, tparm, newterm, …) and
the full BSD termcap API (tgetent, tgetnum, tgetflag, tgetstr, tgoto, tputs) from the one
archive — an intentional SVR4 design (curs_termcap(3CURSES)), not an accident of the install ✅.
libcurses is a static archive only (no libcurses.so), and no *ncurses* file exists
anywhere in the sysroot ✅. Practical policy: link -lcurses and that resolves the
termcap/terminfo/curses dependency for screen, less, vim, a top-class monitor, and their
kin at once — no ncurses port is needed as a prerequisite for that class of tool. The genuine
gap is ncurses-only APIs (wide-char cchar_t, mouse support, use_default_colors): a tool that
hard-requires those needs ncurses ported first, a separate and harder question ✅.
No POSIX regcomp/regexec anywhere on the box ✅¶
Amix's libc and system libraries provide no POSIX regular-expression API at all — no
regcomp/regexec/regfree ✅. What exists instead is the older SVR4 <regexp.h> interface
(compile/step/advance) and the separate libgen regcmp/regex. This hard-blocks any tool
whose portability story assumes POSIX regex is always present (e.g. GNU file, GNU nano) unless it
bundles its own regex engine — which is, independently, the single strongest portability predictor on
this platform ✅.
strcasecmp exists only in the networking libraries, declared in no header ✅¶
strcasecmp is not in libc at all — it lives only inside the networking libraries
(libsocket.a/libnsl.a/libresolv.a, varying by measurement pass), and no header on the system
declares it ✅. A build that calls it gets an implicit K&R declaration (assumed int-returning),
which is harmless for this particular signature but is exactly the trap class the next item shows is
not always harmless. Treat it as absent and shim it ✅.
The implicit-declaration bomb: missing snprintf/vsnprintf/getpagesize, silently miscompiled¶
None of snprintf, vsnprintf or getpagesize exist in Amix's shared libc (libc.so.1), and
snprintf is not even declared in the sysroot's stdio.h ✅ — almost certainly the single most
common blocker for modern C, since snprintf is ubiquitous. The danger is not "missing symbol, build
fails": the cross compiler is GCC 2.7.2.3, whose K&R semantics treat an undeclared call as
returning int and compile it anyway with no error — so a call to an absent, non-int-returning
function (or one taking a double) silently produces wrong values at runtime rather than a
link/compile failure ✅. Observed concretely: an undeclared atof call read the wrong argument value,
because implicit-declaration rules mishandle parameter passing for a function assumed to return int.
House rule: build with
-Walland treat every implicit-declaration warning as a hard error — on this toolchain it is a runtime-corruption warning, not a style nit ✅.
The wider absent surface, corroborated by an independent build-matrix survey, also includes
setenv, mkstemp, usleep, nanosleep, getaddrinfo, random/srandom, daemon, getrusage,
fnmatch, glob, and header gaps for stdint.h/inttypes.h/stdbool.h/strings.h/wchar.h and
ssize_t ✅. (The same-family stock-libc getpwnam() first-call crash and its static-link sidestep
are above.)
sigaltstack is there; vfork is not in the shared libc ✅¶
sigaltstackexists:libc.so.1exportssigaltstack(weak) and_sigaltstack,SYS_sigaltstackis 97,<signal.h>declares it and<sys/signal.h>hasstack_tandSA_ONSTACK,SA_RESETHAND,SA_SIGINFO,SA_NODEFER✅ (presence read from the sysroot withnmand the headers); its runtime behaviour has not been exercised 🟡.vforkis not inlibc.so.1. It exists only as a static member of theusr/ccs/lib/libc.soarchive, so autoconf finds it and a crossconfigureguesses it works ✅. The project's ports pinac_cv_func_vfork=noand usefork, which is proven on the box ✅. That staticvforkis a true vfork: the child shares the parent's memory ✅, and a child that callssignal,sigsetorsigactionrewrites the parent's libc handler table, so the parent dies ofSIGBUSat its next delivery of that signal (quirk 98). In a vfork child call onlyexec*()and_exit().- Imake turns vfork on for SVR4 ✅: X11R6.3's
Imake.tmplgivesHasVFork YESto every platform not declaredSystemV, and a cf file that defines onlySystemV4gets-DHAS_VFORK(xinit,xdmshell,xmh). Say#define HasVFork NO. An audit of the amix packages (2026-11-02) found make, diffutils and pkgtools mappingvforktofork, Dropbear built withDROPBEAR_VFORK 0, Python's_posixsubprocessusing vfork only on Linux, and the staticvsyslogas the one vfork left in built binaries.
More gaps and traps from the 2026-10 ports ✅¶
putenv()a writable buffer, never a literal: the shared libc parsesTZin place, so a literal (read-only under GCC 14) crashes the next time conversion (quirk 89).- Absent:
settimeofday(onlystime),dirfd(), theS_ISLNKmacro (S_IFLNKis defined),mkstemp,asprintf,statfs(usestatvfs; itsf_basetypenames the filesystem type). - Exported by
libc.so.1but declared by no header:fchmod,getrlimit/setrlimit— declare them yourself, or the implicit declaration applies. - Static only:
getmntentandgetutxentexist only as members of theusr/ccs/lib/libc.soarchive, not inlibc.so.1. - Cross
configure: putting-include <decls.h>intoCFLAGSbreaks autoconf'schar f();function probes, so functions that exist read as absent. Keep such includes on themakeline only. - Never
chmoda boundAF_UNIXsocket path: it is a mode-000 FIFO, and afterchmodeveryconnect()failsENOTSOCK(networking). - Shell scripts: stock
seddrops an unterminated last line (quirk 88). ftruncateexists but can panic the box (stock 2.1c and any kernel without thekzerofix): shrinking a big file to an unaligned EOF in its old last block takes a kernel bus error. Truncate to the block boundary, then write the tail back;ftruncateis a weak symbol in the static libc, so a strong replacement in the program overrides it for every caller (coreutilsdd/cp, GNU tar--delete) (quirk 90). The aligned cut also avoids a second defect: after an unaligned shrink to an earlier block, a sparse regrow can read the old bytes back (quirk 91).%lldalso hides in formats built at run time ✅: coreutilsstat -c %sand thestat -fcounts printed 0 becausestat.cassembles its format withstrcat(…, PRIdMAX), which is"lld"whenintmax_tislong long, and libc's printf reads%lldas 32 bits (the printflldefect above).od -t d8/u8/x8builds formats the same way. A check that scans string literals forllcannot see these; grep the sources forPRIdMAX/PRIuMAX/PRIxMAXtoo, and use%ld/%luwhere the values fit a long.%lloreads one word too ✅: Heirloom tar built without its fix formats each size withsprintf(size, "%11.11llo", (long long)st_size), and through the stock libc every member's size came out 0 on 68030 and 68060 systems, so its archives extract as empty files. The printf reads one 32-bit word, the high half on this big-endian CPU. The amix package gate'sll-printf check caught it at build time; the port formats throughlibamixc99'ssnprintf.- A non-blocking TCP
connect()needsO_NDELAY, notO_NONBLOCK: underO_NONBLOCKit fails withEAGAINand reports nothing, even for a refused port (networking). gethostbyname()does not take a dotted quad: parse IPv4 literals withinet_addr()first (networking)./usr/bin/rshis the BSD remote shell (setuid root, 15452 bytes), not the restricted shell ✅.- The unprivileged account is
nobody, uid and gid 60001; there is nonogroup✅ — a configure that probes the build host's/etc/grouppicks the wrong group name.
Pseudo-terminals: /dev/ptmx, and where to push ldterm ✅¶
Amix ptys are SVR4 STREAMS ptys ✅. /dev/ptmx is crw-rw-rw- root root 27, 15: the
clone driver (major 27) opening the ptmx major 15; the slaves are /dev/pts/N, major 14 (the 1.1
disks, 68030 and 68060). grantpt, unlockpt and ptsname are in libc.so.1 (weak, with
strong _ aliases) but no sysroot header declares them, so GCC 14 needs your own prototypes;
/usr/lib/pt_chmod is present. An autoconf configure skips its /dev/ptmx test when
cross-compiling, so define the pty choice yourself (Dropbear: USE_DEV_PTMX).
Measured with GCC 14 probes on the 1.1 disks in emulation (68030 and 68060 kernels) ✅:
- Push
ptem,ldtermandttcompatin the process that will own the session. If a session leader with no controlling tty pushes them on the slave, the pty becomes its controlling tty, even with the slave openedO_NOCTTY. Its forked child'ssetsid()andopen(slave)then get no controlling tty (open("/dev/tty")failsENXIO: no job control, no^C), and the master sees no EOF when the child exits. If the child pushes the modules after its ownsetsid(), as SVR4'stelnetddoes, its/dev/ttyopens and the master reads 0 at exit. A parent that is not a session leader may push without harm. A server that forks a session leader per connection (sshd-style) must push in the session's child. - The master has no line discipline.
tcgetattron it fails, andioctl(master, TIOCSWINSZ)failsEINVALuntilptemis on the slave (then it returns 0 andstty -aon the slave shows the rows and columns). Set terminal modes and the window size on the slave.ldterm's defaults areintrDEL,quit^|,erase^h,kill^x, speed 300, so a session whose modes are never set takes Backspace (DEL on a modern terminal) as interrupt.
The stock math library: libm.a ✅¶
Stock Amix ships its math library only as an archive, /usr/ccs/lib/libm.a (40 members, plus a
profiled copy in libp/); there is no shared libm. GCC 14 code links it and gets correct double
returns ✅ (measured: 357 results over 19 functions on an emulated stock 2.1c 68030, 2026-10-30; the
probe's positive sqrt rows turned out to be GCC's inline fsqrt, not libm — see below).
It is software math on basic 6888x arithmetic ✅ (objdump of every member): no fsin, fcos,
fetox, flogn, fatan or ftan anywhere. It does use two instructions the 040/060 FPUs lack in
hardware: fmovecr (the constant ROM; 21 sites, unimplemented on both the 68040 and the 68060)
and fintrz (16 sites, unimplemented on the 68040, implemented on the 68060). Per function, from
link maps of one-call programs ✅:
| Function | Members linked | 040/060 traps |
|---|---|---|
sin, cos, tan |
reduce.o, sin.o |
fintrz ×3 |
atan, atan2 |
atan.o |
fintrz ×1, fmovecr ×1 |
exp; sinh, cosh, tanh (pull exp.o) |
exp.o |
fintrz ×1 |
pow |
pow.o, rem.o |
fintrz ×4, fmovecr ×1 |
fmod |
rem.o |
fintrz ×1 |
log, log10, sqrt, floor, ceil, asin, acos |
— | none |
hypot, erf, gamma, j0, j1 |
— | fmovecr |
So on a 68060 with its FPU only pow, atan, atan2, hypot, erf, gamma and the Bessel
functions reach the unimplemented-instruction trap; on a 68040 most of the library does. The
040/060 kernel's FPSP handles them: the same program's 357 results on an emulated 68040 (with the
emulator trapping the 040's unimplemented instructions) are bit-identical to the 68030's ✅. No
libm-level run on a 68060 yet 🟡. On FPU-less parts there is no usable floating point yet (see
the 040/060 status) ✅.
Accuracy: within 1 ulp of the correctly rounded result on every function tested ✅ — worst
atan2 0.745, tan 0.721, tanh 0.717, sin 0.63, cosh 0.625, pow 0.565 ulp; floor, ceil
and fmod exact. Not always correctly rounded: pow(2, 0.5) is 0x3ff6a09e667f3bcc, one ulp below
sqrt(2)'s …bcd ✅.
sqrt: libm's or the FPU's ✅. A GCC 14 program that calls sqrt() directly does not call libm:
GCC emits one 68881 fsqrt, which is correctly rounded and implemented in hardware on the 040/060
too (no FPSP trap). A call through a function pointer (as perl makes it) reaches libm's sqrt, which
is within 1 ulp but not correctly rounded: sqrt(2) = 0x3ff6a09e667f3bcc (0.56 ulp; correctly
rounded is …bcd), sqrt(10) 0.57 ulp, sqrt(0.7) 0.63 ulp — deterministic, the same bits whatever
%fp0/%fp1 and the stack held beforehand. So the two routes can differ in the last bit. sin,
log, exp and atan are likewise independent of leftover FP registers ✅.
FP state at start ✅: FPCR is 0x00000000 when a process starts (round to nearest, extended
precision, no exception traps enabled), and the stock fpsetmask(0) that perl calls at startup
rewrites only the exception-enable byte.
Errors are SVID, not IEEE ✅: domain errors set errno to EDOM (33), the default matherr()
prints one line to stderr (log: DOMAIN error, log: SING error, pow: DOMAIN error, …), and the
return values are SVID's rather than NaN or infinity — log(0) and log(-1) → -HUGE
(−3.40282e38), sqrt(-1) → 0, pow(0, -1) → 0, pow(-2, 0.5) → 0, fmod(1, 0) → 1, asin(2) → 0.
A program that defines its own matherr() (the SVID hook) changes this. Code that tests isnan()
after a bad call sees none.
Do not swap in musl's math as is 🟡: musl 1.2.5's src/math built with GCC 14 for this target
gave floor(1.0) = 0, ceil(2.0) = 1 and sin(10) = −24.5. Its round-to-integer trick assumes
double evaluation, and the 68881's extended-precision registers defeat it even with
-fexcess-precision=standard (one build measured, not investigated further).
Cross-build traps that are not Amix's fault ✅¶
- help2man pages come out empty. Patch a source file that a man page is generated from and
maketries to regenerate the page; in a cross buildhelp2mancannot run the target binary, and autoconf'smissingscript then leaves the page empty and exits 0 (GNU diffutils 2.8.1'sdiff.1andsdiff.1came out 0 bytes) ✅. Touch the tarball's pages afterconfigure, and refuse empty payload files when packaging. - GNU diffutils 2.8.1
sdiff -otreats the diff child's normal "files differ" exit (1) as a failure, so every completed interactive merge of differing files ends with "subsidiary program failed" and exit 2, although the merged file is correct ✅ (measured on the box). An upstream defect, fixed in diffutils 2.9 (check_child_status); backport it.
SVR4 /proc is 37 PIOC* ioctls, not Linux-style text files ✅¶
Unlike Linux, Amix's /proc/<pid> is a single file you open() and drive with ioctl() — there
is no /proc/<pid>/stat, no /proc/meminfo, nothing to text-parse ✅. The sysroot's sys/procfs.h
defines 37 PIOC* ioctl codes (PIOCSTATUS, PIOCPSINFO for ps-style info via prpsinfo_t,
PIOCNMAP/PIOCMAP for memory mappings, PIOCCRED, …). Consequence: a tool like htop, whose
entire data-collection layer is written against Linux /proc text files, is not portable as
written — it is a rewrite against the PIOCPSINFO/prpsinfo_t structures, not a patch; prefer a
small native tool over patching a large foreign one ✅. (A top-class monitor is tractable this way:
sys/procfs.h + nlist(), no libkvm.)
Corpus caution: the
PIOC*hits elsewhere in this grimoire are a different, unrelated device — a parallel-port driver's private ioctl namespace (PIOCSETCTL/PIOCGETCTLfor printer handshake lines), which shares thePIOCprefix by convention only. Do not confuse it with the procfs ioctl family here ✅.
SVR4 packaging¶
Amix uses the standard SVR4 pkgadd packaging family to bundle and install software ✅. The full lifecycle is: describe the payload with pkgproto, build a package with pkgmk, optionally bundle it into a single transferable datastream with pkgtrans, and install with pkgadd ✅.
The pkg tools¶
| Tool | Role | Tag |
|---|---|---|
pkgproto |
Generate a prototype file (the manifest of files/dirs/perms) from a built tree | ✅ |
pkgmk |
Build a package from the prototype + a pkginfo file |
✅ |
pkgtrans |
Translate a directory-format package into a single datastream file (for transfer) | ✅ |
pkgadd |
Install a package onto the system | ✅ |
A minimal build-and-install loop looks like:
# 1. Stage your built files under a root, then generate a prototype
pkgproto /staging=/ > prototype # see symlink gotcha below
# 2. Add the 'i pkginfo' line and any install scripts to prototype, then build
pkgmk -o -r /staging # builds into the spool dir
# 3. Bundle to a single datastream for transfer
pkgtrans -s /var/spool/pkg /tmp/MYpkg.pkg MYpkg
# 4. Install
pkgadd -d /tmp/MYpkg.pkg MYpkg
pkgproto symlink gotcha 🟡: pkgproto is reported to omit symbolic links from the generated prototype, so symlinks silently vanish from packages built straight from its output 🟡. Add the missing s-type (symlink) lines to the prototype by hand before running pkgmk. The prototype line format is s none <link>=<target>.
How the installer itself uses packaging¶
The Amix install path is package-driven ✅. The root.adf install scripts wrap the pkg machinery with amixpkg (e.g. amixpkg -i -m -d -r /mnt -y standard) and stream the distribution from tape (dd if=/dev/rmt/4hn bs=256k | cpio -imdcu) ✅. The amixpkg wrapper is widely reported to be flaky/"broken" 🟡, but the underlying pkgadd/pkgmk/pkgtrans tools are the standard SVR4 ones. The installation flow is detailed in §9 of the research brief; see also the installation walkthrough.
AmixBP and the zoo+cpio fallback¶
For getting additional software onto a running system, two community distribution forms dominate ✅:
- AmixBP — Michael Parson's bundle of 40+ re-bundled
.pkgpackages, hosted on amigaunix.com/downloads ✅. This is the practical "package repository" for Amix add-ons in the download-a-bundle sense — distinct from the machine-readable catalog+client repository format documented on the package repository page. (Do not redistribute the packages here; point users to amigaunix.com.) zoo-compressedcpio— an alternative archive form used to ship software when a full.pkgisn't available ✅. Unpack with the on-boxzooandcpio.
The patch disk uses a related (but distinct) self-extracting mechanism — a 1 KB shell header, an SVR4 ASCII cpio (070701) archive, and a bundled lha unpacking archive.lha — documented in the patch ADF anatomy. That is the model to study if you want to build a custom self-extracting add-on disk.
End-to-end: which path do I use?¶
| You are… | Use | Toolchain |
|---|---|---|
| Building a single-file char driver | On-box native | cc driver.c (or newer GCC via CC=) ✅ |
| Building a STREAMS / kernel driver | On-box native | make in the driver dir, then make force to relink ✅ |
| Editing driver or kernel-module source on a modern host | Linux cross | gcc-cross-amix → m68k-cbm-sysv4-gcc (needs an Amix sysroot) ✅; never GCC 14 for kernel space ✅ |
| Porting modern C, C99 or C++ userland | Linux cross | gcc14-cross-amix → m68k-elf-gcc/g++ with the Amix specs (above) ✅ |
| Shipping userland software you compiled | SVR4 packaging | pkgproto → pkgmk → pkgtrans → pkgadd ✅ |
| Installing community add-ons | AmixBP / zoo+cpio |
pkgadd / zoo+cpio ✅ |
Rebuilding /unix after adding a driver |
On-box or cross | see kernel build ✅ |
See also¶
- Building & Relinking the Kernel — the
make→relocunix→make bootpartflow the compilers feed into. - Writing a STREAMS Driver — the worked native build example.
- Hydra Case Study — the real STREAMS driver built natively on Amix (
make/make force). - VA2000 Case Study — a native single-file
ccbuild and the pre-POSIX/bin/shgotcha. isoriano1968/gcc-cross-amix— the Linux-hosted cross-toolchain bootstrap (make all AMIX_ROOT=…); the project builds from thejusii/gcc-cross-amixfork.asokero/gcc14-cross-amix— the GCC 14 userland cross compiler; itsdocs/TOOLCHAIN.mdrecords every measurement behind the flags above.- Loadable kernel modules — kernel-space code built with gcc-cross-amix against the ABI kit.
- The Amix Device-Driver Model — major/minor numbers and the
cdevsw/bdevswtables drivers plug into. - Versions — version matrix (GCC 2.7.2.3 built under 2.03;
rdbunix→relocunixrename).
Sources¶
- The
AF_UNIXchmodtrap (first-party record, 2026-10-30) ✅ — see the matching Sources entry on the networking page. - amix-kerntools brief
tdivs-cross-assembler-miscompile(2026-07-21): gcc-cross-amix984192afix_asm rewrite + test-divmod; on-box measurements; metal confirmation 2026-07-21. - research brief §7 (Toolchain & packaging), §6 (modern driver repos — Hydra native build, GCC 2.7.2.3 — the brief's "from amigaunix.com" attribution is 🔴, unsourced), §11 (userland shells), and §13 (open gaps #1, #3).
- Ditto, Writing Amix Device Drivers, 1990 European Amiga Developer's Conference (kernel build flow;
rdbunixhistorical name). - The stock native compiler, measured on a stock Amix 2.1 install (2026-09-15/16) ✅:
gcc --versionprintsGNU GCC version 1.40.5 (68k, SGS/AT&T syntax)(/usr/public/bin/gcc,/usr/sys/MakefileCC = gcc); it alone has-fpcreland it rejects-fno-builtin. No copy of a native GCC 2.7.2.3 package or source for the "amigaunix.com pkg" attribution has been found — hence 🔴 on that attribution. isoriano1968/hydra-amixrepo (the nativemake/make forcebuild,CFLAGS="-O -D_KERNEL -DSVR40 -DSVR4",elf2brelinboot/, native GCC 2.7.2.3 — origin unsourced 🔴, not an amigaunix.com pkg as far as any source shows): https://github.com/isoriano1968/hydra-amixisoriano1968/gcc-cross-amixrepo (the Linux-hosted cross-toolchain:Makefile+amix-gcc-wrapper.sh; binutils 2.8.1 + GCC 2.7.2.3, targetm68k-cbm-sysv4, user-supplied sysroot;make all/test-hello/test-random; C works, C++ WIP): https://github.com/isoriano1968/gcc-cross-amixasokero/va2000-amixrepo (nativecc va2000.c; pre-POSIX/bin/shgotchas — no$(...), nogrep -q): https://github.com/asokero/va2000-amixamix_21_root.adfanalysis viatools/inspect-adf.sh(install usesamixpkg/cpio/ddfrom tape).- amigaunix.com — downloads page (AmixBP, Michael Parson) and more_software / tips-tricks: https://www.amigaunix.com/doku.php/home
- SVR4 packaging tools (
pkgproto,pkgmk,pkgtrans,pkgadd) — standard AT&T System V Release 4 documentation. - The Installer-NG (not public) and amix-packagemanager work (first-party record, 2026-07-22/24) — platform behaviours root-caused under Amiberry and on a real A4000 + Z3660 (s5/UFS state reads, on-metal digest checks) ✅ (🟡 where tagged).
- The
jusii/gcc-cross-amixfork (main):2ab2b22(the wrapper's 64-bit multiply expansion andm68k-cbm-sysv4-scan060),80b6d67and the README's "68060 Integer Code" section (no-m68060; use-m68040), all 2026-10-05;1bfffbd(the-cwith no-ofix,make test-objdir) and7036952(its README scope), 2026-09-15. The-cfix reached theisoriano1968upstream with #7 (merged 2026-10-19); the 64-bit multiply expansion andscan060are not upstream yet.ef7c86e(the bit-field#→&repair) is in both. The 68060 kernels' integer emulation: the Installer-NG 1.0 release notes (not public; draft) 🟡. - First-party cross-toolchain findings (2026-07-27): SGS-dialect probes of the installed
m68k-cbm-sysv4binutils-2.8.1as(the#-as-comment failure reproduced; per-construct MIT-syntax compatibility probes) and the wrapper-c foo.ssource-destruction defect (minimally reproduced three times; cause chain pinned by tracing the wrapper; empty-stdin control viaas -m68020 -o t.s </dev/null). - The amix-packages porting campaign —
PORTING.mdand a corroborating ports-roadmap research pass (2026-08-28), all from direct Amix-sysroot inspection ✅: the byte-identicallibcurses.a/libtermcap.a/libtermlib.aarchive exporting both APIs and the "link-lcurses" policy (§3.4); no POSIXregcomp/regexec, only SVR4<regexp.h>+ libgenregcmp;strcasecmppresent only in the networking libs and declared in no header (§3.1);snprintf/vsnprintf/getpagesizeabsent fromlibc.so.1(andsnprintfundeclared instdio.h) with the GCC 2.7.2.3 implicit-declaration miscompile (the reproducedatofcase) and the wider absent-symbol list (§3.1); the SVR4/proc37-PIOC*-ioctl model vs. Linux text files, with the parallel-port-PIOC*disambiguation (§3.2); and thefix_asm-only-through-the-wrapper property —-Sbypasses every fixup, including the silentfcmpoperand-order reversal (§2.2). No proprietary SVR4 headers or source reproduced — symbol/header presence and behaviour observed live, paraphrased. asokero/gcc14-cross-amixmain(public) ✅:README.md(scope, "not for the kernel", the X-library repair, the 68060 trap, the sysroot),LICENSE(MIT, Antti Sokero),docs/TOOLCHAIN.md§1–§15 (the ELF and linker facts, §4 the return-value table, §6 why notm68k-linux-gnu, §9 the 68060, §11 the libc gaps, §15.init_array) and its sections "-malign-int is the ABI" and "Linking the 1991 libX11.a";toolchain/install-specs.sh(the specs);toolchain/amix-abi.hand commit78a337a(the return convention, 2026-10-27);e8b6997(-mno-strict-aligndefault);8a4cd12(cas2);tools/check-68060-safe.py.amigaux/amix@95bf2f3:toolchain/README.mdand the two.pinfiles (the split, the container,TOOLCHAIN.sha256),system/README.md(the reference sysroot) ✅.- The gcc14 evaluation and the package-line switch (first-party record, 2026-10-25 to 2026-10-29): probes on the host, an emulated 68030 and a 68060 with FPU; packages rebuilt and compared on a 68030 and a 68060; the return-ABI defect found and re-measured; the
-mno-strict-alignDhrystone row on a real 68LC060 ✅. - The project's system-files inventory (first-party record, 2026-10-29) ✅: the patch disk's
replace.list/modify.list/changes, compared with/usrread from a cleanly installed 2.1c system and with the kernel-work sysroot; the kit rebuilt both ways on both lines. - The amix-packages diffutils 2.8.1 port (first-party record, 2026-10-29) ✅:
recipes/diffutils/build.shandrecipes/make/build.sh(thevforkpin), the emulated 68030 rows (thesdiff -oexit, the empty help2man pages),nmand header reads of the sysroot (sigaltstack). - isoriano1968/gcc-cross-amix #7 (the wrapper fixes, opened 2026-10-13, merged 2026-10-19 as
959c17f; state read withghon 2026-10-29) ✅. - The upstream-runtime FPU check (first-party record, 2026-10-29) ✅: an upstream
58130febuild checked with the project'scheck-68060-safe.py --68881(68060-safe for integer forms; 614 FPU instructions in 040/060-only forms across libgcc and libstdc++); upstream'sREADME.md("defaults to--with-cpu=68060") andtoolchain/build-cross.shat that commit. - The bzip2 1.0.8 and coreutils 5.2.1 ports (first-party record, 2026-10-29) ✅: sysroot reads (
nmoflibc.so.1and thelibc.soarchive, the headers) and the emulated 68030 runs behind the gaps list, theTZcrash and theconfiguretrap. - The rsync 3.1.3 port (first-party record, 2026-10-30) ✅: the
ftruncateprobe rows and thetrussofgethostbyname()on an emulated stock 2.1c (details in quirk 90 and on the networking page);ls -lof/usr/bin/rshand thenobodyentries of/etc/passwdand/etc/groupread on the box; amix-packagese7ac916(recipes/rsync/). - The stock
libm.ameasurements (first-party record, 2026-10-30) ✅ — thesqrtcorrection from follow-up probes calling libm'ssqrtthrough a function pointer with%fp0/%fp1and the stack preloaded,objdumpof the first probe'smain(one inlinefsqrt, re-read for this page), and anFPCR/fpsetmaskread; ulps recomputed for this page:ar tandobjdump -dof the sysroot's/usr/ccs/lib/libm.a(21fmovecr, 16fintrz, no transcendental instructions; counts re-read for this page), link maps of one-call programs, and a GCC 14 test program (19 functions, raw IEEE bits) run on an emulated stock 2.1c 68030 and an emulated 68040 on the 040/060 kernel line with unimplemented-instruction trapping on, the outputs equal; ulps againstmpmathat 200 bits; a musl 1.2.5src/mathbuild of the same program (amix-packages). - The coreutils 5.2.1-3 and gtar 1.14-7 rebuilds (first-party record, 2026-10-30) ✅: the
stat -c %sandstat -frows on the box,stat.c's run-time format assembly,nmoftruncate.oin the static libc archive (_ftruncateT,ftruncateW); amix-packages97bbcf1(patch 09, theftruncatereplacement),cad15e4,cbd8c25. - The Heirloom tar and Dropbear ports (first-party record, 2026-11-01/02) ✅: the
%llosize fields written as 0 by an unpatched Heirloom tar build on 68030 and 68060 systems in emulation, caught by the package gate'sll-printf check (amigaux/amixuserland/packages/heirloom-tar,b07d4cb, onmaster); the pty probes (session leader and child pushes,tcgetattrandTIOCSWINSZon the master,ls -lof the nodes,nm -Dof the sysrootlibc.so.1) on the 1.1 disks booted on 68030 and 68060 kernels in emulation, behind Dropbear's patch 05 (userland/packages/dropbear,51ba153, onmaster). - The static
vforkand the X11R6.3 Imake trap (first-party record, 2026-11-02) ✅: thexinitcores on a real A4000D and the stocklibc.so.1disassembly behind quirk 98;Imake.tmpl; the package audit and the x11r63 fix (amigaux/amixuserland/packages/x11r6.3,c3abeff,443fe46, onmaster).