27 KiB
HUNK Binary Format — Complete Specification
Overview
The HUNK format is the binary container format used throughout AmigaOS. It is not a single file type — it covers two very different kinds of file that happen to share the same record structure:
| File kind | Extension | First longword | Can be executed? |
|---|---|---|---|
| Executable — program, shared library, device driver | (none), .library, .device |
$000003F3 (HUNK_HEADER) |
✅ Yes — loaded directly by dos.library LoadSeg() |
| Object file — compiler/assembler output, needs linking | .o |
$000003E7 (HUNK_UNIT) |
❌ No — must be linked first to produce an executable |
| Static library archive — collection of object files | .lib |
$000003FA (HUNK_LIB) |
❌ No — linker input only |
An object file (.o) is intermediate output from a compiler. It contains relocatable code and unresolved external references. A linker (slink, vlink) combines one or more .o files with library archives into a final executable.
The format is a linear stream of hunk records, each identified by a 32-bit type word followed by type-specific data.
Magic Number — All Valid First Longword Values
Tools and the OS identify a HUNK file by reading its first 32-bit longword. There are exactly three valid opening values:
| First longword | Hex | Dec | Constant | File type | Who reads it |
|---|---|---|---|---|---|
$000003F3 |
0x3F3 |
1011 | HUNK_HEADER |
Loadable executable — program, .library, .device |
dos.library InternalLoadSeg() |
$000003E7 |
0x3E7 |
999 | HUNK_UNIT |
Relocatable object file (.o) — compiler/assembler output |
Linker (slink, vlink) |
$000003FA |
0x3FA |
1018 | HUNK_LIB |
Static library archive (.lib) — collection of .o files |
Linker only |
Any other first longword means the file is not a valid HUNK file. InternalLoadSeg will return an error.
Note
Only
HUNK_HEADERfiles can be passed toLoadSeg(). Passing a.oobject file or a.libarchive toLoadSeg()will fail — those are consumed exclusively by the linker at build time, never at runtime.
What $000003F3 means exactly
The value $000003F3 = decimal 1011 = the constant HUNK_HEADER. Nothing about this value is arbitrary — it is the hunk type code for the header record, used as the magic number because the header is always the first hunk in an executable.
What $000003E7 means exactly
The value $000003E7 = decimal 999 = HUNK_UNIT. This marks the start of one relocatable compilation unit. A .o file may contain multiple HUNK_UNIT records, one per independently-compiled module (though most compilers emit exactly one per file).
Checking the magic yourself
# Check file type from the command line:
python3 -c "
import struct, sys
data = open(sys.argv[1], 'rb').read(4)
tag = struct.unpack('>I', data)[0]
names = {0x3F3:'HUNK_HEADER (executable)', 0x3E7:'HUNK_UNIT (object file)', 0x3FA:'HUNK_LIB (library archive)'}
print(f'{sys.argv[1]}: {names.get(tag, f\"UNKNOWN ({tag:#010x})\")}')
" mybinary
Hunk Type Codes
Source header:
dos/doshunks.h(NDK 3.9). Every hunk record starts with one of these 32-bit tag values. The file is a linear stream — the loader reads tag → payload → next tag, until the file ends.
Terminology
| Term | Meaning |
|---|---|
| longword | 32-bit (4-byte) value — the native word size of the 68000 |
| BPTR | BCPL pointer — byte address right-shifted by 2 (always longword-aligned). Dereference: real_addr = bptr_value << 2 |
| ULONG | Unsigned 32-bit integer |
| UBYTE | Unsigned 8-bit byte |
| size in longs | Content length as a count of 4-byte longwords. Bytes = longs × 4 |
| Exec | Appears in loadable executables only (starts with HUNK_HEADER) |
| Obj | Appears in relocatable object files only (starts with HUNK_UNIT) |
| Both | Valid in either context |
Group 1 — Object File Framing
These two tags appear only in
.ofiles. Never in a final linked executable.
| Hex | Dec | Constant | Wire format | Description |
|---|---|---|---|---|
$3E7 |
999 | HUNK_UNIT |
[tag] [name_len_longs] [name_bytes…] |
Start of a relocatable object unit. Always the very first record in a .o file — the object-file equivalent of HUNK_HEADER. The name field names the compilation unit (e.g. "main.o"). A single .o file may contain multiple HUNK_UNIT records. |
$3E8 |
1000 | HUNK_NAME |
[tag] [name_len_longs] [name_bytes…] |
Section name label. Optional; assigns a human-readable name to the following section. The linker uses it for map files and diagnostics. |
Group 2 — Content Sections
Carry actual program data. Valid in both executables and object files. The type longword may have
HUNKF_CHIP/HUNKF_FASTORed into its upper bits — see Memory Placement Flags.
| Hex | Dec | Constant | Payload | Description |
|---|---|---|---|---|
$3E9 |
1001 | HUNK_CODE |
[tag] [size_longs] [code_bytes × size×4] |
Machine-code section. The loader allocates RAM, copies the bytes, then applies any HUNK_RELOC32 that follows. Holds 68k instructions — never data. |
$3EA |
1002 | HUNK_DATA |
[tag] [size_longs] [data_bytes × size×4] |
Initialized read/write data. Global variables with non-zero values, string literals, jump tables, etc. Any embedded pointers to other hunks require HUNK_RELOC32 fixups. |
$3EB |
1003 | HUNK_BSS |
[tag] [size_longs] (no data bytes) |
Uninitialized data (zero-fill). Only the size is stored — no bytes in the file. The loader calls AllocMem(..., MEMF_CLEAR). A 64 KB zero array costs 4 bytes on disk. |
Group 3 — Relocation Records
Tell the loader which longwords inside the current hunk need to be patched with the actual load address of another hunk. Without relocation, all cross-hunk pointers would point to wrong addresses after the OS places code at a non-zero address.
| Hex | Dec | Constant | Alias | Field width | Description |
|---|---|---|---|---|---|
$3EC |
1004 | HUNK_RELOC32 |
HUNK_ABSRELOC32 |
LONG (32-bit) | Absolute 32-bit fixup — the most common type. Wire format: [tag] { [count] [hunk_idx] [offset_0] … [offset_n] } … [0]. Each offset points to a longword in the current hunk; *(ULONG*)(base+offset) += target_hunk_base. Terminated by count=0. |
$3ED |
1005 | HUNK_RELOC16 |
HUNK_RELRELOC16 |
LONG (32-bit) | 16-bit absolute fixup. Same format as above but patches a UWORD. Rare — 68k branch displacements are PC-relative and need no reloc. |
$3EE |
1006 | HUNK_RELOC8 |
HUNK_RELRELOC8 |
LONG (32-bit) | 8-bit fixup. Patches a UBYTE. Essentially unused — no 68k instruction has an 8-bit absolute address field. |
$3F7 |
1015 | HUNK_DREL32 |
— | WORD (16-bit) | Compact 32-bit reloc. Same semantics as HUNK_RELOC32 but count, hunk index, and offsets are stored as 16-bit WORDs, halving the table size. Valid only when all hunk offsets fit in 16 bits (hunk < 64 KB). Generated by BLink. |
$3F8 |
1016 | HUNK_DREL16 |
— | WORD (16-bit) | Compact 16-bit reloc with WORD-sized fields. Very rare. |
$3F9 |
1017 | HUNK_DREL8 |
— | WORD (16-bit) | Compact 8-bit reloc with WORD-sized fields. Essentially unused. |
$3FC |
1020 | HUNK_RELOC32SHORT |
— | WORD (16-bit) | Compact absolute 32-bit reloc with WORD offsets. Semantically identical to HUNK_RELOC32 with WORD fields. Default output of vasm/vlink when all offsets fit in 16 bits. Preferred over HUNK_DREL32 in OS 3.x-era tools. |
$3FD |
1021 | HUNK_RELRELOC32 |
— | LONG (32-bit) | PC-relative 32-bit reloc. Patch: *(LONG*)(base+off) += target_base − (base+off+4). Used by GCC -fPIC and PIC shared libraries. |
$3FE |
1022 | HUNK_ABSRELOC16 |
— | LONG (32-bit) | Absolute 16-bit fixup. Patches a UWORD with the low 16 bits of the target's absolute address. Required for MOVE.W #abs_addr,Dn patterns. Rare. |
Group 4 — External Symbol Table
Object files only — never present in a linked executable.
| Hex | Dec | Constant | Description |
|---|---|---|---|
$3EF |
1007 | HUNK_EXT |
Import + export symbol table for a compilation unit. A single stream encodes both sides: exports declare symbols defined in this hunk (type EXT_DEF, EXT_ABS, EXT_RES); imports list unresolved references the linker must satisfy from other objects (type EXT_REF32, EXT_REF16, EXT_REF8, EXT_COMMON). The linker resolves all imports and emits HUNK_RELOC32 records in the output executable. Wire format: [tag] { [type_and_namelen] [name_bytes…] [value_or_refcount] [ref_offsets…] } … [0]. See hunk_ext_deep_dive.md for sub-type encoding. |
Group 5 — Debug and Metadata
Completely ignored by the OS loader. Strip with
slink NODBGorm68k-amigaos-strip --strip-debugto reduce file size.
| Hex | Dec | Constant | Payload | Description |
|---|---|---|---|---|
$3F0 |
1008 | HUNK_SYMBOL |
[tag] { [namelen_longs] [name_bytes…] [value] } … [0] |
Local symbol table. Maps label names → offsets within this hunk. Consumed by MonAm, wack, IDA Pro. Terminated by namelen=0. |
$3F1 |
1009 | HUNK_DEBUG |
[tag] [size_longs] [format_tag] [data_bytes…] |
Opaque debug block. The leading format_tag longword identifies the format: $3D415053 = SAS/C stabs; $3D474343 = GCC stabs; $3D574152 = Warp/Storm C. See hunk_debug_info.md. |
Group 6 — Structural Records
| Hex | Dec | Constant | Payload | Description |
|---|---|---|---|---|
$3F2 |
1010 | HUNK_END |
[tag] only — no payload |
Required end-of-hunk marker. Every code/data/BSS hunk (and all reloc/symbol records that follow it) must close with HUNK_END. The loader advances to the next segment slot on reading it. |
$3F3 |
1011 | HUNK_HEADER |
[tag] [0] [num_hunks] [first_hunk] [last_hunk] [size_longs × n] |
Executable magic number and segment size table. Must be the very first longword in a loadable executable. The zero longword is the resident-library list (always 0 in practice). num_hunks = total hunks; first_hunk/last_hunk = inclusive range; followed by one size-in-longs per hunk. |
$3F5 |
1013 | HUNK_OVERLAY |
[tag] [size_longs] [overlay_table_data…] |
Overlay descriptor table. Follows the resident hunks; describes groups of code swapped in from disk on demand. Allows programs larger than available RAM. Obsolete — prefer OpenLibrary(). |
$3F6 |
1014 | HUNK_BREAK |
[tag] only — no payload |
End of overlay tree sentinel. InternalLoadSeg needs this to know where the overlay descriptor ends and the per-overlay hunk data begins. |
Note
Value
$3F4(decimal 1012) is unused — the numbering skips it intentionally.
Group 7 — Static Library Archive
Linker input only. Never loaded by
LoadSeg()at runtime.
| Hex | Dec | Constant | Description |
|---|---|---|---|
$3FA |
1018 | HUNK_LIB |
Static library archive container. A sequence of embedded HUNK_UNIT object files, each preceded by its size in longwords. Produced by ar68k or the AmigaOS join command. The linker extracts only the units needed to resolve outstanding HUNK_EXT imports. |
$3FB |
1019 | HUNK_INDEX |
Symbol index for HUNK_LIB. A packed string table plus a per-unit map of exported symbol names → unit byte offsets. Lets the linker locate a function without scanning every object in the archive. Always immediately follows the HUNK_LIB it describes. |
Memory Placement Flags
The type longword for these three hunks can encode a memory placement request in its upper bits. The loader passes the corresponding MEMF_* flags to AllocMem.
Bit layout of the type longword:
31 30 29 28 ............. 0
┌───┐ ┌────┐ ┌────┐ ┌─────────────────┐
│ 0 │ │CHIP│ │FAST│ │ Hunk type code │
└───┘ └────┘ └────┘ └─────────────────┘
| Bit | Constant | Value | Meaning |
|---|---|---|---|
| 30 | HUNKF_CHIP |
1L<<30 |
Hunk must be in Chip RAM — required for anything the custom chips DMA from (bitmaps, audio, copper lists, sprites) |
| 29 | HUNKF_FAST |
1L<<29 |
Hunk prefers Fast RAM — use for pure CPU data where DMA is not needed; avoids Chip RAM bus contention |
| 30+29 both set | (extended) | 0x60000000 |
Next longword in the file contains full MEMF_* flags for AllocMem — allows any combination |
| neither | (default) | 0 |
MEMF_PUBLIC — any available memory |
Additional helper constants:
| Constant | Value | Meaning |
|---|---|---|
HUNKB_CHIP |
30 |
Bit number (use with bset/btst) |
HUNKB_FAST |
29 |
Bit number |
MEMF_* flags used in extended mode (from exec/memory.h):
| Constant | Value | Meaning |
|---|---|---|
MEMF_ANY |
0 |
No preference — any accessible memory |
MEMF_PUBLIC |
1<<0 |
Must be accessible by all tasks and hardware |
MEMF_CHIP |
1<<1 |
Chip RAM — reachable by DMA controllers |
MEMF_FAST |
1<<2 |
Fast RAM — CPU-only, no chip DMA contention |
MEMF_CLEAR |
1<<16 |
Zero-fill on allocation |
MEMF_LARGEST |
1<<17 |
Return the single largest contiguous free block |
MEMF_REVERSE |
1<<18 |
Allocate from the top of the region (high addresses first) |
MEMF_TOTAL |
1<<19 |
AvailMem: report total installed rather than current free |
Example: force a code hunk into Chip RAM:
type longword = HUNK_CODE | HUNKF_CHIP
= 0x000003E9 | 0x40000000
= 0xC00003E9
Why would code go in Chip RAM? Rare, but needed on an A500 with no Fast RAM — everything including code must fit in the 512 KB Chip RAM.
Quick Reference Table
| Hex | Dec | Constant | Alias | Context | Purpose |
|---|---|---|---|---|---|
$3E7 |
999 | HUNK_UNIT |
— | Obj | Start of relocatable object unit |
$3E8 |
1000 | HUNK_NAME |
— | Obj | Name label for the following section |
$3E9 |
1001 | HUNK_CODE |
— | Both | Machine-code section |
$3EA |
1002 | HUNK_DATA |
— | Both | Initialized read/write data |
$3EB |
1003 | HUNK_BSS |
— | Both | Uninitialized data (size only, no bytes) |
$3EC |
1004 | HUNK_RELOC32 |
HUNK_ABSRELOC32 |
Both | Absolute 32-bit address fixup list |
$3ED |
1005 | HUNK_RELOC16 |
HUNK_RELRELOC16 |
Obj | 16-bit address fixup list |
$3EE |
1006 | HUNK_RELOC8 |
HUNK_RELRELOC8 |
Obj | 8-bit fixup list |
$3EF |
1007 | HUNK_EXT |
— | Obj | Import + export symbol table |
$3F0 |
1008 | HUNK_SYMBOL |
— | Both | Local debug symbol table |
$3F1 |
1009 | HUNK_DEBUG |
— | Both | Opaque debug data (stabs / DWARF) |
$3F2 |
1010 | HUNK_END |
— | Both | End-of-hunk marker — required |
$3F3 |
1011 | HUNK_HEADER |
— | Exec | Executable magic number + size table |
| (none) | 1012 | (unused) | — | — | Gap in the numbering |
$3F5 |
1013 | HUNK_OVERLAY |
— | Exec | Overlay group descriptor |
$3F6 |
1014 | HUNK_BREAK |
— | Exec | End of overlay tree |
$3F7 |
1015 | HUNK_DREL32 |
— | Both | Compact 32-bit reloc (WORD-width fields) |
$3F8 |
1016 | HUNK_DREL16 |
— | Obj | Compact 16-bit reloc |
$3F9 |
1017 | HUNK_DREL8 |
— | Obj | Compact 8-bit reloc |
$3FA |
1018 | HUNK_LIB |
— | Obj | Static library archive |
$3FB |
1019 | HUNK_INDEX |
— | Obj | Symbol index for HUNK_LIB |
$3FC |
1020 | HUNK_RELOC32SHORT |
— | Both | Compact abs 32-bit reloc (WORD offsets) |
$3FD |
1021 | HUNK_RELRELOC32 |
— | Both | PC-relative 32-bit reloc |
$3FE |
1022 | HUNK_ABSRELOC16 |
— | Both | Absolute 16-bit address patch |
Context key: Exec = loadable executable · Obj = object file (HUNK_UNIT stream) · Both = either
/* dos/doshunks.h — NDK 3.9 */
#define HUNK_UNIT 999 /* 0x3E7 — start of relocatable object (.o) */
#define HUNK_NAME 1000 /* 0x3E8 — name of this object unit/section */
#define HUNK_CODE 1001 /* 0x3E9 — machine code section */
#define HUNK_DATA 1002 /* 0x3EA — initialized data section */
#define HUNK_BSS 1003 /* 0x3EB — uninitialized data (zero-fill) */
#define HUNK_RELOC32 1004 /* 0x3EC — 32-bit absolute relocation table */
#define HUNK_ABSRELOC32 HUNK_RELOC32 /* alias */
#define HUNK_RELOC16 1005 /* 0x3ED — 16-bit relocation table */
#define HUNK_RELRELOC16 HUNK_RELOC16 /* alias */
#define HUNK_RELOC8 1006 /* 0x3EE — 8-bit relocation table */
#define HUNK_RELRELOC8 HUNK_RELOC8 /* alias */
#define HUNK_EXT 1007 /* 0x3EF — external symbol table (obj only) */
#define HUNK_SYMBOL 1008 /* 0x3F0 — local debug symbol table */
#define HUNK_DEBUG 1009 /* 0x3F1 — arbitrary debug data (stabs etc.) */
#define HUNK_END 1010 /* 0x3F2 — end-of-hunk marker */
#define HUNK_HEADER 1011 /* 0x3F3 — executable file header (magic) */
/* 0x3F4 — unused */
#define HUNK_OVERLAY 1013 /* 0x3F5 — overlay tree descriptor */
#define HUNK_BREAK 1014 /* 0x3F6 — end of overlay tree */
#define HUNK_DREL32 1015 /* 0x3F7 — compact 32-bit reloc (WORD fields) */
#define HUNK_DREL16 1016 /* 0x3F8 — compact 16-bit reloc */
#define HUNK_DREL8 1017 /* 0x3F9 — compact 8-bit reloc */
#define HUNK_LIB 1018 /* 0x3FA — static library archive */
#define HUNK_INDEX 1019 /* 0x3FB — symbol index for HUNK_LIB */
#define HUNK_RELOC32SHORT 1020 /* 0x3FC — compact 32-bit absolute reloc */
#define HUNK_RELRELOC32 1021 /* 0x3FD — PC-relative 32-bit reloc */
#define HUNK_ABSRELOC16 1022 /* 0x3FE — absolute 16-bit reloc */
/* Memory placement flags — OR'd into HUNK_CODE/DATA/BSS type longword */
#define HUNKB_FAST 29 /* bit number for Fast RAM flag */
#define HUNKB_CHIP 30 /* bit number for Chip RAM flag */
#define HUNKF_FAST (1L<<29) /* request Fast RAM for this hunk */
#define HUNKF_CHIP (1L<<30) /* request Chip RAM for this hunk */
Terminology used in this document
- longword — a 32-bit (4-byte) value; the natural word size of the 68k
- BPTR — a BCPL pointer: the byte address right-shifted by 2 (always longword-aligned). Convert:
byte_addr = BPTR_value << 2 - ULONG — unsigned 32-bit integer (
unsigned longon the 68k) - UBYTE — unsigned 8-bit byte
- size in longs — the hunk content length expressed as a count of 4-byte longwords (multiply by 4 to get bytes)
Quick Reference Table
| Hex | Dec | Name | Alias | Context | Purpose |
|---|---|---|---|---|---|
$3E7 |
999 | HUNK_UNIT |
— | Obj | Start of relocatable object unit |
$3E8 |
1000 | HUNK_NAME |
— | Obj | Name string for this unit/section |
$3E9 |
1001 | HUNK_CODE |
— | Both | Executable machine code section |
$3EA |
1002 | HUNK_DATA |
— | Both | Initialized read/write data section |
$3EB |
1003 | HUNK_BSS |
— | Both | Uninitialized data — size only, no bytes |
$3EC |
1004 | HUNK_RELOC32 |
HUNK_ABSRELOC32 |
Both | Absolute 32-bit address patch list |
$3ED |
1005 | HUNK_RELOC16 |
HUNK_RELRELOC16 |
Obj | 16-bit address patch list |
$3EE |
1006 | HUNK_RELOC8 |
HUNK_RELRELOC8 |
Obj | 8-bit patch list |
$3EF |
1007 | HUNK_EXT |
— | Obj | Import + export symbol table |
$3F0 |
1008 | HUNK_SYMBOL |
— | Both | Local debug symbol table |
$3F1 |
1009 | HUNK_DEBUG |
— | Both | Opaque debug data (stabs, DWARF) |
$3F2 |
1010 | HUNK_END |
— | Both | End of this hunk — required |
$3F3 |
1011 | HUNK_HEADER |
— | Exec | Executable magic + size table |
$3F5 |
1013 | HUNK_OVERLAY |
— | Exec | Overlay group descriptor |
$3F6 |
1014 | HUNK_BREAK |
— | Exec | End of overlay tree |
$3F7 |
1015 | HUNK_DREL32 |
— | Both | Compact 32-bit reloc (WORD-sized fields) |
$3F8 |
1016 | HUNK_DREL16 |
— | Obj | Compact 16-bit reloc |
$3F9 |
1017 | HUNK_DREL8 |
— | Obj | Compact 8-bit reloc |
$3FA |
1018 | HUNK_LIB |
— | Obj | Static library archive (.lib) |
$3FB |
1019 | HUNK_INDEX |
— | Obj | Symbol index for HUNK_LIB |
$3FC |
1020 | HUNK_RELOC32SHORT |
— | Both | Compact abs 32-bit reloc (WORD offsets) |
$3FD |
1021 | HUNK_RELRELOC32 |
— | Both | PC-relative 32-bit reloc |
$3FE |
1022 | HUNK_ABSRELOC16 |
— | Both | Absolute 16-bit address patch |
Context key: Exec = loadable executable only · Obj = object file (HUNK_UNIT stream) only · Both = valid in either
Memory Type Flags on HUNK_CODE / HUNK_DATA / HUNK_BSS
The type longword for code, data, and BSS hunks can carry memory placement hints in its upper two bits:
Bits 31..0 of the type longword:
bit 31: unused (always 0)
bit 30: HUNKF_CHIP — loader must use AllocMem(..., MEMF_CHIP)
bit 29: HUNKF_FAST — loader prefers AllocMem(..., MEMF_FAST)
bits 28..0: the hunk type constant (e.g. 0x3E9 for HUNK_CODE)
| Bit 30 | Bit 29 | Meaning | AllocMem flags used |
|---|---|---|---|
| 0 | 0 | Any memory (default) | MEMF_PUBLIC |
| 1 | 0 | Chip RAM required | MEMF_CHIP |
| 0 | 1 | Fast RAM preferred | MEMF_FAST |
| 1 | 1 | Extended — next longword holds full MEMF_* flags |
caller reads extra longword |
MEMF_* flag constants (from exec/memory.h, used in extended mode):
/* exec/memory.h — NDK39 */
#define MEMF_PUBLIC (1L<<0) /* any accessible memory */
#define MEMF_CHIP (1L<<1) /* Chip RAM (DMA-reachable by custom chips) */
#define MEMF_FAST (1L<<2) /* Fast RAM (CPU-only; faster than Chip) */
#define MEMF_VIRTUAL (1L<<3) /* not used on classic AmigaOS */
#define MEMF_CLEAR (1L<<16) /* zero-fill the allocation */
#define MEMF_LARGEST (1L<<17) /* return the single largest free block */
#define MEMF_REVERSE (1L<<18) /* allocate from top of list (high address) */
#define MEMF_TOTAL (1L<<19) /* AvailMem: return total, not largest */
#define MEMF_ANY 0L /* no preference — equivalent to MEMF_PUBLIC */
Example: HUNK_CODE forced into Chip RAM:
type longword = HUNK_CODE | HUNKF_CHIP
= 0x000003E9 | 0x40000000
= 0xC00003E9
HUNK_HEADER — Executable Header
Appears at the very start of an executable file:
$000003F3 HUNK_HEADER magic
$00000000 Resident library list (always 0 for loadable executables)
<num_hunks> Number of hunks in the executable (longword)
<first_hunk> Index of first loadable hunk (usually 0)
<last_hunk> Index of last loadable hunk (= num_hunks - 1)
<size_0> Size of hunk 0 in longwords
<size_1> Size of hunk 1 in longwords
...
<size_n> Size of last hunk in longwords
Size longword bit encoding:
bits 31-30: Memory type (00=ANY, 10=CHIP, 01=FAST, 11=extended)
bits 29-0: Size in 32-bit longwords
Example header (2 hunks: code + data):
Offset Bytes Meaning
$00: 00 00 03 F3 HUNK_HEADER
$04: 00 00 00 00 no resident library list
$08: 00 00 00 02 2 hunks
$0C: 00 00 00 00 first hunk index = 0
$10: 00 00 00 01 last hunk index = 1
$14: 00 00 00 50 hunk 0: 0x50 longwords = 0x140 bytes (code)
$18: 00 00 00 10 hunk 1: 0x10 longwords = 0x40 bytes (data)
HUNK_CODE / HUNK_DATA
<type> HUNK_CODE ($3E9) or HUNK_DATA ($3EA)
<num_longs> Size in longwords
<data...> Raw code or data bytes (num_longs × 4 bytes)
HUNK_END ($3F2) End of this hunk
HUNK_BSS
<type> HUNK_BSS ($3EB)
<num_longs> Size in longwords (allocate this many bytes, zero-filled)
HUNK_END ($3F2) End of this hunk
No data follows — BSS is zero-initialized by the loader.
HUNK_RELOC32
32-bit absolute relocation records. These patch addresses in the code/data that reference other hunks.
<HUNK_RELOC32> $000003EC
<num_offsets> Number of offsets to patch for the next target hunk
<target_hunk> Index of the hunk whose base address is added
<offset_0> Byte offset within current hunk to patch (longword)
<offset_1>
...
<num_offsets=0> Terminator — end of relocation table
HUNK_END ($3F2)
Example: Code hunk references data hunk. Two addresses need patching:
$000003EC HUNK_RELOC32
$00000002 2 offsets to patch
$00000001 target = hunk 1 (data hunk)
$00000010 patch at offset $10 in code hunk
$00000024 patch at offset $24 in code hunk
$00000000 end of reloc list
$000003F2 HUNK_END
At load time: *(ULONG *)(code_base + 0x10) += data_base
HUNK_SYMBOL
Optional local symbol table for debugging:
<HUNK_SYMBOL> $000003F0
<name_len> String length in longwords (1–N)
<name...> Symbol name (padded to longword boundary)
<value> Symbol value (offset within hunk)
...
<0> Zero name_len terminates
HUNK_END ($3F2)
Complete Executable Structure (Annotated)
[File start]
HUNK_HEADER
num_hunks = 3 ; code, data, BSS
sizes: [0x200, 0x80, 0x100]
--- Hunk 0: Code ---
HUNK_CODE
0x200 bytes of machine code
HUNK_RELOC32
Patches in code referencing hunk 1 (data)
Patches in code referencing hunk 2 (BSS)
HUNK_SYMBOL (optional)
_main at offset 0
_foo at offset 0x40
HUNK_END
--- Hunk 1: Data ---
HUNK_DATA
0x80 bytes of initialized data
HUNK_RELOC32
Patches in data (e.g., pointer tables) referencing code hunk
HUNK_END
--- Hunk 2: BSS ---
HUNK_BSS
0x100 longwords = 1024 bytes (zero-filled at load time)
HUNK_END
[File end]
File Format Diagram
block-beta
columns 1
header["HUNK_HEADER\n(sizes + memory types)"]
code["HUNK_CODE\n(machine code bytes)"]
reloc0["HUNK_RELOC32\n(patch list for code)"]
sym["HUNK_SYMBOL\n(optional debug names)"]
end0["HUNK_END"]
data["HUNK_DATA\n(initialized data)"]
reloc1["HUNK_RELOC32\n(patch list for data)"]
end1["HUNK_END"]
bss["HUNK_BSS\n(zero-fill size)"]
end2["HUNK_END"]
References
- Amiga ROM Kernel Reference Manual: Libraries — AmigaDOS executable format chapter
- ADCD 2.1:
Libraries_Manual_guide/— LoadSeg, InternalLoadSeg - NDK39:
dos/doshunks.h— hunk type constants - http://amigadev.elowar.com/read/ADCD_2.1/Libraries_Manual_guide/node01E0.html
- Community reference: http://sun.hasenbraten.de/vlink/release/vlink.pdf (HUNK format appendix)