authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-08-30 12:08:18-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-21 14:09:14-07:00
logf58200e3f2967a06f343c9fc9dcae9de18def92a
tree84257e40a7a0186fbc10cf7467e65f004036d3e3
parent2a97e0af6d42e038d962890a320e262e676d44cb

Elf2: create a new linker from scratch

This iteration already has significantly better incremental support. Closes #24110

45 files changed, 4141 insertions(+), 536 deletions(-)

CMakeLists.txt+2
......@@ -583,6 +583,7 @@ set(ZIG_STAGE2_SOURCES
583583 src/link/Elf/relocatable.zig
584584 src/link/Elf/relocation.zig
585585 src/link/Elf/synthetic_sections.zig
586 src/link/Elf2.zig
586587 src/link/Goff.zig
587588 src/link/LdScript.zig
588589 src/link/Lld.zig
......@@ -612,6 +613,7 @@ set(ZIG_STAGE2_SOURCES
612613 src/link/MachO/synthetic.zig
613614 src/link/MachO/Thunk.zig
614615 src/link/MachO/uuid.zig
616 src/link/MappedFile.zig
615617 src/link/Queue.zig
616618 src/link/StringTable.zig
617619 src/link/Wasm.zig
build.zig+1
......@@ -202,6 +202,7 @@ pub fn build(b: *std.Build) !void {
202202 });
203203 exe.pie = pie;
204204 exe.entitlements = entitlements;
205 exe.use_new_linker = b.option(bool, "new-linker", "Use the new linker");
205206
206207 const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
207208 exe.use_llvm = use_llvm;
ci/x86_64-linux-debug-llvm.sh created100644→100755
lib/compiler_rt/fma.zig+4-4
......@@ -203,7 +203,7 @@ fn add_adjusted(a: f64, b: f64) f64 {
203203 if (uhii & 1 == 0) {
204204 // hibits += copysign(1.0, sum.hi, sum.lo)
205205 const uloi: u64 = @bitCast(sum.lo);
206 uhii += 1 - ((uhii ^ uloi) >> 62);
206 uhii = uhii + 1 - ((uhii ^ uloi) >> 62);
207207 sum.hi = @bitCast(uhii);
208208 }
209209 }
......@@ -217,7 +217,7 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) f64 {
217217 const bits_lost = -@as(i32, @intCast((uhii >> 52) & 0x7FF)) - scale + 1;
218218 if ((bits_lost != 1) == (uhii & 1 != 0)) {
219219 const uloi: u64 = @bitCast(sum.lo);
220 uhii += 1 - (((uhii ^ uloi) >> 62) & 2);
220 uhii = uhii + 1 - (((uhii ^ uloi) >> 62) & 2);
221221 sum.hi = @bitCast(uhii);
222222 }
223223 }
......@@ -259,7 +259,7 @@ fn add_adjusted128(a: f128, b: f128) f128 {
259259 if (uhii & 1 == 0) {
260260 // hibits += copysign(1.0, sum.hi, sum.lo)
261261 const uloi: u128 = @bitCast(sum.lo);
262 uhii += 1 - ((uhii ^ uloi) >> 126);
262 uhii = uhii + 1 - ((uhii ^ uloi) >> 126);
263263 sum.hi = @bitCast(uhii);
264264 }
265265 }
......@@ -284,7 +284,7 @@ fn add_and_denorm128(a: f128, b: f128, scale: i32) f128 {
284284 const bits_lost = -@as(i32, @intCast((uhii >> 112) & 0x7FFF)) - scale + 1;
285285 if ((bits_lost != 1) == (uhii & 1 != 0)) {
286286 const uloi: u128 = @bitCast(sum.lo);
287 uhii += 1 - (((uhii ^ uloi) >> 126) & 2);
287 uhii = uhii + 1 - (((uhii ^ uloi) >> 126) & 2);
288288 sum.hi = @bitCast(uhii);
289289 }
290290 }
lib/std/Build/Step/Compile.zig+3
......@@ -192,6 +192,7 @@ want_lto: ?bool = null,
192192
193193use_llvm: ?bool,
194194use_lld: ?bool,
195use_new_linker: ?bool,
195196
196197/// Corresponds to the `-fallow-so-scripts` / `-fno-allow-so-scripts` CLI
197198/// flags, overriding the global user setting provided to the `zig build`
......@@ -441,6 +442,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
441442
442443 .use_llvm = options.use_llvm,
443444 .use_lld = options.use_lld,
445 .use_new_linker = null,
444446
445447 .zig_process = null,
446448 };
......@@ -1096,6 +1098,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
10961098
10971099 try addFlag(&zig_args, "llvm", compile.use_llvm);
10981100 try addFlag(&zig_args, "lld", compile.use_lld);
1101 try addFlag(&zig_args, "new-linker", compile.use_new_linker);
10991102
11001103 if (compile.root_module.resolved_target.?.query.ofmt) |ofmt| {
11011104 try zig_args.append(try std.fmt.allocPrint(arena, "-ofmt={s}", .{@tagName(ofmt)}));
lib/std/elf.zig+493-102
......@@ -323,6 +323,8 @@ pub const PT_LOPROC = 0x70000000;
323323/// End of processor-specific
324324pub const PT_HIPROC = 0x7fffffff;
325325
326pub const PN_XNUM = 0xffff;
327
326328/// Section header table entry unused
327329pub const SHT_NULL = 0;
328330/// Program data
......@@ -385,63 +387,149 @@ pub const SHT_HIUSER = 0xffffffff;
385387// Note type for .note.gnu.build_id
386388pub const NT_GNU_BUILD_ID = 3;
387389
388/// Local symbol
389pub const STB_LOCAL = 0;
390/// Global symbol
391pub const STB_GLOBAL = 1;
392/// Weak symbol
393pub const STB_WEAK = 2;
394/// Number of defined types
395pub const STB_NUM = 3;
396/// Start of OS-specific
397pub const STB_LOOS = 10;
398/// Unique symbol
399pub const STB_GNU_UNIQUE = 10;
400/// End of OS-specific
401pub const STB_HIOS = 12;
402/// Start of processor-specific
403pub const STB_LOPROC = 13;
404/// End of processor-specific
405pub const STB_HIPROC = 15;
406
407pub const STB_MIPS_SPLIT_COMMON = 13;
408
409/// Symbol type is unspecified
410pub const STT_NOTYPE = 0;
411/// Symbol is a data object
412pub const STT_OBJECT = 1;
413/// Symbol is a code object
414pub const STT_FUNC = 2;
415/// Symbol associated with a section
416pub const STT_SECTION = 3;
417/// Symbol's name is file name
418pub const STT_FILE = 4;
419/// Symbol is a common data object
420pub const STT_COMMON = 5;
421/// Symbol is thread-local data object
422pub const STT_TLS = 6;
423/// Number of defined types
424pub const STT_NUM = 7;
425/// Start of OS-specific
426pub const STT_LOOS = 10;
427/// Symbol is indirect code object
428pub const STT_GNU_IFUNC = 10;
429/// End of OS-specific
430pub const STT_HIOS = 12;
431/// Start of processor-specific
432pub const STT_LOPROC = 13;
433/// End of processor-specific
434pub const STT_HIPROC = 15;
390/// Deprecated, use `@intFromEnum(std.elf.STB.LOCAL)`
391pub const STB_LOCAL = @intFromEnum(STB.LOCAL);
392/// Deprecated, use `@intFromEnum(std.elf.STB.GLOBAL)`
393pub const STB_GLOBAL = @intFromEnum(STB.GLOBAL);
394/// Deprecated, use `@intFromEnum(std.elf.STB.WEAK)`
395pub const STB_WEAK = @intFromEnum(STB.WEAK);
396/// Deprecated, use `std.elf.STB.NUM`
397pub const STB_NUM = STB.NUM;
398/// Deprecated, use `@intFromEnum(std.elf.STB.LOOS)`
399pub const STB_LOOS = @intFromEnum(STB.LOOS);
400/// Deprecated, use `@intFromEnum(std.elf.STB.GNU_UNIQUE)`
401pub const STB_GNU_UNIQUE = @intFromEnum(STB.GNU_UNIQUE);
402/// Deprecated, use `@intFromEnum(std.elf.STB.HIOS)`
403pub const STB_HIOS = @intFromEnum(STB.HIOS);
404/// Deprecated, use `@intFromEnum(std.elf.STB.LOPROC)`
405pub const STB_LOPROC = @intFromEnum(STB.LOPROC);
406/// Deprecated, use `@intFromEnum(std.elf.STB.HIPROC)`
407pub const STB_HIPROC = @intFromEnum(STB.HIPROC);
408
409/// Deprecated, use `@intFromEnum(std.elf.STB.MIPS_SPLIT_COMMON)`
410pub const STB_MIPS_SPLIT_COMMON = @intFromEnum(STB.MIBS_SPLIT_COMMON);
411
412/// Deprecated, use `@intFromEnum(std.elf.STT.NOTYPE)`
413pub const STT_NOTYPE = @intFromEnum(STT.NOTYPE);
414/// Deprecated, use `@intFromEnum(std.elf.STT.OBJECT)`
415pub const STT_OBJECT = @intFromEnum(STT.OBJECT);
416/// Deprecated, use `@intFromEnum(std.elf.STT.FUNC)`
417pub const STT_FUNC = @intFromEnum(STT.FUNC);
418/// Deprecated, use `@intFromEnum(std.elf.STT.SECTION)`
419pub const STT_SECTION = @intFromEnum(STT.SECTION);
420/// Deprecated, use `@intFromEnum(std.elf.STT.FILE)`
421pub const STT_FILE = @intFromEnum(STT.FILE);
422/// Deprecated, use `@intFromEnum(std.elf.STT.COMMON)`
423pub const STT_COMMON = @intFromEnum(STT.COMMON);
424/// Deprecated, use `@intFromEnum(std.elf.STT.TLS)`
425pub const STT_TLS = @intFromEnum(STT.TLS);
426/// Deprecated, use `std.elf.STT.NUM`
427pub const STT_NUM = STT.NUM;
428/// Deprecated, use `@intFromEnum(std.elf.STT.LOOS)`
429pub const STT_LOOS = @intFromEnum(STT.LOOS);
430/// Deprecated, use `@intFromEnum(std.elf.STT.GNU_IFUNC)`
431pub const STT_GNU_IFUNC = @intFromEnum(STT.GNU_IFUNC);
432/// Deprecated, use `@intFromEnum(std.elf.STT.HIOS)`
433pub const STT_HIOS = @intFromEnum(STT.HIOS);
434/// Deprecated, use `@intFromEnum(std.elf.STT.LOPROC)`
435pub const STT_LOPROC = @intFromEnum(STT.LOPROC);
436/// Deprecated, use `@intFromEnum(std.elf.STT.HIPROC)`
437pub const STT_HIPROC = @intFromEnum(STT.HIPROC);
438
439/// Deprecated, use `@intFromEnum(std.elf.STT.SPARC_REGISTER)`
440pub const STT_SPARC_REGISTER = @intFromEnum(STT.SPARC_REGISTER);
441
442/// Deprecated, use `@intFromEnum(std.elf.STT.PARISC_MILLICODE)`
443pub const STT_PARISC_MILLICODE = @intFromEnum(STT.PARISC_MILLICODE);
444
445/// Deprecated, use `@intFromEnum(std.elf.STT.HP_OPAQUE)`
446pub const STT_HP_OPAQUE = @intFromEnum(STT.HP_OPAQUE);
447/// Deprecated, use `@intFromEnum(std.elf.STT.HP_STUB)`
448pub const STT_HP_STUB = @intFromEnum(STT.HP_STUB);
449
450/// Deprecated, use `@intFromEnum(std.elf.STT.ARM_TFUNC)`
451pub const STT_ARM_TFUNC = @intFromEnum(STT.ARM_TFUNC);
452/// Deprecated, use `@intFromEnum(std.elf.STT.ARM_16BIT)`
453pub const STT_ARM_16BIT = @intFromEnum(STT.ARM_16BIT);
454
455pub const STB = enum(u4) {
456 /// Local symbol
457 LOCAL = 0,
458 /// Global symbol
459 GLOBAL = 1,
460 /// Weak symbol
461 WEAK = 2,
462 _,
463
464 /// Number of defined types
465 pub const NUM = @typeInfo(STB).@"enum".fields.len;
466
467 /// Start of OS-specific
468 pub const LOOS: STB = @enumFromInt(10);
469 /// End of OS-specific
470 pub const HIOS: STB = @enumFromInt(12);
471
472 /// Unique symbol
473 pub const GNU_UNIQUE: STB = @enumFromInt(@intFromEnum(LOOS) + 0);
474
475 /// Start of processor-specific
476 pub const LOPROC: STB = @enumFromInt(13);
477 /// End of processor-specific
478 pub const HIPROC: STB = @enumFromInt(15);
479
480 pub const MIPS_SPLIT_COMMON: STB = @enumFromInt(@intFromEnum(LOPROC) + 0);
481};
482
483pub const STT = enum(u4) {
484 /// Symbol type is unspecified
485 NOTYPE = 0,
486 /// Symbol is a data object
487 OBJECT = 1,
488 /// Symbol is a code object
489 FUNC = 2,
490 /// Symbol associated with a section
491 SECTION = 3,
492 /// Symbol's name is file name
493 FILE = 4,
494 /// Symbol is a common data object
495 COMMON = 5,
496 /// Symbol is thread-local data object
497 TLS = 6,
498 _,
499
500 /// Number of defined types
501 pub const NUM = @typeInfo(STT).@"enum".fields.len;
502
503 /// Start of OS-specific
504 pub const LOOS: STT = @enumFromInt(10);
505 /// End of OS-specific
506 pub const HIOS: STT = @enumFromInt(12);
435507
436pub const STT_SPARC_REGISTER = 13;
508 /// Symbol is indirect code object
509 pub const GNU_IFUNC: STT = @enumFromInt(@intFromEnum(LOOS) + 0);
437510
438pub const STT_PARISC_MILLICODE = 13;
511 pub const HP_OPAQUE: STT = @enumFromInt(@intFromEnum(LOOS) + 1);
512 pub const HP_STUB: STT = @enumFromInt(@intFromEnum(LOOS) + 2);
439513
440pub const STT_HP_OPAQUE = (STT_LOOS + 0x1);
441pub const STT_HP_STUB = (STT_LOOS + 0x2);
514 /// Start of processor-specific
515 pub const LOPROC: STT = @enumFromInt(13);
516 /// End of processor-specific
517 pub const HIPROC: STT = @enumFromInt(15);
442518
443pub const STT_ARM_TFUNC = STT_LOPROC;
444pub const STT_ARM_16BIT = STT_HIPROC;
519 pub const SPARC_REGISTER: STT = @enumFromInt(@intFromEnum(LOPROC) + 0);
520
521 pub const PARISC_MILLICODE: STT = @enumFromInt(@intFromEnum(LOPROC) + 0);
522
523 pub const ARM_TFUNC: STT = @enumFromInt(@intFromEnum(LOPROC) + 0);
524 pub const ARM_16BIT: STT = @enumFromInt(@intFromEnum(HIPROC) + 2);
525};
526
527pub const STV = enum(u3) {
528 DEFAULT = 0,
529 INTERNAL = 1,
530 HIDDEN = 2,
531 PROTECTED = 3,
532};
445533
446534pub const MAGIC = "\x7fELF";
447535
......@@ -534,15 +622,15 @@ pub const Header = struct {
534622 const buf = try r.peek(@sizeOf(Elf64_Ehdr));
535623
536624 if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic;
537 if (buf[EI_VERSION] != 1) return error.InvalidElfVersion;
625 if (buf[EI.VERSION] != 1) return error.InvalidElfVersion;
538626
539 const endian: std.builtin.Endian = switch (buf[EI_DATA]) {
627 const endian: std.builtin.Endian = switch (buf[EI.DATA]) {
540628 ELFDATA2LSB => .little,
541629 ELFDATA2MSB => .big,
542630 else => return error.InvalidElfEndian,
543631 };
544632
545 return switch (buf[EI_CLASS]) {
633 return switch (buf[EI.CLASS]) {
546634 ELFCLASS32 => .init(try r.takeStruct(Elf32_Ehdr, endian), endian),
547635 ELFCLASS64 => .init(try r.takeStruct(Elf64_Ehdr, endian), endian),
548636 else => return error.InvalidElfClass,
......@@ -559,8 +647,8 @@ pub const Header = struct {
559647 else => @compileError("bad type"),
560648 },
561649 .endian = endian,
562 .os_abi = @enumFromInt(hdr.e_ident[EI_OSABI]),
563 .abi_version = hdr.e_ident[EI_ABIVERSION],
650 .os_abi = @enumFromInt(hdr.e_ident[EI.OSABI]),
651 .abi_version = hdr.e_ident[EI.ABIVERSION],
564652 .type = hdr.e_type,
565653 .machine = hdr.e_machine,
566654 .entry = hdr.e_entry,
......@@ -683,38 +771,200 @@ fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {
683771 };
684772}
685773
686pub const ELFCLASSNONE = 0;
687pub const ELFCLASS32 = 1;
688pub const ELFCLASS64 = 2;
689pub const ELFCLASSNUM = 3;
690
691pub const ELFDATANONE = 0;
692pub const ELFDATA2LSB = 1;
693pub const ELFDATA2MSB = 2;
694pub const ELFDATANUM = 3;
695
696pub const EI_CLASS = 4;
697pub const EI_DATA = 5;
698pub const EI_VERSION = 6;
699pub const EI_OSABI = 7;
700pub const EI_ABIVERSION = 8;
701pub const EI_PAD = 9;
702
703pub const EI_NIDENT = 16;
774pub const EI = struct {
775 pub const CLASS = 4;
776 pub const DATA = 5;
777 pub const VERSION = 6;
778 pub const OSABI = 7;
779 pub const ABIVERSION = 8;
780 pub const PAD = 9;
781 pub const NIDENT = 16;
782};
783
784/// Deprecated, use `std.elf.EI.CLASS`
785pub const EI_CLASS = EI.CLASS;
786/// Deprecated, use `std.elf.EI.DATA`
787pub const EI_DATA = EI.DATA;
788/// Deprecated, use `std.elf.EI.VERSION`
789pub const EI_VERSION = EI.VERSION;
790/// Deprecated, use `std.elf.EI.OSABI`
791pub const EI_OSABI = EI.OSABI;
792/// Deprecated, use `std.elf.EI.ABIVERSION`
793pub const EI_ABIVERSION = EI.ABIVERSION;
794/// Deprecated, use `std.elf.EI.PAD`
795pub const EI_PAD = EI.PAD;
796/// Deprecated, use `std.elf.EI.NIDENT`
797pub const EI_NIDENT = EI.NIDENT;
704798
705799pub const Half = u16;
706800pub const Word = u32;
707801pub const Sword = i32;
708pub const Elf32_Xword = u64;
709pub const Elf32_Sxword = i64;
710pub const Elf64_Xword = u64;
802pub const Xword = u64;
803pub const Sxword = i64;
804pub const Section = u16;
805pub const Elf32 = struct {
806 pub const Addr = u32;
807 pub const Off = u32;
808 pub const Ehdr = extern struct {
809 ident: [EI.NIDENT]u8,
810 type: ET,
811 machine: EM,
812 version: Word,
813 entry: Elf32.Addr,
814 phoff: Elf32.Off,
815 shoff: Elf32.Off,
816 flags: Word,
817 ehsize: Half,
818 phentsize: Half,
819 phnum: Half,
820 shentsize: Half,
821 shnum: Half,
822 shstrndx: Half,
823 };
824 pub const Phdr = extern struct {
825 type: Word,
826 offset: Elf32.Off,
827 vaddr: Elf32.Addr,
828 paddr: Elf32.Addr,
829 filesz: Word,
830 memsz: Word,
831 flags: PF,
832 @"align": Word,
833 };
834 pub const Shdr = extern struct {
835 name: Word,
836 type: Word,
837 flags: packed struct { shf: SHF },
838 addr: Elf32.Addr,
839 offset: Elf32.Off,
840 size: Word,
841 link: Word,
842 info: Word,
843 addralign: Word,
844 entsize: Word,
845 };
846 pub const Chdr = extern struct {
847 type: COMPRESS,
848 size: Word,
849 addralign: Word,
850 };
851 pub const Sym = extern struct {
852 name: Word,
853 value: Elf32.Addr,
854 size: Word,
855 info: Info,
856 other: Other,
857 shndx: Section,
858
859 pub const Info = packed struct(u8) {
860 type: STT,
861 bind: STB,
862 };
863
864 pub const Other = packed struct(u8) {
865 visibility: STV,
866 unused: u5 = 0,
867 };
868 };
869 comptime {
870 assert(@sizeOf(Elf32.Ehdr) == 52);
871 assert(@sizeOf(Elf32.Phdr) == 32);
872 assert(@sizeOf(Elf32.Shdr) == 40);
873 assert(@sizeOf(Elf32.Sym) == 16);
874 }
875};
876pub const Elf64 = struct {
877 pub const Addr = u64;
878 pub const Off = u64;
879 pub const Ehdr = extern struct {
880 ident: [EI.NIDENT]u8,
881 type: ET,
882 machine: EM,
883 version: Word,
884 entry: Elf64.Addr,
885 phoff: Elf64.Off,
886 shoff: Elf64.Off,
887 flags: Word,
888 ehsize: Half,
889 phentsize: Half,
890 phnum: Half,
891 shentsize: Half,
892 shnum: Half,
893 shstrndx: Half,
894 };
895 pub const Phdr = extern struct {
896 type: Word,
897 flags: PF,
898 offset: Elf64.Off,
899 vaddr: Elf64.Addr,
900 paddr: Elf64.Addr,
901 filesz: Xword,
902 memsz: Xword,
903 @"align": Xword,
904 };
905 pub const Shdr = extern struct {
906 name: Word,
907 type: Word,
908 flags: packed struct { shf: SHF, unused: Word = 0 },
909 addr: Elf64.Addr,
910 offset: Elf64.Off,
911 size: Xword,
912 link: Word,
913 info: Word,
914 addralign: Xword,
915 entsize: Xword,
916 };
917 pub const Chdr = extern struct {
918 type: COMPRESS,
919 reserved: Word = 0,
920 size: Xword,
921 addralign: Xword,
922 };
923 pub const Sym = extern struct {
924 name: Word,
925 info: Info,
926 other: Other,
927 shndx: Section,
928 value: Elf64.Addr,
929 size: Xword,
930
931 pub const Info = Elf32.Sym.Info;
932 pub const Other = Elf32.Sym.Other;
933 };
934 comptime {
935 assert(@sizeOf(Elf64.Ehdr) == 64);
936 assert(@sizeOf(Elf64.Phdr) == 56);
937 assert(@sizeOf(Elf64.Shdr) == 64);
938 assert(@sizeOf(Elf64.Sym) == 24);
939 }
940};
941pub const ElfN = switch (@sizeOf(usize)) {
942 4 => Elf32,
943 8 => Elf64,
944 else => @compileError("expected pointer size of 32 or 64"),
945};
946
947/// Deprecated, use `std.elf.Xword`
948pub const Elf32_Xword = Xword;
949/// Deprecated, use `std.elf.Sxword`
950pub const Elf32_Sxword = Sxword;
951/// Deprecated, use `std.elf.Xword`
952pub const Elf64_Xword = Xword;
953/// Deprecated, use `std.elf.Sxword`
711954pub const Elf64_Sxword = i64;
955/// Deprecated, use `std.elf.Elf32.Addr`
712956pub const Elf32_Addr = u32;
957/// Deprecated, use `std.elf.Elf64.Addr`
713958pub const Elf64_Addr = u64;
959/// Deprecated, use `std.elf.Elf32.Off`
714960pub const Elf32_Off = u32;
961/// Deprecated, use `std.elf.Elf64.Off`
715962pub const Elf64_Off = u64;
963/// Deprecated, use `std.elf.Section`
716964pub const Elf32_Section = u16;
965/// Deprecated, use `std.elf.Section`
717966pub const Elf64_Section = u16;
967/// Deprecated, use `std.elf.Elf32.Ehdr`
718968pub const Elf32_Ehdr = extern struct {
719969 e_ident: [EI_NIDENT]u8,
720970 e_type: ET,
......@@ -731,8 +981,9 @@ pub const Elf32_Ehdr = extern struct {
731981 e_shnum: Half,
732982 e_shstrndx: Half,
733983};
984/// Deprecated, use `std.elf.Elf64.Ehdr`
734985pub const Elf64_Ehdr = extern struct {
735 e_ident: [EI_NIDENT]u8,
986 e_ident: [EI.NIDENT]u8,
736987 e_type: ET,
737988 e_machine: EM,
738989 e_version: Word,
......@@ -747,6 +998,7 @@ pub const Elf64_Ehdr = extern struct {
747998 e_shnum: Half,
748999 e_shstrndx: Half,
7491000};
1001/// Deprecated, use `std.elf.Elf32.Phdr`
7501002pub const Elf32_Phdr = extern struct {
7511003 p_type: Word,
7521004 p_offset: Elf32_Off,
......@@ -757,6 +1009,7 @@ pub const Elf32_Phdr = extern struct {
7571009 p_flags: Word,
7581010 p_align: Word,
7591011};
1012/// Deprecated, use `std.elf.Elf64.Phdr`
7601013pub const Elf64_Phdr = extern struct {
7611014 p_type: Word,
7621015 p_flags: Word,
......@@ -767,6 +1020,7 @@ pub const Elf64_Phdr = extern struct {
7671020 p_memsz: Elf64_Xword,
7681021 p_align: Elf64_Xword,
7691022};
1023/// Deprecated, use `std.elf.Elf32.Shdr`
7701024pub const Elf32_Shdr = extern struct {
7711025 sh_name: Word,
7721026 sh_type: Word,
......@@ -779,6 +1033,7 @@ pub const Elf32_Shdr = extern struct {
7791033 sh_addralign: Word,
7801034 sh_entsize: Word,
7811035};
1036/// Deprecated, use `std.elf.Elf64.Shdr`
7821037pub const Elf64_Shdr = extern struct {
7831038 sh_name: Word,
7841039 sh_type: Word,
......@@ -791,17 +1046,20 @@ pub const Elf64_Shdr = extern struct {
7911046 sh_addralign: Elf64_Xword,
7921047 sh_entsize: Elf64_Xword,
7931048};
1049/// Deprecated, use `std.elf.Elf32.Chdr`
7941050pub const Elf32_Chdr = extern struct {
7951051 ch_type: COMPRESS,
7961052 ch_size: Word,
7971053 ch_addralign: Word,
7981054};
1055/// Deprecated, use `std.elf.Elf64.Chdr`
7991056pub const Elf64_Chdr = extern struct {
8001057 ch_type: COMPRESS,
8011058 ch_reserved: Word = 0,
8021059 ch_size: Elf64_Xword,
8031060 ch_addralign: Elf64_Xword,
8041061};
1062/// Deprecated, use `std.elf.Elf32.Sym`
8051063pub const Elf32_Sym = extern struct {
8061064 st_name: Word,
8071065 st_value: Elf32_Addr,
......@@ -817,6 +1075,7 @@ pub const Elf32_Sym = extern struct {
8171075 return @truncate(self.st_info >> 4);
8181076 }
8191077};
1078/// Deprecated, use `std.elf.Elf64.Sym`
8201079pub const Elf64_Sym = extern struct {
8211080 st_name: Word,
8221081 st_info: u8,
......@@ -1020,27 +1279,18 @@ pub const Elf_MIPS_ABIFlags_v0 = extern struct {
10201279 flags2: Word,
10211280};
10221281
1023comptime {
1024 assert(@sizeOf(Elf32_Ehdr) == 52);
1025 assert(@sizeOf(Elf64_Ehdr) == 64);
1026
1027 assert(@sizeOf(Elf32_Phdr) == 32);
1028 assert(@sizeOf(Elf64_Phdr) == 56);
1029
1030 assert(@sizeOf(Elf32_Shdr) == 40);
1031 assert(@sizeOf(Elf64_Shdr) == 64);
1032}
1033
10341282pub const Auxv = switch (@sizeOf(usize)) {
10351283 4 => Elf32_auxv_t,
10361284 8 => Elf64_auxv_t,
10371285 else => @compileError("expected pointer size of 32 or 64"),
10381286};
1287/// Deprecated, use `std.elf.ElfN.Ehdr`
10391288pub const Ehdr = switch (@sizeOf(usize)) {
10401289 4 => Elf32_Ehdr,
10411290 8 => Elf64_Ehdr,
10421291 else => @compileError("expected pointer size of 32 or 64"),
10431292};
1293/// Deprecated, use `std.elf.ElfN.Phdr`
10441294pub const Phdr = switch (@sizeOf(usize)) {
10451295 4 => Elf32_Phdr,
10461296 8 => Elf64_Phdr,
......@@ -1071,20 +1321,53 @@ pub const Shdr = switch (@sizeOf(usize)) {
10711321 8 => Elf64_Shdr,
10721322 else => @compileError("expected pointer size of 32 or 64"),
10731323};
1324/// Deprecated, use `std.elf.ElfN.Chdr`
10741325pub const Chdr = switch (@sizeOf(usize)) {
10751326 4 => Elf32_Chdr,
10761327 8 => Elf64_Chdr,
10771328 else => @compileError("expected pointer size of 32 or 64"),
10781329};
1330/// Deprecated, use `std.elf.ElfN.Sym`
10791331pub const Sym = switch (@sizeOf(usize)) {
10801332 4 => Elf32_Sym,
10811333 8 => Elf64_Sym,
10821334 else => @compileError("expected pointer size of 32 or 64"),
10831335};
1084pub const Addr = switch (@sizeOf(usize)) {
1085 4 => Elf32_Addr,
1086 8 => Elf64_Addr,
1087 else => @compileError("expected pointer size of 32 or 64"),
1336/// Deprecated, use `std.elf.ElfN.Addr`
1337pub const Addr = ElfN.Addr;
1338
1339/// Deprecated, use `@intFromEnum(std.elf.CLASS.NONE)`
1340pub const ELFCLASSNONE = @intFromEnum(CLASS.NONE);
1341/// Deprecated, use `@intFromEnum(std.elf.CLASS.@"32")`
1342pub const ELFCLASS32 = @intFromEnum(CLASS.@"32");
1343/// Deprecated, use `@intFromEnum(std.elf.CLASS.@"64")`
1344pub const ELFCLASS64 = @intFromEnum(CLASS.@"64");
1345/// Deprecated, use `@intFromEnum(std.elf.CLASS.NUM)`
1346pub const ELFCLASSNUM = CLASS.NUM;
1347pub const CLASS = enum(u8) {
1348 NONE = 0,
1349 @"32" = 1,
1350 @"64" = 2,
1351 _,
1352
1353 pub const NUM = @typeInfo(CLASS).@"enum".fields.len;
1354};
1355
1356/// Deprecated, use `@intFromEnum(std.elf.DATA.NONE)`
1357pub const ELFDATANONE = @intFromEnum(DATA.NONE);
1358/// Deprecated, use `@intFromEnum(std.elf.DATA.@"2LSB")`
1359pub const ELFDATA2LSB = @intFromEnum(DATA.@"2LSB");
1360/// Deprecated, use `@intFromEnum(std.elf.DATA.@"2MSB")`
1361pub const ELFDATA2MSB = @intFromEnum(DATA.@"2MSB");
1362/// Deprecated, use `@intFromEnum(std.elf.DATA.NUM)`
1363pub const ELFDATANUM = DATA.NUM;
1364pub const DATA = enum(u8) {
1365 NONE = 0,
1366 @"2LSB" = 1,
1367 @"2MSB" = 2,
1368 _,
1369
1370 pub const NUM = @typeInfo(DATA).@"enum".fields.len;
10881371};
10891372
10901373pub const OSABI = enum(u8) {
......@@ -1718,6 +2001,108 @@ pub const SHF_MIPS_STRING = 0x80000000;
17182001/// Make code section unreadable when in execute-only mode
17192002pub const SHF_ARM_PURECODE = 0x2000000;
17202003
2004pub const SHF = packed struct(Word) {
2005 /// Section data should be writable during execution.
2006 WRITE: bool = false,
2007 /// Section occupies memory during program execution.
2008 ALLOC: bool = false,
2009 /// Section contains executable machine instructions.
2010 EXECINSTR: bool = false,
2011 unused3: u1 = 0,
2012 /// The data in this section may be merged.
2013 MERGE: bool = false,
2014 /// The data in this section is null-terminated strings.
2015 STRINGS: bool = false,
2016 /// A field in this section holds a section header table index.
2017 INFO_LINK: bool = false,
2018 /// Adds special ordering requirements for link editors.
2019 LINK_ORDER: bool = false,
2020 /// This section requires special OS-specific processing to avoid incorrect behavior.
2021 OS_NONCONFORMING: bool = false,
2022 /// This section is a member of a section group.
2023 GROUP: bool = false,
2024 /// This section holds Thread-Local Storage.
2025 TLS: bool = false,
2026 /// Identifies a section containing compressed data.
2027 COMPRESSED: bool = false,
2028 unused12: u8 = 0,
2029 OS: packed union {
2030 MASK: u8,
2031 GNU: packed struct(u8) {
2032 unused0: u1 = 0,
2033 /// Not to be GCed by the linker
2034 RETAIN: bool = false,
2035 unused2: u6 = 0,
2036 },
2037 MIPS: packed struct(u8) {
2038 unused0: u4 = 0,
2039 /// Section contains text/data which may be replicated in other sections.
2040 /// Linker must retain only one copy.
2041 NODUPES: bool = false,
2042 /// Linker must generate implicit hidden weak names.
2043 NAMES: bool = false,
2044 /// Section data local to process.
2045 LOCAL: bool = false,
2046 /// Do not strip this section.
2047 NOSTRIP: bool = false,
2048 },
2049 ARM: packed struct(u8) {
2050 unused0: u5 = 0,
2051 /// Make code section unreadable when in execute-only mode
2052 PURECODE: bool = false,
2053 unused6: u2 = 0,
2054 },
2055 } = .{ .MASK = 0 },
2056 PROC: packed union {
2057 MASK: u4,
2058 XCORE: packed struct(u4) {
2059 /// All sections with the "d" flag are grouped together by the linker to form
2060 /// the data section and the dp register is set to the start of the section by
2061 /// the boot code.
2062 DP_SECTION: bool = false,
2063 /// All sections with the "c" flag are grouped together by the linker to form
2064 /// the constant pool and the cp register is set to the start of the constant
2065 /// pool by the boot code.
2066 CP_SECTION: bool = false,
2067 unused2: u1 = 0,
2068 /// This section is excluded from the final executable or shared library.
2069 EXCLUDE: bool = false,
2070 },
2071 X86_64: packed struct(u4) {
2072 /// If an object file section does not have this flag set, then it may not hold
2073 /// more than 2GB and can be freely referred to in objects using smaller code
2074 /// models. Otherwise, only objects using larger code models can refer to them.
2075 /// For example, a medium code model object can refer to data in a section that
2076 /// sets this flag besides being able to refer to data in a section that does
2077 /// not set it; likewise, a small code model object can refer only to code in a
2078 /// section that does not set this flag.
2079 LARGE: bool = false,
2080 unused1: u2 = 0,
2081 /// This section is excluded from the final executable or shared library.
2082 EXCLUDE: bool = false,
2083 },
2084 HEX: packed struct(u4) {
2085 /// All sections with the GPREL flag are grouped into a global data area
2086 /// for faster accesses
2087 GPREL: bool = false,
2088 unused1: u2 = 0,
2089 /// This section is excluded from the final executable or shared library.
2090 EXCLUDE: bool = false,
2091 },
2092 MIPS: packed struct(u4) {
2093 /// All sections with the GPREL flag are grouped into a global data area
2094 /// for faster accesses
2095 GPREL: bool = false,
2096 /// This section should be merged.
2097 MERGE: bool = false,
2098 /// Address size to be inferred from section entry size.
2099 ADDR: bool = false,
2100 /// Section data is string data by default.
2101 STRING: bool = false,
2102 },
2103 } = .{ .MASK = 0 },
2104};
2105
17212106/// Execute
17222107pub const PF_X = 1;
17232108
......@@ -1733,6 +2118,19 @@ pub const PF_MASKOS = 0x0ff00000;
17332118/// Bits for processor-specific semantics.
17342119pub const PF_MASKPROC = 0xf0000000;
17352120
2121pub const PF = packed struct(Word) {
2122 X: bool = false,
2123 W: bool = false,
2124 R: bool = false,
2125 unused3: u17 = 0,
2126 OS: packed union {
2127 MASK: u8,
2128 } = .{ .MASK = 0 },
2129 PROC: packed union {
2130 MASK: u4,
2131 } = .{ .MASK = 0 },
2132};
2133
17362134/// Undefined section
17372135pub const SHN_UNDEF = 0;
17382136/// Start of reserved indices
......@@ -2303,13 +2701,6 @@ pub const R_PPC64 = enum(u32) {
23032701 _,
23042702};
23052703
2306pub const STV = enum(u3) {
2307 DEFAULT = 0,
2308 INTERNAL = 1,
2309 HIDDEN = 2,
2310 PROTECTED = 3,
2311};
2312
23132704pub const ar_hdr = extern struct {
23142705 /// Member file name, sometimes / terminated.
23152706 ar_name: [16]u8,
lib/std/zig/system.zig+6-6
......@@ -516,15 +516,15 @@ pub fn abiAndDynamicLinkerFromFile(
516516 const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
517517 const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
518518 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
519 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
519 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
520520 elf.ELFDATA2LSB => .little,
521521 elf.ELFDATA2MSB => .big,
522522 else => return error.InvalidElfEndian,
523523 };
524524 const need_bswap = elf_endian != native_endian;
525 if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
525 if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
526526
527 const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
527 const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
528528 elf.ELFCLASS32 => false,
529529 elf.ELFCLASS64 => true,
530530 else => return error.InvalidElfClass,
......@@ -920,15 +920,15 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
920920 const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
921921 const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
922922 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
923 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
923 const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
924924 elf.ELFDATA2LSB => .little,
925925 elf.ELFDATA2MSB => .big,
926926 else => return error.InvalidElfEndian,
927927 };
928928 const need_bswap = elf_endian != native_endian;
929 if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
929 if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
930930
931 const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
931 const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
932932 elf.ELFCLASS32 => false,
933933 elf.ELFCLASS64 => true,
934934 else => return error.InvalidElfClass,
src/Compilation.zig+34-13
......@@ -177,7 +177,6 @@ debug_compiler_runtime_libs: bool,
177177debug_compile_errors: bool,
178178/// Do not check this field directly. Instead, use the `debugIncremental` wrapper function.
179179debug_incremental: bool,
180incremental: bool,
181180alloc_failure_occurred: bool = false,
182181last_update_was_cache_hit: bool = false,
183182
......@@ -256,7 +255,9 @@ mutex: if (builtin.single_threaded) struct {
256255test_filters: []const []const u8,
257256
258257link_task_wait_group: WaitGroup = .{},
259link_prog_node: std.Progress.Node = std.Progress.Node.none,
258link_prog_node: std.Progress.Node = .none,
259link_uav_prog_node: std.Progress.Node = .none,
260link_lazy_prog_node: std.Progress.Node = .none,
260261
261262llvm_opt_bisect_limit: c_int,
262263
......@@ -1746,7 +1747,6 @@ pub const CreateOptions = struct {
17461747 debug_compiler_runtime_libs: bool = false,
17471748 debug_compile_errors: bool = false,
17481749 debug_incremental: bool = false,
1749 incremental: bool = false,
17501750 /// Normally when you create a `Compilation`, Zig will automatically build
17511751 /// and link in required dependencies, such as compiler-rt and libc. When
17521752 /// building such dependencies themselves, this flag must be set to avoid
......@@ -1982,6 +1982,7 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
19821982 };
19831983 if (have_zcu and (!need_llvm or use_llvm)) {
19841984 if (output_mode == .Obj) break :s .zcu;
1985 if (options.config.use_new_linker) break :s .zcu;
19851986 switch (target_util.zigBackend(target, use_llvm)) {
19861987 else => {},
19871988 .stage2_aarch64, .stage2_x86_64 => if (target.ofmt == .coff) {
......@@ -2188,8 +2189,8 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
21882189 .inherited = .{},
21892190 .global = options.config,
21902191 .parent = options.root_mod,
2191 }) catch |err| return switch (err) {
2192 error.OutOfMemory => |e| return e,
2192 }) catch |err| switch (err) {
2193 error.OutOfMemory => return error.OutOfMemory,
21932194 // None of these are possible because the configuration matches the root module
21942195 // which already passed these checks.
21952196 error.ValgrindUnsupportedOnTarget => unreachable,
......@@ -2266,7 +2267,6 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
22662267 .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
22672268 .debug_compile_errors = options.debug_compile_errors,
22682269 .debug_incremental = options.debug_incremental,
2269 .incremental = options.incremental,
22702270 .root_name = root_name,
22712271 .sysroot = sysroot,
22722272 .windows_libs = .empty,
......@@ -2409,6 +2409,8 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
24092409 // Synchronize with other matching comments: ZigOnlyHashStuff
24102410 hash.add(use_llvm);
24112411 hash.add(options.config.use_lib_llvm);
2412 hash.add(options.config.use_lld);
2413 hash.add(options.config.use_new_linker);
24122414 hash.add(options.config.dll_export_fns);
24132415 hash.add(options.config.is_test);
24142416 hash.addListOfBytes(options.test_filters);
......@@ -3075,14 +3077,29 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
30753077
30763078 // The linker progress node is set up here instead of in `performAllTheWork`, because
30773079 // we also want it around during `flush`.
3078 const have_link_node = comp.bin_file != null;
3079 if (have_link_node) {
3080 if (comp.bin_file) |lf| {
30803081 comp.link_prog_node = main_progress_node.start("Linking", 0);
3082 if (lf.cast(.elf2)) |elf| {
3083 comp.link_prog_node.increaseEstimatedTotalItems(3);
3084 comp.link_uav_prog_node = comp.link_prog_node.start("Constants", 0);
3085 comp.link_lazy_prog_node = comp.link_prog_node.start("Synthetics", 0);
3086 elf.mf.update_prog_node = comp.link_prog_node.start("Relocations", elf.mf.updates.items.len);
3087 }
30813088 }
3082 defer if (have_link_node) {
3089 defer {
30833090 comp.link_prog_node.end();
30843091 comp.link_prog_node = .none;
3085 };
3092 comp.link_uav_prog_node.end();
3093 comp.link_uav_prog_node = .none;
3094 comp.link_lazy_prog_node.end();
3095 comp.link_lazy_prog_node = .none;
3096 if (comp.bin_file) |lf| {
3097 if (lf.cast(.elf2)) |elf| {
3098 elf.mf.update_prog_node.end();
3099 elf.mf.update_prog_node = .none;
3100 }
3101 }
3102 }
30863103
30873104 try comp.performAllTheWork(main_progress_node);
30883105
......@@ -3100,6 +3117,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
31003117 try pt.populateTestFunctions();
31013118 }
31023119
3120 link.updateErrorData(pt);
3121
31033122 try pt.processExports();
31043123 }
31053124
......@@ -3474,6 +3493,8 @@ fn addNonIncrementalStuffToCacheManifest(
34743493
34753494 man.hash.add(comp.config.use_llvm);
34763495 man.hash.add(comp.config.use_lib_llvm);
3496 man.hash.add(comp.config.use_lld);
3497 man.hash.add(comp.config.use_new_linker);
34773498 man.hash.add(comp.config.is_test);
34783499 man.hash.add(comp.config.import_memory);
34793500 man.hash.add(comp.config.export_memory);
......@@ -4073,7 +4094,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
40734094 defer sorted_failed_analysis.deinit(gpa);
40744095 var added_any_analysis_error = false;
40754096 for (sorted_failed_analysis.items(.key), sorted_failed_analysis.items(.value)) |anal_unit, error_msg| {
4076 if (comp.incremental) {
4097 if (comp.config.incremental) {
40774098 const refs = try zcu.resolveReferences();
40784099 if (!refs.contains(anal_unit)) continue;
40794100 }
......@@ -4240,7 +4261,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
42404261
42414262 // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
42424263 // very common way for incremental compilation bugs to manifest, so let's always check it.
4243 if (comp.zcu) |zcu| if (comp.incremental and bundle.root_list.items.len == 0) {
4264 if (comp.zcu) |zcu| if (comp.config.incremental and bundle.root_list.items.len == 0) {
42444265 for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
42454266 const refs = try zcu.resolveReferences();
42464267 var ref = refs.get(failed_unit) orelse continue;
......@@ -4949,7 +4970,7 @@ fn performAllTheWork(
49494970 tr.stats.n_reachable_files = @intCast(zcu.alive_files.count());
49504971 }
49514972
4952 if (comp.incremental) {
4973 if (comp.config.incremental) {
49534974 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
49544975 defer update_zir_refs_node.end();
49554976 try pt.updateZirRefs();
src/Compilation/Config.zig+24
......@@ -49,6 +49,8 @@ use_lib_llvm: bool,
4949use_lld: bool,
5050c_frontend: CFrontend,
5151lto: std.zig.LtoMode,
52use_new_linker: bool,
53incremental: bool,
5254/// WASI-only. Type of WASI execution model ("command" or "reactor").
5355/// Always set to `command` for non-WASI targets.
5456wasi_exec_model: std.builtin.WasiExecModel,
......@@ -104,6 +106,8 @@ pub const Options = struct {
104106 use_lld: ?bool = null,
105107 use_clang: ?bool = null,
106108 lto: ?std.zig.LtoMode = null,
109 use_new_linker: ?bool = null,
110 incremental: bool = false,
107111 /// WASI-only. Type of WASI execution model ("command" or "reactor").
108112 wasi_exec_model: ?std.builtin.WasiExecModel = null,
109113 import_memory: ?bool = null,
......@@ -147,6 +151,8 @@ pub const ResolveError = error{
147151 LldUnavailable,
148152 ClangUnavailable,
149153 DllExportFnsRequiresWindows,
154 NewLinkerIncompatibleWithLld,
155 NewLinkerIncompatibleObjectFormat,
150156};
151157
152158pub fn resolve(options: Options) ResolveError!Config {
......@@ -458,6 +464,22 @@ pub fn resolve(options: Options) ResolveError!Config {
458464 break :b .none;
459465 };
460466
467 const use_new_linker = b: {
468 if (use_lld) {
469 if (options.use_new_linker == true) return error.NewLinkerIncompatibleWithLld;
470 break :b false;
471 }
472
473 if (!target_util.hasNewLinkerSupport(target.ofmt)) {
474 if (options.use_new_linker == true) return error.NewLinkerIncompatibleObjectFormat;
475 break :b false;
476 }
477
478 if (options.use_new_linker) |x| break :b x;
479
480 break :b options.incremental;
481 };
482
461483 const root_strip = b: {
462484 if (options.root_strip) |x| break :b x;
463485 if (root_optimize_mode == .ReleaseSmall) break :b true;
......@@ -531,6 +553,8 @@ pub fn resolve(options: Options) ResolveError!Config {
531553 .root_error_tracing = root_error_tracing,
532554 .pie = pie,
533555 .lto = lto,
556 .use_new_linker = use_new_linker,
557 .incremental = options.incremental,
534558 .import_memory = import_memory,
535559 .export_memory = export_memory,
536560 .shared_memory = shared_memory,
src/InternPool.zig+15-4
......@@ -6424,14 +6424,25 @@ pub const Alignment = enum(u6) {
64246424 return n + 1;
64256425 }
64266426
6427 pub fn toStdMem(a: Alignment) std.mem.Alignment {
6428 assert(a != .none);
6429 return @enumFromInt(@intFromEnum(a));
6430 }
6431
6432 pub fn fromStdMem(a: std.mem.Alignment) Alignment {
6433 const r: Alignment = @enumFromInt(@intFromEnum(a));
6434 assert(r != .none);
6435 return r;
6436 }
6437
64276438 const LlvmBuilderAlignment = std.zig.llvm.Builder.Alignment;
64286439
6429 pub fn toLlvm(this: @This()) LlvmBuilderAlignment {
6430 return @enumFromInt(@intFromEnum(this));
6440 pub fn toLlvm(a: Alignment) LlvmBuilderAlignment {
6441 return @enumFromInt(@intFromEnum(a));
64316442 }
64326443
6433 pub fn fromLlvm(other: LlvmBuilderAlignment) @This() {
6434 return @enumFromInt(@intFromEnum(other));
6444 pub fn fromLlvm(a: LlvmBuilderAlignment) Alignment {
6445 return @enumFromInt(@intFromEnum(a));
64356446 }
64366447};
64376448
src/Sema.zig+8-8
......@@ -3032,7 +3032,7 @@ fn zirStructDecl(
30323032 });
30333033 errdefer pt.destroyNamespace(new_namespace_index);
30343034
3035 if (pt.zcu.comp.incremental) {
3035 if (pt.zcu.comp.config.incremental) {
30363036 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
30373037 }
30383038
......@@ -3430,7 +3430,7 @@ fn zirUnionDecl(
34303430 });
34313431 errdefer pt.destroyNamespace(new_namespace_index);
34323432
3433 if (pt.zcu.comp.incremental) {
3433 if (pt.zcu.comp.config.incremental) {
34343434 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
34353435 }
34363436
......@@ -6217,7 +6217,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
62176217 if (ptr_info.byte_offset != 0) {
62186218 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
62196219 }
6220 if (options.linkage == .internal) return;
6220 if (zcu.llvm_object != null and options.linkage == .internal) return;
62216221 const export_ty = Value.fromInterned(uav.val).typeOf(zcu);
62226222 if (!try sema.validateExternType(export_ty, .other)) {
62236223 return sema.failWithOwnedErrorMsg(block, msg: {
......@@ -6256,7 +6256,7 @@ pub fn analyzeExport(
62566256 const zcu = pt.zcu;
62576257 const ip = &zcu.intern_pool;
62586258
6259 if (options.linkage == .internal)
6259 if (zcu.llvm_object != null and options.linkage == .internal)
62606260 return;
62616261
62626262 try sema.ensureNavResolved(block, src, orig_nav_index, .fully);
......@@ -7709,7 +7709,7 @@ fn analyzeCall(
77097709 // TODO: comptime call memoization is currently not supported under incremental compilation
77107710 // since dependencies are not marked on callers. If we want to keep this around (we should
77117711 // check that it's worthwhile first!), each memoized call needs an `AnalUnit`.
7712 if (zcu.comp.incremental) break :m false;
7712 if (zcu.comp.config.incremental) break :m false;
77137713 if (!block.isComptime()) break :m false;
77147714 for (args) |a| {
77157715 const val = (try sema.resolveValue(a)).?;
......@@ -31208,7 +31208,7 @@ fn addReferenceEntry(
3120831208 .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced
3120931209 else => {},
3121031210 }
31211 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
31211 if (!zcu.comp.config.incremental and zcu.comp.reference_trace == 0) return;
3121231212 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
3121331213 if (gop.found_existing) return;
3121431214 try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: {
......@@ -31225,7 +31225,7 @@ pub fn addTypeReferenceEntry(
3122531225 referenced_type: InternPool.Index,
3122631226) !void {
3122731227 const zcu = sema.pt.zcu;
31228 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
31228 if (!zcu.comp.config.incremental and zcu.comp.reference_trace == 0) return;
3122931229 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
3123031230 if (gop.found_existing) return;
3123131231 try zcu.addTypeReference(sema.owner, referenced_type, src);
......@@ -36875,7 +36875,7 @@ fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool
3687536875
3687636876pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
3687736877 const pt = sema.pt;
36878 if (!pt.zcu.comp.incremental) return;
36878 if (!pt.zcu.comp.config.incremental) return;
3687936879
3688036880 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
3688136881 if (gop.found_existing) return;
src/Value.zig+1-1
......@@ -23,7 +23,7 @@ pub fn format(val: Value, writer: *std.Io.Writer) !void {
2323
2424/// This is a debug function. In order to print values in a meaningful way
2525/// we also need access to the type.
26pub fn dump(start_val: Value, w: std.Io.Writer) std.Io.Writer.Error!void {
26pub fn dump(start_val: Value, w: *std.Io.Writer) std.Io.Writer.Error!void {
2727 try w.print("(interned: {})", .{start_val.toIntern()});
2828}
2929
src/Zcu.zig+1-1
......@@ -3166,7 +3166,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
31663166}
31673167
31683168pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
3169 if (!zcu.comp.incremental) return null;
3169 if (!zcu.comp.config.incremental) return null;
31703170
31713171 if (zcu.outdated.count() == 0) {
31723172 // Any units in `potentially_outdated` must just be stuck in loops with one another: none of those
src/Zcu/PerThread.zig+1-1
......@@ -1815,7 +1815,7 @@ fn createFileRootStruct(
18151815 wip_ty.setName(ip, try file.internFullyQualifiedName(pt), .none);
18161816 ip.namespacePtr(namespace_index).owner_type = wip_ty.index;
18171817
1818 if (zcu.comp.incremental) {
1818 if (zcu.comp.config.incremental) {
18191819 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
18201820 }
18211821
src/arch/riscv64/CodeGen.zig+5-3
......@@ -858,9 +858,11 @@ pub fn generateLazy(
858858 pt: Zcu.PerThread,
859859 src_loc: Zcu.LazySrcLoc,
860860 lazy_sym: link.File.LazySymbol,
861 code: *std.ArrayListUnmanaged(u8),
861 atom_index: u32,
862 w: *std.Io.Writer,
862863 debug_output: link.File.DebugInfoOutput,
863) CodeGenError!void {
864) (CodeGenError || std.Io.Writer.Error)!void {
865 _ = atom_index;
864866 const comp = bin_file.comp;
865867 const gpa = comp.gpa;
866868 const mod = comp.root_mod;
......@@ -914,7 +916,7 @@ pub fn generateLazy(
914916 },
915917 .bin_file = bin_file,
916918 .debug_output = debug_output,
917 .code = code,
919 .w = w,
918920 .prev_di_pc = undefined, // no debug info yet
919921 .prev_di_line = undefined, // no debug info yet
920922 .prev_di_column = undefined, // no debug info yet
src/arch/riscv64/Emit.zig+8-8
......@@ -3,7 +3,7 @@
33bin_file: *link.File,
44lower: Lower,
55debug_output: link.File.DebugInfoOutput,
6code: *std.ArrayListUnmanaged(u8),
6w: *std.Io.Writer,
77
88prev_di_line: u32,
99prev_di_column: u32,
......@@ -13,7 +13,7 @@ prev_di_pc: usize,
1313code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
1414relocs: std.ArrayListUnmanaged(Reloc) = .empty,
1515
16pub const Error = Lower.Error || error{
16pub const Error = Lower.Error || std.Io.Writer.Error || error{
1717 EmitFail,
1818};
1919
......@@ -25,13 +25,13 @@ pub fn emitMir(emit: *Emit) Error!void {
2525 try emit.code_offset_mapping.putNoClobber(
2626 emit.lower.allocator,
2727 mir_index,
28 @intCast(emit.code.items.len),
28 @intCast(emit.w.end),
2929 );
3030 const lowered = try emit.lower.lowerMir(mir_index, .{ .allow_frame_locs = true });
3131 var lowered_relocs = lowered.relocs;
3232 for (lowered.insts, 0..) |lowered_inst, lowered_index| {
33 const start_offset: u32 = @intCast(emit.code.items.len);
34 std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), lowered_inst.toU32(), .little);
33 const start_offset: u32 = @intCast(emit.w.end);
34 try emit.w.writeInt(u32, lowered_inst.toU32(), .little);
3535
3636 while (lowered_relocs.len > 0 and
3737 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
......@@ -175,7 +175,7 @@ fn fixupRelocs(emit: *Emit) Error!void {
175175 return emit.fail("relocation target not found!", .{});
176176
177177 const disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source));
178 const code: *[4]u8 = emit.code.items[reloc.source + reloc.offset ..][0..4];
178 const code = emit.w.buffered()[reloc.source + reloc.offset ..][0..4];
179179
180180 switch (reloc.fmt) {
181181 .J => riscv_util.writeInstJ(code, @bitCast(disp)),
......@@ -187,7 +187,7 @@ fn fixupRelocs(emit: *Emit) Error!void {
187187
188188fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
189189 const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
190 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
190 const delta_pc: usize = emit.w.end - emit.prev_di_pc;
191191 log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
192192 switch (emit.debug_output) {
193193 .dwarf => |dw| {
......@@ -196,7 +196,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
196196 try dw.advancePCAndLine(delta_line, delta_pc);
197197 emit.prev_di_line = line;
198198 emit.prev_di_column = column;
199 emit.prev_di_pc = emit.code.items.len;
199 emit.prev_di_pc = emit.w.end;
200200 },
201201 .none => {},
202202 }
src/arch/riscv64/Mir.zig+5-3
......@@ -109,9 +109,11 @@ pub fn emit(
109109 pt: Zcu.PerThread,
110110 src_loc: Zcu.LazySrcLoc,
111111 func_index: InternPool.Index,
112 code: *std.ArrayListUnmanaged(u8),
112 atom_index: u32,
113 w: *std.Io.Writer,
113114 debug_output: link.File.DebugInfoOutput,
114) codegen.CodeGenError!void {
115) (codegen.CodeGenError || std.Io.Writer.Error)!void {
116 _ = atom_index;
115117 const zcu = pt.zcu;
116118 const comp = zcu.comp;
117119 const gpa = comp.gpa;
......@@ -132,7 +134,7 @@ pub fn emit(
132134 },
133135 .bin_file = lf,
134136 .debug_output = debug_output,
135 .code = code,
137 .w = w,
136138 .prev_di_pc = 0,
137139 .prev_di_line = func.lbrace_line,
138140 .prev_di_column = func.lbrace_column,
src/arch/sparc64/Emit.zig+7-12
......@@ -21,7 +21,7 @@ debug_output: link.File.DebugInfoOutput,
2121target: *const std.Target,
2222err_msg: ?*ErrorMsg = null,
2323src_loc: Zcu.LazySrcLoc,
24code: *std.ArrayListUnmanaged(u8),
24w: *std.Io.Writer,
2525
2626prev_di_line: u32,
2727prev_di_column: u32,
......@@ -40,7 +40,7 @@ branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUn
4040/// instruction
4141code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
4242
43const InnerError = error{
43const InnerError = std.Io.Writer.Error || error{
4444 OutOfMemory,
4545 EmitFail,
4646};
......@@ -292,7 +292,7 @@ fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
292292 .bpcc => switch (tag) {
293293 .bpcc => {
294294 const branch_predict_int = emit.mir.instructions.items(.data)[inst].branch_predict_int;
295 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_int.inst).?)) - @as(i64, @intCast(emit.code.items.len));
295 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_int.inst).?)) - @as(i64, @intCast(emit.w.end));
296296 log.debug("mirConditionalBranch: {} offset={}", .{ inst, offset });
297297
298298 try emit.writeInstruction(
......@@ -310,7 +310,7 @@ fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
310310 .bpr => switch (tag) {
311311 .bpr => {
312312 const branch_predict_reg = emit.mir.instructions.items(.data)[inst].branch_predict_reg;
313 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_reg.inst).?)) - @as(i64, @intCast(emit.code.items.len));
313 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_reg.inst).?)) - @as(i64, @intCast(emit.w.end));
314314 log.debug("mirConditionalBranch: {} offset={}", .{ inst, offset });
315315
316316 try emit.writeInstruction(
......@@ -494,13 +494,13 @@ fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
494494
495495fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
496496 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
497 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
497 const delta_pc: usize = emit.w.end - emit.prev_di_pc;
498498 switch (emit.debug_output) {
499499 .dwarf => |dbg_out| {
500500 try dbg_out.advancePCAndLine(delta_line, delta_pc);
501501 emit.prev_di_line = line;
502502 emit.prev_di_column = column;
503 emit.prev_di_pc = emit.code.items.len;
503 emit.prev_di_pc = emit.w.end;
504504 },
505505 else => {},
506506 }
......@@ -675,13 +675,8 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
675675}
676676
677677fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
678 const comp = emit.bin_file.comp;
679 const gpa = comp.gpa;
680
681678 // SPARCv9 instructions are always arranged in BE regardless of the
682679 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
683680 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.
684 const endian: Endian = .big;
685
686 std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian);
681 try emit.w.writeInt(u32, instruction.toU32(), .big);
687682}
src/arch/sparc64/Mir.zig+5-3
......@@ -380,9 +380,11 @@ pub fn emit(
380380 pt: Zcu.PerThread,
381381 src_loc: Zcu.LazySrcLoc,
382382 func_index: InternPool.Index,
383 code: *std.ArrayListUnmanaged(u8),
383 atom_index: u32,
384 w: *std.Io.Writer,
384385 debug_output: link.File.DebugInfoOutput,
385) codegen.CodeGenError!void {
386) (codegen.CodeGenError || std.Io.Writer.Error)!void {
387 _ = atom_index;
386388 const zcu = pt.zcu;
387389 const func = zcu.funcInfo(func_index);
388390 const nav = func.owner_nav;
......@@ -393,7 +395,7 @@ pub fn emit(
393395 .debug_output = debug_output,
394396 .target = &mod.resolved_target.result,
395397 .src_loc = src_loc,
396 .code = code,
398 .w = w,
397399 .prev_di_pc = 0,
398400 .prev_di_line = func.lbrace_line,
399401 .prev_di_column = func.lbrace_column,
src/arch/x86_64/CodeGen.zig+9-8
......@@ -550,9 +550,9 @@ pub const MCValue = union(enum) {
550550 @tagName(pl.reg),
551551 }),
552552 .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
553 .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }),
554 .load_frame => |pl| try w.print("[{} + 0x{x}]", .{ pl.index, pl.off }),
555 .lea_frame => |pl| try w.print("{} + 0x{x}", .{ pl.index, pl.off }),
553 .indirect_load_frame => |pl| try w.print("[[{f} + 0x{x}]]", .{ pl.index, pl.off }),
554 .load_frame => |pl| try w.print("[{f} + 0x{x}]", .{ pl.index, pl.off }),
555 .lea_frame => |pl| try w.print("{f} + 0x{x}", .{ pl.index, pl.off }),
556556 .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}),
557557 .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}),
558558 .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}),
......@@ -561,10 +561,10 @@ pub const MCValue = union(enum) {
561561 .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
562562 .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}),
563563 .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}),
564 .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{
564 .elementwise_args => |pl| try w.print("elementwise:{d}:[{f} + 0x{x}]", .{
565565 pl.regs, pl.frame_index, pl.frame_off,
566566 }),
567 .reserved_frame => |pl| try w.print("(dead:{})", .{pl}),
567 .reserved_frame => |pl| try w.print("(dead:{f})", .{pl}),
568568 .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}),
569569 }
570570 }
......@@ -1038,7 +1038,8 @@ pub fn generateLazy(
10381038 pt: Zcu.PerThread,
10391039 src_loc: Zcu.LazySrcLoc,
10401040 lazy_sym: link.File.LazySymbol,
1041 code: *std.ArrayListUnmanaged(u8),
1041 atom_index: u32,
1042 w: *std.Io.Writer,
10421043 debug_output: link.File.DebugInfoOutput,
10431044) codegen.CodeGenError!void {
10441045 const gpa = pt.zcu.gpa;
......@@ -1081,7 +1082,7 @@ pub fn generateLazy(
10811082 else => |e| return e,
10821083 };
10831084
1084 try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, code, debug_output);
1085 try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, atom_index, w, debug_output);
10851086}
10861087
10871088const FormatNavData = struct {
......@@ -2022,7 +2023,7 @@ fn gen(
20222023 .{},
20232024 );
20242025 self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } };
2025 tracking_log.debug("spill {f} to {}", .{ self.ret_mcv.long, frame_index });
2026 tracking_log.debug("spill {f} to {f}", .{ self.ret_mcv.long, frame_index });
20262027 },
20272028 else => unreachable,
20282029 }
src/arch/x86_64/Emit.zig+76-32
......@@ -6,7 +6,7 @@ pt: Zcu.PerThread,
66pic: bool,
77atom_index: u32,
88debug_output: link.File.DebugInfoOutput,
9code: *std.ArrayListUnmanaged(u8),
9w: *std.Io.Writer,
1010
1111prev_di_loc: Loc,
1212/// Relative to the beginning of `code`.
......@@ -18,7 +18,8 @@ table_relocs: std.ArrayListUnmanaged(TableReloc),
1818
1919pub const Error = Lower.Error || error{
2020 EmitFail,
21} || link.File.UpdateDebugInfoError;
21 NotFile,
22} || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError;
2223
2324pub fn emitMir(emit: *Emit) Error!void {
2425 const comp = emit.bin_file.comp;
......@@ -29,12 +30,12 @@ pub fn emitMir(emit: *Emit) Error!void {
2930 var local_index: usize = 0;
3031 for (0..emit.lower.mir.instructions.len) |mir_i| {
3132 const mir_index: Mir.Inst.Index = @intCast(mir_i);
32 emit.code_offset_mapping.items[mir_index] = @intCast(emit.code.items.len);
33 emit.code_offset_mapping.items[mir_index] = @intCast(emit.w.end);
3334 const lowered = try emit.lower.lowerMir(mir_index);
3435 var lowered_relocs = lowered.relocs;
3536 lowered_inst: for (lowered.insts, 0..) |lowered_inst, lowered_index| {
3637 if (lowered_inst.prefix == .directive) {
37 const start_offset: u32 = @intCast(emit.code.items.len);
38 const start_offset: u32 = @intCast(emit.w.end);
3839 switch (emit.debug_output) {
3940 .dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) {
4041 .@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{
......@@ -164,6 +165,8 @@ pub fn emitMir(emit: *Emit) Error!void {
164165 .index = if (emit.bin_file.cast(.elf)) |elf_file|
165166 elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err|
166167 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
168 else if (emit.bin_file.cast(.elf2)) |elf|
169 @intFromEnum(try elf.lazySymbol(lazy_sym))
167170 else if (emit.bin_file.cast(.macho)) |macho_file|
168171 macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
169172 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
......@@ -180,12 +183,15 @@ pub fn emitMir(emit: *Emit) Error!void {
180183 .extern_func => |extern_func| .{
181184 .index = if (emit.bin_file.cast(.elf)) |elf_file|
182185 try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
183 else if (emit.bin_file.cast(.macho)) |macho_file|
186 else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
187 .name = extern_func.toSlice(&emit.lower.mir).?,
188 .type = .FUNC,
189 })) else if (emit.bin_file.cast(.macho)) |macho_file|
184190 try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
185191 else if (emit.bin_file.cast(.coff)) |coff_file|
186192 try coff_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, "compiler_rt")
187193 else
188 return emit.fail("external symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
194 return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
189195 .is_extern = true,
190196 .type = .symbol,
191197 },
......@@ -205,7 +211,7 @@ pub fn emitMir(emit: *Emit) Error!void {
205211 },
206212 else => {},
207213 }
208 if (emit.bin_file.cast(.elf)) |_| {
214 if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) {
209215 if (!emit.pic) switch (lowered_inst.encoding.mnemonic) {
210216 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
211217 lowered_inst.ops[0],
......@@ -315,7 +321,7 @@ pub fn emitMir(emit: *Emit) Error!void {
315321 },
316322 .branch, .tls => unreachable,
317323 .tlv => {
318 if (emit.bin_file.cast(.elf)) |elf_file| {
324 if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) {
319325 // TODO handle extern TLS vars, i.e., emit GD model
320326 if (emit.pic) switch (lowered_inst.encoding.mnemonic) {
321327 .lea, .mov => {
......@@ -337,7 +343,12 @@ pub fn emitMir(emit: *Emit) Error!void {
337343 }, emit.lower.target), &.{.{
338344 .op_index = 0,
339345 .target = .{
340 .index = try elf_file.getGlobalSymbol("__tls_get_addr", null),
346 .index = if (emit.bin_file.cast(.elf)) |elf_file|
347 try elf_file.getGlobalSymbol("__tls_get_addr", null)
348 else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
349 .name = "__tls_get_addr",
350 .type = .FUNC,
351 })) else unreachable,
341352 .is_extern = true,
342353 .type = .branch,
343354 },
......@@ -441,7 +452,7 @@ pub fn emitMir(emit: *Emit) Error!void {
441452 log.debug("mirDbgEnterBlock (line={d}, col={d})", .{
442453 emit.prev_di_loc.line, emit.prev_di_loc.column,
443454 });
444 try dwarf.enterBlock(emit.code.items.len);
455 try dwarf.enterBlock(emit.w.end);
445456 },
446457 .none => {},
447458 },
......@@ -450,7 +461,7 @@ pub fn emitMir(emit: *Emit) Error!void {
450461 log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{
451462 emit.prev_di_loc.line, emit.prev_di_loc.column,
452463 });
453 try dwarf.leaveBlock(emit.code.items.len);
464 try dwarf.leaveBlock(emit.w.end);
454465 },
455466 .none => {},
456467 },
......@@ -459,7 +470,7 @@ pub fn emitMir(emit: *Emit) Error!void {
459470 log.debug("mirDbgEnterInline (line={d}, col={d})", .{
460471 emit.prev_di_loc.line, emit.prev_di_loc.column,
461472 });
462 try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column);
473 try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column);
463474 },
464475 .none => {},
465476 },
......@@ -468,7 +479,7 @@ pub fn emitMir(emit: *Emit) Error!void {
468479 log.debug("mirDbgLeaveInline (line={d}, col={d})", .{
469480 emit.prev_di_loc.line, emit.prev_di_loc.column,
470481 });
471 try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.code.items.len);
482 try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end);
472483 },
473484 .none => {},
474485 },
......@@ -634,7 +645,7 @@ pub fn emitMir(emit: *Emit) Error!void {
634645 for (emit.relocs.items) |reloc| {
635646 const target = emit.code_offset_mapping.items[reloc.target];
636647 const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.inst_offset + reloc.inst_length)) + reloc.target_offset;
637 const inst_bytes = emit.code.items[reloc.inst_offset..][0..reloc.inst_length];
648 const inst_bytes = emit.w.buffered()[reloc.inst_offset..][0..reloc.inst_length];
638649 switch (reloc.source_length) {
639650 else => unreachable,
640651 inline 1, 4 => |source_length| std.mem.writeInt(
......@@ -646,12 +657,12 @@ pub fn emitMir(emit: *Emit) Error!void {
646657 }
647658 }
648659 if (emit.lower.mir.table.len > 0) {
660 const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8);
661 var table_offset = std.mem.alignForward(u32, @intCast(emit.w.end), ptr_size);
649662 if (emit.bin_file.cast(.elf)) |elf_file| {
650663 const zo = elf_file.zigObjectPtr().?;
651664 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
652665
653 const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8);
654 var table_offset = std.mem.alignForward(u32, @intCast(emit.code.items.len), ptr_size);
655666 for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
656667 .r_offset = table_reloc.source_offset,
657668 .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"),
......@@ -665,7 +676,26 @@ pub fn emitMir(emit: *Emit) Error!void {
665676 }, zo);
666677 table_offset += ptr_size;
667678 }
668 try emit.code.appendNTimes(gpa, 0, table_offset - emit.code.items.len);
679 try emit.w.splatByteAll(0, table_offset - emit.w.end);
680 } else if (emit.bin_file.cast(.elf2)) |elf| {
681 for (emit.table_relocs.items) |table_reloc| try elf.addReloc(
682 @enumFromInt(emit.atom_index),
683 table_reloc.source_offset,
684 @enumFromInt(emit.atom_index),
685 @as(i64, table_offset) + table_reloc.target_offset,
686 .{ .x86_64 = .@"32" },
687 );
688 for (emit.lower.mir.table) |entry| {
689 try elf.addReloc(
690 @enumFromInt(emit.atom_index),
691 table_offset,
692 @enumFromInt(emit.atom_index),
693 emit.code_offset_mapping.items[entry],
694 .{ .x86_64 = .@"64" },
695 );
696 table_offset += ptr_size;
697 }
698 try emit.w.splatByteAll(0, table_offset - emit.w.end);
669699 } else unreachable;
670700 }
671701}
......@@ -696,16 +726,12 @@ const RelocInfo = struct {
696726fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocInfo) Error!void {
697727 const comp = emit.bin_file.comp;
698728 const gpa = comp.gpa;
699 const start_offset: u32 = @intCast(emit.code.items.len);
700 {
701 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, emit.code);
702 defer emit.code.* = aw.toArrayList();
703 lowered_inst.encode(&aw.writer, .{}) catch |err| switch (err) {
704 error.WriteFailed => return error.OutOfMemory,
705 else => |e| return e,
706 };
707 }
708 const end_offset: u32 = @intCast(emit.code.items.len);
729 const start_offset: u32 = @intCast(emit.w.end);
730 lowered_inst.encode(emit.w, .{}) catch |err| switch (err) {
731 error.WriteFailed => return error.OutOfMemory,
732 else => |e| return e,
733 };
734 const end_offset: u32 = @intCast(emit.w.end);
709735 for (reloc_info) |reloc| switch (reloc.target.type) {
710736 .inst => {
711737 const inst_length: u4 = @intCast(end_offset - start_offset);
......@@ -769,7 +795,13 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
769795 .symbolnum = @intCast(reloc.target.index),
770796 },
771797 });
772 } else if (emit.bin_file.cast(.coff)) |coff_file| {
798 } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
799 @enumFromInt(emit.atom_index),
800 end_offset - 4,
801 @enumFromInt(reloc.target.index),
802 reloc.off,
803 .{ .x86_64 = .@"32" },
804 ) else if (emit.bin_file.cast(.coff)) |coff_file| {
773805 const atom_index = coff_file.getAtomIndexForSymbol(
774806 .{ .sym_index = emit.atom_index, .file = null },
775807 ).?;
......@@ -794,7 +826,13 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
794826 .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
795827 .r_addend = reloc.off - 4,
796828 }, zo);
797 } else if (emit.bin_file.cast(.macho)) |macho_file| {
829 } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
830 @enumFromInt(emit.atom_index),
831 end_offset - 4,
832 @enumFromInt(reloc.target.index),
833 reloc.off - 4,
834 .{ .x86_64 = .PC32 },
835 ) else if (emit.bin_file.cast(.macho)) |macho_file| {
798836 const zo = macho_file.getZigObject().?;
799837 const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
800838 try atom.addReloc(macho_file, .{
......@@ -849,7 +887,13 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
849887 .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
850888 .r_addend = reloc.off,
851889 }, zo);
852 } else if (emit.bin_file.cast(.macho)) |macho_file| {
890 } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
891 @enumFromInt(emit.atom_index),
892 end_offset - 4,
893 @enumFromInt(reloc.target.index),
894 reloc.off,
895 .{ .x86_64 = .TPOFF32 },
896 ) else if (emit.bin_file.cast(.macho)) |macho_file| {
853897 const zo = macho_file.getZigObject().?;
854898 const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
855899 try atom.addReloc(macho_file, .{
......@@ -908,7 +952,7 @@ const Loc = struct {
908952
909953fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
910954 const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line);
911 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
955 const delta_pc: usize = emit.w.end - emit.prev_di_pc;
912956 log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
913957 switch (emit.debug_output) {
914958 .dwarf => |dwarf| {
......@@ -916,7 +960,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
916960 if (loc.column != emit.prev_di_loc.column) try dwarf.setColumn(loc.column);
917961 try dwarf.advancePCAndLine(delta_line, delta_pc);
918962 emit.prev_di_loc = loc;
919 emit.prev_di_pc = emit.code.items.len;
963 emit.prev_di_pc = emit.w.end;
920964 },
921965 .none => {},
922966 }
src/arch/x86_64/Mir.zig+8-25
......@@ -1976,7 +1976,8 @@ pub fn emit(
19761976 pt: Zcu.PerThread,
19771977 src_loc: Zcu.LazySrcLoc,
19781978 func_index: InternPool.Index,
1979 code: *std.ArrayListUnmanaged(u8),
1979 atom_index: u32,
1980 w: *std.Io.Writer,
19801981 debug_output: link.File.DebugInfoOutput,
19811982) codegen.CodeGenError!void {
19821983 const zcu = pt.zcu;
......@@ -1997,17 +1998,9 @@ pub fn emit(
19971998 .bin_file = lf,
19981999 .pt = pt,
19992000 .pic = mod.pic,
2000 .atom_index = sym: {
2001 if (lf.cast(.elf)) |ef| break :sym try ef.zigObjectPtr().?.getOrCreateMetadataForNav(zcu, nav);
2002 if (lf.cast(.macho)) |mf| break :sym try mf.getZigObject().?.getOrCreateMetadataForNav(mf, nav);
2003 if (lf.cast(.coff)) |cf| {
2004 const atom = try cf.getOrCreateAtomForNav(nav);
2005 break :sym cf.getAtom(atom).getSymbolIndex().?;
2006 }
2007 unreachable;
2008 },
2001 .atom_index = atom_index,
20092002 .debug_output = debug_output,
2010 .code = code,
2003 .w = w,
20112004
20122005 .prev_di_loc = .{
20132006 .line = func.lbrace_line,
......@@ -2037,7 +2030,8 @@ pub fn emitLazy(
20372030 pt: Zcu.PerThread,
20382031 src_loc: Zcu.LazySrcLoc,
20392032 lazy_sym: link.File.LazySymbol,
2040 code: *std.ArrayListUnmanaged(u8),
2033 atom_index: u32,
2034 w: *std.Io.Writer,
20412035 debug_output: link.File.DebugInfoOutput,
20422036) codegen.CodeGenError!void {
20432037 const zcu = pt.zcu;
......@@ -2055,20 +2049,9 @@ pub fn emitLazy(
20552049 .bin_file = lf,
20562050 .pt = pt,
20572051 .pic = mod.pic,
2058 .atom_index = sym: {
2059 if (lf.cast(.elf)) |ef| break :sym ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_sym) catch |err|
2060 return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
2061 if (lf.cast(.macho)) |mf| break :sym mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_sym) catch |err|
2062 return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
2063 if (lf.cast(.coff)) |cf| {
2064 const atom = cf.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err|
2065 return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
2066 break :sym cf.getAtom(atom).getSymbolIndex().?;
2067 }
2068 unreachable;
2069 },
2052 .atom_index = atom_index,
20702053 .debug_output = debug_output,
2071 .code = code,
2054 .w = w,
20722055
20732056 .prev_di_loc = undefined,
20742057 .prev_di_pc = undefined,
src/arch/x86_64/bits.zig+8
......@@ -727,6 +727,14 @@ pub const FrameIndex = enum(u32) {
727727 pub fn isNamed(fi: FrameIndex) bool {
728728 return @intFromEnum(fi) < named_count;
729729 }
730
731 pub fn format(fi: FrameIndex, writer: *std.Io.Writer) std.Io.Writer.Error!void {
732 if (fi.isNamed()) {
733 try writer.print("FrameIndex.{t}", .{fi});
734 } else {
735 try writer.print("FrameIndex({d})", .{@intFromEnum(fi)});
736 }
737 }
730738};
731739
732740pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
src/arch/x86_64/encoder.zig+1-1
......@@ -259,7 +259,7 @@ pub const Instruction = struct {
259259 switch (sib.base) {
260260 .none => any = false,
261261 .reg => |reg| try w.print("{s}", .{@tagName(reg)}),
262 .frame => |frame_index| try w.print("{}", .{frame_index}),
262 .frame => |frame_index| try w.print("{f}", .{frame_index}),
263263 .table => try w.print("Table", .{}),
264264 .rip_inst => |inst_index| try w.print("RipInst({d})", .{inst_index}),
265265 .nav => |nav| try w.print("Nav({d})", .{@intFromEnum(nav)}),
src/codegen.zig+133-138
......@@ -6,7 +6,6 @@ const link = @import("link.zig");
66const log = std.log.scoped(.codegen);
77const mem = std.mem;
88const math = std.math;
9const ArrayList = std.ArrayList;
109const target_util = @import("target.zig");
1110const trace = @import("tracy.zig").trace;
1211
......@@ -179,10 +178,11 @@ pub fn emitFunction(
179178 pt: Zcu.PerThread,
180179 src_loc: Zcu.LazySrcLoc,
181180 func_index: InternPool.Index,
181 atom_index: u32,
182182 any_mir: *const AnyMir,
183 code: *ArrayList(u8),
183 w: *std.Io.Writer,
184184 debug_output: link.File.DebugInfoOutput,
185) CodeGenError!void {
185) (CodeGenError || std.Io.Writer.Error)!void {
186186 const zcu = pt.zcu;
187187 const func = zcu.funcInfo(func_index);
188188 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;
......@@ -195,7 +195,7 @@ pub fn emitFunction(
195195 => |backend| {
196196 dev.check(devFeatureForBackend(backend));
197197 const mir = &@field(any_mir, AnyMir.tag(backend));
198 return mir.emit(lf, pt, src_loc, func_index, code, debug_output);
198 return mir.emit(lf, pt, src_loc, func_index, atom_index, w, debug_output);
199199 },
200200 }
201201}
......@@ -205,9 +205,10 @@ pub fn generateLazyFunction(
205205 pt: Zcu.PerThread,
206206 src_loc: Zcu.LazySrcLoc,
207207 lazy_sym: link.File.LazySymbol,
208 code: *ArrayList(u8),
208 atom_index: u32,
209 w: *std.Io.Writer,
209210 debug_output: link.File.DebugInfoOutput,
210) CodeGenError!void {
211) (CodeGenError || std.Io.Writer.Error)!void {
211212 const zcu = pt.zcu;
212213 const target = if (Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu)) |inst_index|
213214 &zcu.fileByIndex(inst_index.resolveFile(&zcu.intern_pool)).mod.?.resolved_target.result
......@@ -217,19 +218,11 @@ pub fn generateLazyFunction(
217218 else => unreachable,
218219 inline .stage2_riscv64, .stage2_x86_64 => |backend| {
219220 dev.check(devFeatureForBackend(backend));
220 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
221 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, atom_index, w, debug_output);
221222 },
222223 }
223224}
224225
225fn writeFloat(comptime F: type, f: F, target: *const std.Target, endian: std.builtin.Endian, code: []u8) void {
226 _ = target;
227 const bits = @typeInfo(F).float.bits;
228 const Int = @Type(.{ .int = .{ .signedness = .unsigned, .bits = bits } });
229 const int: Int = @bitCast(f);
230 mem.writeInt(Int, code[0..@divExact(bits, 8)], int, endian);
231}
232
233226pub fn generateLazySymbol(
234227 bin_file: *link.File,
235228 pt: Zcu.PerThread,
......@@ -237,17 +230,14 @@ pub fn generateLazySymbol(
237230 lazy_sym: link.File.LazySymbol,
238231 // TODO don't use an "out" parameter like this; put it in the result instead
239232 alignment: *Alignment,
240 code: *ArrayList(u8),
233 w: *std.Io.Writer,
241234 debug_output: link.File.DebugInfoOutput,
242235 reloc_parent: link.File.RelocInfo.Parent,
243) CodeGenError!void {
244 _ = reloc_parent;
245
236) (CodeGenError || std.Io.Writer.Error)!void {
246237 const tracy = trace(@src());
247238 defer tracy.end();
248239
249240 const comp = bin_file.comp;
250 const gpa = comp.gpa;
251241 const zcu = pt.zcu;
252242 const ip = &zcu.intern_pool;
253243 const target = &comp.root_mod.resolved_target.result;
......@@ -260,37 +250,36 @@ pub fn generateLazySymbol(
260250
261251 if (lazy_sym.kind == .code) {
262252 alignment.* = target_util.defaultFunctionAlignment(target);
263 return generateLazyFunction(bin_file, pt, src_loc, lazy_sym, code, debug_output);
253 return generateLazyFunction(bin_file, pt, src_loc, lazy_sym, reloc_parent.atom_index, w, debug_output);
264254 }
265255
266256 if (lazy_sym.ty == .anyerror_type) {
267257 alignment.* = .@"4";
268258 const err_names = ip.global_error_set.getNamesFromMainThread();
269 var offset_index: u32 = @intCast(code.items.len);
270 var string_index: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0)));
271 try code.resize(gpa, offset_index + string_index);
272 mem.writeInt(u32, code.items[offset_index..][0..4], @intCast(err_names.len), endian);
259 const strings_start: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0)));
260 var string_index = strings_start;
261 try w.rebase(w.end, string_index);
262 w.writeInt(u32, @intCast(err_names.len), endian) catch unreachable;
273263 if (err_names.len == 0) return;
274 offset_index += 4;
275264 for (err_names) |err_name_nts| {
276 const err_name = err_name_nts.toSlice(ip);
277 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
278 offset_index += 4;
279 try code.ensureUnusedCapacity(gpa, err_name.len + 1);
280 code.appendSliceAssumeCapacity(err_name);
281 code.appendAssumeCapacity(0);
282 string_index += @intCast(err_name.len + 1);
265 w.writeInt(u32, string_index, endian) catch unreachable;
266 string_index += @intCast(err_name_nts.toSlice(ip).len + 1);
267 }
268 w.writeInt(u32, string_index, endian) catch unreachable;
269 try w.rebase(w.end, string_index - strings_start);
270 for (err_names) |err_name_nts| {
271 w.writeAll(err_name_nts.toSlice(ip)) catch unreachable;
272 w.writeByte(0) catch unreachable;
283273 }
284 mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
285274 } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu) == .@"enum") {
286275 alignment.* = .@"1";
287276 const enum_ty = Type.fromInterned(lazy_sym.ty);
288277 const tag_names = enum_ty.enumFields(zcu);
289278 for (0..tag_names.len) |tag_index| {
290279 const tag_name = tag_names.get(ip)[tag_index].toSlice(ip);
291 try code.ensureUnusedCapacity(gpa, tag_name.len + 1);
292 code.appendSliceAssumeCapacity(tag_name);
293 code.appendAssumeCapacity(0);
280 try w.rebase(w.end, tag_name.len + 1);
281 w.writeAll(tag_name) catch unreachable;
282 w.writeByte(0) catch unreachable;
294283 }
295284 } else {
296285 return zcu.codegenFailType(lazy_sym.ty, "TODO implement generateLazySymbol for {s} {f}", .{
......@@ -312,14 +301,13 @@ pub fn generateSymbol(
312301 pt: Zcu.PerThread,
313302 src_loc: Zcu.LazySrcLoc,
314303 val: Value,
315 code: *ArrayList(u8),
304 w: *std.Io.Writer,
316305 reloc_parent: link.File.RelocInfo.Parent,
317) GenerateSymbolError!void {
306) (GenerateSymbolError || std.Io.Writer.Error)!void {
318307 const tracy = trace(@src());
319308 defer tracy.end();
320309
321310 const zcu = pt.zcu;
322 const gpa = zcu.gpa;
323311 const ip = &zcu.intern_pool;
324312 const ty = val.typeOf(zcu);
325313
......@@ -330,7 +318,7 @@ pub fn generateSymbol(
330318
331319 if (val.isUndef(zcu)) {
332320 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
333 try code.appendNTimes(gpa, 0xaa, abi_size);
321 try w.splatByteAll(0xaa, abi_size);
334322 return;
335323 }
336324
......@@ -360,7 +348,7 @@ pub fn generateSymbol(
360348 .null => unreachable, // non-runtime value
361349 .@"unreachable" => unreachable, // non-runtime value
362350 .empty_tuple => return,
363 .false, .true => try code.append(gpa, switch (simple_value) {
351 .false, .true => try w.writeByte(switch (simple_value) {
364352 .false => 0,
365353 .true => 1,
366354 else => unreachable,
......@@ -376,11 +364,11 @@ pub fn generateSymbol(
376364 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
377365 var space: Value.BigIntSpace = undefined;
378366 const int_val = val.toBigInt(&space, zcu);
379 int_val.writeTwosComplement(try code.addManyAsSlice(gpa, abi_size), endian);
367 int_val.writeTwosComplement(try w.writableSlice(abi_size), endian);
380368 },
381369 .err => |err| {
382370 const int = try pt.getErrorValue(err.name);
383 mem.writeInt(u16, try code.addManyAsArray(gpa, 2), @intCast(int), endian);
371 try w.writeInt(u16, @intCast(int), endian);
384372 },
385373 .error_union => |error_union| {
386374 const payload_ty = ty.errorUnionPayload(zcu);
......@@ -390,7 +378,7 @@ pub fn generateSymbol(
390378 };
391379
392380 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
393 mem.writeInt(u16, try code.addManyAsArray(gpa, 2), err_val, endian);
381 try w.writeInt(u16, err_val, endian);
394382 return;
395383 }
396384
......@@ -400,63 +388,63 @@ pub fn generateSymbol(
400388
401389 // error value first when its type is larger than the error union's payload
402390 if (error_align.order(payload_align) == .gt) {
403 mem.writeInt(u16, try code.addManyAsArray(gpa, 2), err_val, endian);
391 try w.writeInt(u16, err_val, endian);
404392 }
405393
406394 // emit payload part of the error union
407395 {
408 const begin = code.items.len;
396 const begin = w.end;
409397 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (error_union.val) {
410398 .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }),
411399 .payload => |payload| payload,
412 }), code, reloc_parent);
413 const unpadded_end = code.items.len - begin;
400 }), w, reloc_parent);
401 const unpadded_end = w.end - begin;
414402 const padded_end = abi_align.forward(unpadded_end);
415403 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
416404
417405 if (padding > 0) {
418 try code.appendNTimes(gpa, 0, padding);
406 try w.splatByteAll(0, padding);
419407 }
420408 }
421409
422410 // Payload size is larger than error set, so emit our error set last
423411 if (error_align.compare(.lte, payload_align)) {
424 const begin = code.items.len;
425 mem.writeInt(u16, try code.addManyAsArray(gpa, 2), err_val, endian);
426 const unpadded_end = code.items.len - begin;
412 const begin = w.end;
413 try w.writeInt(u16, err_val, endian);
414 const unpadded_end = w.end - begin;
427415 const padded_end = abi_align.forward(unpadded_end);
428416 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
429417
430418 if (padding > 0) {
431 try code.appendNTimes(gpa, 0, padding);
419 try w.splatByteAll(0, padding);
432420 }
433421 }
434422 },
435423 .enum_tag => |enum_tag| {
436424 const int_tag_ty = ty.intTagType(zcu);
437 try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent);
425 try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent);
438426 },
439427 .float => |float| storage: switch (float.storage) {
440 .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(gpa, 2)),
441 .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(gpa, 4)),
442 .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(gpa, 8)),
428 .f16 => |f16_val| try w.writeInt(u16, @bitCast(f16_val), endian),
429 .f32 => |f32_val| try w.writeInt(u32, @bitCast(f32_val), endian),
430 .f64 => |f64_val| try w.writeInt(u64, @bitCast(f64_val), endian),
443431 .f80 => |f80_val| {
444 writeFloat(f80, f80_val, target, endian, try code.addManyAsArray(gpa, 10));
432 try w.writeInt(u80, @bitCast(f80_val), endian);
445433 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
446 try code.appendNTimes(gpa, 0, abi_size - 10);
434 try w.splatByteAll(0, abi_size - 10);
447435 },
448436 .f128 => |f128_val| switch (Type.fromInterned(float.ty).floatBits(target)) {
449437 else => unreachable,
450438 16 => continue :storage .{ .f16 = @floatCast(f128_val) },
451439 32 => continue :storage .{ .f32 = @floatCast(f128_val) },
452440 64 => continue :storage .{ .f64 = @floatCast(f128_val) },
453 128 => writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)),
441 128 => try w.writeInt(u128, @bitCast(f128_val), endian),
454442 },
455443 },
456 .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0),
444 .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), w, reloc_parent, 0),
457445 .slice => |slice| {
458 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent);
459 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent);
446 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), w, reloc_parent);
447 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), w, reloc_parent);
460448 },
461449 .opt => {
462450 const payload_type = ty.optionalChild(zcu);
......@@ -465,9 +453,9 @@ pub fn generateSymbol(
465453
466454 if (ty.optionalReprIsPayload(zcu)) {
467455 if (payload_val) |value| {
468 try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
456 try generateSymbol(bin_file, pt, src_loc, value, w, reloc_parent);
469457 } else {
470 try code.appendNTimes(gpa, 0, abi_size);
458 try w.splatByteAll(0, abi_size);
471459 }
472460 } else {
473461 const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1;
......@@ -475,15 +463,15 @@ pub fn generateSymbol(
475463 const value = payload_val orelse Value.fromInterned(try pt.intern(.{
476464 .undef = payload_type.toIntern(),
477465 }));
478 try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
466 try generateSymbol(bin_file, pt, src_loc, value, w, reloc_parent);
479467 }
480 try code.append(gpa, @intFromBool(payload_val != null));
481 try code.appendNTimes(gpa, 0, padding);
468 try w.writeByte(@intFromBool(payload_val != null));
469 try w.splatByteAll(0, padding);
482470 }
483471 },
484472 .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) {
485473 .array_type => |array_type| switch (aggregate.storage) {
486 .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(array_type.lenIncludingSentinel(), ip)),
474 .bytes => |bytes| try w.writeAll(bytes.toSlice(array_type.lenIncludingSentinel(), ip)),
487475 .elems, .repeated_elem => {
488476 var index: u64 = 0;
489477 while (index < array_type.lenIncludingSentinel()) : (index += 1) {
......@@ -494,14 +482,14 @@ pub fn generateSymbol(
494482 elem
495483 else
496484 array_type.sentinel,
497 }), code, reloc_parent);
485 }), w, reloc_parent);
498486 }
499487 },
500488 },
501489 .vector_type => |vector_type| {
502490 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
503491 if (vector_type.child == .bool_type) {
504 const bytes = try code.addManyAsSlice(gpa, abi_size);
492 const bytes = try w.writableSlice(abi_size);
505493 @memset(bytes, 0xaa);
506494 var index: usize = 0;
507495 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;
......@@ -540,7 +528,7 @@ pub fn generateSymbol(
540528 }
541529 } else {
542530 switch (aggregate.storage) {
543 .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(vector_type.len, ip)),
531 .bytes => |bytes| try w.writeAll(bytes.toSlice(vector_type.len, ip)),
544532 .elems, .repeated_elem => {
545533 var index: u64 = 0;
546534 while (index < vector_type.len) : (index += 1) {
......@@ -550,7 +538,7 @@ pub fn generateSymbol(
550538 math.cast(usize, index) orelse return error.Overflow
551539 ],
552540 .repeated_elem => |elem| elem,
553 }), code, reloc_parent);
541 }), w, reloc_parent);
554542 }
555543 },
556544 }
......@@ -558,11 +546,11 @@ pub fn generateSymbol(
558546 const padding = abi_size -
559547 (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse
560548 return error.Overflow);
561 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
549 if (padding > 0) try w.splatByteAll(0, padding);
562550 }
563551 },
564552 .tuple_type => |tuple| {
565 const struct_begin = code.items.len;
553 const struct_begin = w.end;
566554 for (
567555 tuple.types.get(ip),
568556 tuple.values.get(ip),
......@@ -580,8 +568,8 @@ pub fn generateSymbol(
580568 .repeated_elem => |elem| elem,
581569 };
582570
583 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
584 const unpadded_field_end = code.items.len - struct_begin;
571 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
572 const unpadded_field_end = w.end - struct_begin;
585573
586574 // Pad struct members if required
587575 const padded_field_end = ty.structFieldOffset(index + 1, zcu);
......@@ -589,7 +577,7 @@ pub fn generateSymbol(
589577 return error.Overflow;
590578
591579 if (padding > 0) {
592 try code.appendNTimes(gpa, 0, padding);
580 try w.splatByteAll(0, padding);
593581 }
594582 }
595583 },
......@@ -598,8 +586,9 @@ pub fn generateSymbol(
598586 switch (struct_type.layout) {
599587 .@"packed" => {
600588 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
601 const current_pos = code.items.len;
602 try code.appendNTimes(gpa, 0, abi_size);
589 const start = w.end;
590 const buffer = try w.writableSlice(abi_size);
591 @memset(buffer, 0);
603592 var bits: u16 = 0;
604593
605594 for (struct_type.field_types.get(ip), 0..) |field_ty, index| {
......@@ -619,22 +608,20 @@ pub fn generateSymbol(
619608 error.DivisionByZero => unreachable,
620609 error.UnexpectedRemainder => return error.RelocationNotByteAligned,
621610 };
622 code.items.len = current_pos + field_offset;
623 // TODO: code.lockPointers();
611 w.end = start + field_offset;
624612 defer {
625 assert(code.items.len == current_pos + field_offset + @divExact(target.ptrBitWidth(), 8));
626 // TODO: code.unlockPointers();
627 code.items.len = current_pos + abi_size;
613 assert(w.end == start + field_offset + @divExact(target.ptrBitWidth(), 8));
614 w.end = start + abi_size;
628615 }
629 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
616 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
630617 } else {
631 Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable;
618 Value.fromInterned(field_val).writeToPackedMemory(.fromInterned(field_ty), pt, buffer, bits) catch unreachable;
632619 }
633620 bits += @intCast(Type.fromInterned(field_ty).bitSize(zcu));
634621 }
635622 },
636623 .auto, .@"extern" => {
637 const struct_begin = code.items.len;
624 const struct_begin = w.end;
638625 const field_types = struct_type.field_types.get(ip);
639626 const offsets = struct_type.offsets.get(ip);
640627
......@@ -654,11 +641,11 @@ pub fn generateSymbol(
654641
655642 const padding = math.cast(
656643 usize,
657 offsets[field_index] - (code.items.len - struct_begin),
644 offsets[field_index] - (w.end - struct_begin),
658645 ) orelse return error.Overflow;
659 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
646 if (padding > 0) try w.splatByteAll(0, padding);
660647
661 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
648 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
662649 }
663650
664651 const size = struct_type.sizeUnordered(ip);
......@@ -666,10 +653,9 @@ pub fn generateSymbol(
666653
667654 const padding = math.cast(
668655 usize,
669 std.mem.alignForward(u64, size, @max(alignment, 1)) -
670 (code.items.len - struct_begin),
656 std.mem.alignForward(u64, size, @max(alignment, 1)) - (w.end - struct_begin),
671657 ) orelse return error.Overflow;
672 if (padding > 0) try code.appendNTimes(gpa, 0, padding);
658 if (padding > 0) try w.splatByteAll(0, padding);
673659 },
674660 }
675661 },
......@@ -679,12 +665,12 @@ pub fn generateSymbol(
679665 const layout = ty.unionGetLayout(zcu);
680666
681667 if (layout.payload_size == 0) {
682 return generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
668 return generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent);
683669 }
684670
685671 // Check if we should store the tag first.
686672 if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) {
687 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
673 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent);
688674 }
689675
690676 const union_obj = zcu.typeToUnion(ty).?;
......@@ -692,24 +678,24 @@ pub fn generateSymbol(
692678 const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
693679 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
694680 if (!field_ty.hasRuntimeBits(zcu)) {
695 try code.appendNTimes(gpa, 0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
681 try w.splatByteAll(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
696682 } else {
697 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
683 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), w, reloc_parent);
698684
699685 const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow;
700686 if (padding > 0) {
701 try code.appendNTimes(gpa, 0, padding);
687 try w.splatByteAll(0, padding);
702688 }
703689 }
704690 } else {
705 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
691 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), w, reloc_parent);
706692 }
707693
708694 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
709 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
695 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent);
710696
711697 if (layout.padding > 0) {
712 try code.appendNTimes(gpa, 0, layout.padding);
698 try w.splatByteAll(0, layout.padding);
713699 }
714700 }
715701 },
......@@ -722,30 +708,30 @@ fn lowerPtr(
722708 pt: Zcu.PerThread,
723709 src_loc: Zcu.LazySrcLoc,
724710 ptr_val: InternPool.Index,
725 code: *ArrayList(u8),
711 w: *std.Io.Writer,
726712 reloc_parent: link.File.RelocInfo.Parent,
727713 prev_offset: u64,
728) GenerateSymbolError!void {
714) (GenerateSymbolError || std.Io.Writer.Error)!void {
729715 const zcu = pt.zcu;
730716 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
731717 const offset: u64 = prev_offset + ptr.byte_offset;
732718 return switch (ptr.base_addr) {
733 .nav => |nav| try lowerNavRef(bin_file, pt, nav, code, reloc_parent, offset),
734 .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset),
735 .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent),
719 .nav => |nav| try lowerNavRef(bin_file, pt, nav, w, reloc_parent, offset),
720 .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, w, reloc_parent, offset),
721 .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), w, reloc_parent),
736722 .eu_payload => |eu_ptr| try lowerPtr(
737723 bin_file,
738724 pt,
739725 src_loc,
740726 eu_ptr,
741 code,
727 w,
742728 reloc_parent,
743729 offset + errUnionPayloadOffset(
744730 Value.fromInterned(eu_ptr).typeOf(zcu).childType(zcu).errorUnionPayload(zcu),
745731 zcu,
746732 ),
747733 ),
748 .opt_payload => |opt_ptr| try lowerPtr(bin_file, pt, src_loc, opt_ptr, code, reloc_parent, offset),
734 .opt_payload => |opt_ptr| try lowerPtr(bin_file, pt, src_loc, opt_ptr, w, reloc_parent, offset),
749735 .field => |field| {
750736 const base_ptr = Value.fromInterned(field.base);
751737 const base_ty = base_ptr.typeOf(zcu).childType(zcu);
......@@ -764,7 +750,7 @@ fn lowerPtr(
764750 },
765751 else => unreachable,
766752 };
767 return lowerPtr(bin_file, pt, src_loc, field.base, code, reloc_parent, offset + field_off);
753 return lowerPtr(bin_file, pt, src_loc, field.base, w, reloc_parent, offset + field_off);
768754 },
769755 .arr_elem, .comptime_field, .comptime_alloc => unreachable,
770756 };
......@@ -775,12 +761,11 @@ fn lowerUavRef(
775761 pt: Zcu.PerThread,
776762 src_loc: Zcu.LazySrcLoc,
777763 uav: InternPool.Key.Ptr.BaseAddr.Uav,
778 code: *ArrayList(u8),
764 w: *std.Io.Writer,
779765 reloc_parent: link.File.RelocInfo.Parent,
780766 offset: u64,
781) GenerateSymbolError!void {
767) (GenerateSymbolError || std.Io.Writer.Error)!void {
782768 const zcu = pt.zcu;
783 const gpa = zcu.gpa;
784769 const ip = &zcu.intern_pool;
785770 const comp = lf.comp;
786771 const target = &comp.root_mod.resolved_target.result;
......@@ -790,10 +775,9 @@ fn lowerUavRef(
790775 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";
791776
792777 log.debug("lowerUavRef: ty = {f}", .{uav_ty.fmt(pt)});
793 try code.ensureUnusedCapacity(gpa, ptr_width_bytes);
794778
795779 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {
796 code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
780 try w.splatByteAll(0xaa, ptr_width_bytes);
797781 return;
798782 }
799783
......@@ -804,29 +788,32 @@ fn lowerUavRef(
804788 dev.check(link.File.Tag.wasm.devFeature());
805789 const wasm = lf.cast(.wasm).?;
806790 assert(reloc_parent == .none);
807 try wasm.addUavReloc(code.items.len, uav.val, uav.orig_ty, @intCast(offset));
808 code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
791 try wasm.addUavReloc(w.end, uav.val, uav.orig_ty, @intCast(offset));
792 try w.splatByteAll(0, ptr_width_bytes);
809793 return;
810794 },
811795 else => {},
812796 }
813797
814 const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment;
798 const uav_align = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu);
815799 switch (try lf.lowerUav(pt, uav_val, uav_align, src_loc)) {
816800 .sym_index => {},
817801 .fail => |em| std.debug.panic("TODO rework lowerUav. internal error: {s}", .{em.msg}),
818802 }
819803
820 const vaddr = try lf.getUavVAddr(uav_val, .{
804 const vaddr = lf.getUavVAddr(uav_val, .{
821805 .parent = reloc_parent,
822 .offset = code.items.len,
806 .offset = w.end,
823807 .addend = @intCast(offset),
824 });
808 }) catch |err| switch (err) {
809 error.OutOfMemory => return error.OutOfMemory,
810 else => |e| std.debug.panic("TODO rework lowerUav. internal error: {t}", .{e}),
811 };
825812 const endian = target.cpu.arch.endian();
826813 switch (ptr_width_bytes) {
827 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
828 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
829 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
814 2 => try w.writeInt(u16, @intCast(vaddr), endian),
815 4 => try w.writeInt(u32, @intCast(vaddr), endian),
816 8 => try w.writeInt(u64, vaddr, endian),
830817 else => unreachable,
831818 }
832819}
......@@ -835,10 +822,10 @@ fn lowerNavRef(
835822 lf: *link.File,
836823 pt: Zcu.PerThread,
837824 nav_index: InternPool.Nav.Index,
838 code: *ArrayList(u8),
825 w: *std.Io.Writer,
839826 reloc_parent: link.File.RelocInfo.Parent,
840827 offset: u64,
841) GenerateSymbolError!void {
828) (GenerateSymbolError || std.Io.Writer.Error)!void {
842829 const zcu = pt.zcu;
843830 const gpa = zcu.gpa;
844831 const ip = &zcu.intern_pool;
......@@ -848,10 +835,8 @@ fn lowerNavRef(
848835 const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));
849836 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
850837
851 try code.ensureUnusedCapacity(gpa, ptr_width_bytes);
852
853838 if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {
854 code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
839 try w.splatByteAll(0xaa, ptr_width_bytes);
855840 return;
856841 }
857842
......@@ -870,13 +855,13 @@ fn lowerNavRef(
870855 } else {
871856 try wasm.func_table_fixups.append(gpa, .{
872857 .table_index = @enumFromInt(gop.index),
873 .offset = @intCast(code.items.len),
858 .offset = @intCast(w.end),
874859 });
875860 }
876861 } else {
877862 if (is_obj) {
878863 try wasm.out_relocs.append(gpa, .{
879 .offset = @intCast(code.items.len),
864 .offset = @intCast(w.end),
880865 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) },
881866 .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
882867 .addend = @intCast(offset),
......@@ -885,12 +870,12 @@ fn lowerNavRef(
885870 try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1);
886871 wasm.nav_fixups.appendAssumeCapacity(.{
887872 .navs_exe_index = try wasm.refNavExe(nav_index),
888 .offset = @intCast(code.items.len),
873 .offset = @intCast(w.end),
889874 .addend = @intCast(offset),
890875 });
891876 }
892877 }
893 code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
878 try w.splatByteAll(0, ptr_width_bytes);
894879 return;
895880 },
896881 else => {},
......@@ -898,14 +883,14 @@ fn lowerNavRef(
898883
899884 const vaddr = lf.getNavVAddr(pt, nav_index, .{
900885 .parent = reloc_parent,
901 .offset = code.items.len,
886 .offset = w.end,
902887 .addend = @intCast(offset),
903888 }) catch @panic("TODO rework getNavVAddr");
904889 const endian = target.cpu.arch.endian();
905890 switch (ptr_width_bytes) {
906 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
907 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
908 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
891 2 => try w.writeInt(u16, @intCast(vaddr), endian),
892 4 => try w.writeInt(u32, @intCast(vaddr), endian),
893 8 => try w.writeInt(u64, vaddr, endian),
909894 else => unreachable,
910895 }
911896}
......@@ -962,6 +947,16 @@ pub fn genNavRef(
962947 },
963948 .link_once => unreachable,
964949 }
950 } else if (lf.cast(.elf2)) |elf| {
951 return .{ .sym_index = @intFromEnum(elf.navSymbol(zcu, nav_index) catch |err| switch (err) {
952 error.OutOfMemory => return error.OutOfMemory,
953 else => |e| return .{ .fail = try ErrorMsg.create(
954 zcu.gpa,
955 src_loc,
956 "linker failed to create a nav: {t}",
957 .{e},
958 ) },
959 }) };
965960 } else if (lf.cast(.macho)) |macho_file| {
966961 const zo = macho_file.getZigObject().?;
967962 switch (linkage) {
src/codegen/aarch64/Mir.zig+27-28
......@@ -56,13 +56,13 @@ pub fn emit(
5656 pt: Zcu.PerThread,
5757 src_loc: Zcu.LazySrcLoc,
5858 func_index: InternPool.Index,
59 code: *std.ArrayListUnmanaged(u8),
59 atom_index: u32,
60 w: *std.Io.Writer,
6061 debug_output: link.File.DebugInfoOutput,
6162) !void {
6263 _ = debug_output;
6364 const zcu = pt.zcu;
6465 const ip = &zcu.intern_pool;
65 const gpa = zcu.gpa;
6666 const func = zcu.funcInfo(func_index);
6767 const nav = ip.getNav(func.owner_nav);
6868 const mod = zcu.navFileScope(func.owner_nav).mod.?;
......@@ -81,20 +81,19 @@ pub fn emit(
8181 @as(u5, @intCast(func_align.minStrict(.@"16").toByteUnits().?)),
8282 Instruction.size,
8383 ) - 1);
84 try code.ensureUnusedCapacity(gpa, Instruction.size *
85 (code_len + literals_align_gap + mir.literals.len));
86 emitInstructionsForward(code, mir.prologue);
87 emitInstructionsBackward(code, mir.body);
88 const body_end: u32 = @intCast(code.items.len);
89 emitInstructionsBackward(code, mir.epilogue);
90 code.appendNTimesAssumeCapacity(0, Instruction.size * literals_align_gap);
91 code.appendSliceAssumeCapacity(@ptrCast(mir.literals));
84 try w.rebase(w.end, Instruction.size * (code_len + literals_align_gap + mir.literals.len));
85 emitInstructionsForward(w, mir.prologue) catch unreachable;
86 emitInstructionsBackward(w, mir.body) catch unreachable;
87 const body_end: u32 = @intCast(w.end);
88 emitInstructionsBackward(w, mir.epilogue) catch unreachable;
89 w.splatByteAll(0, Instruction.size * literals_align_gap) catch unreachable;
90 w.writeAll(@ptrCast(mir.literals)) catch unreachable;
9291 mir_log.debug("", .{});
9392
9493 for (mir.nav_relocs) |nav_reloc| try emitReloc(
9594 lf,
9695 zcu,
97 func.owner_nav,
96 atom_index,
9897 switch (try @import("../../codegen.zig").genNavRef(
9998 lf,
10099 pt,
......@@ -112,7 +111,7 @@ pub fn emit(
112111 for (mir.uav_relocs) |uav_reloc| try emitReloc(
113112 lf,
114113 zcu,
115 func.owner_nav,
114 atom_index,
116115 switch (try lf.lowerUav(
117116 pt,
118117 uav_reloc.uav.val,
......@@ -129,7 +128,7 @@ pub fn emit(
129128 for (mir.lazy_relocs) |lazy_reloc| try emitReloc(
130129 lf,
131130 zcu,
132 func.owner_nav,
131 atom_index,
133132 if (lf.cast(.elf)) |ef|
134133 ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_reloc.symbol) catch |err|
135134 return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
......@@ -150,7 +149,7 @@ pub fn emit(
150149 for (mir.global_relocs) |global_reloc| try emitReloc(
151150 lf,
152151 zcu,
153 func.owner_nav,
152 atom_index,
154153 if (lf.cast(.elf)) |ef|
155154 try ef.getGlobalSymbol(std.mem.span(global_reloc.name), null)
156155 else if (lf.cast(.macho)) |mf|
......@@ -168,30 +167,30 @@ pub fn emit(
168167 var instruction = mir.body[literal_reloc.label];
169168 instruction.load_store.register_literal.group.imm19 += literal_reloc_offset;
170169 instruction.write(
171 code.items[body_end - Instruction.size * (1 + literal_reloc.label) ..][0..Instruction.size],
170 w.buffered()[body_end - Instruction.size * (1 + literal_reloc.label) ..][0..Instruction.size],
172171 );
173172 }
174173}
175174
176fn emitInstructionsForward(code: *std.ArrayListUnmanaged(u8), instructions: []const Instruction) void {
177 for (instructions) |instruction| emitInstruction(code, instruction);
175fn emitInstructionsForward(w: *std.Io.Writer, instructions: []const Instruction) !void {
176 for (instructions) |instruction| try emitInstruction(w, instruction);
178177}
179fn emitInstructionsBackward(code: *std.ArrayListUnmanaged(u8), instructions: []const Instruction) void {
178fn emitInstructionsBackward(w: *std.Io.Writer, instructions: []const Instruction) !void {
180179 var instruction_index = instructions.len;
181180 while (instruction_index > 0) {
182181 instruction_index -= 1;
183 emitInstruction(code, instructions[instruction_index]);
182 try emitInstruction(w, instructions[instruction_index]);
184183 }
185184}
186fn emitInstruction(code: *std.ArrayListUnmanaged(u8), instruction: Instruction) void {
185fn emitInstruction(w: *std.Io.Writer, instruction: Instruction) !void {
187186 mir_log.debug(" {f}", .{instruction});
188 instruction.write(code.addManyAsArrayAssumeCapacity(Instruction.size));
187 instruction.write(try w.writableArray(Instruction.size));
189188}
190189
191190fn emitReloc(
192191 lf: *link.File,
193192 zcu: *Zcu,
194 owner_nav: InternPool.Nav.Index,
193 atom_index: u32,
195194 sym_index: u32,
196195 instruction: Instruction,
197196 offset: u32,
......@@ -202,7 +201,7 @@ fn emitReloc(
202201 else => unreachable,
203202 .data_processing_immediate => |decoded| if (lf.cast(.elf)) |ef| {
204203 const zo = ef.zigObjectPtr().?;
205 const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
204 const atom = zo.symbol(atom_index).atom(ef).?;
206205 const r_type: std.elf.R_AARCH64 = switch (decoded.decode()) {
207206 else => unreachable,
208207 .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
......@@ -221,7 +220,7 @@ fn emitReloc(
221220 }, zo);
222221 } else if (lf.cast(.macho)) |mf| {
223222 const zo = mf.getZigObject().?;
224 const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
223 const atom = zo.symbols.items[atom_index].getAtom(mf).?;
225224 switch (decoded.decode()) {
226225 else => unreachable,
227226 .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
......@@ -260,7 +259,7 @@ fn emitReloc(
260259 },
261260 .branch_exception_generating_system => |decoded| if (lf.cast(.elf)) |ef| {
262261 const zo = ef.zigObjectPtr().?;
263 const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
262 const atom = zo.symbol(atom_index).atom(ef).?;
264263 const r_type: std.elf.R_AARCH64 = switch (decoded.decode().unconditional_branch_immediate.group.op) {
265264 .b => .JUMP26,
266265 .bl => .CALL26,
......@@ -272,7 +271,7 @@ fn emitReloc(
272271 }, zo);
273272 } else if (lf.cast(.macho)) |mf| {
274273 const zo = mf.getZigObject().?;
275 const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
274 const atom = zo.symbols.items[atom_index].getAtom(mf).?;
276275 try atom.addReloc(mf, .{
277276 .tag = .@"extern",
278277 .offset = offset,
......@@ -289,7 +288,7 @@ fn emitReloc(
289288 },
290289 .load_store => |decoded| if (lf.cast(.elf)) |ef| {
291290 const zo = ef.zigObjectPtr().?;
292 const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
291 const atom = zo.symbol(atom_index).atom(ef).?;
293292 const r_type: std.elf.R_AARCH64 = switch (decoded.decode().register_unsigned_immediate.decode()) {
294293 .integer => |integer| switch (integer.decode()) {
295294 .unallocated, .prfm => unreachable,
......@@ -316,7 +315,7 @@ fn emitReloc(
316315 }, zo);
317316 } else if (lf.cast(.macho)) |mf| {
318317 const zo = mf.getZigObject().?;
319 const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
318 const atom = zo.symbols.items[atom_index].getAtom(mf).?;
320319 try atom.addReloc(mf, .{
321320 .tag = .@"extern",
322321 .offset = offset,
src/dev.zig+4
......@@ -97,6 +97,7 @@ pub const Env = enum {
9797 .lld_linker,
9898 .coff_linker,
9999 .elf_linker,
100 .elf2_linker,
100101 .macho_linker,
101102 .c_linker,
102103 .wasm_linker,
......@@ -163,6 +164,7 @@ pub const Env = enum {
163164 .incremental,
164165 .aarch64_backend,
165166 .elf_linker,
167 .elf2_linker,
166168 => true,
167169 else => Env.sema.supports(feature),
168170 },
......@@ -210,6 +212,7 @@ pub const Env = enum {
210212 .legalize,
211213 .x86_64_backend,
212214 .elf_linker,
215 .elf2_linker,
213216 => true,
214217 else => Env.sema.supports(feature),
215218 },
......@@ -282,6 +285,7 @@ pub const Feature = enum {
282285 lld_linker,
283286 coff_linker,
284287 elf_linker,
288 elf2_linker,
285289 macho_linker,
286290 c_linker,
287291 wasm_linker,
src/link.zig+80-11
......@@ -219,6 +219,7 @@ pub const Diags = struct {
219219 }
220220
221221 pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void {
222 @branchHint(.cold);
222223 return addErrorSourceLocation(diags, .none, format, args);
223224 }
224225
......@@ -529,7 +530,7 @@ pub const File = struct {
529530 const lld: *Lld = try .createEmpty(arena, comp, emit, options);
530531 return &lld.base;
531532 }
532 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
533 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt, comp.config.use_new_linker)) {
533534 .plan9 => return error.UnsupportedObjectFormat,
534535 inline else => |tag| {
535536 dev.check(tag.devFeature());
......@@ -552,7 +553,7 @@ pub const File = struct {
552553 const lld: *Lld = try .createEmpty(arena, comp, emit, options);
553554 return &lld.base;
554555 }
555 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
556 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt, comp.config.use_new_linker)) {
556557 .plan9 => return error.UnsupportedObjectFormat,
557558 inline else => |tag| {
558559 dev.check(tag.devFeature());
......@@ -579,7 +580,8 @@ pub const File = struct {
579580 const emit = base.emit;
580581 if (base.child_pid) |pid| {
581582 if (builtin.os.tag == .windows) {
582 base.cast(.coff).?.ptraceAttach(pid) catch |err| {
583 const coff_file = base.cast(.coff).?;
584 coff_file.ptraceAttach(pid) catch |err| {
583585 log.warn("attaching failed with error: {s}", .{@errorName(err)});
584586 };
585587 } else {
......@@ -597,8 +599,11 @@ pub const File = struct {
597599 .linux => std.posix.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
598600 log.warn("ptrace failure: {s}", .{@errorName(err)});
599601 },
600 .macos => base.cast(.macho).?.ptraceAttach(pid) catch |err| {
601 log.warn("attaching failed with error: {s}", .{@errorName(err)});
602 .macos => {
603 const macho_file = base.cast(.macho).?;
604 macho_file.ptraceAttach(pid) catch |err| {
605 log.warn("attaching failed with error: {s}", .{@errorName(err)});
606 };
602607 },
603608 .windows => unreachable,
604609 else => return error.HotSwapUnavailableOnHostOperatingSystem,
......@@ -613,6 +618,20 @@ pub const File = struct {
613618 .mode = determineMode(output_mode, link_mode),
614619 });
615620 },
621 .elf2 => {
622 const elf = base.cast(.elf2).?;
623 if (base.file == null) {
624 elf.mf.file = try base.emit.root_dir.handle.createFile(base.emit.sub_path, .{
625 .truncate = false,
626 .read = true,
627 .mode = determineMode(comp.config.output_mode, comp.config.link_mode),
628 });
629 base.file = elf.mf.file;
630 try elf.mf.ensureTotalCapacity(
631 @intCast(elf.mf.nodes.items[0].location().resolve(&elf.mf)[1]),
632 );
633 }
634 },
616635 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
617636 .plan9 => unreachable,
618637 }
......@@ -669,14 +688,30 @@ pub const File = struct {
669688
670689 if (base.child_pid) |pid| {
671690 switch (builtin.os.tag) {
672 .macos => base.cast(.macho).?.ptraceDetach(pid) catch |err| {
673 log.warn("detaching failed with error: {s}", .{@errorName(err)});
691 .macos => {
692 const macho_file = base.cast(.macho).?;
693 macho_file.ptraceDetach(pid) catch |err| {
694 log.warn("detaching failed with error: {s}", .{@errorName(err)});
695 };
696 },
697 .windows => {
698 const coff_file = base.cast(.coff).?;
699 coff_file.ptraceDetach(pid);
674700 },
675 .windows => base.cast(.coff).?.ptraceDetach(pid),
676701 else => return error.HotSwapUnavailableOnHostOperatingSystem,
677702 }
678703 }
679704 },
705 .elf2 => {
706 const elf = base.cast(.elf2).?;
707 if (base.file) |f| {
708 elf.mf.unmap();
709 assert(elf.mf.file.handle == f.handle);
710 elf.mf.file = undefined;
711 f.close();
712 base.file = null;
713 }
714 },
680715 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
681716 .plan9 => unreachable,
682717 }
......@@ -793,6 +828,7 @@ pub const File = struct {
793828 .spirv => {},
794829 .goff, .xcoff => {},
795830 .plan9 => unreachable,
831 .elf2 => {},
796832 inline else => |tag| {
797833 dev.check(tag.devFeature());
798834 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
......@@ -825,6 +861,26 @@ pub const File = struct {
825861 }
826862 }
827863
864 pub fn idle(base: *File, tid: Zcu.PerThread.Id) !bool {
865 switch (base.tag) {
866 else => return false,
867 inline .elf2 => |tag| {
868 dev.check(tag.devFeature());
869 return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid);
870 },
871 }
872 }
873
874 pub fn updateErrorData(base: *File, pt: Zcu.PerThread) !void {
875 switch (base.tag) {
876 else => {},
877 inline .elf2 => |tag| {
878 dev.check(tag.devFeature());
879 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt);
880 },
881 }
882 }
883
828884 pub const FlushError = error{
829885 /// Indicates an error will be present in `Compilation.link_diags`.
830886 LinkFailure,
......@@ -1099,7 +1155,7 @@ pub const File = struct {
10991155 if (base.zcu_object_basename != null) return;
11001156
11011157 switch (base.tag) {
1102 inline .wasm => |tag| {
1158 inline .elf2, .wasm => |tag| {
11031159 dev.check(tag.devFeature());
11041160 return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
11051161 },
......@@ -1110,6 +1166,7 @@ pub const File = struct {
11101166 pub const Tag = enum {
11111167 coff,
11121168 elf,
1169 elf2,
11131170 macho,
11141171 c,
11151172 wasm,
......@@ -1123,6 +1180,7 @@ pub const File = struct {
11231180 return switch (tag) {
11241181 .coff => Coff,
11251182 .elf => Elf,
1183 .elf2 => Elf2,
11261184 .macho => MachO,
11271185 .c => C,
11281186 .wasm => Wasm,
......@@ -1134,10 +1192,10 @@ pub const File = struct {
11341192 };
11351193 }
11361194
1137 fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag {
1195 fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag {
11381196 return switch (ofmt) {
11391197 .coff => .coff,
1140 .elf => .elf,
1198 .elf => if (use_new_linker) .elf2 else .elf,
11411199 .macho => .macho,
11421200 .wasm => .wasm,
11431201 .plan9 => .plan9,
......@@ -1223,6 +1281,7 @@ pub const File = struct {
12231281 pub const C = @import("link/C.zig");
12241282 pub const Coff = @import("link/Coff.zig");
12251283 pub const Elf = @import("link/Elf.zig");
1284 pub const Elf2 = @import("link/Elf2.zig");
12261285 pub const MachO = @import("link/MachO.zig");
12271286 pub const SpirV = @import("link/SpirV.zig");
12281287 pub const Wasm = @import("link/Wasm.zig");
......@@ -1548,6 +1607,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
15481607 }
15491608 }
15501609}
1610pub fn doIdleTask(comp: *Compilation, tid: usize) error{ OutOfMemory, LinkFailure }!bool {
1611 return if (comp.bin_file) |lf| lf.idle(@enumFromInt(tid)) else false;
1612}
15511613/// After the main pipeline is done, but before flush, the compilation may need to link one final
15521614/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running
15531615/// by then, we expose this function which can be called directly.
......@@ -1573,6 +1635,13 @@ pub fn linkTestFunctionsNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
15731635 };
15741636 }
15751637}
1638pub fn updateErrorData(pt: Zcu.PerThread) void {
1639 const comp = pt.zcu.comp;
1640 if (comp.bin_file) |lf| lf.updateErrorData(pt) catch |err| switch (err) {
1641 error.OutOfMemory => comp.link_diags.setAllocFailure(),
1642 error.LinkFailure => {},
1643 };
1644}
15761645
15771646/// Provided by the CLI, processed into `LinkInput` instances at the start of
15781647/// the compilation pipeline.
src/link/Coff.zig+36-31
......@@ -953,7 +953,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
953953}
954954
955955fn markRelocsDirtyByTarget(coff: *Coff, target: SymbolWithLoc) void {
956 if (!coff.base.comp.incremental) return;
956 if (!coff.base.comp.config.incremental) return;
957957 // TODO: reverse-lookup might come in handy here
958958 for (coff.relocs.values()) |*relocs| {
959959 for (relocs.items) |*reloc| {
......@@ -964,7 +964,7 @@ fn markRelocsDirtyByTarget(coff: *Coff, target: SymbolWithLoc) void {
964964}
965965
966966fn markRelocsDirtyByAddress(coff: *Coff, addr: u32) void {
967 if (!coff.base.comp.incremental) return;
967 if (!coff.base.comp.config.incremental) return;
968968 const got_moved = blk: {
969969 const sect_id = coff.got_section_index orelse break :blk false;
970970 break :blk coff.sections.items(.header)[sect_id].virtual_address >= addr;
......@@ -1111,20 +1111,24 @@ pub fn updateFunc(
11111111
11121112 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
11131113
1114 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1115 defer code_buffer.deinit(gpa);
1114 var aw: std.Io.Writer.Allocating = .init(gpa);
1115 defer aw.deinit();
11161116
1117 try codegen.emitFunction(
1117 codegen.emitFunction(
11181118 &coff.base,
11191119 pt,
11201120 zcu.navSrcLoc(nav_index),
11211121 func_index,
1122 coff.getAtom(atom_index).getSymbolIndex().?,
11221123 mir,
1123 &code_buffer,
1124 &aw.writer,
11241125 .none,
1125 );
1126 ) catch |err| switch (err) {
1127 error.WriteFailed => return error.OutOfMemory,
1128 else => |e| return e,
1129 };
11261130
1127 try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION);
1131 try coff.updateNavCode(pt, nav_index, aw.written(), .FUNCTION);
11281132
11291133 // Exports will be updated by `Zcu.processExports` after the update.
11301134}
......@@ -1145,18 +1149,18 @@ fn lowerConst(
11451149) !LowerConstResult {
11461150 const gpa = coff.base.comp.gpa;
11471151
1148 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1149 defer code_buffer.deinit(gpa);
1152 var aw: std.Io.Writer.Allocating = .init(gpa);
1153 defer aw.deinit();
11501154
11511155 const atom_index = try coff.createAtom();
11521156 const sym = coff.getAtom(atom_index).getSymbolPtr(coff);
11531157 try coff.setSymbolName(sym, name);
11541158 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_id + 1));
11551159
1156 try codegen.generateSymbol(&coff.base, pt, src_loc, val, &code_buffer, .{
1160 try codegen.generateSymbol(&coff.base, pt, src_loc, val, &aw.writer, .{
11571161 .atom_index = coff.getAtom(atom_index).getSymbolIndex().?,
11581162 });
1159 const code = code_buffer.items;
1163 const code = aw.written();
11601164
11611165 const atom = coff.getAtomPtr(atom_index);
11621166 atom.size = @intCast(code.len);
......@@ -1170,7 +1174,7 @@ fn lowerConst(
11701174 log.debug("allocated atom for {s} at 0x{x}", .{ name, atom.getSymbol(coff).value });
11711175 log.debug(" (required alignment 0x{x})", .{required_alignment});
11721176
1173 try coff.writeAtom(atom_index, code, coff.base.comp.incremental);
1177 try coff.writeAtom(atom_index, code, coff.base.comp.config.incremental);
11741178
11751179 return .{ .ok = atom_index };
11761180}
......@@ -1214,19 +1218,22 @@ pub fn updateNav(
12141218
12151219 coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
12161220
1217 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1218 defer code_buffer.deinit(gpa);
1221 var aw: std.Io.Writer.Allocating = .init(gpa);
1222 defer aw.deinit();
12191223
1220 try codegen.generateSymbol(
1224 codegen.generateSymbol(
12211225 &coff.base,
12221226 pt,
12231227 zcu.navSrcLoc(nav_index),
12241228 nav_init,
1225 &code_buffer,
1229 &aw.writer,
12261230 .{ .atom_index = atom.getSymbolIndex().? },
1227 );
1231 ) catch |err| switch (err) {
1232 error.WriteFailed => return error.OutOfMemory,
1233 else => |e| return e,
1234 };
12281235
1229 try coff.updateNavCode(pt, nav_index, code_buffer.items, .NULL);
1236 try coff.updateNavCode(pt, nav_index, aw.written(), .NULL);
12301237 }
12311238
12321239 // Exports will be updated by `Zcu.processExports` after the update.
......@@ -1244,8 +1251,8 @@ fn updateLazySymbolAtom(
12441251 const gpa = comp.gpa;
12451252
12461253 var required_alignment: InternPool.Alignment = .none;
1247 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1248 defer code_buffer.deinit(gpa);
1254 var aw: std.Io.Writer.Allocating = .init(gpa);
1255 defer aw.deinit();
12491256
12501257 const name = try allocPrint(gpa, "__lazy_{s}_{f}", .{
12511258 @tagName(sym.kind),
......@@ -1262,11 +1269,11 @@ fn updateLazySymbolAtom(
12621269 src,
12631270 sym,
12641271 &required_alignment,
1265 &code_buffer,
1272 &aw.writer,
12661273 .none,
12671274 .{ .atom_index = local_sym_index },
12681275 );
1269 const code = code_buffer.items;
1276 const code = aw.written();
12701277
12711278 const atom = coff.getAtomPtr(atom_index);
12721279 const symbol = atom.getSymbolPtr(coff);
......@@ -1285,7 +1292,7 @@ fn updateLazySymbolAtom(
12851292 symbol.value = vaddr;
12861293
12871294 try coff.addGotEntry(.{ .sym_index = local_sym_index });
1288 try coff.writeAtom(atom_index, code, coff.base.comp.incremental);
1295 try coff.writeAtom(atom_index, code, coff.base.comp.config.incremental);
12891296}
12901297
12911298pub fn getOrCreateAtomForLazySymbol(
......@@ -1437,7 +1444,7 @@ fn updateNavCode(
14371444 };
14381445 }
14391446
1440 coff.writeAtom(atom_index, code, coff.base.comp.incremental) catch |err| switch (err) {
1447 coff.writeAtom(atom_index, code, coff.base.comp.config.incremental) catch |err| switch (err) {
14411448 error.OutOfMemory => return error.OutOfMemory,
14421449 else => |e| return coff.base.cgFail(nav_index, "failed to write atom: {s}", .{@errorName(e)}),
14431450 };
......@@ -1539,14 +1546,12 @@ pub fn updateExports(
15391546 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(metadata.section + 1));
15401547 sym.type = atom.getSymbol(coff).type;
15411548
1542 switch (exp.opts.linkage) {
1543 .strong => {
1544 sym.storage_class = .EXTERNAL;
1545 },
1546 .internal => @panic("TODO Internal"),
1549 sym.storage_class = switch (exp.opts.linkage) {
1550 .internal => .EXTERNAL,
1551 .strong => .EXTERNAL,
15471552 .weak => @panic("TODO WeakExternal"),
15481553 else => unreachable,
1549 }
1554 };
15501555
15511556 try coff.resolveGlobalSymbol(sym_loc);
15521557 }
src/link/Dwarf.zig+10-7
......@@ -2126,19 +2126,22 @@ pub const WipNav = struct {
21262126 const size = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0;
21272127 try diw.writeUleb128(size);
21282128 if (size == 0) return;
2129 var bytes = wip_nav.debug_info.toArrayList();
2130 defer wip_nav.debug_info = .fromArrayList(wip_nav.dwarf.gpa, &bytes);
2131 const old_len = bytes.items.len;
2129 const old_end = wip_nav.debug_info.writer.end;
21322130 try codegen.generateSymbol(
21332131 wip_nav.dwarf.bin_file,
21342132 wip_nav.pt,
21352133 src_loc,
21362134 val,
2137 &bytes,
2135 &wip_nav.debug_info.writer,
21382136 .{ .debug_output = .{ .dwarf = wip_nav } },
21392137 );
2140 if (old_len + size != bytes.items.len) {
2141 std.debug.print("{f} [{}]: {} != {}\n", .{ ty.fmt(wip_nav.pt), ty.toIntern(), size, bytes.items.len - old_len });
2138 if (old_end + size != wip_nav.debug_info.writer.end) {
2139 std.debug.print("{f} [{}]: {} != {}\n", .{
2140 ty.fmt(wip_nav.pt),
2141 ty.toIntern(),
2142 size,
2143 wip_nav.debug_info.writer.end - old_end,
2144 });
21422145 unreachable;
21432146 }
21442147 }
......@@ -6429,7 +6432,7 @@ fn sleb128Bytes(value: anytype) u32 {
64296432/// overrides `-fno-incremental` for testing incremental debug info until `-fincremental` is functional
64306433const force_incremental = false;
64316434inline fn incremental(dwarf: Dwarf) bool {
6432 return force_incremental or dwarf.bin_file.comp.incremental;
6435 return force_incremental or dwarf.bin_file.comp.config.incremental;
64336436}
64346437
64356438const Allocator = std.mem.Allocator;
src/link/Elf/LinkerDefined.zig+1-1
......@@ -47,7 +47,7 @@ fn newSymbolAssumeCapacity(self: *LinkerDefined, name_off: u32, elf_file: *Elf)
4747 const esym = self.symtab.addOneAssumeCapacity();
4848 esym.* = .{
4949 .st_name = name_off,
50 .st_info = elf.STB_WEAK << 4,
50 .st_info = @as(u8, elf.STB_WEAK) << 4,
5151 .st_other = @intFromEnum(elf.STV.HIDDEN),
5252 .st_shndx = elf.SHN_ABS,
5353 .st_value = 0,
src/link/Elf/SharedObject.zig+1-1
......@@ -105,7 +105,7 @@ pub fn parseHeader(
105105 if (amt != buf.len) return error.UnexpectedEndOfFile;
106106 }
107107 if (!mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")) return error.BadMagic;
108 if (ehdr.e_ident[elf.EI_VERSION] != 1) return error.BadElfVersion;
108 if (ehdr.e_ident[elf.EI.VERSION] != 1) return error.BadElfVersion;
109109 if (ehdr.e_type != elf.ET.DYN) return error.NotSharedObject;
110110
111111 if (target.toElfMachine() != ehdr.e_machine)
src/link/Elf/ZigObject.zig+42-30
......@@ -277,8 +277,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
277277 pt,
278278 .{ .kind = .code, .ty = .anyerror_type },
279279 metadata.text_symbol_index,
280 ) catch |err| return switch (err) {
281 error.CodegenFail => error.LinkFailure,
280 ) catch |err| switch (err) {
281 error.CodegenFail => return error.LinkFailure,
282282 else => |e| return e,
283283 };
284284 if (metadata.rodata_state != .unused) self.updateLazySymbol(
......@@ -286,8 +286,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
286286 pt,
287287 .{ .kind = .const_data, .ty = .anyerror_type },
288288 metadata.rodata_symbol_index,
289 ) catch |err| return switch (err) {
290 error.CodegenFail => error.LinkFailure,
289 ) catch |err| switch (err) {
290 error.CodegenFail => return error.LinkFailure,
291291 else => |e| return e,
292292 };
293293 }
......@@ -1533,22 +1533,26 @@ pub fn updateFunc(
15331533 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);
15341534 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);
15351535
1536 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1537 defer code_buffer.deinit(gpa);
1536 var aw: std.Io.Writer.Allocating = .init(gpa);
1537 defer aw.deinit();
15381538
15391539 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
15401540 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
15411541
1542 try codegen.emitFunction(
1542 codegen.emitFunction(
15431543 &elf_file.base,
15441544 pt,
15451545 zcu.navSrcLoc(func.owner_nav),
15461546 func_index,
1547 sym_index,
15471548 mir,
1548 &code_buffer,
1549 &aw.writer,
15491550 if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,
1550 );
1551 const code = code_buffer.items;
1551 ) catch |err| switch (err) {
1552 error.WriteFailed => return error.OutOfMemory,
1553 else => |e| return e,
1554 };
1555 const code = aw.written();
15521556
15531557 const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code);
15541558 log.debug("setting shdr({x},{s}) for {f}", .{
......@@ -1663,21 +1667,24 @@ pub fn updateNav(
16631667 const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index);
16641668 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
16651669
1666 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1667 defer code_buffer.deinit(zcu.gpa);
1670 var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
1671 defer aw.deinit();
16681672
16691673 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
16701674 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
16711675
1672 try codegen.generateSymbol(
1676 codegen.generateSymbol(
16731677 &elf_file.base,
16741678 pt,
16751679 zcu.navSrcLoc(nav_index),
16761680 Value.fromInterned(nav_init),
1677 &code_buffer,
1681 &aw.writer,
16781682 .{ .atom_index = sym_index },
1679 );
1680 const code = code_buffer.items;
1683 ) catch |err| switch (err) {
1684 error.WriteFailed => return error.OutOfMemory,
1685 else => |e| return e,
1686 };
1687 const code = aw.written();
16811688
16821689 const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code);
16831690 log.debug("setting shdr({x},{s}) for {f}", .{
......@@ -1722,8 +1729,8 @@ fn updateLazySymbol(
17221729 const gpa = zcu.gpa;
17231730
17241731 var required_alignment: InternPool.Alignment = .none;
1725 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1726 defer code_buffer.deinit(gpa);
1732 var aw: std.Io.Writer.Allocating = .init(gpa);
1733 defer aw.deinit();
17271734
17281735 const name_str_index = blk: {
17291736 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
......@@ -1734,18 +1741,20 @@ fn updateLazySymbol(
17341741 break :blk try self.strtab.insert(gpa, name);
17351742 };
17361743
1737 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1738 try codegen.generateLazySymbol(
1744 codegen.generateLazySymbol(
17391745 &elf_file.base,
17401746 pt,
1741 src,
1747 Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse .unneeded,
17421748 sym,
17431749 &required_alignment,
1744 &code_buffer,
1750 &aw.writer,
17451751 .none,
17461752 .{ .atom_index = symbol_index },
1747 );
1748 const code = code_buffer.items;
1753 ) catch |err| switch (err) {
1754 error.WriteFailed => return error.OutOfMemory,
1755 else => |e| return e,
1756 };
1757 const code = aw.written();
17491758
17501759 const output_section_index = switch (sym.kind) {
17511760 .code => if (self.text_index) |sym_index|
......@@ -1807,21 +1816,24 @@ fn lowerConst(
18071816) !codegen.SymbolResult {
18081817 const gpa = pt.zcu.gpa;
18091818
1810 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1811 defer code_buffer.deinit(gpa);
1819 var aw: std.Io.Writer.Allocating = .init(gpa);
1820 defer aw.deinit();
18121821
18131822 const name_off = try self.addString(gpa, name);
18141823 const sym_index = try self.newSymbolWithAtom(gpa, name_off);
18151824
1816 try codegen.generateSymbol(
1825 codegen.generateSymbol(
18171826 &elf_file.base,
18181827 pt,
18191828 src_loc,
18201829 val,
1821 &code_buffer,
1830 &aw.writer,
18221831 .{ .atom_index = sym_index },
1823 );
1824 const code = code_buffer.items;
1832 ) catch |err| switch (err) {
1833 error.WriteFailed => return error.OutOfMemory,
1834 else => |e| return e,
1835 };
1836 const code = aw.written();
18251837
18261838 const local_sym = self.symbol(sym_index);
18271839 const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
src/link/Elf2.zig created+2036
......@@ -0,0 +1,2036 @@
1base: link.File,
2mf: MappedFile,
3nodes: std.MultiArrayList(Node),
4symtab: std.ArrayList(Symbol),
5shstrtab: StringTable,
6strtab: StringTable,
7globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index),
8navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Symbol.Index),
9uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
10lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
11 map: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
12 pending_index: u32,
13}),
14pending_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
15 alignment: InternPool.Alignment,
16 src_loc: Zcu.LazySrcLoc,
17}),
18relocs: std.ArrayList(Reloc),
19/// This is hiding actual bugs with global symbols! Reconsider once they are implemented correctly.
20entry_hack: Symbol.Index,
21
22pub const Node = union(enum) {
23 file,
24 ehdr,
25 shdr,
26 segment: u32,
27 section: Symbol.Index,
28 nav: InternPool.Nav.Index,
29 uav: InternPool.Index,
30 lazy_code: InternPool.Index,
31 lazy_const_data: InternPool.Index,
32
33 pub const Tag = @typeInfo(Node).@"union".tag_type.?;
34
35 const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
36 const known = known: {
37 const Known = enum {
38 file,
39 seg_rodata,
40 ehdr,
41 phdr,
42 shdr,
43 seg_text,
44 seg_data,
45 };
46 var mut_known: std.enums.EnumFieldStruct(
47 Known,
48 MappedFile.Node.Index,
49 null,
50 ) = undefined;
51 for (@typeInfo(Known).@"enum".fields) |field|
52 @field(mut_known, field.name) = @enumFromInt(field.value);
53 break :known mut_known;
54 };
55
56 comptime {
57 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
58 }
59};
60
61pub const StringTable = struct {
62 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
63 size: u32,
64
65 const Context = struct {
66 slice: []const u8,
67
68 pub fn eql(_: Context, lhs_key: u32, rhs_key: u32) bool {
69 return lhs_key == rhs_key;
70 }
71
72 pub fn hash(ctx: Context, key: u32) u64 {
73 return std.hash_map.hashString(std.mem.sliceTo(ctx.slice[key..], 0));
74 }
75 };
76
77 const Adapter = struct {
78 slice: []const u8,
79
80 pub fn eql(adapter: Adapter, lhs_key: []const u8, rhs_key: u32) bool {
81 return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and
82 adapter.slice[rhs_key + lhs_key.len] == 0;
83 }
84
85 pub fn hash(_: Adapter, key: []const u8) u64 {
86 assert(std.mem.indexOfScalar(u8, key, 0) == null);
87 return std.hash_map.hashString(key);
88 }
89 };
90
91 pub fn get(
92 st: *StringTable,
93 gpa: std.mem.Allocator,
94 mf: *MappedFile,
95 ni: MappedFile.Node.Index,
96 key: []const u8,
97 ) !u32 {
98 const slice_const = ni.sliceConst(mf);
99 const gop = try st.map.getOrPutContextAdapted(
100 gpa,
101 key,
102 StringTable.Adapter{ .slice = slice_const },
103 .{ .slice = slice_const },
104 );
105 if (gop.found_existing) return gop.key_ptr.*;
106 const old_size = st.size;
107 const new_size: u32 = @intCast(old_size + key.len + 1);
108 st.size = new_size;
109 try ni.resize(mf, gpa, new_size);
110 const slice = ni.slice(mf)[old_size..];
111 @memcpy(slice[0..key.len], key);
112 slice[key.len] = 0;
113 gop.key_ptr.* = old_size;
114 return old_size;
115 }
116};
117
118pub const Symbol = struct {
119 ni: MappedFile.Node.Index,
120 /// Relocations contained within this symbol
121 loc_relocs: Reloc.Index,
122 /// Relocations targeting this symbol
123 target_relocs: Reloc.Index,
124 unused: u32 = 0,
125
126 pub const Index = enum(u32) {
127 null,
128 symtab,
129 shstrtab,
130 strtab,
131 rodata,
132 text,
133 data,
134 tdata,
135 _,
136
137 pub fn get(si: Symbol.Index, elf: *Elf) *Symbol {
138 return &elf.symtab.items[@intFromEnum(si)];
139 }
140
141 pub fn node(si: Symbol.Index, elf: *Elf) MappedFile.Node.Index {
142 const ni = si.get(elf).ni;
143 assert(ni != .none);
144 return ni;
145 }
146
147 pub const InitOptions = struct {
148 name: []const u8 = "",
149 size: std.elf.Word = 0,
150 type: std.elf.STT,
151 bind: std.elf.STB = .LOCAL,
152 visibility: std.elf.STV = .DEFAULT,
153 shndx: std.elf.Section = std.elf.SHN_UNDEF,
154 };
155 pub fn init(si: Symbol.Index, elf: *Elf, opts: InitOptions) !void {
156 const name_entry = try elf.string(.strtab, opts.name);
157 try Symbol.Index.symtab.node(elf).resize(
158 &elf.mf,
159 elf.base.comp.gpa,
160 @as(usize, switch (elf.identClass()) {
161 .NONE, _ => unreachable,
162 .@"32" => @sizeOf(std.elf.Elf32.Sym),
163 .@"64" => @sizeOf(std.elf.Elf64.Sym),
164 }) * elf.symtab.items.len,
165 );
166 switch (elf.symPtr(si)) {
167 inline else => |sym| sym.* = .{
168 .name = name_entry,
169 .value = 0,
170 .size = opts.size,
171 .info = .{
172 .type = opts.type,
173 .bind = opts.bind,
174 },
175 .other = .{
176 .visibility = opts.visibility,
177 },
178 .shndx = opts.shndx,
179 },
180 }
181 }
182
183 pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void {
184 for (elf.relocs.items[@intFromEnum(si.get(elf).loc_relocs)..]) |*reloc| {
185 if (reloc.loc != si) break;
186 reloc.apply(elf);
187 }
188 }
189
190 pub fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void {
191 var ri = si.get(elf).target_relocs;
192 while (ri != .none) {
193 const reloc = ri.get(elf);
194 assert(reloc.target == si);
195 reloc.apply(elf);
196 ri = reloc.next;
197 }
198 }
199
200 pub fn deleteLocationRelocs(si: Symbol.Index, elf: *Elf) void {
201 const sym = si.get(elf);
202 for (elf.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
203 if (reloc.loc != si) break;
204 reloc.delete(elf);
205 }
206 sym.loc_relocs = .none;
207 }
208 };
209
210 comptime {
211 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 16);
212 }
213};
214
215pub const Reloc = extern struct {
216 type: Reloc.Type,
217 prev: Reloc.Index,
218 next: Reloc.Index,
219 loc: Symbol.Index,
220 target: Symbol.Index,
221 unused: u32,
222 offset: u64,
223 addend: i64,
224
225 pub const Type = extern union {
226 x86_64: std.elf.R_X86_64,
227 aarch64: std.elf.R_AARCH64,
228 riscv: std.elf.R_RISCV,
229 ppc64: std.elf.R_PPC64,
230 };
231
232 pub const Index = enum(u32) {
233 none = std.math.maxInt(u32),
234 _,
235
236 pub fn get(si: Reloc.Index, elf: *Elf) *Reloc {
237 return &elf.relocs.items[@intFromEnum(si)];
238 }
239 };
240
241 pub fn apply(reloc: *const Reloc, elf: *Elf) void {
242 const target_endian = elf.endian();
243 switch (reloc.loc.get(elf).ni) {
244 .none => return,
245 else => |ni| if (ni.hasMoved(&elf.mf)) return,
246 }
247 switch (reloc.target.get(elf).ni) {
248 .none => return,
249 else => |ni| if (ni.hasMoved(&elf.mf)) return,
250 }
251 switch (elf.shdrSlice()) {
252 inline else => |shdr, class| {
253 const sym = @field(elf.symSlice(), @tagName(class));
254 const loc_sym = &sym[@intFromEnum(reloc.loc)];
255 const loc_shndx =
256 std.mem.toNative(@TypeOf(loc_sym.shndx), loc_sym.shndx, target_endian);
257 assert(loc_shndx != std.elf.SHN_UNDEF);
258 const loc_sh = &shdr[loc_shndx];
259 const loc_value = std.mem.toNative(
260 @TypeOf(loc_sym.value),
261 loc_sym.value,
262 target_endian,
263 ) + reloc.offset;
264 const loc_sh_addr =
265 std.mem.toNative(@TypeOf(loc_sh.addr), loc_sh.addr, target_endian);
266 const loc_sh_offset =
267 std.mem.toNative(@TypeOf(loc_sh.offset), loc_sh.offset, target_endian);
268 const loc_file_offset: usize = @intCast(loc_value - loc_sh_addr + loc_sh_offset);
269 const target_sym = &sym[@intFromEnum(reloc.target)];
270 const target_value = std.mem.toNative(
271 @TypeOf(target_sym.value),
272 target_sym.value,
273 target_endian,
274 ) +% @as(u64, @bitCast(reloc.addend));
275 switch (elf.ehdrField(.machine)) {
276 else => |machine| @panic(@tagName(machine)),
277 .X86_64 => switch (reloc.type.x86_64) {
278 else => |kind| @panic(@tagName(kind)),
279 .@"64" => std.mem.writeInt(
280 u64,
281 elf.mf.contents[loc_file_offset..][0..8],
282 target_value,
283 target_endian,
284 ),
285 .PC32 => std.mem.writeInt(
286 i32,
287 elf.mf.contents[loc_file_offset..][0..4],
288 @intCast(@as(i64, @bitCast(target_value -% loc_value))),
289 target_endian,
290 ),
291 .@"32" => std.mem.writeInt(
292 u32,
293 elf.mf.contents[loc_file_offset..][0..4],
294 @intCast(target_value),
295 target_endian,
296 ),
297 .TPOFF32 => {
298 const phdr = @field(elf.phdrSlice(), @tagName(class));
299 const ph = &phdr[4];
300 assert(std.mem.toNative(
301 @TypeOf(ph.type),
302 ph.type,
303 target_endian,
304 ) == std.elf.PT_TLS);
305 std.mem.writeInt(
306 i32,
307 elf.mf.contents[loc_file_offset..][0..4],
308 @intCast(@as(i64, @bitCast(target_value -% std.mem.toNative(
309 @TypeOf(ph.memsz),
310 ph.memsz,
311 target_endian,
312 )))),
313 target_endian,
314 );
315 },
316 },
317 }
318 },
319 }
320 }
321
322 pub fn delete(reloc: *Reloc, elf: *Elf) void {
323 switch (reloc.prev) {
324 .none => {
325 const target = reloc.target.get(elf);
326 assert(target.target_relocs.get(elf) == reloc);
327 target.target_relocs = reloc.next;
328 },
329 else => |prev| prev.get(elf).next = reloc.next,
330 }
331 switch (reloc.next) {
332 .none => {},
333 else => |next| next.get(elf).prev = reloc.prev,
334 }
335 reloc.* = undefined;
336 }
337
338 comptime {
339 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
340 }
341};
342
343pub fn open(
344 arena: std.mem.Allocator,
345 comp: *Compilation,
346 path: std.Build.Cache.Path,
347 options: link.File.OpenOptions,
348) !*Elf {
349 return create(arena, comp, path, options);
350}
351pub fn createEmpty(
352 arena: std.mem.Allocator,
353 comp: *Compilation,
354 path: std.Build.Cache.Path,
355 options: link.File.OpenOptions,
356) !*Elf {
357 return create(arena, comp, path, options);
358}
359fn create(
360 arena: std.mem.Allocator,
361 comp: *Compilation,
362 path: std.Build.Cache.Path,
363 options: link.File.OpenOptions,
364) !*Elf {
365 _ = options;
366 const target = &comp.root_mod.resolved_target.result;
367 assert(target.ofmt == .elf);
368 const class: std.elf.CLASS = switch (target.ptrBitWidth()) {
369 0...32 => .@"32",
370 33...64 => .@"64",
371 else => return error.UnsupportedELFArchitecture,
372 };
373 const data: std.elf.DATA = switch (target.cpu.arch.endian()) {
374 .little => .@"2LSB",
375 .big => .@"2MSB",
376 };
377 const osabi: std.elf.OSABI = switch (target.os.tag) {
378 else => .NONE,
379 .freestanding, .other => .STANDALONE,
380 .netbsd => .NETBSD,
381 .solaris => .SOLARIS,
382 .aix => .AIX,
383 .freebsd => .FREEBSD,
384 .cuda => .CUDA,
385 .amdhsa => .AMDGPU_HSA,
386 .amdpal => .AMDGPU_PAL,
387 .mesa3d => .AMDGPU_MESA3D,
388 };
389 const @"type": std.elf.ET = switch (comp.config.output_mode) {
390 .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,
391 .Lib => switch (comp.config.link_mode) {
392 .static => .REL,
393 .dynamic => .DYN,
394 },
395 .Obj => .REL,
396 };
397 const machine: std.elf.EM = switch (target.cpu.arch) {
398 .spirv32, .spirv64, .wasm32, .wasm64 => .NONE,
399 .sparc => .SPARC,
400 .x86 => .@"386",
401 .m68k => .@"68K",
402 .mips, .mipsel, .mips64, .mips64el => .MIPS,
403 .powerpc, .powerpcle => .PPC,
404 .powerpc64, .powerpc64le => .PPC64,
405 .s390x => .S390,
406 .arm, .armeb, .thumb, .thumbeb => .ARM,
407 .hexagon => .SH,
408 .sparc64 => .SPARCV9,
409 .arc => .ARC,
410 .x86_64 => .X86_64,
411 .or1k => .OR1K,
412 .xtensa => .XTENSA,
413 .msp430 => .MSP430,
414 .avr => .AVR,
415 .nvptx, .nvptx64 => .CUDA,
416 .kalimba => .CSR_KALIMBA,
417 .aarch64, .aarch64_be => .AARCH64,
418 .xcore => .XCORE,
419 .amdgcn => .AMDGPU,
420 .riscv32, .riscv32be, .riscv64, .riscv64be => .RISCV,
421 .lanai => .LANAI,
422 .bpfel, .bpfeb => .BPF,
423 .ve => .VE,
424 .csky => .CSKY,
425 .loongarch32, .loongarch64 => .LOONGARCH,
426 .propeller => if (target.cpu.has(.propeller, .p2)) .PROPELLER2 else .PROPELLER,
427 };
428 const maybe_interp = switch (comp.config.output_mode) {
429 .Exe, .Lib => switch (comp.config.link_mode) {
430 .static => null,
431 .dynamic => target.dynamic_linker.get(),
432 },
433 .Obj => null,
434 };
435
436 const elf = try arena.create(Elf);
437 const file = try path.root_dir.handle.createFile(path.sub_path, .{
438 .read = true,
439 .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
440 });
441 errdefer file.close();
442 elf.* = .{
443 .base = .{
444 .tag = .elf2,
445
446 .comp = comp,
447 .emit = path,
448
449 .file = file,
450 .gc_sections = false,
451 .print_gc_sections = false,
452 .build_id = .none,
453 .allow_shlib_undefined = false,
454 .stack_size = 0,
455 },
456 .mf = try .init(file, comp.gpa),
457 .nodes = .empty,
458 .symtab = .empty,
459 .shstrtab = .{
460 .map = .empty,
461 .size = 1,
462 },
463 .strtab = .{
464 .map = .empty,
465 .size = 1,
466 },
467 .globals = .empty,
468 .navs = .empty,
469 .uavs = .empty,
470 .lazy = .initFill(.{
471 .map = .empty,
472 .pending_index = 0,
473 }),
474 .pending_uavs = .empty,
475 .relocs = .empty,
476 .entry_hack = .null,
477 };
478 errdefer elf.deinit();
479
480 switch (class) {
481 .NONE, _ => unreachable,
482 inline .@"32", .@"64" => |ct_class| try elf.initHeaders(
483 ct_class,
484 data,
485 osabi,
486 @"type",
487 machine,
488 maybe_interp,
489 ),
490 }
491
492 return elf;
493}
494
495pub fn deinit(elf: *Elf) void {
496 const gpa = elf.base.comp.gpa;
497 elf.mf.deinit(gpa);
498 elf.nodes.deinit(gpa);
499 elf.symtab.deinit(gpa);
500 elf.shstrtab.map.deinit(gpa);
501 elf.strtab.map.deinit(gpa);
502 elf.globals.deinit(gpa);
503 elf.navs.deinit(gpa);
504 elf.uavs.deinit(gpa);
505 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
506 elf.pending_uavs.deinit(gpa);
507 elf.relocs.deinit(gpa);
508 elf.* = undefined;
509}
510
511fn initHeaders(
512 elf: *Elf,
513 comptime class: std.elf.CLASS,
514 data: std.elf.DATA,
515 osabi: std.elf.OSABI,
516 @"type": std.elf.ET,
517 machine: std.elf.EM,
518 maybe_interp: ?[]const u8,
519) !void {
520 const comp = elf.base.comp;
521 const gpa = comp.gpa;
522 const ElfN = switch (class) {
523 .NONE, _ => comptime unreachable,
524 .@"32" => std.elf.Elf32,
525 .@"64" => std.elf.Elf64,
526 };
527 const addr_align: std.mem.Alignment = comptime .fromByteUnits(@sizeOf(ElfN.Addr));
528 const target_endian: std.builtin.Endian = switch (data) {
529 .NONE, _ => unreachable,
530 .@"2LSB" => .little,
531 .@"2MSB" => .big,
532 };
533
534 var phnum: u32 = 0;
535 const phdr_phndx = phnum;
536 phnum += 1;
537 const interp_phndx = if (maybe_interp) |_| phndx: {
538 defer phnum += 1;
539 break :phndx phnum;
540 } else undefined;
541 const rodata_phndx = phnum;
542 phnum += 1;
543 const text_phndx = phnum;
544 phnum += 1;
545 const data_phndx = phnum;
546 phnum += 1;
547 const tls_phndx = if (comp.config.any_non_single_threaded) phndx: {
548 defer phnum += 1;
549 break :phndx phnum;
550 } else undefined;
551
552 try elf.nodes.ensureTotalCapacity(gpa, Node.known_count);
553 elf.nodes.appendAssumeCapacity(.file);
554
555 const seg_rodata_ni = Node.known.seg_rodata;
556 assert(seg_rodata_ni == try elf.mf.addOnlyChildNode(gpa, .root, .{
557 .alignment = elf.mf.flags.block_size,
558 .fixed = true,
559 .moved = true,
560 }));
561 elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });
562
563 const ehdr_ni = Node.known.ehdr;
564 assert(ehdr_ni == try elf.mf.addOnlyChildNode(gpa, seg_rodata_ni, .{
565 .size = @sizeOf(ElfN.Ehdr),
566 .alignment = addr_align,
567 .fixed = true,
568 }));
569 elf.nodes.appendAssumeCapacity(.ehdr);
570
571 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(ehdr_ni.slice(&elf.mf)));
572 const EI = std.elf.EI;
573 @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC);
574 ehdr.ident[EI.CLASS] = @intFromEnum(class);
575 ehdr.ident[EI.DATA] = @intFromEnum(data);
576 ehdr.ident[EI.VERSION] = 1;
577 ehdr.ident[EI.OSABI] = @intFromEnum(osabi);
578 ehdr.ident[EI.ABIVERSION] = 0;
579 @memset(ehdr.ident[EI.PAD..], 0);
580 ehdr.type = @"type";
581 ehdr.machine = machine;
582 ehdr.version = 1;
583 ehdr.entry = 0;
584 ehdr.phoff = 0;
585 ehdr.shoff = 0;
586 ehdr.flags = 0;
587 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
588 ehdr.phentsize = @sizeOf(ElfN.Phdr);
589 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
590 ehdr.shentsize = @sizeOf(ElfN.Shdr);
591 ehdr.shnum = 1;
592 ehdr.shstrndx = 0;
593 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
594
595 const phdr_ni = Node.known.phdr;
596 assert(phdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
597 .size = @sizeOf(ElfN.Phdr) * phnum,
598 .alignment = addr_align,
599 .moved = true,
600 .resized = true,
601 }));
602 elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });
603
604 const shdr_ni = Node.known.shdr;
605 assert(shdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
606 .size = @sizeOf(ElfN.Shdr),
607 .alignment = addr_align,
608 }));
609 elf.nodes.appendAssumeCapacity(.shdr);
610
611 const seg_text_ni = Node.known.seg_text;
612 assert(seg_text_ni == try elf.mf.addLastChildNode(gpa, .root, .{
613 .alignment = elf.mf.flags.block_size,
614 .moved = true,
615 }));
616 elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });
617
618 const seg_data_ni = Node.known.seg_data;
619 assert(seg_data_ni == try elf.mf.addLastChildNode(gpa, .root, .{
620 .alignment = elf.mf.flags.block_size,
621 .moved = true,
622 }));
623 elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });
624
625 assert(elf.nodes.len == Node.known_count);
626
627 {
628 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(phdr_ni.slice(&elf.mf)));
629 const ph_phdr = &phdr[phdr_phndx];
630 ph_phdr.* = .{
631 .type = std.elf.PT_PHDR,
632 .offset = 0,
633 .vaddr = 0,
634 .paddr = 0,
635 .filesz = 0,
636 .memsz = 0,
637 .flags = .{ .R = true },
638 .@"align" = @intCast(phdr_ni.alignment(&elf.mf).toByteUnits()),
639 };
640 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
641
642 if (maybe_interp) |_| {
643 const ph_interp = &phdr[interp_phndx];
644 ph_interp.* = .{
645 .type = std.elf.PT_INTERP,
646 .offset = 0,
647 .vaddr = 0,
648 .paddr = 0,
649 .filesz = 0,
650 .memsz = 0,
651 .flags = .{ .R = true },
652 .@"align" = 1,
653 };
654 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
655 }
656
657 const ph_rodata = &phdr[rodata_phndx];
658 ph_rodata.* = .{
659 .type = std.elf.PT_NULL,
660 .offset = 0,
661 .vaddr = 0,
662 .paddr = 0,
663 .filesz = 0,
664 .memsz = 0,
665 .flags = .{ .R = true },
666 .@"align" = @intCast(seg_rodata_ni.alignment(&elf.mf).toByteUnits()),
667 };
668 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
669
670 const ph_text = &phdr[text_phndx];
671 ph_text.* = .{
672 .type = std.elf.PT_NULL,
673 .offset = 0,
674 .vaddr = 0,
675 .paddr = 0,
676 .filesz = 0,
677 .memsz = 0,
678 .flags = .{ .R = true, .X = true },
679 .@"align" = @intCast(seg_text_ni.alignment(&elf.mf).toByteUnits()),
680 };
681 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
682
683 const ph_data = &phdr[data_phndx];
684 ph_data.* = .{
685 .type = std.elf.PT_NULL,
686 .offset = 0,
687 .vaddr = 0,
688 .paddr = 0,
689 .filesz = 0,
690 .memsz = 0,
691 .flags = .{ .R = true, .W = true },
692 .@"align" = @intCast(seg_data_ni.alignment(&elf.mf).toByteUnits()),
693 };
694 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
695
696 if (comp.config.any_non_single_threaded) {
697 const ph_tls = &phdr[tls_phndx];
698 ph_tls.* = .{
699 .type = std.elf.PT_TLS,
700 .offset = 0,
701 .vaddr = 0,
702 .paddr = 0,
703 .filesz = 0,
704 .memsz = 0,
705 .flags = .{ .R = true },
706 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
707 };
708 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
709 }
710
711 const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(shdr_ni.slice(&elf.mf)));
712 sh_null.* = .{
713 .name = try elf.string(.shstrtab, ""),
714 .type = std.elf.SHT_NULL,
715 .flags = .{ .shf = .{} },
716 .addr = 0,
717 .offset = 0,
718 .size = 0,
719 .link = 0,
720 .info = if (phnum >= std.elf.PN_XNUM) phnum else 0,
721 .addralign = 0,
722 .entsize = 0,
723 };
724 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null);
725 }
726
727 try elf.symtab.ensureTotalCapacity(gpa, 1);
728 elf.symtab.addOneAssumeCapacity().* = .{
729 .ni = .none,
730 .loc_relocs = .none,
731 .target_relocs = .none,
732 .unused = 0,
733 };
734 assert(try elf.addSection(seg_rodata_ni, .{
735 .type = std.elf.SHT_SYMTAB,
736 .addralign = addr_align,
737 .entsize = @sizeOf(ElfN.Sym),
738 }) == .symtab);
739 const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf)));
740 symtab.* = .{
741 .name = try elf.string(.strtab, ""),
742 .value = 0,
743 .size = 0,
744 .info = .{
745 .type = .NOTYPE,
746 .bind = .LOCAL,
747 },
748 .other = .{
749 .visibility = .DEFAULT,
750 },
751 .shndx = std.elf.SHN_UNDEF,
752 };
753 ehdr.shstrndx = ehdr.shnum;
754 assert(try elf.addSection(seg_rodata_ni, .{
755 .type = std.elf.SHT_STRTAB,
756 .addralign = elf.mf.flags.block_size,
757 .entsize = 1,
758 }) == .shstrtab);
759 assert(try elf.addSection(seg_rodata_ni, .{
760 .type = std.elf.SHT_STRTAB,
761 .addralign = elf.mf.flags.block_size,
762 .entsize = 1,
763 }) == .strtab);
764 try elf.renameSection(.symtab, ".symtab");
765 try elf.renameSection(.shstrtab, ".shstrtab");
766 try elf.renameSection(.strtab, ".strtab");
767 try elf.linkSections(.symtab, .strtab);
768 Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[0] = 0;
769 Symbol.Index.strtab.node(elf).slice(&elf.mf)[0] = 0;
770
771 assert(try elf.addSection(seg_rodata_ni, .{
772 .name = ".rodata",
773 .flags = .{ .ALLOC = true },
774 .addralign = elf.mf.flags.block_size,
775 }) == .rodata);
776 assert(try elf.addSection(seg_text_ni, .{
777 .name = ".text",
778 .flags = .{ .ALLOC = true, .EXECINSTR = true },
779 .addralign = elf.mf.flags.block_size,
780 }) == .text);
781 assert(try elf.addSection(seg_data_ni, .{
782 .name = ".data",
783 .flags = .{ .WRITE = true, .ALLOC = true },
784 .addralign = elf.mf.flags.block_size,
785 }) == .data);
786 if (comp.config.any_non_single_threaded) {
787 try elf.nodes.ensureUnusedCapacity(gpa, 1);
788 const seg_tls_ni = try elf.mf.addLastChildNode(gpa, seg_data_ni, .{
789 .alignment = elf.mf.flags.block_size,
790 .moved = true,
791 });
792 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
793
794 assert(try elf.addSection(seg_tls_ni, .{
795 .name = ".tdata",
796 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
797 .addralign = elf.mf.flags.block_size,
798 }) == .tdata);
799 }
800 if (maybe_interp) |interp| {
801 try elf.nodes.ensureUnusedCapacity(gpa, 1);
802 const seg_interp_ni = try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
803 .size = interp.len + 1,
804 .moved = true,
805 .resized = true,
806 });
807 elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
808
809 const sec_interp_si = try elf.addSection(seg_interp_ni, .{
810 .name = ".interp",
811 .size = @intCast(interp.len + 1),
812 .flags = .{ .ALLOC = true },
813 });
814 const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
815 @memcpy(sec_interp[0..interp.len], interp);
816 sec_interp[interp.len] = 0;
817 }
818}
819
820fn getNode(elf: *Elf, ni: MappedFile.Node.Index) Node {
821 return elf.nodes.get(@intFromEnum(ni));
822}
823
824pub const EhdrPtr = union(std.elf.CLASS) {
825 NONE: noreturn,
826 @"32": *std.elf.Elf32.Ehdr,
827 @"64": *std.elf.Elf64.Ehdr,
828};
829pub fn ehdrPtr(elf: *Elf) EhdrPtr {
830 const slice = Node.known.ehdr.slice(&elf.mf);
831 return switch (elf.identClass()) {
832 .NONE, _ => unreachable,
833 inline .@"32", .@"64" => |class| @unionInit(
834 EhdrPtr,
835 @tagName(class),
836 @ptrCast(@alignCast(slice)),
837 ),
838 };
839}
840pub fn ehdrField(
841 elf: *Elf,
842 comptime field: enum { type, machine },
843) @FieldType(std.elf.Elf32.Ehdr, @tagName(field)) {
844 const Field = @FieldType(std.elf.Elf32.Ehdr, @tagName(field));
845 comptime assert(@FieldType(std.elf.Elf64.Ehdr, @tagName(field)) == Field);
846 return @enumFromInt(std.mem.toNative(
847 @typeInfo(Field).@"enum".tag_type,
848 @intFromEnum(switch (elf.ehdrPtr()) {
849 inline else => |ehdr| @field(ehdr, @tagName(field)),
850 }),
851 elf.endian(),
852 ));
853}
854
855pub fn identClass(elf: *Elf) std.elf.CLASS {
856 return @enumFromInt(elf.mf.contents[std.elf.EI.CLASS]);
857}
858
859pub fn identData(elf: *Elf) std.elf.DATA {
860 return @enumFromInt(elf.mf.contents[std.elf.EI.DATA]);
861}
862fn endianForData(data: std.elf.DATA) std.builtin.Endian {
863 return switch (data) {
864 .NONE, _ => unreachable,
865 .@"2LSB" => .little,
866 .@"2MSB" => .big,
867 };
868}
869pub fn endian(elf: *Elf) std.builtin.Endian {
870 return endianForData(elf.identData());
871}
872
873fn baseAddrForType(@"type": std.elf.ET) u64 {
874 return switch (@"type") {
875 else => 0,
876 .EXEC => 0x1000000,
877 };
878}
879pub fn baseAddr(elf: *Elf) u64 {
880 return baseAddrForType(elf.ehdrField(.type));
881}
882
883pub const PhdrSlice = union(std.elf.CLASS) {
884 NONE: noreturn,
885 @"32": []std.elf.Elf32.Phdr,
886 @"64": []std.elf.Elf64.Phdr,
887};
888pub fn phdrSlice(elf: *Elf) PhdrSlice {
889 const slice = Node.known.phdr.slice(&elf.mf);
890 return switch (elf.identClass()) {
891 .NONE, _ => unreachable,
892 inline .@"32", .@"64" => |class| @unionInit(
893 PhdrSlice,
894 @tagName(class),
895 @ptrCast(@alignCast(slice)),
896 ),
897 };
898}
899
900pub const ShdrSlice = union(std.elf.CLASS) {
901 NONE: noreturn,
902 @"32": []std.elf.Elf32.Shdr,
903 @"64": []std.elf.Elf64.Shdr,
904};
905pub fn shdrSlice(elf: *Elf) ShdrSlice {
906 const slice = Node.known.shdr.slice(&elf.mf);
907 return switch (elf.identClass()) {
908 .NONE, _ => unreachable,
909 inline .@"32", .@"64" => |class| @unionInit(
910 ShdrSlice,
911 @tagName(class),
912 @ptrCast(@alignCast(slice)),
913 ),
914 };
915}
916
917pub const SymSlice = union(std.elf.CLASS) {
918 NONE: noreturn,
919 @"32": []std.elf.Elf32.Sym,
920 @"64": []std.elf.Elf64.Sym,
921};
922pub fn symSlice(elf: *Elf) SymSlice {
923 const slice = Symbol.Index.symtab.node(elf).slice(&elf.mf);
924 return switch (elf.identClass()) {
925 .NONE, _ => unreachable,
926 inline .@"32", .@"64" => |class| @unionInit(
927 SymSlice,
928 @tagName(class),
929 @ptrCast(@alignCast(slice)),
930 ),
931 };
932}
933
934pub const SymPtr = union(std.elf.CLASS) {
935 NONE: noreturn,
936 @"32": *std.elf.Elf32.Sym,
937 @"64": *std.elf.Elf64.Sym,
938};
939pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr {
940 return switch (elf.symSlice()) {
941 inline else => |sym, class| @unionInit(SymPtr, @tagName(class), &sym[@intFromEnum(si)]),
942 };
943}
944
945fn addSymbolAssumeCapacity(elf: *Elf) !Symbol.Index {
946 defer elf.symtab.addOneAssumeCapacity().* = .{
947 .ni = .none,
948 .loc_relocs = .none,
949 .target_relocs = .none,
950 .unused = 0,
951 };
952 return @enumFromInt(elf.symtab.items.len);
953}
954
955fn initSymbolAssumeCapacity(elf: *Elf, opts: Symbol.Index.InitOptions) !Symbol.Index {
956 const si = try elf.addSymbolAssumeCapacity();
957 try si.init(elf, opts);
958 return si;
959}
960
961pub fn globalSymbol(
962 elf: *Elf,
963 opts: struct {
964 name: []const u8,
965 type: std.elf.STT,
966 bind: std.elf.STB = .GLOBAL,
967 visibility: std.elf.STV = .DEFAULT,
968 },
969) !Symbol.Index {
970 const gpa = elf.base.comp.gpa;
971 try elf.symtab.ensureUnusedCapacity(gpa, 1);
972 const sym_gop = try elf.globals.getOrPut(gpa, try elf.string(.strtab, opts.name));
973 if (!sym_gop.found_existing) sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
974 .name = opts.name,
975 .type = opts.type,
976 .bind = opts.bind,
977 .visibility = opts.visibility,
978 });
979 return sym_gop.value_ptr.*;
980}
981
982fn navType(
983 ip: *const InternPool,
984 nav_status: @FieldType(InternPool.Nav, "status"),
985 any_non_single_threaded: bool,
986) std.elf.STT {
987 return switch (nav_status) {
988 .unresolved => unreachable,
989 .type_resolved => |tr| if (any_non_single_threaded and tr.is_threadlocal)
990 .TLS
991 else if (ip.isFunctionType(tr.type))
992 .FUNC
993 else
994 .OBJECT,
995 .fully_resolved => |fr| switch (ip.indexToKey(fr.val)) {
996 else => .OBJECT,
997 .variable => |variable| if (any_non_single_threaded and variable.is_threadlocal)
998 .TLS
999 else
1000 .OBJECT,
1001 .@"extern" => |@"extern"| if (any_non_single_threaded and @"extern".is_threadlocal)
1002 .TLS
1003 else if (ip.isFunctionType(@"extern".ty))
1004 .FUNC
1005 else
1006 .OBJECT,
1007 .func => .FUNC,
1008 },
1009 };
1010}
1011pub fn navSymbol(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
1012 const gpa = zcu.gpa;
1013 const ip = &zcu.intern_pool;
1014 const nav = ip.getNav(nav_index);
1015 if (nav.getExtern(ip)) |@"extern"| return elf.globalSymbol(.{
1016 .name = @"extern".name.toSlice(ip),
1017 .type = navType(ip, nav.status, elf.base.comp.config.any_non_single_threaded),
1018 .bind = switch (@"extern".linkage) {
1019 .internal => .LOCAL,
1020 .strong => .GLOBAL,
1021 .weak => .WEAK,
1022 .link_once => return error.LinkOnceUnsupported,
1023 },
1024 .visibility = switch (@"extern".visibility) {
1025 .default => .DEFAULT,
1026 .hidden => .HIDDEN,
1027 .protected => .PROTECTED,
1028 },
1029 });
1030 try elf.symtab.ensureUnusedCapacity(gpa, 1);
1031 const sym_gop = try elf.navs.getOrPut(gpa, nav_index);
1032 if (!sym_gop.found_existing) {
1033 sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
1034 .name = nav.fqn.toSlice(ip),
1035 .type = navType(ip, nav.status, elf.base.comp.config.any_non_single_threaded),
1036 });
1037 }
1038 return sym_gop.value_ptr.*;
1039}
1040
1041pub fn uavSymbol(elf: *Elf, uav_val: InternPool.Index) !Symbol.Index {
1042 const gpa = elf.base.comp.gpa;
1043 try elf.symtab.ensureUnusedCapacity(gpa, 1);
1044 const sym_gop = try elf.uavs.getOrPut(gpa, uav_val);
1045 if (!sym_gop.found_existing)
1046 sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{ .type = .OBJECT });
1047 return sym_gop.value_ptr.*;
1048}
1049
1050pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index {
1051 const gpa = elf.base.comp.gpa;
1052 try elf.symtab.ensureUnusedCapacity(gpa, 1);
1053 const sym_gop = try elf.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
1054 if (!sym_gop.found_existing) {
1055 sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
1056 .type = switch (lazy.kind) {
1057 .code => .FUNC,
1058 .const_data => .OBJECT,
1059 },
1060 });
1061 elf.base.comp.link_lazy_prog_node.increaseEstimatedTotalItems(1);
1062 }
1063 return sym_gop.value_ptr.*;
1064}
1065
1066pub fn getNavVAddr(
1067 elf: *Elf,
1068 pt: Zcu.PerThread,
1069 nav: InternPool.Nav.Index,
1070 reloc_info: link.File.RelocInfo,
1071) !u64 {
1072 return elf.getVAddr(reloc_info, try elf.navSymbol(pt.zcu, nav));
1073}
1074
1075pub fn getUavVAddr(
1076 elf: *Elf,
1077 uav: InternPool.Index,
1078 reloc_info: link.File.RelocInfo,
1079) !u64 {
1080 return elf.getVAddr(reloc_info, try elf.uavSymbol(uav));
1081}
1082
1083pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
1084 try elf.addReloc(
1085 @enumFromInt(reloc_info.parent.atom_index),
1086 reloc_info.offset,
1087 target_si,
1088 reloc_info.addend,
1089 switch (elf.ehdrField(.machine)) {
1090 else => unreachable,
1091 .X86_64 => .{ .x86_64 = switch (elf.identClass()) {
1092 .NONE, _ => unreachable,
1093 .@"32" => .@"32",
1094 .@"64" => .@"64",
1095 } },
1096 },
1097 );
1098 return 0;
1099}
1100
1101fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
1102 name: []const u8 = "",
1103 type: std.elf.Word = std.elf.SHT_NULL,
1104 size: std.elf.Word = 0,
1105 flags: std.elf.SHF = .{},
1106 addralign: std.mem.Alignment = .@"1",
1107 entsize: std.elf.Word = 0,
1108}) !Symbol.Index {
1109 const gpa = elf.base.comp.gpa;
1110 const target_endian = elf.endian();
1111 try elf.nodes.ensureUnusedCapacity(gpa, 1);
1112 try elf.symtab.ensureUnusedCapacity(gpa, 1);
1113
1114 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
1115 const shndx, const shdr_size = shndx: switch (elf.ehdrPtr()) {
1116 inline else => |ehdr| {
1117 const shentsize = std.mem.toNative(@TypeOf(ehdr.shentsize), ehdr.shentsize, target_endian);
1118 const shndx = std.mem.toNative(@TypeOf(ehdr.shnum), ehdr.shnum, target_endian);
1119 const shnum = shndx + 1;
1120 ehdr.shnum = std.mem.nativeTo(@TypeOf(ehdr.shnum), shnum, target_endian);
1121 break :shndx .{ shndx, shentsize * shnum };
1122 },
1123 };
1124 try Node.known.shdr.resize(&elf.mf, gpa, shdr_size);
1125 const ni = try elf.mf.addLastChildNode(gpa, segment_ni, .{
1126 .alignment = opts.addralign,
1127 .size = opts.size,
1128 .moved = true,
1129 });
1130 const si = try elf.addSymbolAssumeCapacity();
1131 elf.nodes.appendAssumeCapacity(.{ .section = si });
1132 si.get(elf).ni = ni;
1133 try si.init(elf, .{
1134 .name = opts.name,
1135 .size = opts.size,
1136 .type = .SECTION,
1137 .shndx = shndx,
1138 });
1139 switch (elf.shdrSlice()) {
1140 inline else => |shdr| {
1141 const sh = &shdr[shndx];
1142 sh.* = .{
1143 .name = shstrtab_entry,
1144 .type = opts.type,
1145 .flags = .{ .shf = opts.flags },
1146 .addr = 0,
1147 .offset = 0,
1148 .size = opts.size,
1149 .link = 0,
1150 .info = 0,
1151 .addralign = @intCast(opts.addralign.toByteUnits()),
1152 .entsize = opts.entsize,
1153 };
1154 if (target_endian != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh);
1155 },
1156 }
1157 return si;
1158}
1159
1160fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void {
1161 const strtab_entry = try elf.string(.strtab, name);
1162 const shstrtab_entry = try elf.string(.shstrtab, name);
1163 const target_endian = elf.endian();
1164 switch (elf.shdrSlice()) {
1165 inline else => |shdr, class| {
1166 const sym = @field(elf.symPtr(si), @tagName(class));
1167 sym.name = std.mem.nativeTo(@TypeOf(sym.name), strtab_entry, target_endian);
1168 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1169 const sh = &shdr[shndx];
1170 sh.name = std.mem.nativeTo(@TypeOf(sh.name), shstrtab_entry, target_endian);
1171 },
1172 }
1173}
1174
1175fn linkSections(elf: *Elf, si: Symbol.Index, link_si: Symbol.Index) !void {
1176 const target_endian = elf.endian();
1177 switch (elf.shdrSlice()) {
1178 inline else => |shdr, class| {
1179 const sym = @field(elf.symPtr(si), @tagName(class));
1180 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1181 shdr[shndx].link = @field(elf.symPtr(link_si), @tagName(class)).shndx;
1182 },
1183 }
1184}
1185
1186fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 {
1187 const target_endian = elf.endian();
1188 const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[name: switch (elf.shdrSlice()) {
1189 inline else => |shndx, class| {
1190 const sym = @field(elf.symPtr(si), @tagName(class));
1191 const sh = &shndx[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
1192 break :name std.mem.toNative(@TypeOf(sh.name), sh.name, target_endian);
1193 },
1194 }..];
1195 return name[0..std.mem.indexOfScalar(u8, name, 0).? :0];
1196}
1197
1198fn string(elf: *Elf, comptime section: enum { shstrtab, strtab }, key: []const u8) !u32 {
1199 if (key.len == 0) return 0;
1200 return @field(elf, @tagName(section)).get(
1201 elf.base.comp.gpa,
1202 &elf.mf,
1203 @field(Symbol.Index, @tagName(section)).node(elf),
1204 key,
1205 );
1206}
1207
1208pub fn addReloc(
1209 elf: *Elf,
1210 loc_si: Symbol.Index,
1211 offset: u64,
1212 target_si: Symbol.Index,
1213 addend: i64,
1214 @"type": Reloc.Type,
1215) !void {
1216 const gpa = elf.base.comp.gpa;
1217 const target = target_si.get(elf);
1218 const ri: link.File.Elf2.Reloc.Index = @enumFromInt(elf.relocs.items.len);
1219 (try elf.relocs.addOne(gpa)).* = .{
1220 .type = @"type",
1221 .prev = .none,
1222 .next = target.target_relocs,
1223 .loc = loc_si,
1224 .target = target_si,
1225 .unused = 0,
1226 .offset = offset,
1227 .addend = addend,
1228 };
1229 switch (target.target_relocs) {
1230 .none => {},
1231 else => |target_ri| target_ri.get(elf).prev = ri,
1232 }
1233 target.target_relocs = ri;
1234}
1235
1236pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) void {
1237 _ = elf;
1238 _ = prog_node;
1239}
1240
1241pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
1242 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
1243 error.OutOfMemory,
1244 error.Overflow,
1245 error.RelocationNotByteAligned,
1246 => |e| return e,
1247 else => |e| return elf.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}),
1248 };
1249}
1250fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
1251 const comp = elf.base.comp;
1252 const zcu = pt.zcu;
1253 const gpa = zcu.gpa;
1254 const ip = &zcu.intern_pool;
1255
1256 const nav = ip.getNav(nav_index);
1257 const nav_val = nav.status.fully_resolved.val;
1258 const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) {
1259 else => .{ nav_val, false },
1260 .variable => |variable| .{ variable.init, variable.is_threadlocal },
1261 .@"extern" => return,
1262 .func => .{ .none, false },
1263 };
1264 if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
1265
1266 const si = try elf.navSymbol(zcu, nav_index);
1267 const ni = ni: {
1268 const sym = si.get(elf);
1269 switch (sym.ni) {
1270 .none => {
1271 try elf.nodes.ensureUnusedCapacity(gpa, 1);
1272 const sec_si: Symbol.Index =
1273 if (is_threadlocal and comp.config.any_non_single_threaded) .tdata else .data;
1274 const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
1275 .alignment = pt.navAlignment(nav_index).toStdMem(),
1276 .moved = true,
1277 });
1278 elf.nodes.appendAssumeCapacity(.{ .nav = nav_index });
1279 sym.ni = ni;
1280 switch (elf.symPtr(si)) {
1281 inline else => |sym_ptr, class| sym_ptr.shndx =
1282 @field(elf.symPtr(sec_si), @tagName(class)).shndx,
1283 }
1284 },
1285 else => si.deleteLocationRelocs(elf),
1286 }
1287 assert(sym.loc_relocs == .none);
1288 sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
1289 break :ni sym.ni;
1290 };
1291
1292 const size = size: {
1293 var nw: MappedFile.Node.Writer = undefined;
1294 ni.writer(&elf.mf, gpa, &nw);
1295 defer nw.deinit();
1296 codegen.generateSymbol(
1297 &elf.base,
1298 pt,
1299 zcu.navSrcLoc(nav_index),
1300 .fromInterned(nav_init),
1301 &nw.interface,
1302 .{ .atom_index = @intFromEnum(si) },
1303 ) catch |err| switch (err) {
1304 error.WriteFailed => return error.OutOfMemory,
1305 else => |e| return e,
1306 };
1307 break :size nw.interface.end;
1308 };
1309
1310 const target_endian = elf.endian();
1311 switch (elf.symPtr(si)) {
1312 inline else => |sym| sym.size =
1313 std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
1314 }
1315 si.applyLocationRelocs(elf);
1316}
1317
1318pub fn lowerUav(
1319 elf: *Elf,
1320 pt: Zcu.PerThread,
1321 uav_val: InternPool.Index,
1322 uav_align: InternPool.Alignment,
1323 src_loc: Zcu.LazySrcLoc,
1324) !codegen.SymbolResult {
1325 const zcu = pt.zcu;
1326 const gpa = zcu.gpa;
1327
1328 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
1329 const si = elf.uavSymbol(uav_val) catch |err| switch (err) {
1330 error.OutOfMemory => return error.OutOfMemory,
1331 else => |e| return .{ .fail = try Zcu.ErrorMsg.create(
1332 gpa,
1333 src_loc,
1334 "linker failed to update constant: {s}",
1335 .{@errorName(e)},
1336 ) },
1337 };
1338 if (switch (si.get(elf).ni) {
1339 .none => true,
1340 else => |ni| uav_align.toStdMem().order(ni.alignment(&elf.mf)).compare(.gt),
1341 }) {
1342 const gop = elf.pending_uavs.getOrPutAssumeCapacity(uav_val);
1343 if (gop.found_existing) {
1344 gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
1345 } else {
1346 gop.value_ptr.* = .{
1347 .alignment = uav_align,
1348 .src_loc = src_loc,
1349 };
1350 elf.base.comp.link_uav_prog_node.increaseEstimatedTotalItems(1);
1351 }
1352 }
1353 return .{ .sym_index = @intFromEnum(si) };
1354}
1355
1356pub fn updateFunc(
1357 elf: *Elf,
1358 pt: Zcu.PerThread,
1359 func_index: InternPool.Index,
1360 mir: *const codegen.AnyMir,
1361) !void {
1362 elf.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
1363 error.OutOfMemory,
1364 error.Overflow,
1365 error.RelocationNotByteAligned,
1366 error.CodegenFail,
1367 => |e| return e,
1368 else => |e| return elf.base.cgFail(
1369 pt.zcu.funcInfo(func_index).owner_nav,
1370 "linker failed to update function: {s}",
1371 .{@errorName(e)},
1372 ),
1373 };
1374}
1375fn updateFuncInner(
1376 elf: *Elf,
1377 pt: Zcu.PerThread,
1378 func_index: InternPool.Index,
1379 mir: *const codegen.AnyMir,
1380) !void {
1381 const zcu = pt.zcu;
1382 const gpa = zcu.gpa;
1383 const ip = &zcu.intern_pool;
1384 const func = zcu.funcInfo(func_index);
1385 const nav = ip.getNav(func.owner_nav);
1386
1387 const si = try elf.navSymbol(zcu, func.owner_nav);
1388 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
1389 const ni = ni: {
1390 const sym = si.get(elf);
1391 switch (sym.ni) {
1392 .none => {
1393 try elf.nodes.ensureUnusedCapacity(gpa, 1);
1394 const mod = zcu.navFileScope(func.owner_nav).mod.?;
1395 const target = &mod.resolved_target.result;
1396 const ni = try elf.mf.addLastChildNode(gpa, Symbol.Index.text.node(elf), .{
1397 .alignment = switch (nav.status.fully_resolved.alignment) {
1398 .none => switch (mod.optimize_mode) {
1399 .Debug,
1400 .ReleaseSafe,
1401 .ReleaseFast,
1402 => target_util.defaultFunctionAlignment(target),
1403 .ReleaseSmall => target_util.minFunctionAlignment(target),
1404 },
1405 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
1406 }.toStdMem(),
1407 .moved = true,
1408 });
1409 elf.nodes.appendAssumeCapacity(.{ .nav = func.owner_nav });
1410 sym.ni = ni;
1411 switch (elf.symPtr(si)) {
1412 inline else => |sym_ptr, class| sym_ptr.shndx =
1413 @field(elf.symPtr(.text), @tagName(class)).shndx,
1414 }
1415 },
1416 else => si.deleteLocationRelocs(elf),
1417 }
1418 assert(sym.loc_relocs == .none);
1419 sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
1420 break :ni sym.ni;
1421 };
1422
1423 const size = size: {
1424 var nw: MappedFile.Node.Writer = undefined;
1425 ni.writer(&elf.mf, gpa, &nw);
1426 defer nw.deinit();
1427 codegen.emitFunction(
1428 &elf.base,
1429 pt,
1430 zcu.navSrcLoc(func.owner_nav),
1431 func_index,
1432 @intFromEnum(si),
1433 mir,
1434 &nw.interface,
1435 .none,
1436 ) catch |err| switch (err) {
1437 error.WriteFailed => return nw.err.?,
1438 else => |e| return e,
1439 };
1440 break :size nw.interface.end;
1441 };
1442
1443 const target_endian = elf.endian();
1444 switch (elf.symPtr(si)) {
1445 inline else => |sym| sym.size =
1446 std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
1447 }
1448 si.applyLocationRelocs(elf);
1449}
1450
1451pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) !void {
1452 const si = elf.lazy.getPtr(.const_data).map.get(.anyerror_type) orelse return;
1453 elf.flushLazy(pt, .{ .kind = .const_data, .ty = .anyerror_type }, si) catch |err| switch (err) {
1454 error.OutOfMemory => return error.OutOfMemory,
1455 error.CodegenFail => return error.LinkFailure,
1456 else => |e| return elf.base.comp.link_diags.fail("updateErrorData failed {t}", .{e}),
1457 };
1458}
1459
1460pub fn flush(
1461 elf: *Elf,
1462 arena: std.mem.Allocator,
1463 tid: Zcu.PerThread.Id,
1464 prog_node: std.Progress.Node,
1465) !void {
1466 _ = arena;
1467 _ = prog_node;
1468 while (try elf.idle(tid)) {}
1469}
1470
1471pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
1472 const comp = elf.base.comp;
1473 task: {
1474 while (elf.pending_uavs.pop()) |pending_uav| {
1475 const sub_prog_node =
1476 elf.idleProgNode(
1477 tid,
1478 comp.link_uav_prog_node,
1479 .{ .uav = pending_uav.key },
1480 );
1481 defer sub_prog_node.end();
1482 break :task elf.flushUav(
1483 .{ .zcu = elf.base.comp.zcu.?, .tid = tid },
1484 pending_uav.key,
1485 pending_uav.value.alignment,
1486 pending_uav.value.src_loc,
1487 ) catch |err| switch (err) {
1488 error.OutOfMemory => return error.OutOfMemory,
1489 else => |e| return elf.base.comp.link_diags.fail(
1490 "linker failed to lower constant: {t}",
1491 .{e},
1492 ),
1493 };
1494 }
1495 var lazy_it = elf.lazy.iterator();
1496 while (lazy_it.next()) |lazy| for (
1497 lazy.value.map.keys()[lazy.value.pending_index..],
1498 lazy.value.map.values()[lazy.value.pending_index..],
1499 ) |ty, si| {
1500 lazy.value.pending_index += 1;
1501 const pt: Zcu.PerThread = .{ .zcu = elf.base.comp.zcu.?, .tid = tid };
1502 const kind = switch (lazy.key) {
1503 .code => "code",
1504 .const_data => "data",
1505 };
1506 var name: [std.Progress.Node.max_name_len]u8 = undefined;
1507 const sub_prog_node = comp.link_lazy_prog_node.start(
1508 std.fmt.bufPrint(&name, "lazy {s} for {f}", .{
1509 kind,
1510 Type.fromInterned(ty).fmt(pt),
1511 }) catch &name,
1512 0,
1513 );
1514 defer sub_prog_node.end();
1515 break :task elf.flushLazy(pt, .{
1516 .kind = lazy.key,
1517 .ty = ty,
1518 }, si) catch |err| switch (err) {
1519 error.OutOfMemory => return error.OutOfMemory,
1520 else => |e| return elf.base.comp.link_diags.fail(
1521 "linker failed to lower lazy {s}: {t}",
1522 .{ kind, e },
1523 ),
1524 };
1525 };
1526 while (elf.mf.updates.pop()) |ni| {
1527 const clean_moved = ni.cleanMoved(&elf.mf);
1528 const clean_resized = ni.cleanResized(&elf.mf);
1529 if (clean_moved or clean_resized) {
1530 const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni));
1531 defer sub_prog_node.end();
1532 if (clean_moved) try elf.flushMoved(ni);
1533 if (clean_resized) try elf.flushResized(ni);
1534 break :task;
1535 } else elf.mf.update_prog_node.completeOne();
1536 }
1537 }
1538 if (elf.pending_uavs.count() > 0) return true;
1539 for (&elf.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
1540 if (elf.mf.updates.items.len > 0) return true;
1541 return false;
1542}
1543
1544fn idleProgNode(
1545 elf: *Elf,
1546 tid: Zcu.PerThread.Id,
1547 prog_node: std.Progress.Node,
1548 node: Node,
1549) std.Progress.Node {
1550 var name: [std.Progress.Node.max_name_len]u8 = undefined;
1551 return prog_node.start(name: switch (node) {
1552 else => |tag| @tagName(tag),
1553 .section => |si| elf.sectionName(si),
1554 .nav => |nav| {
1555 const ip = &elf.base.comp.zcu.?.intern_pool;
1556 break :name ip.getNav(nav).fqn.toSlice(ip);
1557 },
1558 .uav => |uav| std.fmt.bufPrint(&name, "{f}", .{
1559 Value.fromInterned(uav).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }),
1560 }) catch &name,
1561 }, 0);
1562}
1563
1564fn flushUav(
1565 elf: *Elf,
1566 pt: Zcu.PerThread,
1567 uav_val: InternPool.Index,
1568 uav_align: InternPool.Alignment,
1569 src_loc: Zcu.LazySrcLoc,
1570) !void {
1571 const zcu = pt.zcu;
1572 const gpa = zcu.gpa;
1573
1574 const si = try elf.uavSymbol(uav_val);
1575 const ni = ni: {
1576 const sym = si.get(elf);
1577 switch (sym.ni) {
1578 .none => {
1579 try elf.nodes.ensureUnusedCapacity(gpa, 1);
1580 const ni = try elf.mf.addLastChildNode(gpa, Symbol.Index.data.node(elf), .{
1581 .alignment = uav_align.toStdMem(),
1582 .moved = true,
1583 });
1584 elf.nodes.appendAssumeCapacity(.{ .uav = uav_val });
1585 sym.ni = ni;
1586 switch (elf.symPtr(si)) {
1587 inline else => |sym_ptr, class| sym_ptr.shndx =
1588 @field(elf.symPtr(.data), @tagName(class)).shndx,
1589 }
1590 },
1591 else => {
1592 if (sym.ni.alignment(&elf.mf).order(uav_align.toStdMem()).compare(.gte)) return;
1593 si.deleteLocationRelocs(elf);
1594 },
1595 }
1596 assert(sym.loc_relocs == .none);
1597 sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
1598 break :ni sym.ni;
1599 };
1600
1601 const size = size: {
1602 var nw: MappedFile.Node.Writer = undefined;
1603 ni.writer(&elf.mf, gpa, &nw);
1604 defer nw.deinit();
1605 codegen.generateSymbol(
1606 &elf.base,
1607 pt,
1608 src_loc,
1609 .fromInterned(uav_val),
1610 &nw.interface,
1611 .{ .atom_index = @intFromEnum(si) },
1612 ) catch |err| switch (err) {
1613 error.WriteFailed => return error.OutOfMemory,
1614 else => |e| return e,
1615 };
1616 break :size nw.interface.end;
1617 };
1618
1619 const target_endian = elf.endian();
1620 switch (elf.symPtr(si)) {
1621 inline else => |sym| sym.size =
1622 std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
1623 }
1624 si.applyLocationRelocs(elf);
1625}
1626
1627fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lazy: link.File.LazySymbol, si: Symbol.Index) !void {
1628 const zcu = pt.zcu;
1629 const gpa = zcu.gpa;
1630
1631 const ni = ni: {
1632 const sym = si.get(elf);
1633 switch (sym.ni) {
1634 .none => {
1635 try elf.nodes.ensureUnusedCapacity(gpa, 1);
1636 const sec_si: Symbol.Index = switch (lazy.kind) {
1637 .code => .text,
1638 .const_data => .rodata,
1639 };
1640 const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{ .moved = true });
1641 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
1642 .code => .{ .lazy_code = lazy.ty },
1643 .const_data => .{ .lazy_const_data = lazy.ty },
1644 });
1645 sym.ni = ni;
1646 switch (elf.symPtr(si)) {
1647 inline else => |sym_ptr, class| sym_ptr.shndx =
1648 @field(elf.symPtr(sec_si), @tagName(class)).shndx,
1649 }
1650 },
1651 else => si.deleteLocationRelocs(elf),
1652 }
1653 assert(sym.loc_relocs == .none);
1654 sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
1655 break :ni sym.ni;
1656 };
1657
1658 const size = size: {
1659 var required_alignment: InternPool.Alignment = .none;
1660 var nw: MappedFile.Node.Writer = undefined;
1661 ni.writer(&elf.mf, gpa, &nw);
1662 defer nw.deinit();
1663 try codegen.generateLazySymbol(
1664 &elf.base,
1665 pt,
1666 Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded,
1667 lazy,
1668 &required_alignment,
1669 &nw.interface,
1670 .none,
1671 .{ .atom_index = @intFromEnum(si) },
1672 );
1673 break :size nw.interface.end;
1674 };
1675
1676 const target_endian = elf.endian();
1677 switch (elf.symPtr(si)) {
1678 inline else => |sym| sym.size =
1679 std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
1680 }
1681 si.applyLocationRelocs(elf);
1682}
1683
1684fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
1685 const target_endian = elf.endian();
1686 const file_offset = ni.fileLocation(&elf.mf, false).offset;
1687 const node = elf.getNode(ni);
1688 switch (node) {
1689 else => |tag| @panic(@tagName(tag)),
1690 .ehdr => assert(file_offset == 0),
1691 .shdr => switch (elf.ehdrPtr()) {
1692 inline else => |ehdr| ehdr.shoff =
1693 std.mem.nativeTo(@TypeOf(ehdr.shoff), @intCast(file_offset), target_endian),
1694 },
1695 .segment => |phndx| switch (elf.phdrSlice()) {
1696 inline else => |phdr, class| {
1697 const ph = &phdr[phndx];
1698 switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
1699 else => unreachable,
1700 std.elf.PT_NULL, std.elf.PT_LOAD, std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {},
1701 std.elf.PT_PHDR => {
1702 const ehdr = @field(elf.ehdrPtr(), @tagName(class));
1703 ehdr.phoff =
1704 std.mem.nativeTo(@TypeOf(ehdr.phoff), @intCast(file_offset), target_endian);
1705 },
1706 std.elf.PT_TLS => {},
1707 }
1708 ph.offset = std.mem.nativeTo(@TypeOf(ph.offset), @intCast(file_offset), target_endian);
1709 ph.vaddr = std.mem.nativeTo(
1710 @TypeOf(ph.vaddr),
1711 @intCast(elf.baseAddr() + file_offset),
1712 target_endian,
1713 );
1714 ph.paddr = ph.vaddr;
1715 },
1716 },
1717 .section => |si| switch (elf.shdrSlice()) {
1718 inline else => |shdr, class| {
1719 const sym = @field(elf.symPtr(si), @tagName(class));
1720 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1721 const sh = &shdr[shndx];
1722 const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
1723 @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
1724 @bitCast(sh.flags),
1725 target_endian,
1726 ));
1727 if (flags.shf.ALLOC) {
1728 sym.value = std.mem.nativeTo(
1729 @TypeOf(sym.value),
1730 @intCast(elf.baseAddr() + file_offset),
1731 target_endian,
1732 );
1733 sh.addr = sym.value;
1734 }
1735 sh.offset = std.mem.nativeTo(@TypeOf(sh.offset), @intCast(file_offset), target_endian);
1736 },
1737 },
1738 .nav, .uav, .lazy_code, .lazy_const_data => {
1739 const si = switch (node) {
1740 else => unreachable,
1741 .nav => |nav| elf.navs.get(nav),
1742 .uav => |uav| elf.uavs.get(uav),
1743 .lazy_code => |ty| elf.lazy.getPtr(.code).map.get(ty),
1744 .lazy_const_data => |ty| elf.lazy.getPtr(.const_data).map.get(ty),
1745 }.?;
1746 switch (elf.shdrSlice()) {
1747 inline else => |shdr, class| {
1748 const sym = @field(elf.symPtr(si), @tagName(class));
1749 const sh = &shdr[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
1750 const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
1751 @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
1752 @bitCast(sh.flags),
1753 target_endian,
1754 ));
1755 const sh_addr = if (flags.shf.TLS)
1756 0
1757 else
1758 std.mem.toNative(@TypeOf(sh.addr), sh.addr, target_endian);
1759 const sh_offset = std.mem.toNative(@TypeOf(sh.offset), sh.offset, target_endian);
1760 sym.value = std.mem.nativeTo(
1761 @TypeOf(sym.value),
1762 @intCast(file_offset - sh_offset + sh_addr),
1763 target_endian,
1764 );
1765 if (si == elf.entry_hack) @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value;
1766 },
1767 }
1768 si.applyLocationRelocs(elf);
1769 si.applyTargetRelocs(elf);
1770 },
1771 }
1772 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1773}
1774
1775fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
1776 const target_endian = elf.endian();
1777 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
1778 const node = elf.getNode(ni);
1779 switch (node) {
1780 else => |tag| @panic(@tagName(tag)),
1781 .file, .shdr => {},
1782 .segment => |phndx| switch (elf.phdrSlice()) {
1783 inline else => |phdr| {
1784 const ph = &phdr[phndx];
1785 ph.filesz = std.mem.nativeTo(@TypeOf(ph.filesz), @intCast(size), target_endian);
1786 ph.memsz = ph.filesz;
1787 switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
1788 else => unreachable,
1789 std.elf.PT_NULL => {
1790 if (size > 0) ph.type = std.mem.nativeTo(
1791 @TypeOf(ph.type),
1792 std.elf.PT_LOAD,
1793 target_endian,
1794 );
1795 },
1796 std.elf.PT_LOAD => {
1797 if (size == 0) ph.type = std.mem.nativeTo(
1798 @TypeOf(ph.type),
1799 std.elf.PT_NULL,
1800 target_endian,
1801 );
1802 },
1803 std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => {},
1804 std.elf.PT_TLS => try ni.childrenMoved(elf.base.comp.gpa, &elf.mf),
1805 }
1806 },
1807 },
1808 .section => |si| switch (elf.shdrSlice()) {
1809 inline else => |shdr, class| {
1810 const sym = @field(elf.symPtr(si), @tagName(class));
1811 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1812 const sh = &shdr[shndx];
1813 switch (std.mem.toNative(@TypeOf(sh.type), sh.type, target_endian)) {
1814 else => unreachable,
1815 std.elf.SHT_NULL => {
1816 if (size > 0) sh.type = std.mem.nativeTo(
1817 @TypeOf(sh.type),
1818 std.elf.SHT_PROGBITS,
1819 target_endian,
1820 );
1821 },
1822 std.elf.SHT_PROGBITS => {
1823 if (size == 0) sh.type = std.mem.nativeTo(
1824 @TypeOf(sh.type),
1825 std.elf.SHT_NULL,
1826 target_endian,
1827 );
1828 },
1829 std.elf.SHT_SYMTAB => sh.info = std.mem.nativeTo(
1830 @TypeOf(sh.info),
1831 @intCast(@divExact(
1832 size,
1833 std.mem.toNative(@TypeOf(sh.entsize), sh.entsize, target_endian),
1834 )),
1835 target_endian,
1836 ),
1837 std.elf.SHT_STRTAB => {},
1838 }
1839 sh.size = std.mem.nativeTo(@TypeOf(sh.size), @intCast(size), target_endian);
1840 },
1841 },
1842 .nav, .uav, .lazy_code, .lazy_const_data => {},
1843 }
1844}
1845
1846pub fn updateExports(
1847 elf: *Elf,
1848 pt: Zcu.PerThread,
1849 exported: Zcu.Exported,
1850 export_indices: []const Zcu.Export.Index,
1851) !void {
1852 return elf.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
1853 error.OutOfMemory => error.OutOfMemory,
1854 error.LinkFailure => error.AnalysisFail,
1855 else => |e| switch (elf.base.comp.link_diags.fail(
1856 "linker failed to update exports: {t}",
1857 .{e},
1858 )) {
1859 error.LinkFailure => return error.AnalysisFail,
1860 },
1861 };
1862}
1863fn updateExportsInner(
1864 elf: *Elf,
1865 pt: Zcu.PerThread,
1866 exported: Zcu.Exported,
1867 export_indices: []const Zcu.Export.Index,
1868) !void {
1869 const zcu = pt.zcu;
1870 const gpa = zcu.gpa;
1871 const ip = &zcu.intern_pool;
1872
1873 switch (exported) {
1874 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
1875 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
1876 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
1877 Value.fromInterned(uav).fmtValue(pt),
1878 }),
1879 }
1880 try elf.symtab.ensureUnusedCapacity(gpa, export_indices.len);
1881 const exported_si: Symbol.Index, const @"type": std.elf.STT = switch (exported) {
1882 .nav => |nav| .{
1883 try elf.navSymbol(zcu, nav),
1884 navType(ip, ip.getNav(nav).status, elf.base.comp.config.any_non_single_threaded),
1885 },
1886 .uav => |uav| .{ @enumFromInt(switch (try elf.lowerUav(
1887 pt,
1888 uav,
1889 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
1890 export_indices[0].ptr(zcu).src,
1891 )) {
1892 .sym_index => |si| si,
1893 .fail => |em| {
1894 defer em.destroy(gpa);
1895 return elf.base.comp.link_diags.fail("{s}", .{em.msg});
1896 },
1897 }), .OBJECT },
1898 };
1899 while (try elf.idle(pt.tid)) {}
1900 const exported_ni = exported_si.node(elf);
1901 const value, const size, const shndx = switch (elf.symPtr(exported_si)) {
1902 inline else => |exported_sym| .{ exported_sym.value, exported_sym.size, exported_sym.shndx },
1903 };
1904 for (export_indices) |export_index| {
1905 const @"export" = export_index.ptr(zcu);
1906 const name = @"export".opts.name.toSlice(ip);
1907 const export_si = try elf.globalSymbol(.{
1908 .name = name,
1909 .type = @"type",
1910 .bind = switch (@"export".opts.linkage) {
1911 .internal => .LOCAL,
1912 .strong => .GLOBAL,
1913 .weak => .WEAK,
1914 .link_once => return error.LinkOnceUnsupported,
1915 },
1916 .visibility = switch (@"export".opts.visibility) {
1917 .default => .DEFAULT,
1918 .hidden => .HIDDEN,
1919 .protected => .PROTECTED,
1920 },
1921 });
1922 export_si.get(elf).ni = exported_ni;
1923 switch (elf.symPtr(export_si)) {
1924 inline else => |export_sym| {
1925 export_sym.value = @intCast(value);
1926 export_sym.size = @intCast(size);
1927 export_sym.shndx = shndx;
1928 },
1929 }
1930 export_si.applyTargetRelocs(elf);
1931 if (std.mem.eql(u8, name, "_start")) {
1932 elf.entry_hack = exported_si;
1933 switch (elf.ehdrPtr()) {
1934 inline else => |ehdr| ehdr.entry = @intCast(value),
1935 }
1936 }
1937 }
1938}
1939
1940pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
1941 _ = elf;
1942 _ = exported;
1943 _ = name;
1944}
1945
1946pub fn dump(elf: *Elf, tid: Zcu.PerThread.Id) void {
1947 const w = std.debug.lockStderrWriter(&.{});
1948 defer std.debug.unlockStderrWriter();
1949 elf.printNode(tid, w, .root, 0) catch {};
1950}
1951
1952pub fn printNode(
1953 elf: *Elf,
1954 tid: Zcu.PerThread.Id,
1955 w: *std.Io.Writer,
1956 ni: MappedFile.Node.Index,
1957 indent: usize,
1958) !void {
1959 const node = elf.getNode(ni);
1960 const mf_node = &elf.mf.nodes.items[@intFromEnum(ni)];
1961 const off, const size = mf_node.location().resolve(&elf.mf);
1962 try w.splatByteAll(' ', indent);
1963 try w.writeAll(@tagName(node));
1964 switch (node) {
1965 else => {},
1966 .section => |si| try w.print("({s})", .{elf.sectionName(si)}),
1967 .nav => |nav_index| {
1968 const zcu = elf.base.comp.zcu.?;
1969 const ip = &zcu.intern_pool;
1970 const nav = ip.getNav(nav_index);
1971 try w.print("({f}, {f})", .{
1972 Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
1973 nav.fqn.fmt(ip),
1974 });
1975 },
1976 .uav => |uav| {
1977 const zcu = elf.base.comp.zcu.?;
1978 const val: Value = .fromInterned(uav);
1979 try w.print("({f}, {f})", .{
1980 val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
1981 val.fmtValue(.{ .zcu = zcu, .tid = tid }),
1982 });
1983 },
1984 }
1985 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{
1986 @intFromEnum(ni),
1987 off,
1988 size,
1989 mf_node.flags.alignment.toByteUnits(),
1990 if (mf_node.flags.fixed) " fixed" else "",
1991 if (mf_node.flags.moved) " moved" else "",
1992 if (mf_node.flags.resized) " resized" else "",
1993 if (mf_node.flags.has_content) " has_content" else "",
1994 });
1995 var child_ni = mf_node.first;
1996 switch (child_ni) {
1997 .none => {
1998 const file_loc = ni.fileLocation(&elf.mf, false);
1999 if (file_loc.size == 0) return;
2000 var address = file_loc.offset;
2001 const line_len = 0x10;
2002 var line_it = std.mem.window(
2003 u8,
2004 elf.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
2005 line_len,
2006 line_len,
2007 );
2008 while (line_it.next()) |line_bytes| : (address += line_len) {
2009 try w.splatByteAll(' ', indent + 1);
2010 try w.print("{x:0>8}", .{address});
2011 for (line_bytes) |byte| try w.print(" {x:0>2}", .{byte});
2012 try w.writeByte('\n');
2013 }
2014 },
2015 else => while (child_ni != .none) {
2016 try elf.printNode(tid, w, child_ni, indent + 1);
2017 child_ni = elf.mf.nodes.items[@intFromEnum(child_ni)].next;
2018 },
2019 }
2020}
2021
2022const assert = std.debug.assert;
2023const builtin = @import("builtin");
2024const codegen = @import("../codegen.zig");
2025const Compilation = @import("../Compilation.zig");
2026const Elf = @This();
2027const InternPool = @import("../InternPool.zig");
2028const link = @import("../link.zig");
2029const log = std.log.scoped(.link);
2030const MappedFile = @import("MappedFile.zig");
2031const native_endian = builtin.cpu.arch.endian();
2032const std = @import("std");
2033const target_util = @import("../target.zig");
2034const Type = @import("../Type.zig");
2035const Value = @import("../Value.zig");
2036const Zcu = @import("../Zcu.zig");
src/link/MachO/ZigObject.zig+32-22
......@@ -784,22 +784,26 @@ pub fn updateFunc(
784784 const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);
785785 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
786786
787 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
788 defer code_buffer.deinit(gpa);
787 var aw: std.Io.Writer.Allocating = .init(gpa);
788 defer aw.deinit();
789789
790790 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
791791 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
792792
793 try codegen.emitFunction(
793 codegen.emitFunction(
794794 &macho_file.base,
795795 pt,
796796 zcu.navSrcLoc(func.owner_nav),
797797 func_index,
798 sym_index,
798799 mir,
799 &code_buffer,
800 &aw.writer,
800801 if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
801 );
802 const code = code_buffer.items;
802 ) catch |err| switch (err) {
803 error.WriteFailed => return error.OutOfMemory,
804 else => |e| return e,
805 };
806 const code = aw.written();
803807
804808 const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
805809 const old_rva, const old_alignment = blk: {
......@@ -895,21 +899,24 @@ pub fn updateNav(
895899 const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);
896900 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
897901
898 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
899 defer code_buffer.deinit(zcu.gpa);
902 var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
903 defer aw.deinit();
900904
901905 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
902906 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
903907
904 try codegen.generateSymbol(
908 codegen.generateSymbol(
905909 &macho_file.base,
906910 pt,
907911 zcu.navSrcLoc(nav_index),
908912 Value.fromInterned(nav_init),
909 &code_buffer,
913 &aw.writer,
910914 .{ .atom_index = sym_index },
911 );
912 const code = code_buffer.items;
915 ) catch |err| switch (err) {
916 error.WriteFailed => return error.OutOfMemory,
917 else => |e| return e,
918 };
919 const code = aw.written();
913920
914921 const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);
915922 if (isThreadlocal(macho_file, nav_index))
......@@ -1198,21 +1205,24 @@ fn lowerConst(
11981205) !codegen.SymbolResult {
11991206 const gpa = macho_file.base.comp.gpa;
12001207
1201 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1202 defer code_buffer.deinit(gpa);
1208 var aw: std.Io.Writer.Allocating = .init(gpa);
1209 defer aw.deinit();
12031210
12041211 const name_str = try self.addString(gpa, name);
12051212 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
12061213
1207 try codegen.generateSymbol(
1214 codegen.generateSymbol(
12081215 &macho_file.base,
12091216 pt,
12101217 src_loc,
12111218 val,
1212 &code_buffer,
1219 &aw.writer,
12131220 .{ .atom_index = sym_index },
1214 );
1215 const code = code_buffer.items;
1221 ) catch |err| switch (err) {
1222 error.WriteFailed => return error.OutOfMemory,
1223 else => |e| return e,
1224 };
1225 const code = aw.written();
12161226
12171227 const sym = &self.symbols.items[sym_index];
12181228 sym.out_n_sect = output_section_index;
......@@ -1349,8 +1359,8 @@ fn updateLazySymbol(
13491359 const gpa = zcu.gpa;
13501360
13511361 var required_alignment: Atom.Alignment = .none;
1352 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1353 defer code_buffer.deinit(gpa);
1362 var aw: std.Io.Writer.Allocating = .init(gpa);
1363 defer aw.deinit();
13541364
13551365 const name_str = blk: {
13561366 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
......@@ -1368,11 +1378,11 @@ fn updateLazySymbol(
13681378 src,
13691379 lazy_sym,
13701380 &required_alignment,
1371 &code_buffer,
1381 &aw.writer,
13721382 .none,
13731383 .{ .atom_index = symbol_index },
13741384 );
1375 const code = code_buffer.items;
1385 const code = aw.written();
13761386
13771387 const output_section_index = switch (lazy_sym.kind) {
13781388 .code => macho_file.zig_text_sect_index.?,
src/link/MappedFile.zig created+929
......@@ -0,0 +1,929 @@
1file: std.fs.File,
2flags: packed struct {
3 block_size: std.mem.Alignment,
4 copy_file_range_unsupported: bool,
5 fallocate_punch_hole_unsupported: bool,
6 fallocate_insert_range_unsupported: bool,
7},
8section: if (is_windows) windows.HANDLE else void,
9contents: []align(std.heap.page_size_min) u8,
10nodes: std.ArrayList(Node),
11free_ni: Node.Index,
12large: std.ArrayList(u64),
13updates: std.ArrayList(Node.Index),
14update_prog_node: std.Progress.Node,
15writers: std.SinglyLinkedList,
16
17pub const Error = std.posix.MMapError ||
18 std.posix.MRemapError ||
19 std.fs.File.SetEndPosError ||
20 std.fs.File.CopyRangeError ||
21 error{NotFile};
22
23pub fn init(file: std.fs.File, gpa: std.mem.Allocator) !MappedFile {
24 var mf: MappedFile = .{
25 .file = file,
26 .flags = undefined,
27 .section = if (is_windows) windows.INVALID_HANDLE_VALUE else {},
28 .contents = &.{},
29 .nodes = .empty,
30 .free_ni = .none,
31 .large = .empty,
32 .updates = .empty,
33 .update_prog_node = .none,
34 .writers = .{},
35 };
36 errdefer mf.deinit(gpa);
37 const size: u64, const blksize = if (is_windows)
38 .{ try windows.GetFileSizeEx(file.handle), 1 }
39 else stat: {
40 const stat = try std.posix.fstat(mf.file.handle);
41 if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists;
42 break :stat .{ @bitCast(stat.size), stat.blksize };
43 };
44 mf.flags = .{
45 .block_size = .fromByteUnits(
46 std.math.ceilPowerOfTwoAssert(usize, @max(std.heap.pageSize(), blksize)),
47 ),
48 .copy_file_range_unsupported = false,
49 .fallocate_insert_range_unsupported = false,
50 .fallocate_punch_hole_unsupported = false,
51 };
52 try mf.nodes.ensureUnusedCapacity(gpa, 1);
53 assert(try mf.addNode(gpa, .{
54 .add_node = .{
55 .size = size,
56 .fixed = true,
57 },
58 }) == Node.Index.root);
59 try mf.ensureTotalCapacity(@intCast(size));
60 return mf;
61}
62
63pub fn deinit(mf: *MappedFile, gpa: std.mem.Allocator) void {
64 mf.unmap();
65 mf.nodes.deinit(gpa);
66 mf.large.deinit(gpa);
67 mf.updates.deinit(gpa);
68 mf.update_prog_node.end();
69 assert(mf.writers.first == null);
70 mf.* = undefined;
71}
72
73pub const Node = extern struct {
74 parent: Node.Index,
75 prev: Node.Index,
76 next: Node.Index,
77 first: Node.Index,
78 last: Node.Index,
79 flags: Flags,
80 location_payload: Location.Payload,
81
82 pub const Flags = packed struct(u32) {
83 location_tag: Location.Tag,
84 alignment: std.mem.Alignment,
85 /// Whether this node can be moved.
86 fixed: bool,
87 /// Whether this node has been moved.
88 moved: bool,
89 /// Whether this node has been resized.
90 resized: bool,
91 /// Whether this node might contain non-zero bytes.
92 has_content: bool,
93 unused: @Type(.{ .int = .{
94 .signedness = .unsigned,
95 .bits = 32 - @bitSizeOf(std.mem.Alignment) - 5,
96 } }) = 0,
97 };
98
99 pub const Location = union(enum(u1)) {
100 small: extern struct {
101 /// Relative to `parent`.
102 offset: u32,
103 size: u32,
104 },
105 large: extern struct {
106 index: usize,
107 unused: @Type(.{ .int = .{
108 .signedness = .unsigned,
109 .bits = 64 - @bitSizeOf(usize),
110 } }) = 0,
111 },
112
113 pub const Tag = @typeInfo(Location).@"union".tag_type.?;
114 pub const Payload = @Type(.{ .@"union" = .{
115 .layout = .@"extern",
116 .tag_type = null,
117 .fields = @typeInfo(Location).@"union".fields,
118 .decls = &.{},
119 } });
120
121 pub fn resolve(loc: Location, mf: *const MappedFile) [2]u64 {
122 return switch (loc) {
123 .small => |small| .{ small.offset, small.size },
124 .large => |large| mf.large.items[large.index..][0..2].*,
125 };
126 }
127 };
128
129 pub const Index = enum(u32) {
130 none,
131 _,
132
133 pub const root: Node.Index = .none;
134
135 fn get(ni: Node.Index, mf: *const MappedFile) *Node {
136 return &mf.nodes.items[@intFromEnum(ni)];
137 }
138
139 pub fn childrenMoved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void {
140 var child_ni = ni.get(mf).last;
141 while (child_ni != .none) {
142 try child_ni.moved(gpa, mf);
143 child_ni = child_ni.get(mf).prev;
144 }
145 }
146
147 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
148 var parent_ni = ni;
149 while (parent_ni != .none) {
150 const parent = parent_ni.get(mf);
151 if (parent.flags.moved) return true;
152 parent_ni = parent.parent;
153 }
154 return false;
155 }
156 pub fn moved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void {
157 try mf.updates.ensureUnusedCapacity(gpa, 1);
158 ni.movedAssumeCapacity(mf);
159 }
160 pub fn cleanMoved(ni: Node.Index, mf: *const MappedFile) bool {
161 const node_moved = &ni.get(mf).flags.moved;
162 defer node_moved.* = false;
163 return node_moved.*;
164 }
165 fn movedAssumeCapacity(ni: Node.Index, mf: *MappedFile) void {
166 var parent_ni = ni;
167 while (parent_ni != .none) {
168 const parent_node = parent_ni.get(mf);
169 if (parent_node.flags.moved) return;
170 parent_ni = parent_node.parent;
171 }
172 const node = ni.get(mf);
173 node.flags.moved = true;
174 if (node.flags.resized) return;
175 mf.updates.appendAssumeCapacity(ni);
176 mf.update_prog_node.increaseEstimatedTotalItems(1);
177 }
178
179 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {
180 return ni.get(mf).flags.resized;
181 }
182 pub fn resized(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void {
183 try mf.updates.ensureUnusedCapacity(gpa, 1);
184 ni.resizedAssumeCapacity(mf);
185 }
186 pub fn cleanResized(ni: Node.Index, mf: *const MappedFile) bool {
187 const node_resized = &ni.get(mf).flags.resized;
188 defer node_resized.* = false;
189 return node_resized.*;
190 }
191 fn resizedAssumeCapacity(ni: Node.Index, mf: *MappedFile) void {
192 const node = ni.get(mf);
193 if (node.flags.resized) return;
194 node.flags.resized = true;
195 if (node.flags.moved) return;
196 mf.updates.appendAssumeCapacity(ni);
197 mf.update_prog_node.increaseEstimatedTotalItems(1);
198 }
199
200 pub fn alignment(ni: Node.Index, mf: *const MappedFile) std.mem.Alignment {
201 return ni.get(mf).flags.alignment;
202 }
203
204 fn setLocationAssumeCapacity(ni: Node.Index, mf: *MappedFile, offset: u64, size: u64) void {
205 const node = ni.get(mf);
206 if (size == 0) node.flags.has_content = false;
207 switch (node.location()) {
208 .small => |small| {
209 if (small.offset != offset) ni.movedAssumeCapacity(mf);
210 if (small.size != size) ni.resizedAssumeCapacity(mf);
211 if (std.math.cast(u32, offset)) |small_offset| {
212 if (std.math.cast(u32, size)) |small_size| {
213 node.location_payload.small = .{
214 .offset = small_offset,
215 .size = small_size,
216 };
217 return;
218 }
219 }
220 defer mf.large.appendSliceAssumeCapacity(&.{ offset, size });
221 node.flags.location_tag = .large;
222 node.location_payload = .{ .large = .{ .index = mf.large.items.len } };
223 },
224 .large => |large| {
225 const large_items = mf.large.items[large.index..][0..2];
226 if (large_items[0] != offset) ni.movedAssumeCapacity(mf);
227 if (large_items[1] != size) ni.resizedAssumeCapacity(mf);
228 large_items.* = .{ offset, size };
229 },
230 }
231 }
232
233 pub fn location(ni: Node.Index, mf: *const MappedFile) Location {
234 return ni.get(mf).location();
235 }
236
237 pub fn fileLocation(
238 ni: Node.Index,
239 mf: *const MappedFile,
240 set_has_content: bool,
241 ) struct { offset: u64, size: u64 } {
242 var offset, const size = ni.location(mf).resolve(mf);
243 var parent_ni = ni;
244 while (true) {
245 const parent = parent_ni.get(mf);
246 if (set_has_content) parent.flags.has_content = true;
247 if (parent_ni == .none) break;
248 parent_ni = parent.parent;
249 offset += parent_ni.location(mf).resolve(mf)[0];
250 }
251 return .{ .offset = offset, .size = size };
252 }
253
254 pub fn slice(ni: Node.Index, mf: *const MappedFile) []u8 {
255 const file_loc = ni.fileLocation(mf, true);
256 return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
257 }
258
259 pub fn sliceConst(ni: Node.Index, mf: *const MappedFile) []const u8 {
260 const file_loc = ni.fileLocation(mf, false);
261 return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
262 }
263
264 pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, size: u64) !void {
265 try mf.resizeNode(gpa, ni, size);
266 var writers_it = mf.writers.first;
267 while (writers_it) |writer_node| : (writers_it = writer_node.next) {
268 const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node);
269 w.interface.buffer = w.ni.slice(mf);
270 }
271 }
272
273 pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, w: *Writer) void {
274 w.* = .{
275 .gpa = gpa,
276 .mf = mf,
277 .writer_node = .{},
278 .ni = ni,
279 .interface = .{
280 .buffer = ni.slice(mf),
281 .vtable = &Writer.vtable,
282 },
283 .err = null,
284 };
285 mf.writers.prepend(&w.writer_node);
286 }
287 };
288
289 pub fn location(node: *const Node) Location {
290 return switch (node.flags.location_tag) {
291 inline else => |tag| @unionInit(
292 Location,
293 @tagName(tag),
294 @field(node.location_payload, @tagName(tag)),
295 ),
296 };
297 }
298
299 pub const Writer = struct {
300 gpa: std.mem.Allocator,
301 mf: *MappedFile,
302 writer_node: std.SinglyLinkedList.Node,
303 ni: Node.Index,
304 interface: std.Io.Writer,
305 err: ?Error,
306
307 pub fn deinit(w: *Writer) void {
308 assert(w.mf.writers.popFirst() == &w.writer_node);
309 w.* = undefined;
310 }
311
312 const vtable: std.Io.Writer.VTable = .{
313 .drain = drain,
314 .sendFile = sendFile,
315 .flush = std.Io.Writer.noopFlush,
316 .rebase = growingRebase,
317 };
318
319 fn drain(
320 interface: *std.Io.Writer,
321 data: []const []const u8,
322 splat: usize,
323 ) std.Io.Writer.Error!usize {
324 const pattern = data[data.len - 1];
325 const splat_len = pattern.len * splat;
326 const start_len = interface.end;
327 assert(data.len != 0);
328 for (data) |bytes| {
329 try growingRebase(interface, interface.end, bytes.len + splat_len + 1);
330 @memcpy(interface.buffer[interface.end..][0..bytes.len], bytes);
331 interface.end += bytes.len;
332 }
333 if (splat == 0) {
334 interface.end -= pattern.len;
335 } else switch (pattern.len) {
336 0 => {},
337 1 => {
338 @memset(interface.buffer[interface.end..][0 .. splat - 1], pattern[0]);
339 interface.end += splat - 1;
340 },
341 else => for (0..splat - 1) |_| {
342 @memcpy(interface.buffer[interface.end..][0..pattern.len], pattern);
343 interface.end += pattern.len;
344 },
345 }
346 return interface.end - start_len;
347 }
348
349 fn sendFile(
350 interface: *std.Io.Writer,
351 file_reader: *std.fs.File.Reader,
352 limit: std.Io.Limit,
353 ) std.Io.Writer.FileError!usize {
354 if (limit == .nothing) return 0;
355 const pos = file_reader.logicalPos();
356 const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
357 if (additional == 0) return error.EndOfStream;
358 try growingRebase(interface, interface.end, limit.minInt64(additional));
359 switch (file_reader.mode) {
360 .positional => {
361 const fr_buf = file_reader.interface.buffered();
362 const buf_copy_size = interface.write(fr_buf) catch unreachable;
363 file_reader.interface.toss(buf_copy_size);
364 if (buf_copy_size < fr_buf.len) return buf_copy_size;
365 assert(file_reader.logicalPos() == file_reader.pos);
366
367 const w: *Writer = @fieldParentPtr("interface", interface);
368 const copy_size: usize = @intCast(w.mf.copyFileRange(
369 file_reader.file,
370 file_reader.pos,
371 w.ni.fileLocation(w.mf, true).offset + interface.end,
372 limit.minInt(interface.unusedCapacityLen()),
373 ) catch |err| {
374 w.err = err;
375 return error.WriteFailed;
376 });
377 interface.end += copy_size;
378 return copy_size;
379 },
380 .streaming,
381 .streaming_reading,
382 .positional_reading,
383 .failure,
384 => {
385 const dest = limit.slice(interface.unusedCapacitySlice());
386 const n = try file_reader.read(dest);
387 interface.end += n;
388 return n;
389 },
390 }
391 }
392
393 fn growingRebase(
394 interface: *std.Io.Writer,
395 preserve: usize,
396 unused_capacity: usize,
397 ) std.Io.Writer.Error!void {
398 _ = preserve;
399 const total_capacity = interface.end + unused_capacity;
400 if (interface.buffer.len >= total_capacity) return;
401 const w: *Writer = @fieldParentPtr("interface", interface);
402 w.ni.resize(w.mf, w.gpa, total_capacity +| total_capacity / 2) catch |err| {
403 w.err = err;
404 return error.WriteFailed;
405 };
406 }
407 };
408
409 comptime {
410 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 32);
411 }
412};
413
414fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
415 parent: Node.Index = .none,
416 prev: Node.Index = .none,
417 next: Node.Index = .none,
418 offset: u64 = 0,
419 add_node: AddNodeOptions,
420}) !Node.Index {
421 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
422 const offset = opts.add_node.alignment.forward(@intCast(opts.offset));
423 const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {
424 if (std.math.cast(u32, offset)) |small_offset| break :location .{ .small, .{
425 .small = .{ .offset = small_offset, .size = 0 },
426 } };
427 try mf.large.ensureUnusedCapacity(gpa, 2);
428 defer mf.large.appendSliceAssumeCapacity(&.{ offset, 0 });
429 break :location .{ .large, .{ .large = .{ .index = mf.large.items.len } } };
430 };
431 const free_ni: Node.Index, const free_node = free: switch (mf.free_ni) {
432 .none => .{ @enumFromInt(mf.nodes.items.len), mf.nodes.addOneAssumeCapacity() },
433 else => |free_ni| {
434 const free_node = free_ni.get(mf);
435 mf.free_ni = free_node.next;
436 break :free .{ free_ni, free_node };
437 },
438 };
439 free_node.* = .{
440 .parent = opts.parent,
441 .prev = opts.prev,
442 .next = opts.next,
443 .first = .none,
444 .last = .none,
445 .flags = .{
446 .location_tag = location_tag,
447 .alignment = opts.add_node.alignment,
448 .fixed = opts.add_node.fixed,
449 .moved = true,
450 .resized = true,
451 .has_content = false,
452 },
453 .location_payload = location_payload,
454 };
455 {
456 defer {
457 free_node.flags.moved = false;
458 free_node.flags.resized = false;
459 }
460 if (offset > opts.parent.location(mf).resolve(mf)[1]) try opts.parent.resize(mf, gpa, offset);
461 try free_ni.resize(mf, gpa, opts.add_node.size);
462 }
463 if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf);
464 if (opts.add_node.resized) free_ni.resizedAssumeCapacity(mf);
465 return free_ni;
466}
467
468pub const AddNodeOptions = struct {
469 size: u64 = 0,
470 alignment: std.mem.Alignment = .@"1",
471 fixed: bool = false,
472 moved: bool = false,
473 resized: bool = false,
474};
475
476pub fn addOnlyChildNode(
477 mf: *MappedFile,
478 gpa: std.mem.Allocator,
479 parent_ni: Node.Index,
480 opts: AddNodeOptions,
481) !Node.Index {
482 try mf.nodes.ensureUnusedCapacity(gpa, 1);
483 const parent = parent_ni.get(mf);
484 assert(parent.first == .none and parent.last == .none);
485 const ni = try mf.addNode(gpa, .{
486 .parent = parent_ni,
487 .add_node = opts,
488 });
489 parent.first = ni;
490 parent.last = ni;
491 return ni;
492}
493
494pub fn addLastChildNode(
495 mf: *MappedFile,
496 gpa: std.mem.Allocator,
497 parent_ni: Node.Index,
498 opts: AddNodeOptions,
499) !Node.Index {
500 try mf.nodes.ensureUnusedCapacity(gpa, 1);
501 const parent = parent_ni.get(mf);
502 const ni = try mf.addNode(gpa, .{
503 .parent = parent_ni,
504 .prev = parent.last,
505 .offset = offset: switch (parent.last) {
506 .none => 0,
507 else => |last_ni| {
508 const last_offset, const last_size = last_ni.location(mf).resolve(mf);
509 break :offset last_offset + last_size;
510 },
511 },
512 .add_node = opts,
513 });
514 switch (parent.last) {
515 .none => parent.first = ni,
516 else => |last_ni| last_ni.get(mf).next = ni,
517 }
518 parent.last = ni;
519 return ni;
520}
521
522pub fn addNodeAfter(
523 mf: *MappedFile,
524 gpa: std.mem.Allocator,
525 prev_ni: Node.Index,
526 opts: AddNodeOptions,
527) !Node.Index {
528 assert(prev_ni != .none);
529 try mf.nodes.ensureUnusedCapacity(gpa, 1);
530 const prev = prev_ni.get(mf);
531 const prev_offset, const prev_size = prev.location().resolve(mf);
532 const ni = try mf.addNode(gpa, .{
533 .parent = prev.parent,
534 .prev = prev_ni,
535 .next = prev.next,
536 .offset = prev_offset + prev_size,
537 .add_node = opts,
538 });
539 switch (prev.next) {
540 .none => prev.parent.get(mf).last = ni,
541 else => |next_ni| next_ni.get(mf).prev = ni,
542 }
543 prev.next = ni;
544 return ni;
545}
546
547fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) !void {
548 const node = ni.get(mf);
549 var old_offset, const old_size = node.location().resolve(mf);
550 const new_size = node.flags.alignment.forward(@intCast(requested_size));
551 // Resize the entire file
552 if (ni == Node.Index.root) {
553 try mf.file.setEndPos(new_size);
554 try mf.ensureTotalCapacity(@intCast(new_size));
555 try mf.ensureCapacityForSetLocation(gpa);
556 ni.setLocationAssumeCapacity(mf, old_offset, new_size);
557 return;
558 }
559 while (true) {
560 const parent = node.parent.get(mf);
561 _, const old_parent_size = parent.location().resolve(mf);
562 const trailing_end = switch (node.next) {
563 .none => parent.location().resolve(mf)[1],
564 else => |next_ni| next_ni.location(mf).resolve(mf)[0],
565 };
566 assert(old_offset + old_size <= trailing_end);
567 // Expand the node into available trailing free space
568 if (old_offset + new_size <= trailing_end) {
569 try mf.ensureCapacityForSetLocation(gpa);
570 ni.setLocationAssumeCapacity(mf, old_offset, new_size);
571 return;
572 }
573 // Ask the filesystem driver to insert an extent into the file without copying any data
574 if (is_linux and !mf.flags.fallocate_insert_range_unsupported and
575 node.flags.alignment.order(mf.flags.block_size).compare(.gte))
576 insert_range: {
577 const last_offset, const last_size = parent.last.location(mf).resolve(mf);
578 const last_end = last_offset + last_size;
579 assert(last_end <= old_parent_size);
580 const range_size =
581 node.flags.alignment.forward(@intCast(requested_size +| requested_size / 2)) - old_size;
582 const new_parent_size = last_end + range_size;
583 if (new_parent_size > old_parent_size) {
584 try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / 2);
585 continue;
586 }
587 const range_file_offset = ni.fileLocation(mf, false).offset + old_size;
588 retry: while (true) {
589 switch (linux.E.init(linux.fallocate(
590 mf.file.handle,
591 linux.FALLOC.FL_INSERT_RANGE,
592 @intCast(range_file_offset),
593 @intCast(range_size),
594 ))) {
595 .SUCCESS => {
596 var enclosing_ni = ni;
597 while (enclosing_ni != .none) {
598 try mf.ensureCapacityForSetLocation(gpa);
599 const enclosing = enclosing_ni.get(mf);
600 const enclosing_offset, const enclosing_size =
601 enclosing.location().resolve(mf);
602 enclosing_ni.setLocationAssumeCapacity(
603 mf,
604 enclosing_offset,
605 enclosing_size + range_size,
606 );
607 var after_ni = enclosing.next;
608 while (after_ni != .none) {
609 try mf.ensureCapacityForSetLocation(gpa);
610 const after = after_ni.get(mf);
611 const after_offset, const after_size = after.location().resolve(mf);
612 after_ni.setLocationAssumeCapacity(
613 mf,
614 range_size + after_offset,
615 after_size,
616 );
617 after_ni = after.next;
618 }
619 enclosing_ni = enclosing.parent;
620 }
621 return;
622 },
623 .INTR => continue :retry,
624 .BADF, .FBIG, .INVAL => unreachable,
625 .IO => return error.InputOutput,
626 .NODEV => return error.NotFile,
627 .NOSPC => return error.NoSpaceLeft,
628 .NOSYS, .OPNOTSUPP => {
629 mf.flags.fallocate_insert_range_unsupported = true;
630 break :insert_range;
631 },
632 .PERM => return error.PermissionDenied,
633 .SPIPE => return error.Unseekable,
634 .TXTBSY => return error.FileBusy,
635 else => |e| return std.posix.unexpectedErrno(e),
636 }
637 }
638 }
639 switch (node.next) {
640 .none => {
641 // As this is the last node, we simply need more space in the parent
642 const new_parent_size = old_offset + new_size;
643 try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / 2);
644 },
645 else => |*next_ni_ptr| switch (node.flags.fixed) {
646 false => {
647 // Make space at the end of the parent for this floating node
648 const last = parent.last.get(mf);
649 const last_offset, const last_size = last.location().resolve(mf);
650 const new_offset = node.flags.alignment.forward(@intCast(last_offset + last_size));
651 const new_parent_size = new_offset + new_size;
652 if (new_parent_size > old_parent_size) {
653 try mf.resizeNode(
654 gpa,
655 node.parent,
656 new_parent_size +| new_parent_size / 2,
657 );
658 continue;
659 }
660 const next_ni = next_ni_ptr.*;
661 next_ni.get(mf).prev = node.prev;
662 switch (node.prev) {
663 .none => parent.first = next_ni,
664 else => |prev_ni| prev_ni.get(mf).next = next_ni,
665 }
666 last.next = ni;
667 node.prev = parent.last;
668 next_ni_ptr.* = .none;
669 parent.last = ni;
670 if (node.flags.has_content) {
671 const parent_file_offset = node.parent.fileLocation(mf, false).offset;
672 try mf.moveRange(
673 parent_file_offset + old_offset,
674 parent_file_offset + new_offset,
675 old_size,
676 );
677 }
678 old_offset = new_offset;
679 },
680 true => {
681 // Move the next floating node to make space for this fixed node
682 const next_ni = next_ni_ptr.*;
683 const next = next_ni.get(mf);
684 assert(!next.flags.fixed);
685 const next_offset, const next_size = next.location().resolve(mf);
686 const last = parent.last.get(mf);
687 const last_offset, const last_size = last.location().resolve(mf);
688 const new_offset = next.flags.alignment.forward(@intCast(
689 @max(old_offset + new_size, last_offset + last_size),
690 ));
691 const new_parent_size = new_offset + next_size;
692 if (new_parent_size > old_parent_size) {
693 try mf.resizeNode(
694 gpa,
695 node.parent,
696 new_parent_size +| new_parent_size / 2,
697 );
698 continue;
699 }
700 try mf.ensureCapacityForSetLocation(gpa);
701 next.prev = parent.last;
702 parent.last = next_ni;
703 last.next = next_ni;
704 next_ni_ptr.* = next.next;
705 switch (next.next) {
706 .none => {},
707 else => |next_next_ni| next_next_ni.get(mf).prev = ni,
708 }
709 next.next = .none;
710 if (node.flags.has_content) {
711 const parent_file_offset = node.parent.fileLocation(mf, false).offset;
712 try mf.moveRange(
713 parent_file_offset + next_offset,
714 parent_file_offset + new_offset,
715 next_size,
716 );
717 }
718 next_ni.setLocationAssumeCapacity(mf, new_offset, next_size);
719 },
720 },
721 }
722 }
723}
724
725fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
726 // make a copy of this node at the new location
727 try mf.copyRange(old_file_offset, new_file_offset, size);
728 // delete the copy of this node at the old location
729 if (is_linux and !mf.flags.fallocate_punch_hole_unsupported and
730 size >= mf.flags.block_size.toByteUnits() * 2 - 1) while (true)
731 {
732 switch (linux.E.init(linux.fallocate(
733 mf.file.handle,
734 linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,
735 @intCast(old_file_offset),
736 @intCast(size),
737 ))) {
738 .SUCCESS => return,
739 .INTR => continue,
740 .BADF, .FBIG, .INVAL => unreachable,
741 .IO => return error.InputOutput,
742 .NODEV => return error.NotFile,
743 .NOSPC => return error.NoSpaceLeft,
744 .NOSYS, .OPNOTSUPP => {
745 mf.flags.fallocate_punch_hole_unsupported = true;
746 break;
747 },
748 .PERM => return error.PermissionDenied,
749 .SPIPE => return error.Unseekable,
750 .TXTBSY => return error.FileBusy,
751 else => |e| return std.posix.unexpectedErrno(e),
752 }
753 };
754 @memset(mf.contents[@intCast(old_file_offset)..][0..@intCast(size)], 0);
755}
756
757fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
758 const copy_size = try mf.copyFileRange(mf.file, old_file_offset, new_file_offset, size);
759 if (copy_size < size) @memcpy(
760 mf.contents[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)],
761 mf.contents[@intCast(old_file_offset + copy_size)..][0..@intCast(size - copy_size)],
762 );
763}
764
765fn copyFileRange(
766 mf: *MappedFile,
767 old_file: std.fs.File,
768 old_file_offset: u64,
769 new_file_offset: u64,
770 size: u64,
771) !u64 {
772 var remaining_size = size;
773 if (is_linux and !mf.flags.copy_file_range_unsupported) {
774 var old_file_offset_mut: i64 = @intCast(old_file_offset);
775 var new_file_offset_mut: i64 = @intCast(new_file_offset);
776 while (remaining_size >= mf.flags.block_size.toByteUnits() * 2 - 1) {
777 const copy_len = linux.copy_file_range(
778 old_file.handle,
779 &old_file_offset_mut,
780 mf.file.handle,
781 &new_file_offset_mut,
782 @intCast(remaining_size),
783 0,
784 );
785 switch (linux.E.init(copy_len)) {
786 .SUCCESS => {
787 if (copy_len == 0) break;
788 remaining_size -= copy_len;
789 if (remaining_size == 0) break;
790 },
791 .INTR => continue,
792 .BADF, .FBIG, .INVAL, .OVERFLOW => unreachable,
793 .IO => return error.InputOutput,
794 .ISDIR => return error.IsDir,
795 .NOMEM => return error.SystemResources,
796 .NOSPC => return error.NoSpaceLeft,
797 .NOSYS, .OPNOTSUPP, .XDEV => {
798 mf.flags.copy_file_range_unsupported = true;
799 break;
800 },
801 .PERM => return error.PermissionDenied,
802 .TXTBSY => return error.FileBusy,
803 else => |e| return std.posix.unexpectedErrno(e),
804 }
805 }
806 }
807 return size - remaining_size;
808}
809
810fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) !void {
811 try mf.large.ensureUnusedCapacity(gpa, 2);
812 try mf.updates.ensureUnusedCapacity(gpa, 1);
813}
814
815pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) !void {
816 if (mf.contents.len >= new_capacity) return;
817 try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / 2);
818}
819
820pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void {
821 if (mf.contents.len >= new_capacity) return;
822 const aligned_capacity = mf.flags.block_size.forward(new_capacity);
823 if (!is_linux) mf.unmap() else if (mf.contents.len > 0) {
824 mf.contents = try std.posix.mremap(
825 mf.contents.ptr,
826 mf.contents.len,
827 aligned_capacity,
828 .{ .MAYMOVE = true },
829 null,
830 );
831 return;
832 }
833 if (is_windows) {
834 if (mf.section == windows.INVALID_HANDLE_VALUE) switch (windows.ntdll.NtCreateSection(
835 &mf.section,
836 windows.STANDARD_RIGHTS_REQUIRED | windows.SECTION_QUERY |
837 windows.SECTION_MAP_WRITE | windows.SECTION_MAP_READ | windows.SECTION_EXTEND_SIZE,
838 null,
839 @constCast(&@as(i64, @intCast(aligned_capacity))),
840 windows.PAGE_READWRITE,
841 windows.SEC_COMMIT,
842 mf.file.handle,
843 )) {
844 .SUCCESS => {},
845 else => return error.MemoryMappingNotSupported,
846 };
847 var contents_ptr: ?[*]align(std.heap.page_size_min) u8 = null;
848 var contents_len = aligned_capacity;
849 switch (windows.ntdll.NtMapViewOfSection(
850 mf.section,
851 windows.GetCurrentProcess(),
852 @ptrCast(&contents_ptr),
853 null,
854 0,
855 null,
856 &contents_len,
857 .ViewUnmap,
858 0,
859 windows.PAGE_READWRITE,
860 )) {
861 .SUCCESS => mf.contents = contents_ptr.?[0..contents_len],
862 else => return error.MemoryMappingNotSupported,
863 }
864 } else mf.contents = try std.posix.mmap(
865 null,
866 aligned_capacity,
867 std.posix.PROT.READ | std.posix.PROT.WRITE,
868 .{ .TYPE = if (is_linux) .SHARED_VALIDATE else .SHARED },
869 mf.file.handle,
870 0,
871 );
872}
873
874pub fn unmap(mf: *MappedFile) void {
875 if (mf.contents.len == 0) return;
876 if (is_windows)
877 _ = windows.ntdll.NtUnmapViewOfSection(windows.GetCurrentProcess(), mf.contents.ptr)
878 else
879 std.posix.munmap(mf.contents);
880 mf.contents = &.{};
881 if (is_windows and mf.section != windows.INVALID_HANDLE_VALUE) {
882 windows.CloseHandle(mf.section);
883 mf.section = windows.INVALID_HANDLE_VALUE;
884 }
885}
886
887fn verify(mf: *MappedFile) void {
888 const root = Node.Index.root.get(mf);
889 assert(root.parent == .none);
890 assert(root.prev == .none);
891 assert(root.next == .none);
892 mf.verifyNode(Node.Index.root);
893}
894
895fn verifyNode(mf: *MappedFile, parent_ni: Node.Index) void {
896 const parent = parent_ni.get(mf);
897 const parent_offset, const parent_size = parent.location().resolve(mf);
898 var prev_ni: Node.Index = .none;
899 var prev_end: u64 = 0;
900 var ni = parent.first;
901 while (true) {
902 if (ni == .none) {
903 assert(parent.last == prev_ni);
904 return;
905 }
906 const node = ni.get(mf);
907 assert(node.parent == parent_ni);
908 const offset, const size = node.location().resolve(mf);
909 assert(node.flags.alignment.check(@intCast(offset)));
910 assert(node.flags.alignment.check(@intCast(size)));
911 const end = offset + size;
912 assert(end <= parent_offset + parent_size);
913 assert(offset >= prev_end);
914 assert(node.prev == prev_ni);
915 mf.verifyNode(ni);
916 prev_ni = ni;
917 prev_end = end;
918 ni = node.next;
919 }
920}
921
922const assert = std.debug.assert;
923const builtin = @import("builtin");
924const is_linux = builtin.os.tag == .linux;
925const is_windows = builtin.os.tag == .windows;
926const linux = std.os.linux;
927const MappedFile = @This();
928const std = @import("std");
929const windows = std.os.windows;
src/link/Queue.zig+56-24
......@@ -22,17 +22,17 @@ prelink_wait_count: u32,
2222
2323/// Prelink tasks which have been enqueued and are not yet owned by the worker thread.
2424/// Allocated into `gpa`, guarded by `mutex`.
25queued_prelink: std.ArrayListUnmanaged(PrelinkTask),
25queued_prelink: std.ArrayList(PrelinkTask),
2626/// The worker thread moves items from `queued_prelink` into this array in order to process them.
2727/// Allocated into `gpa`, accessed only by the worker thread.
28wip_prelink: std.ArrayListUnmanaged(PrelinkTask),
28wip_prelink: std.ArrayList(PrelinkTask),
2929
3030/// Like `queued_prelink`, but for ZCU tasks.
3131/// Allocated into `gpa`, guarded by `mutex`.
32queued_zcu: std.ArrayListUnmanaged(ZcuTask),
32queued_zcu: std.ArrayList(ZcuTask),
3333/// Like `wip_prelink`, but for ZCU tasks.
3434/// Allocated into `gpa`, accessed only by the worker thread.
35wip_zcu: std.ArrayListUnmanaged(ZcuTask),
35wip_zcu: std.ArrayList(ZcuTask),
3636
3737/// When processing ZCU link tasks, we might have to block due to unpopulated MIR. When this
3838/// happens, some tasks in `wip_zcu` have been run, and some are still pending. This is the
......@@ -213,32 +213,41 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error!
213213
214214fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
215215 q.flush_safety.lock(); // every `return` site should unlock this before unlocking `q.mutex`
216
217216 if (std.debug.runtime_safety) {
218217 q.mutex.lock();
219218 defer q.mutex.unlock();
220219 assert(q.state == .running);
221220 }
221
222 var have_idle_tasks = true;
222223 prelink: while (true) {
223224 assert(q.wip_prelink.items.len == 0);
224 {
225 q.mutex.lock();
226 defer q.mutex.unlock();
227 std.mem.swap(std.ArrayListUnmanaged(PrelinkTask), &q.queued_prelink, &q.wip_prelink);
228 if (q.wip_prelink.items.len == 0) {
229 if (q.prelink_wait_count == 0) {
230 break :prelink; // prelink is done
231 } else {
225 swap_queues: while (true) {
226 {
227 q.mutex.lock();
228 defer q.mutex.unlock();
229 std.mem.swap(std.ArrayList(PrelinkTask), &q.queued_prelink, &q.wip_prelink);
230 if (q.wip_prelink.items.len > 0) break :swap_queues;
231 if (q.prelink_wait_count == 0) break :prelink; // prelink is done
232 if (!have_idle_tasks) {
232233 // We're expecting more prelink tasks so can't move on to ZCU tasks.
233234 q.state = .finished;
234235 q.flush_safety.unlock();
235236 return;
236237 }
237238 }
239 have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
240 error.OutOfMemory => have_idle_tasks: {
241 comp.link_diags.setAllocFailure();
242 break :have_idle_tasks false;
243 },
244 error.LinkFailure => false,
245 };
238246 }
239247 for (q.wip_prelink.items) |task| {
240248 link.doPrelinkTask(comp, task);
241249 }
250 have_idle_tasks = true;
242251 q.wip_prelink.clearRetainingCapacity();
243252 }
244253
......@@ -256,17 +265,29 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
256265
257266 // Now we can run ZCU tasks.
258267 while (true) {
259 if (q.wip_zcu.items.len == q.wip_zcu_idx) {
268 if (q.wip_zcu.items.len == q.wip_zcu_idx) swap_queues: {
260269 q.wip_zcu.clearRetainingCapacity();
261270 q.wip_zcu_idx = 0;
262 q.mutex.lock();
263 defer q.mutex.unlock();
264 std.mem.swap(std.ArrayListUnmanaged(ZcuTask), &q.queued_zcu, &q.wip_zcu);
265 if (q.wip_zcu.items.len == 0) {
266 // We've exhausted all available tasks.
267 q.state = .finished;
268 q.flush_safety.unlock();
269 return;
271 while (true) {
272 {
273 q.mutex.lock();
274 defer q.mutex.unlock();
275 std.mem.swap(std.ArrayList(ZcuTask), &q.queued_zcu, &q.wip_zcu);
276 if (q.wip_zcu.items.len > 0) break :swap_queues;
277 if (!have_idle_tasks) {
278 // We've exhausted all available tasks.
279 q.state = .finished;
280 q.flush_safety.unlock();
281 return;
282 }
283 }
284 have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
285 error.OutOfMemory => have_idle_tasks: {
286 comp.link_diags.setAllocFailure();
287 break :have_idle_tasks false;
288 },
289 error.LinkFailure => false,
290 };
270291 }
271292 }
272293 const task = q.wip_zcu.items[q.wip_zcu_idx];
......@@ -274,8 +295,18 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
274295 pending: {
275296 if (task != .link_func) break :pending;
276297 const status_ptr = &task.link_func.mir.status;
277 // First check without the mutex to optimize for the common case where MIR is ready.
278 if (status_ptr.load(.acquire) != .pending) break :pending;
298 while (true) {
299 // First check without the mutex to optimize for the common case where MIR is ready.
300 if (status_ptr.load(.acquire) != .pending) break :pending;
301 if (have_idle_tasks) have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
302 error.OutOfMemory => have_idle_tasks: {
303 comp.link_diags.setAllocFailure();
304 break :have_idle_tasks false;
305 },
306 error.LinkFailure => false,
307 };
308 if (!have_idle_tasks) break;
309 }
279310 q.mutex.lock();
280311 defer q.mutex.unlock();
281312 if (status_ptr.load(.acquire) != .pending) break :pending;
......@@ -298,6 +329,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
298329 }
299330 }
300331 q.wip_zcu_idx += 1;
332 have_idle_tasks = true;
301333 }
302334}
303335
src/link/Wasm.zig+8-1
......@@ -4257,7 +4257,14 @@ fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !Zcu
42574257 const func_table_fixups_start: u32 = @intCast(wasm.func_table_fixups.items.len);
42584258 wasm.string_bytes_lock.lock();
42594259
4260 try codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &wasm.string_bytes, .none);
4260 {
4261 var aw: std.Io.Writer.Allocating = .fromArrayList(wasm.base.comp.gpa, &wasm.string_bytes);
4262 defer wasm.string_bytes = aw.toArrayList();
4263 codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &aw.writer, .none) catch |err| switch (err) {
4264 error.WriteFailed => return error.OutOfMemory,
4265 else => |e| return e,
4266 };
4267 }
42614268
42624269 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
42634270 const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start);
src/main.zig+9-5
......@@ -904,7 +904,6 @@ fn buildOutputType(
904904 var mingw_unicode_entry_point: bool = false;
905905 var enable_link_snapshots: bool = false;
906906 var debug_compiler_runtime_libs = false;
907 var opt_incremental: ?bool = null;
908907 var install_name: ?[]const u8 = null;
909908 var hash_style: link.File.Lld.Elf.HashStyle = .both;
910909 var entitlements: ?[]const u8 = null;
......@@ -1374,9 +1373,9 @@ fn buildOutputType(
13741373 }
13751374 } else if (mem.eql(u8, arg, "-fincremental")) {
13761375 dev.check(.incremental);
1377 opt_incremental = true;
1376 create_module.opts.incremental = true;
13781377 } else if (mem.eql(u8, arg, "-fno-incremental")) {
1379 opt_incremental = false;
1378 create_module.opts.incremental = false;
13801379 } else if (mem.eql(u8, arg, "--entitlements")) {
13811380 entitlements = args_iter.nextOrFatal();
13821381 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
......@@ -1479,6 +1478,10 @@ fn buildOutputType(
14791478 create_module.opts.use_lld = true;
14801479 } else if (mem.eql(u8, arg, "-fno-lld")) {
14811480 create_module.opts.use_lld = false;
1481 } else if (mem.eql(u8, arg, "-fnew-linker")) {
1482 create_module.opts.use_new_linker = true;
1483 } else if (mem.eql(u8, arg, "-fno-new-linker")) {
1484 create_module.opts.use_new_linker = false;
14821485 } else if (mem.eql(u8, arg, "-fclang")) {
14831486 create_module.opts.use_clang = true;
14841487 } else if (mem.eql(u8, arg, "-fno-clang")) {
......@@ -3371,7 +3374,7 @@ fn buildOutputType(
33713374 else => false,
33723375 };
33733376
3374 const incremental = opt_incremental orelse false;
3377 const incremental = create_module.resolved_options.incremental;
33753378 if (debug_incremental and !incremental) {
33763379 fatal("--debug-incremental requires -fincremental", .{});
33773380 }
......@@ -3502,7 +3505,6 @@ fn buildOutputType(
35023505 .subsystem = subsystem,
35033506 .debug_compile_errors = debug_compile_errors,
35043507 .debug_incremental = debug_incremental,
3505 .incremental = incremental,
35063508 .enable_link_snapshots = enable_link_snapshots,
35073509 .install_name = install_name,
35083510 .entitlements = entitlements,
......@@ -4016,6 +4018,8 @@ fn createModule(
40164018 error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}),
40174019 error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}),
40184020 error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}),
4021 error.NewLinkerIncompatibleObjectFormat => fatal("using the new linker to link {s} files is unsupported", .{@tagName(target.ofmt)}),
4022 error.NewLinkerIncompatibleWithLld => fatal("using the new linker is incompatible with using lld", .{}),
40194023 };
40204024 }
40214025
src/target.zig+7
......@@ -231,6 +231,13 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
231231 };
232232}
233233
234pub fn hasNewLinkerSupport(ofmt: std.Target.ObjectFormat) bool {
235 return switch (ofmt) {
236 .elf => true,
237 else => false,
238 };
239}
240
234241/// The set of targets that our own self-hosted backends have robust support for.
235242/// Used to select between LLVM backend and self-hosted backend when compiling in
236243/// debug mode. A given target should only return true here if it is passing greater
test/incremental/change_exports+1-1
......@@ -1,4 +1,4 @@
1//#target=x86_64-linux-selfhosted
1#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
44
test/incremental/change_panic_handler+1
......@@ -1,3 +1,4 @@
1#target=x86_64-linux-selfhosted
12#target=x86_64-linux-cbe
23#target=x86_64-windows-cbe
34#update=initial version
test/incremental/change_panic_handler_explicit+1
......@@ -1,3 +1,4 @@
1#target=x86_64-linux-selfhosted
12#target=x86_64-linux-cbe
23#target=x86_64-windows-cbe
34#update=initial version
test/incremental/change_struct_same_fields+1-1
......@@ -1,4 +1,4 @@
1//#target=x86_64-linux-selfhosted
1#target=x86_64-linux-selfhosted
22#target=x86_64-linux-cbe
33#target=x86_64-windows-cbe
44#target=wasm32-wasi-selfhosted
test/incremental/type_becomes_comptime_only+1
......@@ -1,3 +1,4 @@
1#target=x86_64-linux-selfhosted
12#target=x86_64-linux-cbe
23#target=x86_64-windows-cbe
34#target=wasm32-wasi-selfhosted