mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
exec_os: enrich all stubs to bootcamp-quality reference articles
Complete rewrite of 14 exec_os articles from stubs to comprehensive deep-dive technical references with architecture diagrams, pitfalls, and best practices. New: multitasking.md (scheduler, IPC, memory safety, real-world scenarios) Enriched: exec_base, tasks_processes, library_system, library_vectors, interrupts, exceptions_traps, memory_management, message_ports, signals, semaphores, io_requests, lists_nodes, resident_modules Updated indexes: 06_exec_os/README.md, root README.md
This commit is contained in:
parent
4d136b0672
commit
59929047d4
16 changed files with 4463 additions and 678 deletions
|
|
@ -4,25 +4,46 @@
|
|||
|
||||
## Overview
|
||||
|
||||
Every AmigaOS library exposes its functions via a **JMP table** at negative offsets from the library base. This document covers the structure of the table, how LVOs are assigned, and how to create or patch one programmatically.
|
||||
Every AmigaOS library exposes its functions via a **JMP table** at negative offsets from the library base. This document covers the structure of the table, how LVOs (Library Vector Offsets) are assigned, and how to create or patch one programmatically. Understanding the JMP table is essential for both library development and reverse engineering.
|
||||
|
||||
---
|
||||
|
||||
## JMP Table Structure
|
||||
|
||||
```
|
||||
Address Content Description
|
||||
lib_base - N×6: 4EF9 XXXXXXXX JMP <absolute address> ← function N
|
||||
Address Content Description
|
||||
─────────────────────────────────────────────────────────
|
||||
lib_base - N×6: 4EF9 XXXXXXXX JMP <absolute> ← function N
|
||||
...
|
||||
lib_base - 24: 4EF9 XXXXXXXX JMP Reserved
|
||||
lib_base - 18: 4EF9 XXXXXXXX JMP Expunge
|
||||
lib_base - 12: 4EF9 XXXXXXXX JMP Close
|
||||
lib_base - 6: 4EF9 XXXXXXXX JMP Open
|
||||
lib_base + 0: struct Library ← pointer returned by OpenLibrary
|
||||
lib_base - 30: 4EF9 XXXXXXXX JMP FirstUserFunc
|
||||
lib_base - 24: 4EF9 XXXXXXXX JMP Reserved
|
||||
lib_base - 18: 4EF9 XXXXXXXX JMP Expunge
|
||||
lib_base - 12: 4EF9 XXXXXXXX JMP Close
|
||||
lib_base - 6: 4EF9 XXXXXXXX JMP Open
|
||||
─────────────────────────────────────────────────────────
|
||||
lib_base + 0: struct Library ← OpenLibrary returns this
|
||||
lib_base + PosSize: (end)
|
||||
```
|
||||
|
||||
Each slot is exactly **6 bytes**: opcode `$4EF9` (JMP abs.l) + 4-byte target address.
|
||||
|
||||
### Calling Convention
|
||||
|
||||
```c
|
||||
/* C — compiler generates: */
|
||||
result = LibraryFunction(args);
|
||||
/* Internally: */
|
||||
/* MOVEA.L LibBase,A6 ; load library base into A6 */
|
||||
/* JSR LVO(A6) ; jump to base+LVO → hits JMP instruction */
|
||||
/* ; JMP redirects to actual function */
|
||||
```
|
||||
|
||||
```asm
|
||||
; Assembly — explicit:
|
||||
MOVEA.L _DOSBase,A6
|
||||
JSR -30(A6) ; LVO -30 = first user function
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LVO Formula
|
||||
|
|
@ -31,89 +52,194 @@ Each slot is exactly **6 bytes**: opcode `$4EF9` (JMP abs.l) + 4-byte target add
|
|||
LVO = −6 × slot_index
|
||||
|
||||
where slot_index counts from 1 (Open) upward:
|
||||
Open = slot 1 → LVO = −6
|
||||
Close = slot 2 → LVO = −12
|
||||
Expunge = slot 3 → LVO = −18
|
||||
Reserved = slot 4 → LVO = −24
|
||||
First user fn = slot 5 → LVO = −30
|
||||
Second user fn = slot 6 → LVO = −36
|
||||
Open = slot 1 → LVO = −6
|
||||
Close = slot 2 → LVO = −12
|
||||
Expunge = slot 3 → LVO = −18
|
||||
Reserved = slot 4 → LVO = −24
|
||||
First user = slot 5 → LVO = −30
|
||||
Second user = slot 6 → LVO = −36
|
||||
...
|
||||
```
|
||||
|
||||
The `.fd` file `##bias` value is the positive LVO: `bias 30` → LVO `−30`.
|
||||
### From .fd Files
|
||||
|
||||
```
|
||||
##base _DOSBase
|
||||
##bias 30
|
||||
##public
|
||||
Open(name,accessMode)(D1/D2)
|
||||
Close(file)(D1)
|
||||
Read(file,buffer,length)(D1/D2/D3)
|
||||
```
|
||||
|
||||
`##bias 30` means the first user function has LVO `−30`. Each subsequent function adds `−6`.
|
||||
|
||||
| Function | .fd Position | LVO |
|
||||
|---|---|---|
|
||||
| `Open` | 1st after bias | −30 |
|
||||
| `Close` | 2nd | −36 |
|
||||
| `Read` | 3rd | −42 |
|
||||
| `Write` | 4th | −48 |
|
||||
|
||||
---
|
||||
|
||||
## Standard Vectors (Slots 1–4)
|
||||
|
||||
Every library must implement these four vectors:
|
||||
|
||||
| Slot | LVO | Function | Purpose |
|
||||
|---|---|---|---|
|
||||
| 1 | −6 | `Open` | Called by `OpenLibrary()` — increment open count, init per-opener state |
|
||||
| 2 | −12 | `Close` | Called by `CloseLibrary()` — decrement open count, cleanup |
|
||||
| 3 | −18 | `Expunge` | Called to unload — free resources if `OpenCnt == 0` |
|
||||
| 4 | −24 | `Reserved` | Must exist — returns NULL. Reserved for future use |
|
||||
|
||||
---
|
||||
|
||||
## MakeFunctions — Building a JMP Table
|
||||
|
||||
`exec.library MakeFunctions()` fills in the JMP table from a function pointer array:
|
||||
```c
|
||||
ULONG MakeFunctions(APTR target, APTR funcArray, APTR funcDispBase);
|
||||
/* LVO -420 */
|
||||
```
|
||||
|
||||
Writes JMP instructions into the negative offset area of `target`:
|
||||
|
||||
```c
|
||||
ULONG MakeFunctions(APTR targetLib, APTR funcArray, APTR funcDispBase);
|
||||
/* funcArray: NULL-terminated table of function pointers */
|
||||
APTR myFuncs[] = {
|
||||
LibOpen,
|
||||
LibClose,
|
||||
LibExpunge,
|
||||
LibReserved,
|
||||
MyFunc1,
|
||||
MyFunc2,
|
||||
(APTR)-1 /* terminator */
|
||||
};
|
||||
|
||||
MakeFunctions(libBase, myFuncs, NULL);
|
||||
/* Writes: JMP LibOpen at base-6, JMP LibClose at base-12, etc. */
|
||||
```
|
||||
|
||||
Typical usage in library `InitLib`:
|
||||
### Assembly Example
|
||||
|
||||
```asm
|
||||
; funcArray: table of function pointers, terminated by -1
|
||||
_LibFuncTable:
|
||||
dc.l _LibOpen
|
||||
dc.l _LibClose
|
||||
dc.l _LibExpunge
|
||||
dc.l _LibNull ; Reserved — returns NULL
|
||||
dc.l _MyFunc1
|
||||
dc.l _MyFunc2
|
||||
dc.l -1 ; terminator
|
||||
dc.l _LibOpen ; slot 1 → LVO -6
|
||||
dc.l _LibClose ; slot 2 → LVO -12
|
||||
dc.l _LibExpunge ; slot 3 → LVO -18
|
||||
dc.l _LibNull ; slot 4 → LVO -24 (Reserved)
|
||||
dc.l _MyFunc1 ; slot 5 → LVO -30
|
||||
dc.l _MyFunc2 ; slot 6 → LVO -36
|
||||
dc.l -1 ; terminator
|
||||
|
||||
LibInit:
|
||||
LEA _LibFuncTable(PC), A0
|
||||
MOVEA.L A6, A1 ; library base (passed in A6 by exec)
|
||||
MOVEQ #0, D0 ; funcDispBase = 0 (absolute addresses)
|
||||
MOVEA.L 4.W, A6
|
||||
JSR (-420,A6) ; MakeFunctions(A1, A0, D0)
|
||||
LEA _LibFuncTable(PC),A0
|
||||
MOVEA.L A6,A1 ; library base
|
||||
MOVEQ #0,D0 ; funcDispBase = 0 (absolute addresses)
|
||||
MOVEA.L 4.W,A6
|
||||
JSR -420(A6) ; MakeFunctions
|
||||
```
|
||||
|
||||
`MakeFunctions` writes `JMP <ptr>` for each entry, filling the table downward from `lib_base − 6`.
|
||||
|
||||
---
|
||||
|
||||
## SetFunction — Patching a Single Slot
|
||||
|
||||
```c
|
||||
APTR SetFunction(struct Library *library, LONG funcOffset, APTR newFunction);
|
||||
/* LVO -420 */
|
||||
/* Returns: old function pointer */
|
||||
```
|
||||
|
||||
- `funcOffset` is the negative LVO (e.g., `−30` for the first user function)
|
||||
- Returns the old function pointer
|
||||
This is the primary mechanism for **system patching** — replacing a library function with your own:
|
||||
|
||||
```c
|
||||
/* Hook dos.library Write() */
|
||||
old_write = SetFunction((struct Library *)DOSBase, -48, my_write_hook);
|
||||
typedef LONG (*WriteFunc)(BPTR file, APTR buf, LONG len);
|
||||
|
||||
WriteFunc oldWrite;
|
||||
oldWrite = (WriteFunc)SetFunction(
|
||||
(struct Library *)DOSBase,
|
||||
-48, /* LVO for Write */
|
||||
(APTR)MyWriteHook
|
||||
);
|
||||
|
||||
/* Your hook: */
|
||||
LONG __saveds MyWriteHook(
|
||||
BPTR file __asm("d1"),
|
||||
APTR buf __asm("d2"),
|
||||
LONG len __asm("d3"))
|
||||
{
|
||||
/* Log the write, then call original */
|
||||
LogWrite(file, len);
|
||||
return oldWrite(file, buf, len);
|
||||
}
|
||||
|
||||
/* Restore on cleanup: */
|
||||
SetFunction((struct Library *)DOSBase, -48, (APTR)oldWrite);
|
||||
```
|
||||
|
||||
See [setfunction_patching.md](../05_reversing/dynamic/setfunction_patching.md) for trampoline patterns.
|
||||
### SetFunction Gotchas
|
||||
|
||||
| Issue | Details |
|
||||
|---|---|
|
||||
| **Not atomic** | Between SetFunction calls, another task may see inconsistent state |
|
||||
| **Checksum invalidation** | SetFunction sets `LIBF_CHANGED` — must call `SumLibrary()` |
|
||||
| **Multiple patchers** | If two programs patch the same LVO, unpatching in wrong order breaks the chain |
|
||||
| **ROM functions** | SetFunction works even on ROM libraries — the JMP table is in RAM |
|
||||
| **Caching on 040/060** | Must flush instruction cache after patching |
|
||||
|
||||
---
|
||||
|
||||
## Checksum Maintenance
|
||||
|
||||
After `MakeFunctions` or `SetFunction`, exec updates `lib_Sum` via `SumLibrary`:
|
||||
|
||||
```c
|
||||
SumLibrary((struct Library *)myLib);
|
||||
/* After modifying the JMP table: */
|
||||
SumLibrary((struct Library *)myLib); /* LVO -426 */
|
||||
```
|
||||
|
||||
If `LIBF_SUMUSED` is set, exec verifies the checksum at `CloseLibrary` time. Patching the JMP table without calling `SumLibrary` will trigger a checksum failure (alert box or guru).
|
||||
If `LIBF_SUMUSED` is set, exec verifies the checksum periodically. Patching without updating the checksum triggers a "Library checksum failure" alert.
|
||||
|
||||
---
|
||||
|
||||
## Viewing Vectors in IDA Pro
|
||||
## MakeLibrary — One-Shot Library Creation
|
||||
|
||||
1. Navigate to `lib_base − 6` (first standard vector)
|
||||
2. Each 6-byte group: opcode `4EF9` + 4-byte address
|
||||
```c
|
||||
struct Library *MakeLibrary(
|
||||
APTR funcArray, /* function pointer table */
|
||||
APTR structInit, /* InitStruct data table */
|
||||
APTR initFunc, /* init function (receives lib base in D0) */
|
||||
ULONG dataSize, /* size of library base struct */
|
||||
BPTR segList /* segment list (for expunge) */
|
||||
); /* LVO -84 */
|
||||
```
|
||||
|
||||
This combines allocation, JMP table construction, data initialisation, and init-function calling into one operation. Used by `RTF_AUTOINIT` modules and direct library creation.
|
||||
|
||||
---
|
||||
|
||||
## Reverse Engineering: Viewing Vectors in IDA Pro
|
||||
|
||||
1. Navigate to `lib_base − 6` (first standard vector = `Open`)
|
||||
2. Each 6-byte group: opcode `$4EF9` + 4-byte absolute address
|
||||
3. Press `C` to disassemble if not auto-detected
|
||||
4. The 4-byte value is the actual function address — press `G` (Go to) to navigate
|
||||
5. Name each function with the `.fd` file as reference
|
||||
4. Follow the 4-byte address to the actual function body
|
||||
5. Name each function using the `.fd` file as reference
|
||||
6. The `.fd` bias tells you which LVO maps to which function name
|
||||
|
||||
### Reconstructing the Full Vector Table
|
||||
|
||||
```python
|
||||
# Python script for reading JMP table from binary
|
||||
import struct
|
||||
|
||||
def dump_vectors(rom_data, lib_base, num_vectors):
|
||||
for i in range(1, num_vectors + 1):
|
||||
offset = lib_base - (i * 6)
|
||||
opcode, target = struct.unpack('>HI', rom_data[offset:offset+6])
|
||||
if opcode == 0x4EF9: # JMP abs.l
|
||||
print(f" LVO -{i*6:4d}: JMP ${target:08X}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -121,5 +247,7 @@ If `LIBF_SUMUSED` is set, exec verifies the checksum at `CloseLibrary` time. Pat
|
|||
|
||||
- NDK39: `exec/execbase.h`, `exec/libraries.h`
|
||||
- ADCD 2.1: `MakeFunctions`, `MakeLibrary`, `SetFunction`, `SumLibrary`
|
||||
- [library_jmp_table.md](../05_reversing/static/library_jmp_table.md) — reconstruction workflow
|
||||
- [lvo_table.md](../04_linking_and_libraries/lvo_table.md) — complete LVO reference tables
|
||||
- See also: [Library System](library_system.md) — open/close/expunge lifecycle
|
||||
- See also: [LVO Table](../04_linking_and_libraries/lvo_table.md) — complete LVO reference tables
|
||||
- See also: [SetFunction Patching](../05_reversing/dynamic/setfunction_patching.md) — trampoline patterns
|
||||
- *Amiga ROM Kernel Reference Manual: Exec* — library vectors chapter
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue