[← Home](../README.md) · [Linking & Libraries](README.md) # Compiler Library Call Stubs ## Overview This document explains how each major AmigaOS compiler generates library calls and what the resulting machine code looks like — essential for recognising compiler signatures during reverse engineering. --- ## The AmigaOS Calling Convention All library calls use the same register-based convention: - **A6** = library base pointer - Arguments in **A0–A3**, **D0–D7** as specified by the `.fd` file - Return value in **D0** (or D0+D1 for 64-bit returns) - **Callee** preserves: D2–D7, A2–A6 - **Caller** may destroy: D0, D1, A0, A1 The code generated by all compilers follows this — the difference is how they **set up A6** before the `JSR`. --- ## SAS/C 6.x SAS/C uses **global library base variables** (`_DOSBase`, `_SysBase`, etc.) and generates direct `JSR LVO(A6)` calls. The standard pattern: ```asm ; Calling Write(file, buffer, length): ; D1=file, D2=buffer, D3=length, A6=_DOSBase MOVE.L _DOSBase, A6 ; load library base from global MOVE.L file, D1 ; arg 1 MOVE.L buffer, D2 ; arg 2 MOVE.L length, D3 ; arg 3 JSR -48(A6) ; Write LVO = -48 ``` ### SAS/C Inline Pragmas SAS/C uses `#pragma libcall` to define inline stubs: ```c #pragma libcall DOSBase Write 30 32103 /* 30 = bias/6 = 8th function (LVO -48) 32103 = register encoding: D3,D2,D1 → args 3,2,1 */ ``` The pragma-generated stub wraps the JSR into a C-callable inline function. ### SAS/C Library Base Globals SAS/C's `clib/dos_protos.h` + startup code declares: ```c extern struct DosLibrary *DOSBase; extern struct ExecBase *SysBase; ``` These are initialized in `c.o` (the startup stub): ```asm _c_start: MOVE.L 4.W, A6 ; SysBase from exception vector MOVE.L A6, _SysBase ; store in global LEA _dosname, A1 ; "dos.library" MOVEQ #0, D0 ; version 0 JSR -552(A6) ; OpenLibrary MOVE.L D0, _DOSBase ``` --- ## GCC (m68k-amigaos / bebbo GCC) GCC uses a different stub mechanism — **inline functions** from `inline/` headers or the `libnix` library: ```c /* inline/exec.h (generated from exec_lib.fd) */ static __inline APTR AllocMem(ULONG byteSize, ULONG requirements) { register APTR _res __asm("d0"); register struct ExecBase *const _SysBase __asm("a6") = SysBase; register ULONG _byteSize __asm("d0") = byteSize; register ULONG _requirements __asm("d1") = requirements; __asm volatile ("jsr a6@(-198:W)" : "=r"(_res) : "r"(_SysBase), "r"(_byteSize), "r"(_requirements) : "a0", "a1", "fp0", "fp1", "cc", "memory"); return _res; } ``` The `jsr a6@(-198:W)` emits a 4-byte instruction: `JSR -198(A6)` using 16-bit word displacement. ### GCC Code Pattern ```asm ; GCC calling AllocMem(1024, MEMF_CHIP): MOVEA.L _SysBase, A6 ; GCC uses same global SysBase MOVEQ #$01, D1 ; MEMF_CHIP = 2... actually: MOVE.L #$00000400, D0 ; byteSize = 1024 MOVE.L #$00000002, D1 ; MEMF_CHIP JSR -$C6(A6) ; -198 decimal = AllocMem MOVE.L D0, _mybuf ; save return value ``` ### PC-Relative Addressing (GCC Default) GCC with `-fpic` or `-fbaserel` generates **PC-relative** accesses: ```asm LEA _SysBase(PC), A0 ; load SysBase address PC-relative MOVEA.L (A0), A6 ; dereference to get SysBase value JSR -$228(A6) ; OpenLibrary LVO = -552 ``` This reduces relocation entries and is the default for GCC on AmigaOS. --- ## VBCC (vbcc m68k-amigaos) VBCC uses `inline.h` style stubs similar to GCC. The calling pattern is identical at the assembly level: ```asm ; VBCC compiled library call — indistinguishable from GCC at asm level MOVEA.L (_SysBase), A6 MOVE.L D2, -(SP) ; VBCC may push/pop D2 as scratch MOVE.L arg, D0 JSR -$C6(A6) MOVE.L (SP)+, D2 ``` VBCC is slightly more aggressive about not clobbering D2-D7, matching the calling convention exactly. --- ## Recognising Compiler Signatures During RE ### SAS/C Signature ```asm ; Entry prologue: LINK A5, #-N ; frame setup (A5 = frame pointer) MOVEM.L D2-D7/A2-A4, -(SP) ; save callee-saved regs ; ... MOVEM.L (SP)+, D2-D7/A2-A4 ; restore UNLK A5 ; teardown RTS ``` SAS/C uses `A5` as frame pointer and `LINK/UNLK` for stack frames. ### GCC Signature ```asm ; Entry prologue (GCC without frame pointer): MOVEM.L D2/A2, -(SP) ; only saves what it uses ; ... MOVEM.L (SP)+, D2/A2 RTS ``` GCC typically does **not** use LINK/UNLK unless required. It uses `A5` as an additional scratch register and often generates `JSR` through `A0` for indirect calls. ### VBCC Signature VBCC generates very clean, minimal code — similar to GCC but with more consistent callee-save patterns and no GCC-specific idioms (e.g., no `__builtin_` calls). --- ## Library Call Pattern Summary | Pattern | Compiler | Key Signature | |---|---|---| | `LINK A5, #-N` + `MOVEM D2-D7/A2-A4` | SAS/C | Frame pointer A5, full reglist | | `JSR LVO(A6)` with global `_LibBase` | SAS/C / GCC | Both use global | | `jsr a6@(-LVO:W)` (GAS syntax) | GCC inline | 16-bit short displacement | | PC-relative `LEA _SysBase(PC)` | GCC -fpic | No absolute refs | | No LINK, minimal MOVEM | GCC / VBCC | No frame pointer | --- ## References - NDK39: `fd/`, `proto/`, `inline/`, `clib/` directories - SAS/C 6.x Programmer's Guide — pragma libcall chapter - GCC m68k-amigaos port documentation (bebbo/m68k-amigaos-toolchain) - VBCC documentation — register calling convention - *Amiga ROM Kernel Reference Manual: Libraries* — calling conventions appendix