Executable/library format for SLUSYS ==================================== Minimum needed data: - section with rodata and code - rodata/code break (used to mark pages with only rodata as non-executable) (or to split code/data for harvard architectures) - should code or data come first - string table (with names, versions, etc.) - imported ABIs: - length - array: - hash, name-id & version-id of ABI - exported ABIs (this includes "main" for executables): - length - array: - hash, name-id & version-id of ABI - array: - index of first function symbol - index of first data symbol - number of function symbols - number of data symbols - function symbol table - length - array: - relative pointer to function entry point - data symbol table - lengh - array: - relative pointer to function entry point Design issues to solve: - How to call into external libraries? - Relocate call addrs? - Global Offset Table + extra register? - Global Offset Table loaded at addrs just below the start addr? - (no?) this prevents placing libraries "back to back" in the same page - Nice to haves - compressed pages Format ------ Format structure: - Fixed-Size Header - Variable-Size Header - Array of imported APIs, hashes - Array of exported APIs, hashes - Compression offset table - LZ4 (or zstd?) compressed data (in 4 KiB chunks, but first one is smaller, such that each chunk ends on a 4 KiB boundary): - Array of exported APIs, number of functions/data addresses - Array of exported function addresses (XXX do we need to know the calling convention?) - Array of exported data addresses - (Optional alignment) - Code - (Optional alignment) - Read-Only Data - String table (byte array) - (Alignment to length_t) - Array of imported APIs, corresponding name & version (for informational purposes) - Array of exported APIs, corresponding name & version (for informational purposes) (- File hash or signature(s)?) Fixed-Size Header: - "SLUEXEC\0" - uint8: format flags: .... ...X : 0=Little endian, 1=Big endian .... .XX. : Length / Relative Virtual Address size in file: 00=16, 01=32, 10=64, 11=reserved (note: this has nothing to do with the arch bitness) other bits are reserved and must be 0 - uint8: requested CPU/platform features: .... ...1 : floating point (if a task does not use fp, then context switching can be optimized) .... ..1. : trapping (XXX but can't this always happen? e.g. stack overflow) .... .1.. : handling traps (XXX) .... 1... : managing owned data (XXX) other bits are reserved and must be 0 - uint16: CPU type (use the same as for the ELF format? should work if there is a common CPU type number "namespace" for ELF32 and ELF64) - uint16: reserved, must be 0 - uint16: length of variable-sized header Variable-Sized Header (missing fields are taken to be 0. fields marked with * are always required): - length_t: *size of compressed data - length_t: *number of imported APIs - length_t: *size of code - length_t: required alignment of code - length_t: size of ro-data - length_t: required alignment of ro-data - length_t: number of exported APIs - length_t: number of exported function addresses (XXX do we need to know the calling convention?) - length_t: number of exported data addresses - length_t: size of string table API entry, hash part: - 256 bit / 32 byte hash Compression offset entry: - uint16: uncompressed size of chunk - b (b = worst case minimum uncompressed size) (<= 0x7FFF: length below 32+b KiB is stored directly) (>= 0x8000: this and the following uint16 make up a 31-bit length) API entry, name/version part: - length_t: name id - length_t: version id Export API entry, number of functions/data addresses: - length_t: number of exported function addresses in this API - length_t: number of exported data addresses in this API Exported function address entry: - length_t: Offset from start of code Exported data address entry: - length_t: Offset from start of ro-data