Task 026: .AML File Format

Task 026: .AML File Format

1. List of Properties Intrinsic to the .AML File Format

The .AML file format, as defined in the ACPI specification, is a binary format for ACPI Machine Language (AML) bytecode, typically embedded within ACPI tables like DSDT or SSDT. The format begins with a fixed 36-byte header, followed by the AML bytecode. The intrinsic properties (structural elements) are:

  • Table Signature: 4-byte ASCII string (e.g., "DSDT" or "SSDT"), identifying the table type.
  • Table Length: 4-byte unsigned integer (little-endian), representing the total length of the file in bytes (including the header).
  • Revision: 1-byte unsigned integer, indicating the ACPI specification revision the table complies with.
  • Checksum: 1-byte unsigned integer, a byte-wise checksum of the entire file (must sum to 0 modulo 256 for validity).
  • OEM ID: 6-byte ASCII string (null-padded if shorter), identifying the Original Equipment Manufacturer.
  • OEM Table ID: 8-byte ASCII string (null-padded if shorter), providing an OEM-specific table identifier.
  • OEM Revision: 4-byte unsigned integer (little-endian), the revision number assigned by the OEM.
  • Creator ID: 4-byte ASCII string, identifying the vendor of the ASL compiler (e.g., "INTL" for Intel).
  • Creator Revision: 4-byte unsigned integer (little-endian), the revision of the ASL compiler used to generate the file.

These properties are fixed in position and format, with all multi-byte integers in little-endian order. The remaining bytes after offset 36 contain the AML bytecode, which is not part of the header properties but defines the namespace objects and methods.

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .AML File Dump

AML File Property Dumper

Drag and drop a .AML file here to dump its properties

4. Python Class for .AML File Handling

import struct

class AMLParser:
    def __init__(self, filename=None):
        self.signature = None
        self.length = None
        self.revision = None
        self.checksum = None
        self.oem_id = None
        self.oem_table_id = None
        self.oem_revision = None
        self.creator_id = None
        self.creator_revision = None
        self.body = b''  # AML bytecode after header
        if filename:
            self.read(filename)

    def read(self, filename):
        with open(filename, 'rb') as f:
            data = f.read()
        if len(data) < 36:
            raise ValueError("File too small to be a valid .AML")
        (
            self.signature,
            self.length,
            self.revision,
            self.checksum,
            self.oem_id,
            self.oem_table_id,
            self.oem_revision,
            self.creator_id,
            self.oem_revision
        ) = struct.unpack('<4s I B B 6s 8s I 4s I', data[:36])
        self.signature = self.signature.decode('ascii')
        self.oem_id = self.oem_id.decode('ascii').rstrip('\x00')
        self.oem_table_id = self.oem_table_id.decode('ascii').rstrip('\x00')
        self.creator_id = self.creator_id.decode('ascii')
        self.body = data[36:]
        # Validate length and checksum for completeness
        if self.length != len(data):
            print("Warning: Length mismatch")
        computed_checksum = -sum(data) & 0xFF
        if computed_checksum != 0:
            print("Warning: Checksum invalid")

    def print_properties(self):
        if not self.signature:
            raise ValueError("No file loaded")
        print(f"Table Signature: {self.signature}")
        print(f"Table Length: {self.length}")
        print(f"Revision: {self.revision}")
        print(f"Checksum: {self.checksum}")
        print(f"OEM ID: {self.oem_id}")
        print(f"OEM Table ID: {self.oem_table_id}")
        print(f"OEM Revision: {self.oem_revision}")
        print(f"Creator ID: {self.creator_id}")
        print(f"Creator Revision: {self.creator_revision}")

    def write(self, filename):
        if not self.signature:
            raise ValueError("No data to write")
        header = struct.pack(
            '<4s I B B 6s 8s I 4s I',
            self.signature.encode('ascii'),
            self.length,
            self.revision,
            self.checksum,
            self.oem_id.encode('ascii').ljust(6, b'\x00'),
            self.oem_table_id.encode('ascii').ljust(8, b'\x00'),
            self.oem_revision,
            self.creator_id.encode('ascii'),
            self.creator_revision
        )
        data = header + self.body
        # Update length and checksum if needed
        self.length = len(data)
        checksum = -sum(data) & 0xFF
        data = data[:9] + struct.pack('B', checksum) + data[10:]
        self.checksum = checksum
        with open(filename, 'wb') as f:
            f.write(data)

