mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-12 16:16:28 +00:00
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.
6.8 KiB
6.8 KiB
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 = -6LVO_Close = -12LVO_Expunge = -18LVO_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):
- Find the library base — usually pointed to by a global variable or passed in A6
- Read
lib_NegSizeatbase+0x10— this gives the total JMP table byte count - Scan the JMP table — from
base - lib_NegSizetobase - 6in 6-byte steps - Decode each JMP —
4E F9 AA AA AA AA→ target atAAAAAAAA - Match to .fd file — entry at offset
(-6 × n)corresponds to functionn
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