amiga-bootcamp/04_linking_and_libraries/lvo_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

6.8 KiB
Raw Permalink Blame History

← Home · Linking & Libraries

LVO Table Layout & Reconstruction

Overview

The Library Vector Offset (LVO) table is the core mechanism of the AmigaOS ABI. Understanding and reconstructing it is essential for reversing any Amiga binary that calls system libraries.


Memory Layout of a Loaded Library

Low addresses
←──────────────────────────────────────────────────────────→
                                                High addresses

[JMP table (negative area)]          [struct Library + private data]
...  -36  -30  -24  -18  -12   -6   base+0  base+2  ...

 ↑                               ↑
 Last user function          Library base pointer
 (e.g., -576 for graphics)   (returned by OpenLibrary)

Each slot is 6 bytes:

4E F9 AA AA AA AA    JMP.L AAAAAAAA

Calculating LVOs

Given ##bias N in the .fd file, the LVO of the kth public function (1-indexed) is:

LVO_k = -(N + (k-1) × 6)

The four mandatory functions before public functions (Open, Close, Expunge, Reserved) are at:

  • LVO_Open = -6
  • LVO_Close = -12
  • LVO_Expunge = -18
  • LVO_Reserved = -24

For a library with ##bias 30, the first public function is at -30.


Key Library LVO Tables

exec.library (##bias 30)

LVO Function Registers
-30 Supervisor A5=function
-36 ExitIntr (internal)
-42 Schedule (internal)
-48 Reschedule (internal)
-54 Switch (internal)
-60 Dispatch (internal)
-66 Exception (internal)
-72 InitCode D0=startClass, D1=version
-78 InitStruct A1=mem, A2=table, D0=size
-84 MakeLibrary A0=vectors, A1=struct, A2=init, D0=dataSize, D1=segList
-90 MakeFunctions A0=target, A1=funcArray, A2=funcDispBase
-96 FindResident A1=name
-102 InitResident A1=resident, D1=segList
-108 Alert D7=alertNum
-114 Debug D0=flags
-120 Disable
-126 Enable
-132 Forbid
-138 Permit
-144 SetSR D0=newSR, D1=mask
-150 SuperState
-156 UserState D0=savedSR
-162 SetIntVector D0=intNum, A1=interrupt
-168 AddIntServer D0=intNum, A1=interrupt
-174 RemIntServer D0=intNum, A1=interrupt
-180 Cause A1=interrupt
-186 Allocate A0=memHeader, D0=size
-192 Deallocate A0=memHeader, A1=memBlock, D0=size
-198 AllocMem D0=byteSize, D1=requirements
-204 AllocAbs D0=byteSize, A1=location
-210 FreeMem A1=memBlock, D0=byteSize
-216 AvailMem D1=requirements
-222 AllocEntry A0=entry
-228 FreeEntry A0=entry
-234 Insert A0=list, A1=node, A2=listNode
-240 AddHead A0=list, A1=node
-246 AddTail A0=list, A1=node
-252 Remove A1=node
-258 RemHead A0=list
-264 RemTail A0=list
-270 Enqueue A0=list, A1=node
-276 FindName A0=list, A1=name
-282 AddTask A1=task, A2=initialPC, A3=finalPC
-288 RemTask A1=task
-294 FindTask A1=name
-300 SetTaskPri A1=task, D0=priority
-306 SetSignal D0=newSignals, D1=signalSet
-312 SetExcept D0=newSignals, D1=signalSet
-318 Wait D0=signalSet
-324 Signal A1=task, D0=signals
-330 AllocSignal D0=signalNum
-336 FreeSignal D0=signalNum
-342 AllocTrap D0=trapNum
-348 FreeTrap D0=trapNum
-354 AddPort A1=port
-360 RemPort A1=port
-366 PutMsg A0=port, A1=message
-372 GetMsg A0=port
-378 ReplyMsg A1=message
-384 WaitPort A0=port
-390 FindPort A1=name
-396 AddLibrary A1=library
-402 RemLibrary A1=library
-408 OldOpenLibrary A1=libName
-414 CloseLibrary A1=library
-420 SetFunction A1=library, A0=funcOffset, D0=newFunc
-426 SumLibrary A1=library
-432 AddDevice A1=device
-438 RemDevice A1=device
-444 OpenDevice A0=devName, D0=unit, A1=ioReq, D1=flags
-450 CloseDevice A1=ioReq
-456 DoIO A1=ioReq
-462 SendIO A1=ioReq
-468 CheckIO A1=ioReq
-474 WaitIO A1=ioReq
-480 AbortIO A1=ioReq
-486 AddResource A1=resource
-492 RemResource A1=resource
-498 OpenResource A1=resName
-552 OpenLibrary A1=libName, D0=version
-558 InitSemaphore A0=semaphore
-564 ObtainSemaphore A0=semaphore
-570 ReleaseSemaphore A0=semaphore
-576 AttemptSemaphore A0=semaphore
-582 ObtainSemaphoreList A0=sigSemList
-588 ReleaseSemaphoreList A0=sigSemList
-594 FindSemaphore A1=sigSem
-600 AddSemaphore A1=sigSem
-606 RemSemaphore A1=sigSem
-612 SumKickData
-618 AddMemList D0=size, D1=attr, D2=pri, A0=base, A1=name
-624 CopyMem A0=source, A1=dest, D0=size
-630 CopyMemQuick A0=source, A1=dest, D0=size
-636 CacheClearU
-642 CacheClearE A0=addr, D0=len, D1=caches
-648 CacheControl D0=cacheBits, D1=cacheMask
-654 CreateIORequest A0=port, D0=size
-660 DeleteIORequest A0=ioreq
-666 CreateMsgPort
-672 DeleteMsgPort A0=port
-678 ObtainSemaphoreShared A0=sigSem
-684 AllocVec D0=size, D1=attr
-690 FreeVec A1=mem
-726 NewAddTask A1=task, A2=initialPC, A3=finalPC, A4=??

Reconstructing the JMP Table During Reverse Engineering

When analysing a patched library (e.g., bsdsocket.library):

  1. Find the library base — usually pointed to by a global variable or passed in A6
  2. Read lib_NegSize at base+0x10 — this gives the total JMP table byte count
  3. Scan the JMP table — from base - lib_NegSize to base - 6 in 6-byte steps
  4. Decode each JMP4E F9 AA AA AA AA → target at AAAAAAAA
  5. Match to .fd file — entry at offset (-6 × n) corresponds to function n

IDA Pro script to dump JMP table:

import idc, idaapi

def dump_jmp_table(lib_base, num_funcs, fd_names):
    for i, name in enumerate(fd_names):
        slot = lib_base - 6 * (i + 1)
        opcode = idc.get_wide_word(slot)
        if opcode == 0x4EF9:
            target = idc.get_wide_dword(slot + 2)
            idc.set_name(slot, f"lvo_{name}", idc.SN_NOWARN)
            idc.set_name(target, name, idc.SN_NOWARN)
            print(f"LVO -{6*(i+1):4d}  {name:40s}{target:#010x}")

References

  • NDK39: fd/exec_lib.fd, fd/dos_lib.fd, fd/graphics_lib.fd
  • NDK39: include/exec/execbase.h — SysBase layout
  • ADCD 2.1 Autodocs: all library function descriptions