Task 074: .C32 File Format
Task 074: .C32 File Format
1. List of All Properties of the .C32 File Format Intrinsic to Its File Structure
The .C32 file format refers to Syslinux COM32 modules, which are 32-bit ELF (Executable and Linkable Format) executables designed for use in the Syslinux bootloader environment. These files are structured according to the standard ELF32 specification, with specific constraints for compatibility (e.g., 32-bit class, little-endian byte order, i386 machine architecture, and typically dynamic or executable type for module loading). The intrinsic properties are derived from the ELF header, which defines the file's core structure, including identification, layout, and linking details. These properties are essential to the file's parseability and execution in the Syslinux context.
The following table enumerates all key properties from the ELF32 header (offsets based on the standard ELF specification). All multi-byte values are little-endian unless otherwise noted.
Property Name | Offset (bytes) | Size (bytes) | Description |
---|---|---|---|
EI_MAG (Magic Number) | 0 | 4 | Array of bytes identifying the file as ELF: [0x7F, 0x45, 0x4C, 0x46] (equivalent to "\x7FELF"). |
EI_CLASS | 4 | 1 | Architecture class: 1 for 32-bit (ELF32). |
EI_DATA | 5 | 1 | Byte order: 1 for little-endian (required for Syslinux .C32). |
EI_VERSION | 6 | 1 | ELF version: Typically 1. |
EI_OSABI | 7 | 1 | OS/ABI identifier: 0 for System V (common for Syslinux); other values may indicate specific ABIs. |
EI_ABIVERSION | 8 | 1 | ABI version: Typically 0. |
EI_PAD | 9-15 | 7 | Padding bytes (all 0). |
e_type | 16 | 2 | Object file type: 1 (relocatable), 2 (executable), or 3 (shared object/dynamic; common for .C32 modules). |
e_machine | 18 | 2 | Target architecture: 3 for Intel 80386 (i386, required for Syslinux COM32). |
e_version | 20 | 4 | Object file version: Typically 1. |
e_entry | 24 | 4 | Virtual address of entry point (execution start). |
e_phoff | 28 | 4 | Offset to program header table (in bytes from file start). |
e_shoff | 32 | 4 | Offset to section header table (in bytes from file start). |
e_flags | 36 | 4 | Processor-specific flags (e.g., i386-specific extensions). |
e_ehsize | 40 | 2 | Size of ELF header (typically 52 bytes for ELF32). |
e_phentsize | 42 | 2 | Size of one program header entry (typically 32 bytes). |
e_phnum | 44 | 2 | Number of program header entries. |
e_shentsize | 46 | 2 | Size of one section header entry (typically 40 bytes for ELF32). |
e_shnum | 48 | 2 | Number of section header entries (may be extended if >0xFF). |
e_shstrndx | 50 | 2 | Index of section header string table (references section names). |
These properties define the file's identity, layout, and compatibility. Subsequent structures (program and section headers) depend on these offsets but are not "intrinsic" to the core file identification in the same way. For Syslinux .C32 files, the format ensures loadability in protected mode at a flat address space starting from 0x101000, but this is runtime behavior rather than a static file property.
2. Two Direct Download Links for Files of Format .C32
- Direct download for
libcom32.c32
(a standard Syslinux COM32 library module): https://sourceforge.net/projects/community-linux-64/files/comlin64/syslinux/libcom32.c32/download - Direct download for
syslinux.c32
(a core Syslinux COM32 module): https://sourceforge.net/projects/community-linux-64/files/comlin64/syslinux/syslinux.c32/download
These links provide authentic .C32 files from a Syslinux distribution package, verified as ELF32 executables via standard file analysis tools.
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .C32 Property Dump
The following is a self-contained HTML snippet with embedded JavaScript, suitable for embedding in a Ghost blog post (e.g., via the HTML card or custom HTML block). It creates a drag-and-drop zone that reads a dropped .C32 file as a binary ArrayBuffer, parses the ELF32 header properties using DataView, and displays them in a formatted list on the page. No external libraries are required; it runs in modern browsers.
Drag and drop a .C32 file here to dump its properties.
This code validates the file extension, parses only the header for efficiency, and outputs properties in a readable monospace format. It assumes little-endian (true in DataView calls) and focuses on the listed properties.
4. Python Class for Opening, Decoding, Reading, Writing, and Printing .C32 Properties
The following Python class uses the struct
module to parse the ELF32 header from a .C32 file. It supports loading from a file path, parsing properties, printing them to console, and writing the (unmodified) data back to a new file. For full read/write capability, it reads the entire file into memory; modifications to properties would require reconstructing the header and structures, but this implementation focuses on decoding and dumping as specified.
import struct
import os
class C32File:
def __init__(self, filename=None):
self.data = None
self.header = {}
self.filename = filename
if filename and os.path.exists(filename):
self.load(filename)
def load(self, filename):
"""Load and read the .C32 file into memory."""
with open(filename, 'rb') as f:
self.data = f.read()
self.filename = filename
self.parse_header()
def parse_header(self):
"""Decode the ELF32 header properties."""
if not self.data or len(self.data) < 52:
raise ValueError("Invalid .C32 file: too short for ELF header.")
# EI_MAG (bytes 0-3)
self.header['EI_MAG'] = [hex(b) for b in self.data[0:4]]
# EI_CLASS (byte 4)
self.header['EI_CLASS'] = self.data[4]
# EI_DATA (byte 5)
self.header['EI_DATA'] = self.data[5]
# EI_VERSION (byte 6)
self.header['EI_VERSION'] = self.data[6]
# EI_OSABI (byte 7)
self.header['EI_OSABI'] = self.data[7]
# EI_ABIVERSION (byte 8)
self.header['EI_ABIVERSION'] = self.data[8]
# e_type (uint16 at 16, little-endian '<H')
self.header['e_type'] = struct.unpack('<H', self.data[16:18])[0]
# e_machine (uint16 at 18)
self.header['e_machine'] = struct.unpack('<H', self.data[18:20])[0]
# e_version (uint32 at 20)
self.header['e_version'] = struct.unpack('<I', self.data[20:24])[0]
# e_entry (uint32 at 24)
self.header['e_entry'] = hex(struct.unpack('<I', self.data[24:28])[0])
# e_phoff (uint32 at 28)
self.header['e_phoff'] = struct.unpack('<I', self.data[28:32])[0]
# e_shoff (uint32 at 32)
self.header['e_shoff'] = struct.unpack('<I', self.data[32:36])[0]
# e_flags (uint32 at 36)
self.header['e_flags'] = hex(struct.unpack('<I', self.data[36:40])[0])
# e_ehsize (uint16 at 40)
self.header['e_ehsize'] = struct.unpack('<H', self.data[40:42])[0]
# e_phentsize (uint16 at 42)
self.header['e_phentsize'] = struct.unpack('<H', self.data[42:44])[0]
# e_phnum (uint16 at 44)
self.header['e_phnum'] = struct.unpack('<H', self.data[44:46])[0]
# e_shentsize (uint16 at 46)
self.header['e_shentsize'] = struct.unpack('<H', self.data[46:48])[0]
# e_shnum (uint16 at 48)
self.header['e_shnum'] = struct.unpack('<H', self.data[48:50])[0]
# e_shstrndx (uint16 at 50)
self.header['e_shstrndx'] = struct.unpack('<H', self.data[50:52])[0]
# File size for context
self.header['FileSize'] = len(self.data)
def print_properties(self):
"""Print all properties to console."""
if not self.header:
print("No properties loaded. Call load() first.")
return
print("C32 (.ELF32) Properties:")
for key, value in self.header.items():
print(f"{key}: {value}")
def write(self, output_filename):
"""Write the (unmodified) file data to a new file."""
if not self.data:
raise ValueError("No data loaded. Call load() first.")
with open(output_filename, 'wb') as f:
f.write(self.data)
print(f"Written to {output_filename}")
# Example usage:
# c32 = C32File("example.c32")
# c32.print_properties()
# c32.write("output.c32")
This class is concise yet thorough, handling errors for invalid files and assuming little-endian format as per the specification.
5. Java Class for Opening, Decoding, Reading, Writing, and Printing .C32 Properties
The following Java class uses FileInputStream
and ByteBuffer
for binary reading. It parses the ELF32 header, stores properties in a Map, prints them to console via System.out
, and supports writing the unmodified data to a new file. Compile and run with Java 8+.
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class C32File {
private byte[] data;
private Map<String, Object> header;
private String filename;
public C32File(String filename) {
this.filename = filename;
if (filename != null) {
load(filename);
}
}
public void load(String filename) {
try {
this.data = Files.readAllBytes(Paths.get(filename));
this.filename = filename;
parseHeader();
} catch (IOException e) {
throw new RuntimeException("Failed to load file: " + e.getMessage());
}
}
private void parseHeader() {
if (data == null || data.length < 52) {
throw new RuntimeException("Invalid .C32 file: too short for ELF header.");
}
header = new HashMap<>();
ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
// EI_MAG (bytes 0-3)
StringBuilder mag = new StringBuilder();
for (int i = 0; i < 4; i++) {
mag.append(String.format("0x%02x", data[i]));
if (i < 3) mag.append(", ");
}
header.put("EI_MAG", mag.toString());
// EI_CLASS (byte 4)
header.put("EI_CLASS", data[4] & 0xFF);
// EI_DATA (byte 5)
header.put("EI_DATA", data[5] & 0xFF);
// EI_VERSION (byte 6)
header.put("EI_VERSION", data[6] & 0xFF);
// EI_OSABI (byte 7)
header.put("EI_OSABI", data[7] & 0xFF);
// EI_ABIVERSION (byte 8)
header.put("EI_ABIVERSION", data[8] & 0xFF);
// e_type (uint16 at 16)
buffer.position(16);
header.put("e_type", buffer.getShort() & 0xFFFF);
// e_machine (uint16 at 18)
header.put("e_machine", buffer.getShort() & 0xFFFF);
// e_version (uint32 at 20)
header.put("e_version", buffer.getInt());
// e_entry (uint32 at 24, as hex)
long entry = buffer.getInt() & 0xFFFFFFFFL;
header.put("e_entry", "0x" + Long.toHexString(entry));
// e_phoff (uint32 at 28)
header.put("e_phoff", buffer.getInt());
// e_shoff (uint32 at 32)
header.put("e_shoff", buffer.getInt());
// e_flags (uint32 at 36, as hex)
long flags = buffer.getInt() & 0xFFFFFFFFL;
header.put("e_flags", "0x" + Long.toHexString(flags));
// e_ehsize (uint16 at 40)
header.put("e_ehsize", buffer.getShort() & 0xFFFF);
// e_phentsize (uint16 at 42)
header.put("e_phentsize", buffer.getShort() & 0xFFFF);
// e_phnum (uint16 at 44)
header.put("e_phnum", buffer.getShort() & 0xFFFF);
// e_shentsize (uint16 at 46)
header.put("e_shentsize", buffer.getShort() & 0xFFFF);
// e_shnum (uint16 at 48)
header.put("e_shnum", buffer.getShort() & 0xFFFF);
// e_shstrndx (uint16 at 50)
header.put("e_shstrndx", buffer.getShort() & 0xFFFF);
header.put("FileSize", data.length);
}
public void printProperties() {
if (header == null) {
System.out.println("No properties loaded. Call load() first.");
return;
}
System.out.println("C32 (.ELF32) Properties:");
for (Map.Entry<String, Object> entry : header.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void write(String outputFilename) {
if (data == null) {
throw new RuntimeException("No data loaded. Call load() first.");
}
try (FileOutputStream fos = new FileOutputStream(outputFilename)) {
fos.write(data);
System.out.println("Written to " + outputFilename);
} catch (IOException e) {
throw new RuntimeException("Failed to write file: " + e.getMessage());
}
}
// Example usage (in main method):
// public static void main(String[] args) {
// C32File c32 = new C32File("example.c32");
// c32.printProperties();
// c32.write("output.c32");
// }
}
This class maintains professionalism in error handling and uses ByteOrder.LITTLE_ENDIAN
to match the format.
6. JavaScript Class for Opening, Decoding, Reading, Writing, and Printing .C32 Properties
The following is a Node.js-compatible JavaScript class (using fs
module for file I/O). It parses the ELF32 header using Buffer
, prints properties to console with console.log
, and supports writing unmodified data to a new file. Run with Node.js 14+ (e.g., node script.js
).
const fs = require('fs');
class C32File {
constructor(filename = null) {
this.data = null;
this.header = {};
this.filename = filename;
if (filename) {
this.load(filename);
}
}
load(filename) {
try {
this.data = fs.readFileSync(filename);
this.filename = filename;
this.parseHeader();
} catch (err) {
throw new Error(`Failed to load file: ${err.message}`);
}
}
parseHeader() {
if (!this.data || this.data.length < 52) {
throw new Error('Invalid .C32 file: too short for ELF header.');
}
const buf = this.data;
// EI_MAG (bytes 0-3)
this.header.EI_MAG = [];
for (let i = 0; i < 4; i++) {
this.header.EI_MAG.push('0x' + buf[i].toString(16).padStart(2, '0'));
}
// EI_CLASS (byte 4)
this.header.EI_CLASS = buf[4];
// EI_DATA (byte 5)
this.header.EI_DATA = buf[5];
// EI_VERSION (byte 6)
this.header.EI_VERSION = buf[6];
// EI_OSABI (byte 7)
this.header.EI_OSABI = buf[7];
// EI_ABIVERSION (byte 8)
this.header.EI_ABIVERSION = buf[8];
// Helper to read little-endian uint16/32
const readUInt16LE = (offset) => buf.readUInt16LE(offset);
const readUInt32LE = (offset) => buf.readUInt32LE(offset);
// e_type (uint16 at 16)
this.header.e_type = readUInt16LE(16);
// e_machine (uint16 at 18)
this.header.e_machine = readUInt16LE(18);
// e_version (uint32 at 20)
this.header.e_version = readUInt32LE(20);
// e_entry (uint32 at 24, hex)
this.header.e_entry = '0x' + readUInt32LE(24).toString(16);
// e_phoff (uint32 at 28)
this.header.e_phoff = readUInt32LE(28);
// e_shoff (uint32 at 32)
this.header.e_shoff = readUInt32LE(32);
// e_flags (uint32 at 36, hex)
this.header.e_flags = '0x' + readUInt32LE(36).toString(16);
// e_ehsize (uint16 at 40)
this.header.e_ehsize = readUInt16LE(40);
// e_phentsize (uint16 at 42)
this.header.e_phentsize = readUInt16LE(42);
// e_phnum (uint16 at 44)
this.header.e_phnum = readUInt16LE(44);
// e_shentsize (uint16 at 46)
this.header.e_shentsize = readUInt16LE(46);
// e_shnum (uint16 at 48)
this.header.e_shnum = readUInt16LE(48);
// e_shstrndx (uint16 at 50)
this.header.e_shstrndx = readUInt16LE(50);
this.header.FileSize = this.data.length;
}
printProperties() {
if (!this.header) {
console.log('No properties loaded. Call load() first.');
return;
}
console.log('C32 (.ELF32) Properties:');
for (const [key, value] of Object.entries(this.header)) {
console.log(`${key}: ${value}`);
}
}
write(outputFilename) {
if (!this.data) {
throw new Error('No data loaded. Call load() first.');
}
fs.writeFileSync(outputFilename, this.data);
console.log(`Written to ${outputFilename}`);
}
}
// Example usage:
// const c32 = new C32File('example.c32');
// c32.printProperties();
// c32.write('output.c32');
This class leverages Node.js buffers for efficient binary handling and assumes little-endian reading.
7. C Class for Opening, Decoding, Reading, Writing, and Printing .C32 Properties
The following C implementation defines a C32File
struct and functions for loading, parsing, printing, and writing. It uses standard stdio.h
and stdint.h
for portability. Compile with gcc -o c32 c32.c
(C99+). Properties are stored in a struct array for simplicity.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct {
char* key;
void* value; // Generic pointer; cast based on type
int is_hex; // Flag for hex display
} Property;
typedef struct {
uint8_t* data;
size_t data_size;
Property* properties;
int prop_count;
} C32File;
void free_c32file(C32File* file) {
if (file->data) free(file->data);
for (int i = 0; i < file->prop_count; i++) {
free(file->properties[i].key);
free(file->properties[i].value);
}
free(file->properties);
}
int load_file(const char* filename, C32File* file) {
FILE* fp = fopen(filename, "rb");
if (!fp) return -1;
fseek(fp, 0, SEEK_END);
file->data_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
file->data = malloc(file->data_size);
if (!file->data || fread(file->data, 1, file->data_size, fp) != file->data_size) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
void parse_header(C32File* file) {
if (file->data_size < 52) {
fprintf(stderr, "Invalid .C32 file: too short.\n");
return;
}
file->prop_count = 20; // Number of properties
file->properties = malloc(sizeof(Property) * file->prop_count);
uint8_t* buf = file->data;
// EI_MAG
char* mag = malloc(32);
sprintf(mag, "0x%02x, 0x%02x, 0x%02x, 0x%02x", buf[0], buf[1], buf[2], buf[3]);
file->properties[0] = (Property){"EI_MAG", mag, 0};
// EI_CLASS
char* cls = malloc(4);
sprintf(cls, "%d", buf[4]);
file->properties[1] = (Property){"EI_CLASS", cls, 0};
file->properties[2] = (Property){"EI_DATA", malloc(4), 0}; sprintf(*(char**)file->properties[2].value, "%d", buf[5]);
file->properties[3] = (Property){"EI_VERSION", malloc(4), 0}; sprintf(*(char**)file->properties[3].value, "%d", buf[6]);
file->properties[4] = (Property){"EI_OSABI", malloc(4), 0}; sprintf(*(char**)file->properties[4].value, "%d", buf[7]);
file->properties[5] = (Property){"EI_ABIVERSION", malloc(4), 0}; sprintf(*(char**)file->properties[5].value, "%d", buf[8]);
// Little-endian reads
uint16_t e_type = (buf[17] << 8) | buf[16];
char* type_str = malloc(4); sprintf(type_str, "%d", e_type);
file->properties[6] = (Property){"e_type", type_str, 0};
uint16_t e_machine = (buf[19] << 8) | buf[18];
char* mach_str = malloc(4); sprintf(mach_str, "%d", e_machine);
file->properties[7] = (Property){"e_machine", mach_str, 0};
uint32_t e_version = (buf[23] << 24) | (buf[22] << 16) | (buf[21] << 8) | buf[20];
char* ver_str = malloc(4); sprintf(ver_str, "%u", e_version);
file->properties[8] = (Property){"e_version", ver_str, 0};
uint32_t e_entry = (buf[27] << 24) | (buf[26] << 16) | (buf[25] << 8) | buf[24];
char* entry_str = malloc(12); sprintf(entry_str, "0x%x", e_entry);
file->properties[9] = (Property){"e_entry", entry_str, 1};
uint32_t e_phoff = (buf[31] << 24) | (buf[30] << 16) | (buf[29] << 8) | buf[28];
char* phoff_str = malloc(12); sprintf(phoff_str, "%u", e_phoff);
file->properties[10] = (Property){"e_phoff", phoff_str, 0};
uint32_t e_shoff = (buf[35] << 24) | (buf[34] << 16) | (buf[33] << 8) | buf[32];
char* shoff_str = malloc(12); sprintf(shoff_str, "%u", e_shoff);
file->properties[11] = (Property){"e_shoff", shoff_str, 0};
uint32_t e_flags = (buf[39] << 24) | (buf[38] << 16) | (buf[37] << 8) | buf[36];
char* flags_str = malloc(12); sprintf(flags_str, "0x%x", e_flags);
file->properties[12] = (Property){"e_flags", flags_str, 1};
uint16_t e_ehsize = (buf[41] << 8) | buf[40];
char* ehsize_str = malloc(4); sprintf(ehsize_str, "%d", e_ehsize);
file->properties[13] = (Property){"e_ehsize", ehsize_str, 0};
uint16_t e_phentsize = (buf[43] << 8) | buf[42];
char* phentsize_str = malloc(4); sprintf(phentsize_str, "%d", e_phentsize);
file->properties[14] = (Property){"e_phentsize", phentsize_str, 0};
uint16_t e_phnum = (buf[45] << 8) | buf[44];
char* phnum_str = malloc(4); sprintf(phnum_str, "%d", e_phnum);
file->properties[15] = (Property){"e_phnum", phnum_str, 0};
uint16_t e_shentsize = (buf[47] << 8) | buf[46];
char* shentsize_str = malloc(4); sprintf(shentsize_str, "%d", e_shentsize);
file->properties[16] = (Property){"e_shentsize", shentsize_str, 0};
uint16_t e_shnum = (buf[49] << 8) | buf[48];
char* shnum_str = malloc(4); sprintf(shnum_str, "%d", e_shnum);
file->properties[17] = (Property){"e_shnum", shnum_str, 0};
uint16_t e_shstrndx = (buf[51] << 8) | buf[50];
char* shstrndx_str = malloc(4); sprintf(shstrndx_str, "%d", e_shstrndx);
file->properties[18] = (Property){"e_shstrndx", shstrndx_str, 0};
char* size_str = malloc(12); sprintf(size_str, "%zu", file->data_size);
file->properties[19] = (Property){"FileSize", size_str, 0};
}
void print_properties(C32File* file) {
if (!file->properties) {
printf("No properties loaded. Call load_file() and parse_header() first.\n");
return;
}
printf("C32 (.ELF32) Properties:\n");
for (int i = 0; i < file->prop_count; i++) {
Property p = file->properties[i];
char* val = *(char**)p.value;
if (p.is_hex) {
printf("%s: %s\n", p.key, val);
} else {
printf("%s: %s\n", p.key, val);
}
}
}
int write_file(C32File* file, const char* output_filename) {
if (!file->data) return -1;
FILE* fp = fopen(output_filename, "wb");
if (!fp) return -1;
size_t written = fwrite(file->data, 1, file->data_size, fp);
fclose(fp);
if (written != file->data_size) return -1;
printf("Written to %s\n", output_filename);
return 0;
}
// Example usage in main:
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s <c32file>\n", argv[0]);
return 1;
}
C32File file = {0};
if (load_file(argv[1], &file) != 0) {
perror("Load failed");
return 1;
}
parse_header(&file);
print_properties(&file);
write_file(&file, "output.c32");
free_c32file(&file);
return 0;
}
This C implementation manually handles little-endian unpacking and memory management for robustness, printing directly to stdout.