├── .gitignore ├── Code.zig ├── README.md ├── bdiff.sh ├── bf ├── dv.bf ├── fib.bf ├── hello.bf ├── sq.bf ├── test.bf └── test2.bf ├── build.zig ├── elf.asm └── main.zig /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .gdb_history 3 | .idea 4 | *.log 5 | tmp/ 6 | code 7 | tiny 8 | *.o 9 | hello 10 | tiny 11 | a.out 12 | 13 | 14 | zig-cache/ 15 | zig-out/ 16 | hello.asm 17 | fib 18 | -------------------------------------------------------------------------------- /Code.zig: -------------------------------------------------------------------------------- 1 | const main = @import("main.zig"); 2 | const cast = main.cast; 3 | 4 | const std = @import("std"); 5 | 6 | const Code = @This(); 7 | 8 | output: []const u8, 9 | 10 | pub fn deinit(self: Code, gpa: *std.mem.Allocator) void { 11 | gpa.free(self.output); 12 | } 13 | 14 | /// generate the assembly from the brainfuck input 15 | /// ASSUMTIONS: 16 | /// at the start of the code running, 17 | /// dat_idx is r10, 18 | pub fn gen(gpa: *std.mem.Allocator, bfsrc: []const u8) !Code { 19 | var code = std.ArrayList(u8).init(gpa); 20 | errdefer code.deinit(); 21 | 22 | var loop_stack = std.ArrayList(u64).init(gpa); 23 | defer loop_stack.deinit(); 24 | 25 | for (bfsrc) |c, i| { 26 | switch (c) { 27 | // inc dword [dat_idx] 28 | '+' => try code.appendSlice(&.{ 0x41, 0xff, 0x02 }), 29 | // dec dword qword [dat_idx] 30 | '-' => try code.appendSlice(&.{ 0x41, 0xff, 0x0a }), 31 | // add r10, 8 32 | '>' => try code.appendSlice(&.{ 0x49, 0x83, 0xc2, 0x08 }), 33 | // sub r10, 8 34 | '<' => try code.appendSlice(&.{ 0x49, 0x83, 0xea, 0x08 }), 35 | // write(1, dat_idx, 1) 36 | '.' => try write(&code), 37 | // read(0, dat_idx, 1) 38 | ',' => try read(&code), 39 | // NOP 40 | '[' => { 41 | // jumped to by the closing bracket 42 | try code.append(0x90); 43 | try loop_stack.append(code.items.len); 44 | // cmp QWORD PTR [r10],0x0 45 | try code.appendSlice(&.{ 46 | 0x41, 0x83, 0x3a, 0x00, 47 | }); 48 | // je { 57 | const popped = loop_stack.popOrNull() orelse { 58 | std.log.emerg("found a ] without a matching [: at index {d}", .{i}); 59 | std.process.exit(1); 60 | }; 61 | // jmp {}, 73 | } 74 | } 75 | if (loop_stack.items.len != 0) { 76 | std.log.emerg("found a [ without a matching ]", .{}); 77 | } 78 | try exit0(&code); 79 | 80 | return Code{ .output = code.toOwnedSlice() }; 81 | } 82 | 83 | fn exit0(c: *std.ArrayList(u8)) !void { 84 | try c.appendSlice(&.{ 85 | 0x48, 86 | 0x31, 87 | 0xff, 88 | 0xb8, 89 | 0x3c, 90 | 0x00, 91 | 0x00, 92 | 0x00, 93 | }); 94 | try syscall(c); 95 | } 96 | 97 | // TODO optimisation since all the things we store to 1 won't change 98 | // just don't do them 99 | fn read(c: *std.ArrayList(u8)) !void { 100 | // mov rax, 0 101 | try c.appendSlice(&.{ 102 | 0xb8, 103 | 0x00, 104 | 0x00, 105 | 0x00, 106 | 0x00, 107 | }); 108 | // mov rdi, 1 109 | try c.appendSlice(&.{ 110 | 0xbf, 111 | 0x01, 112 | 0x00, 113 | 0x00, 114 | 0x00, 115 | }); 116 | // mov rdx, 1 117 | try c.appendSlice(&.{ 118 | 0xba, 119 | 0x01, 120 | 0x00, 121 | 0x00, 122 | 0x00, 123 | }); 124 | // mov rsi, r10 125 | try c.appendSlice(&.{ 126 | 0x4c, 127 | 0x89, 128 | 0xd6, 129 | }); 130 | try syscall(c); 131 | } 132 | // TODO optimisation since all the things we store to 1 won't change 133 | // just don't do them 134 | fn write(c: *std.ArrayList(u8)) !void { 135 | // mov rax, 1 136 | try c.appendSlice(&.{ 137 | 0xb8, 138 | 0x01, 139 | 0x00, 140 | 0x00, 141 | 0x00, 142 | }); 143 | // mov rdi, 1 144 | try c.appendSlice(&.{ 145 | 0xbf, 146 | 0x01, 147 | 0x00, 148 | 0x00, 149 | 0x00, 150 | }); 151 | // mov rdx, 1 152 | try c.appendSlice(&.{ 153 | 0xba, 154 | 0x01, 155 | 0x00, 156 | 0x00, 157 | 0x00, 158 | }); 159 | // mov rsi, r10 160 | try c.appendSlice(&.{ 161 | 0x4c, 162 | 0x89, 163 | 0xd6, 164 | }); 165 | try syscall(c); 166 | } 167 | 168 | fn syscall(c: *std.ArrayList(u8)) !void { 169 | try c.appendSlice(&.{ 170 | 0x0f, 0x05, 171 | }); 172 | } 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # An attempt at an elf linker 2 | 3 | This is meant to be a simple learning project. 4 | 5 | # Resources For ELF Stuff 6 | 7 | https://raw.githubusercontent.com/corkami/pics/28cb0226093ed57b348723bc473cea0162dad366/binary/elf101/elf101-64.svg 8 | 9 | https://cirosantilli.com/elf-hello-world 10 | 11 | https://github.com/mewmew/dissection/blob/master/elf64/elf64.asm 12 | 13 | https://chris.bracken.jp/2018/10/decoding-an-elf-binary/ 14 | -------------------------------------------------------------------------------- /bdiff.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | readelf -a $1 > /tmp/d1.tmp 5 | readelf -a $2 > /tmp/d2.tmp 6 | diff /tmp/d1.tmp /tmp/d2.tmp 7 | -------------------------------------------------------------------------------- /bf/dv.bf: -------------------------------------------------------------------------------- 1 | +>>>>>>>++[+<[>]>[<++>-]<]<[[>+>+<<-]>>-]>+++++[>+++++++<-]>[[<<+>>-]<<-] 2 | ++++++[>++++++++++<-]>+<<<<<<<<<<++++++<<<<<<<+++++[<<+++>+>-]<++ 3 | <[<<<<<<<+++++>>+++++>+>+++>>+++++>>+++++<-]<<<+<<--->--[[<<+>>-]<<-]>---<<<<- 4 | <++++[<<<<++>->+++++++>+>-]<<[<<+>+>>+>>+>>++>>+<<<<<<<-]<[>+<-]<<- 5 | >[[<<+>>-]<<-]<<<<++++++++++++[<<+>---->-]<<[[<<+>>-]<<-]+++[>---------<-] 6 | <<<<<<<<<<<<<<<<<+<++++[<<++++>>-]<<[<<<--->>>>->>-->>>>>>---<<<<<<<<<-]<<<--<<[ 7 | >>+>+++++++++++[<--->>>>---->>--->>--<<<<<<<-]>>>>>+>>+++ 8 | >>>+++++++[<->>---->>->>--<<<<<-]>+>>---->>>>+++++>>---->>--> 9 | ++++++[>--------<-]>+>>---->>+++>>------------>>>>++>>+++++++++>>-->>------ 10 | >>---->>++>>+>+++++++[<++>>-<-]>>>+>>>+++++++[<+>>+++>>>>>>++++<<<<<<<-]>+ 11 | >>>>>>>> 12 | ]>[<+>-]>[>>]<,[[[<<+>>-]<<-]>.[>>]<,] 13 | [Filter for typing in Dvorak on a QWERTY keyboard. Needs a fast implementation. 14 | Assumes no-change-on-EOF or EOF->0. 15 | Daniel B Cristofani (cristofdathevanetdotcom) 16 | http://www.hevanet.com/cristofd/brainfuck/] 17 | -------------------------------------------------------------------------------- /bf/fib.bf: -------------------------------------------------------------------------------- 1 | >++++++++++>+>+[ 2 | [+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[ 3 | [-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 4 | [>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>> 5 | ]<<< 6 | ] 7 | This program doesn't terminate; you will have to kill it. 8 | Daniel B Cristofani (cristofdathevanetdotcom) 9 | http://www.hevanet.com/cristofd/brainfuck/ 10 | -------------------------------------------------------------------------------- /bf/hello.bf: -------------------------------------------------------------------------------- 1 | ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. 2 | -------------------------------------------------------------------------------- /bf/sq.bf: -------------------------------------------------------------------------------- 1 | Although brainfuck is basically useless, it isn't quite so crashingly useless 2 | as people usually say. Some small tasks can be done fairly concisely. 3 | (Some of these are fairly UNIX-specific.) 4 | 5 | Do nothing, terminate successfully. 6 | (Also called "true".) 7 | (This is the shortest brainfuck quine as well.) 8 | 9 | +[>.+<] 10 | Real random byte generator. 11 | (Send output to file, stop program after a while, and take the last byte.) 12 | 13 | +++++++. 14 | Beep. 15 | (Small binary data files are easy to make with brainfuck and a pipe, too.) 16 | 17 | ,[.[-],] 18 | Copy input to output. With a pipe, a no-frills way to make text files. 19 | (Unlike most programs assuming EOF->0, this needed modification before it would 20 | work with EOF->"no change" as well.) 21 | (remove the . to get an input-devourer.) 22 | 23 | >,[>,]<[.<] 24 | Reverse input. 25 | 26 | unmatched [. 27 | A Kimian quine, for my Sun compiler. 28 | 29 | ,[---------[-[++++++++++.[-]]],] 30 | Strip tabs and linefeeds. 31 | 32 | ++++[>++++++++<-],[[>+.-<-]>.<,] 33 | Show ASCII values of input in unary, separated by spaces. 34 | (Useful for checking your implementation's newline behavior on input.) 35 | 36 | ++++++++++[>++++++++++>+<<-]>[>.<-] 37 | Clear the screen. 38 | 39 | +++++++++++[>++++++>+<<-]>+.>-[.[-],] 40 | Play "Prisoner's Dilemma" using the robust "Tit for Tat" strategy. 41 | 42 | +++++++++++[>++++++>+<<-]>++.>-.>,[,[-]<<.>.>,] 43 | ...and using the not-so-robust "All D" strategy. 44 | 45 | +++++[>+++++++++<-],[[>--.++>+<<-]>+.->[<.>-]<<,] 46 | Translate text to brainfuck that prints it. 47 | 48 | >>,[>>,]<<[[-<+<]>[>[>>]<[.[-]<[[>>+<<-]<]>>]>]<<] 49 | Sort bytes in ascending order. 50 | (These last two are solutions from Brainfuck Golf.) 51 | 52 | >,[>>>++++++++[<[<++>-]<+[>+<-]<-[-[-<]>]>[-<]<,>>>-]<.[-]<<] 53 | Translate "binary" to ASCII, e.g. "0110100001101001" to "hi". 54 | 55 | +++++[>+++++<-]>[>++>++++<<-]>-->-<[[>[[>>+<<-]<]>>>-]>-[>+>+<<-]>] 56 | A novel way of moving to the 9999th cell. 57 | 58 | >++++[>++++++<-]>-[[<+++++>>+<-]>-]<<[<]>>>>- 59 | -.<<<-.>>>-.<.<.>---.<<+++.>>>++.<<---.[>]<<. 60 | Print "brainfuck" with a linefeed. 61 | 62 | >>++>+<[[>>]+>>+[-[++++++[>+++++++>+<<-]>-.>[< 63 | ------>-]++<<]<[>>[-]]>[>[-<<]+<[<+<]]+<<]>>] 64 | Version of random.b that outputs printable ASCII: "11011100..." 65 | 66 | ++++++++[>++++[>++>+++>+++>+<<<<-]>+>->+>>+[<]<-]>>.> 67 | >---.+++++++..+++.>.<<-.>.+++.------.--------.>+.>++. 68 | Print "Hello World!" with a linefeed. 69 | 70 | >++++++++++>>+<+[[+++++[>++++++++<-]>.<++++++[>--------<-]+<<]>.>[->[ 71 | <++>-[<++>-[<++>-[<++>-[<-------->>[-]++<-[<++>-]]]]]]<[>+<-]+>>]<<] 72 | Output powers of two, in decimal. (Doesn't terminate.) 73 | 74 | Daniel B Cristofani (cristofdathevanetdotcom) 75 | http://www.hevanet.com/cristofd/brainfuck/ 76 | -------------------------------------------------------------------------------- /bf/test.bf: -------------------------------------------------------------------------------- 1 | +[,.] 2 | -------------------------------------------------------------------------------- /bf/test2.bf: -------------------------------------------------------------------------------- 1 | -[--->+<]>-------.-[--->+<]>.-------------- 2 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const Builder = @import("std").build.Builder; 2 | 3 | pub fn build(b: *Builder) !void { 4 | // Standard target options allows the person running `zig build` to choose 5 | // what target to build for. Here we do not override the defaults, which 6 | // means any target is allowed, and the default is native. Other options 7 | // for restricting supported target set are available. 8 | const target = b.standardTargetOptions(.{}); 9 | 10 | // Standard release options allow the person running `zig build` to select 11 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. 12 | const mode = b.standardReleaseOptions(); 13 | 14 | const exe = b.addExecutable("bz", "main.zig"); 15 | exe.setTarget(target); 16 | exe.setBuildMode(mode); 17 | exe.install(); 18 | 19 | // _ = try b.exec(&.{ "nasm", "-fbin", "-otiny", "elf.asm" }); 20 | // _ = try b.exec(&.{ "chmod", "+x", "tiny" }); 21 | // _ = try b.exec(&.{ "nasm", "-felf64", "hello.asm", "-ohello.o" }); 22 | // _ = try b.exec(&.{ "ld", "-ohello", "hello.o" }); 23 | 24 | const run_cmd = exe.run(); 25 | if (b.args) |args| { 26 | run_cmd.addArgs(args); 27 | } 28 | 29 | const run_step = b.step("run", "run the exe"); 30 | run_step.dependOn(&run_cmd.step); 31 | } 32 | -------------------------------------------------------------------------------- /elf.asm: -------------------------------------------------------------------------------- 1 | ; nasm -f bin -o tiny64 tiny64.asm 2 | BITS 64 3 | org 0x400000 4 | 5 | ehdr: ; Elf64_Ehdr 6 | db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident 7 | times 8 db 0 8 | dw 2 ; e_type 9 | dw 0x3e ; e_machine 10 | dd 1 ; e_version 11 | dq _start ; e_entry 12 | dq phdr - $$ ; e_phoff 13 | dq 0 ; e_shoff 14 | dd 0 ; e_flags 15 | dw ehdrsize ; e_ehsize 16 | dw phdrsize ; e_phentsize 17 | dw 1 ; e_phnum 18 | dw 0 ; e_shentsize 19 | dw 0 ; e_shnum 20 | dw 0 ; e_shstrndx 21 | ehdrsize equ $ - ehdr 22 | 23 | phdr: ; Elf64_Phdr 24 | dd 1 ; p_type 25 | dd 5 ; p_flags 26 | dq 0 ; p_offset 27 | dq $$ ; p_vaddr 28 | dq $$ ; p_paddr 29 | dq filesize ; p_filesz 30 | dq filesize ; p_memsz 31 | dq 0x1000 ; p_align 32 | phdrsize equ $ - phdr 33 | 34 | _start: 35 | mov rax, 231 ; sys_exit_group 36 | mov rdi, [ecode] ; int status 37 | syscall 38 | ecode: 39 | db 42 40 | 41 | filesize equ $ - $$ 42 | -------------------------------------------------------------------------------- /main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | pub const base_point: u64 = 0x200000; 3 | const prologue_len = 10; 4 | 5 | const Code = @import("Code.zig"); 6 | 7 | const number_sections: u16 = 5; 8 | pub fn cast(i: anytype) [@sizeOf(@TypeOf(i))]u8 { 9 | return @bitCast([@sizeOf(@TypeOf(i))]u8, i); 10 | } 11 | const help = 12 | \\bz - brainfuck to elf compiler in zig 13 | \\Usage: 14 | \\bz [file] 15 | \\options: 16 | \\-o [file to output] (default "a.out") 17 | \\-h, --help | show this help text 18 | \\-b [size of brainfuck array] (default 30_000) 19 | \\ 20 | ; 21 | 22 | pub fn main() !void { 23 | var general_pa = std.heap.GeneralPurposeAllocator(.{}){}; 24 | defer _ = general_pa.deinit(); 25 | const gpa = &general_pa.allocator; 26 | var output: ?[]const u8 = null; 27 | var input: ?[]const u8 = null; 28 | var bss_size: ?u32 = null; 29 | { 30 | var i: usize = 1; 31 | const argv = std.os.argv; 32 | while (i < argv.len) : (i += 1) { 33 | const arg = argv[i]; 34 | if (std.mem.eql(u8, std.mem.spanZ(arg), "-o")) { 35 | if (output != null) 36 | fatal("cannot have -o more than once"); 37 | if (i + 1 == argv.len) 38 | fatal("expected another argument after -o"); 39 | i += 1; 40 | output = std.mem.spanZ(argv[i]); 41 | } else if (std.mem.eql(u8, std.mem.spanZ(arg), "-h") or std.mem.eql(u8, std.mem.spanZ(arg), "--help")) { 42 | const stdout = std.io.getStdOut(); 43 | try stdout.writeAll(help); 44 | std.process.exit(0); 45 | } else if (std.mem.eql(u8, std.mem.spanZ(arg), "-b")) { 46 | if (bss_size != null) 47 | fatal("cannot have -b more than once"); 48 | if (i + 1 == argv.len) 49 | fatal("expected another argument after -b"); 50 | i += 1; 51 | bss_size = std.fmt.parseUnsigned(u32, std.mem.spanZ(argv[i]), 10) catch { 52 | fatal("unable to parse argument after -b"); 53 | }; 54 | } else { 55 | if (input != null) 56 | fatal("cannot have more than 1 input file"); 57 | input = std.mem.spanZ(argv[i]); 58 | } 59 | } 60 | if (input == null) { 61 | fatal("need one input file\n" ++ help); 62 | } 63 | } 64 | const bfcode = try std.fs.cwd().readFileAlloc(gpa, input.?, std.math.maxInt(usize)); 65 | defer gpa.free(bfcode); 66 | try genElfAndWriteToFs(gpa, bfcode, .{ .output_name = output }); 67 | } 68 | 69 | fn fatal(comptime msg: []const u8) noreturn { 70 | std.log.emerg(msg, .{}); 71 | std.process.exit(1); 72 | } 73 | 74 | const ElfOpts = struct { 75 | bss_len: u16 = 30_000, 76 | output_name: ?[]const u8, 77 | }; 78 | fn genElfAndWriteToFs(gpa: *std.mem.Allocator, bfcode: []const u8, opts: ElfOpts) !void { 79 | var dat = std.ArrayList(u8).init(gpa); 80 | defer dat.deinit(); 81 | 82 | // === the code 83 | const genned = try Code.gen(gpa, bfcode); 84 | defer genned.deinit(gpa); 85 | const data = ""; 86 | const shstrtab = "\x00.text\x00.data\x00.shstrtab\x00.bss\x00"; 87 | 88 | const genned_len = prologue_len + genned.output.len; 89 | // zeros are for the prologue after we figure out the spacing 90 | const c = try std.mem.concat(gpa, u8, &.{ &[_]u8{0} ** prologue_len, genned.output, data, shstrtab }); 91 | defer gpa.free(c); 92 | 93 | // === our elf linking 94 | const header_off = getSize(ElfHeader) + getSize(ProgHeader); 95 | const entry_off = base_point + header_off; 96 | const genned_o = header_off; 97 | const data_o = header_off + genned_len; 98 | const shstrtab_o = data_o + data.len; 99 | const bss_o = shstrtab_o + shstrtab.len; 100 | const sections_off = header_off + c.len; 101 | const sh_size = number_sections * getSize(SectionHeader); 102 | const sh_off = sections_off + sh_size; 103 | const filesize = sh_off; 104 | { 105 | // doing some ad-hoc relocations for the prologue 106 | std.mem.copy(u8, c[0..2], &.{ 0x49, 0xba }); 107 | std.mem.copy(u8, c[2..prologue_len], &cast(bss_o + base_point)); 108 | } 109 | 110 | // ELF HEADER 111 | try writeTypeToCode(&dat, ElfHeader, .{ 112 | .e_entry = cast(entry_off), 113 | .e_shoff = cast(sections_off), 114 | .e_shnum = cast(number_sections), 115 | .e_shstrndx = cast(@as(u16, 3)), 116 | }); 117 | 118 | // PROGRAM HEADERS 119 | try writeTypeToCode(&dat, ProgHeader, .{ 120 | .p_filesz = cast(filesize), 121 | .p_memsz = cast(filesize), 122 | .p_offset = .{0} ** 8, 123 | }); 124 | 125 | // all the sections 126 | 127 | try dat.appendSlice(c); 128 | 129 | // OUR SECTION HEADERS 130 | // the null section header 131 | try writeTypeToCode(&dat, SectionHeader, .{ 132 | .sh_name = cast(@as(u32, 0)), 133 | .sh_type = cast(SHT_NOBITS), 134 | .sh_flags = cast(@as(u64, 0)), 135 | .sh_addr = cast(@as(u64, 0)), 136 | .sh_offset = cast(@as(u64, 0)), 137 | .sh_size = cast(@as(u64, 0)), 138 | .sh_link = cast(@as(u32, 0)), 139 | .sh_info = cast(@as(u32, 0)), 140 | .sh_addralign = cast(@as(u64, 0)), 141 | .sh_entsize = cast(@as(u64, 0)), 142 | }); 143 | // .text 144 | const text_off = @truncate(u32, std.mem.indexOf(u8, shstrtab, ".text").?); 145 | try writeTypeToCode(&dat, SectionHeader, .{ 146 | .sh_name = cast(@truncate(u32, text_off)), 147 | .sh_type = cast(SHT_PROGBITS), 148 | .sh_flags = cast(@as(u64, 0)), 149 | .sh_addr = cast(@as(u64, base_point + genned_o)), 150 | .sh_offset = cast(@as(u64, genned_o)), 151 | .sh_size = cast(@as(u64, genned_len)), 152 | .sh_link = cast(@as(u32, 0)), 153 | .sh_info = cast(@as(u32, 0)), 154 | .sh_addralign = cast(@as(u64, 0)), 155 | .sh_entsize = cast(@as(u64, 0)), 156 | }); 157 | // .data 158 | const data_off = @truncate(u32, std.mem.indexOf(u8, shstrtab, ".data").?); 159 | try writeTypeToCode(&dat, SectionHeader, .{ 160 | .sh_name = cast(@truncate(u32, data_off)), 161 | // TODO find right sh_type for this 162 | .sh_type = cast(SHT_PROGBITS), 163 | .sh_flags = cast(@as(u64, 0)), 164 | .sh_addr = cast(base_point + data_o), 165 | .sh_offset = cast(data_o), 166 | .sh_size = cast(@as(u64, data.len)), 167 | .sh_link = cast(@as(u32, 0)), 168 | .sh_info = cast(@as(u32, 0)), 169 | .sh_addralign = cast(@as(u64, 0)), 170 | .sh_entsize = cast(@as(u64, 0)), 171 | }); 172 | // .shstrtab 173 | const shstrtab_off = @truncate(u32, std.mem.indexOf(u8, shstrtab, ".shstrtab").?); 174 | try writeTypeToCode(&dat, SectionHeader, .{ 175 | .sh_name = cast(@truncate(u32, shstrtab_off)), 176 | .sh_type = cast(SHT_STRTAB), 177 | .sh_flags = cast(@as(u64, 0)), 178 | .sh_addr = cast(@as(u64, base_point + shstrtab_o)), 179 | .sh_offset = cast(@as(u64, shstrtab_o)), 180 | .sh_size = cast(@as(u64, shstrtab.len)), 181 | .sh_link = cast(@as(u32, 0)), 182 | .sh_info = cast(@as(u32, 0)), 183 | .sh_addralign = cast(@as(u64, 0)), 184 | .sh_entsize = cast(@as(u64, 0)), 185 | }); 186 | // .bss 187 | const bss_off = @truncate(u32, std.mem.indexOf(u8, shstrtab, ".bss").?); 188 | try writeTypeToCode(&dat, SectionHeader, .{ 189 | .sh_name = cast(@truncate(u32, bss_off)), 190 | .sh_type = cast(SHT_NOBITS), 191 | .sh_flags = cast(@as(u64, 0)), 192 | .sh_addr = cast(@as(u64, base_point + bss_o)), 193 | .sh_offset = cast(@as(u64, bss_o)), 194 | .sh_size = cast(@as(u64, opts.bss_len)), 195 | .sh_link = cast(@as(u32, 0)), 196 | .sh_info = cast(@as(u32, 0)), 197 | .sh_addralign = cast(@as(u64, 0)), 198 | .sh_entsize = cast(@as(u64, 0)), 199 | }); 200 | 201 | // === write to filesystem 202 | 203 | var name: []const u8 = "a.out"; 204 | if (opts.output_name) |n| name = n; 205 | const file = try std.fs.cwd().createFile(name, .{ 206 | .mode = 0o777, 207 | }); 208 | defer file.close(); 209 | _ = try file.write(dat.items); 210 | } 211 | 212 | fn writeTypeToCode(c: *std.ArrayList(u8), comptime T: type, s: T) !void { 213 | inline for (std.meta.fields(T)) |f| { 214 | switch (f.field_type) { 215 | u8 => try c.append(@field(s, f.name)), 216 | else => try c.appendSlice(&@field(s, f.name)), 217 | } 218 | } 219 | } 220 | 221 | fn getSize(comptime t: type) usize { 222 | comptime { 223 | var i: usize = 0; 224 | inline for (std.meta.fields(t)) |f| { 225 | i += @sizeOf(f.field_type); 226 | } 227 | return i; 228 | } 229 | } 230 | 231 | const ElfHeader = struct { 232 | /// e_ident 233 | magic: [4]u8 = "\x7fELF".*, 234 | /// 32 bit (1) or 64 (2) 235 | class: u8 = 2, 236 | /// endianness little (1) or big (2) 237 | endianness: u8 = 1, 238 | /// elf version 239 | version: u8 = 1, 240 | /// osabi: we want systemv which is 0 241 | abi: u8 = 0, 242 | /// abiversion: 0 243 | abi_version: u8 = 0, 244 | /// paddding 245 | padding: [7]u8 = [_]u8{0} ** 7, 246 | 247 | /// object type 248 | e_type: [2]u8 = cast(@as(u16, 2)), 249 | 250 | /// arch 251 | e_machine: [2]u8 = cast(@as(u16, 0x3e)), 252 | 253 | /// version 254 | e_version: [4]u8 = cast(@as(u32, 1)), 255 | 256 | /// entry point 257 | e_entry: [8]u8, 258 | 259 | /// start of program header 260 | /// It usually follows the file header immediately, 261 | /// making the offset 0x34 or 0x40 262 | /// for 32- and 64-bit ELF executables, respectively. 263 | e_phoff: [8]u8 = cast(@as(u64, 0x40)), 264 | 265 | /// e_shoff 266 | /// start of section header table 267 | e_shoff: [8]u8, 268 | 269 | /// ??? 270 | e_flags: [4]u8 = .{0} ** 4, 271 | 272 | /// Contains the size of this header, 273 | /// normally 64 Bytes for 64-bit and 52 Bytes for 32-bit format. 274 | e_ehsize: [2]u8 = cast(@as(u16, 0x40)), 275 | 276 | /// size of program header 277 | e_phentsize: [2]u8 = cast(@as(u16, 56)), 278 | 279 | /// number of entries in program header table 280 | e_phnum: [2]u8 = cast(@as(u16, 1)), 281 | 282 | /// size of section header table entry 283 | e_shentsize: [2]u8 = cast(@as(u16, 0x40)), 284 | 285 | /// number of section header entries 286 | e_shnum: [2]u8, 287 | 288 | /// index of section header table entry that contains section names (.shstrtab) 289 | e_shstrndx: [2]u8, 290 | }; 291 | 292 | const PF_X = 0x1; 293 | const PF_W = 0x2; 294 | const PF_R = 0x4; 295 | 296 | const ProgHeader = struct { 297 | /// type of segment 298 | /// 1 for loadable 299 | p_type: [4]u8 = cast(@as(u32, 1)), 300 | 301 | /// segment dependent 302 | /// NO PROTECTION 303 | p_flags: [4]u8 = cast(@as(u32, PF_R | PF_W | PF_X)), 304 | 305 | /// offset of the segment in the file image 306 | p_offset: [8]u8, 307 | 308 | /// virtual addr of segment in memory. start of this segment 309 | p_vaddr: [8]u8 = cast(@as(u64, base_point)), 310 | 311 | /// same as vaddr except on physical systems 312 | p_paddr: [8]u8 = cast(@as(u64, base_point)), 313 | 314 | p_filesz: [8]u8, 315 | 316 | p_memsz: [8]u8, 317 | 318 | /// 0 and 1 specify no alignment. 319 | /// Otherwise should be a positive, integral power of 2, 320 | /// with p_vaddr equating p_offset modulus p_align. 321 | p_align: [8]u8 = cast(@as(u64, 0x100)), 322 | }; 323 | 324 | const SHT_NOBITS: u32 = 8; 325 | const SHT_NULL: u32 = 0; 326 | const SHT_PROGBITS: u32 = 1; 327 | const SHT_STRTAB: u32 = 3; 328 | 329 | const SectionHeader = struct { 330 | /// offset into .shstrtab that contains the name of the section 331 | sh_name: [4]u8, 332 | 333 | /// type of this header 334 | sh_type: [4]u8, 335 | 336 | /// attrs of the section 337 | sh_flags: [8]u8, 338 | 339 | /// virtual addr of section in memory 340 | sh_addr: [8]u8, 341 | 342 | /// offset in file image 343 | sh_offset: [8]u8, 344 | 345 | /// size of section in bytes (0 is allowed) 346 | sh_size: [8]u8, 347 | 348 | /// section index 349 | sh_link: [4]u8, 350 | 351 | /// extra info abt section 352 | sh_info: [4]u8, 353 | 354 | /// alignment of section (power of 2) 355 | sh_addralign: [8]u8, 356 | 357 | /// size of bytes of section that contains fixed-size entry otherwise 0 358 | sh_entsize: [8]u8, 359 | }; 360 | --------------------------------------------------------------------------------