authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-04 16:05:58-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-09-04 16:05:58-04:00
logcff14dc2c67d9a35ae2c3e07bd6d2c5594d8a0a1
treef0b980018b99375e198081ae1e24859bf5695900
parent209a3da4f73ab6dd4182649af8fb7439e9145441
parente9807418e7e58f3cb85d5d3a6d114d5084e305bd
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6247 from alexnask/stage2_pe

Added a basic Portable Executable linker to stage2

10 files changed, 1123 insertions(+), 145 deletions(-)

lib/std/coff.zig+66
...@@ -18,11 +18,77 @@ const IMAGE_FILE_MACHINE_I386 = 0x014c;...@@ -18,11 +18,77 @@ const IMAGE_FILE_MACHINE_I386 = 0x014c;
18const IMAGE_FILE_MACHINE_IA64 = 0x0200;18const IMAGE_FILE_MACHINE_IA64 = 0x0200;
19const IMAGE_FILE_MACHINE_AMD64 = 0x8664;19const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
2020
21pub const MachineType = enum(u16) {
22 Unknown = 0x0,
23 /// Matsushita AM33
24 AM33 = 0x1d3,
25 /// x64
26 X64 = 0x8664,
27 /// ARM little endian
28 ARM = 0x1c0,
29 /// ARM64 little endian
30 ARM64 = 0xaa64,
31 /// ARM Thumb-2 little endian
32 ARMNT = 0x1c4,
33 /// EFI byte code
34 EBC = 0xebc,
35 /// Intel 386 or later processors and compatible processors
36 I386 = 0x14c,
37 /// Intel Itanium processor family
38 IA64 = 0x200,
39 /// Mitsubishi M32R little endian
40 M32R = 0x9041,
41 /// MIPS16
42 MIPS16 = 0x266,
43 /// MIPS with FPU
44 MIPSFPU = 0x366,
45 /// MIPS16 with FPU
46 MIPSFPU16 = 0x466,
47 /// Power PC little endian
48 POWERPC = 0x1f0,
49 /// Power PC with floating point support
50 POWERPCFP = 0x1f1,
51 /// MIPS little endian
52 R4000 = 0x166,
53 /// RISC-V 32-bit address space
54 RISCV32 = 0x5032,
55 /// RISC-V 64-bit address space
56 RISCV64 = 0x5064,
57 /// RISC-V 128-bit address space
58 RISCV128 = 0x5128,
59 /// Hitachi SH3
60 SH3 = 0x1a2,
61 /// Hitachi SH3 DSP
62 SH3DSP = 0x1a3,
63 /// Hitachi SH4
64 SH4 = 0x1a6,
65 /// Hitachi SH5
66 SH5 = 0x1a8,
67 /// Thumb
68 Thumb = 0x1c2,
69 /// MIPS little-endian WCE v2
70 WCEMIPSV2 = 0x169,
71};
72
21// OptionalHeader.magic values73// OptionalHeader.magic values
22// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx74// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
23const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;75const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
24const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;76const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
2577
78// Image Characteristics
79pub const IMAGE_FILE_RELOCS_STRIPPED = 0x1;
80pub const IMAGE_FILE_DEBUG_STRIPPED = 0x200;
81pub const IMAGE_FILE_EXECUTABLE_IMAGE = 0x2;
82pub const IMAGE_FILE_32BIT_MACHINE = 0x100;
83pub const IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x20;
84
85// Section flags
86pub const IMAGE_SCN_CNT_INITIALIZED_DATA = 0x40;
87pub const IMAGE_SCN_MEM_READ = 0x40000000;
88pub const IMAGE_SCN_CNT_CODE = 0x20;
89pub const IMAGE_SCN_MEM_EXECUTE = 0x20000000;
90pub const IMAGE_SCN_MEM_WRITE = 0x80000000;
91
26const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;92const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
27const IMAGE_DEBUG_TYPE_CODEVIEW = 2;93const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
28const DEBUG_DIRECTORY = 6;94const DEBUG_DIRECTORY = 6;
lib/std/target.zig+58
...@@ -468,6 +468,7 @@ pub const Target = struct {...@@ -468,6 +468,7 @@ pub const Target = struct {
468 /// TODO Get rid of this one.468 /// TODO Get rid of this one.
469 unknown,469 unknown,
470 coff,470 coff,
471 pe,
471 elf,472 elf,
472 macho,473 macho,
473 wasm,474 wasm,
...@@ -771,6 +772,63 @@ pub const Target = struct {...@@ -771,6 +772,63 @@ pub const Target = struct {
771 };772 };
772 }773 }
773774
775 pub fn toCoffMachine(arch: Arch) std.coff.MachineType {
776 return switch (arch) {
777 .avr => .Unknown,
778 .msp430 => .Unknown,
779 .arc => .Unknown,
780 .arm => .ARM,
781 .armeb => .Unknown,
782 .hexagon => .Unknown,
783 .le32 => .Unknown,
784 .mips => .Unknown,
785 .mipsel => .Unknown,
786 .powerpc => .POWERPC,
787 .r600 => .Unknown,
788 .riscv32 => .RISCV32,
789 .sparc => .Unknown,
790 .sparcel => .Unknown,
791 .tce => .Unknown,
792 .tcele => .Unknown,
793 .thumb => .Thumb,
794 .thumbeb => .Thumb,
795 .i386 => .I386,
796 .xcore => .Unknown,
797 .nvptx => .Unknown,
798 .amdil => .Unknown,
799 .hsail => .Unknown,
800 .spir => .Unknown,
801 .kalimba => .Unknown,
802 .shave => .Unknown,
803 .lanai => .Unknown,
804 .wasm32 => .Unknown,
805 .renderscript32 => .Unknown,
806 .aarch64_32 => .ARM64,
807 .aarch64 => .ARM64,
808 .aarch64_be => .Unknown,
809 .mips64 => .Unknown,
810 .mips64el => .Unknown,
811 .powerpc64 => .Unknown,
812 .powerpc64le => .Unknown,
813 .riscv64 => .RISCV64,
814 .x86_64 => .X64,
815 .nvptx64 => .Unknown,
816 .le64 => .Unknown,
817 .amdil64 => .Unknown,
818 .hsail64 => .Unknown,
819 .spir64 => .Unknown,
820 .wasm64 => .Unknown,
821 .renderscript64 => .Unknown,
822 .amdgcn => .Unknown,
823 .bpfel => .Unknown,
824 .bpfeb => .Unknown,
825 .sparcv9 => .Unknown,
826 .s390x => .Unknown,
827 .ve => .Unknown,
828 .spu_2 => .Unknown,
829 };
830 }
831
774 pub fn endian(arch: Arch) builtin.Endian {832 pub fn endian(arch: Arch) builtin.Endian {
775 return switch (arch) {833 return switch (arch) {
776 .avr,834 .avr,
src-self-hosted/Module.zig+5
...@@ -1820,6 +1820,9 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {...@@ -1820,6 +1820,9 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
1820 try self.markOutdatedDecl(decl);1820 try self.markOutdatedDecl(decl);
1821 decl.contents_hash = contents_hash;1821 decl.contents_hash = contents_hash;
1822 } else switch (self.bin_file.tag) {1822 } else switch (self.bin_file.tag) {
1823 .coff => {
1824 // TODO Implement for COFF
1825 },
1823 .elf => if (decl.fn_link.elf.len != 0) {1826 .elf => if (decl.fn_link.elf.len != 0) {
1824 // TODO Look into detecting when this would be unnecessary by storing enough state1827 // TODO Look into detecting when this would be unnecessary by storing enough state
1825 // in `Decl` to notice that the line number did not change.1828 // in `Decl` to notice that the line number did not change.
...@@ -2078,12 +2081,14 @@ fn allocateNewDecl(...@@ -2078,12 +2081,14 @@ fn allocateNewDecl(
2078 .deletion_flag = false,2081 .deletion_flag = false,
2079 .contents_hash = contents_hash,2082 .contents_hash = contents_hash,
2080 .link = switch (self.bin_file.tag) {2083 .link = switch (self.bin_file.tag) {
2084 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
2081 .elf => .{ .elf = link.File.Elf.TextBlock.empty },2085 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
2082 .macho => .{ .macho = link.File.MachO.TextBlock.empty },2086 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
2083 .c => .{ .c = {} },2087 .c => .{ .c = {} },
2084 .wasm => .{ .wasm = {} },2088 .wasm => .{ .wasm = {} },
2085 },2089 },
2086 .fn_link = switch (self.bin_file.tag) {2090 .fn_link = switch (self.bin_file.tag) {
2091 .coff => .{ .coff = {} },
2087 .elf => .{ .elf = link.File.Elf.SrcFn.empty },2092 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
2088 .macho => .{ .macho = link.File.MachO.SrcFn.empty },2093 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
2089 .c => .{ .c = {} },2094 .c => .{ .c = {} },
src-self-hosted/codegen.zig+166-112
...@@ -59,14 +59,21 @@ pub const GenerateSymbolError = error{...@@ -59,14 +59,21 @@ pub const GenerateSymbolError = error{
59 AnalysisFail,59 AnalysisFail,
60};60};
6161
62pub const DebugInfoOutput = union(enum) {
63 dwarf: struct {
64 dbg_line: *std.ArrayList(u8),
65 dbg_info: *std.ArrayList(u8),
66 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
67 },
68 none,
69};
70
62pub fn generateSymbol(71pub fn generateSymbol(
63 bin_file: *link.File,72 bin_file: *link.File,
64 src: usize,73 src: usize,
65 typed_value: TypedValue,74 typed_value: TypedValue,
66 code: *std.ArrayList(u8),75 code: *std.ArrayList(u8),
67 dbg_line: *std.ArrayList(u8),76 debug_output: DebugInfoOutput,
68 dbg_info: *std.ArrayList(u8),
69 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
70) GenerateSymbolError!Result {77) GenerateSymbolError!Result {
71 const tracy = trace(@src());78 const tracy = trace(@src());
72 defer tracy.end();79 defer tracy.end();
...@@ -76,56 +83,56 @@ pub fn generateSymbol(...@@ -76,56 +83,56 @@ pub fn generateSymbol(
76 switch (bin_file.options.target.cpu.arch) {83 switch (bin_file.options.target.cpu.arch) {
77 .wasm32 => unreachable, // has its own code path84 .wasm32 => unreachable, // has its own code path
78 .wasm64 => unreachable, // has its own code path85 .wasm64 => unreachable, // has its own code path
79 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),86 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output),
80 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),87 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
81 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),88 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output),
82 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),89 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output),
83 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),90 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output),
84 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),91 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output),
85 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),92 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output),
86 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),93 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, debug_output),
87 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),94 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
88 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),95 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, debug_output),
89 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),96 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, debug_output),
90 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),97 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, debug_output),
91 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),98 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, debug_output),
92 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),99 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, debug_output),
93 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),100 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, debug_output),
94 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),101 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, debug_output),
95 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),102 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, debug_output),
96 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),103 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, debug_output),
97 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),104 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, debug_output),
98 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),105 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, debug_output),
99 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),106 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, debug_output),
100 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),107 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, debug_output),
101 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),108 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, debug_output),
102 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),109 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, debug_output),
103 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),110 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, debug_output),
104 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),111 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, debug_output),
105 .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),112 .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, debug_output),
106 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),113 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, debug_output),
107 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),114 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, debug_output),
108 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),115 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, debug_output),
109 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),116 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
110 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),117 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, debug_output),
111 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),118 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, debug_output),
112 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),119 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, debug_output),
113 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),120 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, debug_output),
114 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),121 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, debug_output),
115 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),122 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, debug_output),
116 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),123 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, debug_output),
117 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),124 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, debug_output),
118 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),125 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, debug_output),
119 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),126 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, debug_output),
120 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),127 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, debug_output),
121 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),128 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, debug_output),
122 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),129 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, debug_output),
123 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),130 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, debug_output),
124 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),131 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, debug_output),
125 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),132 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, debug_output),
126 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),133 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, debug_output),
127 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),134 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, debug_output),
128 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),135 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, debug_output),
129 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),136 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
130 }137 }
131 },138 },
...@@ -139,7 +146,7 @@ pub fn generateSymbol(...@@ -139,7 +146,7 @@ pub fn generateSymbol(
139 switch (try generateSymbol(bin_file, src, .{146 switch (try generateSymbol(bin_file, src, .{
140 .ty = typed_value.ty.elemType(),147 .ty = typed_value.ty.elemType(),
141 .val = sentinel,148 .val = sentinel,
142 }, code, dbg_line, dbg_info, dbg_info_type_relocs)) {149 }, code, debug_output)) {
143 .appended => return Result{ .appended = {} },150 .appended => return Result{ .appended = {} },
144 .externally_managed => |slice| {151 .externally_managed => |slice| {
145 code.appendSliceAssumeCapacity(slice);152 code.appendSliceAssumeCapacity(slice);
...@@ -239,9 +246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -239,9 +246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
239 target: *const std.Target,246 target: *const std.Target,
240 mod_fn: *const Module.Fn,247 mod_fn: *const Module.Fn,
241 code: *std.ArrayList(u8),248 code: *std.ArrayList(u8),
242 dbg_line: *std.ArrayList(u8),249 debug_output: DebugInfoOutput,
243 dbg_info: *std.ArrayList(u8),
244 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
245 err_msg: ?*ErrorMsg,250 err_msg: ?*ErrorMsg,
246 args: []MCValue,251 args: []MCValue,
247 ret_mcv: MCValue,252 ret_mcv: MCValue,
...@@ -419,9 +424,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -419,9 +424,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
419 src: usize,424 src: usize,
420 typed_value: TypedValue,425 typed_value: TypedValue,
421 code: *std.ArrayList(u8),426 code: *std.ArrayList(u8),
422 dbg_line: *std.ArrayList(u8),427 debug_output: DebugInfoOutput,
423 dbg_info: *std.ArrayList(u8),
424 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
425 ) GenerateSymbolError!Result {428 ) GenerateSymbolError!Result {
426 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;429 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
427430
...@@ -457,9 +460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -457,9 +460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
457 .bin_file = bin_file,460 .bin_file = bin_file,
458 .mod_fn = module_fn,461 .mod_fn = module_fn,
459 .code = code,462 .code = code,
460 .dbg_line = dbg_line,463 .debug_output = debug_output,
461 .dbg_info = dbg_info,
462 .dbg_info_type_relocs = dbg_info_type_relocs,
463 .err_msg = null,464 .err_msg = null,
464 .args = undefined, // populated after `resolveCallingConventionValues`465 .args = undefined, // populated after `resolveCallingConventionValues`
465 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`466 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
...@@ -598,35 +599,50 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -598,35 +599,50 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
598 }599 }
599600
600 fn dbgSetPrologueEnd(self: *Self) InnerError!void {601 fn dbgSetPrologueEnd(self: *Self) InnerError!void {
601 try self.dbg_line.append(DW.LNS_set_prologue_end);602 switch (self.debug_output) {
602 try self.dbgAdvancePCAndLine(self.prev_di_src);603 .dwarf => |dbg_out| {
604 try dbg_out.dbg_line.append(DW.LNS_set_prologue_end);
605 try self.dbgAdvancePCAndLine(self.prev_di_src);
606 },
607 .none => {},
608 }
603 }609 }
604610
605 fn dbgSetEpilogueBegin(self: *Self) InnerError!void {611 fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
606 try self.dbg_line.append(DW.LNS_set_epilogue_begin);612 switch (self.debug_output) {
607 try self.dbgAdvancePCAndLine(self.prev_di_src);613 .dwarf => |dbg_out| {
614 try dbg_out.dbg_line.append(DW.LNS_set_epilogue_begin);
615 try self.dbgAdvancePCAndLine(self.prev_di_src);
616 },
617 .none => {},
618 }
608 }619 }
609620
610 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {621 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
611 // TODO Look into improving the performance here by adding a token-index-to-line
612 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
613 // this involves scanning over the source code for newlines
614 // (but only from the previous byte offset to the new one).
615 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
616 const delta_pc = self.code.items.len - self.prev_di_pc;
617 self.prev_di_src = src;622 self.prev_di_src = src;
618 self.prev_di_pc = self.code.items.len;623 self.prev_di_pc = self.code.items.len;
619 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit624 switch (self.debug_output) {
620 // single-byte opcodes that add different numbers to both the PC and the line number625 .dwarf => |dbg_out| {
621 // at the same time.626 // TODO Look into improving the performance here by adding a token-index-to-line
622 try self.dbg_line.ensureCapacity(self.dbg_line.items.len + 11);627 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
623 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);628 // this involves scanning over the source code for newlines
624 leb128.writeULEB128(self.dbg_line.writer(), delta_pc) catch unreachable;629 // (but only from the previous byte offset to the new one).
625 if (delta_line != 0) {630 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
626 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);631 const delta_pc = self.code.items.len - self.prev_di_pc;
627 leb128.writeILEB128(self.dbg_line.writer(), delta_line) catch unreachable;632 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
633 // single-byte opcodes that add different numbers to both the PC and the line number
634 // at the same time.
635 try dbg_out.dbg_line.ensureCapacity(dbg_out.dbg_line.items.len + 11);
636 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
637 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
638 if (delta_line != 0) {
639 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
640 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
641 }
642 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_copy);
643 },
644 .none => {},
628 }645 }
629 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
630 }646 }
631647
632 /// Asserts there is already capacity to insert into top branch inst_table.648 /// Asserts there is already capacity to insert into top branch inst_table.
...@@ -654,18 +670,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -654,18 +670,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
654 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,670 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
655 /// after codegen for this symbol is done.671 /// after codegen for this symbol is done.
656 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {672 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
657 assert(ty.hasCodeGenBits());673 switch (self.debug_output) {
658 const index = self.dbg_info.items.len;674 .dwarf => |dbg_out| {
659 try self.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4675 assert(ty.hasCodeGenBits());
660676 const index = dbg_out.dbg_info.items.len;
661 const gop = try self.dbg_info_type_relocs.getOrPut(self.gpa, ty);677 try dbg_out.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
662 if (!gop.found_existing) {678
663 gop.entry.value = .{679 const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
664 .off = undefined,680 if (!gop.found_existing) {
665 .relocs = .{},681 gop.entry.value = .{
666 };682 .off = undefined,
683 .relocs = .{},
684 };
685 }
686 try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
687 },
688 .none => {},
667 }689 }
668 try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
669 }690 }
670691
671 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {692 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
...@@ -1258,14 +1279,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1258,14 +1279,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1258 self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);1279 self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
1259 self.markRegUsed(reg);1280 self.markRegUsed(reg);
12601281
1261 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);1282 switch (self.debug_output) {
1262 self.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);1283 .dwarf => |dbg_out| {
1263 self.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc1284 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 8 + name_with_null.len);
1264 1, // ULEB128 dwarf expression length1285 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1265 reg.dwarfLocOp(),1286 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
1266 });1287 1, // ULEB128 dwarf expression length
1267 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref41288 reg.dwarfLocOp(),
1268 self.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string1289 });
1290 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
1291 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
1292 },
1293 .none => {},
1294 }
1269 },1295 },
1270 else => {},1296 else => {},
1271 }1297 }
...@@ -1302,7 +1328,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1302,7 +1328,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13021328
1303 // Due to incremental compilation, how function calls are generated depends1329 // Due to incremental compilation, how function calls are generated depends
1304 // on linking.1330 // on linking.
1305 if (self.bin_file.cast(link.File.Elf)) |elf_file| {1331 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
1306 switch (arch) {1332 switch (arch) {
1307 .x86_64 => {1333 .x86_64 => {
1308 for (info.args) |mc_arg, arg_i| {1334 for (info.args) |mc_arg, arg_i| {
...@@ -1341,10 +1367,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1341,10 +1367,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1341 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1367 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1342 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1368 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1343 const func = func_val.func;1369 const func = func_val.func;
1344 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];1370
1345 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1371 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1346 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1372 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1347 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);1373 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1374 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1375 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1376 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1377 @intCast(u32, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes)
1378 else
1379 unreachable;
1380
1348 // ff 14 25 xx xx xx xx call [addr]1381 // ff 14 25 xx xx xx xx call [addr]
1349 try self.code.ensureCapacity(self.code.items.len + 7);1382 try self.code.ensureCapacity(self.code.items.len + 7);
1350 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });1383 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
...@@ -1362,10 +1395,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1362,10 +1395,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1362 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1395 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1363 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1396 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1364 const func = func_val.func;1397 const func = func_val.func;
1365 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];1398
1366 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1399 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1367 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1400 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1368 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);1401 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1402 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1403 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1404 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1405 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
1406 else
1407 unreachable;
13691408
1370 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });1409 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1371 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());1410 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
...@@ -1383,8 +1422,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1383,8 +1422,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1383 }1422 }
1384 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1423 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1385 const func = func_val.func;1424 const func = func_val.func;
1386 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];1425 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1387 const got_addr = @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);1426 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1427 break :blk @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);
1428 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1429 @intCast(u16, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * 2)
1430 else
1431 unreachable;
1432
1388 const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();1433 const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
1389 // First, push the return address, then jump; if noreturn, don't bother with the first step1434 // First, push the return address, then jump; if noreturn, don't bother with the first step
1390 // TODO: implement packed struct -> u16 at comptime and move the bitcast here1435 // TODO: implement packed struct -> u16 at comptime and move the bitcast here
...@@ -1420,10 +1465,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1420,10 +1465,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1420 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1465 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1421 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1466 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1422 const func = func_val.func;1467 const func = func_val.func;
1423 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1424 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1468 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1425 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1469 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1426 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);1470 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1471 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1472 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1473 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1474 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
1475 else
1476 unreachable;
14271477
1428 // TODO only works with leaf functions1478 // TODO only works with leaf functions
1429 // at the moment, which works fine for1479 // at the moment, which works fine for
...@@ -1983,7 +2033,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1983,7 +2033,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19832033
1984 if (mem.eql(u8, inst.asm_source, "syscall")) {2034 if (mem.eql(u8, inst.asm_source, "syscall")) {
1985 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });2035 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
1986 } else {2036 } else if (inst.asm_source.len != 0) {
1987 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});2037 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
1988 }2038 }
19892039
...@@ -2541,6 +2591,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2541,6 +2591,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2541 const got = &macho_file.sections.items[macho_file.got_section_index.?];2591 const got = &macho_file.sections.items[macho_file.got_section_index.?];
2542 const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes;2592 const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes;
2543 return MCValue{ .memory = got_addr };2593 return MCValue{ .memory = got_addr };
2594 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
2595 const decl = payload.decl;
2596 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
2597 return MCValue{ .memory = got_addr };
2544 } else {2598 } else {
2545 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});2599 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
2546 }2600 }
src-self-hosted/link.zig+20-2
...@@ -34,6 +34,7 @@ pub const File = struct {...@@ -34,6 +34,7 @@ pub const File = struct {
3434
35 pub const LinkBlock = union {35 pub const LinkBlock = union {
36 elf: Elf.TextBlock,36 elf: Elf.TextBlock,
37 coff: Coff.TextBlock,
37 macho: MachO.TextBlock,38 macho: MachO.TextBlock,
38 c: void,39 c: void,
39 wasm: void,40 wasm: void,
...@@ -41,6 +42,7 @@ pub const File = struct {...@@ -41,6 +42,7 @@ pub const File = struct {
4142
42 pub const LinkFn = union {43 pub const LinkFn = union {
43 elf: Elf.SrcFn,44 elf: Elf.SrcFn,
45 coff: Coff.SrcFn,
44 macho: MachO.SrcFn,46 macho: MachO.SrcFn,
45 c: void,47 c: void,
46 wasm: ?Wasm.FnData,48 wasm: ?Wasm.FnData,
...@@ -66,7 +68,7 @@ pub const File = struct {...@@ -66,7 +68,7 @@ pub const File = struct {
66 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {68 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
67 switch (options.object_format) {69 switch (options.object_format) {
68 .unknown => unreachable,70 .unknown => unreachable,
69 .coff => return error.TODOImplementCoff,71 .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options),
70 .elf => return Elf.openPath(allocator, dir, sub_path, options),72 .elf => return Elf.openPath(allocator, dir, sub_path, options),
71 .macho => return MachO.openPath(allocator, dir, sub_path, options),73 .macho => return MachO.openPath(allocator, dir, sub_path, options),
72 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),74 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
...@@ -85,7 +87,7 @@ pub const File = struct {...@@ -85,7 +87,7 @@ pub const File = struct {
8587
86 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {88 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
87 switch (base.tag) {89 switch (base.tag) {
88 .elf, .macho => {90 .coff, .elf, .macho => {
89 if (base.file != null) return;91 if (base.file != null) return;
90 base.file = try dir.createFile(sub_path, .{92 base.file = try dir.createFile(sub_path, .{
91 .truncate = false,93 .truncate = false,
...@@ -112,6 +114,7 @@ pub const File = struct {...@@ -112,6 +114,7 @@ pub const File = struct {
112 /// after allocateDeclIndexes for any given Decl.114 /// after allocateDeclIndexes for any given Decl.
113 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {115 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
114 switch (base.tag) {116 switch (base.tag) {
117 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
115 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),118 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
116 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),119 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
117 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),120 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
...@@ -121,6 +124,7 @@ pub const File = struct {...@@ -121,6 +124,7 @@ pub const File = struct {
121124
122 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {125 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
123 switch (base.tag) {126 switch (base.tag) {
127 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
124 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),128 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
125 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),129 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
126 .c, .wasm => {},130 .c, .wasm => {},
...@@ -131,6 +135,7 @@ pub const File = struct {...@@ -131,6 +135,7 @@ pub const File = struct {
131 /// any given Decl.135 /// any given Decl.
132 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {136 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
133 switch (base.tag) {137 switch (base.tag) {
138 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
134 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),139 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
135 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),140 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
136 .c, .wasm => {},141 .c, .wasm => {},
...@@ -140,6 +145,7 @@ pub const File = struct {...@@ -140,6 +145,7 @@ pub const File = struct {
140 pub fn deinit(base: *File) void {145 pub fn deinit(base: *File) void {
141 if (base.file) |f| f.close();146 if (base.file) |f| f.close();
142 switch (base.tag) {147 switch (base.tag) {
148 .coff => @fieldParentPtr(Coff, "base", base).deinit(),
143 .elf => @fieldParentPtr(Elf, "base", base).deinit(),149 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
144 .macho => @fieldParentPtr(MachO, "base", base).deinit(),150 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
145 .c => @fieldParentPtr(C, "base", base).deinit(),151 .c => @fieldParentPtr(C, "base", base).deinit(),
...@@ -149,6 +155,11 @@ pub const File = struct {...@@ -149,6 +155,11 @@ pub const File = struct {
149155
150 pub fn destroy(base: *File) void {156 pub fn destroy(base: *File) void {
151 switch (base.tag) {157 switch (base.tag) {
158 .coff => {
159 const parent = @fieldParentPtr(Coff, "base", base);
160 parent.deinit();
161 base.allocator.destroy(parent);
162 },
152 .elf => {163 .elf => {
153 const parent = @fieldParentPtr(Elf, "base", base);164 const parent = @fieldParentPtr(Elf, "base", base);
154 parent.deinit();165 parent.deinit();
...@@ -177,6 +188,7 @@ pub const File = struct {...@@ -177,6 +188,7 @@ pub const File = struct {
177 defer tracy.end();188 defer tracy.end();
178189
179 try switch (base.tag) {190 try switch (base.tag) {
191 .coff => @fieldParentPtr(Coff, "base", base).flush(module),
180 .elf => @fieldParentPtr(Elf, "base", base).flush(module),192 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
181 .macho => @fieldParentPtr(MachO, "base", base).flush(module),193 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
182 .c => @fieldParentPtr(C, "base", base).flush(module),194 .c => @fieldParentPtr(C, "base", base).flush(module),
...@@ -186,6 +198,7 @@ pub const File = struct {...@@ -186,6 +198,7 @@ pub const File = struct {
186198
187 pub fn freeDecl(base: *File, decl: *Module.Decl) void {199 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188 switch (base.tag) {200 switch (base.tag) {
201 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
189 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),202 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),203 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
191 .c => unreachable,204 .c => unreachable,
...@@ -195,6 +208,7 @@ pub const File = struct {...@@ -195,6 +208,7 @@ pub const File = struct {
195208
196 pub fn errorFlags(base: *File) ErrorFlags {209 pub fn errorFlags(base: *File) ErrorFlags {
197 return switch (base.tag) {210 return switch (base.tag) {
211 .coff => @fieldParentPtr(Coff, "base", base).error_flags,
198 .elf => @fieldParentPtr(Elf, "base", base).error_flags,212 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
199 .macho => @fieldParentPtr(MachO, "base", base).error_flags,213 .macho => @fieldParentPtr(MachO, "base", base).error_flags,
200 .c => return .{ .no_entry_point_found = false },214 .c => return .{ .no_entry_point_found = false },
...@@ -211,6 +225,7 @@ pub const File = struct {...@@ -211,6 +225,7 @@ pub const File = struct {
211 exports: []const *Module.Export,225 exports: []const *Module.Export,
212 ) !void {226 ) !void {
213 switch (base.tag) {227 switch (base.tag) {
228 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
214 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),229 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
215 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),230 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
216 .c => return {},231 .c => return {},
...@@ -220,6 +235,7 @@ pub const File = struct {...@@ -220,6 +235,7 @@ pub const File = struct {
220235
221 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {236 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
222 switch (base.tag) {237 switch (base.tag) {
238 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
223 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),239 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
224 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),240 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
225 .c => unreachable,241 .c => unreachable,
...@@ -228,6 +244,7 @@ pub const File = struct {...@@ -228,6 +244,7 @@ pub const File = struct {
228 }244 }
229245
230 pub const Tag = enum {246 pub const Tag = enum {
247 coff,
231 elf,248 elf,
232 macho,249 macho,
233 c,250 c,
...@@ -239,6 +256,7 @@ pub const File = struct {...@@ -239,6 +256,7 @@ pub const File = struct {
239 };256 };
240257
241 pub const C = @import("link/C.zig");258 pub const C = @import("link/C.zig");
259 pub const Coff = @import("link/Coff.zig");
242 pub const Elf = @import("link/Elf.zig");260 pub const Elf = @import("link/Elf.zig");
243 pub const MachO = @import("link/MachO.zig");261 pub const MachO = @import("link/MachO.zig");
244 pub const Wasm = @import("link/Wasm.zig");262 pub const Wasm = @import("link/Wasm.zig");
src-self-hosted/link/Coff.zig created+792
...@@ -0,0 +1,792 @@
1const Coff = @This();
2
3const std = @import("std");
4const log = std.log.scoped(.link);
5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;
7const fs = std.fs;
8
9const trace = @import("../tracy.zig").trace;
10const Module = @import("../Module.zig");
11const codegen = @import("../codegen.zig");
12const link = @import("../link.zig");
13
14const allocation_padding = 4 / 3;
15const minimum_text_block_size = 64 * allocation_padding;
16
17const section_alignment = 4096;
18const file_alignment = 512;
19const image_base = 0x400_000;
20const section_table_size = 2 * 40;
21comptime {
22 std.debug.assert(std.mem.isAligned(image_base, section_alignment));
23}
24
25pub const base_tag: link.File.Tag = .coff;
26
27const msdos_stub = @embedFile("msdos-stub.bin");
28
29base: link.File,
30ptr_width: enum { p32, p64 },
31error_flags: link.File.ErrorFlags = .{},
32
33text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
34last_text_block: ?*TextBlock = null,
35
36/// Section table file pointer.
37section_table_offset: u32 = 0,
38/// Section data file pointer.
39section_data_offset: u32 = 0,
40/// Optiona header file pointer.
41optional_header_offset: u32 = 0,
42
43/// Absolute virtual address of the offset table when the executable is loaded in memory.
44offset_table_virtual_address: u32 = 0,
45/// Current size of the offset table on disk, must be a multiple of `file_alignment`
46offset_table_size: u32 = 0,
47/// Contains absolute virtual addresses
48offset_table: std.ArrayListUnmanaged(u64) = .{},
49/// Free list of offset table indices
50offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
51
52/// Virtual address of the entry point procedure relative to `image_base`
53entry_addr: ?u32 = null,
54
55/// Absolute virtual address of the text section when the executable is loaded in memory.
56text_section_virtual_address: u32 = 0,
57/// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
58text_section_size: u32 = 0,
59
60offset_table_size_dirty: bool = false,
61text_section_size_dirty: bool = false,
62/// This flag is set when the virtual size of the whole image file when loaded in memory has changed
63/// and needs to be updated in the optional header.
64size_of_image_dirty: bool = false,
65
66pub const TextBlock = struct {
67 /// Offset of the code relative to the start of the text section
68 text_offset: u32,
69 /// Used size of the text block
70 size: u32,
71 /// This field is undefined for symbols with size = 0.
72 offset_table_index: u32,
73 /// Points to the previous and next neighbors, based on the `text_offset`.
74 /// This can be used to find, for example, the capacity of this `TextBlock`.
75 prev: ?*TextBlock,
76 next: ?*TextBlock,
77
78 pub const empty = TextBlock{
79 .text_offset = 0,
80 .size = 0,
81 .offset_table_index = undefined,
82 .prev = null,
83 .next = null,
84 };
85
86 /// Returns how much room there is to grow in virtual address space.
87 fn capacity(self: TextBlock) u64 {
88 if (self.next) |next| {
89 return next.text_offset - self.text_offset;
90 }
91 // This is the last block, the capacity is only limited by the address space.
92 return std.math.maxInt(u32) - self.text_offset;
93 }
94
95 fn freeListEligible(self: TextBlock) bool {
96 // No need to keep a free list node for the last block.
97 const next = self.next orelse return false;
98 const cap = next.text_offset - self.text_offset;
99 const ideal_cap = self.size * allocation_padding;
100 if (cap <= ideal_cap) return false;
101 const surplus = cap - ideal_cap;
102 return surplus >= minimum_text_block_size;
103 }
104
105 /// Absolute virtual address of the text block when the file is loaded in memory.
106 fn getVAddr(self: TextBlock, coff: Coff) u32 {
107 return coff.text_section_virtual_address + self.text_offset;
108 }
109};
110
111pub const SrcFn = void;
112
113pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
114 assert(options.object_format == .coff);
115
116 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
117 errdefer file.close();
118
119 var coff_file = try allocator.create(Coff);
120 errdefer allocator.destroy(coff_file);
121
122 coff_file.* = openFile(allocator, file, options) catch |err| switch (err) {
123 error.IncrFailed => try createFile(allocator, file, options),
124 else => |e| return e,
125 };
126
127 return &coff_file.base;
128}
129
130/// Returns error.IncrFailed if incremental update could not be performed.
131fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
132 switch (options.output_mode) {
133 .Exe => {},
134 .Obj => return error.IncrFailed,
135 .Lib => return error.IncrFailed,
136 }
137 var self: Coff = .{
138 .base = .{
139 .file = file,
140 .tag = .coff,
141 .options = options,
142 .allocator = allocator,
143 },
144 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
145 32 => .p32,
146 64 => .p64,
147 else => return error.UnsupportedELFArchitecture,
148 },
149 };
150 errdefer self.deinit();
151
152 // TODO implement reading the PE/COFF file
153 return error.IncrFailed;
154}
155
156/// Truncates the existing file contents and overwrites the contents.
157/// Returns an error if `file` is not already open with +read +write +seek abilities.
158fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
159 // TODO Write object specific relocations, COFF symbol table, then enable object file output.
160 switch (options.output_mode) {
161 .Exe => {},
162 .Obj => return error.TODOImplementWritingObjFiles,
163 .Lib => return error.TODOImplementWritingLibFiles,
164 }
165 var self: Coff = .{
166 .base = .{
167 .tag = .coff,
168 .options = options,
169 .allocator = allocator,
170 .file = file,
171 },
172 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
173 32 => .p32,
174 64 => .p64,
175 else => return error.UnsupportedCOFFArchitecture,
176 },
177 };
178 errdefer self.deinit();
179
180 var coff_file_header_offset: u32 = 0;
181 if (options.output_mode == .Exe) {
182 // Write the MS-DOS stub and the PE signature
183 try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
184 coff_file_header_offset = msdos_stub.len + 4;
185 }
186
187 // COFF file header
188 const data_directory_count = 0;
189 var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
190 var index: usize = 0;
191
192 const machine = self.base.options.target.cpu.arch.toCoffMachine();
193 if (machine == .Unknown) {
194 return error.UnsupportedCOFFArchitecture;
195 }
196 std.mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
197 index += 2;
198
199 // Number of sections (we only use .got, .text)
200 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
201 index += 2;
202 // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
203 std.mem.set(u8, hdr_data[index..][0..12], 0);
204 index += 12;
205
206 const optional_header_size = switch (options.output_mode) {
207 .Exe => data_directory_count * 8 + switch (self.ptr_width) {
208 .p32 => @as(u16, 96),
209 .p64 => 112,
210 },
211 else => 0,
212 };
213
214 const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
215 const default_offset_table_size = file_alignment;
216 const default_size_of_code = 0;
217
218 self.section_data_offset = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
219 const section_data_relative_virtual_address = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
220 self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
221 self.offset_table_size = default_offset_table_size;
222 self.section_table_offset = section_table_offset;
223 self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
224 self.text_section_size = default_size_of_code;
225
226 // Size of file when loaded in memory
227 const size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
228
229 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
230 index += 2;
231
232 // Characteristics
233 var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
234 if (options.output_mode == .Exe) {
235 characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
236 }
237 switch (self.ptr_width) {
238 .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
239 .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
240 }
241 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
242 index += 2;
243
244 assert(index == 20);
245 try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
246
247 if (options.output_mode == .Exe) {
248 self.optional_header_offset = coff_file_header_offset + 20;
249 // Optional header
250 index = 0;
251 std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
252 .p32 => @as(u16, 0x10b),
253 .p64 => 0x20b,
254 });
255 index += 2;
256
257 // Linker version (u8 + u8)
258 std.mem.set(u8, hdr_data[index..][0..2], 0);
259 index += 2;
260
261 // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
262 std.mem.set(u8, hdr_data[index..][0..20], 0);
263 index += 20;
264
265 if (self.ptr_width == .p32) {
266 // Base of data relative to the image base (UNUSED)
267 std.mem.set(u8, hdr_data[index..][0..4], 0);
268 index += 4;
269
270 // Image base address
271 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
272 index += 4;
273 } else {
274 // Image base address
275 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
276 index += 8;
277 }
278
279 // Section alignment
280 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
281 index += 4;
282 // File alignment
283 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
284 index += 4;
285 // Required OS version, 6.0 is vista
286 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
287 index += 2;
288 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
289 index += 2;
290 // Image version
291 std.mem.set(u8, hdr_data[index..][0..4], 0);
292 index += 4;
293 // Required subsystem version, same as OS version
294 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
295 index += 2;
296 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
297 index += 2;
298 // Reserved zeroes (u32)
299 std.mem.set(u8, hdr_data[index..][0..4], 0);
300 index += 4;
301 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
302 index += 4;
303 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
304 index += 4;
305 // CheckSum (u32)
306 std.mem.set(u8, hdr_data[index..][0..4], 0);
307 index += 4;
308 // Subsystem, TODO: Let users specify the subsystem, always CUI for now
309 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
310 index += 2;
311 // DLL characteristics
312 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
313 index += 2;
314
315 switch (self.ptr_width) {
316 .p32 => {
317 // Size of stack reserve + commit
318 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
319 index += 4;
320 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
321 index += 4;
322 // Size of heap reserve + commit
323 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
324 index += 4;
325 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
326 index += 4;
327 },
328 .p64 => {
329 // Size of stack reserve + commit
330 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
331 index += 8;
332 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
333 index += 8;
334 // Size of heap reserve + commit
335 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
336 index += 8;
337 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
338 index += 8;
339 },
340 }
341
342 // Reserved zeroes
343 std.mem.set(u8, hdr_data[index..][0..4], 0);
344 index += 4;
345
346 // Number of data directories
347 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
348 index += 4;
349 // Initialize data directories to zero
350 std.mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
351 index += data_directory_count * 8;
352
353 assert(index == optional_header_size);
354 }
355
356 // Write section table.
357 // First, the .got section
358 hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
359 index += 8;
360 if (options.output_mode == .Exe) {
361 // Virtual size (u32)
362 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
363 index += 4;
364 // Virtual address (u32)
365 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
366 index += 4;
367 } else {
368 std.mem.set(u8, hdr_data[index..][0..8], 0);
369 index += 8;
370 }
371 // Size of raw data (u32)
372 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
373 index += 4;
374 // File pointer to the start of the section
375 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
376 index += 4;
377 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
378 std.mem.set(u8, hdr_data[index..][0..12], 0);
379 index += 12;
380 // Section flags
381 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
382 index += 4;
383 // Then, the .text section
384 hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
385 index += 8;
386 if (options.output_mode == .Exe) {
387 // Virtual size (u32)
388 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
389 index += 4;
390 // Virtual address (u32)
391 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
392 index += 4;
393 } else {
394 std.mem.set(u8, hdr_data[index..][0..8], 0);
395 index += 8;
396 }
397 // Size of raw data (u32)
398 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
399 index += 4;
400 // File pointer to the start of the section
401 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
402 index += 4;
403 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
404 std.mem.set(u8, hdr_data[index..][0..12], 0);
405 index += 12;
406 // Section flags
407 std.mem.writeIntLittle(
408 u32,
409 hdr_data[index..][0..4],
410 std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
411 );
412 index += 4;
413
414 assert(index == optional_header_size + section_table_size);
415 try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
416 try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
417
418 return self;
419}
420
421pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
422 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
423
424 if (self.offset_table_free_list.popOrNull()) |i| {
425 decl.link.coff.offset_table_index = i;
426 } else {
427 decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
428 _ = self.offset_table.addOneAssumeCapacity();
429
430 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
431 if (self.offset_table.items.len > self.offset_table_size / entry_size) {
432 self.offset_table_size_dirty = true;
433 }
434 }
435
436 self.offset_table.items[decl.link.coff.offset_table_index] = 0;
437}
438
439fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
440 const new_block_min_capacity = new_block_size * allocation_padding;
441
442 // We use these to indicate our intention to update metadata, placing the new block,
443 // and possibly removing a free list node.
444 // It would be simpler to do it inside the for loop below, but that would cause a
445 // problem if an error was returned later in the function. So this action
446 // is actually carried out at the end of the function, when errors are no longer possible.
447 var block_placement: ?*TextBlock = null;
448 var free_list_removal: ?usize = null;
449
450 const vaddr = blk: {
451 var i: usize = 0;
452 while (i < self.text_block_free_list.items.len) {
453 const free_block = self.text_block_free_list.items[i];
454
455 const next_block_text_offset = free_block.text_offset + free_block.capacity();
456 const new_block_text_offset = std.mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
457 if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
458 block_placement = free_block;
459
460 const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
461 if (remaining_capacity < minimum_text_block_size) {
462 free_list_removal = i;
463 }
464
465 break :blk new_block_text_offset + self.text_section_virtual_address;
466 } else {
467 if (!free_block.freeListEligible()) {
468 _ = self.text_block_free_list.swapRemove(i);
469 } else {
470 i += 1;
471 }
472 continue;
473 }
474 } else if (self.last_text_block) |last| {
475 const new_block_vaddr = std.mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
476 block_placement = last;
477 break :blk new_block_vaddr;
478 } else {
479 break :blk self.text_section_virtual_address;
480 }
481 };
482
483 const expand_text_section = block_placement == null or block_placement.?.next == null;
484 if (expand_text_section) {
485 const needed_size = @intCast(u32, std.mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
486 if (needed_size > self.text_section_size) {
487 const current_text_section_virtual_size = std.mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
488 const new_text_section_virtual_size = std.mem.alignForwardGeneric(u32, needed_size, section_alignment);
489 if (current_text_section_virtual_size != new_text_section_virtual_size) {
490 self.size_of_image_dirty = true;
491 // Write new virtual size
492 var buf: [4]u8 = undefined;
493 std.mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
494 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
495 }
496
497 self.text_section_size = needed_size;
498 self.text_section_size_dirty = true;
499 }
500 self.last_text_block = text_block;
501 }
502 text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
503 text_block.size = @intCast(u32, new_block_size);
504
505 // This function can also reallocate a text block.
506 // In this case we need to "unplug" it from its previous location before
507 // plugging it in to its new location.
508 if (text_block.prev) |prev| {
509 prev.next = text_block.next;
510 }
511 if (text_block.next) |next| {
512 next.prev = text_block.prev;
513 }
514
515 if (block_placement) |big_block| {
516 text_block.prev = big_block;
517 text_block.next = big_block.next;
518 big_block.next = text_block;
519 } else {
520 text_block.prev = null;
521 text_block.next = null;
522 }
523 if (free_list_removal) |i| {
524 _ = self.text_block_free_list.swapRemove(i);
525 }
526 return vaddr;
527}
528
529fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
530 const block_vaddr = text_block.getVAddr(self.*);
531 const align_ok = std.mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
532 const need_realloc = !align_ok or new_block_size > text_block.capacity();
533 if (!need_realloc) return @as(u64, block_vaddr);
534 return self.allocateTextBlock(text_block, new_block_size, alignment);
535}
536
537fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
538 text_block.size = @intCast(u32, new_block_size);
539 if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
540 self.text_block_free_list.append(self.base.allocator, text_block) catch {};
541 }
542}
543
544fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
545 var already_have_free_list_node = false;
546 {
547 var i: usize = 0;
548 // TODO turn text_block_free_list into a hash map
549 while (i < self.text_block_free_list.items.len) {
550 if (self.text_block_free_list.items[i] == text_block) {
551 _ = self.text_block_free_list.swapRemove(i);
552 continue;
553 }
554 if (self.text_block_free_list.items[i] == text_block.prev) {
555 already_have_free_list_node = true;
556 }
557 i += 1;
558 }
559 }
560 if (self.last_text_block == text_block) {
561 self.last_text_block = text_block.prev;
562 }
563 if (text_block.prev) |prev| {
564 prev.next = text_block.next;
565
566 if (!already_have_free_list_node and prev.freeListEligible()) {
567 // The free list is heuristics, it doesn't have to be perfect, so we can
568 // ignore the OOM here.
569 self.text_block_free_list.append(self.base.allocator, prev) catch {};
570 }
571 }
572
573 if (text_block.next) |next| {
574 next.prev = text_block.prev;
575 }
576}
577
578fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
579 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
580 const endian = self.base.options.target.cpu.arch.endian();
581
582 const offset_table_start = self.section_data_offset;
583 if (self.offset_table_size_dirty) {
584 const current_raw_size = self.offset_table_size;
585 const new_raw_size = self.offset_table_size * 2;
586 log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
587
588 // Move the text section to a new place in the executable
589 const current_text_section_start = self.section_data_offset + current_raw_size;
590 const new_text_section_start = self.section_data_offset + new_raw_size;
591
592 const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
593 if (amt != self.text_section_size) return error.InputOutput;
594
595 // Write the new raw size in the .got header
596 var buf: [8]u8 = undefined;
597 std.mem.writeIntLittle(u32, buf[0..4], new_raw_size);
598 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
599 // Write the new .text section file offset in the .text section header
600 std.mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
601 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
602
603 const current_virtual_size = std.mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
604 const new_virtual_size = std.mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
605 // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
606 // and the virutal size of the `.got` section
607
608 if (new_virtual_size != current_virtual_size) {
609 log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
610 self.size_of_image_dirty = true;
611 const va_offset = new_virtual_size - current_virtual_size;
612
613 // Write .got virtual size
614 std.mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
615 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
616
617 // Write .text new virtual address
618 self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
619 std.mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
620 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
621
622 // Fix the VAs in the offset table
623 for (self.offset_table.items) |*va, idx| {
624 if (va.* != 0) {
625 va.* += va_offset;
626
627 switch (entry_size) {
628 4 => {
629 std.mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
630 try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
631 },
632 8 => {
633 std.mem.writeInt(u64, &buf, va.*, endian);
634 try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
635 },
636 else => unreachable,
637 }
638 }
639 }
640 }
641 self.offset_table_size = new_raw_size;
642 self.offset_table_size_dirty = false;
643 }
644 // Write the new entry
645 switch (entry_size) {
646 4 => {
647 var buf: [4]u8 = undefined;
648 std.mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
649 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
650 },
651 8 => {
652 var buf: [8]u8 = undefined;
653 std.mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
654 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
655 },
656 else => unreachable,
657 }
658}
659
660pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
661 // TODO COFF/PE debug information
662 // TODO Implement exports
663 const tracy = trace(@src());
664 defer tracy.end();
665
666 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
667 defer code_buffer.deinit();
668
669 const typed_value = decl.typed_value.most_recent.typed_value;
670 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
671 const code = switch (res) {
672 .externally_managed => |x| x,
673 .appended => code_buffer.items,
674 .fail => |em| {
675 decl.analysis = .codegen_failure;
676 try module.failed_decls.put(module.gpa, decl, em);
677 return;
678 },
679 };
680
681 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
682 const curr_size = decl.link.coff.size;
683 if (curr_size != 0) {
684 const capacity = decl.link.coff.capacity();
685 const need_realloc = code.len > capacity or
686 !std.mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
687 if (need_realloc) {
688 const curr_vaddr = self.getDeclVAddr(decl);
689 const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
690 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
691 if (vaddr != curr_vaddr) {
692 log.debug(" (writing new offset table entry)\n", .{});
693 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
694 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
695 }
696 } else if (code.len < curr_size) {
697 self.shrinkTextBlock(&decl.link.coff, code.len);
698 }
699 } else {
700 const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
701 log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ std.mem.spanZ(decl.name), vaddr, code.len });
702 errdefer self.freeTextBlock(&decl.link.coff);
703 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
704 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
705 }
706
707 // Write the code into the file
708 try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
709
710 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
711 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
712 return self.updateDeclExports(module, decl, decl_exports);
713}
714
715pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
716 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
717 self.freeTextBlock(&decl.link.coff);
718 self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
719}
720
721pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
722 for (exports) |exp| {
723 if (exp.options.section) |section_name| {
724 if (!std.mem.eql(u8, section_name, ".text")) {
725 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
726 module.failed_exports.putAssumeCapacityNoClobber(
727 exp,
728 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
729 );
730 continue;
731 }
732 }
733 if (std.mem.eql(u8, exp.options.name, "_start")) {
734 self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
735 } else {
736 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
737 module.failed_exports.putAssumeCapacityNoClobber(
738 exp,
739 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
740 );
741 continue;
742 }
743 }
744}
745
746pub fn flush(self: *Coff, module: *Module) !void {
747 if (self.text_section_size_dirty) {
748 // Write the new raw size in the .text header
749 var buf: [4]u8 = undefined;
750 std.mem.writeIntLittle(u32, &buf, self.text_section_size);
751 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
752 try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
753 self.text_section_size_dirty = false;
754 }
755
756 if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
757 const new_size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
758 var buf: [4]u8 = undefined;
759 std.mem.writeIntLittle(u32, &buf, new_size_of_image);
760 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
761 self.size_of_image_dirty = false;
762 }
763
764 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
765 log.debug("flushing. no_entry_point_found = true\n", .{});
766 self.error_flags.no_entry_point_found = true;
767 } else {
768 log.debug("flushing. no_entry_point_found = false\n", .{});
769 self.error_flags.no_entry_point_found = false;
770
771 if (self.base.options.output_mode == .Exe) {
772 // Write AddressOfEntryPoint
773 var buf: [4]u8 = undefined;
774 std.mem.writeIntLittle(u32, &buf, self.entry_addr.?);
775 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
776 }
777 }
778}
779
780pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
781 return self.text_section_virtual_address + decl.link.coff.text_offset;
782}
783
784pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
785 // TODO Implement this
786}
787
788pub fn deinit(self: *Coff) void {
789 self.text_block_free_list.deinit(self.base.allocator);
790 self.offset_table.deinit(self.base.allocator);
791 self.offset_table_free_list.deinit(self.base.allocator);
792}
src-self-hosted/link/Elf.zig+7-1
...@@ -1735,7 +1735,13 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -1735,7 +1735,13 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1735 } else {1735 } else {
1736 // TODO implement .debug_info for global variables1736 // TODO implement .debug_info for global variables
1737 }1737 }
1738 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer, &dbg_info_type_relocs);1738 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
1739 .dwarf = .{
1740 .dbg_line = &dbg_line_buffer,
1741 .dbg_info = &dbg_info_buffer,
1742 .dbg_info_type_relocs = &dbg_info_type_relocs,
1743 },
1744 });
1739 const code = switch (res) {1745 const code = switch (res) {
1740 .externally_managed => |x| x,1746 .externally_managed => |x| x,
1741 .appended => code_buffer.items,1747 .appended => code_buffer.items,
src-self-hosted/link/MachO.zig+1-24
...@@ -316,31 +316,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -316,31 +316,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
316 var code_buffer = std.ArrayList(u8).init(self.base.allocator);316 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
317 defer code_buffer.deinit();317 defer code_buffer.deinit();
318318
319 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
320 defer dbg_line_buffer.deinit();
321
322 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
323 defer dbg_info_buffer.deinit();
324
325 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
326 defer {
327 var it = dbg_info_type_relocs.iterator();
328 while (it.next()) |entry| {
329 entry.value.relocs.deinit(self.base.allocator);
330 }
331 dbg_info_type_relocs.deinit(self.base.allocator);
332 }
333
334 const typed_value = decl.typed_value.most_recent.typed_value;319 const typed_value = decl.typed_value.most_recent.typed_value;
335 const res = try codegen.generateSymbol(320 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
336 &self.base,
337 decl.src(),
338 typed_value,
339 &code_buffer,
340 &dbg_line_buffer,
341 &dbg_info_buffer,
342 &dbg_info_type_relocs,
343 );
344321
345 const code = switch (res) {322 const code = switch (res) {
346 .externally_managed => |x| x,323 .externally_managed => |x| x,
src-self-hosted/link/msdos-stub.bin created
Binary files /dev/null and b/src-self-hosted/link/msdos-stub.bin differ
src-self-hosted/main.zig+8-6
...@@ -153,8 +153,8 @@ const usage_build_generic =...@@ -153,8 +153,8 @@ const usage_build_generic =
153 \\ elf Executable and Linking Format153 \\ elf Executable and Linking Format
154 \\ c Compile to C source code154 \\ c Compile to C source code
155 \\ wasm WebAssembly155 \\ wasm WebAssembly
156 \\ pe Portable Executable (Windows)
156 \\ coff (planned) Common Object File Format (Windows)157 \\ coff (planned) Common Object File Format (Windows)
157 \\ pe (planned) Portable Executable (Windows)
158 \\ macho (planned) macOS relocatables158 \\ macho (planned) macOS relocatables
159 \\ hex (planned) Intel IHEX159 \\ hex (planned) Intel IHEX
160 \\ raw (planned) Dump machine code directly160 \\ raw (planned) Dump machine code directly
...@@ -451,7 +451,7 @@ fn buildOutputType(...@@ -451,7 +451,7 @@ fn buildOutputType(
451 } else if (mem.eql(u8, ofmt, "coff")) {451 } else if (mem.eql(u8, ofmt, "coff")) {
452 break :blk .coff;452 break :blk .coff;
453 } else if (mem.eql(u8, ofmt, "pe")) {453 } else if (mem.eql(u8, ofmt, "pe")) {
454 break :blk .coff;454 break :blk .pe;
455 } else if (mem.eql(u8, ofmt, "macho")) {455 } else if (mem.eql(u8, ofmt, "macho")) {
456 break :blk .macho;456 break :blk .macho;
457 } else if (mem.eql(u8, ofmt, "wasm")) {457 } else if (mem.eql(u8, ofmt, "wasm")) {
...@@ -524,17 +524,19 @@ fn buildOutputType(...@@ -524,17 +524,19 @@ fn buildOutputType(
524 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});524 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
525 continue;525 continue;
526 }) |line| {526 }) |line| {
527 if (mem.eql(u8, line, "update")) {527 const actual_line = mem.trimRight(u8, line, "\r\n ");
528
529 if (mem.eql(u8, actual_line, "update")) {
528 if (output_mode == .Exe) {530 if (output_mode == .Exe) {
529 try module.makeBinFileWritable();531 try module.makeBinFileWritable();
530 }532 }
531 try updateModule(gpa, &module, zir_out_path);533 try updateModule(gpa, &module, zir_out_path);
532 } else if (mem.eql(u8, line, "exit")) {534 } else if (mem.eql(u8, actual_line, "exit")) {
533 break;535 break;
534 } else if (mem.eql(u8, line, "help")) {536 } else if (mem.eql(u8, actual_line, "help")) {
535 try stderr.writeAll(repl_help);537 try stderr.writeAll(repl_help);
536 } else {538 } else {
537 try stderr.print("unknown command: {}\n", .{line});539 try stderr.print("unknown command: {}\n", .{actual_line});
538 }540 }
539 } else {541 } else {
540 break;542 break;