╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ link.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
889 .reify_enum, 890 .reify_struct, 891 .reify_union, 892 => {}, 893 else => unreachable, 894 }, 895 else => unreachable, 896 } 897 } 898 switch (base.tag) { 899 .lld => unreachable, 900 .plan9 => unreachable, 901 .spirv => {}, 902 .coff2 => {}, 903 inline else => |tag| { 904 dev.check(tag.devFeature()); 905 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id, line); 906 }, 907 } 908 } 909 910 fn lostTracking(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) Error!void { 911 assert(base.comp.zcu.?.llvm_object == null); 912 switch (base.tag) { 913 .lld => unreachable, 914 .plan9 => unreachable, 915 else => {}, 916 inline .elf2 => |tag| { 917 dev.check(tag.devFeature()); 918 return @as(*tag.Type(), @fieldParentPtr("base", base)).lostTracking(pt, ti_id); 919 }, 920 } 921 } 922 923 pub fn releaseLock(base: *File) void { 924 const comp = base.comp; 925 const io = comp.io; 926 if (base.lock) |*lock| { 927 lock.release(io); 928 base.lock = null; 929 } 930 } 931 932 pub fn toOwnedLock(self: *File) Cache.Lock { 933 const lock = self.lock.?; 934 self.lock = null; 935 return lock; 936 } 937 938 pub fn destroy(base: *File) void { 939 const io = base.comp.io; 940 base.releaseLock(); 941 if (base.file) |f| f.close(io); 942 switch (base.tag) { 943 .plan9 => unreachable, 944 inline else => |tag| { 945 dev.check(tag.devFeature()); 946 @as(*tag.Type(), @fieldParentPtr("base", base)).deinit(); 947 }, 948 } 949 } 950 951 pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool { 952 switch (base.tag) { 953 else => return false, 954 inline .elf2, .coff2 => |tag| { 955 dev.check(tag.devFeature()); 956 return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid); 957 }, 958 } 959 } 960 961 pub fn updateErrorData(base: *File, pt: Zcu.PerThread) Error!void { 962 switch (base.tag) { 963 else => {}, 964 inline .elf2, .coff2 => |tag| { 965 dev.check(tag.devFeature()); 966 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt); 967 }, 968 } 969 } 970 971 /// Commit pending changes and write headers. Takes into account final output mode. 972 /// `arena` has the lifetime of the call to `Compilation.update`. 973 pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) Error!void { 974 crash_report.LinkerOp.start(base, tid); 975 defer crash_report.LinkerOp.stop(base, tid); 976 977 const comp = base.comp; 978 const io = comp.io; 979 if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) { 980 dev.check(.clang_command); 981 const emit = base.emit; 982 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) 983 // Until then, we do `lld -r -o output.o input.o` even though the output is the same 984 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file 985 // to the final location. See also the corresponding TODO in Coff linking. 986 assert(comp.c_objects.items.len == 1); 987 const the_key = comp.c_objects.items[0]; 988 const cached_pp_file_path = the_key.status.success.object_path; 989 Io.Dir.copyFile( 990 cached_pp_file_path.root_dir.handle, 991 cached_pp_file_path.sub_path, 992 emit.root_dir.handle, 993 emit.sub_path, 994 io, 995 .{}, 996 ) catch |err| { 997 const diags = &base.comp.link_diags; 998 return diags.fail("failed to copy '{f}' to '{f}': {t}", .{ 999 std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar), 1000 std.fmt.alt(@as(Path, emit), .formatEscapeChar), 1001 err, 1002 }); 1003 }; 1004 return; 1005 } 1006 assert(base.post_prelink); 1007 switch (base.tag) { 1008 .plan9 => unreachable, 1009 inline else => |tag| { 1010 dev.check(tag.devFeature()); 1011 return @as(*tag.Type(), @fieldParentPtr("base", base)).flush(arena, tid, prog_node); 1012 }, 1013 } 1014 } 1015 1016 /// This is called once per update, before `flush`. 1017 /// 1018 /// `export_indices` contains the index of every export from the ZCU which should be performed 1019 /// on this update. "Removal" of exports is signaled implicitly by the export being in this 1020 /// slice on one update but not the next. 1021 /// 1022 /// Never called when LLVM is codegenning the ZCU. 1023 pub fn updateExports( 1024 base: *File, 1025 pt: Zcu.PerThread, 1026 export_indices: []const Zcu.Export.Index, 1027 ) Error!void { 1028 assert(pt.zcu.llvm_object == null); 1029 1030 crash_report.LinkerOp.start(base, pt.tid); 1031 defer crash_report.LinkerOp.stop(base, pt.tid); 1032 1033 switch (base.tag) { 1034 .lld => unreachable, 1035 .plan9 => unreachable, 1036 inline else => |tag| { 1037 dev.check(tag.devFeature()); 1038 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, export_indices); 1039 }, 1040 } 1041 } 1042 1043 pub const RelocInfo = struct { 1044 parent: Parent, 1045 offset: u64, 1046 addend: u32, 1047 1048 pub const Parent = union(enum) { 1049 none, 1050 atom_index: AtomId, 1051 debug_output: DebugInfoOutput, 1052 }; 1053 }; 1054 1055 /// Get allocated `Nav`'s address in virtual memory. 1056 /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's 1057 /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the 1058 /// `Nav`'s address was not yet resolved, or the containing atom gets moved in virtual memory. 1059 /// May be called before or after updateFunc/updateNav therefore it is up to the linker to allocate 1060 /// the block/atom. 1061 /// Never called when LLVM is codegenning the ZCU. 1062 pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) Error!u64 { 1063 assert(pt.zcu.llvm_object == null); 1064 1065 switch (base.tag) { 1066 .lld => unreachable, 1067 .c => unreachable, 1068 .spirv => unreachable, 1069 .wasm => unreachable, 1070 .plan9 => unreachable, 1071 .spork8 => unreachable, 1072 inline else => |tag| { 1073 dev.check(tag.devFeature()); 1074 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info); 1075 }, 1076 } 1077 } 1078 1079 /// Never called when LLVM is codegenning the ZCU. 1080 pub fn lowerUav( 1081 base: *File, 1082 pt: Zcu.PerThread, 1083 decl_val: InternPool.Index, 1084 decl_align: InternPool.Alignment, 1085 ) Error!SymbolId { 1086 assert(pt.zcu.llvm_object == null); 1087 1088 switch (base.tag) { 1089 .lld => unreachable, 1090 .c => unreachable, 1091 .spirv => unreachable, 1092 .wasm => unreachable, 1093 .plan9 => unreachable, 1094 .spork8 => unreachable, 1095 inline else => |tag| { 1096 dev.check(tag.devFeature()); 1097 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align); 1098 }, 1099 } 1100 } 1101 1102 /// Never called when LLVM is codegenning the ZCU. 1103 pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) Error!u64 { 1104 assert(base.comp.zcu.?.llvm_object == null); 1105 1106 switch (base.tag) { 1107 .lld => unreachable, 1108 .c => unreachable, 1109 .spirv => unreachable, 1110 .wasm => unreachable, 1111 .plan9 => unreachable, 1112 .spork8 => unreachable, 1113 inline else => |tag| { 1114 dev.check(tag.devFeature()); 1115 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info); 1116 }, 1117 } 1118 } 1119 1120 pub const DumpResult = enum { 1121 unimplemented, 1122 needs_extensions, 1123 disabled, 1124 enabled, 1125 }; 1126 1127 pub fn dump(base: *File, w: *Io.Writer, tid: Zcu.PerThread.Id) !DumpResult { 1128 if (!build_options.enable_debug_extensions) return .not_built; 1129 switch (base.tag) { 1130 .elf, 1131 .macho, 1132 .c, 1133 .wasm, 1134 .spirv, 1135 .plan9, 1136 .lld, 1137 .spork8, 1138 => return .unimplemented, 1139 inline else => |tag| { 1140 dev.check(tag.devFeature()); 1141 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w, tid); 1142 }, 1143 } 1144 } 1145 1146 /// Opens a path as an object file and parses it into the linker. 1147 fn openLoadObject(base: *File, path: Path) anyerror!void { 1148 if (base.tag == .lld) return; 1149 const io = base.comp.io; 1150 const diags = &base.comp.link_diags; 1151 const input = try openObjectInput(io, diags, path); 1152 errdefer input.object.file.close(io); 1153 try loadInput(base, input); 1154 } 1155 1156 /// Opens a path as a static library and parses it into the linker. 1157 fn openLoadArchive(base: *File, path: Path, must_link: bool) anyerror!void { 1158 if (base.tag == .lld) return; 1159 const io = base.comp.io; 1160 const archive = try openObject(io, path, must_link, false); 1161 errdefer archive.file.close(io); 1162 try loadInput(base, .{ .archive = archive }); 1163 } 1164 1165 /// Opens a path as a static library and parses it into the linker. Allows GNU ld scripts. 1166 fn openLoadArchiveQuery(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void { 1167 if (base.tag == .lld) return; 1168 const io = base.comp.io; 1169 const archive = try openObject(io, path, query.must_link, query.hidden); 1170 errdefer archive.file.close(io); 1171 loadInput(base, .{ .archive = archive }) catch |err| switch (err) { 1172 error.BadMagic, error.UnexpectedEndOfFile => { 1173 if (base.tag != .elf and base.tag != .elf2) return err; 1174 try loadGnuLdScript(base, path, query, archive.file); 1175 archive.file.close(io); 1176 return; 1177 }, 1178 else => return err, 1179 }; 1180 } 1181 1182 /// Opens a path as a shared library and parses it into the linker. 1183 /// Handles GNU ld scripts. 1184 fn openLoadDso(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void { 1185 if (base.tag == .lld) return; 1186 const io = base.comp.io; 1187 const dso = try openDso(io, path, query.needed, query.weak, query.reexport); 1188 errdefer dso.file.close(io); 1189 loadInput(base, .{ .dso = dso }) catch |err| switch (err) { 1190 error.BadMagic, error.UnexpectedEndOfFile => { 1191 if (base.tag != .elf and base.tag != .elf2) return err; 1192 try loadGnuLdScript(base, path, query, dso.file); 1193 dso.file.close(io); 1194 return; 1195 }, 1196 else => return err, 1197 }; 1198 } 1199 1200 fn loadGnuLdScript(base: *File, path: Path, parent_query: UnresolvedInput.Query, file: Io.File) anyerror!void { 1201 const comp = base.comp; 1202 const io = comp.io; 1203 const diags = &comp.link_diags; 1204 const gpa = comp.gpa; 1205 const stat = try file.stat(io); 1206 const size = std.math.cast(u32, stat.size) orelse return error.FileTooBig; 1207 const buf = try gpa.alloc(u8, size); 1208 defer gpa.free(buf); 1209 const n = try file.readPositionalAll(io, buf, 0); 1210 if (buf.len != n) return error.UnexpectedEndOfFile; 1211 var ld_script = try LdScript.parse(gpa, diags, path, buf); 1212 defer ld_script.deinit(gpa); 1213 for (ld_script.args) |arg| { 1214 const query: UnresolvedInput.Query = .{ 1215 .needed = arg.needed or parent_query.needed, 1216 .weak = parent_query.weak, 1217 .reexport = parent_query.reexport, 1218 .preferred_mode = parent_query.preferred_mode, 1219 .search_strategy = parent_query.search_strategy, 1220 .allow_so_scripts = parent_query.allow_so_scripts, 1221 }; 1222 if (mem.startsWith(u8, arg.path, "-l")) { 1223 @panic("TODO"); 1224 } else { 1225 if (fs.path.isAbsolute(arg.path)) { 1226 const new_path = Path.initCwd(path: { 1227 comp.mutex.lockUncancelable(io); 1228 defer comp.mutex.unlock(io); 1229 break :path try comp.arena.dupe(u8, arg.path); 1230 }); 1231 switch (Compilation.classifyFileExt(arg.path)) { 1232 .shared_library => try openLoadDso(base, new_path, query), 1233 .object => try openLoadObject(base, new_path), 1234 .static_library => try openLoadArchiveQuery(base, new_path, query), 1235 else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}), 1236 } 1237 } else { 1238 @panic("TODO"); 1239 } 1240 } 1241 } 1242 } 1243 1244 pub fn loadInput(base: *File, input: Input) anyerror!void { 1245 if (base.tag == .lld) return; 1246 assert(!base.post_prelink); 1247 1248 switch (base.tag) { 1249 inline .coff2, .elf, .elf2, .wasm, .spirv => |tag| { 1250 dev.check(tag.devFeature()); 1251 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input); 1252 }, 1253 else => {}, 1254 } 1255 } 1256 1257 /// Called when all linker inputs have been sent via `loadInput`. After 1258 /// this, `loadInput` will not be called anymore. 1259 pub fn prelink(base: *File) Error!void { 1260 // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with 1261 // `-fincremental`. This works only because `File.Lld` does nothing in prelink. 1262 // Related: https://codeberg.org/ziglang/zig/issues/32081 1263 if (base.tag != .lld) { 1264 assert(!base.post_prelink); 1265 } 1266 1267 switch (base.tag) { 1268 inline .elf2, .coff2, .wasm, .c => |tag| { 1269 dev.check(tag.devFeature()); 1270 try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node); 1271 }, 1272 else => base.comp.link_prog_node.completeOne(), 1273 } 1274 1275 base.post_prelink = true; 1276 } 1277 1278 /// Legacy function for old linker code 1279 pub fn copyRangeAll(base: *File, old_offset: u64, new_offset: u64, size: u64) !void { 1280 const comp = base.comp; 1281 const io = comp.io; 1282 const file = base.file.?; 1283 return copyRangeAll2(io, file, file, old_offset, new_offset, size); 1284 } 1285 1286 /// Legacy function for old linker code 1287 pub fn copyRangeAll2(io: Io, src_file: Io.File, dst_file: Io.File, old_offset: u64, new_offset: u64, size: u64) !void { 1288 var write_buffer: [2048]u8 = undefined; 1289 var file_reader = src_file.reader(io, &.{}); 1290 file_reader.pos = old_offset; 1291 var file_writer = dst_file.writer(io, &write_buffer); 1292 file_writer.pos = new_offset; 1293 const size_u = std.math.cast(usize, size) orelse return error.Overflow; 1294 const n = file_writer.interface.sendFileAll(&file_reader, .limited(size_u)) catch |err| switch (err) { 1295 error.ReadFailed => switch (file_reader.err.?) { 1296 error.ConnectionResetByPeer => return error.Unexpected, // not a socket 1297 error.SocketUnconnected => return error.Unexpected, // not a socket 1298 else => |e| return e, 1299 }, 1300 error.WriteFailed => return file_writer.err.?, 1301 }; 1302 assert(n == size_u); 1303 file_writer.interface.flush() catch |err| switch (err) { 1304 error.WriteFailed => return file_writer.err.?, 1305 }; 1306 } 1307 1308 pub const Tag = enum { 1309 coff2, 1310 elf, 1311 elf2, 1312 macho, 1313 c, 1314 wasm, 1315 spirv, 1316 spork8, 1317 plan9, 1318 lld, 1319 1320 pub fn Type(comptime tag: Tag) type { 1321 return switch (tag) { 1322 .coff2 => Coff2, 1323 .elf => Elf, 1324 .elf2 => Elf2, 1325 .macho => MachO, 1326 .c => C, 1327 .wasm => Wasm, 1328 .spirv => Spirv, 1329 .lld => Lld, 1330 .plan9 => comptime unreachable, 1331 .spork8 => Spork8, 1332 }; 1333 } 1334 1335 fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag { 1336 return switch (ofmt) { 1337 .coff => .coff2, 1338 .elf => if (use_new_linker) .elf2 else .elf, 1339 .macho => .macho, 1340 .wasm => .wasm, 1341 .plan9 => .plan9, 1342 .c => .c, 1343 .spirv => .spirv, 1344 .hex => @panic("TODO implement hex object format"), 1345 // This may seem surprising at first, but with a little massaging, the spork8 linker 1346 // could and probably should be generalized into a "raw linker" which is used to output 1347 // bare machine code for any architecture for which a corresponding backend exists. 1348 .raw => .spork8, 1349 }; 1350 } 1351 1352 fn devFeature(tag: Tag) dev.Feature { 1353 return @field(dev.Feature, @tagName(tag) ++ "_linker"); 1354 } 1355 }; 1356 1357 pub const LazySymbol = struct { 1358 pub const Kind = enum { code, const_data }; 1359 1360 kind: Kind, 1361 ty: InternPool.Index, 1362 }; 1363 1364 pub fn determinePermissions( 1365 output_mode: std.lang.OutputMode, 1366 link_mode: std.lang.LinkMode, 1367 ) Io.File.Permissions { 1368 // On common systems with a 0o022 umask, 0o777 will still result in a file created 1369 // with 0o755 permissions, but it works appropriately if the system is configured 1370 // more leniently. As another data point, C's fopen seems to open files with the 1371 // 666 mode. 1372 const executable_mode: Io.File.Permissions = if (builtin.target.os.tag == .windows or std.posix.mode_t == u0) 1373 .default_file 1374 else 1375 .fromMode(0o777); 1376 1377 switch (output_mode) { 1378 .Lib => return switch (link_mode) { 1379 .dynamic => executable_mode, 1380 .static => .default_file, 1381 }, 1382 .Exe => return executable_mode, 1383 .Obj => return .default_file, 1384 } 1385 } 1386 1387 pub fn isStatic(self: File) bool { 1388 return self.comp.config.link_mode == .static; 1389 } 1390 1391 pub fn isObject(self: File) bool { 1392 const output_mode = self.comp.config.output_mode; 1393 return output_mode == .Obj; 1394 } 1395 1396 pub fn isExe(self: File) bool { 1397 const output_mode = self.comp.config.output_mode; 1398 return output_mode == .Exe; 1399 } 1400 1401 pub fn isStaticLib(self: File) bool { 1402 const output_mode = self.comp.config.output_mode; 1403 return output_mode == .Lib and self.isStatic(); 1404 } 1405 1406 pub fn isRelocatable(self: File) bool { 1407 return self.isObject() or self.isStaticLib(); 1408 } 1409 1410 pub fn isDynLib(self: File) bool { 1411 const output_mode = self.comp.config.output_mode; 1412 return output_mode == .Lib and !self.isStatic(); 1413 } 1414 1415 pub fn cgFail( 1416 base: *File, 1417 nav_index: InternPool.Nav.Index, 1418 comptime format: []const u8, 1419 args: anytype, 1420 ) Zcu.CodegenFailError { 1421 @branchHint(.cold); 1422 return base.comp.zcu.?.codegenFail(nav_index, format, args); 1423 } 1424 1425 pub const Lld = @import("link/Lld.zig"); 1426 pub const C = @import("link/C.zig"); 1427 pub const Coff2 = @import("link/Coff.zig"); 1428 pub const Spork8 = @import("link/Spork8.zig"); 1429 pub const Elf = @import("link/Elf.zig"); 1430 pub const Elf2 = @import("link/Elf2.zig"); 1431 pub const MachO = @import("link/MachO.zig"); 1432 pub const Spirv = @import("link/Spirv.zig"); 1433 pub const Wasm = @import("link/Wasm.zig"); 1434 pub const Dwarf = @import("link/Dwarf.zig"); 1435 pub const Dwarf2 = @import("link/Dwarf2.zig"); 1436 }; 1437 1438 pub const PrelinkTask = union(enum) { 1439 /// Loads the objects, shared objects, and archives that are already 1440 /// known from the command line. 1441 load_explicitly_provided, 1442 /// Loads the shared objects and archives by resolving 1443 /// `target_util.libcFullLinkFlags()` against the host libc 1444 /// installation. 1445 load_host_libc, 1446 /// Tells the linker to load an object file by path. 1447 load_object: Path, 1448 /// Tells the linker to load a static library by path. 1449 load_archive: struct { 1450 path: Path, 1451 must_link: bool, 1452 }, 1453 /// Tells the linker to load a shared library, possibly one that is a 1454 /// GNU ld script. 1455 load_dso: Path, 1456 }; 1457 pub const ZcuTask = union(enum) { 1458 /// Sent once per update, as the very first `ZcuTask` in the update. Indicates that all per-file 1459 /// state (e.g. `Zcu.alive_files`) is populated so can now be safely accessed by the linker. 1460 files_ready, 1461 /// Write the constant value for a Decl to the output file. 1462 link_nav: InternPool.Nav.Index, 1463 /// Write the machine code for a function to the output file. 1464 link_func: Zcu.CodegenTaskPool.Index, 1465 /// This struct/union/enum type has finished type resolution (successfully or otherwise), so the 1466 /// linker can now lower debug information for this type (and any structural types which depend 1467 /// on it, such as `?T`, `struct { T }`, `[2]T`, etc). 1468 debug_update_container_type: struct { 1469 ty: InternPool.Index, 1470 success: bool, 1471 }, 1472 debug_update_line_number: struct { 1473 inst: InternPool.TrackedInst.Index, 1474 line: u32, 1475 }, 1476 lost_tracking: InternPool.TrackedInst.Index, 1477 }; 1478 1479 pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { 1480 const io = comp.io; 1481 const diags = &comp.link_diags; 1482 const base = comp.bin_file orelse { 1483 comp.link_prog_node.completeOne(); 1484 return; 1485 }; 1486 1487 // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with 1488 // `-fincremental`. This works only because `File.Lld` does nothing in prelink. 1489 // Related: https://codeberg.org/ziglang/zig/issues/32081 1490 if (base.tag != .lld) { 1491 assert(!base.post_prelink); 1492 } 1493 1494 var timer = comp.startTimer(); 1495 defer if (timer.finish(io)) |ns| { 1496 comp.mutex.lockUncancelable(io); 1497 defer comp.mutex.unlock(io); 1498 comp.time_report.?.stats.cpu_ns_link += ns; 1499 }; 1500 1501 switch (task) { 1502 .load_explicitly_provided => { 1503 const prog_node = comp.link_prog_node.start("Parse Inputs", comp.link_inputs.len); 1504 defer prog_node.end(); 1505 for (comp.link_inputs) |input| { 1506 base.loadInput(input) catch |err| switch (err) { 1507 error.AlreadyReported => return, // error reported via diags 1508 else => |e| switch (input) { 1509 .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {t}", .{e}), 1510 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}), 1511 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}), 1512 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {t}", .{e}), 1513 .dso_exact => diags.addError("failed to handle dso_exact: {t}", .{e}), 1514 }, 1515 }; 1516 prog_node.completeOne(); 1517 } 1518 }, 1519 .load_host_libc => { 1520 const prog_node = comp.link_prog_node.start("Parse Host libc", 0); 1521 defer prog_node.end(); 1522 1523 const target = &comp.root_mod.resolved_target.result; 1524 const flags = target_util.libcFullLinkFlags(target); 1525 const libc_installation = comp.libc_installation.?; 1526 const crt_dir = libc_installation.crt_dir.?; 1527 const sep = std.fs.path.sep_str; 1528 for (flags) |flag| { 1529 assert(mem.startsWith(u8, flag, "-l")); 1530 const lib_name = flag["-l".len..]; 1531 switch (comp.config.link_mode) { 1532 .dynamic => { 1533 const dso_path = Path.initCwd( 1534 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ 1535 crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(), 1536 }) catch return diags.setAllocFailure(), 1537 ); 1538 base.openLoadDso(dso_path, .{ 1539 .preferred_mode = .dynamic, 1540 .search_strategy = .paths_first, 1541 }) catch |err| switch (err) { 1542 error.FileNotFound => { 1543 // Also try static. 1544 const archive_path = Path.initCwd( 1545 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ 1546 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), 1547 }) catch return diags.setAllocFailure(), 1548 ); 1549 base.openLoadArchiveQuery(archive_path, .{ 1550 .preferred_mode = .dynamic, 1551 .search_strategy = .paths_first, 1552 }) catch |archive_err| switch (archive_err) { 1553 error.AlreadyReported => return, // error reported via diags 1554 else => |e| diags.addParseError(dso_path, "failed to parse archive {f}: {s}", .{ archive_path, @errorName(e) }), 1555 }; 1556 }, 1557 error.AlreadyReported => return, // error reported via diags 1558 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}), 1559 }; 1560 }, 1561 .static => { 1562 const path = Path.initCwd( 1563 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ 1564 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), 1565 }) catch return diags.setAllocFailure(), 1566 ); 1567 // glibc sometimes makes even archive files GNU ld scripts. 1568 base.openLoadArchiveQuery(path, .{ 1569 .preferred_mode = .static, 1570 .search_strategy = .no_fallback, 1571 }) catch |err| switch (err) { 1572 error.AlreadyReported => return, // error reported via diags 1573 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), 1574 }; 1575 }, 1576 } 1577 } 1578 1579 if (target.os.tag == .windows and target.abi == .msvc) { 1580 const inputs: []const struct { 1581 dir: enum { crt, msvc_lib, kernel32_lib }, 1582 name: []const u8, 1583 } = switch (comp.config.link_mode) { 1584 .dynamic => &.{ 1585 .{ .dir = .msvc_lib, .name = "msvcrt.lib" }, 1586 .{ .dir = .msvc_lib, .name = "vcruntime.lib" }, 1587 .{ .dir = .msvc_lib, .name = "legacy_stdio_definitions.lib" }, 1588 .{ .dir = .crt, .name = "ucrt.lib" }, 1589 .{ .dir = .kernel32_lib, .name = "kernel32.lib" }, 1590 .{ .dir = .kernel32_lib, .name = "ntdll.lib" }, 1591 }, 1592 .static => &.{ 1593 .{ .dir = .msvc_lib, .name = "libcmt.lib" }, 1594 .{ .dir = .msvc_lib, .name = "libvcruntime.lib" }, 1595 .{ .dir = .msvc_lib, .name = "legacy_stdio_definitions.lib" }, 1596 .{ .dir = .crt, .name = "libucrt.lib" }, 1597 .{ .dir = .kernel32_lib, .name = "kernel32.lib" }, 1598 .{ .dir = .kernel32_lib, .name = "ntdll.lib" }, 1599 }, 1600 }; 1601 1602 for (inputs) |lib| { 1603 const path = Path.initCwd( 1604 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}", .{ 1605 switch (lib.dir) { 1606 .crt => crt_dir, 1607 .msvc_lib => libc_installation.msvc_lib_dir.?, 1608 .kernel32_lib => libc_installation.kernel32_lib_dir.?, 1609 }, 1610 lib.name, 1611 }) catch return diags.setAllocFailure(), 1612 ); 1613 if (std.mem.endsWith(u8, lib.name, "lib")) { 1614 base.openLoadArchive(path, false) catch |err| switch (err) { 1615 error.LinkFailure => return, // error reported via diags 1616 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), 1617 }; 1618 } else { 1619 base.openLoadObject(path) catch |err| switch (err) { 1620 error.LinkFailure => return, // error reported via diags 1621 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}), 1622 }; 1623 } 1624 } 1625 } 1626 }, 1627 .load_object => |path| { 1628 const prog_node = comp.link_prog_node.start("Parse Object", 0); 1629 defer prog_node.end(); 1630 base.openLoadObject(path) catch |err| switch (err) { 1631 error.AlreadyReported => return, // error reported via diags 1632 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}), 1633 }; 1634 }, 1635 .load_archive => |load_archive| { 1636 const prog_node = comp.link_prog_node.start("Parse Archive", 0); 1637 defer prog_node.end(); 1638 base.openLoadArchive(load_archive.path, load_archive.must_link) catch |err| switch (err) { 1639 error.AlreadyReported => return, // error reported via link_diags 1640 else => |e| diags.addParseError(load_archive.path, "failed to parse archive: {s}", .{@errorName(e)}), 1641 }; 1642 }, 1643 .load_dso => |path| { 1644 const prog_node = comp.link_prog_node.start("Parse Shared Library", 0); 1645 defer prog_node.end(); 1646 base.openLoadDso(path, .{ 1647 .preferred_mode = .dynamic, 1648 .search_strategy = .paths_first, 1649 }) catch |err| switch (err) { 1650 error.AlreadyReported => return, // error reported via link_diags 1651 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}), 1652 }; 1653 }, 1654 } 1655 } 1656 pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void { 1657 const io = comp.io; 1658 const diags = &comp.link_diags; 1659 const zcu = comp.zcu.?; 1660 const ip = &zcu.intern_pool; 1661 const active = zcu.activate(tid); 1662 defer active.deactivate(); 1663 const pt = active.pt; 1664 1665 var timer = comp.startTimer(); 1666 1667 const maybe_nav: ?InternPool.Nav.Index = switch (task) { 1668 .files_ready => { 1669 if (zcu.llvm_object != null) return; 1670 const lf = comp.bin_file orelse return; 1671 lf.zcuFilesReady(zcu) catch |err| switch (err) { 1672 error.Canceled => io.recancel(), 1673 error.AlreadyReported => return, 1674 error.OutOfMemory => return diags.setAllocFailure(), 1675 }; 1676 return; 1677 }, 1678 .link_nav => |nav_index| nav: { 1679 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); 1680 const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0); 1681 defer nav_prog_node.end(); 1682 if (zcu.llvm_object) |llvm_object| { 1683 llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { 1684 error.OutOfMemory => diags.setAllocFailure(), 1685 }; 1686 } else if (comp.bin_file) |lf| { 1687 lf.updateNav(pt, nav_index) catch |err| switch (err) { 1688 error.Canceled => io.recancel(), 1689 error.AlreadyReported => return, 1690 error.OutOfMemory => diags.setAllocFailure(), 1691 }; 1692 } 1693 break :nav nav_index; 1694 }, 1695 .link_func => |codegen_task| nav: { 1696 timer.pause(io); 1697 const func, var mir = codegen_task.wait(&zcu.codegen_task_pool, zcu) catch |err| switch (err) { 1698 error.Canceled, error.AlreadyReported => { 1699 comp.link_prog_node.completeOne(); 1700 return; 1701 }, 1702 }; 1703 defer mir.deinit(zcu); 1704 timer.@"resume"(io); 1705 1706 const nav = zcu.funcInfo(func).owner_nav; 1707 const fqn_slice = ip.getNav(nav).fqn.toSlice(ip); 1708 1709 const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0); 1710 defer nav_prog_node.end(); 1711 1712 assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR 1713 if (comp.bin_file) |lf| { 1714 lf.updateFunc(pt, func, &mir) catch |err| switch (err) { 1715 error.Canceled => io.recancel(), 1716 error.AlreadyReported => return, 1717 error.OutOfMemory => return diags.setAllocFailure(), 1718 }; 1719 } 1720 break :nav ip.indexToKey(func).func.owner_nav; 1721 }, 1722 .debug_update_container_type => |container_update| nav: { 1723 const fqn = Type.fromInterned(container_update.ty).containerTypeName(ip).fqn.toSlice(ip); 1724 const ty_prog_node = comp.link_prog_node.start(fqn, 0); 1725 defer ty_prog_node.end(); 1726 (if (zcu.llvm_object) |llvm_object| 1727 llvm_object.updateContainerType(pt, container_update.ty, container_update.success) 1728 else if (comp.bin_file) |lf| 1729 lf.updateContainerType(pt, container_update.ty, container_update.success)) catch |err| switch (err) { 1730 error.OutOfMemory => diags.setAllocFailure(), 1731 error.Canceled => io.recancel(), 1732 error.AlreadyReported => {}, 1733 }; 1734 break :nav null; 1735 }, 1736 .debug_update_line_number => |line_update| nav: { 1737 const nav_prog_node = comp.link_prog_node.start("Update line number", 0); 1738 defer nav_prog_node.end(); 1739 if (pt.zcu.llvm_object == null) { 1740 if (comp.bin_file) |lf| { 1741 lf.updateLineNumber(pt, line_update.inst, line_update.line) catch |err| switch (err) { 1742 error.OutOfMemory => diags.setAllocFailure(), 1743 else => |e| log.err("update line number failed: {s}", .{@errorName(e)}), 1744 }; 1745 } 1746 } 1747 break :nav null; 1748 }, 1749 .lost_tracking => |ti| nav: { 1750 const nav_prog_node = comp.link_prog_node.start("Lost tracking", 0); 1751 defer nav_prog_node.end(); 1752 if (pt.zcu.llvm_object == null) { 1753 if (comp.bin_file) |lf| { 1754 lf.lostTracking(pt, ti) catch |err| switch (err) { 1755 error.OutOfMemory => diags.setAllocFailure(), 1756 else => |e| log.err("lost tracking failed: {s}", .{@errorName(e)}), 1757 }; 1758 } 1759 } 1760 break :nav null; 1761 }, 1762 }; 1763 1764 if (timer.finish(io)) |ns_link| report_time: { 1765 comp.mutex.lockUncancelable(io); 1766 defer comp.mutex.unlock(io); 1767 const tr = &zcu.comp.time_report.?; 1768 tr.stats.cpu_ns_link += ns_link; 1769 if (maybe_nav) |nav| { 1770 const zir_decl = ip.getNav(nav).srcInst(ip); 1771 const gop = tr.decl_link_ns.getOrPut(zcu.gpa, zir_decl) catch |err| switch (err) { 1772 error.OutOfMemory => { 1773 zcu.comp.setAllocFailure(); 1774 break :report_time; 1775 }, 1776 }; 1777 if (!gop.found_existing) gop.value_ptr.* = 0; 1778 gop.value_ptr.* += ns_link; 1779 } 1780 } 1781 } 1782 pub fn doIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) Error!bool { 1783 return if (comp.bin_file) |lf| lf.idle(tid) else false; 1784 } 1785 /// After the main pipeline is done, but before flush, the compilation may need to link one final 1786 /// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running 1787 /// by then, we expose this function which can be called directly. 1788 pub fn linkTestFunctionsNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) void { 1789 const zcu = pt.zcu; 1790 const comp = zcu.comp; 1791 const diags = &comp.link_diags; 1792 if (zcu.llvm_object) |llvm_object| { 1793 llvm_object.updateNav(pt, nav_index) catch |err| switch (err) { 1794 error.OutOfMemory => diags.setAllocFailure(), 1795 }; 1796 } else if (comp.bin_file) |lf| { 1797 lf.updateNav(pt, nav_index) catch |err| switch (err) { 1798 error.Canceled => comp.io.recancel(), 1799 error.AlreadyReported => return, 1800 error.OutOfMemory => diags.setAllocFailure(), 1801 }; 1802 } 1803 } 1804 pub fn updateErrorData(pt: Zcu.PerThread) void { 1805 const comp = pt.zcu.comp; 1806 if (comp.bin_file) |lf| lf.updateErrorData(pt) catch |err| switch (err) { 1807 error.OutOfMemory => comp.link_diags.setAllocFailure(), 1808 error.Canceled => comp.io.recancel(), 1809 error.AlreadyReported => {}, 1810 }; 1811 } 1812 1813 /// Provided by the CLI, processed into `LinkInput` instances at the start of 1814 /// the compilation pipeline. 1815 pub const UnresolvedInput = union(enum) { 1816 /// A library name that could potentially be dynamic or static depending on 1817 /// query parameters, resolved according to library directories. 1818 /// This could potentially resolve to a GNU ld script, resulting in more 1819 /// library dependencies. 1820 name_query: NameQuery, 1821 /// When a file path is provided, query info is still needed because the 1822 /// path may point to a .so file which may actually be a GNU ld script that 1823 /// references library names which need to be resolved. 1824 path_query: PathQuery, 1825 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path? 1826 /// Who knows! Fuck around and find out. 1827 ambiguous_name: NameQuery, 1828 /// Put exactly this string in the dynamic section, no rpath. 1829 dso_exact: Input.DsoExact, 1830 1831 pub const NameQuery = struct { 1832 name: []const u8, 1833 query: Query, 1834 }; 1835 1836 pub const PathQuery = struct { 1837 path: Path, 1838 query: Query, 1839 }; 1840 1841 pub const Query = struct { 1842 needed: bool = false, 1843 weak: bool = false, 1844 reexport: bool = false, 1845 must_link: bool = false, 1846 hidden: bool = false, 1847 allow_so_scripts: bool = false, 1848 preferred_mode: std.lang.LinkMode, 1849 search_strategy: SearchStrategy, 1850 1851 fn fallbackMode(q: Query) std.lang.LinkMode { 1852 assert(q.search_strategy != .no_fallback); 1853 return switch (q.preferred_mode) { 1854 .dynamic => .static, 1855 .static => .dynamic, 1856 }; 1857 } 1858 }; 1859 1860 pub const SearchStrategy = enum { 1861 paths_first, 1862 mode_first, 1863 no_fallback, 1864 }; 1865 }; 1866 1867 pub const Input = union(enum) { 1868 object: Object, 1869 archive: Object, 1870 res: Res, 1871 /// May not be a GNU ld script. Those are resolved when converting from 1872 /// `UnresolvedInput` to `Input` values. 1873 dso: Dso, 1874 dso_exact: DsoExact, 1875 1876 pub const Object = struct { 1877 path: Path, 1878 file: Io.File, 1879 must_link: bool, 1880 hidden: bool, 1881 }; 1882 1883 pub const Res = struct { 1884 path: Path, 1885 file: Io.File, 1886 }; 1887 1888 pub const Dso = struct { 1889 path: Path, 1890 file: Io.File, 1891 needed: bool, 1892 weak: bool, 1893 reexport: bool, 1894 }; 1895 1896 pub const DsoExact = struct { 1897 /// Includes the ":" prefix. This is intended to be put into the DSO 1898 /// section verbatim with no corresponding rpaths. 1899 name: []const u8, 1900 }; 1901 1902 /// Returns `null` in the case of `dso_exact`. 1903 pub fn path(input: Input) ?Path { 1904 return switch (input) { 1905 .object, .archive => |obj| obj.path, 1906 inline .res, .dso => |x| x.path, 1907 .dso_exact => null, 1908 }; 1909 } 1910 1911 /// Returns `null` in the case of `dso_exact`. 1912 pub fn pathAndFile(input: Input) ?struct { Path, Io.File } { 1913 return switch (input) { 1914 .object, .archive => |obj| .{ obj.path, obj.file }, 1915 inline .res, .dso => |x| .{ x.path, x.file }, 1916 .dso_exact => null, 1917 }; 1918 } 1919 1920 pub fn taskName(input: Input) []const u8 { 1921 return switch (input) { 1922 .object, .archive => |obj| obj.path.basename(), 1923 inline .res, .dso => |x| x.path.basename(), 1924 .dso_exact => "dso_exact", 1925 }; 1926 } 1927 }; 1928 1929 pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void { 1930 for (link_inputs) |link_input| { 1931 man.hash.add(@as(@typeInfo(Input).@"union".tag_type.?, link_input)); 1932 switch (link_input) { 1933 .object, .archive => |obj| { 1934 _ = try man.addInputPath(obj.path, .{ 1935 .handle = .{ .file = obj.file }, 1936 .request_handle = true, 1937 }); 1938 man.hash.add(obj.must_link); 1939 man.hash.add(obj.hidden); 1940 }, 1941 .res => |res| { 1942 _ = try man.addInputPath(res.path, .{ 1943 .handle = .{ .file = res.file }, 1944 .request_handle = true, 1945 }); 1946 }, 1947 .dso => |dso| { 1948 _ = try man.addInputPath(dso.path, .{ 1949 .handle = .{ .file = dso.file }, 1950 .request_handle = true, 1951 }); 1952 man.hash.add(dso.needed); 1953 man.hash.add(dso.weak); 1954 man.hash.add(dso.reexport); 1955 }, 1956 .dso_exact => |dso_exact| { 1957 man.hash.addBytes(dso_exact.name); 1958 }, 1959 } 1960 } 1961 } 1962 1963 pub fn resolveInputs( 1964 gpa: Allocator, 1965 arena: Allocator, 1966 io: Io, 1967 target: *const std.Target, 1968 /// This function mutates this array but does not take ownership. 1969 /// Allocated with `gpa`. 1970 unresolved_inputs: *std.ArrayList(UnresolvedInput), 1971 /// Allocated with `gpa`. 1972 resolved_inputs: *std.ArrayList(Input), 1973 lib_directories: []const Cache.Directory, 1974 color: std.zig.Color, 1975 ) Allocator.Error!void { 1976 var checked_paths: std.ArrayList(u8) = .empty; 1977 defer checked_paths.deinit(gpa); 1978 1979 var ld_script_bytes: std.ArrayList(u8) = .empty; 1980 defer ld_script_bytes.deinit(gpa); 1981 1982 var archive_dedup: ArchiveDedupMap = .empty; 1983 defer archive_dedup.deinit(gpa); 1984 1985 var failed_libs: std.ArrayList(struct { 1986 name: []const u8, 1987 strategy: UnresolvedInput.SearchStrategy, 1988 checked_paths: []const u8, 1989 preferred_mode: std.lang.LinkMode, 1990 }) = .empty; 1991 1992 // Convert external system libs into a stack so that items can be 1993 // pushed to it. 1994 // 1995 // This is necessary because shared objects might turn out to be 1996 // "linker scripts" that in fact resolve to one or more other 1997 // external system libs, including parameters such as "needed". 1998 // 1999 // Unfortunately, such files need to be detected immediately, so 2000 // that this library search logic can be applied to them. 2001 mem.reverse(UnresolvedInput, unresolved_inputs.items); 2002 2003 syslib: while (unresolved_inputs.pop()) |unresolved_input| { 2004 switch (unresolved_input) { 2005 .name_query => |name_query| { 2006 const query = name_query.query; 2007 2008 // Checked in the first pass above while looking for libc libraries. 2009 assert(!fs.path.isAbsolute(name_query.name)); 2010 2011 checked_paths.clearRetainingCapacity(); 2012 2013 switch (query.search_strategy) { 2014 .mode_first, .no_fallback => { 2015 // check for preferred mode 2016 for (lib_directories) |lib_directory| switch (try resolveLibInput( 2017 gpa, 2018 arena, 2019 io, 2020 unresolved_inputs, 2021 resolved_inputs, 2022 &checked_paths, 2023 &ld_script_bytes, 2024 &archive_dedup, 2025 lib_directory, 2026 name_query, 2027 target, 2028 query.preferred_mode, 2029 color, 2030 )) { 2031 .ok => continue :syslib, 2032 .no_match => {}, 2033 }; 2034 // check for fallback mode 2035 if (query.search_strategy == .no_fallback) { 2036 try failed_libs.append(arena, .{ 2037 .name = name_query.name, 2038 .strategy = query.search_strategy, 2039 .checked_paths = try arena.dupe(u8, checked_paths.items), 2040 .preferred_mode = query.preferred_mode, 2041 }); 2042 continue :syslib; 2043 } 2044 for (lib_directories) |lib_directory| switch (try resolveLibInput( 2045 gpa, 2046 arena, 2047 io, 2048 unresolved_inputs, 2049 resolved_inputs, 2050 &checked_paths, 2051 &ld_script_bytes, 2052 &archive_dedup, 2053 lib_directory, 2054 name_query, 2055 target, 2056 query.fallbackMode(), 2057 color, 2058 )) { 2059 .ok => continue :syslib, 2060 .no_match => {}, 2061 }; 2062 try failed_libs.append(arena, .{ 2063 .name = name_query.name, 2064 .strategy = query.search_strategy, 2065 .checked_paths = try arena.dupe(u8, checked_paths.items), 2066 .preferred_mode = query.preferred_mode, 2067 }); 2068 continue :syslib; 2069 }, 2070 .paths_first => { 2071 for (lib_directories) |lib_directory| { 2072 // check for preferred mode 2073 switch (try resolveLibInput( 2074 gpa, 2075 arena, 2076 io, 2077 unresolved_inputs, 2078 resolved_inputs, 2079 &checked_paths, 2080 &ld_script_bytes, 2081 &archive_dedup, 2082 lib_directory, 2083 name_query, 2084 target, 2085 query.preferred_mode, 2086 color, 2087 )) { 2088 .ok => continue :syslib, 2089 .no_match => {}, 2090 } 2091 2092 // check for fallback mode 2093 switch (try resolveLibInput( 2094 gpa, 2095 arena, 2096 io, 2097 unresolved_inputs, 2098 resolved_inputs, 2099 &checked_paths, 2100 &ld_script_bytes, 2101 &archive_dedup, 2102 lib_directory, 2103 name_query, 2104 target, 2105 query.fallbackMode(), 2106 color, 2107 )) { 2108 .ok => continue :syslib, 2109 .no_match => {}, 2110 } 2111 } 2112 try failed_libs.append(arena, .{ 2113 .name = name_query.name, 2114 .strategy = query.search_strategy, 2115 .checked_paths = try arena.dupe(u8, checked_paths.items), 2116 .preferred_mode = query.preferred_mode, 2117 }); 2118 continue :syslib; 2119 }, 2120 } 2121 }, 2122 .ambiguous_name => |an| { 2123 // First check the path relative to the current working directory. 2124 // If the file is a library and is not found there, check the library search paths as well. 2125 // This is consistent with the behavior of GNU ld. 2126 if (try resolvePathInput( 2127 gpa, 2128 arena, 2129 io, 2130 unresolved_inputs, 2131 resolved_inputs, 2132 &ld_script_bytes, 2133 &archive_dedup, 2134 target, 2135 .{ 2136 .path = Path.initCwd(an.name), 2137 .query = an.query, 2138 }, 2139 color, 2140 )) |lib_result| { 2141 switch (lib_result) { 2142 .ok => continue :syslib, 2143 .no_match => { 2144 for (lib_directories) |lib_directory| { 2145 switch ((try resolvePathInput( 2146 gpa, 2147 arena, 2148 io, 2149 unresolved_inputs, 2150 resolved_inputs, 2151 &ld_script_bytes, 2152 &archive_dedup, 2153 target, 2154 .{ 2155 .path = .{ 2156 .root_dir = lib_directory, 2157 .sub_path = an.name, 2158 }, 2159 .query = an.query, 2160 }, 2161 color, 2162 )).?) { 2163 .ok => continue :syslib, 2164 .no_match => {}, 2165 } 2166 } 2167 fatal("{s}: file listed in linker script not found", .{an.name}); 2168 }, 2169 } 2170 } 2171 continue; 2172 }, 2173 .path_query => |pq| { 2174 if (try resolvePathInput( 2175 gpa, 2176 arena, 2177 io, 2178 unresolved_inputs, 2179 resolved_inputs, 2180 &ld_script_bytes, 2181 &archive_dedup, 2182 target, 2183 pq, 2184 color, 2185 )) |lib_result| { 2186 switch (lib_result) { 2187 .ok => {}, 2188 .no_match => fatal("{f}: file not found", .{pq.path}), 2189 } 2190 } 2191 continue; 2192 }, 2193 .dso_exact => |dso_exact| { 2194 try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact }); 2195 continue; 2196 }, 2197 } 2198 comptime unreachable; 2199 } 2200 2201 if (failed_libs.items.len > 0) { 2202 for (failed_libs.items) |f| { 2203 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; 2204 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ 2205 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths, 2206 }); 2207 } 2208 std.process.exit(1); 2209 } 2210 } 2211 2212 const ResolveLibInputResult = enum { ok, no_match }; 2213 const fatal = std.process.fatal; 2214 2215 fn resolveLibInput( 2216 gpa: Allocator, 2217 arena: Allocator, 2218 io: Io, 2219 /// Allocated via `gpa`. 2220 unresolved_inputs: *std.ArrayList(UnresolvedInput), 2221 /// Allocated via `gpa`. 2222 resolved_inputs: *std.ArrayList(Input), 2223 /// Allocated via `gpa`. 2224 checked_paths: *std.ArrayList(u8), 2225 /// Allocated via `gpa`. 2226 ld_script_bytes: *std.ArrayList(u8), 2227 /// Allocated via `gpa`. 2228 archive_dedup: *ArchiveDedupMap, 2229 lib_directory: Directory, 2230 name_query: UnresolvedInput.NameQuery, 2231 target: *const std.Target, 2232 link_mode: std.lang.LinkMode, 2233 color: std.zig.Color, 2234 ) Allocator.Error!ResolveLibInputResult { 2235 try resolved_inputs.ensureUnusedCapacity(gpa, 1); 2236 try archive_dedup.ensureUnusedCapacity(gpa, 1); 2237 2238 const lib_name = name_query.name; 2239 2240 if (target.os.tag.isDarwin() and link_mode == .dynamic) tbd: { 2241 // Prefer .tbd over .dylib. 2242 const test_path: Path = .{ 2243 .root_dir = lib_directory, 2244 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.tbd", .{lib_name}), 2245 }; 2246 try checked_paths.print(gpa, "\n {f}", .{test_path}); 2247 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { 2248 error.FileNotFound => break :tbd, 2249 else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }), 2250 }; 2251 errdefer file.close(io); 2252 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); 2253 } 2254 2255 { 2256 const test_path: Path = .{ 2257 .root_dir = lib_directory, 2258 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{ 2259 target.libPrefix(), lib_name, 2260 switch (link_mode) { 2261 .static => target.staticLibSuffix(), 2262 .dynamic => target.dynamicLibSuffix(), 2263 }, 2264 }), 2265 }; 2266 try checked_paths.print(gpa, "\n {f}", .{test_path}); 2267 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{ 2268 .path = test_path, 2269 .query = name_query.query, 2270 }, link_mode, color)) { 2271 .no_match => {}, 2272 .ok => return .ok, 2273 } 2274 } 2275 2276 // In the case of Darwin, the main check will be .dylib, so here we 2277 // additionally check for .so files. 2278 if (target.os.tag.isDarwin() and link_mode == .dynamic) so: { 2279 const test_path: Path = .{ 2280 .root_dir = lib_directory, 2281 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}), 2282 }; 2283 try checked_paths.print(gpa, "\n {f}", .{test_path}); 2284 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { 2285 error.FileNotFound => break :so, 2286 else => |e| fatal("unable to search for so library '{f}': {s}", .{ 2287 test_path, @errorName(e), 2288 }), 2289 }; 2290 errdefer file.close(io); 2291 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); 2292 } 2293 2294 // In the case of MinGW, the main check will be .lib but we also need to 2295 // look for `libfoo.a`. 2296 if (target.isMinGW() and link_mode == .static) mingw: { 2297 const test_path: Path = .{ 2298 .root_dir = lib_directory, 2299 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}), 2300 }; 2301 try checked_paths.print(gpa, "\n {f}", .{test_path}); 2302 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { 2303 error.FileNotFound => break :mingw, 2304 else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }), 2305 }; 2306 errdefer file.close(io); 2307 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); 2308 } 2309 2310 // In the case of OpenBSD, dynamic libraries are always versioned, without 2311 // unversioned symlinks. OpenBSD patches LLD to select the highest-versioned 2312 // shared library, and this code is intended to match that upstream behavior. 2313 if (target.isOpenBSDLibC() and link_mode == .dynamic) versioned: { 2314 const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name}); 2315 2316 var dir = lib_directory.handle.openDir(io, ".", .{ .iterate = true }) catch |err| switch (err) { 2317 error.NotDir, error.FileNotFound => break :versioned, 2318 else => |e| fatal("unable to search for shared library '{s}.*': {s}", .{ prefix, @errorName(e) }), 2319 }; 2320 defer dir.close(io); 2321 2322 var best_match_major: u32 = 0; 2323 var best_match_minor: u32 = 0; 2324 var best_match: ?[]const u8 = null; 2325 2326 var iter = dir.iterate(); 2327 while (iter.next(io) catch |err| { 2328 fatal("unable to scan library directory '{s}'", .{@errorName(err)}); 2329 }) |entry| { 2330 if (entry.kind != .file) continue; 2331 if (!std.mem.startsWith(u8, entry.name, prefix)) continue; 2332 2333 const rest = entry.name[prefix.len..]; 2334 var sit = std.mem.splitScalar(u8, rest, '.'); 2335 const major_str = sit.next() orelse continue; 2336 const minor_str = sit.next() orelse continue; 2337 if (sit.next() != null) continue; 2338 const major = std.fmt.parseInt(u32, major_str, 10) catch continue; 2339 const minor = std.fmt.parseInt(u32, minor_str, 10) catch continue; 2340 2341 if (major > best_match_major or (major == best_match_major and minor >= best_match_minor)) { 2342 best_match_major = major; 2343 best_match_minor = minor; 2344 best_match = try arena.dupe(u8, entry.name); 2345 } 2346 } 2347 2348 if (best_match) |found| { 2349 const test_path: Path = .{ 2350 .root_dir = lib_directory, 2351 .sub_path = found, 2352 }; 2353 try checked_paths.print(gpa, "\n {f}", .{test_path}); 2354 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{ 2355 .path = test_path, 2356 .query = name_query.query, 2357 }, link_mode, color)) { 2358 .no_match => {}, 2359 .ok => return .ok, 2360 } 2361 } 2362 } 2363 2364 return .no_match; 2365 } 2366 2367 /// Deduplicates static archive link inputs based on their path. This is done for efficiency, so 2368 /// that linker implementations do not need to open and scan the archive just to determine that they 2369 /// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions 2370 /// of symbol" errors in incomplete linker implementations. 2371 /// 2372 /// Key is index into `resolved_inputs` of an `Input.archive`. 2373 /// 2374 /// Accessed through `ArchiveDedupAdapter`. 2375 /// 2376 const ArchiveDedupMap = std.array_hash_map.Custom(u32, void, void, true); 2377 /// Adapter for accessing `ArchiveDedupMap` with an effective key type of `Path`. 2378 const ArchiveDedupAdapter = struct { 2379 resolved_inputs: []const Input, 2380 pub fn hash(ctx: ArchiveDedupAdapter, path: Path) u32 { 2381 _ = ctx; 2382 return Path.TableAdapter.hash(.{}, path); 2383 } 2384 pub fn eql(ctx: ArchiveDedupAdapter, a_path: Path, b_input_index: u32, _: usize) bool { 2385 const b_path = ctx.resolved_inputs[b_input_index].archive.path; 2386 return a_path.eql(b_path); 2387 } 2388 }; 2389 2390 fn finishResolveLibInput( 2391 io: Io, 2392 resolved_inputs: *std.ArrayList(Input), 2393 archive_dedup: *ArchiveDedupMap, 2394 path: Path, 2395 file: Io.File, 2396 link_mode: std.lang.LinkMode, 2397 query: UnresolvedInput.Query, 2398 ) ResolveLibInputResult { 2399 switch (link_mode) { 2400 .static => { 2401 const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items }; 2402 const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx); 2403 if (gop.found_existing) { 2404 // Ignore duplicate archive input 2405 file.close(io); 2406 return .ok; 2407 } 2408 gop.key_ptr.* = @intCast(resolved_inputs.items.len); 2409 resolved_inputs.appendAssumeCapacity(.{ .archive = .{ 2410 .path = path, 2411 .file = file, 2412 .must_link = query.must_link, 2413 .hidden = query.hidden, 2414 } }); 2415 }, 2416 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{ 2417 .path = path, 2418 .file = file, 2419 .needed = query.needed, 2420 .weak = query.weak, 2421 .reexport = query.reexport, 2422 } }), 2423 } 2424 return .ok; 2425 } 2426 2427 fn resolvePathInput( 2428 gpa: Allocator, 2429 arena: Allocator, 2430 io: Io, 2431 /// Allocated with `gpa`. 2432 unresolved_inputs: *std.ArrayList(UnresolvedInput), 2433 /// Allocated with `gpa`. 2434 resolved_inputs: *std.ArrayList(Input), 2435 /// Allocated via `gpa`. 2436 ld_script_bytes: *std.ArrayList(u8), 2437 /// Allocated via `gpa`. 2438 archive_dedup: *ArchiveDedupMap, 2439 target: *const std.Target, 2440 pq: UnresolvedInput.PathQuery, 2441 color: std.zig.Color, 2442 ) Allocator.Error!?ResolveLibInputResult { 2443 switch (Compilation.classifyFileExt(pq.path.sub_path)) { 2444 .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .static, color), 2445 .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .dynamic, color), 2446 .object => { 2447 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| 2448 fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) }); 2449 errdefer file.close(io); 2450 try resolved_inputs.append(gpa, .{ .object = .{ 2451 .path = pq.path, 2452 .file = file, 2453 .must_link = pq.query.must_link, 2454 .hidden = pq.query.hidden, 2455 } }); 2456 return null; 2457 }, 2458 .res => { 2459 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| 2460 fatal("failed to open windows resource {f}: {s}", .{ pq.path, @errorName(err) }); 2461 errdefer file.close(io); 2462 try resolved_inputs.append(gpa, .{ .res = .{ 2463 .path = pq.path, 2464 .file = file, 2465 } }); 2466 return null; 2467 }, 2468 else => fatal("{f}: unrecognized file extension", .{pq.path}), 2469 } 2470 } 2471 2472 fn resolvePathInputLib( 2473 gpa: Allocator, 2474 arena: Allocator, 2475 io: Io, 2476 /// Allocated with `gpa`. 2477 unresolved_inputs: *std.ArrayList(UnresolvedInput), 2478 /// Allocated with `gpa`. 2479 resolved_inputs: *std.ArrayList(Input), 2480 /// Allocated via `gpa`. 2481 ld_script_bytes: *std.ArrayList(u8), 2482 /// Allocated via `gpa`. 2483 archive_dedup: *ArchiveDedupMap, 2484 target: *const std.Target, 2485 pq: UnresolvedInput.PathQuery, 2486 link_mode: std.lang.LinkMode, 2487 color: std.zig.Color, 2488 ) Allocator.Error!ResolveLibInputResult { 2489 try resolved_inputs.ensureUnusedCapacity(gpa, 1); 2490 try archive_dedup.ensureUnusedCapacity(gpa, 1); 2491 2492 const test_path: Path = pq.path; 2493 // In the case of shared libraries, they might actually be "linker scripts" 2494 // that contain references to other libraries. 2495 if (pq.query.allow_so_scripts and target.ofmt == .elf and switch (Compilation.classifyFileExt(test_path.sub_path)) { 2496 .static_library, .shared_library => true, 2497 else => false, 2498 }) { 2499 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { 2500 error.FileNotFound => return .no_match, 2501 else => |e| fatal("unable to search for {t} library '{f}': {t}", .{ 2502 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e, 2503 }), 2504 }; 2505 errdefer file.close(io); 2506 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len)); 2507 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err| 2508 fatal("failed to read '{f}': {t}", .{ std.fmt.alt(test_path, .formatEscapeChar), err }); 2509 const buf = ld_script_bytes.items[0..n]; 2510 if (mem.startsWith(u8, buf, std.elf.MAGIC) or 2511 mem.startsWith(u8, buf, std.elf.ARMAG) or 2512 mem.startsWith(u8, buf, std.elf.ARMAG_THIN)) 2513 { 2514 // Appears to be an ELF or archive file. 2515 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); 2516 } 2517 const stat = file.stat(io) catch |err| 2518 fatal("failed to stat {f}: {t}", .{ test_path, err }); 2519 const size = std.math.cast(u32, stat.size) orelse 2520 fatal("{f}: linker script too big", .{test_path}); 2521 try ld_script_bytes.resize(gpa, size); 2522 const buf2 = ld_script_bytes.items[n..]; 2523 const n2 = file.readPositionalAll(io, buf2, n) catch |err| 2524 fatal("failed to read {f}: {t}", .{ test_path, err }); 2525 if (n2 != buf2.len) fatal("failed to read {f}: unexpected end of file", .{test_path}); 2526 2527 // This `Io` is only used for a mutex, and we know we aren't doing anything async/concurrent. 2528 var threaded: Io.Threaded = .init_single_threaded; 2529 defer threaded.deinit(); 2530 var diags: Diags = .init(gpa, threaded.io()); 2531 defer diags.deinit(); 2532 2533 const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items); 2534 if (diags.hasErrors()) { 2535 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 2536 try wip_errors.init(gpa); 2537 defer wip_errors.deinit(); 2538 2539 try diags.addMessagesToBundle(&wip_errors, null); 2540 2541 var error_bundle = try wip_errors.toOwnedBundle(""); 2542 defer error_bundle.deinit(gpa); 2543 2544 error_bundle.renderToStderr(io, .{}, color) catch {}; 2545 std.process.exit(1); 2546 } 2547 2548 var ld_script = ld_script_result catch |err| 2549 fatal("{f}: failed to parse linker script: {t}", .{ test_path, err }); 2550 defer ld_script.deinit(gpa); 2551 2552 try unresolved_inputs.ensureUnusedCapacity(gpa, ld_script.args.len); 2553 for (ld_script.args) |arg| { 2554 const query: UnresolvedInput.Query = .{ 2555 .needed = arg.needed or pq.query.needed, 2556 .weak = pq.query.weak, 2557 .reexport = pq.query.reexport, 2558 .preferred_mode = pq.query.preferred_mode, 2559 .search_strategy = pq.query.search_strategy, 2560 .allow_so_scripts = pq.query.allow_so_scripts, 2561 }; 2562 if (mem.startsWith(u8, arg.path, "-l")) { 2563 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{ 2564 .name = try arena.dupe(u8, arg.path["-l".len..]), 2565 .query = query, 2566 } }); 2567 } else { 2568 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{ 2569 .name = try arena.dupe(u8, arg.path), 2570 .query = query, 2571 } }); 2572 } 2573 } 2574 file.close(io); 2575 return .ok; 2576 } 2577 2578 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { 2579 error.FileNotFound => return .no_match, 2580 else => |e| fatal("unable to search for {s} library {f}: {s}", .{ 2581 @tagName(link_mode), test_path, @errorName(e), 2582 }), 2583 }; 2584 errdefer file.close(io); 2585 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); 2586 } 2587 2588 pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object { 2589 var file = try path.root_dir.handle.openFile(io, path.sub_path, .{}); 2590 errdefer file.close(io); 2591 return .{ 2592 .path = path, 2593 .file = file, 2594 .must_link = must_link, 2595 .hidden = hidden, 2596 }; 2597 } 2598 2599 pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso { 2600 var file = try path.root_dir.handle.openFile(io, path.sub_path, .{}); 2601 errdefer file.close(io); 2602 return .{ 2603 .path = path, 2604 .file = file, 2605 .needed = needed, 2606 .weak = weak, 2607 .reexport = reexport, 2608 }; 2609 } 2610 2611 pub fn openObjectInput(io: Io, diags: *Diags, path: Path) error{AlreadyReported}!Input { 2612 return .{ .object = openObject(io, path, false, false) catch |err| { 2613 return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); 2614 } }; 2615 } 2616 2617 pub fn openArchiveInput(io: Io, diags: *Diags, path: Path, must_link: bool, hidden: bool) error{AlreadyReported}!Input { 2618 return .{ .archive = openObject(io, path, must_link, hidden) catch |err| { 2619 return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); 2620 } }; 2621 } 2622 2623 pub fn openDsoInput(io: Io, diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{AlreadyReported}!Input { 2624 return .{ .dso = openDso(io, path, needed, weak, reexport) catch |err| { 2625 return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); 2626 } }; 2627 } 2628 2629 /// Returns true if and only if there is at least one input of type object, 2630 /// archive, or Windows resource file. 2631 pub fn anyObjectInputs(inputs: []const Input) bool { 2632 return countObjectInputs(inputs) != 0; 2633 } 2634 2635 /// Returns the number of inputs of type object, archive, or Windows resource file. 2636 pub fn countObjectInputs(inputs: []const Input) usize { 2637 var count: usize = 0; 2638 for (inputs) |input| switch (input) { 2639 .dso, .dso_exact => continue, 2640 .res, .object, .archive => count += 1, 2641 }; 2642 return count; 2643 } 2644 2645 /// Returns the first input of type object or archive. 2646 pub fn firstObjectInput(inputs: []const Input) ?Input.Object { 2647 for (inputs) |input| switch (input) { 2648 .object, .archive => |obj| return obj, 2649 .res, .dso, .dso_exact => continue, 2650 }; 2651 return null; 2652 } 2653