-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a9a741e
Showing
6 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target | ||
binary |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"editor.tabSize": 3 | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "spine" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# See https://github.com/rust-lang/rustfmt/blob/master/Configurations.md | ||
# for actual up-to-date config options. | ||
edition = "2021" | ||
binop_separator = "Back" | ||
match_arm_blocks = false | ||
match_block_trailing_comma = true | ||
hard_tabs = true | ||
tab_spaces = 3 | ||
blank_lines_upper_bound = 1 | ||
use_field_init_shorthand = true | ||
max_width = 100 | ||
struct_lit_width = 65 | ||
struct_lit_single_line = true | ||
struct_variant_width = 50 | ||
wrap_comments = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
struct ByteBuffer { | ||
bytes: Vec<u8>, | ||
} | ||
impl ByteBuffer { | ||
fn new() -> ByteBuffer { | ||
ByteBuffer { bytes: vec![] } | ||
} | ||
fn into_bytes(self) -> Vec<u8> { | ||
self.bytes | ||
} | ||
|
||
fn make_sure_index_exists(&mut self, index: usize) { | ||
if self.bytes.len() <= index { | ||
self.bytes.resize(index + 1, 0); | ||
} | ||
} | ||
|
||
fn write_bytes(&mut self, index: usize, bytes: &[u8]) -> usize { | ||
self.make_sure_index_exists(index + bytes.len() - 1); | ||
self.bytes[index..].clone_from_slice(bytes); | ||
index + bytes.len() | ||
} | ||
} | ||
|
||
macro_rules! byte_buffer_fn_write { | ||
($func:ident, $type:ty) => { | ||
impl ByteBuffer { | ||
fn $func(&mut self, index: usize, value: $type) -> usize { | ||
self.make_sure_index_exists(index + std::mem::size_of::<$type>() - 1); | ||
self.bytes[index..].clone_from_slice(&value.to_le_bytes()); | ||
index + std::mem::size_of::<$type>() | ||
} | ||
} | ||
}; | ||
} | ||
byte_buffer_fn_write!(write_u8, u8); | ||
byte_buffer_fn_write!(write_u16, u16); | ||
byte_buffer_fn_write!(write_u32, u32); | ||
byte_buffer_fn_write!(write_u64, u64); | ||
|
||
struct Binary { | ||
entry_point_offset_in_code: usize, | ||
code_segment_address: usize, | ||
data_segment_address: usize, | ||
} | ||
|
||
impl Binary { | ||
fn new() -> Binary { | ||
Binary { | ||
entry_point_offset_in_code: 0, | ||
code_segment_address: 0x400000, | ||
data_segment_address: 0x600000, | ||
} | ||
} | ||
|
||
fn code_segment_binary_machine_code(&self) -> Vec<u8> { | ||
let mut buf = ByteBuffer::new(); | ||
let mut i = 0; | ||
// Exit(0) | ||
i = buf.write_bytes(i, &[0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00]); // movq $60, %rax | ||
i = buf.write_bytes(i, &[0x48, 0xc7, 0xc7, 0x00, 0x00, 0x00, 0x00]); // movq $0, %rdi | ||
buf.write_bytes(i, &[0x0f, 0x05]); // syscall | ||
buf.into_bytes() | ||
} | ||
|
||
fn code_size_in_binary(&self) -> usize { | ||
self.code_segment_binary_machine_code().len() | ||
} | ||
|
||
fn data_segment_binary_content(&self) -> Vec<u8> { | ||
ByteBuffer::new().into_bytes() | ||
} | ||
|
||
fn data_size_in_binary(&self) -> usize { | ||
self.data_segment_binary_content().len() | ||
} | ||
|
||
fn to_binary(&self) -> ByteBuffer { | ||
let mut buf = ByteBuffer::new(); | ||
let mut i = 0; | ||
|
||
// See https://en.wikipedia.org/wiki/Executable_and_Linkable_Format | ||
// Also see https://github.com/vishen/go-x64-executable/blob/master/main.go | ||
// Beware! This is a certified ELF moment! | ||
// 64 bits | ||
|
||
const ELF_HEADER_SIZE: usize = 0x40; | ||
const PROGRAM_HEADER_TABLE_ENTRY_SIZE: usize = 0x38; | ||
const PROGRAM_HEADER_TABLE_LENGTH: usize = 2; // Code and data are enough for us | ||
const CODE_OFFSET_IN_BINARY: usize = ELF_HEADER_SIZE + PROGRAM_HEADER_TABLE_ENTRY_SIZE * 2; | ||
|
||
let entry_point_address = | ||
CODE_OFFSET_IN_BINARY + self.entry_point_offset_in_code + self.code_segment_address; | ||
|
||
// ELF header | ||
i = buf.write_bytes(i, &[0x7f, b'E', b'L', b'F']); // ELF magic number | ||
i = buf.write_u8(i, 2); // 1 -> 32-bits, 2 -> 64-bits | ||
i = buf.write_u8(i, 1); // 1 -> little endian, 2 -> big endian | ||
i = buf.write_u8(i, 1); // ELF format version (still 1 in 2023) | ||
i = buf.write_u8(i, 3); // Target Linux | ||
i = buf.write_u8(i, 0); // Required dynamic linker version (we don't care) | ||
i = buf.write_bytes(i, &[0, 0, 0, 0, 0, 0, 0]); // Padding | ||
i = buf.write_u16(i, 2); // This is an executable | ||
i = buf.write_u16(i, 0x3e); // Target x86-64 | ||
i = buf.write_u32(i, 1); // ELF format version (again??) | ||
i = buf.write_u64(i, entry_point_address as u64); // Entry point address | ||
i = buf.write_u64(i, ELF_HEADER_SIZE as u64); // Program header table offset in binary | ||
i = buf.write_u64(i, 0); // Section header table offset in binary (we don't have one) | ||
i = buf.write_u32(i, 0); // Target architecture dependent flags | ||
i = buf.write_u16(i, ELF_HEADER_SIZE as u16); // Size of this header | ||
i = buf.write_u16(i, PROGRAM_HEADER_TABLE_ENTRY_SIZE as u16); // Size of a prog entry | ||
i = buf.write_u16(i, PROGRAM_HEADER_TABLE_LENGTH as u16); // Number of entries in program header table | ||
i = buf.write_u16(i, 0); // Size of a section header table entry (we don't have one) | ||
i = buf.write_u16(i, 0); // Number of entries in section header table | ||
i = buf.write_u16(i, 0); // Index of the section header table entry that has the section names | ||
assert_eq!(i, ELF_HEADER_SIZE); | ||
|
||
// Program header table | ||
let mut program_header_table_entry_count = 0; | ||
{ | ||
// Code segment | ||
let bin_offset = CODE_OFFSET_IN_BINARY as u64; | ||
let addr_offset = self.code_segment_address as u64; | ||
let size = self.code_size_in_binary() as u64; | ||
|
||
i = buf.write_u32(i, 1); // Loadable segment | ||
i = buf.write_u32( | ||
i, | ||
(1 << 0/*Readable*/) | (1 << 1/*Writable*/) | (1 << 2/*Executable*/), | ||
); // Flags | ||
i = buf.write_u64(i, bin_offset); // Offset in binary | ||
i = buf.write_u64(i, addr_offset + bin_offset); // Address in virtual memory | ||
i = buf.write_u64(i, addr_offset + bin_offset); // Address in physical memory (wtf) | ||
i = buf.write_u64(i, size); // Size in binary | ||
i = buf.write_u64(i, size); // Size in memory | ||
i = buf.write_u64(i, 0); // Alignment (0 means no alignment) | ||
assert_eq!(i, ELF_HEADER_SIZE + PROGRAM_HEADER_TABLE_ENTRY_SIZE); | ||
} | ||
program_header_table_entry_count += 1; | ||
{ | ||
// Data segment | ||
let bin_offset = (CODE_OFFSET_IN_BINARY + self.code_size_in_binary()) as u64; | ||
let addr_offset = self.data_segment_address as u64; | ||
let size = self.data_size_in_binary() as u64; | ||
|
||
i = buf.write_u32(i, 1); // Loadable segment | ||
i = buf.write_u32( | ||
i, | ||
(1 << 0/*Readable*/) | (1 << 1/*Writable*/) | (1 << 2/*Executable*/), | ||
); // Flags | ||
i = buf.write_u64(i, bin_offset); // Offset in binary | ||
i = buf.write_u64(i, addr_offset + bin_offset); // Address in virtual memory | ||
i = buf.write_u64(i, addr_offset + bin_offset); // Address in physical memory (wtf) | ||
i = buf.write_u64(i, size); // Size in binary | ||
i = buf.write_u64(i, size); // Size in memory | ||
i = buf.write_u64(i, 0); // Alignment (0 means no alignment) | ||
assert_eq!(i, ELF_HEADER_SIZE + PROGRAM_HEADER_TABLE_ENTRY_SIZE * 2); | ||
} | ||
program_header_table_entry_count += 1; | ||
assert_eq!( | ||
program_header_table_entry_count, | ||
PROGRAM_HEADER_TABLE_LENGTH | ||
); | ||
|
||
// Code | ||
i = buf.write_bytes(i, self.code_segment_binary_machine_code().as_slice()); | ||
|
||
// Data | ||
i = buf.write_bytes(i, self.data_segment_binary_content().as_slice()); | ||
|
||
assert_eq!(i, buf.bytes.len()); | ||
|
||
buf | ||
} | ||
} | ||
|
||
fn main() { | ||
let bin = Binary::new(); | ||
std::fs::write("binary", bin.to_binary().bytes).unwrap(); | ||
} |