# Example usage:
# parser = AMLParser('example.aml')
# parser.print_properties()
# parser.write('output.aml')

5. Java Class for .AML File Handling

import java.io.*;
import java.nio.*;
import java.nio.file.*;

public class AMLParser {
    private String signature;
    private int length;
    private byte revision;
    private byte checksum;
    private String oemId;
    private String oemTableId;
    private int oemRevision;
    private String creatorId;
    private int creatorRevision;
    private byte[] body;  // AML bytecode after header

    public AMLParser(String filename) throws IOException {
        read(filename);
    }

    public void read(String filename) throws IOException {
        byte[] data = Files.readAllBytes(Paths.get(filename));
        if (data.length < 36) {
            throw new IllegalArgumentException("File too small to be a valid .AML");
        }
        ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
        byte[] sigBytes = new byte[4];
        bb.get(sigBytes);
        signature = new String(sigBytes);
        length = bb.getInt();
        revision = bb.get();
        checksum = bb.get();
        byte[] oemBytes = new byte[6];
        bb.get(oemBytes);
        oemId = new String(oemBytes).replaceAll("\0", "");
        byte[] oemTblBytes = new byte[8];
        bb.get(oemTblBytes);
        oemTableId = new String(oemTblBytes).replaceAll("\0", "");
        oemRevision = bb.getInt();
        byte[] creBytes = new byte[4];
        bb.get(creBytes);
        creatorId = new String(creBytes);
        creatorRevision = bb.getInt();
        body = new byte[data.length - 36];
        System.arraycopy(data, 36, body, 0, body.length);
        // Validate length and checksum
        if (length != data.length) {
            System.out.println("Warning: Length mismatch");
        }
        int computedChecksum = 0;
        for (byte b : data) {
            computedChecksum = (computedChecksum + (b & 0xFF)) & 0xFF;
        }
        if (computedChecksum != 0) {
            System.out.println("Warning: Checksum invalid");
        }
    }

    public void printProperties() {
        System.out.println("Table Signature: " + signature);
        System.out.println("Table Length: " + length);
        System.out.println("Revision: " + (revision & 0xFF));
        System.out.println("Checksum: " + (checksum & 0xFF));
        System.out.println("OEM ID: " + oemId);
        System.out.println("OEM Table ID: " + oemTableId);
        System.out.println("OEM Revision: " + oemRevision);
        System.out.println("Creator ID: " + creatorId);
        System.out.println("Creator Revision: " + creatorRevision);
    }

    public void write(String filename) throws IOException {
        ByteBuffer bb = ByteBuffer.allocate(36 + body.length).order(ByteOrder.LITTLE_ENDIAN);
        bb.put(signature.getBytes());
        bb.putInt(length);
        bb.put(revision);
        bb.put(checksum);
        bb.put(oemId.getBytes());
        for (int i = oemId.length(); i < 6; i++) bb.put((byte) 0);
        bb.put(oemTableId.getBytes());
        for (int i = oemTableId.length(); i < 8; i++) bb.put((byte) 0);
        bb.putInt(oemRevision);
        bb.put(creatorId.getBytes());
        bb.putInt(creatorRevision);
        bb.put(body);
        byte[] data = bb.array();
        // Update length and checksum
        length = data.length;
        int sum = 0;
        for (byte b : data) {
            sum += b & 0xFF;
        }
        checksum = (byte) (-sum & 0xFF);
        data[9] = checksum;
        Files.write(Paths.get(filename), data);
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     AMLParser parser = new AMLParser("example.aml");
    //     parser.printProperties();
    //     parser.write("output.aml");
    // }
}

6. JavaScript Class for .AML File Handling (Node.js)

const fs = require('fs');

class AMLParser {
    constructor(filename = null) {
        this.signature = null;
        this.length = null;
        this.revision = null;
        this.checksum = null;
        this.oemId = null;
        this.oemTableId = null;
        this.oemRevision = null;
        this.creatorId = null;
        this.creatorRevision = null;
        this.body = Buffer.alloc(0);  // AML bytecode after header
        if (filename) {
            this.read(filename);
        }
    }

