summary refs log tree commit diff
path: root/src/structs.rs
blob: 850a47cd570d095dde7bbe502513bdff81eb7fbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
pub struct ElfIdent {
	/// Magic number - 0x7F, then 'ELF' in ASCII
	pub magic: [u8; 4],
	
	/// 1 = 32 bit, 2 = 64 bit
	pub class: Class,

	// 1 = little endian, 2 = big endian
	pub data: Endian,

	/// ELF header version
	pub version: u8,

	/// OS ABI - usually 0 for System V
	pub OSABI: OSABI,

	// This field is used to distinguish among incompatible versions of an ABI.
	pub ABIVersion: u8,

	/// Unused/padding
	pub _unused: [u8; 7],
}

impl std::fmt::Display for ElfIdent {
    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("  Magic:   ");
        
        for b in unsafe {*(std::ptr::addr_of!(self.magic) as *const [u8; 16])} {
            formatter.pad(&format!("{:02x?} ", b));
        }

		formatter.write_str("\n");

        formatter.write_fmt(format_args!("  Class:                             {:?}\n", self.class));
        formatter.write_fmt(format_args!("  Data:                              {}\n", self.data));
        formatter.write_fmt(format_args!("  Version:                           {} ({})\n", self.version, match self.version {
			0 => "invalid",
			1 => "current",
			2_u8..=u8::MAX => todo!(), // need to cover all cases
		}));
        formatter.write_fmt(format_args!("  OS/ABI:                            {}\n", self.OSABI));
        formatter.write_fmt(format_args!("  ABI Version:                       {}\n", self.ABIVersion))
    }
}

// https://man7.org/linux/man-pages/man5/elf.5.html
// https://wiki.osdev.org/ELF
// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
#[repr(C)]
#[derive(Debug)]
pub struct ElfHeader {
	pub ident: ElfIdent,

	/// 1 = relocatable, 2 = executable, 3 = shared, 4 = core
	pub r#type: Type,
	
	/// Instruction set - see table below
	pub machine: Machine,

	/// ELF Version
	pub version: u32,

	/// Program entry position (virtual)
	pub entry: u64,

	/// Program header table position (bytes into file)
	pub phoff: u64,

	/// Section header table position (bytes into file)
	pub shoff: u64,

	/// This member holds processor-specific flags associated with the file
	/// Currently, no flags have been defined.
	pub flags: u32,

	/// Header size
	pub ehsize: u16,

	/// Size of an entry in the program header table
	pub phentsize: u16,

	/// Number of entries in the program header table
	pub phnum: u16,

	/// Size of an entry in the section header table
	pub shentsize: u16,

	/// Number of entries in the section header table
	pub shnum: u16,
	
	/// Index in section header table with the section names
	pub shstrndx: u16,
}

impl std::fmt::Display for ElfHeader {
    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
		// yes, this is really what they do in readelf.c
        formatter.write_fmt(format_args!("{}", self.ident));
        formatter.write_fmt(format_args!("  Type:                              {}\n", self.r#type));
        formatter.write_fmt(format_args!("  Machine:                           {}\n", self.machine));
        formatter.write_fmt(format_args!("  Version:                           {:#02x?}\n", self.version));
        formatter.write_fmt(format_args!("  Entry point address:               {:#x?}\n", self.entry));
        formatter.write_fmt(format_args!("  Start of program headers:          {} (bytes into file)\n", self.phoff));
        formatter.write_fmt(format_args!("  Start of section headers:          {} (bytes into file)\n", self.shoff));
        formatter.write_fmt(format_args!("  Flags:                             {:#x?}\n", self.flags));
        formatter.write_fmt(format_args!("  Size of this header:               {} (bytes)\n", self.ehsize));
        formatter.write_fmt(format_args!("  Size of program headers:           {} (bytes)\n", self.phentsize));
        formatter.write_fmt(format_args!("  Number of program headers:         {}\n", self.phnum));
        formatter.write_fmt(format_args!("  Size of section headers:           {} (bytes)\n", self.shentsize));
        formatter.write_fmt(format_args!("  Number of section headers:         {}\n", self.shnum));
        formatter.write_fmt(format_args!("  Section header string table index: {}\n", self.shstrndx))
    }
}

#[derive(Debug, Copy, Clone)]
#[repr(u16)]
pub enum Type {
	None = 0,
	Rel = 1,
	Exec = 2,
	Dyn = 3,
	Core = 4,
}

impl std::fmt::Display for Type {
    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
			Type::None => formatter.write_str("NONE (None)"),
			Type::Rel => formatter.write_str("REL (Relocatable file)"),
			Type::Exec => formatter.write_str("EXEC (Executable file)"),
			Type::Dyn => formatter.write_str("DYN (Shared object file)"),
			Type::Core => formatter.write_str("CORE (Core file)"),
		}
    }
}

#[derive(Debug, Copy, Clone)]
#[repr(u16)]
pub enum Machine {
	// there are many many many more, im just gonna do x86
	// https://github.com/bminor/binutils-gdb/blob/master/binutils/readelf.c#L2746
	None = 1,
	X86_64 = 62,
}

impl std::fmt::Display for Machine {
    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
			Machine::None => formatter.write_str("None"),
			Machine::X86_64 => formatter.write_str("Advanced Micro Devices X86-64"),
		}
    }
}


#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum Class {
	ELF32 = 1,
	ELF64 = 2,
}

#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum OSABI {
	None = 0x00,
	SystemV = 0x01,
	// HPUX = 0x02,
	// Solaris = 0x03,
	// IRIX = 0x04,
	// FreeBSD = 0x05,
	// TRU64 = 0x06,
	// ARM = 0x07,
	Standalone = 0x08,
}

impl std::fmt::Display for OSABI {
    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
			OSABI::None => formatter.write_str("UNIX - System V"),
			OSABI::SystemV => formatter.write_str("UNIX - System V"),
			OSABI::Standalone => formatter.write_str("Standalone"),
		}
    }
}

#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum Endian {
	Little = 1,
	Big = 2,
}

impl std::fmt::Display for Endian {
    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
			Endian::Little => formatter.write_str("2's complement, little endian"),
			Endian::Big => formatter.write_str("1's complement, big endian"),
		}
    }
}