[← Home](../README.md) · [Toolchain](README.md) # FD/SFD Files — Function Definition Format and LVO Generation ## Overview **FD files** (Function Definition files) are the machine-readable specification of a library's public API. They define every function's name, LVO offset, and register argument mapping. FD files are the **source of truth** for generating pragma files, inline headers, and IDA LVO scripts. --- ## File Location ``` NDK:fd/exec_lib.fd NDK:fd/dos_lib.fd NDK:fd/intuition_lib.fd NDK:fd/graphics_lib.fd NDK:sfd/exec_lib.sfd (SFD = extended format with C prototypes) ``` --- ## FD File Syntax ``` * "exec.library" ##base _SysBase ##bias 30 ##public Supervisor(userFunction)(A5) ExitIntr()() Schedule()() Reschedule()() Switch()() Dispatch()() Exception()() InitCode(startClass,version)(D0,D1) InitStruct(initTable,memory,size)(A1,A2,D0) MakeLibrary(funcInit,structInit,libInit,dataSize,segList)(A0,A1,A2,D0,D1) MakeFunctions(target,functionArray,funcDispBase)(A0,A1,A2) FindResident(name)(A1) InitResident(resident,segList)(A1,D1) Alert(alertNum)(D7) Debug(flags)(D0) ##bias 120 Forbid()() Permit()() ##bias 132 Disable()() Enable()() ... ``` ### Syntax Rules | Element | Meaning | |---|---| | `* "name"` | Comment; library identity | | `##base _Symbol` | Global base pointer symbol name | | `##bias N` | Set current LVO offset to −N | | `##public` | Following functions are public | | `##private` | Following functions are private (reserved) | | `FuncName(args)(regs)` | Function: name, C parameter names, register assignments | | `##end` | End of file | **LVO auto-increment:** After each function, the bias increases by 6 (one JMP instruction slot = 6 bytes). --- ## SFD File Format (Extended) SFD adds full C prototypes: ``` ==id $Id: exec_lib.sfd,v 1.0 2003/01/01 00:00:00 Exp $ ==base _SysBase ==basetype struct ExecBase * ==libname exec.library ==bias 30 ==public APTR Supervisor(ULONG (*userFunction)()) (A5) ==end ``` --- ## Parsing FD Files — Python Script ```python #!/usr/bin/env python3 """parse_fd.py — Parse AmigaOS FD files into LVO tables. Usage: python3 parse_fd.py exec_lib.fd [--json] [--ida] """ import re, sys, json, argparse def parse_fd(path): """Parse an FD file and return list of (offset, name, args, regs).""" funcs = [] bias = 30 public = True with open(path) as f: for line in f: line = line.strip() if not line or line.startswith('*'): continue if line.startswith('##base'): continue if line.startswith('##bias'): bias = int(line.split()[1]) continue if line == '##public': public = True continue if line == '##private': public = False continue if line == '##end': break # Parse: FuncName(args)(regs) m = re.match(r'(\w+)\(([^)]*)\)\(([^)]*)\)', line) if m and public: name = m.group(1) args = [a.strip() for a in m.group(2).split(',') if a.strip()] regs = [r.strip() for r in m.group(3).split(',') if r.strip()] funcs.append({ 'name': name, 'lvo': -bias, 'bias': bias, 'args': args, 'regs': regs }) bias += 6 # always advance, even for private return funcs def output_ida_script(funcs, base_name): """Generate IDA Python LVO dict.""" print(f"# Auto-generated from FD file") print(f"LVO_{base_name} = {{") for f in funcs: print(f" {f['lvo']:+5d}: \"{f['name']}\",") print("}") def output_json(funcs): print(json.dumps(funcs, indent=2)) def output_table(funcs): print(f"{'LVO':>6} {'Bias':>5} {'Function':<30} {'Registers'}") print(f"{'─'*6} {'─'*5} {'─'*30} {'─'*30}") for f in funcs: regs = ', '.join(f['regs']) if f['regs'] else '(none)' print(f"{f['lvo']:>+6d} {f['bias']:>5d} {f['name']:<30} {regs}") if __name__ == '__main__': ap = argparse.ArgumentParser(description='Parse AmigaOS FD files') ap.add_argument('fdfile', help='Path to .fd file') ap.add_argument('--json', action='store_true', help='Output JSON') ap.add_argument('--ida', action='store_true', help='Output IDA script') args = ap.parse_args() funcs = parse_fd(args.fdfile) if args.json: output_json(funcs) elif args.ida: output_ida_script(funcs, args.fdfile.split('/')[-1].replace('_lib.fd','')) else: output_table(funcs) sys.stderr.write(f"Parsed {len(funcs)} public functions\n") ``` --- ## Where to Get NDK FD Files | NDK | Source | FD path | |---|---|---| | NDK 3.9 | Aminet: `dev/misc/NDK39.lha` (free) | `NDK_3.9/Include/fd/` | | NDK 3.2 | Hyperion (commercial, ~€30) | `NDK3.2/Include_H/fd/` | | NDK 3.1 | Commodore (archived) | `NDK3.1/Include/fd/` | --- ## Auto-Generating IDA LVO Labels ```bash # Generate IDA script from all FD files: for fd in NDK_3.9/Include/fd/*_lib.fd; do python3 parse_fd.py "$fd" --ida >> all_lvos.py done ``` Then in IDA: File → Script command → Run `all_lvos.py` --- ## References - NDK39: `fd/` directory - [api_call_identification.md](../05_reversing/static/api_call_identification.md) — using LVOs in RE