    read(filename) {
        const data = fs.readFileSync(filename);
        if (data.length < 36) {
            throw new Error('File too small to be a valid .AML');
        }
        this.signature = data.slice(0, 4).toString('ascii');
        this.length = data.readUInt32LE(4);
        this.revision = data[8];
        this.checksum = data[9];
        this.oemId = data.slice(10, 16).toString('ascii').replace(/\0/g, '');
        this.oemTableId = data.slice(16, 24).toString('ascii').replace(/\0/g, '');
        this.oemRevision = data.readUInt32LE(24);
        this.creatorId = data.slice(28, 32).toString('ascii');
        this.creatorRevision = data.readUInt32LE(32);
        this.body = data.slice(36);
        // Validate length and checksum
        if (this.length !== data.length) {
            console.warn('Warning: Length mismatch');
        }
        let sum = 0;
        for (let b of data) {
            sum = (sum + (b & 0xFF)) & 0xFF;
        }
        if (sum !== 0) {
            console.warn('Warning: Checksum invalid');
        }
    }

    printProperties() {
        console.log(`Table Signature: ${this.signature}`);
        console.log(`Table Length: ${this.length}`);
        console.log(`Revision: ${this.revision}`);
        console.log(`Checksum: ${this.checksum}`);
        console.log(`OEM ID: ${this.oemId}`);
        console.log(`OEM Table ID: ${this.oemTableId}`);
        console.log(`OEM Revision: ${this.oemRevision}`);
        console.log(`Creator ID: ${this.creatorId}`);
        console.log(`Creator Revision: ${this.creatorRevision}`);
    }

    write(filename) {
        const header = Buffer.alloc(36);
        header.write(this.signature, 0, 4, 'ascii');
        header.writeUInt32LE(this.length, 4);
        header[8] = this.revision;
        header[9] = this.checksum;
        header.write(this.oemId.padEnd(6, '\x00'), 10, 6, 'ascii');
        header.write(this.oemTableId.padEnd(8, '\x00'), 16, 8, 'ascii');
        header.writeUInt32LE(this.oemRevision, 24);
        header.write(this.creatorId, 28, 4, 'ascii');
        header.writeUInt32LE(this.creatorRevision, 32);
        const data = Buffer.concat([header, this.body]);
        // Update length and checksum
        this.length = data.length;
        let sum = 0;
        for (let b of data) {
            sum += b & 0xFF;
        }
        this.checksum = (-sum & 0xFF);
        data[9] = this.checksum;
        fs.writeFileSync(filename, data);
    }
}

// Example usage:
// const parser = new AMLParser('example.aml');
// parser.printProperties();
// parser.write('output.aml');

7. C Class for .AML File Handling (Using C++ for Class Structure)

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>

class AMLParser {
private:
    std::string signature;
    uint32_t length;
    uint8_t revision;
    uint8_t checksum;
    std::string oemId;
    std::string oemTableId;
    uint32_t oemRevision;
    std::string creatorId;
    uint32_t creatorRevision;
    std::vector<uint8_t> body;  // AML bytecode after header

public:
    AMLParser(const std::string& filename = "") {
        if (!filename.empty()) {
            read(filename);
        }
    }

