amiga-bootcamp/04_linking_and_libraries/library_structure.md
Ilia Sharin 21751c0025 docs(amiga): complete AmigaOS 3.1/3.2 developer reference — 172 files across 17 sections
Comprehensive technical documentation covering:
- Hardware: OCS/ECS/AGA custom chip registers, Copper & Blitter deep dives
- Boot sequence: cold boot through startup-sequence
- Binary format: HUNK executable spec, relocation, debug info
- Linking & ABI: .fd files, LVO tables, register calling conventions
- Exec kernel: tasks, interrupts, memory, signals, semaphores
- AmigaDOS: file I/O, FFS/OFS layout, CLI/Shell scripting
- Graphics: planar bitmaps, Copper programming, HAM/EHB modes
- Intuition: screens, windows, IDCMP, BOOPSI
- Devices: trackdisk, SCSI, serial, timer, audio, keyboard
- Libraries: utility, expansion, IFFParse, locale, ARexx
- Networking: bsdsocket API, SANA-II, TCP/IP stack comparison
- Toolchain: GCC, vasm/vlink, SAS/C, NDK, debugging
- Reverse engineering: IDA/Ghidra setup, compiler fingerprints, case studies
- CPU & MMU: 68040/060 emulation libs, PMMU, cache management
- Driver development: SANA-II, Picasso96/RTG, AHI audio

All files include breadcrumb navigation. No local paths or proprietary content.
2026-04-23 12:17:35 -04:00

4.8 KiB
Raw Blame History

← Home · Linking & Libraries

AmigaOS Library Structure

The Library Node

Every AmigaOS library (and device, resource) begins with a struct Library at its base address:

/* exec/libraries.h */
struct Library {
    struct Node  lib_Node;       /* +$00: linked list node */
    UBYTE        lib_Flags;      /* +$0E: LIBF_ flags */
    UBYTE        lib_pad;        /* +$0F: (reserved) */
    UWORD        lib_NegSize;    /* +$10: bytes of function table (negative area) */
    UWORD        lib_PosSize;    /* +$12: bytes of struct Library + private data */
    UWORD        lib_Version;    /* +$14: major version */
    UWORD        lib_Revision;   /* +$16: minor revision */
    APTR         lib_IdString;   /* +$18: pointer to ID string */
    ULONG        lib_Sum;        /* +$1C: checksum of the function table */
    UWORD        lib_OpenCnt;    /* +$20: number of current openers */
};

lib_NegSize is the total byte size of the JMP table (the negative-address area preceding the struct). It equals num_functions × 6 bytes per JMP instruction.


JMP Table (Negative Offset Area)

The function table is constructed below the library base address. Each entry is a 6-byte JMP instruction:

Library base - 6:    4E F9 xx xx xx xx   JMP  AbsAddress  (Open)
Library base - 12:   4E F9 xx xx xx xx   JMP  AbsAddress  (Close)
Library base - 18:   4E F9 xx xx xx xx   JMP  AbsAddress  (Expunge)
Library base - 24:   4E F9 xx xx xx xx   JMP  AbsAddress  (Reserved)
Library base - 30:   4E F9 xx xx xx xx   JMP  AbsAddress  (first user function)
Library base - 36:   ...

Standard Functions (Fixed LVOs for All Libraries)

LVO (offset) Function
-6 Open
-12 Close
-18 Expunge
-24 Reserved (must return 0)

These four are mandatory for every library. User functions start at LVO -30.

JMP Encoding

4E F9 AAAA AAAA = JMP.L absolute_address

To call function at LVO -30:

MOVEA.L LibBase, A6
JSR     -30(A6)         ; call through JMP table

The JSR -30(A6) does not jump directly to the function — it jumps to the JMP slot, which then jumps to the real function. This indirection is essential for SetFunction() patching.


MakeLibrary() — Constructing the Table

exec.library MakeLibrary() builds a library:

struct Library *MakeLibrary(
    APTR funcArray,    /* array of function pointers (APTR) or LONG offsets */
    APTR structInit,   /* structure initialiser table (or NULL) */
    ULONG (*initFunc)(), /* init function (or NULL) */
    ULONG dataSize,    /* size of library data area */
    BPTR segList       /* segment list of the library code */
);

funcArray is a NULL-terminated list of function addresses. MakeLibrary allocates the combined negative+positive area and fills in the JMP table.


Library Initialisation

At MakeNode() / MakeLibrary() time:

  1. AllocMem(lib_NegSize + lib_PosSize, MEMF_PUBLIC | MEMF_CLEAR)
  2. Fill JMP table at negative offsets
  3. Initialise struct Library fields at positive offsets
  4. Set lib_Sum to the checksum of the JMP table

At AddLibrary():

  1. Library is added to SysBase->LibList
  2. Future OpenLibrary() calls find it by name via FindName()

OpenLibrary() Path

struct Library *base = OpenLibrary("mylib.library", MIN_VERSION);

Internally:

  1. exec searches SysBase->LibList for a node with ln_Name == "mylib.library"
  2. If found and version sufficient: calls LVO_Open (offset -6) on the library
  3. If not found: attempts to load LIBS:mylib.library from disk via LoadSeg() + InitResident()
  4. Returns library base pointer, or NULL on failure

ROM Libraries (Kickstart)

Some libraries are resident — embedded directly in the Kickstart ROM:

  • exec.library — always in ROM; base at $4 in exec exception vector
  • graphics.library, intuition.library, dos.library — loaded from ROM on boot

ROM-resident libraries are listed in the Resident module list. During boot, exec calls InitResident() for each module marked as auto-init.


Library Flags

/* lib_Flags bits: */
#define LIBF_SUMMING   (1<<0)   /* currently computing checksum */
#define LIBF_CHANGED   (1<<1)   /* function table was patched (SetFunction) */
#define LIBF_SUMUSED   (1<<2)   /* checksum is valid */
#define LIBF_DELEXP    (1<<3)   /* delayed expunge requested */

LIBF_CHANGED is set by SetFunction() to signal that the checksum is no longer valid — tools like ShowConfig use this to detect patched libraries.


References