commit
eb1f02bb47
1 changed files with 693 additions and 0 deletions
693
src/kernel/elf.zig
Normal file
693
src/kernel/elf.zig
Normal file
|
@ -0,0 +1,693 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const Arch = std.Target.Cpu.Arch;
|
||||||
|
const Endian = builtin.Endian;
|
||||||
|
const log = std.log.scoped(.elf);
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
/// The data sizes that ELF files support. The int value corresponds to the value used in the file
|
||||||
|
pub const DataSize = enum(u8) {
|
||||||
|
/// 32-bit
|
||||||
|
ThirtyTwoBit = 1,
|
||||||
|
/// 64-bit
|
||||||
|
SixtyFourBit = 2,
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Get the number of bits taken by the data size
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN self: DataSize - The data size to get the number of bits for
|
||||||
|
///
|
||||||
|
/// Return: usize
|
||||||
|
/// The number of bits
|
||||||
|
///
|
||||||
|
pub fn toNumBits(self: @This()) usize {
|
||||||
|
return switch (self) {
|
||||||
|
.ThirtyTwoBit => 32,
|
||||||
|
.SixtyFourBit => 64,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The endiannesses supported by Elf files. The int value corresponds to the value used in the file
|
||||||
|
pub const Endianness = enum(u8) {
|
||||||
|
/// Little-endian
|
||||||
|
Little = 1,
|
||||||
|
/// Big-endian
|
||||||
|
Big = 2,
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Translate into the corresponding std lib Endian
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN self: Endianness - The endianness to translate
|
||||||
|
///
|
||||||
|
/// Return: Endian
|
||||||
|
/// The corresponding std lib Endian value
|
||||||
|
///
|
||||||
|
pub fn toEndian(self: @This()) Endian {
|
||||||
|
return switch (self) {
|
||||||
|
.Big => .Big,
|
||||||
|
.Little => .Little,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The type of the elf file. The int value corresponds to the value used in the file
|
||||||
|
pub const Type = enum(u16) {
|
||||||
|
/// Unused
|
||||||
|
None = 0,
|
||||||
|
/// Relocatable
|
||||||
|
Rel = 1,
|
||||||
|
/// Executable
|
||||||
|
Executable = 2,
|
||||||
|
/// Dynamic linking
|
||||||
|
Dynamic = 3,
|
||||||
|
/// Core dump
|
||||||
|
Core = 4,
|
||||||
|
/// OS-specific
|
||||||
|
LowOS = 0xFE00,
|
||||||
|
/// OS-specific
|
||||||
|
HighOS = 0xFEFF,
|
||||||
|
/// CPU-specific
|
||||||
|
LowCPU = 0xFF00,
|
||||||
|
/// CPU-specific
|
||||||
|
HighCPU = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The architectures supported by ELF
|
||||||
|
pub const Architecture = enum(u16) {
|
||||||
|
/// No specific instruction set
|
||||||
|
None = 0,
|
||||||
|
WE32100 = 1,
|
||||||
|
Sparc = 2,
|
||||||
|
x86 = 3,
|
||||||
|
Motoroloa_68k = 4,
|
||||||
|
Motorola_88k = 5,
|
||||||
|
Intel_MCU = 6,
|
||||||
|
Intel_80860 = 7,
|
||||||
|
MIPS = 8,
|
||||||
|
IBM_370 = 9,
|
||||||
|
MIPS_RS3000_LE = 10,
|
||||||
|
Reserved1 = 11,
|
||||||
|
Reserved2 = 12,
|
||||||
|
Reserved3 = 13,
|
||||||
|
HP_PA_RISC = 14,
|
||||||
|
Reserbed4 = 15,
|
||||||
|
Intel_80960 = 19,
|
||||||
|
PowerPC = 20,
|
||||||
|
PowerPC_64 = 21,
|
||||||
|
S390 = 22,
|
||||||
|
ARM = 0x28,
|
||||||
|
SuperH = 0x2A,
|
||||||
|
IA_64 = 0x32,
|
||||||
|
AMD_64 = 0x3E,
|
||||||
|
TMS = 0x8C,
|
||||||
|
Aarch64 = 0xB7,
|
||||||
|
RISC_V = 0xF3,
|
||||||
|
WDC_65C816 = 0x101,
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Translate to a std lib Arch
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN self: Architecture - The architecture to translate
|
||||||
|
///
|
||||||
|
/// Return: Arch
|
||||||
|
/// The corresponding std lib Arch
|
||||||
|
///
|
||||||
|
pub fn toArch(self: @This()) Error!Arch {
|
||||||
|
return switch (self) {
|
||||||
|
.None, .TMS, .WDC_65C816, .SuperH, .IA_64, .S390, .IBM_370, .Reserved1, .Reserved2, .WE32100, .Motorola_88k, .Motoroloa_68k, .Intel_MCU, .Intel_80860, .Intel_80960, .MIPS_RS3000_LE, .HP_PA_RISC, .Reserved3, .Reserbed4 => Error.UnknownArchitecture,
|
||||||
|
.Sparc => .sparc,
|
||||||
|
.x86 => .i386,
|
||||||
|
.MIPS => .mips,
|
||||||
|
.PowerPC => .powerpc,
|
||||||
|
.PowerPC_64 => .powerpc64,
|
||||||
|
.ARM => .arm,
|
||||||
|
.AMD_64 => .x86_64,
|
||||||
|
.Aarch64 => .aarch64,
|
||||||
|
.RISC_V => .riscv32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The header describing the entire ELF file
|
||||||
|
pub const Header = packed struct {
|
||||||
|
/// Should be 0x7f | 0x45 | 0x4C | 0x46
|
||||||
|
magic_number: u32,
|
||||||
|
/// The size of the fields in the header after file_type. Should be the size/s compatibile with the machine
|
||||||
|
data_size: DataSize,
|
||||||
|
/// The endianness of the fields in the header after file_type. Should be the endianness compatible with the machine
|
||||||
|
endianness: Endianness,
|
||||||
|
/// ELF version. Set to 1
|
||||||
|
version: u8,
|
||||||
|
/// The target OS' ABI. Normally set to 0 no matter the platform, so we ignore it
|
||||||
|
abi: u8,
|
||||||
|
/// The version of the above ABI. Mostly ignored but some toolchains put expected linker features here
|
||||||
|
abi_version: u8,
|
||||||
|
/// All zeroes
|
||||||
|
padding: u32,
|
||||||
|
padding2: u16,
|
||||||
|
padding3: u8,
|
||||||
|
/// The type of elf file
|
||||||
|
file_type: Type,
|
||||||
|
/// The target architecture. Should be compatible with the machine
|
||||||
|
architecture: Architecture,
|
||||||
|
/// Same as above version field
|
||||||
|
version2: u32,
|
||||||
|
/// Execution entry point
|
||||||
|
entry_address: usize,
|
||||||
|
/// Offset of the program header form the start of the elf file
|
||||||
|
program_header_offset: usize,
|
||||||
|
/// offset of the section header from the start of the elf file
|
||||||
|
section_header_offset: usize,
|
||||||
|
/// Architecture-dependent flags
|
||||||
|
flags: u32,
|
||||||
|
/// The size of this elf header in bytes. 64 for the 64-bit format and 52 for the 32-bit format
|
||||||
|
elf_header_size: u16,
|
||||||
|
/// The size of a program header table entry
|
||||||
|
program_header_entry_size: u16,
|
||||||
|
/// The number of entries in the program header table
|
||||||
|
program_header_entries: u16,
|
||||||
|
/// The size of a section header table entry
|
||||||
|
section_header_entry_size: u16,
|
||||||
|
/// The number of entries in the section header table
|
||||||
|
section_header_entries: u16,
|
||||||
|
/// The index into the section header table that contains the section names
|
||||||
|
section_name_index: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The type of a program header entry
|
||||||
|
pub const ProgramEntryType = enum(u32) {
|
||||||
|
Unused = 0,
|
||||||
|
/// Should be loaded into memory
|
||||||
|
Loadable = 1,
|
||||||
|
/// Dynamic linking info
|
||||||
|
Dynamic = 2,
|
||||||
|
/// Interpreter info
|
||||||
|
InterpreterInfo = 3,
|
||||||
|
/// Extra information used depending on the elf type
|
||||||
|
Auxiliary = 4,
|
||||||
|
Reserved = 5,
|
||||||
|
/// Entry containing the program header table
|
||||||
|
ProgramHeader = 6,
|
||||||
|
/// Info for thread-local storage
|
||||||
|
ThreadLocalStorage = 7,
|
||||||
|
/// Gnu toolchain-specific information
|
||||||
|
GnuStack = 0x6474E551,
|
||||||
|
/// OS-specific info
|
||||||
|
LowOS = 0x60000000,
|
||||||
|
/// OS-specific info
|
||||||
|
HighOS = 0x6FFFFFFF,
|
||||||
|
/// CPU-specific info
|
||||||
|
LowCPU = 0x70000000,
|
||||||
|
/// CPU-specific info
|
||||||
|
HighCPU = 0x7FFFFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The header desribing the program entries
|
||||||
|
pub const ProgramHeader = packed struct {
|
||||||
|
/// The type of the entry
|
||||||
|
entry_type: ProgramEntryType,
|
||||||
|
/// Entry type-specific flags for 64-bit ELF files
|
||||||
|
flags_64bit: if (@bitSizeOf(usize) == 32) u0 else u32,
|
||||||
|
/// Offset of the entry within the ELF file
|
||||||
|
offset: usize,
|
||||||
|
/// The virtual address associated with the entry
|
||||||
|
virtual_address: usize,
|
||||||
|
/// The virtual address associated with the entry, if applicable
|
||||||
|
physical_address: usize,
|
||||||
|
/// Size of the entry in the file
|
||||||
|
file_size: usize,
|
||||||
|
/// Size of the entry in memory
|
||||||
|
mem_size: usize,
|
||||||
|
/// Entry type-specific flags for 32-bit ELF files
|
||||||
|
flags_32bit: if (@bitSizeOf(usize) == 64) u0 else u32,
|
||||||
|
/// Alignment of the entry
|
||||||
|
alignment: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The type of section
|
||||||
|
pub const SectionType = enum(u32) {
|
||||||
|
Unused = 0,
|
||||||
|
/// Executable code
|
||||||
|
ProgramData = 1,
|
||||||
|
/// The symbol table
|
||||||
|
SymbolTable = 2,
|
||||||
|
/// The table containing all strings used by other sections
|
||||||
|
StringTable = 3,
|
||||||
|
/// Relocation data with addends
|
||||||
|
RelocationWithAddends = 4,
|
||||||
|
/// The symbol has table
|
||||||
|
SymbolHashTable = 5,
|
||||||
|
/// Dynamic linking info
|
||||||
|
Dynamic = 6,
|
||||||
|
/// Extra information
|
||||||
|
Auxiliary = 7,
|
||||||
|
/// Space within the program, normally used to store data
|
||||||
|
ProgramSpace = 8,
|
||||||
|
/// Relocation data without addends
|
||||||
|
RelocationWithoutAddends = 9,
|
||||||
|
Reserved = 10,
|
||||||
|
/// The dynamic linker symbol table
|
||||||
|
DynamicSymbolTable = 11,
|
||||||
|
/// List of constructors
|
||||||
|
Constructors = 14,
|
||||||
|
/// List of destructors
|
||||||
|
Destructors = 15,
|
||||||
|
/// List of pre-contructors
|
||||||
|
PreConstructors = 16,
|
||||||
|
/// A group of sections
|
||||||
|
SectionGroup = 17,
|
||||||
|
/// Extended section indices
|
||||||
|
ExtendedSectionIndices = 18,
|
||||||
|
/// The number of defined types
|
||||||
|
NumberDefinedType = 19,
|
||||||
|
/// OS-specific
|
||||||
|
LowOS = 0x60000000,
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Check if the section has an associated chunk of data in the ELF file
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN self: SectionType - The section type
|
||||||
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// Whether the section type has associated data
|
||||||
|
///
|
||||||
|
pub fn hasData(self: @This()) bool {
|
||||||
|
return switch (self) {
|
||||||
|
.Unused, .ProgramData, .ProgramSpace, .Reserved => false,
|
||||||
|
else => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
std.debug.assert(@sizeOf(SectionHeader) == if (@bitSizeOf(usize) == 32) 0x28 else 0x40);
|
||||||
|
std.debug.assert(@sizeOf(Header) == if (@bitSizeOf(usize) == 32) 0x32 else 0x40);
|
||||||
|
std.debug.assert(@sizeOf(ProgramHeader) == if (@bitSizeOf(usize) == 32) 0x20 else 0x38);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The section is writable
|
||||||
|
pub const SECTION_WRITABLE = 1;
|
||||||
|
/// The section occupies memory during execution
|
||||||
|
pub const SECTION_ALLOCATABLE = 2;
|
||||||
|
/// The section is executable
|
||||||
|
pub const SECTION_EXECUTABLE = 4;
|
||||||
|
/// The section may be merged
|
||||||
|
pub const SECTION_MERGED = 16;
|
||||||
|
/// The section contains strings
|
||||||
|
pub const SECTION_HAS_STRINGS = 32;
|
||||||
|
/// Contains a SHT index
|
||||||
|
pub const SECTION_INFO_LINK = 64;
|
||||||
|
/// Preserve the section order after combining
|
||||||
|
pub const SECTION_PRESERVE_ORDER = 128;
|
||||||
|
/// Non-standard OS-specific handling is required
|
||||||
|
pub const SECTION_OS_NON_STANDARD = 256;
|
||||||
|
/// Member of a group
|
||||||
|
pub const SECTION_GROUP = 512;
|
||||||
|
/// The section contains thread-local data
|
||||||
|
pub const SECTION_THREAD_LOCAL_DATA = 1024;
|
||||||
|
/// OS-specific
|
||||||
|
pub const SECTION_OS_MASK = 0x0FF00000;
|
||||||
|
/// CPU-specific
|
||||||
|
pub const SECTION_CPU_MASK = 0xF0000000;
|
||||||
|
|
||||||
|
/// The header for an ELF section
|
||||||
|
pub const SectionHeader = packed struct {
|
||||||
|
/// Offset into the string table of the section's name
|
||||||
|
name_offset: u32,
|
||||||
|
/// The section's type
|
||||||
|
section_type: SectionType,
|
||||||
|
/// Flags for this section
|
||||||
|
flags: usize,
|
||||||
|
/// The virtual address at which this section should be loaded (if it is loadable)
|
||||||
|
virtual_address: usize,
|
||||||
|
/// Offset of the section's data into the file
|
||||||
|
offset: usize,
|
||||||
|
/// The size of the section's data
|
||||||
|
size: usize,
|
||||||
|
/// An associated section. Usage depends on the section type
|
||||||
|
linked_section_idx: u32,
|
||||||
|
/// Extra info. Usage depends on the section type
|
||||||
|
info: u32,
|
||||||
|
/// The section's alignment
|
||||||
|
alignment: usize,
|
||||||
|
/// The size of each entry within this section, for sections that contain sub-entries
|
||||||
|
entry_size: usize,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Find the name of this section from the ELF's string table.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN self: SectionHeader - The header to get the name for
|
||||||
|
/// IN elf: Elf - The elf file
|
||||||
|
///
|
||||||
|
/// Return: []const u8
|
||||||
|
/// The name of the section
|
||||||
|
///
|
||||||
|
pub fn getName(self: Self, elf: Elf) []const u8 {
|
||||||
|
// section_name_index has already been checked so will exist
|
||||||
|
const string_table = elf.section_data[elf.header.section_name_index] orelse unreachable;
|
||||||
|
const str = @ptrCast([*]const u8, string_table.ptr + self.name_offset);
|
||||||
|
var len: usize = 0;
|
||||||
|
while (str[len] != 0) : (len += 1) {}
|
||||||
|
const name = str[0..len];
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A loaded ELF file
|
||||||
|
pub const Elf = struct {
|
||||||
|
/// The ELF header that describes the entire file
|
||||||
|
header: Header,
|
||||||
|
/// The program entry headers
|
||||||
|
program_headers: []ProgramHeader,
|
||||||
|
/// The section headers
|
||||||
|
section_headers: []SectionHeader,
|
||||||
|
/// The data associated with each section, or null if a section doesn't have a data area
|
||||||
|
section_data: []?[]const u8,
|
||||||
|
/// The allocator used
|
||||||
|
allocator: *std.mem.Allocator,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Load and initialise from a data stream for a specific architecture
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN elf_data: []const u8 - The data stream to load the elf information from
|
||||||
|
/// IN arch: Arch - The intended architecture to load for
|
||||||
|
/// IN allocator: Allocator - The allocator to use when needing memory
|
||||||
|
///
|
||||||
|
/// Return: Elf
|
||||||
|
/// The loaded ELF file
|
||||||
|
///
|
||||||
|
/// Error: Allocator.Error || Error
|
||||||
|
/// Allocator.Error - There wasn't enough memory free to allocate the required state
|
||||||
|
/// Error.InvalidMagicNumber - The ELF file magic number wasn't as expected
|
||||||
|
/// Error.InvalidArchitecture - The ELF file wasn't built for the expected architecture
|
||||||
|
/// Error.InvalidDataSize - The ELF file wasn't built for the data size supported by the given architecture
|
||||||
|
/// Error.InvalidEndianness - The ELF file wasn't built with the endianness supported by the given architecture
|
||||||
|
/// Error.WrongStringTableIndex - The string table index in the header does not point to a StringTable section
|
||||||
|
///
|
||||||
|
pub fn init(elf_data: []const u8, arch: Arch, allocator: *std.mem.Allocator) (std.mem.Allocator.Error || Error)!Self {
|
||||||
|
const header = std.mem.bytesToValue(Header, elf_data[0..@sizeOf(Header)]);
|
||||||
|
if (header.magic_number != 0x464C457F) {
|
||||||
|
return Error.InvalidMagicNumber;
|
||||||
|
}
|
||||||
|
if ((try header.architecture.toArch()) != arch) {
|
||||||
|
return Error.InvalidArchitecture;
|
||||||
|
}
|
||||||
|
if (header.data_size.toNumBits() != @bitSizeOf(usize)) {
|
||||||
|
return Error.InvalidDataSize;
|
||||||
|
}
|
||||||
|
if (header.endianness.toEndian() != arch.endian()) {
|
||||||
|
return Error.InvalidEndianness;
|
||||||
|
}
|
||||||
|
if (header.section_name_index >= header.section_header_entries)
|
||||||
|
return Error.WrongStringTableIndex;
|
||||||
|
|
||||||
|
var program_segments = try allocator.alloc(ProgramHeader, header.program_header_entries);
|
||||||
|
errdefer allocator.free(program_segments);
|
||||||
|
var seg_offset = header.program_header_offset;
|
||||||
|
for (program_segments) |*segment| {
|
||||||
|
segment.* = @ptrCast(*const ProgramHeader, elf_data.ptr + seg_offset).*;
|
||||||
|
seg_offset += header.program_header_entry_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
var section_headers = try allocator.alloc(SectionHeader, header.section_header_entries);
|
||||||
|
errdefer allocator.free(section_headers);
|
||||||
|
var section_data = try allocator.alloc(?[]const u8, header.section_header_entries);
|
||||||
|
errdefer allocator.free(section_data);
|
||||||
|
var sec_offset = header.section_header_offset;
|
||||||
|
for (section_headers) |*section, i| {
|
||||||
|
section.* = std.mem.bytesToValue(SectionHeader, (elf_data.ptr + sec_offset)[0..@sizeOf(SectionHeader)]);
|
||||||
|
section_data[i] = if (section.section_type.hasData()) elf_data[section.offset .. section.offset + section.size] else null;
|
||||||
|
sec_offset += header.section_header_entry_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section_headers[header.section_name_index].section_type != .StringTable) {
|
||||||
|
return Error.WrongStringTableIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Elf{
|
||||||
|
.header = header,
|
||||||
|
.program_headers = program_segments,
|
||||||
|
.section_headers = section_headers,
|
||||||
|
.section_data = section_data,
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *const Self) void {
|
||||||
|
self.allocator.free(self.section_data);
|
||||||
|
self.allocator.free(self.section_headers);
|
||||||
|
self.allocator.free(self.program_headers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Error = error{
|
||||||
|
UnknownArchitecture,
|
||||||
|
InvalidArchitecture,
|
||||||
|
InvalidDataSize,
|
||||||
|
InvalidMagicNumber,
|
||||||
|
InvalidEndianness,
|
||||||
|
WrongStringTableIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn testSetHeader(data: []u8, header: Header) void {
|
||||||
|
std.mem.copy(u8, data[0..@sizeOf(Header)], @ptrCast([*]const u8, &header)[0..@sizeOf(Header)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testSetSection(data: []u8, header: SectionHeader, idx: usize) void {
|
||||||
|
const offset = @sizeOf(Header) + @sizeOf(SectionHeader) * idx;
|
||||||
|
std.mem.copy(u8, data[offset .. offset + @sizeOf(SectionHeader)], @ptrCast([*]const u8, &header)[0..@sizeOf(SectionHeader)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testInitData(section_name: []const u8, string_section_name: []const u8, file_type: Type, entry_address: usize, flags: u32, section_flags: u32, strings_flags: u32, section_address: usize, strings_address: usize) []u8 {
|
||||||
|
const is_32_bit = @bitSizeOf(usize) == 32;
|
||||||
|
const header_size = if (is_32_bit) 0x34 else 0x40;
|
||||||
|
const p_header_size = if (is_32_bit) 0x20 else 0x38;
|
||||||
|
const s_header_size = if (is_32_bit) 0x28 else 0x40;
|
||||||
|
const data_size = header_size + s_header_size + s_header_size + section_name.len + 1 + string_section_name.len + 1;
|
||||||
|
var data = testing.allocator.alloc(u8, data_size) catch unreachable;
|
||||||
|
|
||||||
|
var header = Header{
|
||||||
|
.magic_number = 0x464C457F,
|
||||||
|
.data_size = switch (@bitSizeOf(usize)) {
|
||||||
|
32 => .ThirtyTwoBit,
|
||||||
|
64 => .SixtyFourBit,
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
.endianness = switch (builtin.arch.endian()) {
|
||||||
|
.Big => .Big,
|
||||||
|
.Little => .Little,
|
||||||
|
},
|
||||||
|
.version = 1,
|
||||||
|
.abi = 0,
|
||||||
|
.abi_version = 0,
|
||||||
|
.padding = 0,
|
||||||
|
.padding2 = 0,
|
||||||
|
.padding3 = 0,
|
||||||
|
.file_type = file_type,
|
||||||
|
.architecture = .AMD_64,
|
||||||
|
.version2 = 1,
|
||||||
|
.entry_address = entry_address,
|
||||||
|
.program_header_offset = undefined,
|
||||||
|
.section_header_offset = header_size,
|
||||||
|
.flags = flags,
|
||||||
|
.elf_header_size = header_size,
|
||||||
|
.program_header_entry_size = p_header_size,
|
||||||
|
.program_header_entries = 0,
|
||||||
|
.section_header_entry_size = s_header_size,
|
||||||
|
.section_header_entries = 2,
|
||||||
|
.section_name_index = 1,
|
||||||
|
};
|
||||||
|
var data_offset: usize = 0;
|
||||||
|
testSetHeader(data, header);
|
||||||
|
data_offset += header_size;
|
||||||
|
|
||||||
|
var section_header = SectionHeader{
|
||||||
|
.name_offset = 0,
|
||||||
|
.section_type = .ProgramData,
|
||||||
|
.flags = section_flags,
|
||||||
|
.virtual_address = section_address,
|
||||||
|
.offset = 0,
|
||||||
|
.size = 0,
|
||||||
|
.linked_section_idx = undefined,
|
||||||
|
.info = undefined,
|
||||||
|
.alignment = 1,
|
||||||
|
.entry_size = undefined,
|
||||||
|
};
|
||||||
|
testSetSection(data, section_header, 0);
|
||||||
|
data_offset += s_header_size;
|
||||||
|
|
||||||
|
var string_section_header = SectionHeader{
|
||||||
|
.name_offset = @intCast(u32, section_name.len) + 1,
|
||||||
|
.section_type = .StringTable,
|
||||||
|
.flags = strings_flags,
|
||||||
|
.virtual_address = strings_address,
|
||||||
|
.offset = data_offset + s_header_size,
|
||||||
|
.size = section_name.len + 1 + string_section_name.len + 1,
|
||||||
|
.linked_section_idx = undefined,
|
||||||
|
.info = undefined,
|
||||||
|
.alignment = 1,
|
||||||
|
.entry_size = undefined,
|
||||||
|
};
|
||||||
|
testSetSection(data, string_section_header, 1);
|
||||||
|
data_offset += s_header_size;
|
||||||
|
|
||||||
|
std.mem.copy(u8, data[data_offset .. data_offset + section_name.len], section_name);
|
||||||
|
data_offset += section_name.len;
|
||||||
|
data[data_offset] = 0;
|
||||||
|
data_offset += 1;
|
||||||
|
|
||||||
|
std.mem.copy(u8, data[data_offset .. data_offset + string_section_name.len], string_section_name);
|
||||||
|
data_offset += string_section_name.len;
|
||||||
|
data[data_offset] = 0;
|
||||||
|
data_offset += 1;
|
||||||
|
return data[0..data_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
test "init" {
|
||||||
|
const section_name = "some_section";
|
||||||
|
const string_section_name = "strings";
|
||||||
|
const is_32_bit = @bitSizeOf(usize) == 32;
|
||||||
|
var data = testInitData(section_name, string_section_name, .Executable, 0, undefined, 123, 789, 456, 012);
|
||||||
|
defer testing.allocator.free(data);
|
||||||
|
const elf = try Elf.init(data, builtin.arch, testing.allocator);
|
||||||
|
defer elf.deinit();
|
||||||
|
|
||||||
|
testing.expectEqual(elf.header.data_size, if (is_32_bit) .ThirtyTwoBit else .SixtyFourBit);
|
||||||
|
testing.expectEqual(elf.header.file_type, .Executable);
|
||||||
|
testing.expectEqual(elf.header.architecture, .AMD_64);
|
||||||
|
testing.expectEqual(elf.header.entry_address, 0);
|
||||||
|
testing.expectEqual(elf.header.flags, undefined);
|
||||||
|
testing.expectEqual(elf.header.section_name_index, 1);
|
||||||
|
|
||||||
|
testing.expectEqual(elf.program_headers.len, 0);
|
||||||
|
|
||||||
|
testing.expectEqual(elf.section_headers.len, 2);
|
||||||
|
const section_one = elf.section_headers[0];
|
||||||
|
testing.expectEqual(@as(u32, 0), section_one.name_offset);
|
||||||
|
testing.expectEqual(SectionType.ProgramData, section_one.section_type);
|
||||||
|
testing.expectEqual(@as(usize, 123), section_one.flags);
|
||||||
|
testing.expectEqual(@as(usize, 456), section_one.virtual_address);
|
||||||
|
|
||||||
|
const section_two = elf.section_headers[1];
|
||||||
|
testing.expectEqual(section_name.len + 1, section_two.name_offset);
|
||||||
|
testing.expectEqual(SectionType.StringTable, section_two.section_type);
|
||||||
|
testing.expectEqual(@as(usize, 789), section_two.flags);
|
||||||
|
testing.expectEqual(@as(usize, 012), section_two.virtual_address);
|
||||||
|
|
||||||
|
testing.expectEqual(@as(usize, 2), elf.section_data.len);
|
||||||
|
testing.expectEqual(@as(?[]const u8, null), elf.section_data[0]);
|
||||||
|
for ("some_section" ++ [_]u8{0} ++ "strings" ++ [_]u8{0}) |char, i| {
|
||||||
|
testing.expectEqual(char, elf.section_data[1].?[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the string section having the wrong type
|
||||||
|
var section_header = elf.section_headers[1];
|
||||||
|
section_header.section_type = .ProgramData;
|
||||||
|
testSetSection(data, section_header, 1);
|
||||||
|
testing.expectError(Error.WrongStringTableIndex, Elf.init(data, builtin.arch, testing.allocator));
|
||||||
|
testSetSection(data, elf.section_headers[1], 1);
|
||||||
|
|
||||||
|
// Test the section_name_index being out of bounds
|
||||||
|
var header = elf.header;
|
||||||
|
header.section_name_index = 3;
|
||||||
|
testSetHeader(data, header);
|
||||||
|
testing.expectError(Error.WrongStringTableIndex, Elf.init(data, builtin.arch, testing.allocator));
|
||||||
|
|
||||||
|
// Test incorrect endianness
|
||||||
|
header = elf.header;
|
||||||
|
header.endianness = switch (builtin.arch.endian()) {
|
||||||
|
.Big => .Little,
|
||||||
|
.Little => .Big,
|
||||||
|
};
|
||||||
|
testSetHeader(data, header);
|
||||||
|
testing.expectError(Error.InvalidEndianness, Elf.init(data, builtin.arch, testing.allocator));
|
||||||
|
|
||||||
|
// Test invalid data size
|
||||||
|
header.data_size = switch (@bitSizeOf(usize)) {
|
||||||
|
32 => .SixtyFourBit,
|
||||||
|
else => .ThirtyTwoBit,
|
||||||
|
};
|
||||||
|
testSetHeader(data, header);
|
||||||
|
testing.expectError(Error.InvalidDataSize, Elf.init(data, builtin.arch, testing.allocator));
|
||||||
|
|
||||||
|
// Test invalid architecture
|
||||||
|
header.architecture = switch (builtin.arch) {
|
||||||
|
.x86_64 => .Aarch64,
|
||||||
|
else => .AMD_64,
|
||||||
|
};
|
||||||
|
testSetHeader(data, header);
|
||||||
|
testing.expectError(Error.InvalidArchitecture, Elf.init(data, builtin.arch, testing.allocator));
|
||||||
|
|
||||||
|
// Test incorrect magic number
|
||||||
|
header.magic_number = 123;
|
||||||
|
testSetHeader(data, header);
|
||||||
|
testing.expectError(Error.InvalidMagicNumber, Elf.init(data, builtin.arch, testing.allocator));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "getName" {
|
||||||
|
// The entire ELF test data. The header, program header, two section headers and the section name (with the null terminator)
|
||||||
|
var section_name = "some_section";
|
||||||
|
var string_section_name = "strings";
|
||||||
|
const data = testInitData(section_name, string_section_name, .Executable, 0, undefined, undefined, undefined, undefined, undefined);
|
||||||
|
defer testing.allocator.free(data);
|
||||||
|
const elf = try Elf.init(data, builtin.arch, testing.allocator);
|
||||||
|
defer elf.deinit();
|
||||||
|
testing.expectEqualSlices(u8, elf.section_headers[0].getName(elf), section_name);
|
||||||
|
testing.expectEqualSlices(u8, elf.section_headers[1].getName(elf), string_section_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "toNumBits" {
|
||||||
|
testing.expectEqual(DataSize.ThirtyTwoBit.toNumBits(), 32);
|
||||||
|
testing.expectEqual(DataSize.SixtyFourBit.toNumBits(), 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "toEndian" {
|
||||||
|
testing.expectEqual(Endianness.Little.toEndian(), Endian.Little);
|
||||||
|
testing.expectEqual(Endianness.Big.toEndian(), Endian.Big);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "toArch" {
|
||||||
|
const known_architectures = [_]Architecture{ .Sparc, .x86, .MIPS, .PowerPC, .PowerPC_64, .ARM, .AMD_64, .Aarch64, .RISC_V };
|
||||||
|
const known_archs = [known_architectures.len]Arch{ .sparc, .i386, .mips, .powerpc, .powerpc64, .arm, .x86_64, .aarch64, .riscv32 };
|
||||||
|
|
||||||
|
inline for (@typeInfo(Architecture).Enum.fields) |field| {
|
||||||
|
const architecture = @field(Architecture, field.name);
|
||||||
|
|
||||||
|
const is_known = inline for (known_architectures) |known_architecture, i| {
|
||||||
|
if (known_architecture == architecture) {
|
||||||
|
testing.expectEqual(architecture.toArch(), known_archs[i]);
|
||||||
|
break true;
|
||||||
|
}
|
||||||
|
} else false;
|
||||||
|
|
||||||
|
if (!is_known) {
|
||||||
|
testing.expectError(Error.UnknownArchitecture, architecture.toArch());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "hasData" {
|
||||||
|
const no_data = [_]SectionType{ .Unused, .ProgramSpace, .Reserved, .ProgramData };
|
||||||
|
|
||||||
|
inline for (@typeInfo(SectionType).Enum.fields) |field| {
|
||||||
|
const sec_type = @field(SectionType, field.name);
|
||||||
|
const has_data = inline for (no_data) |no_data_type| {
|
||||||
|
if (sec_type == no_data_type) {
|
||||||
|
break false;
|
||||||
|
}
|
||||||
|
} else true;
|
||||||
|
|
||||||
|
testing.expectEqual(has_data, sec_type.hasData());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue