UNPKG

uae-dap

Version:

Debug Adapter Protocol for Amiga development with FS-UAE or WinUAE

285 lines 10.6 kB
"use strict"; /** * Parses Hunk data in Amiga executable file * * @see {@link http://amiga-dev.wikidot.com/file-format:hunk} */ Object.defineProperty(exports, "__esModule", { value: true }); exports.parseHunks = exports.parseHunksFromFile = exports.MemoryType = exports.HunkType = void 0; const promises_1 = require("fs/promises"); var HunkType; (function (HunkType) { HunkType["CODE"] = "CODE"; HunkType["DATA"] = "DATA"; HunkType["BSS"] = "BSS"; })(HunkType = exports.HunkType || (exports.HunkType = {})); var MemoryType; (function (MemoryType) { MemoryType["ANY"] = "ANY"; MemoryType["CHIP"] = "CHIP"; MemoryType["FAST"] = "FAST"; })(MemoryType = exports.MemoryType || (exports.MemoryType = {})); const BlockTypes = { CODE: 0x3e9, DATA: 0x3ea, BSS: 0x3eb, RELOC32: 0x3ec, SYMBOL: 0x3f0, DEBUG: 0x3f1, END: 0x3f2, HEADER: 0x3f3, }; /** * Extract an array of Hunks from an Amiga executable file */ async function parseHunksFromFile(filename) { const buffer = await (0, promises_1.readFile)(filename); return parseHunks(buffer); } exports.parseHunksFromFile = parseHunksFromFile; /** * Extract an array of Hunks from Amiga executable file data */ function parseHunks(contents) { const reader = new BufferReader(contents); // Check 'magic cookie' to ensure header type const type = reader.readLong(); if (type !== BlockTypes.HEADER) { throw new Error("Not a valid hunk file : Unable to find correct HUNK_HEADER"); } return parseHeader(reader).map((hunkInfo, index) => createHunk(hunkInfo, index, reader)); } exports.parseHunks = parseHunks; /** * Parse header block to build hunk table, containing the size and memory type for each hunk */ function parseHeader(reader) { // HUNK_HEADER: // strings A number of resident library names. // uint32 Table size. The highest hunk number plus one. // uint32 F First hunk. The first hunk that should be used in loading. // uint32 L Last hunk. The last hunk that should be used in loading. // uint32 * (L-F+1) A list of hunk sizes. reader.skip(4); // Skip header/string section const tableSize = reader.readLong(); const firstHunk = reader.readLong(); const lastHunk = reader.readLong(); // Validate sizes if (tableSize < 0 || firstHunk < 0 || lastHunk < 0) { throw new Error("Not a valid hunk file : Invalid sizes for hunks"); } const hunkTable = []; const hunkCount = lastHunk - firstHunk + 1; for (let i = 0; i < hunkCount; i++) { // The hunk size of each block is expected to indicate in its two highest bits which flags to pass to AllocMem. // Bit 31 Bit 30 Description // 0 0 The hunk can be loaded into whatever memory is available, with a preference for fast memory. // 1 0 The hunk should be loaded into fast memory or the process should fail. // 0 1 The hunk should be loaded into chip memory or the process should fail. // TODO: not supported // 1 1 Indicates an additional following longword containing the specific flags, of which bit 30 gets cleared before use. const hunkSize = reader.readLong(); let memType = MemoryType.ANY; const masked = hunkSize & 0xf0000000; // Mask lower bytes containing size if (masked === 1 << 30) { memType = MemoryType.CHIP; } else if (masked === 1 << 31) { memType = MemoryType.FAST; } hunkTable.push({ memType, allocSize: (hunkSize & 0x0fffffff) * 4, // Mask upper bytes containing memory type }); } return hunkTable; } function createHunk({ memType, allocSize }, index, reader) { // Create a minimal object with the info we have: const hunk = { index, fileOffset: reader.offset(), memType, hunkType: HunkType.CODE, allocSize, symbols: [], reloc32: [], lineDebugInfo: [], }; // Populate with block data from the reader: let blockType = reader.readLong(); while (blockType !== BlockTypes.END) { switch (blockType) { // Initial hunk blocks: // These define the type and content of the hunk case BlockTypes.CODE: // uint32 N The number of longwords of code. // uint32 * N Machine code. hunk.hunkType = HunkType.CODE; hunk.dataSize = reader.readLong() * 4; hunk.dataOffset = reader.offset(); hunk.data = reader.readBytes(hunk.dataSize); break; case BlockTypes.DATA: // uint32 N The number of longwords of data. // uint32 * N Data. hunk.hunkType = HunkType.DATA; hunk.dataSize = reader.readLong() * 4; hunk.dataOffset = reader.offset(); hunk.data = reader.readBytes(hunk.dataSize); break; case BlockTypes.BSS: // uint32 The number of longwords of zeroed memory to allocate. hunk.hunkType = HunkType.BSS; hunk.allocSize = reader.readLong() * 4; // Is this always the same as in hunk table? break; // Additional hunk blocks: // These provide additional properties case BlockTypes.DEBUG: { const info = parseDebug(reader); if (info) { hunk.lineDebugInfo.push(info); } break; } case BlockTypes.RELOC32: hunk.reloc32.push(...parseReloc32(reader)); break; case BlockTypes.SYMBOL: hunk.symbols.push(...parseSymbols(reader)); break; // Skip all other block types default: reader.skip(reader.readLong() * 4); break; } if (reader.finished()) { break; } blockType = reader.readLong(); } return hunk; } function parseSymbols(reader) { const symbols = []; // HUNK_SYMBOL [0x3F0] // string The name of the current symbol. A zero size indicates the immediate end of this block. // uint32 The offset of the current symbol from the start of the hunk. let numLongs = reader.readLong(); while (numLongs > 0) { // String: // uint32 N The number of uint32s that compose the string. // uint32 * N Each uint32 is composed of four characters, with the exception of the last uint32. // Extra space at the end of the last uint32 is filled with the 0 byte. symbols.push({ name: reader.readString(numLongs * 4), offset: reader.readLong(), }); numLongs = reader.readLong(); } // Sort symbols by offset ? if (symbols.length > 0) { symbols.sort(function (a, b) { return a.offset > b.offset ? 1 : b.offset > a.offset ? -1 : 0; }); } return symbols; } function parseDebug(reader) { // "LINE" - Generic debug hunk format // uint32 N The number of longwords following in the given hunk. If this value is zero, // then it indicates the immediate end of this block. // uint32 The base offset within the source file. // char[4] "LINE" // string The source file name. // line_info[M] The table of line offsets within the local code, data or bss section. const numLongs = reader.readLong(); const baseOffset = reader.readLong(); const debugTag = reader.readString(4); // We only support debug line as debug format currently so skip others if (debugTag !== "LINE") { reader.skip((numLongs - 2) * 4); return null; } // String: // uint32 N The number of uint32s that compose the string. // uint32 * N Each uint32 is composed of four characters, with the exception of the last uint32. // Extra space at the end of the last uint32 is filled with the 0 byte. const numNameLongs = reader.readLong(); const sourceFilename = reader.readString(numNameLongs * 4); const numLines = (numLongs - numNameLongs - 3) / 2; // 3 longs + name already read, 2 per item const lines = []; for (let i = 0; i < numLines; i++) { // line_info: // uint32 Line number. // uint32 Offset of line from base offset. lines.push({ line: reader.readLong() & 0xffffff, offset: baseOffset + reader.readLong(), }); } return { sourceFilename, lines, baseOffset }; } function parseReloc32(reader) { // HUNK_RELOC32 [0x3EC]: // uint32 N The number of offsets for a given hunk. // If this value is zero, then it indicates the immediate end of this block. // uint32 The number of the hunk the offsets are to point into. // uint32 * N Offsets in the current CODE or DATA hunk to relocate. const relocs = []; let count = reader.readLong(); while (count !== 0) { const target = reader.readLong(); const offsets = []; for (let i = 0; i < count; i++) { offsets.push(reader.readLong()); } relocs.push({ target, offsets }); count = reader.readLong(); } return relocs; } /** * Manages reading BE data from buffer and tracking offset position */ class BufferReader { constructor(buffer) { this.buffer = buffer; this.pos = 0; } readLong() { const value = this.buffer.readUInt32BE(this.pos); this.pos += 4; return value; } readByte() { return this.buffer.readUInt8(this.pos++); } readBytes(length) { const slice = this.buffer.slice(this.pos, this.pos + length); this.pos += length; return slice; } readString(length) { const startPos = this.pos; const charCodes = []; for (let i = 0; i < length; i++) { const v = this.readByte(); if (v === 0) break; charCodes.push(v); } this.pos = startPos + length; return String.fromCharCode(...charCodes); } skip(bytes) { this.pos += bytes; } finished() { return this.pos > this.buffer.length - 2; } offset() { return this.pos; } } //# sourceMappingURL=amigaHunkParser.js.map