amiga-bootcamp/05_reversing/static/library_jmp_table.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

3.7 KiB
Raw Blame History

← Home · Reverse Engineering

Reconstructing Library JMP Tables

Overview

Every AmigaOS library has a JMP table at negative offsets from its base pointer. Reconstructing this table maps LVOs to function names and is essential for identifying all OS calls made by a binary under analysis.


JMP Table Layout

lib_base - N*6:  JFF xxxx xxxx   ; JMP to function N (6 bytes)
...
lib_base - 24:   JMP Reserved()
lib_base - 18:   JMP Expunge()
lib_base - 12:   JMP Close()
lib_base -  6:   JMP Open()
lib_base + 0:    struct Library  ; lib_Node, lib_Version, ...

Each entry is a 68k JMP (abs.l) — opcode 4EF9 followed by a 4-byte absolute address, totalling 6 bytes. Hence LVO = 6 × slot_index.


Finding the Library Base

From SysBase LibList

The exec.library maintains a doubly-linked list at SysBase→LibList:

struct ExecBase {
    ...
    struct List LibList;  /* offset +378 — list of open libraries */
    ...
};

/* Walk the list: */
struct Node *n = SysBase->LibList.lh_Head;
while (n->ln_Succ) {
    struct Library *lib = (struct Library *)n;
    printf("%s v%d\n", lib->lib_Node.ln_Name, lib->lib_Version);
    n = n->ln_Succ;
}

In IDA Pro

After loading, SysBase is at $4. Use Edit → Segments → Create Segment pointed at $4 with type WORD to follow the pointer to ExecBase. Then navigate to LibList at offset +0x17A and walk the linked list.


Reading the JMP Table in IDA

  1. Know the library base address (e.g., DOSBase from the OpenLibrary result)
  2. Navigate to lib_base - 6 — first user function slot
  3. IDA shows JMP sub_XXXXXX — the target is the actual function implementation
  4. Rename each sub_ with the function name from the LVO table

Automated Script: apply_lvo_names.py

import idaapi, idc

LVO_DOS = {
    -30: "Open",      # LVO -30 = Open(name, mode) d1/d2
    -36: "Close",
    -42: "Read",
    -48: "Write",
    -54: "Input",
    -60: "Output",
    -126: "WaitForChar",
    -138: "Delay",
    # ... extend from dos_lib.fd
}

DOS_BASE = idc.get_name_ea_simple("_DOSBase")
dos_ptr  = idc.get_wide_dword(DOS_BASE)

for lvo, name in LVO_DOS.items():
    jmp_entry = dos_ptr + lvo
    # read the JMP target: opcode at jmp_entry is 4EF9, target at +2
    target = idc.get_wide_dword(jmp_entry + 2)
    idc.set_name(target, f"dos_{name}", idaapi.SN_NOWARN)
    print(f"LVO {lvo:+d}: {name}{target:#010x}")

Mapping LVO → Function via .fd Files

NDK39 .fd files define the exact register assignments and bias (LVO offset):

## NDK39/fd/dos_lib.fd (excerpt)
##base _DOSBase
##bias 30
##public
Open(name,accessMode)(d1,d2)
##bias 36
Close(file)(d1)
##bias 42
Read(file,buffer,length)(d1,d2,d3)
##bias 48
Write(file,buffer,length)(d1,d2,d3)

The ##bias value is the positive LVO — the actual call offset is bias.


JSR LVO(A6) Pattern in Disassembly

; Typical OS call site in disassembly:
MOVEA.L  (_DOSBase).L, A6
JSR      (-30,A6)          ; Open(d1=name, d2=mode)
; D0 = file handle (BPTR) or 0 on error

In IDA, this appears as jsr ($fffffffe2,a6) with displacement -30 ($FFFFFFE2 in two's complement 16-bit). Applying LVO names makes this jsr (Open,a6).


Common Library Bases and LVO Tables

See ../../../04_linking_and_libraries/lvo_table.md for complete LVO offset tables for:

  • exec.library
  • dos.library
  • graphics.library
  • intuition.library

References

  • NDK39: fd/ directory — all library .fd files
  • 04_linking_and_libraries/lvo_table.md
  • ADCD 2.1: Libraries_Manual_guide/
  • IDA Pro scripting: idc.py reference