    void read(const std::string& filename) {
        std::ifstream file(filename, std::ios::binary | std::ios::ate);
        if (!file) {
            throw std::runtime_error("Unable to open file");
        }
        std::streamsize size = file.tellg();
        file.seekg(0, std::ios::beg);
        std::vector<uint8_t> data(size);
        if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
            throw std::runtime_error("Error reading file");
        }
        if (data.size() < 36) {
            throw std::runtime_error("File too small to be a valid .AML");
        }
        signature.assign(reinterpret_cast<char*>(data.data()), 4);
        memcpy(&length, data.data() + 4, 4);
        revision = data[8];
        checksum = data[9];
        oemId.assign(reinterpret_cast<char*>(data.data() + 10), 6);
        oemId.erase(oemId.find('\0'));
        oemTableId.assign(reinterpret_cast<char*>(data.data() + 16), 8);
        oemTableId.erase(oemTableId.find('\0'));
        memcpy(&oemRevision, data.data() + 24, 4);
        creatorId.assign(reinterpret_cast<char*>(data.data() + 28), 4);
        memcpy(&creatorRevision, data.data() + 32, 4);
        body.assign(data.begin() + 36, data.end());
        // Validate length and checksum (little-endian assumed)
        if (length != data.size()) {
            std::cout << "Warning: Length mismatch" << std::endl;
        }
        uint8_t computedChecksum = 0;
        for (uint8_t b : data) {
            computedChecksum += b;
        }
        if (computedChecksum != 0) {
            std::cout << "Warning: Checksum invalid" << std::endl;
        }
    }

    void printProperties() const {
        std::cout << "Table Signature: " << signature << std::endl;
        std::cout << "Table Length: " << length << std::endl;
        std::cout << "Revision: " << static_cast<unsigned>(revision) << std::endl;
        std::cout << "Checksum: " << static_cast<unsigned>(checksum) << std::endl;
        std::cout << "OEM ID: " << oemId << std::endl;
        std::cout << "OEM Table ID: " << oemTableId << std::endl;
        std::cout << "OEM Revision: " << oemRevision << std::endl;
        std::cout << "Creator ID: " << creatorId << std::endl;
        std::cout << "Creator Revision: " << creatorRevision << std::endl;
    }

    void write(const std::string& filename) const {
        std::vector<uint8_t> data(36 + body.size());
        memcpy(data.data(), signature.c_str(), 4);
        memcpy(data.data() + 4, &length, 4);
        data[8] = revision;
        data[9] = checksum;
        std::string paddedOem = oemId + std::string(6 - oemId.size(), '\0');
        memcpy(data.data() + 10, paddedOem.c_str(), 6);
        std::string paddedOemTbl = oemTableId + std::string(8 - oemTableId.size(), '\0');
        memcpy(data.data() + 16, paddedOemTbl.c_str(), 8);
        memcpy(data.data() + 24, &oemRevision, 4);
        memcpy(data.data() + 28, creatorId.c_str(), 4);
        memcpy(data.data() + 32, &creatorRevision, 4);
        std::copy(body.begin(), body.end(), data.begin() + 36);
        // Update length and checksum
        uint32_t newLength = static_cast<uint32_t>(data.size());
        memcpy(data.data() + 4, &newLength, 4);
        uint8_t newChecksum = 0;
        for (uint8_t b : data) {
            newChecksum += b;
        }
        newChecksum = -newChecksum;
        data[9] = newChecksum;
        std::ofstream file(filename, std::ios::binary);
        if (!file.write(reinterpret_cast<const char*>(data.data()), data.size())) {
            throw std::runtime_error("Error writing file");
        }
    }
};

// Example usage:
// int main() {
//     try {
//         AMLParser parser("example.aml");
//         parser.printProperties();
//         parser.write("output.aml");
//     } catch (const std::exception& e) {
//         std::cerr << e.what() << std::endl;
//     }
//     return 0;
// }