| ... | ... | @@ -44,6 +44,12 @@ shndx: struct { |
| 44 | 44 | fini_array: Section.Index, |
| 45 | 45 | preinit_array: Section.Index, |
| 46 | 46 | }, |
| 47 | dynamic: struct { |
| 48 | flags: u32, |
| 49 | flags_1: u32, |
| 50 | rpath: String(.dynstr), |
| 51 | soname: String(.dynstr), |
| 52 | }, |
| 47 | 53 | symtab: std.ArrayList(Symbol), |
| 48 | 54 | globals: struct { |
| 49 | 55 | strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global), |
| ... | ... | @@ -58,7 +64,7 @@ copied_globals: std.array_hash_map.Auto(String(.strtab), struct { |
| 58 | 64 | rela_index: Section.RelaIndex, |
| 59 | 65 | }), |
| 60 | 66 | /// Key is the name of an undef global for which we would *like* to create a copy relocation |
| 61 | | /// (`R_*_COPY`), but cannot because we have not seen an appropriate definition in a linked DSO yet. |
| 67 | /// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet. |
| 62 | 68 | /// |
| 63 | 69 | /// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we |
| 64 | 70 | /// will remove it from this map and call `maybeAddCopyRelocation`. |
| ... | ... | @@ -165,6 +171,9 @@ changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), |
| 165 | 171 | /// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`. |
| 166 | 172 | textrel_count: u32, |
| 167 | 173 | |
| 174 | overflowed_reloc_count: u32, |
| 175 | misaligned_reloc_count: u32, |
| 176 | |
| 168 | 177 | const_prog_node: std.Progress.Node, |
| 169 | 178 | synth_prog_node: std.Progress.Node, |
| 170 | 179 | input_prog_node: std.Progress.Node, |
| ... | ... | @@ -486,6 +495,12 @@ const Section = struct { |
| 486 | 495 | }; |
| 487 | 496 | } |
| 488 | 497 | |
| 498 | fn size(s: Index, elf: *Elf) u64 { |
| 499 | return switch (elf.shdrPtr(s)) { |
| 500 | inline else => |shdr| elf.targetLoad(&shdr.size), |
| 501 | }; |
| 502 | } |
| 503 | |
| 489 | 504 | fn flags(s: Index, elf: *Elf) std.elf.SHF { |
| 490 | 505 | return switch (elf.shdrPtr(s)) { |
| 491 | 506 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, |
| ... | ... | @@ -769,36 +784,111 @@ const GotReloc = struct { |
| 769 | 784 | target: GotKey, |
| 770 | 785 | addend: i64, |
| 771 | 786 | type: GotReloc.Type, |
| 787 | result: enum(u8) { ok, overflowed, misaligned }, |
| 788 | |
| 789 | /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target` |
| 790 | /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview. |
| 791 | const Type = packed struct(u16) { |
| 792 | fn simple(target: Target, action: Simple) GotReloc.Type { |
| 793 | assert(target != .special); |
| 794 | return .{ .target = target, .action = .{ .simple = action } }; |
| 795 | } |
| 772 | 796 | |
| 773 | | const deleted: GotReloc = .{ |
| 774 | | .node = .none, |
| 775 | | .offset = undefined, |
| 776 | | .target = undefined, |
| 777 | | .addend = undefined, |
| 778 | | .type = undefined, |
| 779 | | }; |
| 797 | fn special(s: Special) GotReloc.Type { |
| 798 | return .{ .target = .special, .action = .{ .special = s } }; |
| 799 | } |
| 800 | |
| 801 | target: Target, |
| 802 | action: packed union { |
| 803 | simple: Simple, |
| 804 | special: Special, |
| 805 | }, |
| 780 | 806 | |
| 781 | | const Type = enum(u8) { |
| 782 | | offset32, |
| 783 | | offset64, |
| 784 | | rel32, |
| 785 | | rel64, |
| 786 | | |
| 787 | | larch_rel32_hi20, |
| 788 | | larch_rel64_lo20, |
| 789 | | larch_rel64_hi12, |
| 790 | | larch_abs32_lo12, |
| 791 | | larch_abs32_hi20, |
| 792 | | larch_abs64_lo20, |
| 793 | | larch_abs64_hi12, |
| 794 | | |
| 795 | | sparc_10, |
| 796 | | sparc_13, |
| 797 | | sparc_22, |
| 798 | | sparc_ldm_hi22, |
| 799 | | sparc_ldm_lo10, |
| 800 | | sparc_op_hix22, |
| 801 | | sparc_op_lox10, |
| 807 | /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there |
| 808 | /// are fewer different kinds of GOT relocation. |
| 809 | const Target = enum(u3) { |
| 810 | /// This is a "special" relocation whose specific type is in the `action.special` field. |
| 811 | special, |
| 812 | |
| 813 | /// Absolute address of the GOT entry. |
| 814 | abs, |
| 815 | /// Offset from the relocation itself to the GOT entry ("PC-relative"). |
| 816 | rel, |
| 817 | /// Offset from the base of the GOT to the GOT entry. |
| 818 | offset, |
| 819 | }; |
| 820 | |
| 821 | const Simple = SymbolReloc.Type.Simple; |
| 822 | |
| 823 | /// Like `SymbolReloc.Special`, but for GOT relocations. |
| 824 | const Special = enum(u13) { |
| 825 | larch_pcala_hi20, |
| 826 | larch_pcala64_lo20, |
| 827 | larch_pcala64_hi12, |
| 828 | |
| 829 | sparc_op_lox10, |
| 830 | sparc_op_hix22, |
| 831 | |
| 832 | fn applyInner( |
| 833 | s: Special, |
| 834 | elf: *Elf, |
| 835 | got_vaddr: u64, |
| 836 | got_offset: u64, |
| 837 | addend: u64, |
| 838 | dest_vaddr: u64, |
| 839 | dest_slice: []u8, |
| 840 | ) error{ RelocationMisaligned, RelocationOverflow }!void { |
| 841 | switch (s) { |
| 842 | .larch_pcala_hi20 => { |
| 843 | const val = got_vaddr +% got_offset +% addend; |
| 844 | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 845 | elf.targetStore(inst, .{ |
| 846 | .b0_4 = elf.targetLoad(inst).b0_4, |
| 847 | .j20 = link.loongarch.pcalaHi20(val, dest_vaddr), |
| 848 | .b25_31 = elf.targetLoad(inst).b25_31, |
| 849 | }); |
| 850 | }, |
| 851 | .larch_pcala64_lo20 => { |
| 852 | const val = got_vaddr +% got_offset +% addend; |
| 853 | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 854 | elf.targetStore(inst, .{ |
| 855 | .b0_4 = elf.targetLoad(inst).b0_4, |
| 856 | .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr), |
| 857 | .b25_31 = elf.targetLoad(inst).b25_31, |
| 858 | }); |
| 859 | }, |
| 860 | .larch_pcala64_hi12 => { |
| 861 | const val = got_vaddr +% got_offset +% addend; |
| 862 | const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); |
| 863 | elf.targetStore(inst, .{ |
| 864 | .b0_9 = elf.targetLoad(inst).b0_9, |
| 865 | .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr), |
| 866 | .b22_31 = elf.targetLoad(inst).b22_31, |
| 867 | }); |
| 868 | }, |
| 869 | .sparc_op_lox10 => { |
| 870 | const dest_ptr: *align(1) packed struct(u32) { |
| 871 | imm13: u13, |
| 872 | b13_31: u19, |
| 873 | } = @ptrCast(dest_slice); |
| 874 | elf.targetStore(dest_ptr, .{ |
| 875 | .imm13 = @as(u10, @truncate(got_offset)), |
| 876 | .b13_31 = elf.targetLoad(dest_ptr).b13_31, |
| 877 | }); |
| 878 | }, |
| 879 | .sparc_op_hix22 => { |
| 880 | const dest_ptr: *align(1) packed struct(u32) { |
| 881 | imm22: u22, |
| 882 | b22_31: u10, |
| 883 | } = @ptrCast(dest_slice); |
| 884 | elf.targetStore(dest_ptr, .{ |
| 885 | .imm22 = @truncate(got_offset >> 10), |
| 886 | .b22_31 = elf.targetLoad(dest_ptr).b22_31, |
| 887 | }); |
| 888 | }, |
| 889 | } |
| 890 | } |
| 891 | }; |
| 802 | 892 | }; |
| 803 | 893 | |
| 804 | 894 | const Index = enum(u32) { |
| ... | ... | @@ -810,14 +900,34 @@ const GotReloc = struct { |
| 810 | 900 | } |
| 811 | 901 | }; |
| 812 | 902 | |
| 813 | | fn apply(reloc: *const GotReloc, elf: *Elf) void { |
| 814 | | assert(elf.ehdrField(.type) != .REL); |
| 903 | fn apply(reloc: *GotReloc, elf: *Elf) void { |
| 904 | assert(elf.ehdrType() != .REL); |
| 815 | 905 | if (reloc.node == .none) return; // deleted |
| 816 | 906 | if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { |
| 817 | 907 | // There's no point applying the relocation now, because it will be re-applied by |
| 818 | 908 | // `flushMoved` at some point anyway. |
| 819 | 909 | return; |
| 820 | 910 | } |
| 911 | switch (reloc.result) { |
| 912 | .ok => {}, |
| 913 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 914 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 915 | } |
| 916 | if (reloc.applyInner(elf)) { |
| 917 | @branchHint(.likely); |
| 918 | reloc.result = .ok; |
| 919 | } else |err| switch (err) { |
| 920 | error.RelocationOverflow => { |
| 921 | reloc.result = .overflowed; |
| 922 | elf.overflowed_reloc_count += 1; |
| 923 | }, |
| 924 | error.RelocationMisaligned => { |
| 925 | reloc.result = .misaligned; |
| 926 | elf.misaligned_reloc_count += 1; |
| 927 | }, |
| 928 | } |
| 929 | } |
| 930 | fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { |
| 821 | 931 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 822 | 932 | .file => unreachable, |
| 823 | 933 | .ehdr => unreachable, |
| ... | ... | @@ -834,7 +944,7 @@ const GotReloc = struct { |
| 834 | 944 | }; |
| 835 | 945 | const dest_vaddr = node_vaddr + reloc.offset; |
| 836 | 946 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 837 | | const target_endian = elf.targetEndian(); |
| 947 | |
| 838 | 948 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 839 | 949 | const got_index: u64 = elf.got.getIndex(reloc.target).?; |
| 840 | 950 | const got_offset: u64 = switch (elf.identClass()) { |
| ... | ... | @@ -842,228 +952,201 @@ const GotReloc = struct { |
| 842 | 952 | inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, |
| 843 | 953 | }; |
| 844 | 954 | const addend: u64 = @bitCast(reloc.addend); |
| 845 | | switch (reloc.type) { |
| 846 | | .offset64 => std.mem.writeInt( |
| 847 | | u64, |
| 848 | | dest_slice[0..8], |
| 849 | | got_offset +% addend, |
| 850 | | target_endian, |
| 851 | | ), |
| 852 | | .offset32 => std.mem.writeInt( |
| 853 | | u32, |
| 854 | | dest_slice[0..4], |
| 855 | | @intCast(got_offset +% addend), |
| 856 | | target_endian, |
| 857 | | ), |
| 858 | | .rel64 => std.mem.writeInt( |
| 859 | | i64, |
| 860 | | dest_slice[0..8], |
| 861 | | @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr), |
| 862 | | target_endian, |
| 863 | | ), |
| 864 | | .rel32 => std.mem.writeInt( |
| 865 | | i32, |
| 866 | | dest_slice[0..4], |
| 867 | | @intCast(@as(i64, @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr))), |
| 868 | | target_endian, |
| 869 | | ), |
| 870 | 955 | |
| 871 | | .larch_rel32_hi20 => { |
| 872 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 873 | | const target_value = got_vaddr +% got_offset +% addend; |
| 874 | | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr)); |
| 875 | | }, |
| 876 | | .larch_rel64_lo20 => { |
| 877 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 878 | | const target_value = got_vaddr +% got_offset +% addend; |
| 879 | | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr)); |
| 880 | | }, |
| 881 | | .larch_rel64_hi12 => { |
| 882 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 883 | | const target_value = got_vaddr +% got_offset +% addend; |
| 884 | | link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr)); |
| 885 | | }, |
| 886 | | .larch_abs32_lo12 => { |
| 887 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 888 | | const target_value = got_vaddr +% got_offset +% addend; |
| 889 | | link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value)); |
| 890 | | }, |
| 891 | | .larch_abs32_hi20 => { |
| 892 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 893 | | const target_value = got_vaddr +% got_offset +% addend; |
| 894 | | link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 12)); |
| 895 | | }, |
| 896 | | .larch_abs64_lo20 => { |
| 897 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 898 | | const target_value = got_vaddr +% got_offset +% addend; |
| 899 | | link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 32)); |
| 900 | | }, |
| 901 | | .larch_abs64_hi12 => { |
| 902 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 903 | | const target_value = got_vaddr +% got_offset +% addend; |
| 904 | | link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value >> 52)); |
| 905 | | }, |
| 956 | const target_val: u64 = switch (reloc.type.target) { |
| 957 | .abs => got_vaddr +% got_offset +% addend, |
| 958 | .rel => got_vaddr +% got_offset +% addend -% dest_vaddr, |
| 959 | .offset => got_offset +% addend, |
| 960 | .special => return reloc.type.action.special.applyInner( |
| 961 | elf, |
| 962 | got_vaddr, |
| 963 | got_offset, |
| 964 | addend, |
| 965 | dest_vaddr, |
| 966 | dest_slice, |
| 967 | ), |
| 968 | }; |
| 969 | try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); |
| 970 | } |
| 906 | 971 | |
| 907 | | .sparc_10 => { |
| 908 | | const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice)); |
| 909 | | var result = elf.targetLoad(dest_ptr); |
| 910 | | result.simm13 = @as(u10, @truncate(got_offset)); |
| 911 | | elf.targetStore(dest_ptr, result); |
| 912 | | }, |
| 913 | | .sparc_13 => { |
| 914 | | const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice)); |
| 915 | | var result = elf.targetLoad(dest_ptr); |
| 916 | | result.simm13 = @truncate(got_offset); |
| 917 | | elf.targetStore(dest_ptr, result); |
| 918 | | }, |
| 919 | | .sparc_22 => { |
| 920 | | const dest_ptr: *link.sparc.reloc.Simm22 = @ptrCast(@alignCast(dest_slice)); |
| 921 | | var result = elf.targetLoad(dest_ptr); |
| 922 | | result.simm22 = @truncate(got_offset >> 10); |
| 923 | | elf.targetStore(dest_ptr, result); |
| 924 | | }, |
| 925 | | .sparc_ldm_hi22 => { |
| 926 | | const dest_ptr: *link.sparc.reloc.Simm22 = @ptrCast(@alignCast(dest_slice)); |
| 927 | | var result = elf.targetLoad(dest_ptr); |
| 928 | | result.simm22 = @truncate((got_offset +% addend) >> 10); |
| 929 | | elf.targetStore(dest_ptr, result); |
| 930 | | }, |
| 931 | | .sparc_ldm_lo10 => { |
| 932 | | const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice)); |
| 933 | | var result = elf.targetLoad(dest_ptr); |
| 934 | | result.simm13 = @as(u10, @truncate(got_offset +% addend)); |
| 935 | | elf.targetStore(dest_ptr, result); |
| 936 | | }, |
| 937 | | .sparc_op_hix22 => { |
| 938 | | const dest_ptr: *link.sparc.reloc.Imm22 = @ptrCast(@alignCast(dest_slice)); |
| 939 | | var result = elf.targetLoad(dest_ptr); |
| 940 | | result.imm22 = @truncate(got_offset >> 10); |
| 941 | | elf.targetStore(dest_ptr, result); |
| 942 | | }, |
| 943 | | .sparc_op_lox10 => { |
| 944 | | const dest_ptr: *link.sparc.reloc.Imm13 = @ptrCast(@alignCast(dest_slice)); |
| 945 | | var result = elf.targetLoad(dest_ptr); |
| 946 | | result.imm13 = @as(u10, @truncate(got_offset)); |
| 947 | | elf.targetStore(dest_ptr, result); |
| 948 | | }, |
| 972 | fn delete(reloc: *GotReloc, elf: *Elf) void { |
| 973 | switch (reloc.result) { |
| 974 | .ok => {}, |
| 975 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 976 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 949 | 977 | } |
| 978 | reloc.* = .{ |
| 979 | .node = .none, |
| 980 | .offset = undefined, |
| 981 | .target = undefined, |
| 982 | .addend = undefined, |
| 983 | .type = undefined, |
| 984 | .result = undefined, |
| 985 | }; |
| 950 | 986 | } |
| 951 | 987 | }; |
| 952 | 988 | |
| 953 | 989 | pub const MachineRelocType = union { |
| 954 | 990 | AARCH64: std.elf.R_AARCH64, |
| 955 | | LOONGARCH: std.elf.R_LARCH, |
| 991 | LARCH: std.elf.R_LARCH, |
| 956 | 992 | PPC64: std.elf.R_PPC64, |
| 957 | 993 | RISCV: std.elf.R_RISCV, |
| 958 | 994 | SPARC: std.elf.R_SPARC, |
| 959 | 995 | X86_64: std.elf.R_X86_64, |
| 960 | 996 | |
| 961 | | pub fn none(elf: *Elf) MachineRelocType { |
| 962 | | return switch (elf.ehdrField(.machine)) { |
| 963 | | else => unreachable, |
| 997 | pub const Format = struct { |
| 998 | rt: MachineRelocType, |
| 999 | elf: *const Elf, |
| 1000 | |
| 1001 | pub fn format(f: Format, w: *Io.Writer) Io.Writer.Error!void { |
| 1002 | switch (f.elf.ehdrMachine()) { |
| 1003 | .AARCH64 => try w.print("R_AARCH64_{t}", .{f.rt.AARCH64}), |
| 1004 | .LOONGARCH => try w.print("R_LARCH_{t}", .{f.rt.LARCH}), |
| 1005 | .PPC64 => try w.print("R_PPC64_{t}", .{f.rt.PPC64}), |
| 1006 | .RISCV => try w.print("R_RISCV_{t}", .{f.rt.RISCV}), |
| 1007 | .SPARCV9 => try w.print("R_SPARC_{t}", .{f.rt.SPARC}), |
| 1008 | .X86_64 => try w.print("R_X86_64_{t}", .{f.rt.X86_64}), |
| 1009 | } |
| 1010 | } |
| 1011 | }; |
| 1012 | |
| 1013 | pub fn fmt(rt: MachineRelocType, elf: *const Elf) Format { |
| 1014 | return .{ .rt = rt, .elf = elf }; |
| 1015 | } |
| 1016 | |
| 1017 | pub fn none(elf: *const Elf) MachineRelocType { |
| 1018 | return switch (elf.ehdrMachine()) { |
| 964 | 1019 | .AARCH64 => .{ .AARCH64 = .NONE }, |
| 965 | | .LOONGARCH => .{ .LOONGARCH = .NONE }, |
| 1020 | .LOONGARCH => .{ .LARCH = .NONE }, |
| 966 | 1021 | .PPC64 => .{ .PPC64 = .NONE }, |
| 967 | 1022 | .RISCV => .{ .RISCV = .NONE }, |
| 968 | 1023 | .SPARCV9 => .{ .SPARC = .NONE }, |
| 969 | 1024 | .X86_64 => .{ .X86_64 = .NONE }, |
| 970 | 1025 | }; |
| 971 | 1026 | } |
| 972 | | pub fn copy(elf: *Elf) MachineRelocType { |
| 973 | | return switch (elf.ehdrField(.machine)) { |
| 974 | | else => unreachable, |
| 1027 | pub fn copy(elf: *const Elf) MachineRelocType { |
| 1028 | return switch (elf.ehdrMachine()) { |
| 975 | 1029 | .AARCH64 => .{ .AARCH64 = .COPY }, |
| 976 | | .LOONGARCH => .{ .LOONGARCH = .COPY }, |
| 1030 | .LOONGARCH => .{ .LARCH = .COPY }, |
| 977 | 1031 | .PPC64 => .{ .PPC64 = .COPY }, |
| 978 | 1032 | .RISCV => .{ .RISCV = .COPY }, |
| 979 | 1033 | .SPARCV9 => .{ .SPARC = .COPY }, |
| 980 | 1034 | .X86_64 => .{ .X86_64 = .COPY }, |
| 981 | 1035 | }; |
| 982 | 1036 | } |
| 983 | | pub fn relative(elf: *Elf) MachineRelocType { |
| 984 | | return switch (elf.ehdrField(.machine)) { |
| 985 | | else => unreachable, |
| 1037 | pub fn relative(elf: *const Elf) MachineRelocType { |
| 1038 | return switch (elf.ehdrMachine()) { |
| 986 | 1039 | .AARCH64 => .{ .AARCH64 = .RELATIVE }, |
| 987 | | .LOONGARCH => .{ .LOONGARCH = .RELATIVE }, |
| 1040 | .LOONGARCH => .{ .LARCH = .RELATIVE }, |
| 988 | 1041 | .PPC64 => .{ .PPC64 = .RELATIVE }, |
| 989 | 1042 | .RISCV => .{ .RISCV = .RELATIVE }, |
| 990 | 1043 | .SPARCV9 => .{ .SPARC = .RELATIVE }, |
| 991 | 1044 | .X86_64 => .{ .X86_64 = .RELATIVE }, |
| 992 | 1045 | }; |
| 993 | 1046 | } |
| 994 | | pub fn jumpSlot(elf: *Elf) MachineRelocType { |
| 995 | | return switch (elf.ehdrField(.machine)) { |
| 996 | | else => unreachable, |
| 1047 | pub fn jumpSlot(elf: *const Elf) MachineRelocType { |
| 1048 | return switch (elf.ehdrMachine()) { |
| 997 | 1049 | .AARCH64 => .{ .AARCH64 = .JUMP_SLOT }, |
| 998 | | .LOONGARCH => .{ .LOONGARCH = .JUMP_SLOT }, |
| 1050 | .LOONGARCH => .{ .LARCH = .JUMP_SLOT }, |
| 999 | 1051 | .PPC64 => .{ .PPC64 = .JMP_SLOT }, |
| 1000 | 1052 | .RISCV => .{ .RISCV = .JUMP_SLOT }, |
| 1001 | 1053 | .SPARCV9 => .{ .SPARC = .JMP_SLOT }, |
| 1002 | 1054 | .X86_64 => .{ .X86_64 = .JUMP_SLOT }, |
| 1003 | 1055 | }; |
| 1004 | 1056 | } |
| 1005 | | pub fn globDat(elf: *Elf) MachineRelocType { |
| 1006 | | return switch (elf.ehdrField(.machine)) { |
| 1007 | | else => unreachable, |
| 1057 | pub fn globDat(elf: *const Elf) MachineRelocType { |
| 1058 | return switch (elf.ehdrMachine()) { |
| 1008 | 1059 | .AARCH64 => .{ .AARCH64 = .GLOB_DAT }, |
| 1009 | | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1060 | .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1010 | 1061 | .PPC64 => .{ .PPC64 = .GLOB_DAT }, |
| 1011 | 1062 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1012 | 1063 | .SPARCV9 => .{ .SPARC = .GLOB_DAT }, |
| 1013 | 1064 | .X86_64 => .{ .X86_64 = .GLOB_DAT }, |
| 1014 | 1065 | }; |
| 1015 | 1066 | } |
| 1016 | | pub fn dtpOffAddr(elf: *Elf) MachineRelocType { |
| 1017 | | return switch (elf.ehdrField(.machine)) { |
| 1018 | | else => unreachable, |
| 1019 | | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, |
| 1067 | pub fn dtpMod(elf: *const Elf) MachineRelocType { |
| 1068 | return switch (elf.ehdrMachine()) { |
| 1069 | .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPMOD else .P32_TLS_DTPMOD }, |
| 1070 | .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 1071 | .PPC64 => .{ .PPC64 = .DTPMOD64 }, |
| 1072 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 1073 | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 1074 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, |
| 1075 | }; |
| 1076 | } |
| 1077 | pub fn dtpOff(elf: *const Elf) MachineRelocType { |
| 1078 | return switch (elf.ehdrMachine()) { |
| 1079 | .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPREL else .P32_TLS_DTPREL }, |
| 1080 | .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, |
| 1020 | 1081 | .PPC64 => .{ .PPC64 = .DTPREL64 }, |
| 1021 | 1082 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, |
| 1022 | 1083 | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPOFF64 else .TLS_DTPOFF32 }, |
| 1023 | | .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .DTPOFF64 else .DTPOFF32 }, |
| 1084 | .X86_64 => .{ .X86_64 = .DTPOFF64 }, |
| 1024 | 1085 | }; |
| 1025 | 1086 | } |
| 1026 | | pub fn absAddr(elf: *Elf) MachineRelocType { |
| 1027 | | return switch (elf.ehdrField(.machine)) { |
| 1028 | | else => unreachable, |
| 1087 | pub fn tpOff(elf: *const Elf) MachineRelocType { |
| 1088 | return switch (elf.ehdrMachine()) { |
| 1089 | .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_TPREL else .P32_TLS_TPREL }, |
| 1090 | .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, |
| 1091 | .PPC64 => .{ .PPC64 = .TPREL64 }, |
| 1092 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, |
| 1093 | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_TPOFF64 else .TLS_TPOFF32 }, |
| 1094 | .X86_64 => .{ .X86_64 = .TPOFF64 }, |
| 1095 | }; |
| 1096 | } |
| 1097 | pub fn absAddr(elf: *const Elf) MachineRelocType { |
| 1098 | return switch (elf.ehdrMachine()) { |
| 1029 | 1099 | .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .ABS64 else .P32_ABS32 }, |
| 1030 | | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1100 | .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1031 | 1101 | .PPC64 => .{ .PPC64 = .ADDR64 }, |
| 1032 | 1102 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1033 | 1103 | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1034 | 1104 | .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1035 | 1105 | }; |
| 1036 | 1106 | } |
| 1037 | | pub fn sizeAddr(elf: *Elf) MachineRelocType { |
| 1038 | | return switch (elf.ehdrField(.machine)) { |
| 1039 | | else => unreachable, |
| 1040 | | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .SIZE64 else .SIZE32 }, |
| 1041 | | .X86_64 => .{ .X86_64 = .SIZE64 }, |
| 1042 | | }; |
| 1043 | | } |
| 1044 | | |
| 1045 | | pub fn wrap(int: u32, elf: *Elf) MachineRelocType { |
| 1046 | | return switch (elf.ehdrField(.machine)) { |
| 1047 | | else => unreachable, |
| 1048 | | .SPARCV9 => .{ .SPARC = @enumFromInt(int) }, |
| 1049 | | inline .AARCH64, |
| 1107 | pub fn size32(elf: *const Elf) ?MachineRelocType { |
| 1108 | return switch (elf.ehdrMachine()) { |
| 1109 | .AARCH64, |
| 1050 | 1110 | .LOONGARCH, |
| 1051 | 1111 | .PPC64, |
| 1052 | 1112 | .RISCV, |
| 1053 | | .X86_64, |
| 1054 | | => |machine| @unionInit(MachineRelocType, @tagName(machine), @enumFromInt(int)), |
| 1113 | => null, |
| 1114 | |
| 1115 | .SPARCV9 => .{ .SPARC = .SIZE32 }, |
| 1116 | .X86_64 => .{ .X86_64 = .SIZE32 }, |
| 1055 | 1117 | }; |
| 1056 | 1118 | } |
| 1057 | | pub fn unwrap(rt: MachineRelocType, elf: *Elf) u32 { |
| 1058 | | return switch (elf.ehdrField(.machine)) { |
| 1059 | | else => unreachable, |
| 1060 | | .SPARCV9 => @intFromEnum(rt.SPARC), |
| 1061 | | inline .AARCH64, |
| 1119 | pub fn size64(elf: *const Elf) ?MachineRelocType { |
| 1120 | return switch (elf.ehdrMachine()) { |
| 1121 | .AARCH64, |
| 1062 | 1122 | .LOONGARCH, |
| 1063 | 1123 | .PPC64, |
| 1064 | 1124 | .RISCV, |
| 1065 | | .X86_64, |
| 1066 | | => |machine| @intFromEnum(@field(rt, @tagName(machine))), |
| 1125 | => null, |
| 1126 | |
| 1127 | .SPARCV9 => .{ .SPARC = .SIZE64 }, |
| 1128 | .X86_64 => .{ .X86_64 = .SIZE64 }, |
| 1129 | }; |
| 1130 | } |
| 1131 | |
| 1132 | pub fn wrap(int: u32, elf: *const Elf) MachineRelocType { |
| 1133 | return switch (elf.ehdrMachine()) { |
| 1134 | .AARCH64 => .{ .AARCH64 = @enumFromInt(int) }, |
| 1135 | .LOONGARCH => .{ .LARCH = @enumFromInt(int) }, |
| 1136 | .PPC64 => .{ .PPC64 = @enumFromInt(int) }, |
| 1137 | .RISCV => .{ .RISCV = @enumFromInt(int) }, |
| 1138 | .SPARCV9 => .{ .SPARC = @enumFromInt(int) }, |
| 1139 | .X86_64 => .{ .X86_64 = @enumFromInt(int) }, |
| 1140 | }; |
| 1141 | } |
| 1142 | pub fn unwrap(rt: MachineRelocType, elf: *const Elf) u32 { |
| 1143 | return switch (elf.ehdrMachine()) { |
| 1144 | .AARCH64 => @intFromEnum(rt.AARCH64), |
| 1145 | .LOONGARCH => @intFromEnum(rt.LARCH), |
| 1146 | .PPC64 => @intFromEnum(rt.PPC64), |
| 1147 | .RISCV => @intFromEnum(rt.RISCV), |
| 1148 | .SPARCV9 => @intFromEnum(rt.SPARC), |
| 1149 | .X86_64 => @intFromEnum(rt.X86_64), |
| 1067 | 1150 | }; |
| 1068 | 1151 | } |
| 1069 | 1152 | }; |
| ... | ... | @@ -1082,6 +1165,8 @@ const SymbolReloc = struct { |
| 1082 | 1165 | /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`. |
| 1083 | 1166 | addend: i64, |
| 1084 | 1167 | /// Specifies how to apply the relocation. |
| 1168 | /// |
| 1169 | /// When emitting a relocatable, this field is `undefined`. |
| 1085 | 1170 | type: SymbolReloc.Type, |
| 1086 | 1171 | /// Forms a linked list of all symbol relocations with the same `target`. This list exists so |
| 1087 | 1172 | /// that all relocations targeting a particular symbol can be re-applied if that symbol moves. |
| ... | ... | @@ -1100,6 +1185,7 @@ const SymbolReloc = struct { |
| 1100 | 1185 | /// relocation entry. The entry will be removed if we discover a definition which allows us to |
| 1101 | 1186 | /// statically resolve the relocation. |
| 1102 | 1187 | rela_index: Section.RelaIndex.Optional, |
| 1188 | result: enum(u8) { ok, overflowed, misaligned }, |
| 1103 | 1189 | |
| 1104 | 1190 | /// Determines the section in which this relocation will be placed if it is outstanding. |
| 1105 | 1191 | /// |
| ... | ... | @@ -1110,8 +1196,7 @@ const SymbolReloc = struct { |
| 1110 | 1196 | /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt` |
| 1111 | 1197 | /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries. |
| 1112 | 1198 | fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index { |
| 1113 | | const shndx = switch (elf.ehdrField(.type)) { |
| 1114 | | .NONE, .CORE, _ => unreachable, |
| 1199 | const shndx = switch (elf.ehdrType()) { |
| 1115 | 1200 | .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx, |
| 1116 | 1201 | .EXEC, .DYN => elf.shndx.rela_dyn, |
| 1117 | 1202 | }; |
| ... | ... | @@ -1128,151 +1213,439 @@ const SymbolReloc = struct { |
| 1128 | 1213 | } |
| 1129 | 1214 | }; |
| 1130 | 1215 | |
| 1131 | | const Type = enum { |
| 1132 | | /// This input relocation is being directly forwarded to an `ElfN.Rela` entry in the output |
| 1133 | | /// file. `rela_index` is guaranteed to be populated. The ELF relocation type is available |
| 1134 | | /// in the `ElfN.Rela` entry. |
| 1135 | | /// |
| 1136 | | /// If we are emitting a relocatable (`ET_REL`), all symbol relocs use this type (since we |
| 1137 | | /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type. |
| 1138 | | write_rela, |
| 1216 | /// Instead of using the ELF relocation enums, we have our own internal representation for |
| 1217 | /// relocation types. This representation is more compact (requiring only 16 bits), and allows |
| 1218 | /// sharing a lot of relocation handling between multiple relocs and target architectures. |
| 1219 | /// |
| 1220 | /// A relocation type can be "simple" or "special". |
| 1221 | /// |
| 1222 | /// "Simple" relocations are designed to cover the majority of cases. They can represent most |
| 1223 | /// relocations which either write 8-bit, 16-bit, 32-bit, or 64-bit integers, or which write one |
| 1224 | /// contiguous bit-field within such an integer (e.g. an instruction operand). For more details, |
| 1225 | /// see `Simple`. |
| 1226 | /// |
| 1227 | /// "Special" relocations handle anything which does not fit into the above category, such as |
| 1228 | /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on |
| 1229 | /// a symbol value. The representation is simply a big enum containing all of these exceptional |
| 1230 | /// cases---see `Special`. This representation is in use when `Type.target == .special`. |
| 1231 | const Type = packed struct(u16) { |
| 1232 | /// Helper function for constructing a "simple" relocation type. This mainly exists to |
| 1233 | /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`. |
| 1234 | fn simple(target: Target, action: Simple) SymbolReloc.Type { |
| 1235 | assert(target != .special); |
| 1236 | return .{ .target = target, .action = .{ .simple = action } }; |
| 1237 | } |
| 1238 | |
| 1239 | /// Helper function for constructing a "special" relocation type. This mainly exists to |
| 1240 | /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`. |
| 1241 | fn special(s: Special) SymbolReloc.Type { |
| 1242 | return .{ .target = .special, .action = .{ .special = s } }; |
| 1243 | } |
| 1139 | 1244 | |
| 1140 | | /// Address relative to the DSO base. Like `.abs64` but does not emit `R_*_RELATIVE` relocs. |
| 1245 | /// See doc comment on `Target`. |
| 1246 | target: Target, |
| 1247 | /// If `target == .special`, the `special` field is used. |
| 1141 | 1248 | /// |
| 1142 | | /// This is only used targeting local symbols so can always be statically resolved. |
| 1143 | | dsorel64, |
| 1144 | | /// Address relative to the DSO base. Like `.abs32` but does not emit `R_*_RELATIVE` relocs. |
| 1249 | /// Otherwise, the `.simple` field is used. |
| 1250 | action: packed union { |
| 1251 | simple: Simple, |
| 1252 | special: Special, |
| 1253 | }, |
| 1254 | |
| 1255 | /// If a relocation is "special", indicates that using the value `.@"special"`. |
| 1145 | 1256 | /// |
| 1146 | | /// This is only used targeting local symbols so can always be statically resolved. |
| 1147 | | dsorel32, |
| 1148 | | |
| 1149 | | abs8, |
| 1150 | | abs16, |
| 1151 | | abs32, |
| 1152 | | abs32s, |
| 1153 | | abs64, |
| 1154 | | rel8, |
| 1155 | | rel16, |
| 1156 | | rel32, |
| 1157 | | rel64, |
| 1158 | | pltabs32, |
| 1159 | | pltabs64, |
| 1160 | | pltrel32, |
| 1161 | | pltrel64, |
| 1162 | | dtpoff32, |
| 1163 | | dtpoff64, |
| 1164 | | tpoff32, |
| 1165 | | tpoff64, |
| 1166 | | size32, |
| 1167 | | size64, |
| 1168 | | |
| 1169 | | larch_abs32_lo12, |
| 1170 | | larch_rel32_hi20, |
| 1171 | | larch_rel64_lo20, |
| 1172 | | larch_rel64_hi12, |
| 1173 | | larch_branch_rel18, |
| 1174 | | larch_branch_rel23, |
| 1175 | | larch_branch_rel28, |
| 1176 | | larch_call_rel38, |
| 1177 | | larch_tpoff32_lo12, |
| 1178 | | larch_tpoff32_hi20, |
| 1179 | | larch_tpoff64_lo20, |
| 1180 | | larch_tpoff64_hi12, |
| 1181 | | |
| 1182 | | sparc_wdisp30, |
| 1183 | | sparc_pc10, |
| 1184 | | sparc_pc22, |
| 1185 | | sparc_wplt30, |
| 1186 | | sparc_h44, |
| 1187 | | sparc_m44, |
| 1188 | | sparc_l44, |
| 1189 | | sparc_ldo_hix22, |
| 1190 | | sparc_ldo_lox10, |
| 1191 | | sparc_le_hix22, |
| 1192 | | sparc_le_lox10, |
| 1193 | | |
| 1194 | | fn dependsOnTlsSize(t: SymbolReloc.Type) bool { |
| 1195 | | return switch (t) { |
| 1196 | | .tpoff32, |
| 1197 | | .tpoff64, |
| 1198 | | => true, |
| 1257 | /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the |
| 1258 | /// relocation---whether we care about the target symbol's absolute address, its PC-relative |
| 1259 | /// address, its PLT entry, etc. |
| 1260 | const Target = enum(u3) { |
| 1261 | /// This is a "special" relocation whose specific type is in the `action.special` field. |
| 1262 | special, |
| 1263 | |
| 1264 | /// Absolute value of the target symbol. |
| 1265 | abs, |
| 1266 | /// Offset from the relocation itself to the target symbol ("PC-relative"). |
| 1267 | rel, |
| 1268 | /// Address of the target symbol's PLT entry. |
| 1269 | /// |
| 1270 | /// If the target symbol does not have a PLT entry, equivalent to `.abs`. |
| 1271 | pltabs, |
| 1272 | /// Offset from the relocation itself to the target symbol's PLT entry ("PC-relative"). |
| 1273 | /// |
| 1274 | /// If the target symbol does not have a PLT entry, equivalent to `.rel`. |
| 1275 | pltrel, |
| 1276 | /// Offset of the target TLS symbol from the base of this DSO's own TLS region. |
| 1277 | dtpoff, |
| 1278 | /// Offset of the target TLS symbol from the raw thread pointer. |
| 1279 | tpoff, |
| 1280 | /// Size of the target symbol. |
| 1281 | size, |
| 1282 | }; |
| 1199 | 1283 | |
| 1200 | | .larch_tpoff32_lo12, |
| 1201 | | .larch_tpoff32_hi20, |
| 1202 | | .larch_tpoff64_lo20, |
| 1203 | | .larch_tpoff64_hi12, |
| 1204 | | => true, |
| 1284 | /// For a "simple" relocation, after the initial value is computed according to `Target`, a |
| 1285 | /// `Simple` value communicates how to shift, truncate, and store that value into memory. |
| 1286 | const Simple = packed struct(u13) { |
| 1287 | /// The field being written to, represented as a sequence of bits in a backing integer |
| 1288 | /// of 8, 16, 32, or 64 bits. |
| 1289 | /// |
| 1290 | /// The `.@"8"`, `.@"16"`, `.@"32"`, and `.@"64"` fields simply write to all bits of the |
| 1291 | /// backing integer; i.e. the existing value is entirely overwritten. |
| 1292 | /// |
| 1293 | /// Other fields are named like "B[H:L]", where "B" is the backing integer type, and |
| 1294 | /// "H" and "L" are the indices of the highest and lowest bits in the bit field (in |
| 1295 | /// other words, an inclusive bit range). This notation was chosen because it seems to |
| 1296 | /// be one of the more common ways that bit relocations are written in ABIs. |
| 1297 | /// |
| 1298 | /// e.g. 8[6:3] writes the relocated value to this 4-bit field in an 8-bit integer: |
| 1299 | /// |
| 1300 | /// MSB ___ ### ### ### ### ___ ___ ___ LSB |
| 1301 | /// 7 6 5 4 3 2 1 0 |
| 1302 | /// bit index |
| 1303 | /// |
| 1304 | /// This enum is not intended to be able to represent every possible bit field in the |
| 1305 | /// backing integer types. Instead, to keep `SymbolReloc.Type` compact, fields are added |
| 1306 | /// to this enum only as needed. If the enum ever becomes full, some lesser-used tags |
| 1307 | /// can have their handling moved into `Special` to free up space. |
| 1308 | dest: enum(u6) { |
| 1309 | @"8", |
| 1310 | @"16", |
| 1311 | @"32", |
| 1312 | @"64", |
| 1313 | |
| 1314 | @"32[4:0]", |
| 1315 | @"32[5:0]", |
| 1316 | @"32[6:0]", |
| 1317 | @"32[9:0]", |
| 1318 | @"32[10:0]", |
| 1319 | @"32[11:0]", |
| 1320 | @"32[12:0]", |
| 1321 | @"32[21:0]", |
| 1322 | @"32[21:10]", |
| 1323 | @"32[24:5]", |
| 1324 | @"32[25:10]", |
| 1325 | @"32[29:0]", |
| 1326 | |
| 1327 | /// Returns `true` iff `dest` writes a full address for the target. |
| 1328 | /// |
| 1329 | /// i.e. checks for `.@"32"` on 32-bit targets; for `.@"64"` on 64-bit targets. |
| 1330 | fn isAddr(dest: @This(), elf: *const Elf) bool { |
| 1331 | return switch (elf.identClass()) { |
| 1332 | .NONE, _ => unreachable, |
| 1333 | .@"32" => dest == .@"32", |
| 1334 | .@"64" => dest == .@"64", |
| 1335 | }; |
| 1336 | } |
| 1337 | }, |
| 1205 | 1338 | |
| 1206 | | .sparc_le_hix22, |
| 1207 | | .sparc_le_lox10, |
| 1208 | | => true, |
| 1339 | /// After the relocation value is shifted (see `shift`), it is truncated to the size of |
| 1340 | /// the bit field (see `dest`). This field specifies whether the linker will check for, |
| 1341 | /// and error in the case of, truncated bits (in other words, relocation overflow). |
| 1342 | cast: enum(u2) { |
| 1343 | /// Do not perform any check when truncating unused bits. |
| 1344 | trunc, |
| 1345 | /// Error if the truncated value cannot be zero-extended back to the original value, |
| 1346 | /// i.e. if the truncated value is different when interpreted as unsigned. |
| 1347 | unsigned, |
| 1348 | /// Error if the truncated value cannot be sign-extended back to the original value. |
| 1349 | /// i.e. if the truncated value is different when interpreted as signed. |
| 1350 | signed, |
| 1351 | }, |
| 1209 | 1352 | |
| 1210 | | else => false, |
| 1211 | | }; |
| 1212 | | } |
| 1353 | /// The relocation value (computed based on the `Target`) gets shifted to the right by |
| 1354 | /// this amount. By default, the shifted-out bits can be anything, but tags ending in |
| 1355 | /// "_exact" introduce a check that the shifted-out bits are all zeroes (an error is |
| 1356 | /// emitted if not), similar to the behavior of `@shrExact`. |
| 1357 | shift: enum(u5) { |
| 1358 | @"0", |
| 1359 | @"2_exact", |
| 1360 | @"10", |
| 1361 | @"12", |
| 1362 | @"22", |
| 1363 | @"32", |
| 1364 | @"52", |
| 1365 | }, |
| 1213 | 1366 | |
| 1214 | | fn isAbsAddr(t: SymbolReloc.Type, elf: *const Elf) bool { |
| 1215 | | return switch (elf.identClass()) { |
| 1216 | | .NONE, _ => unreachable, |
| 1217 | | .@"32" => switch (t) { |
| 1218 | | .abs32, |
| 1219 | | .pltabs32, |
| 1220 | | => true, |
| 1221 | | else => false, |
| 1222 | | }, |
| 1223 | | .@"64" => switch (t) { |
| 1224 | | .abs64, |
| 1225 | | .pltabs64, |
| 1226 | | => true, |
| 1227 | | else => false, |
| 1367 | /// Given a value (computed based on the `Target`), applies the shift and truncation |
| 1368 | /// operations specified by `s`, then writes the result to the start of `dest_slice` as |
| 1369 | /// specified by `s.dest`. |
| 1370 | fn write( |
| 1371 | s: Simple, |
| 1372 | val: u64, |
| 1373 | dest_slice: []u8, |
| 1374 | target_endian: std.lang.Endian, |
| 1375 | ) error{ RelocationMisaligned, RelocationOverflow }!void { |
| 1376 | const shift: u6, const shift_exact: bool = switch (s.shift) { |
| 1377 | .@"0" => .{ 0, false }, |
| 1378 | .@"2_exact" => .{ 2, true }, |
| 1379 | .@"10" => .{ 10, true }, |
| 1380 | .@"12" => .{ 12, false }, |
| 1381 | .@"22" => .{ 22, false }, |
| 1382 | .@"32" => .{ 32, false }, |
| 1383 | .@"52" => .{ 52, false }, |
| 1384 | }; |
| 1385 | |
| 1386 | if (shift_exact and (val >> shift) << shift != val) { |
| 1387 | return error.RelocationMisaligned; |
| 1388 | } |
| 1389 | |
| 1390 | const dest_word_bits: u8, const dest_high_bit: u6, const dest_low_bit: u6 = switch (s.dest) { |
| 1391 | // zig fmt: off |
| 1392 | .@"8" => .{ 8, 7, 0 }, |
| 1393 | .@"16" => .{ 16, 15, 0 }, |
| 1394 | .@"32" => .{ 32, 31, 0 }, |
| 1395 | .@"64" => .{ 64, 63, 0 }, |
| 1396 | .@"32[4:0]" => .{ 32, 4, 0 }, |
| 1397 | .@"32[5:0]" => .{ 32, 5, 0 }, |
| 1398 | .@"32[6:0]" => .{ 32, 6, 0 }, |
| 1399 | .@"32[9:0]" => .{ 32, 9, 0 }, |
| 1400 | .@"32[10:0]" => .{ 32, 10, 0 }, |
| 1401 | .@"32[11:0]" => .{ 32, 11, 0 }, |
| 1402 | .@"32[12:0]" => .{ 32, 12, 0 }, |
| 1403 | .@"32[21:0]" => .{ 32, 21, 0 }, |
| 1404 | .@"32[21:10]" => .{ 32, 21, 10 }, |
| 1405 | .@"32[24:5]" => .{ 32, 24, 5 }, |
| 1406 | .@"32[25:10]" => .{ 32, 25, 10 }, |
| 1407 | .@"32[29:0]" => .{ 32, 29, 0 }, |
| 1408 | // zig fmt: on |
| 1409 | }; |
| 1410 | |
| 1411 | // The number of bits we are truncating from the full 64-bit relocation value. |
| 1412 | const trunc_bits: u6 = 63 - dest_high_bit + dest_low_bit; |
| 1413 | |
| 1414 | // When we shift, whether we do an arithmetic or logical shift depends on what cast |
| 1415 | // behavior we are going to use. If we'll be doing a signed int cast, we must shift |
| 1416 | // in sign bits so that we don't incorrectly cause a failure, and vice versa for an |
| 1417 | // unsigned int cast. Either is fine when truncating (here we pick logical shift). |
| 1418 | const shifted_val: u64 = switch (s.cast) { |
| 1419 | .trunc => val >> shift, |
| 1420 | inline else => |cast| shifted: { |
| 1421 | const ShiftInt = if (cast == .signed) i64 else u64; |
| 1422 | const x: ShiftInt = @bitCast(val); |
| 1423 | const shifted: ShiftInt = x >> shift; |
| 1424 | |
| 1425 | if ((shifted << trunc_bits) >> trunc_bits != shifted) { |
| 1426 | return error.RelocationOverflow; |
| 1427 | } |
| 1428 | |
| 1429 | break :shifted @bitCast(shifted); |
| 1430 | }, |
| 1431 | }; |
| 1432 | |
| 1433 | // Create a bit-mask for the field being populated, e.g. 8[3:1] -> 0b00001110 |
| 1434 | const field_mask = (~@as(u64, 0) >> trunc_bits) << dest_low_bit; |
| 1435 | |
| 1436 | // Shift and mask the value to be in the correct bits, leaving the others zeroed. |
| 1437 | const masked_field: u64 = (shifted_val << dest_low_bit) & field_mask; |
| 1438 | |
| 1439 | // Now we just need to actually apply the relocation by loading a word, replacing |
| 1440 | // the field bits with those in `masked_field`, and storing the result back. |
| 1441 | switch (dest_word_bits) { |
| 1442 | inline 8, 16, 32, 64 => |bits| { |
| 1443 | const word_slice = dest_slice[0..@divExact(bits, 8)]; |
| 1444 | const Int = @Int(.unsigned, bits); |
| 1445 | const old: u64 = std.mem.readInt(Int, word_slice, target_endian); |
| 1446 | const new: u64 = (old & ~field_mask) | masked_field; |
| 1447 | std.mem.writeInt(Int, word_slice, @intCast(new), target_endian); |
| 1448 | }, |
| 1449 | else => unreachable, |
| 1450 | } |
| 1451 | } |
| 1452 | }; |
| 1453 | |
| 1454 | /// Enum representing "special" relocation types, i.e. those which cannot be represented |
| 1455 | /// just with `Target` and `Simple`. These relocations have completely custom handling in |
| 1456 | /// the `Special.applyInner` function. |
| 1457 | const Special = enum(u13) { |
| 1458 | larch_pcala_hi20, |
| 1459 | larch_pcala64_lo20, |
| 1460 | larch_pcala64_hi12, |
| 1461 | larch_b21, |
| 1462 | larch_b26, |
| 1463 | larch_call36, |
| 1464 | |
| 1465 | sparc_le_hix22, |
| 1466 | |
| 1467 | fn applyInner( |
| 1468 | s: Special, |
| 1469 | elf: *Elf, |
| 1470 | target: Symbol.Id, |
| 1471 | addend: u64, |
| 1472 | dest_vaddr: u64, |
| 1473 | dest_slice: []u8, |
| 1474 | ) error{ RelocationMisaligned, RelocationOverflow }!void { |
| 1475 | switch (s) { |
| 1476 | .larch_pcala_hi20 => { |
| 1477 | const val = target.value(elf) +% addend; |
| 1478 | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 1479 | elf.targetStore(inst, .{ |
| 1480 | .b0_4 = elf.targetLoad(inst).b0_4, |
| 1481 | .j20 = link.loongarch.pcalaHi20(val, dest_vaddr), |
| 1482 | .b25_31 = elf.targetLoad(inst).b25_31, |
| 1483 | }); |
| 1484 | }, |
| 1485 | .larch_pcala64_lo20 => { |
| 1486 | const val = target.value(elf) +% addend; |
| 1487 | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 1488 | elf.targetStore(inst, .{ |
| 1489 | .b0_4 = elf.targetLoad(inst).b0_4, |
| 1490 | .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr), |
| 1491 | .b25_31 = elf.targetLoad(inst).b25_31, |
| 1492 | }); |
| 1493 | }, |
| 1494 | .larch_pcala64_hi12 => { |
| 1495 | const val = target.value(elf) +% addend; |
| 1496 | const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); |
| 1497 | elf.targetStore(inst, .{ |
| 1498 | .b0_9 = elf.targetLoad(inst).b0_9, |
| 1499 | .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr), |
| 1500 | .b22_31 = elf.targetLoad(inst).b22_31, |
| 1501 | }); |
| 1502 | }, |
| 1503 | .larch_b21, .larch_b26, .larch_call36 => { |
| 1504 | const target_vaddr: u64 = elf.pltEntryTargetAddr(target) orelse target.value(elf); |
| 1505 | const jump_offset: i64 = @bitCast(target_vaddr +% addend -% dest_vaddr); |
| 1506 | if ((jump_offset >> 2) << 2 != jump_offset) { |
| 1507 | return error.RelocationMisaligned; |
| 1508 | } |
| 1509 | const shifted_jump_offset: i64 = @shrExact(jump_offset, 2); |
| 1510 | switch (s) { |
| 1511 | .larch_b21 => { |
| 1512 | if ((shifted_jump_offset << (64 - 21)) >> (64 - 21) != shifted_jump_offset) { |
| 1513 | return error.RelocationOverflow; |
| 1514 | } |
| 1515 | const truncated: i21 = @intCast(shifted_jump_offset); |
| 1516 | const parts: packed struct { lo16: u16, hi5: u5 } = @bitCast(truncated); |
| 1517 | const inst: *align(1) link.loongarch.D5K16 = @ptrCast(dest_slice[0..4]); |
| 1518 | elf.targetStore(inst, .{ |
| 1519 | .d5 = parts.hi5, |
| 1520 | .b5_9 = elf.targetLoad(inst).b5_9, |
| 1521 | .k16 = parts.lo16, |
| 1522 | .b26_31 = elf.targetLoad(inst).b26_31, |
| 1523 | }); |
| 1524 | }, |
| 1525 | .larch_b26 => { |
| 1526 | if ((shifted_jump_offset << (64 - 26)) >> (64 - 26) != shifted_jump_offset) { |
| 1527 | return error.RelocationOverflow; |
| 1528 | } |
| 1529 | const truncated: i26 = @intCast(shifted_jump_offset); |
| 1530 | const parts: packed struct { lo16: u16, hi10: u10 } = @bitCast(truncated); |
| 1531 | const inst: *align(1) link.loongarch.D10K16 = @ptrCast(dest_slice[0..4]); |
| 1532 | elf.targetStore(inst, .{ |
| 1533 | .d10 = parts.hi10, |
| 1534 | .k16 = parts.lo16, |
| 1535 | .b26_31 = elf.targetLoad(inst).b26_31, |
| 1536 | }); |
| 1537 | }, |
| 1538 | .larch_call36 => { |
| 1539 | // The allowed range of destination addresses here is non-trivial: |
| 1540 | // [PC - 128 GiB - 0x20_000, PC + 128 GiB - 0x20_000 - 4] |
| 1541 | const gib = 1024 * 1024 * 1024; |
| 1542 | if (jump_offset < -128 * gib - 0x20_000 or |
| 1543 | jump_offset > 128 * gib - 0x20_000 - 4) |
| 1544 | { |
| 1545 | return error.RelocationOverflow; |
| 1546 | } |
| 1547 | // The values we write into the instructions are a little weird too: |
| 1548 | const hi: i20 = @intCast((shifted_jump_offset +% 0x8000) >> 16); |
| 1549 | const lo: i16 = @truncate(shifted_jump_offset); |
| 1550 | |
| 1551 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 1552 | const inst1: *align(1) link.loongarch.K16 = @ptrCast(dest_slice[4..8]); |
| 1553 | |
| 1554 | const old0 = elf.targetLoad(inst0); |
| 1555 | elf.targetStore(inst0, .{ .b0_4 = old0.b0_4, .j20 = @bitCast(hi), .b25_31 = old0.b25_31 }); |
| 1556 | |
| 1557 | const old1 = elf.targetLoad(inst1); |
| 1558 | elf.targetStore(inst1, .{ .b0_9 = old1.b0_9, .k16 = @bitCast(lo), .b26_31 = old1.b26_31 }); |
| 1559 | }, |
| 1560 | else => unreachable, |
| 1561 | } |
| 1562 | }, |
| 1563 | .sparc_le_hix22 => { |
| 1564 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1565 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1566 | inline else => |phdr| tls_size: { |
| 1567 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1568 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1569 | }, |
| 1570 | }; |
| 1571 | const dest_ptr: *align(1) packed struct(u32) { |
| 1572 | imm22: u22, |
| 1573 | b22_31: u10, |
| 1574 | } = @ptrCast(dest_slice); |
| 1575 | elf.targetStore(dest_ptr, .{ |
| 1576 | .imm22 = @truncate(~(target.value(elf) +% addend -% tls_size) >> 10), |
| 1577 | .b22_31 = elf.targetLoad(dest_ptr).b22_31, |
| 1578 | }); |
| 1579 | }, |
| 1580 | } |
| 1581 | } |
| 1582 | }; |
| 1583 | |
| 1584 | fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool { |
| 1585 | return switch (elf.targetTlsVariant()) { |
| 1586 | // In TLS variant I, the executable's TLS block starts at a fixed offset from the |
| 1587 | // thread pointer, so everything is fine... |
| 1588 | .I_original, .I_modified => false, |
| 1589 | // ...but in variant II, the executable's TLS block *ends* at a fixed offset from |
| 1590 | // the thread pointer, so the offset from the thread pointer to the *start* of the |
| 1591 | // TLS block depends on the size of the block, and we need that offset to resolve |
| 1592 | // 'tpoff' relocations. |
| 1593 | .II => switch (t.target) { |
| 1594 | .abs, |
| 1595 | .rel, |
| 1596 | .pltabs, |
| 1597 | .pltrel, |
| 1598 | .dtpoff, |
| 1599 | .size, |
| 1600 | => false, |
| 1601 | |
| 1602 | .tpoff => true, |
| 1603 | |
| 1604 | .special => switch (t.action.special) { |
| 1605 | .sparc_le_hix22, |
| 1606 | => true, |
| 1607 | |
| 1608 | .larch_pcala_hi20, |
| 1609 | .larch_pcala64_lo20, |
| 1610 | .larch_pcala64_hi12, |
| 1611 | .larch_b21, |
| 1612 | .larch_b26, |
| 1613 | .larch_call36, |
| 1614 | => false, |
| 1615 | }, |
| 1228 | 1616 | }, |
| 1229 | 1617 | }; |
| 1230 | 1618 | } |
| 1231 | 1619 | }; |
| 1232 | 1620 | |
| 1233 | | fn apply(reloc: *const SymbolReloc, elf: *Elf) void { |
| 1234 | | assert(elf.ehdrField(.type) != .REL); |
| 1621 | fn apply(reloc: *SymbolReloc, elf: *Elf) void { |
| 1622 | assert(elf.ehdrType() != .REL); |
| 1235 | 1623 | assert(reloc.node != .none); |
| 1236 | | |
| 1237 | 1624 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 1238 | 1625 | // There's no point applying the relocation now, because it will be re-applied by |
| 1239 | 1626 | // `flushMoved` at some point anyway. |
| 1240 | 1627 | return; |
| 1241 | 1628 | } |
| 1242 | | |
| 1243 | | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { |
| 1244 | | .static => unreachable, |
| 1245 | | .dynamic => return, // the relocation happens at runtime |
| 1246 | | .static_relative => { |
| 1247 | | // We have emitted an R_*_RELATIVE relocation to help lower an abs32/abs64 reloc. |
| 1248 | | // This is a simplified version of the general relocation handling logic, where we |
| 1249 | | // know we're using '.abs64' or '.abs32' (matching the ELF ident class). |
| 1250 | | const value = type: switch (reloc.type) { |
| 1251 | | .abs32, |
| 1252 | | .abs64, |
| 1253 | | => reloc.target.value(elf) +% @as(u64, @bitCast(reloc.addend)), |
| 1254 | | .pltabs32, |
| 1255 | | .pltabs64, |
| 1256 | | => value: { |
| 1257 | | const plt_index = switch (reloc.target.unwrap()) { |
| 1258 | | .local => continue :type .abs32, |
| 1259 | | .global => |name| elf.plt.getIndex(name) orelse continue :type .abs32, |
| 1260 | | }; |
| 1261 | | if (elf.pltEntryIsDead(plt_index)) continue :type .abs32; |
| 1262 | | const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1263 | | else => |machine| @panic(@tagName(machine)), |
| 1264 | | .SPARCV9 => .{ elf.shndx.plt, 4, 32 }, |
| 1265 | | .X86_64 => .{ elf.shndx.plt_sec, 0, 16 }, |
| 1266 | | }; |
| 1267 | | const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size; |
| 1268 | | break :value plt_entry +% @as(u64, @bitCast(reloc.addend)); |
| 1269 | | }, |
| 1270 | | else => unreachable, |
| 1271 | | }; |
| 1272 | | elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, value); |
| 1273 | | return; |
| 1629 | switch (reloc.result) { |
| 1630 | .ok => {}, |
| 1631 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1632 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 1633 | } |
| 1634 | if (reloc.applyInner(elf)) { |
| 1635 | @branchHint(.likely); |
| 1636 | reloc.result = .ok; |
| 1637 | } else |err| switch (err) { |
| 1638 | error.RelocationOverflow => { |
| 1639 | reloc.result = .overflowed; |
| 1640 | elf.overflowed_reloc_count += 1; |
| 1274 | 1641 | }, |
| 1275 | | }; |
| 1642 | error.RelocationMisaligned => { |
| 1643 | reloc.result = .misaligned; |
| 1644 | elf.misaligned_reloc_count += 1; |
| 1645 | }, |
| 1646 | } |
| 1647 | } |
| 1648 | fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { |
| 1276 | 1649 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 1277 | 1650 | .file => unreachable, |
| 1278 | 1651 | .ehdr => unreachable, |
| ... | ... | @@ -1289,349 +1662,75 @@ const SymbolReloc = struct { |
| 1289 | 1662 | }; |
| 1290 | 1663 | const dest_vaddr = node_vaddr + reloc.offset; |
| 1291 | 1664 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 1292 | | const target_endian = elf.targetEndian(); |
| 1293 | | const sym_value: u64 = reloc.target.value(elf); |
| 1294 | | const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) { |
| 1295 | | inline else => |target_sym| elf.targetLoad(&target_sym.size), |
| 1296 | | }; |
| 1297 | | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); |
| 1298 | | type: switch (reloc.type) { |
| 1299 | | .write_rela => unreachable, |
| 1300 | | .abs64, .dsorel64 => std.mem.writeInt( |
| 1301 | | u64, |
| 1302 | | dest_slice[0..8], |
| 1303 | | target_value, |
| 1304 | | target_endian, |
| 1305 | | ), |
| 1306 | | .abs32, .dsorel32 => std.mem.writeInt( |
| 1307 | | u32, |
| 1308 | | dest_slice[0..4], |
| 1309 | | @intCast(target_value), |
| 1310 | | target_endian, |
| 1311 | | ), |
| 1312 | | .abs32s => std.mem.writeInt( |
| 1313 | | i32, |
| 1314 | | dest_slice[0..4], |
| 1315 | | @intCast(@as(i64, @bitCast(target_value))), |
| 1316 | | target_endian, |
| 1317 | | ), |
| 1318 | | .abs16 => std.mem.writeInt( |
| 1319 | | u16, |
| 1320 | | dest_slice[0..2], |
| 1321 | | @intCast(target_value), |
| 1322 | | target_endian, |
| 1323 | | ), |
| 1324 | | .abs8 => dest_slice[0] = @intCast(target_value), |
| 1325 | | .rel64 => std.mem.writeInt( |
| 1326 | | i64, |
| 1327 | | dest_slice[0..8], |
| 1328 | | @bitCast(target_value -% dest_vaddr), |
| 1329 | | target_endian, |
| 1330 | | ), |
| 1331 | | .rel32 => std.mem.writeInt( |
| 1332 | | i32, |
| 1333 | | dest_slice[0..4], |
| 1334 | | @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))), |
| 1335 | | target_endian, |
| 1336 | | ), |
| 1337 | | .rel16 => std.mem.writeInt( |
| 1338 | | i16, |
| 1339 | | dest_slice[0..2], |
| 1340 | | @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))), |
| 1341 | | target_endian, |
| 1342 | | ), |
| 1343 | | .rel8 => dest_slice[0] = @bitCast(@as(i8, @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))))), |
| 1344 | | .pltabs64 => { |
| 1345 | | const plt_index = switch (reloc.target.unwrap()) { |
| 1346 | | .local => continue :type .abs64, |
| 1347 | | .global => |name| elf.plt.getIndex(name) orelse continue :type .abs64, |
| 1348 | | }; |
| 1349 | | if (elf.pltEntryIsDead(plt_index)) continue :type .abs64; |
| 1350 | | const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1351 | | else => |machine| @panic(@tagName(machine)), |
| 1352 | | .SPARCV9 => .{ elf.shndx.plt, 4, 32 }, |
| 1353 | | .X86_64 => .{ elf.shndx.plt_sec, 0, 16 }, |
| 1354 | | }; |
| 1355 | | const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size; |
| 1356 | | std.mem.writeInt( |
| 1357 | | i64, |
| 1358 | | dest_slice[0..8], |
| 1359 | | @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend))), |
| 1360 | | target_endian, |
| 1361 | | ); |
| 1665 | |
| 1666 | const addend: u64 = @bitCast(reloc.addend); |
| 1667 | const target_val: u64 = type: switch (reloc.type.target) { |
| 1668 | .abs => reloc.target.value(elf) +% addend, |
| 1669 | .rel => reloc.target.value(elf) +% addend -% dest_vaddr, |
| 1670 | .pltabs => { |
| 1671 | const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .abs; |
| 1672 | break :type plt_entry_addr +% addend; |
| 1362 | 1673 | }, |
| 1363 | | .pltabs32 => { |
| 1364 | | const plt_index = switch (reloc.target.unwrap()) { |
| 1365 | | .local => continue :type .abs32, |
| 1366 | | .global => |name| elf.plt.getIndex(name) orelse continue :type .abs32, |
| 1367 | | }; |
| 1368 | | if (elf.pltEntryIsDead(plt_index)) continue :type .abs32; |
| 1369 | | const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1370 | | else => |machine| @panic(@tagName(machine)), |
| 1371 | | .SPARCV9 => .{ elf.shndx.plt, 4, 32 }, |
| 1372 | | .X86_64 => .{ elf.shndx.plt_sec, 0, 16 }, |
| 1373 | | }; |
| 1374 | | const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size; |
| 1375 | | std.mem.writeInt( |
| 1376 | | i32, |
| 1377 | | dest_slice[0..4], |
| 1378 | | @intCast(@as(i64, @bitCast( |
| 1379 | | plt_entry +% @as(u64, @bitCast(reloc.addend)), |
| 1380 | | ))), |
| 1381 | | target_endian, |
| 1382 | | ); |
| 1674 | .pltrel => { |
| 1675 | const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .rel; |
| 1676 | break :type plt_entry_addr +% addend -% dest_vaddr; |
| 1383 | 1677 | }, |
| 1384 | | .pltrel64 => { |
| 1385 | | const plt_index = switch (reloc.target.unwrap()) { |
| 1386 | | .local => continue :type .rel64, |
| 1387 | | .global => |name| elf.plt.getIndex(name) orelse continue :type .rel64, |
| 1388 | | }; |
| 1389 | | if (elf.pltEntryIsDead(plt_index)) continue :type .rel64; |
| 1390 | | const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1391 | | else => |machine| @panic(@tagName(machine)), |
| 1392 | | .SPARCV9 => .{ elf.shndx.plt, 4, 32 }, |
| 1393 | | .X86_64 => .{ elf.shndx.plt_sec, 0, 16 }, |
| 1394 | | }; |
| 1395 | | const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size; |
| 1396 | | std.mem.writeInt( |
| 1397 | | i64, |
| 1398 | | dest_slice[0..8], |
| 1399 | | @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr), |
| 1400 | | target_endian, |
| 1401 | | ); |
| 1678 | .dtpoff => reloc.target.value(elf) +% addend, |
| 1679 | .tpoff => switch (elf.targetTlsVariant()) { |
| 1680 | .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend, |
| 1681 | .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend, |
| 1682 | .II => { |
| 1683 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1684 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1685 | inline else => |phdr| tls_size: { |
| 1686 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1687 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1688 | }, |
| 1689 | }; |
| 1690 | break :type reloc.target.value(elf) +% addend -% tls_size; |
| 1691 | }, |
| 1402 | 1692 | }, |
| 1403 | | .pltrel32 => { |
| 1404 | | const plt_index = switch (reloc.target.unwrap()) { |
| 1405 | | .local => continue :type .rel32, |
| 1406 | | .global => |name| elf.plt.getIndex(name) orelse continue :type .rel32, |
| 1407 | | }; |
| 1408 | | if (elf.pltEntryIsDead(plt_index)) continue :type .rel32; |
| 1409 | | const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1410 | | else => |machine| @panic(@tagName(machine)), |
| 1411 | | .SPARCV9 => .{ elf.shndx.plt, 4, 32 }, |
| 1412 | | .X86_64 => .{ elf.shndx.plt_sec, 0, 16 }, |
| 1413 | | }; |
| 1414 | | const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size; |
| 1415 | | std.mem.writeInt( |
| 1416 | | i32, |
| 1417 | | dest_slice[0..4], |
| 1418 | | @intCast(@as(i64, @bitCast( |
| 1419 | | plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr, |
| 1420 | | ))), |
| 1421 | | target_endian, |
| 1422 | | ); |
| 1693 | .size => switch (elf.symPtr(reloc.target.index(elf))) { |
| 1694 | inline else => |sym| elf.targetLoad(&sym.size), |
| 1423 | 1695 | }, |
| 1424 | | .size64 => std.mem.writeInt( |
| 1425 | | u64, |
| 1426 | | dest_slice[0..8], |
| 1427 | | sym_size +% @as(u64, @bitCast(reloc.addend)), |
| 1428 | | target_endian, |
| 1429 | | ), |
| 1430 | | .size32 => std.mem.writeInt( |
| 1431 | | u32, |
| 1432 | | dest_slice[0..4], |
| 1433 | | @intCast(sym_size +% @as(u64, @bitCast(reloc.addend))), |
| 1434 | | target_endian, |
| 1435 | | ), |
| 1436 | | .dtpoff64 => std.mem.writeInt( |
| 1437 | | i64, |
| 1438 | | dest_slice[0..8], |
| 1439 | | @bitCast(target_value), |
| 1440 | | target_endian, |
| 1441 | | ), |
| 1442 | | .dtpoff32 => std.mem.writeInt( |
| 1443 | | i32, |
| 1444 | | dest_slice[0..4], |
| 1445 | | @intCast(@as(i64, @bitCast(target_value))), |
| 1446 | | target_endian, |
| 1696 | .special => return reloc.type.action.special.applyInner( |
| 1697 | elf, |
| 1698 | reloc.target, |
| 1699 | addend, |
| 1700 | dest_vaddr, |
| 1701 | dest_slice, |
| 1447 | 1702 | ), |
| 1448 | | .tpoff64 => { |
| 1449 | | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1450 | | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1451 | | inline else => |phdr| tls_size: { |
| 1452 | | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1453 | | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1454 | | }, |
| 1455 | | }; |
| 1456 | | std.mem.writeInt( |
| 1457 | | i64, |
| 1458 | | dest_slice[0..8], |
| 1459 | | @bitCast(target_value -% tls_size), |
| 1460 | | target_endian, |
| 1461 | | ); |
| 1462 | | }, |
| 1463 | | .tpoff32 => { |
| 1464 | | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1465 | | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1466 | | inline else => |phdr| tls_size: { |
| 1467 | | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1468 | | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1469 | | }, |
| 1470 | | }; |
| 1471 | | std.mem.writeInt( |
| 1472 | | i32, |
| 1473 | | dest_slice[0..4], |
| 1474 | | @intCast(@as(i64, @bitCast(target_value -% tls_size))), |
| 1475 | | target_endian, |
| 1476 | | ); |
| 1477 | | }, |
| 1703 | }; |
| 1478 | 1704 | |
| 1479 | | .larch_abs32_lo12 => { |
| 1480 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1481 | | link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value)); |
| 1482 | | }, |
| 1483 | | .larch_rel32_hi20 => { |
| 1484 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1485 | | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr)); |
| 1486 | | }, |
| 1487 | | .larch_rel64_lo20 => { |
| 1488 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1489 | | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr)); |
| 1490 | | }, |
| 1491 | | .larch_rel64_hi12 => { |
| 1492 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1493 | | link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr)); |
| 1494 | | }, |
| 1495 | | // TODO: handle bad alignment and overflow gracefully |
| 1496 | | .larch_branch_rel18 => { |
| 1497 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1498 | | const target_rel: i64 = @bitCast(target_value -% dest_vaddr); |
| 1499 | | const slot_target: i16 = @intCast(@shrExact(target_rel, 2)); |
| 1500 | | link.loongarch.writeK16(dest_slice[0..4], @bitCast(slot_target)); |
| 1501 | | }, |
| 1502 | | .larch_branch_rel23 => { |
| 1503 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1504 | | const target_rel: i64 = @bitCast(target_value -% dest_vaddr); |
| 1505 | | const slot_target: i21 = @intCast(@shrExact(target_rel, 2)); |
| 1506 | | link.loongarch.writeD5K16(dest_slice[0..4], @bitCast(slot_target)); |
| 1507 | | }, |
| 1508 | | .larch_branch_rel28 => { |
| 1509 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1510 | | const target_rel: i64 = @bitCast(target_value -% dest_vaddr); |
| 1511 | | const slot_target: i26 = @intCast(@shrExact(target_rel, 2)); |
| 1512 | | link.loongarch.writeD10K16(dest_slice[0..4], @bitCast(slot_target)); |
| 1513 | | }, |
| 1514 | | .larch_call_rel38 => { |
| 1515 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1516 | | const target_rel: i64 = @bitCast(target_value -% dest_vaddr); |
| 1517 | | // We use i64 instead of i36 here because the allowed range is |
| 1518 | | // [PC - 128 GiB - 0x20000, PC + 128GiB - 0x20000 - 4]. |
| 1519 | | // The intCast in writeJ20 will do the final check. |
| 1520 | | const slot_target: i64 = @intCast(@shrExact(target_rel, 2)); |
| 1521 | | link.loongarch.writeJ20(dest_slice[0..4], @bitCast(@as(i20, @intCast((slot_target +% 0x8000) >> 16)))); |
| 1522 | | link.loongarch.writeK16(dest_slice[4..8], @bitCast(@as(i16, @truncate(slot_target)))); |
| 1523 | | }, |
| 1524 | | .larch_tpoff32_lo12 => { |
| 1525 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1526 | | link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value)); |
| 1527 | | }, |
| 1528 | | .larch_tpoff32_hi20 => { |
| 1529 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1530 | | link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 12)); |
| 1531 | | }, |
| 1532 | | .larch_tpoff64_lo20 => { |
| 1533 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1534 | | link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 32)); |
| 1535 | | }, |
| 1536 | | .larch_tpoff64_hi12 => { |
| 1537 | | assert(elf.ehdrField(.machine) == .LOONGARCH); |
| 1538 | | link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value >> 52)); |
| 1705 | // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast |
| 1706 | // is required, meaning we can handle it now and return early. |
| 1707 | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { |
| 1708 | .static => unreachable, |
| 1709 | .dynamic => return, // the relocation happens at runtime |
| 1710 | .static_relative => { |
| 1711 | // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address |
| 1712 | // relocation. The value computed above is valid, but instead of writing it to the |
| 1713 | // destination slice, we actually want to write it to the runtime relocation entry. |
| 1714 | switch (elf.identClass()) { |
| 1715 | .NONE, _ => unreachable, |
| 1716 | .@"32" => assert(reloc.type.action.simple.dest == .@"32"), |
| 1717 | .@"64" => assert(reloc.type.action.simple.dest == .@"64"), |
| 1718 | } |
| 1719 | assert(reloc.type.action.simple.cast == .unsigned); |
| 1720 | assert(reloc.type.action.simple.shift == .@"0"); |
| 1721 | elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, target_val); |
| 1722 | return; |
| 1539 | 1723 | }, |
| 1724 | }; |
| 1540 | 1725 | |
| 1541 | | .sparc_wdisp30 => { |
| 1542 | | const dest_ptr: *link.sparc.reloc.Disp30 = @ptrCast(@alignCast(dest_slice)); |
| 1543 | | var result = elf.targetLoad(dest_ptr); |
| 1544 | | result.disp30 = @truncate((target_value -% dest_vaddr) >> 2); |
| 1545 | | elf.targetStore(dest_ptr, result); |
| 1546 | | }, |
| 1547 | | .sparc_pc10 => { |
| 1548 | | const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice)); |
| 1549 | | var result = elf.targetLoad(dest_ptr); |
| 1550 | | result.simm13 = @as(u10, @truncate(target_value -% dest_vaddr)); |
| 1551 | | elf.targetStore(dest_ptr, result); |
| 1552 | | }, |
| 1553 | | .sparc_pc22 => { |
| 1554 | | const dest_ptr: *link.sparc.reloc.Disp22 = @ptrCast(@alignCast(dest_slice)); |
| 1555 | | var result = elf.targetLoad(dest_ptr); |
| 1556 | | result.disp22 = @truncate((target_value -% dest_vaddr) >> 10); |
| 1557 | | elf.targetStore(dest_ptr, result); |
| 1558 | | }, |
| 1559 | | .sparc_wplt30 => { |
| 1560 | | const plt_index = switch (reloc.target.unwrap()) { |
| 1561 | | .local => continue :type .sparc_wdisp30, |
| 1562 | | .global => |name| elf.plt.getIndex(name) orelse continue :type .sparc_wdisp30, |
| 1563 | | }; |
| 1564 | | if (elf.pltEntryIsDead(plt_index)) continue :type .sparc_wdisp30; |
| 1565 | | const plt_entry = elf.shndx.plt.vaddr(elf) +% (4 + plt_index) * 32; |
| 1566 | | const dest_ptr: *link.sparc.reloc.Disp30 = @ptrCast(@alignCast(dest_slice)); |
| 1567 | | var result = elf.targetLoad(dest_ptr); |
| 1568 | | result.disp30 = @truncate((plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr) >> 2); |
| 1569 | | elf.targetStore(dest_ptr, result); |
| 1570 | | }, |
| 1571 | | .sparc_h44 => { |
| 1572 | | const dest_ptr: *link.sparc.reloc.Imm22 = @ptrCast(@alignCast(dest_slice)); |
| 1573 | | var result = elf.targetLoad(dest_ptr); |
| 1574 | | result.imm22 = @truncate(target_value >> 22); |
| 1575 | | elf.targetStore(dest_ptr, result); |
| 1576 | | }, |
| 1577 | | .sparc_m44 => { |
| 1578 | | const dest_ptr: *link.sparc.reloc.Imm10 = @ptrCast(@alignCast(dest_slice)); |
| 1579 | | var result = elf.targetLoad(dest_ptr); |
| 1580 | | result.imm10 = @truncate(target_value >> 12); |
| 1581 | | elf.targetStore(dest_ptr, result); |
| 1582 | | }, |
| 1583 | | .sparc_l44 => { |
| 1584 | | const dest_ptr: *link.sparc.reloc.Imm13 = @ptrCast(@alignCast(dest_slice)); |
| 1585 | | var result = elf.targetLoad(dest_ptr); |
| 1586 | | result.imm13 = @as(u12, @truncate(target_value)); |
| 1587 | | elf.targetStore(dest_ptr, result); |
| 1588 | | }, |
| 1589 | | .sparc_ldo_hix22 => { |
| 1590 | | const dest_ptr: *link.sparc.reloc.Simm22 = @ptrCast(@alignCast(dest_slice)); |
| 1591 | | var result = elf.targetLoad(dest_ptr); |
| 1592 | | result.simm22 = @truncate(target_value >> 10); |
| 1593 | | elf.targetStore(dest_ptr, result); |
| 1594 | | }, |
| 1595 | | .sparc_ldo_lox10 => { |
| 1596 | | const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice)); |
| 1597 | | var result = elf.targetLoad(dest_ptr); |
| 1598 | | result.simm13 = @as(u10, @truncate(target_value)); |
| 1599 | | elf.targetStore(dest_ptr, result); |
| 1600 | | }, |
| 1601 | | .sparc_le_hix22 => { |
| 1602 | | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1603 | | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1604 | | inline else => |phdr| tls_size: { |
| 1605 | | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1606 | | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1607 | | }, |
| 1608 | | }; |
| 1609 | | const dest_ptr: *link.sparc.reloc.Imm22 = @ptrCast(@alignCast(dest_slice)); |
| 1610 | | var result = elf.targetLoad(dest_ptr); |
| 1611 | | result.imm22 = @truncate(~(target_value -% tls_size) >> 10); |
| 1612 | | elf.targetStore(dest_ptr, result); |
| 1613 | | }, |
| 1614 | | .sparc_le_lox10 => { |
| 1615 | | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1616 | | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1617 | | inline else => |phdr| tls_size: { |
| 1618 | | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1619 | | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1620 | | }, |
| 1621 | | }; |
| 1622 | | const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice)); |
| 1623 | | var result = elf.targetLoad(dest_ptr); |
| 1624 | | result.simm13 = @as(u13, 0b1110000000000) | @as(u10, @truncate(target_value -% tls_size)); |
| 1625 | | elf.targetStore(dest_ptr, result); |
| 1626 | | }, |
| 1627 | | } |
| 1726 | try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); |
| 1628 | 1727 | } |
| 1629 | 1728 | |
| 1630 | 1729 | fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void { |
| 1631 | 1730 | assert(index.get(elf) == reloc); |
| 1632 | 1731 | |
| 1633 | 1732 | reloc.deleteOutputRel(elf); |
| 1634 | | if (reloc.type.dependsOnTlsSize()) { |
| 1733 | if (reloc.type.dependsOnTlsSize(elf)) { |
| 1635 | 1734 | assert(elf.tls_size_symbol_relocs.swapRemove(index)); |
| 1636 | 1735 | } |
| 1637 | 1736 | |
| ... | ... | @@ -1647,6 +1746,11 @@ const SymbolReloc = struct { |
| 1647 | 1746 | .none => {}, |
| 1648 | 1747 | else => |next| next.get(elf).prev = reloc.prev, |
| 1649 | 1748 | } |
| 1749 | switch (reloc.result) { |
| 1750 | .ok => {}, |
| 1751 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1752 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 1753 | } |
| 1650 | 1754 | |
| 1651 | 1755 | reloc.* = undefined; |
| 1652 | 1756 | } |
| ... | ... | @@ -1656,8 +1760,7 @@ const SymbolReloc = struct { |
| 1656 | 1760 | fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void { |
| 1657 | 1761 | const rela_index = reloc.rela_index.unwrap() orelse return; |
| 1658 | 1762 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| 1659 | | switch (elf.ehdrField(.type)) { |
| 1660 | | .NONE, .CORE, _ => unreachable, |
| 1763 | switch (elf.ehdrType()) { |
| 1661 | 1764 | .REL => {}, |
| 1662 | 1765 | .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) { |
| 1663 | 1766 | .no => unreachable, // there *was* a dynamic relocation! |
| ... | ... | @@ -1715,37 +1818,26 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void { |
| 1715 | 1818 | try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); |
| 1716 | 1819 | |
| 1717 | 1820 | try elf.plt.ensureUnusedCapacity(gpa, len); |
| 1718 | | const need_plt_capacity = elf.plt.count() + len; |
| 1821 | const need_plt_count = elf.plt.count() + len; |
| 1719 | 1822 | |
| 1720 | | switch (elf.ehdrField(.machine)) { |
| 1721 | | else => |machine| @panic(@tagName(machine)), |
| 1722 | | .X86_64 => { |
| 1723 | | // Ensure the `.plt` section's node is big enough |
| 1724 | | const plt_need_size: usize = 16 * (1 + need_plt_capacity); |
| 1725 | | try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size); |
| 1823 | const plt = elf.targetPltInfo(); |
| 1726 | 1824 | |
| 1727 | | // Ensure the `.got.plt` section's node is big enough |
| 1728 | | const got_plt_need_size: usize = elf.targetPtrSize() * (3 + need_plt_capacity); |
| 1729 | | try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size); |
| 1825 | // Ensure the `.plt` section's node is big enough: |
| 1826 | { |
| 1827 | const need_size: usize = plt.entry_size * (1 + need_plt_count); |
| 1828 | try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, need_size); |
| 1829 | } |
| 1730 | 1830 | |
| 1731 | | // Ensure the `.plt.sec` section's node is big enough |
| 1732 | | const plt_sec_need_size: usize = 16 * need_plt_capacity; |
| 1733 | | try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size); |
| 1734 | | }, |
| 1735 | | .LOONGARCH => { |
| 1736 | | // Ensure the `.plt` section's node is big enough |
| 1737 | | const plt_need_size: usize = 16 * (2 + need_plt_capacity); |
| 1738 | | try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size); |
| 1831 | // If there is a `.got.plt` section, ensure its node is big enough |
| 1832 | if (plt.got_plt) |got_plt| { |
| 1833 | const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count); |
| 1834 | try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, need_size); |
| 1835 | } |
| 1739 | 1836 | |
| 1740 | | // Ensure the `.got.plt` section's node is big enough |
| 1741 | | const got_plt_need_size: usize = elf.targetPtrSize() * (2 + need_plt_capacity); |
| 1742 | | try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size); |
| 1743 | | }, |
| 1744 | | .SPARCV9 => { |
| 1745 | | // Ensure the `.plt` section's node is big enough |
| 1746 | | const plt_need_size: usize = 32 * (4 + need_plt_capacity); |
| 1747 | | try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size); |
| 1748 | | }, |
| 1837 | // If there is a `.plt.sec` section, ensure its node is big enough |
| 1838 | if (plt.plt_sec) |plt_sec| { |
| 1839 | const need_size: usize = plt_sec.entry_size * need_plt_count; |
| 1840 | try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, need_size); |
| 1749 | 1841 | } |
| 1750 | 1842 | } |
| 1751 | 1843 | /// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at |
| ... | ... | @@ -1808,7 +1900,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L |
| 1808 | 1900 | const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name)); |
| 1809 | 1901 | elf.globalByName(global_name).?.symtab_index = new_index; |
| 1810 | 1902 | |
| 1811 | | if (elf.ehdrField(.type) == .REL and target_index.ptr(elf).first_target_reloc != .none) { |
| 1903 | if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) { |
| 1812 | 1904 | // This symbol's index is changing, so queue an update of relocs targeting it. |
| 1813 | 1905 | elf.changed_symtab_index.putAssumeCapacity(global_name, {}); |
| 1814 | 1906 | } |
| ... | ... | @@ -1972,7 +2064,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 1972 | 2064 | }; |
| 1973 | 2065 | |
| 1974 | 2066 | const force_local_bind: bool = switch (opts.visibility) { |
| 1975 | | .HIDDEN, .INTERNAL => elf.ehdrField(.type) != .REL, |
| 2067 | .HIDDEN, .INTERNAL => elf.ehdrType() != .REL, |
| 1976 | 2068 | .PROTECTED, .DEFAULT => false, |
| 1977 | 2069 | }; |
| 1978 | 2070 | |
| ... | ... | @@ -2071,7 +2163,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 2071 | 2163 | } |
| 2072 | 2164 | |
| 2073 | 2165 | switch (@"type") { |
| 2074 | | .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and |
| 2166 | .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and |
| 2075 | 2167 | elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic) |
| 2076 | 2168 | { |
| 2077 | 2169 | // This STT_FUNC symbol might be defined externally, so it needs a PLT entry. |
| ... | ... | @@ -2240,7 +2332,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi |
| 2240 | 2332 | // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are |
| 2241 | 2333 | // putting the global in this state for the first time---let's call it "demoting" the global to |
| 2242 | 2334 | // STB_LOCAL---we need to update its bind in the symtab. |
| 2243 | | const demote_to_local = newly_hidden and elf.ehdrField(.type) != .REL; |
| 2335 | const demote_to_local = newly_hidden and elf.ehdrType() != .REL; |
| 2244 | 2336 | switch (elf.symPtr(global_ptr.symtab_index)) { |
| 2245 | 2337 | inline else => |sym, class| { |
| 2246 | 2338 | const old_info = elf.targetLoad(&sym.info); |
| ... | ... | @@ -2274,7 +2366,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi |
| 2274 | 2366 | /// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF |
| 2275 | 2367 | /// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols. |
| 2276 | 2368 | fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 2277 | | assert(elf.ehdrField(.type) != .REL); // demotion only happens when emitting an ELF module |
| 2369 | assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module |
| 2278 | 2370 | switch (elf.shdrPtr(.symtab)) { |
| 2279 | 2371 | inline else => |shdr, class| { |
| 2280 | 2372 | // `shdr.info` stores the index of the first global symbol. We are going to swap the |
| ... | ... | @@ -2292,240 +2384,64 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 2292 | 2384 | // The demoted global was not the first global in the symtab, so we need to swap it |
| 2293 | 2385 | // to its new location. |
| 2294 | 2386 | |
| 2295 | | const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class)); |
| 2296 | | const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class)); |
| 2297 | | |
| 2298 | | const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name)); |
| 2299 | | assert(elf.globalByName(this_name).? == global_ptr); |
| 2300 | | |
| 2301 | | const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name)); |
| 2302 | | const other_global_ptr = elf.globalByName(other_name).?; |
| 2303 | | assert(other_global_ptr.symtab_index == dest_index); |
| 2304 | | |
| 2305 | | // First swap the symtab entries... |
| 2306 | | std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr); |
| 2307 | | // ...then the `elf.symtab` metadata... |
| 2308 | | std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf)); |
| 2309 | | // ...then update the `elf.globals` tracking. |
| 2310 | | global_ptr.symtab_index = dest_index; |
| 2311 | | other_global_ptr.symtab_index = src_index; |
| 2312 | | } |
| 2313 | | |
| 2314 | | // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact, |
| 2315 | | // we'll move another symbol into its place just like we did above. |
| 2316 | | if (global_ptr.dynsym_index != 0) { |
| 2317 | | const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class)); |
| 2318 | | |
| 2319 | | const ent_size = @sizeOf(class.ElfN().Sym); |
| 2320 | | assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size); |
| 2321 | | |
| 2322 | | // We're going to decrease the size of `.dynsym`, thereby removing its last index. |
| 2323 | | const old_size = elf.targetLoad(&dynsym_shdr.size); |
| 2324 | | const new_size = old_size - ent_size; |
| 2325 | | const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size)); |
| 2326 | | |
| 2327 | | const free_dynsym_index = global_ptr.dynsym_index; |
| 2328 | | global_ptr.dynsym_index = 0; |
| 2329 | | |
| 2330 | | if (free_dynsym_index != remove_dynsym_index) { |
| 2331 | | // The demoted global wasn't the last entry, so move whatever entry we just |
| 2332 | | // truncated out of dynsym into its place. |
| 2333 | | |
| 2334 | | const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class)); |
| 2335 | | const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class)); |
| 2336 | | |
| 2337 | | const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name)); |
| 2338 | | const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf)); |
| 2339 | | const moved_global_ptr = elf.globalByName(moved_name).?; |
| 2340 | | |
| 2341 | | dest_dynsym_ptr.* = src_dynsym_ptr.*; |
| 2342 | | |
| 2343 | | assert(moved_global_ptr.dynsym_index == remove_dynsym_index); |
| 2344 | | moved_global_ptr.dynsym_index = free_dynsym_index; |
| 2345 | | |
| 2346 | | // Since that symbol's dynsym index has changed, we'll have to update any |
| 2347 | | // relocation entries targeting it. |
| 2348 | | elf.changed_symtab_index.putAssumeCapacity(moved_name, {}); |
| 2349 | | } |
| 2350 | | |
| 2351 | | // Now that we've given that symbol a new home, actually decrease the section size. |
| 2352 | | elf.targetStore(&dynsym_shdr.size, new_size); |
| 2353 | | } |
| 2354 | | }, |
| 2355 | | } |
| 2356 | | } |
| 2357 | | fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void { |
| 2358 | | const target_endian = elf.targetEndian(); |
| 2359 | | |
| 2360 | | // We use the existing free-list tracking of the `.rela.plt` section to also behave as a |
| 2361 | | // free-list for the PLT itself---see `pltEntryIsDead` for details. |
| 2362 | | const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{ |
| 2363 | | .type = .jumpSlot(elf), |
| 2364 | | .offset = 0, // populated later |
| 2365 | | .raw_sym_index = dynsym_index, |
| 2366 | | .addend = 0, |
| 2367 | | })); |
| 2368 | | |
| 2369 | | // Note that some architectures don't have .got.plt (e.g. SPARC), and so |
| 2370 | | // these values actually refer to .plt. |
| 2371 | | const got_plt_section, const got_plt_offset = switch (elf.ehdrField(.machine)) { |
| 2372 | | else => |machine| @panic(@tagName(machine)), |
| 2373 | | .LOONGARCH => .{ elf.shndx.got_plt, elf.targetPtrSize() * (2 + plt_index) }, |
| 2374 | | .SPARCV9 => .{ elf.shndx.plt, 32 * (4 + plt_index) }, |
| 2375 | | .X86_64 => .{ elf.shndx.got_plt, elf.targetPtrSize() * (3 + plt_index) }, |
| 2376 | | }; |
| 2377 | | |
| 2378 | | // Now that we know the index, we can set the relocation's offset. |
| 2379 | | elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset); |
| 2380 | | |
| 2381 | | if (plt_index < elf.plt.count()) { |
| 2382 | | // We reused a free entry, so we're already done! |
| 2383 | | elf.plt.setKey(plt_index, global_name); |
| 2384 | | return; |
| 2385 | | } |
| 2386 | | |
| 2387 | | // We added a new entry, so we now need to extend the PLT sections. |
| 2388 | | assert(plt_index == elf.plt.count()); |
| 2389 | | elf.plt.putAssumeCapacityNoClobber(global_name, {}); |
| 2390 | | |
| 2391 | | switch (elf.ehdrField(.machine)) { |
| 2392 | | else => |machine| @panic(@tagName(machine)), |
| 2393 | | .X86_64 => { |
| 2394 | | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 2395 | | const plt_addr = plt_addr: switch (elf.shdrPtr(elf.shndx.plt)) { |
| 2396 | | inline else => |shdr| { |
| 2397 | | const old_size = 16 * (1 + plt_index); |
| 2398 | | assert(elf.targetLoad(&shdr.size) == old_size); |
| 2399 | | elf.targetStore(&shdr.size, old_size + 16); |
| 2400 | | const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16]; |
| 2401 | | @memcpy(plt_slice, &[16]u8{ |
| 2402 | | 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 |
| 2403 | | 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0 |
| 2404 | | 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0 |
| 2405 | | 0x66, 0x90, // xchg %ax,%ax |
| 2406 | | }); |
| 2407 | | std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian); |
| 2408 | | std.mem.writeInt( |
| 2409 | | i32, |
| 2410 | | plt_slice[10..][0..4], |
| 2411 | | -@as(i32, @intCast(old_size + 14)), |
| 2412 | | target_endian, |
| 2413 | | ); |
| 2414 | | break :plt_addr elf.targetLoad(&shdr.addr) + old_size; |
| 2415 | | }, |
| 2416 | | }; |
| 2417 | | |
| 2418 | | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; |
| 2419 | | switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 2420 | | inline else => |shdr, class| { |
| 2421 | | assert(elf.targetLoad(&shdr.size) == got_plt_offset); |
| 2422 | | elf.targetStore(&shdr.size, got_plt_offset + @sizeOf(class.ElfN().Addr)); |
| 2423 | | std.mem.writeInt( |
| 2424 | | class.ElfN().Addr, |
| 2425 | | got_plt_ni.slice(&elf.mf)[got_plt_offset..][0..@sizeOf(class.ElfN().Addr)], |
| 2426 | | @intCast(plt_addr), |
| 2427 | | target_endian, |
| 2428 | | ); |
| 2429 | | }, |
| 2430 | | } |
| 2431 | | |
| 2432 | | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; |
| 2433 | | switch (elf.shdrPtr(elf.shndx.plt_sec)) { |
| 2434 | | inline else => |shdr| { |
| 2435 | | const old_size = 16 * plt_index; |
| 2436 | | elf.targetStore(&shdr.size, old_size + 16); |
| 2437 | | const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..][0..16]; |
| 2438 | | @memcpy(plt_sec_slice, &[16]u8{ |
| 2439 | | 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 |
| 2440 | | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) |
| 2441 | | 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1) |
| 2442 | | }); |
| 2443 | | std.mem.writeInt( |
| 2444 | | i32, |
| 2445 | | plt_sec_slice[6..][0..4], |
| 2446 | | @intCast(@as(i64, @bitCast( |
| 2447 | | (got_plt_section.vaddr(elf) + got_plt_offset) -% (elf.targetLoad(&shdr.addr) + old_size + 10), |
| 2448 | | ))), |
| 2449 | | target_endian, |
| 2450 | | ); |
| 2451 | | }, |
| 2452 | | } |
| 2453 | | }, |
| 2454 | | .LOONGARCH => { |
| 2455 | | // add a .PLT entry, writing the template |
| 2456 | | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 2457 | | const plt_addr, const plt_slice = plt_entry: switch (elf.shdrPtr(elf.shndx.plt)) { |
| 2458 | | inline else => |shdr| { |
| 2459 | | const old_size = 16 * (1 + plt_index); |
| 2460 | | assert(elf.targetLoad(&shdr.size) == old_size); |
| 2461 | | elf.targetStore(&shdr.size, old_size + 16); |
| 2462 | | const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16]; |
| 2463 | | @memcpy(plt_slice, source: switch (elf.identClass()) { |
| 2464 | | .NONE, _ => unreachable, |
| 2465 | | inline .@"32", .@"64" => |elf_class| { |
| 2466 | | const ld_byte = if (elf_class == .@"64") 0xc0 else 0x80; |
| 2467 | | break :source &[16]u8{ |
| 2468 | | 0x1a, 0x00, 0x00, 0x0f, // pcalau12i $t3, %pc_hi20(func@.got.plt) |
| 2469 | | 0x28, ld_byte, 0x01, 0xef, // ld.w/d $t3, $t3, %lo12(func@.got.plt) |
| 2470 | | 0x4c, 0x00, 0x01, 0xed, // jirl $t1, $t3, 0 |
| 2471 | | 0x00, 0x2a, 0x00, 0x00, // break |
| 2472 | | }; |
| 2473 | | }, |
| 2474 | | }); |
| 2475 | | break :plt_entry .{ elf.targetLoad(&shdr.addr) + old_size, plt_slice }; |
| 2476 | | }, |
| 2477 | | }; |
| 2387 | const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class)); |
| 2388 | const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class)); |
| 2478 | 2389 | |
| 2479 | | // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry |
| 2480 | | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; |
| 2481 | | switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 2482 | | inline else => |shdr, class| { |
| 2483 | | assert(elf.targetLoad(&shdr.size) == got_plt_offset); |
| 2484 | | elf.targetStore(&shdr.size, got_plt_offset + @sizeOf(class.ElfN().Addr)); |
| 2485 | | std.mem.writeInt( |
| 2486 | | class.ElfN().Addr, |
| 2487 | | got_plt_ni.slice(&elf.mf)[got_plt_offset..][0..@sizeOf(class.ElfN().Addr)], |
| 2488 | | @intCast(plt_addr), |
| 2489 | | target_endian, |
| 2490 | | ); |
| 2491 | | }, |
| 2390 | const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name)); |
| 2391 | assert(elf.globalByName(this_name).? == global_ptr); |
| 2392 | |
| 2393 | const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name)); |
| 2394 | const other_global_ptr = elf.globalByName(other_name).?; |
| 2395 | assert(other_global_ptr.symtab_index == dest_index); |
| 2396 | |
| 2397 | // First swap the symtab entries... |
| 2398 | std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr); |
| 2399 | // ...then the `elf.symtab` metadata... |
| 2400 | std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf)); |
| 2401 | // ...then update the `elf.globals` tracking. |
| 2402 | global_ptr.symtab_index = dest_index; |
| 2403 | other_global_ptr.symtab_index = src_index; |
| 2492 | 2404 | } |
| 2493 | 2405 | |
| 2494 | | // relocate the PLT entry to point to the .GOT.PLT entry |
| 2495 | | const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset; |
| 2496 | | // TODO: handle overflow gracefully |
| 2497 | | link.loongarch.writeJ20(plt_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr)); |
| 2498 | | link.loongarch.writeK12(plt_slice[4..8], @truncate(got_plt_abs)); |
| 2499 | | }, |
| 2500 | | .SPARCV9 => { |
| 2501 | | // add a .PLT entry, writing the template |
| 2502 | | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 2503 | | switch (elf.shdrPtr(elf.shndx.plt)) { |
| 2504 | | inline else => |shdr| { |
| 2505 | | assert(elf.targetLoad(&shdr.size) == got_plt_offset); |
| 2506 | | elf.targetStore(&shdr.size, got_plt_offset + 32); |
| 2507 | | const plt_slice: []u32 = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[got_plt_offset..][0..32])); |
| 2508 | | // sethi (. - .plt[0]), %g1 |
| 2509 | | // ba,a %xcc, .plt[1] |
| 2510 | | // nop |
| 2511 | | // nop |
| 2512 | | // nop |
| 2513 | | // nop |
| 2514 | | // nop |
| 2515 | | // nop |
| 2516 | | @memcpy(plt_slice, &([2]u32{ |
| 2517 | | // TODO: handle overflow gracefully |
| 2518 | | @bitCast(link.sparc.reloc.Imm22{ |
| 2519 | | .imm22 = @truncate(got_plt_offset), |
| 2520 | | .b22_31 = 0b0000000011, |
| 2521 | | }), |
| 2522 | | @bitCast(link.sparc.reloc.Disp19{ |
| 2523 | | .disp19 = @truncate((got_plt_offset + 4 - 32) >> 2), |
| 2524 | | .b19_31 = 0b1100001101000, |
| 2525 | | }), |
| 2526 | | } ++ @as([6]u32, @splat(0x01000000)))); |
| 2527 | | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllElements(u32, plt_slice); |
| 2528 | | }, |
| 2406 | // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact, |
| 2407 | // we'll move another symbol into its place just like we did above. |
| 2408 | if (global_ptr.dynsym_index != 0) { |
| 2409 | const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class)); |
| 2410 | |
| 2411 | const ent_size = @sizeOf(class.ElfN().Sym); |
| 2412 | assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size); |
| 2413 | |
| 2414 | // We're going to decrease the size of `.dynsym`, thereby removing its last index. |
| 2415 | const old_size = elf.targetLoad(&dynsym_shdr.size); |
| 2416 | const new_size = old_size - ent_size; |
| 2417 | const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size)); |
| 2418 | |
| 2419 | const free_dynsym_index = global_ptr.dynsym_index; |
| 2420 | global_ptr.dynsym_index = 0; |
| 2421 | |
| 2422 | if (free_dynsym_index != remove_dynsym_index) { |
| 2423 | // The demoted global wasn't the last entry, so move whatever entry we just |
| 2424 | // truncated out of dynsym into its place. |
| 2425 | |
| 2426 | const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class)); |
| 2427 | const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class)); |
| 2428 | |
| 2429 | const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name)); |
| 2430 | const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf)); |
| 2431 | const moved_global_ptr = elf.globalByName(moved_name).?; |
| 2432 | |
| 2433 | dest_dynsym_ptr.* = src_dynsym_ptr.*; |
| 2434 | |
| 2435 | assert(moved_global_ptr.dynsym_index == remove_dynsym_index); |
| 2436 | moved_global_ptr.dynsym_index = free_dynsym_index; |
| 2437 | |
| 2438 | // Since that symbol's dynsym index has changed, we'll have to update any |
| 2439 | // relocation entries targeting it. |
| 2440 | elf.changed_symtab_index.putAssumeCapacity(moved_name, {}); |
| 2441 | } |
| 2442 | |
| 2443 | // Now that we've given that symbol a new home, actually decrease the section size. |
| 2444 | elf.targetStore(&dynsym_shdr.size, new_size); |
| 2529 | 2445 | } |
| 2530 | 2446 | }, |
| 2531 | 2447 | } |
| ... | ... | @@ -2660,7 +2576,7 @@ const Symbol = struct { |
| 2660 | 2576 | } |
| 2661 | 2577 | |
| 2662 | 2578 | // Re-apply relocations targeting this symbol |
| 2663 | | if (elf.ehdrField(.type) != .REL) { |
| 2579 | if (elf.ehdrType() != .REL) { |
| 2664 | 2580 | sym_id.applyTargetRelocs(elf); |
| 2665 | 2581 | } |
| 2666 | 2582 | |
| ... | ... | @@ -2678,7 +2594,7 @@ const Symbol = struct { |
| 2678 | 2594 | } |
| 2679 | 2595 | |
| 2680 | 2596 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 2681 | | assert(elf.ehdrField(.type) != .REL); |
| 2597 | assert(elf.ehdrType() != .REL); |
| 2682 | 2598 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 2683 | 2599 | while (ri != .none) { |
| 2684 | 2600 | const reloc = ri.get(elf); |
| ... | ... | @@ -2693,7 +2609,7 @@ const Symbol = struct { |
| 2693 | 2609 | /// |
| 2694 | 2610 | /// Asserts we are creating a DSO. |
| 2695 | 2611 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 2696 | | assert(elf.ehdrField(.type) != .REL); |
| 2612 | assert(elf.ehdrType() != .REL); |
| 2697 | 2613 | assert(elf.shndx.dynamic != .UNDEF); |
| 2698 | 2614 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 2699 | 2615 | while (ri != .none) { |
| ... | ... | @@ -2713,7 +2629,20 @@ const Symbol = struct { |
| 2713 | 2629 | const reloc = ri.get(elf); |
| 2714 | 2630 | ri = reloc.next; |
| 2715 | 2631 | assert(reloc.target == sym_id); |
| 2716 | | if (!reloc.type.isAbsAddr(elf)) continue; |
| 2632 | switch (reloc.type.target) { |
| 2633 | // Only relocations which resolve to absolute addresses require runtime |
| 2634 | // `R_*_RELATIVE` relocations. |
| 2635 | .special, |
| 2636 | .pltrel, |
| 2637 | .rel, |
| 2638 | .dtpoff, |
| 2639 | .tpoff, |
| 2640 | .size, |
| 2641 | => continue, |
| 2642 | |
| 2643 | .abs, .pltabs => {}, |
| 2644 | } |
| 2645 | if (!reloc.type.action.simple.dest.isAddr(elf)) continue; |
| 2717 | 2646 | switch (elf.nodeWantsDsoRelocation(reloc.node)) { |
| 2718 | 2647 | .no => continue, |
| 2719 | 2648 | .yes_textrel => elf.textrel_count += 1, |
| ... | ... | @@ -2781,8 +2710,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| 2781 | 2710 | } { |
| 2782 | 2711 | const comp = elf.base.comp; |
| 2783 | 2712 | |
| 2784 | | const runtime_load_addr = switch (elf.ehdrField(.type)) { |
| 2785 | | .NONE, .CORE, _ => unreachable, |
| 2713 | const runtime_load_addr = switch (elf.ehdrType()) { |
| 2786 | 2714 | .REL => unreachable, |
| 2787 | 2715 | .DYN => true, |
| 2788 | 2716 | .EXEC => false, |
| ... | ... | @@ -2933,10 +2861,10 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id { |
| 2933 | 2861 | .size = 0, |
| 2934 | 2862 | .type = opts.type, |
| 2935 | 2863 | .bind = switch (opts.linkage) { |
| 2936 | | .internal => @panic("TODO internal extern symbol"), |
| 2937 | 2864 | .strong => .strong, |
| 2938 | 2865 | .weak => .weak, |
| 2939 | | .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): link_once is not supported", .{}), |
| 2866 | .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}), |
| 2867 | .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}), |
| 2940 | 2868 | }, |
| 2941 | 2869 | .visibility = switch (opts.visibility) { |
| 2942 | 2870 | .default => .DEFAULT, |
| ... | ... | @@ -2965,6 +2893,9 @@ pub fn addReloc( |
| 2965 | 2893 | }; |
| 2966 | 2894 | elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) { |
| 2967 | 2895 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), |
| 2896 | error.UnknownRelocation => unreachable, // codegen bug |
| 2897 | error.NonStaticRelocation => unreachable, // codegen bug |
| 2898 | error.UnimplementedRelocation => unreachable, // codegen bug (asking Elf2 for a relocation it does not support) |
| 2968 | 2899 | else => |e| return e, |
| 2969 | 2900 | }; |
| 2970 | 2901 | } |
| ... | ... | @@ -3149,23 +3080,6 @@ const StringTable = struct { |
| 3149 | 3080 | } |
| 3150 | 3081 | }; |
| 3151 | 3082 | |
| 3152 | | const GotIndex = enum(u32) { |
| 3153 | | none = std.math.maxInt(u32), |
| 3154 | | _, |
| 3155 | | |
| 3156 | | pub fn wrap(i: ?u32) GotIndex { |
| 3157 | | const gi: GotIndex = @enumFromInt(i orelse return .none); |
| 3158 | | assert(gi != .none); |
| 3159 | | return gi; |
| 3160 | | } |
| 3161 | | pub fn unwrap(gi: GotIndex) ?u32 { |
| 3162 | | return switch (gi) { |
| 3163 | | _ => @intFromEnum(gi), |
| 3164 | | .none => null, |
| 3165 | | }; |
| 3166 | | } |
| 3167 | | }; |
| 3168 | | |
| 3169 | 3083 | pub fn open( |
| 3170 | 3084 | arena: std.mem.Allocator, |
| 3171 | 3085 | comp: *Compilation, |
| ... | ... | @@ -3212,7 +3126,7 @@ fn create( |
| 3212 | 3126 | .amdpal => .AMDGPU_PAL, |
| 3213 | 3127 | .mesa3d => .AMDGPU_MESA3D, |
| 3214 | 3128 | }; |
| 3215 | | const @"type": std.elf.ET = switch (comp.config.output_mode) { |
| 3129 | const @"type": EhdrType = switch (comp.config.output_mode) { |
| 3216 | 3130 | .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC, |
| 3217 | 3131 | .Lib => switch (comp.config.link_mode) { |
| 3218 | 3132 | .static => .REL, |
| ... | ... | @@ -3220,7 +3134,9 @@ fn create( |
| 3220 | 3134 | }, |
| 3221 | 3135 | .Obj => .REL, |
| 3222 | 3136 | }; |
| 3223 | | const machine = target.toElfMachine(); |
| 3137 | const machine = EhdrMachine.fromElf(target.toElfMachine()) orelse { |
| 3138 | std.debug.panic("TODO(Elf2): add support for target machine '{t}'", .{target.toElfMachine()}); |
| 3139 | }; |
| 3224 | 3140 | const maybe_interp = switch (comp.config.link_mode) { |
| 3225 | 3141 | .static => null, |
| 3226 | 3142 | .dynamic => switch (comp.config.output_mode) { |
| ... | ... | @@ -3276,6 +3192,12 @@ fn create( |
| 3276 | 3192 | .fini_array = .UNDEF, |
| 3277 | 3193 | .preinit_array = .UNDEF, |
| 3278 | 3194 | }, |
| 3195 | .dynamic = .{ |
| 3196 | .flags = 0, |
| 3197 | .flags_1 = 0, |
| 3198 | .rpath = .empty, |
| 3199 | .soname = .empty, |
| 3200 | }, |
| 3279 | 3201 | .symtab = .empty, |
| 3280 | 3202 | .globals = .{ |
| 3281 | 3203 | .strong_def = .empty, |
| ... | ... | @@ -3310,10 +3232,12 @@ fn create( |
| 3310 | 3232 | .tls_size_symbol_relocs = .empty, |
| 3311 | 3233 | .section_by_name = .empty, |
| 3312 | 3234 | .changed_symtab_index = .empty, |
| 3235 | .textrel_count = 0, |
| 3236 | .overflowed_reloc_count = 0, |
| 3237 | .misaligned_reloc_count = 0, |
| 3313 | 3238 | .const_prog_node = .none, |
| 3314 | 3239 | .synth_prog_node = .none, |
| 3315 | 3240 | .input_prog_node = .none, |
| 3316 | | .textrel_count = 0, |
| 3317 | 3241 | }; |
| 3318 | 3242 | errdefer elf.deinit(); |
| 3319 | 3243 | |
| ... | ... | @@ -3362,14 +3286,14 @@ fn initHeaders( |
| 3362 | 3286 | class: std.elf.CLASS, |
| 3363 | 3287 | data: std.elf.DATA, |
| 3364 | 3288 | osabi: std.elf.OSABI, |
| 3365 | | @"type": std.elf.ET, |
| 3366 | | machine: std.elf.EM, |
| 3289 | @"type": EhdrType, |
| 3290 | machine: EhdrMachine, |
| 3367 | 3291 | maybe_interp: ?[]const u8, |
| 3368 | | ) !void { |
| 3292 | ) Error!void { |
| 3369 | 3293 | const comp = elf.base.comp; |
| 3370 | 3294 | const gpa = comp.gpa; |
| 3295 | |
| 3371 | 3296 | const have_dynamic_section = switch (@"type") { |
| 3372 | | .NONE, .CORE, _ => unreachable, |
| 3373 | 3297 | .REL => false, |
| 3374 | 3298 | .EXEC => comp.config.link_mode == .dynamic, |
| 3375 | 3299 | .DYN => true, |
| ... | ... | @@ -3380,13 +3304,7 @@ fn initHeaders( |
| 3380 | 3304 | .@"64" => .@"8", |
| 3381 | 3305 | }; |
| 3382 | 3306 | |
| 3383 | | const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const got_plt, const plt_sec = |
| 3384 | | switch (machine) { |
| 3385 | | else => @panic(@tagName(machine)), |
| 3386 | | .LOONGARCH => .{ 16 * 2, .@"4", true, false }, |
| 3387 | | .SPARCV9 => .{ 32 * 4, .fromByteUnits(256), false, false }, |
| 3388 | | .X86_64 => .{ 16, .@"16", true, true }, |
| 3389 | | }; |
| 3307 | const plt: PltInfo = .fromMachine(machine); |
| 3390 | 3308 | |
| 3391 | 3309 | const shnum: u32 = shnum: { |
| 3392 | 3310 | var shnum: u32 = 1; // reserved ("null") shdr |
| ... | ... | @@ -3408,9 +3326,9 @@ fn initHeaders( |
| 3408 | 3326 | } |
| 3409 | 3327 | if (@"type" != .REL) { |
| 3410 | 3328 | shnum += 1; // .got |
| 3411 | | shnum += @intFromBool(got_plt); // .got.plt |
| 3329 | shnum += @intFromBool(plt.got_plt != null); // .got.plt |
| 3412 | 3330 | shnum += 1; // .plt |
| 3413 | | shnum += @intFromBool(plt_sec); // .plt_sec |
| 3331 | shnum += @intFromBool(plt.plt_sec != null); // .plt_sec |
| 3414 | 3332 | } |
| 3415 | 3333 | break :shnum shnum; |
| 3416 | 3334 | }; |
| ... | ... | @@ -3427,7 +3345,6 @@ fn initHeaders( |
| 3427 | 3345 | gnu_stack: u32, |
| 3428 | 3346 | }, const phnum: u32 = ph: { |
| 3429 | 3347 | switch (@"type") { |
| 3430 | | .NONE, .CORE, _ => unreachable, |
| 3431 | 3348 | .REL => break :ph .{ undefined, 0 }, |
| 3432 | 3349 | .EXEC, .DYN => {}, |
| 3433 | 3350 | } |
| ... | ... | @@ -3483,9 +3400,9 @@ fn initHeaders( |
| 3483 | 3400 | try elf.symtab.ensureTotalCapacity(gpa, 1); |
| 3484 | 3401 | elf.nodes.appendAssumeCapacity(.file); |
| 3485 | 3402 | |
| 3486 | | switch (class) { |
| 3403 | const entsize: struct { ph: u32, sh: u32 } = switch (class) { |
| 3487 | 3404 | .NONE, _ => unreachable, |
| 3488 | | inline else => |ct_class| { |
| 3405 | inline else => |ct_class| entsize: { |
| 3489 | 3406 | const ElfN = ct_class.ElfN(); |
| 3490 | 3407 | assert(elf.ni.ehdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.file, .{ |
| 3491 | 3408 | .size = @sizeOf(ElfN.Ehdr), |
| ... | ... | @@ -3502,42 +3419,35 @@ fn initHeaders( |
| 3502 | 3419 | .osabi = osabi, |
| 3503 | 3420 | .abiversion = 0, |
| 3504 | 3421 | }; |
| 3505 | | ehdr.type = @"type"; |
| 3506 | | ehdr.machine = machine; |
| 3422 | ehdr.type = @"type".toElf(); |
| 3423 | ehdr.machine = machine.toElf(); |
| 3507 | 3424 | ehdr.version = 1; |
| 3508 | 3425 | ehdr.entry = 0; |
| 3509 | 3426 | ehdr.phoff = 0; |
| 3510 | 3427 | ehdr.shoff = 0; |
| 3511 | 3428 | ehdr.flags = switch (machine) { |
| 3512 | | .LOONGARCH => e_flags: { |
| 3513 | | const target_cpu = &elf.base.comp.getTarget().cpu; |
| 3514 | | const e_flags: std.elf.loongarch.EFlags = .{ |
| 3515 | | .base_abi_modifier = if (target_cpu.has(.loongarch, .d)) |
| 3516 | | .d |
| 3517 | | else if (target_cpu.has(.loongarch, .f)) |
| 3518 | | .f |
| 3519 | | else |
| 3520 | | .s, |
| 3521 | | .abi_extension = .base, |
| 3522 | | .abi_version = 1, |
| 3523 | | }; |
| 3524 | | break :e_flags @bitCast(e_flags); |
| 3525 | | }, |
| 3526 | | .SPARCV9 => e_flags: { |
| 3527 | | const e_flags: std.elf.sparc.EFlags = .{ |
| 3528 | | .mm = .rmo, |
| 3529 | | .ext = .{ |
| 3530 | | .@"32plus" = false, |
| 3531 | | .sun_us1 = false, |
| 3532 | | .hal_r1 = false, |
| 3533 | | .sun_us3 = false, |
| 3534 | | .le_data = false, |
| 3535 | | }, |
| 3536 | | }; |
| 3537 | | break :e_flags @bitCast(e_flags); |
| 3538 | | }, |
| 3539 | | .X86_64 => 0, |
| 3540 | | else => @panic(@tagName(machine)), |
| 3429 | .LOONGARCH => .{ .loongarch = .{ |
| 3430 | .base_abi_modifier = mod: { |
| 3431 | const cpu = comp.getTarget().cpu; |
| 3432 | if (cpu.has(.loongarch, .d)) break :mod .d; |
| 3433 | if (cpu.has(.loongarch, .f)) break :mod .f; |
| 3434 | break :mod .s; |
| 3435 | }, |
| 3436 | .abi_extension = .base, |
| 3437 | .abi_version = 1, |
| 3438 | } }, |
| 3439 | .SPARCV9 => .{ .sparc = .{ |
| 3440 | .mm = .rmo, |
| 3441 | .ext = .{ |
| 3442 | .@"32plus" = false, |
| 3443 | .sun_us1 = false, |
| 3444 | .hal_r1 = false, |
| 3445 | .sun_us3 = false, |
| 3446 | .le_data = false, |
| 3447 | }, |
| 3448 | } }, |
| 3449 | .X86_64 => .{ .int = 0 }, |
| 3450 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 3541 | 3451 | }; |
| 3542 | 3452 | ehdr.ehsize = @sizeOf(ElfN.Ehdr); |
| 3543 | 3453 | ehdr.phentsize = @sizeOf(ElfN.Phdr); |
| ... | ... | @@ -3546,11 +3456,13 @@ fn initHeaders( |
| 3546 | 3456 | ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection` |
| 3547 | 3457 | ehdr.shstrndx = std.elf.SHN_UNDEF; |
| 3548 | 3458 | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); |
| 3459 | |
| 3460 | break :entsize .{ .ph = @sizeOf(ElfN.Phdr), .sh = @sizeOf(ElfN.Shdr) }; |
| 3549 | 3461 | }, |
| 3550 | | } |
| 3462 | }; |
| 3551 | 3463 | |
| 3552 | 3464 | assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ |
| 3553 | | .size = @as(u64, elf.ehdrField(.shentsize)) * @as(u64, elf.ehdrField(.shnum)), |
| 3465 | .size = 1 * entsize.sh, // as above, only the null shdr initially |
| 3554 | 3466 | .alignment = elf.mf.flags.block_size, |
| 3555 | 3467 | .moved = true, |
| 3556 | 3468 | .resized = true, |
| ... | ... | @@ -3558,28 +3470,24 @@ fn initHeaders( |
| 3558 | 3470 | elf.nodes.appendAssumeCapacity(.shdr); |
| 3559 | 3471 | |
| 3560 | 3472 | const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) { |
| 3561 | | .BPF, |
| 3562 | | .SPARCV9, |
| 3563 | | => 0x100000, |
| 3564 | | .AARCH64, |
| 3565 | | .AMDGPU, |
| 3566 | | .QDSP6, |
| 3567 | | .MIPS, |
| 3568 | | .PPC, |
| 3569 | | .PPC64, |
| 3570 | | .SPARC, |
| 3571 | | .SPARC32PLUS, |
| 3572 | | => 0x10000, |
| 3573 | | .LOONGARCH, |
| 3574 | | => 0x4000, |
| 3575 | | .ARC_COMPACT2, |
| 3576 | | .@"68K", |
| 3577 | | => 0x2000, |
| 3578 | | .MSP430, |
| 3579 | | => 0x4, |
| 3580 | | .AVR, |
| 3581 | | => 0x1, |
| 3582 | | else => 0x1000, |
| 3473 | .AARCH64 => 0x10000, |
| 3474 | .LOONGARCH => 0x4000, |
| 3475 | .PPC64 => 0x10000, |
| 3476 | .RISCV => 0x1000, |
| 3477 | .SPARCV9 => 0x100000, |
| 3478 | .X86_64 => 0x1000, |
| 3479 | |
| 3480 | //.@"68K" => 0x2000, |
| 3481 | //.AMDGPU => 0x10000, |
| 3482 | //.ARC_COMPACT2 => 0x2000, |
| 3483 | //.AVR => 0x1, |
| 3484 | //.BPF => 0x100000, |
| 3485 | //.MIPS => 0x10000, |
| 3486 | //.MSP430 => 0x4, |
| 3487 | //.PPC => 0x10000, |
| 3488 | //.QDSP6 => 0x10000, |
| 3489 | //.SPARC => 0x10000, |
| 3490 | //.SPARC32PLUS => 0x10000, |
| 3583 | 3491 | }); |
| 3584 | 3492 | |
| 3585 | 3493 | var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: { |
| ... | ... | @@ -3592,7 +3500,7 @@ fn initHeaders( |
| 3592 | 3500 | elf.phdrs.items[phndx.rodata] = elf.ni.rodata; |
| 3593 | 3501 | |
| 3594 | 3502 | assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ |
| 3595 | | .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum), |
| 3503 | .size = @as(u64, phnum) * entsize.ph, |
| 3596 | 3504 | .alignment = addr_align, |
| 3597 | 3505 | .moved = true, |
| 3598 | 3506 | .resized = true, |
| ... | ... | @@ -3627,16 +3535,16 @@ fn initHeaders( |
| 3627 | 3535 | |
| 3628 | 3536 | elf.phdrs.items[phndx.gnu_stack] = .none; |
| 3629 | 3537 | |
| 3630 | | break :ph_vaddr switch (elf.ehdrField(.type)) { |
| 3631 | | .NONE, .CORE, _ => unreachable, |
| 3538 | break :ph_vaddr switch (elf.ehdrType()) { |
| 3632 | 3539 | .REL, .DYN => 0, |
| 3633 | 3540 | .EXEC => switch (machine) { |
| 3634 | | .@"386" => 0x400000, |
| 3635 | | .AARCH64, .X86_64 => 0x200000, |
| 3636 | | .PPC, .PPC64 => 0x10000000, |
| 3637 | | .S390 => 0x1000000, |
| 3541 | .AARCH64, |
| 3542 | => 0x200000, |
| 3543 | .LOONGARCH => 0x10000, |
| 3544 | .PPC64 => 0x10000000, |
| 3545 | .RISCV => 0x10000, |
| 3638 | 3546 | .SPARCV9 => 0x100000, |
| 3639 | | else => 0x10000, |
| 3547 | .X86_64 => 0x200000, |
| 3640 | 3548 | }, |
| 3641 | 3549 | }; |
| 3642 | 3550 | } else undefined; |
| ... | ... | @@ -3870,28 +3778,21 @@ fn initHeaders( |
| 3870 | 3778 | .type = .PROGBITS, |
| 3871 | 3779 | // Reserve space for the reserved words, populated later. |
| 3872 | 3780 | .size = switch (machine) { |
| 3873 | | else => @panic(@tagName(machine)), |
| 3781 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 3874 | 3782 | .X86_64 => 3 * elf.targetPtrSize(), |
| 3875 | | .LOONGARCH, |
| 3876 | | .SPARCV9, |
| 3877 | | => elf.targetPtrSize(), |
| 3783 | .LOONGARCH, .SPARCV9 => elf.targetPtrSize(), |
| 3878 | 3784 | }, |
| 3879 | 3785 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 3880 | 3786 | .addralign = addr_align, |
| 3881 | 3787 | .entsize = @intCast(addr_align.toByteUnits()), |
| 3882 | 3788 | }); |
| 3883 | | if (got_plt) elf.shndx.got_plt = try elf.addSection( |
| 3789 | if (plt.got_plt) |got_plt| elf.shndx.got_plt = try elf.addSection( |
| 3884 | 3790 | if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data, |
| 3885 | 3791 | .{ |
| 3886 | 3792 | .name = ".got.plt", |
| 3887 | 3793 | .type = .PROGBITS, |
| 3888 | 3794 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 3889 | | .size = switch (machine) { |
| 3890 | | else => @panic(@tagName(machine)), |
| 3891 | | .@"386" => 3 * 4, |
| 3892 | | .X86_64 => 3 * 8, |
| 3893 | | .LOONGARCH => 2 * elf.targetPtrSize(), |
| 3894 | | }, |
| 3795 | .size = got_plt.header_entries * elf.targetPtrSize(), |
| 3895 | 3796 | .addralign = addr_align, |
| 3896 | 3797 | .entsize = @intCast(addr_align.toByteUnits()), |
| 3897 | 3798 | }, |
| ... | ... | @@ -3902,19 +3803,16 @@ fn initHeaders( |
| 3902 | 3803 | .flags = .{ |
| 3903 | 3804 | .ALLOC = true, |
| 3904 | 3805 | .EXECINSTR = true, |
| 3905 | | .WRITE = switch (machine) { |
| 3906 | | .SPARCV9 => true, |
| 3907 | | else => false, |
| 3908 | | }, |
| 3806 | .WRITE = plt.got_plt == null, |
| 3909 | 3807 | }, |
| 3910 | | .size = init_plt_size, |
| 3911 | | .addralign = plt_align, |
| 3808 | .size = plt.entry_size * plt.header_entries, |
| 3809 | .addralign = plt.@"align", |
| 3912 | 3810 | .node_align = elf.mf.flags.block_size, |
| 3913 | 3811 | }); |
| 3914 | | if (plt_sec) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{ |
| 3812 | if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{ |
| 3915 | 3813 | .name = ".plt.sec", |
| 3916 | 3814 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, |
| 3917 | | .addralign = plt_align, |
| 3815 | .addralign = plt.@"align", |
| 3918 | 3816 | .node_align = elf.mf.flags.block_size, |
| 3919 | 3817 | }); |
| 3920 | 3818 | if (maybe_interp) |interp| { |
| ... | ... | @@ -4005,7 +3903,7 @@ fn initHeaders( |
| 4005 | 3903 | .type = .RELA, |
| 4006 | 3904 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, |
| 4007 | 3905 | .link = elf.shndx.dynsym.toSection().?, |
| 4008 | | .info = (if (got_plt) elf.shndx.got_plt else elf.shndx.plt).toSection().?, |
| 3906 | .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?, |
| 4009 | 3907 | .addralign = addr_align, |
| 4010 | 3908 | .entsize = rela_size, |
| 4011 | 3909 | .node_align = elf.mf.flags.block_size, |
| ... | ... | @@ -4019,7 +3917,7 @@ fn initHeaders( |
| 4019 | 3917 | .node_align = addr_align, |
| 4020 | 3918 | }); |
| 4021 | 3919 | switch (machine) { |
| 4022 | | else => @panic(@tagName(machine)), |
| 3920 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 4023 | 3921 | .X86_64 => { |
| 4024 | 3922 | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 4025 | 3923 | const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi); |
| ... | ... | @@ -4035,14 +3933,14 @@ fn initHeaders( |
| 4035 | 3933 | 2, |
| 4036 | 3934 | got_plt_sym, |
| 4037 | 3935 | 8 * 1 - 4, |
| 4038 | | .rel32, |
| 3936 | .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }), |
| 4039 | 3937 | ); |
| 4040 | 3938 | try elf.addSymbolRelocAssumeCapacity( |
| 4041 | 3939 | plt_ni, |
| 4042 | 3940 | 8, |
| 4043 | 3941 | got_plt_sym, |
| 4044 | 3942 | 8 * 2 - 4, |
| 4045 | | .rel32, |
| 3943 | .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }), |
| 4046 | 3944 | ); |
| 4047 | 3945 | }, |
| 4048 | 3946 | .LOONGARCH => { |
| ... | ... | @@ -4073,9 +3971,24 @@ fn initHeaders( |
| 4073 | 3971 | }); |
| 4074 | 3972 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 4075 | 3973 | try elf.ensureUnusedRelocCapacity(plt_ni, 3); |
| 4076 | | try elf.addSymbolRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .larch_rel32_hi20); |
| 4077 | | try elf.addSymbolRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .larch_abs32_lo12); |
| 4078 | | try elf.addSymbolRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .larch_abs32_lo12); |
| 3974 | elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LARCH = .PCALA_HI20 }) catch |err| switch (err) { |
| 3975 | error.UnknownRelocation => unreachable, |
| 3976 | error.NonStaticRelocation => unreachable, |
| 3977 | error.UnimplementedRelocation => unreachable, |
| 3978 | else => |e| return e, |
| 3979 | }; |
| 3980 | elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) { |
| 3981 | error.UnknownRelocation => unreachable, |
| 3982 | error.NonStaticRelocation => unreachable, |
| 3983 | error.UnimplementedRelocation => unreachable, |
| 3984 | else => |e| return e, |
| 3985 | }; |
| 3986 | elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) { |
| 3987 | error.UnknownRelocation => unreachable, |
| 3988 | error.NonStaticRelocation => unreachable, |
| 3989 | error.UnimplementedRelocation => unreachable, |
| 3990 | else => |e| return e, |
| 3991 | }; |
| 4079 | 3992 | }, |
| 4080 | 3993 | .SPARCV9 => {}, |
| 4081 | 3994 | } |
| ... | ... | @@ -4092,7 +4005,7 @@ fn initHeaders( |
| 4092 | 4005 | |
| 4093 | 4006 | // Populate reserved GOT words. |
| 4094 | 4007 | switch (machine) { |
| 4095 | | else => @panic(@tagName(machine)), |
| 4008 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 4096 | 4009 | .X86_64 => { |
| 4097 | 4010 | try elf.got.ensureUnusedCapacity(gpa, 3); |
| 4098 | 4011 | elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { |
| ... | ... | @@ -4102,9 +4015,7 @@ fn initHeaders( |
| 4102 | 4015 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none); |
| 4103 | 4016 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none); |
| 4104 | 4017 | }, |
| 4105 | | .LOONGARCH, |
| 4106 | | .SPARCV9, |
| 4107 | | => { |
| 4018 | .LOONGARCH, .SPARCV9 => { |
| 4108 | 4019 | try elf.got.ensureUnusedCapacity(gpa, 1); |
| 4109 | 4020 | elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { |
| 4110 | 4021 | true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, |
| ... | ... | @@ -4153,8 +4064,17 @@ fn initHeaders( |
| 4153 | 4064 | .node = elf.shndx.got.get(elf).ni, |
| 4154 | 4065 | .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"), |
| 4155 | 4066 | .value = switch (machine) { |
| 4156 | | .QDSP6, .@"386", .X86_64 => elf.shndx.got_plt.vaddr(elf), |
| 4157 | | else => elf.shndx.got.vaddr(elf), |
| 4067 | .AARCH64, |
| 4068 | .LOONGARCH, |
| 4069 | .PPC64, |
| 4070 | .RISCV, |
| 4071 | .SPARCV9, |
| 4072 | => elf.shndx.got.vaddr(elf), |
| 4073 | |
| 4074 | //.QDSP6, |
| 4075 | //.@"386", |
| 4076 | .X86_64, |
| 4077 | => elf.shndx.got_plt.vaddr(elf), |
| 4158 | 4078 | }, |
| 4159 | 4079 | .size = 0, |
| 4160 | 4080 | .type = .NOTYPE, |
| ... | ... | @@ -4267,6 +4187,29 @@ fn initHeaders( |
| 4267 | 4187 | const shndx: Section.Index = @enumFromInt(shndx_raw); |
| 4268 | 4188 | elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); |
| 4269 | 4189 | } |
| 4190 | |
| 4191 | if (have_dynamic_section) elf.dynamic = .{ |
| 4192 | .flags = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0, |
| 4193 | .flags_1 = f: { |
| 4194 | var f: u32 = 0; |
| 4195 | if (elf.options.z_now) f |= std.elf.DF_1_NOW; |
| 4196 | if (comp.config.output_mode == .Exe and comp.config.pie) f |= std.elf.DF_1_PIE; |
| 4197 | break :f f; |
| 4198 | }, |
| 4199 | .rpath = str: { |
| 4200 | var buf: std.ArrayList(u8) = .empty; |
| 4201 | defer buf.deinit(gpa); |
| 4202 | for (elf.options.rpath_list, 0..) |path, i| { |
| 4203 | if (i > 0) try buf.append(gpa, ':'); |
| 4204 | try buf.appendSlice(gpa, path); |
| 4205 | } |
| 4206 | break :str try elf.string(.dynstr, buf.items); |
| 4207 | }, |
| 4208 | .soname = str: { |
| 4209 | const slice = elf.options.soname orelse break :str .empty; |
| 4210 | break :str try elf.string(.dynstr, slice); |
| 4211 | }, |
| 4212 | }; |
| 4270 | 4213 | } |
| 4271 | 4214 | |
| 4272 | 4215 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { |
| ... | ... | @@ -4379,7 +4322,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 4379 | 4322 | if (ptr.* != .none) { |
| 4380 | 4323 | for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| { |
| 4381 | 4324 | if (reloc.node != ni) break; |
| 4382 | | reloc.* = .deleted; |
| 4325 | reloc.delete(elf); |
| 4383 | 4326 | } |
| 4384 | 4327 | } |
| 4385 | 4328 | ptr.* = @enumFromInt(elf.got_relocs.items.len); |
| ... | ... | @@ -4418,15 +4361,124 @@ fn flushMovedNodeRelocs( |
| 4418 | 4361 | fn identClass(elf: *const Elf) std.elf.CLASS { |
| 4419 | 4362 | return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]); |
| 4420 | 4363 | } |
| 4421 | | fn identData(elf: *const Elf) std.elf.DATA { |
| 4422 | | return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]); |
| 4364 | |
| 4365 | /// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can |
| 4366 | /// use exhaustive `switch` statements in the linker implementation. |
| 4367 | const EhdrMachine = enum(u16) { |
| 4368 | AARCH64 = @intFromEnum(std.elf.EM.AARCH64), |
| 4369 | LOONGARCH = @intFromEnum(std.elf.EM.LOONGARCH), |
| 4370 | PPC64 = @intFromEnum(std.elf.EM.PPC64), |
| 4371 | RISCV = @intFromEnum(std.elf.EM.RISCV), |
| 4372 | SPARCV9 = @intFromEnum(std.elf.EM.SPARCV9), |
| 4373 | X86_64 = @intFromEnum(std.elf.EM.X86_64), |
| 4374 | |
| 4375 | fn toElf(m: EhdrMachine) std.elf.EM { |
| 4376 | return @bitCast(m); |
| 4377 | } |
| 4378 | /// Returns `null` if `m` is not a supported ELF machine architecture. |
| 4379 | fn fromElf(m: std.elf.EM) ?EhdrMachine { |
| 4380 | return std.enums.fromInt(EhdrMachine, @intFromEnum(m)); |
| 4381 | } |
| 4382 | }; |
| 4383 | /// Like `std.elf.ET`, but only includes the types of ELF file we can produce, so that we can use |
| 4384 | /// exhaustive `switch` statements in the linker implementation. |
| 4385 | const EhdrType = enum(u16) { |
| 4386 | REL = @intFromEnum(std.elf.ET.REL), |
| 4387 | EXEC = @intFromEnum(std.elf.ET.EXEC), |
| 4388 | DYN = @intFromEnum(std.elf.ET.DYN), |
| 4389 | fn toElf(t: EhdrType) std.elf.ET { |
| 4390 | return @bitCast(t); |
| 4391 | } |
| 4392 | }; |
| 4393 | fn ehdrMachine(elf: *const Elf) EhdrMachine { |
| 4394 | const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf); |
| 4395 | switch (elf.identClass()) { |
| 4396 | .NONE, _ => unreachable, |
| 4397 | inline else => |class| { |
| 4398 | const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice)); |
| 4399 | return @bitCast(elf.targetLoad(&ehdr.machine)); |
| 4400 | }, |
| 4401 | } |
| 4402 | } |
| 4403 | fn ehdrType(elf: *const Elf) EhdrType { |
| 4404 | const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf); |
| 4405 | switch (elf.identClass()) { |
| 4406 | .NONE, _ => unreachable, |
| 4407 | inline else => |class| { |
| 4408 | const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice)); |
| 4409 | return @bitCast(elf.targetLoad(&ehdr.type)); |
| 4410 | }, |
| 4411 | } |
| 4423 | 4412 | } |
| 4424 | 4413 | |
| 4425 | | fn targetPtrSize(elf: *const Elf) u32 { |
| 4414 | fn targetPtrSize(elf: *const Elf) u8 { |
| 4426 | 4415 | return elf.identClass().size(); |
| 4427 | 4416 | } |
| 4428 | 4417 | fn targetEndian(elf: *const Elf) std.lang.Endian { |
| 4429 | | return elf.identData().endian(); |
| 4418 | const ident_data: std.elf.DATA = @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]); |
| 4419 | return ident_data.endian(); |
| 4420 | } |
| 4421 | fn targetTlsVariant(elf: *const Elf) union(enum) { |
| 4422 | /// TP points to the start of the TCB, which immediately precedes the executable's TLS block. |
| 4423 | I_original: struct { tcb_size: u8 }, |
| 4424 | /// TP points at a fixed offset from the start of the executable's TLS block. |
| 4425 | I_modified: struct { tp_off: u32 }, |
| 4426 | /// TP points to the TCB, which immediately *succeeds* the executable's TLS block. (In other |
| 4427 | /// words, TP points to the *end* of the executable's TLS block.) |
| 4428 | II, |
| 4429 | } { |
| 4430 | return switch (elf.ehdrMachine()) { |
| 4431 | .AARCH64 => .{ .I_original = .{ .tcb_size = 2 * elf.targetPtrSize() } }, |
| 4432 | .LOONGARCH => .{ .I_original = .{ .tcb_size = elf.targetPtrSize() } }, |
| 4433 | .PPC64 => .{ .I_modified = .{ .tp_off = 0x7000 } }, |
| 4434 | .RISCV => .{ .I_modified = .{ .tp_off = 0 } }, |
| 4435 | .SPARCV9 => .II, |
| 4436 | .X86_64 => .II, |
| 4437 | }; |
| 4438 | } |
| 4439 | const PltInfo = struct { |
| 4440 | /// If not `null`, there is a `.got.plt` section containing the target addresses, and the PLT |
| 4441 | /// itself is immutable. If `false`, JUMP_SLOT relocations write directly to the `.plt` section, |
| 4442 | /// which must therefore be mutable. |
| 4443 | got_plt: ?struct { header_entries: u8 }, |
| 4444 | /// If not `null`, there is a `.plt.sec` section, and every function in the PLT has both a |
| 4445 | /// `.plt` entry and a `.plt.sec` entry. Jumps targeting the PLT should jump to the `.plt.sec` |
| 4446 | /// entry, not the `.plt` entry. The `.plt.sec` section has no header entries, and is aligned to |
| 4447 | /// the same boundary as the `.plt` section. |
| 4448 | plt_sec: ?struct { entry_size: u8 }, |
| 4449 | @"align": std.mem.Alignment, |
| 4450 | entry_size: u8, |
| 4451 | header_entries: u8, |
| 4452 | |
| 4453 | fn fromMachine(machine: EhdrMachine) PltInfo { |
| 4454 | return switch (machine) { |
| 4455 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 4456 | .LOONGARCH => .{ |
| 4457 | .got_plt = .{ .header_entries = 2 }, |
| 4458 | .plt_sec = null, |
| 4459 | .@"align" = .@"4", |
| 4460 | .entry_size = 16, |
| 4461 | .header_entries = 2, |
| 4462 | }, |
| 4463 | .SPARCV9 => .{ |
| 4464 | .got_plt = null, |
| 4465 | .plt_sec = null, |
| 4466 | .@"align" = .fromByteUnits(256), |
| 4467 | .entry_size = 32, |
| 4468 | .header_entries = 4, |
| 4469 | }, |
| 4470 | .X86_64 => .{ |
| 4471 | .got_plt = .{ .header_entries = 3 }, |
| 4472 | .plt_sec = .{ .entry_size = 16 }, |
| 4473 | .@"align" = .@"16", |
| 4474 | .entry_size = 16, |
| 4475 | .header_entries = 1, |
| 4476 | }, |
| 4477 | }; |
| 4478 | } |
| 4479 | }; |
| 4480 | fn targetPltInfo(elf: *const Elf) PltInfo { |
| 4481 | return .fromMachine(elf.ehdrMachine()); |
| 4430 | 4482 | } |
| 4431 | 4483 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 4432 | 4484 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| ... | ... | @@ -4435,7 +4487,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi |
| 4435 | 4487 | return switch (@typeInfo(Child)) { |
| 4436 | 4488 | else => @compileError(@typeName(Child)), |
| 4437 | 4489 | .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()), |
| 4438 | | .@"enum" => |@"enum"| @enumFromInt(elf.targetLoad(@as(*align(alignment) @"enum".tag_type, @ptrCast(ptr)))), |
| 4490 | .@"enum" => |@"enum"| @enumFromInt(elf.targetLoad(@as(*align(alignment) const @"enum".tag_type, @ptrCast(ptr)))), |
| 4439 | 4491 | .@"struct" => |@"struct"| @bitCast( |
| 4440 | 4492 | elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))), |
| 4441 | 4493 | ), |
| ... | ... | @@ -4475,14 +4527,6 @@ fn ehdrPtr(elf: *Elf) EhdrPtr { |
| 4475 | 4527 | ), |
| 4476 | 4528 | }; |
| 4477 | 4529 | } |
| 4478 | | fn ehdrField( |
| 4479 | | elf: *Elf, |
| 4480 | | comptime field: std.meta.FieldEnum(std.elf.Elf64.Ehdr), |
| 4481 | | ) @FieldType(std.elf.Elf64.Ehdr, @tagName(field)) { |
| 4482 | | return switch (elf.ehdrPtr()) { |
| 4483 | | inline else => |ehdr| elf.targetLoad(&@field(ehdr, @tagName(field))), |
| 4484 | | }; |
| 4485 | | } |
| 4486 | 4530 | |
| 4487 | 4531 | const PhdrSlice = union(std.elf.CLASS) { |
| 4488 | 4532 | NONE: noreturn, |
| ... | ... | @@ -4490,7 +4534,7 @@ const PhdrSlice = union(std.elf.CLASS) { |
| 4490 | 4534 | @"64": []std.elf.Elf64.Phdr, |
| 4491 | 4535 | }; |
| 4492 | 4536 | fn phdrSlice(elf: *Elf) PhdrSlice { |
| 4493 | | assert(elf.ehdrField(.type) != .REL); |
| 4537 | assert(elf.ehdrType() != .REL); |
| 4494 | 4538 | const slice = elf.ni.phdr.slice(&elf.mf); |
| 4495 | 4539 | return switch (elf.identClass()) { |
| 4496 | 4540 | .NONE, _ => unreachable, |
| ... | ... | @@ -4587,8 +4631,7 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 4587 | 4631 | return error.StripSection; |
| 4588 | 4632 | } |
| 4589 | 4633 | |
| 4590 | | const name: []const u8 = switch (elf.ehdrField(.type)) { |
| 4591 | | .NONE, .CORE, _ => unreachable, |
| 4634 | const name: []const u8 = switch (elf.ehdrType()) { |
| 4592 | 4635 | .REL => opts.name, |
| 4593 | 4636 | .EXEC, .DYN => name: { |
| 4594 | 4637 | if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text"; |
| ... | ... | @@ -5050,7 +5093,7 @@ fn loadObject( |
| 5050 | 5093 | const ElfN = class.ElfN(); |
| 5051 | 5094 | const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); |
| 5052 | 5095 | if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{}); |
| 5053 | | if (ehdr.machine != elf.ehdrField(.machine)) |
| 5096 | if (ehdr.machine != elf.ehdrMachine().toElf()) |
| 5054 | 5097 | return diags.failParse(path, "bad machine", .{}); |
| 5055 | 5098 | if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; |
| 5056 | 5099 | if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size) |
| ... | ... | @@ -5383,23 +5426,43 @@ fn loadObject( |
| 5383 | 5426 | ); |
| 5384 | 5427 | const target = symmap.items[rel.info.sym - 1]; |
| 5385 | 5428 | if (target == Symbol.Id.null) { |
| 5386 | | // If this is not an SHF_ALLOC section, then let's let this |
| 5387 | | // slide for now, because it probably doesn't affect the final |
| 5429 | // If this is not an SHF_ALLOC section, then let's not report |
| 5430 | // this for now, because it probably doesn't affect the final |
| 5388 | 5431 | // binary's functionality for this section to be a bit broken. |
| 5389 | | if (!loc_sec.shdr.flags.shf.ALLOC) continue; |
| 5390 | | return diags.failParse( |
| 5391 | | path, |
| 5392 | | "unsupported symbol at index {d} required for relocation", |
| 5393 | | .{rel.info.sym}, |
| 5394 | | ); |
| 5432 | if (loc_sec.shdr.flags.shf.ALLOC) { |
| 5433 | diags.addParseError( |
| 5434 | path, |
| 5435 | "unsupported symbol at index {d} required for relocation", |
| 5436 | .{rel.info.sym}, |
| 5437 | ); |
| 5438 | } |
| 5439 | continue; |
| 5395 | 5440 | } |
| 5396 | | try elf.addRelocAssumeCapacity( |
| 5441 | const rt: MachineRelocType = .wrap(rel.info.type, elf); |
| 5442 | elf.addRelocAssumeCapacity( |
| 5397 | 5443 | loc_node, |
| 5398 | 5444 | rel.offset - loc_sec.shdr.addr, |
| 5399 | 5445 | target, |
| 5400 | 5446 | rel.addend, |
| 5401 | | .wrap(rel.info.type, elf), |
| 5402 | | ); |
| 5447 | rt, |
| 5448 | ) catch |err| switch (err) { |
| 5449 | error.UnknownRelocation => diags.addParseError( |
| 5450 | path, |
| 5451 | "unknown relocation type '{f}'", |
| 5452 | .{rt.fmt(elf)}, |
| 5453 | ), |
| 5454 | error.NonStaticRelocation => diags.addParseError( |
| 5455 | path, |
| 5456 | "non-static relocation type '{f}'", |
| 5457 | .{rt.fmt(elf)}, |
| 5458 | ), |
| 5459 | error.UnimplementedRelocation => diags.addParseError( |
| 5460 | path, |
| 5461 | "TODO(Elf2): unimplemented relocation type '{f}'", |
| 5462 | .{rt.fmt(elf)}, |
| 5463 | ), |
| 5464 | else => |e| return e, |
| 5465 | }; |
| 5403 | 5466 | } |
| 5404 | 5467 | }, |
| 5405 | 5468 | }; |
| ... | ... | @@ -5423,7 +5486,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars |
| 5423 | 5486 | const ElfN = class.ElfN(); |
| 5424 | 5487 | const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); |
| 5425 | 5488 | if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{}); |
| 5426 | | if (ehdr.machine != elf.ehdrField(.machine)) |
| 5489 | if (ehdr.machine != elf.ehdrMachine().toElf()) |
| 5427 | 5490 | return diags.failParse(path, "bad machine", .{}); |
| 5428 | 5491 | if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff); |
| 5429 | 5492 | // We're going to need to know the alignment of every section later. |
| ... | ... | @@ -5782,222 +5845,143 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5782 | 5845 | .file_symbol = zcu_file_symbol, |
| 5783 | 5846 | }; |
| 5784 | 5847 | } |
| 5848 | } |
| 5785 | 5849 | |
| 5786 | | const got_plt = switch (elf.ehdrField(.machine)) { |
| 5787 | | .SPARCV9 => false, |
| 5788 | | else => true, |
| 5789 | | }; |
| 5850 | fn prepareDynamic(elf: *Elf) Error!void { |
| 5851 | const comp = elf.base.comp; |
| 5852 | |
| 5853 | if (elf.shndx.dynamic == .UNDEF) return; |
| 5854 | |
| 5855 | // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries. |
| 5856 | const use_plt = !(comp.config.output_mode == .Exe and |
| 5857 | comp.config.link_mode == .static and |
| 5858 | comp.config.pie); |
| 5859 | |
| 5860 | const dynamic_len: u64 = elf.needed.count() + @intFromBool(elf.dynamic.soname != .empty) + |
| 5861 | @intFromBool(elf.dynamic.rpath != .empty) + |
| 5862 | @intFromBool(elf.dynamic.flags != 0) + @intFromBool(elf.dynamic.flags_1 != 0) + |
| 5863 | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + |
| 5864 | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + |
| 5865 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + |
| 5866 | @as(usize, @intFromBool(use_plt)) * 4 + |
| 5867 | @intFromBool(comp.config.output_mode == .Exe) + |
| 5868 | @intFromBool(elf.textrel_count > 0) + 8; |
| 5869 | |
| 5870 | const dynamic_size = dynamic_len * 2 * elf.targetPtrSize(); |
| 5871 | |
| 5872 | try elf.shndx.dynamic.get(elf).ni.resize(&elf.mf, comp.gpa, dynamic_size); |
| 5873 | switch (elf.shdrPtr(elf.shndx.dynamic)) { |
| 5874 | inline else => |shdr| elf.targetStore(&shdr.size, @intCast(dynamic_size)), |
| 5875 | } |
| 5876 | } |
| 5877 | |
| 5878 | fn flushDynamic(elf: *Elf) void { |
| 5879 | const comp = elf.base.comp; |
| 5880 | |
| 5881 | if (elf.shndx.dynamic == .UNDEF) return; |
| 5790 | 5882 | |
| 5791 | | if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) { |
| 5883 | switch (elf.identClass()) { |
| 5792 | 5884 | .NONE, _ => unreachable, |
| 5793 | | inline else => |ct_class| { |
| 5794 | | const ElfN = ct_class.ElfN(); |
| 5795 | | const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0; |
| 5796 | | const flags_1: ElfN.Addr = if (elf.options.z_now) std.elf.DF_1_NOW else 0; |
| 5797 | | const rpath: String(.dynstr) = rpath: { |
| 5798 | | var buf: std.ArrayList(u8) = .empty; |
| 5799 | | defer buf.deinit(gpa); |
| 5800 | | for (elf.options.rpath_list, 0..) |path, i| { |
| 5801 | | if (i > 0) try buf.append(gpa, ':'); |
| 5802 | | try buf.appendSlice(gpa, path); |
| 5803 | | } |
| 5804 | | break :rpath try elf.string(.dynstr, buf.items); |
| 5805 | | }; |
| 5885 | inline else => |class| { |
| 5886 | const ElfN = class.ElfN(); |
| 5887 | |
| 5806 | 5888 | // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries. |
| 5807 | 5889 | const use_plt = !(comp.config.output_mode == .Exe and |
| 5808 | 5890 | comp.config.link_mode == .static and |
| 5809 | 5891 | comp.config.pie); |
| 5810 | | const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: { |
| 5811 | | break :str try elf.string(.dynstr, soname_slice); |
| 5812 | | } else null; |
| 5813 | | const needed_len = elf.needed.count(); |
| 5814 | | const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) + |
| 5815 | | @intFromBool(rpath != .empty) + |
| 5816 | | @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) + |
| 5817 | | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + |
| 5818 | | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + |
| 5819 | | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + |
| 5820 | | @as(usize, @intFromBool(use_plt)) * 4 + |
| 5821 | | @intFromBool(comp.config.output_mode == .Exe) + 8; |
| 5822 | | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); |
| 5823 | | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; |
| 5824 | | try dynamic_ni.resize(&elf.mf, gpa, dynamic_size); |
| 5825 | | switch (elf.shdrPtr(elf.shndx.dynamic)) { |
| 5826 | | inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size), |
| 5892 | |
| 5893 | const dynamic_size = elf.targetLoad(&@field(elf.shdrPtr(elf.shndx.dynamic), @tagName(class)).size); |
| 5894 | const dynamic_slice = elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)]; |
| 5895 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(dynamic_slice)); |
| 5896 | |
| 5897 | var dynamic_index: usize = 0; |
| 5898 | |
| 5899 | for ( |
| 5900 | dynamic_entries[dynamic_index..][0..elf.needed.count()], |
| 5901 | elf.needed.keys(), |
| 5902 | ) |*dynamic_entry, needed| { |
| 5903 | dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) }; |
| 5827 | 5904 | } |
| 5905 | dynamic_index += elf.needed.count(); |
| 5828 | 5906 | |
| 5829 | | const dynamic_indices: struct { |
| 5830 | | init_array: ?usize, |
| 5831 | | fini_array: ?usize, |
| 5832 | | preinit_array: ?usize, |
| 5833 | | jmprel: ?usize, |
| 5834 | | pltgot: ?usize, |
| 5835 | | } = indices: { |
| 5836 | | const sec_dynamic = dynamic_ni.slice(&elf.mf); |
| 5837 | | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); |
| 5838 | | errdefer comptime unreachable; // don't invalidate `dynamic_entries` |
| 5839 | | var dynamic_index: usize = 0; |
| 5840 | | for ( |
| 5841 | | dynamic_entries[dynamic_index..][0..needed_len], |
| 5842 | | elf.needed.keys(), |
| 5843 | | ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) }; |
| 5844 | | dynamic_index += needed_len; |
| 5845 | | if (soname) |soname_dynstr| { |
| 5846 | | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(soname_dynstr) }; |
| 5847 | | dynamic_index += 1; |
| 5848 | | } |
| 5849 | | if (rpath != .empty) { |
| 5850 | | dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) }; |
| 5851 | | dynamic_index += 1; |
| 5852 | | } |
| 5853 | | if (flags != 0) { |
| 5854 | | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags }; |
| 5855 | | dynamic_index += 1; |
| 5856 | | } |
| 5857 | | if (flags_1 != 0) { |
| 5858 | | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 }; |
| 5859 | | dynamic_index += 1; |
| 5860 | | } |
| 5861 | | if (comp.config.output_mode == .Exe) { |
| 5862 | | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; |
| 5863 | | dynamic_index += 1; |
| 5864 | | } |
| 5865 | | const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: { |
| 5866 | | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5867 | | .{ std.elf.DT_INIT_ARRAY, 0 }, // reloc added below |
| 5868 | | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( |
| 5869 | | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, |
| 5870 | | ) }, |
| 5871 | | }; |
| 5872 | | defer dynamic_index += 2; |
| 5873 | | break :i dynamic_index; |
| 5874 | | } else null; |
| 5875 | | const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: { |
| 5876 | | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5877 | | .{ std.elf.DT_FINI_ARRAY, 0 }, // reloc added below |
| 5878 | | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( |
| 5879 | | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, |
| 5880 | | ) }, |
| 5881 | | }; |
| 5882 | | defer dynamic_index += 2; |
| 5883 | | break :i dynamic_index; |
| 5884 | | } else null; |
| 5885 | | const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: { |
| 5886 | | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5887 | | .{ std.elf.DT_PREINIT_ARRAY, 0 }, // reloc added below |
| 5888 | | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( |
| 5889 | | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, |
| 5890 | | ) }, |
| 5891 | | }; |
| 5892 | | defer dynamic_index += 2; |
| 5893 | | break :i dynamic_index; |
| 5894 | | } else null; |
| 5895 | | const jmprel_index: ?usize, const pltgot_index: ?usize = if (use_plt) i: { |
| 5896 | | dynamic_entries[dynamic_index..][0..4].* = .{ |
| 5897 | | .{ std.elf.DT_JMPREL, 0 }, // reloc added below |
| 5898 | | .{ std.elf.DT_PLTGOT, 0 }, // reloc added below |
| 5899 | | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( |
| 5900 | | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, |
| 5901 | | ) }, |
| 5902 | | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, |
| 5903 | | }; |
| 5904 | | defer dynamic_index += 4; |
| 5905 | | break :i .{ dynamic_index, dynamic_index + 1 }; |
| 5906 | | } else .{ null, null }; |
| 5907 | | dynamic_entries[dynamic_index..][0..8].* = .{ |
| 5908 | | .{ std.elf.DT_RELA, 0 }, // reloc added below |
| 5909 | | .{ std.elf.DT_RELASZ, elf.targetLoad( |
| 5910 | | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, |
| 5911 | | ) }, |
| 5912 | | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| 5913 | | .{ std.elf.DT_SYMTAB, 0 }, // reloc added below |
| 5914 | | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, |
| 5915 | | .{ std.elf.DT_STRTAB, 0 }, // reloc added below |
| 5916 | | .{ std.elf.DT_STRSZ, elf.targetLoad( |
| 5917 | | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, |
| 5918 | | ) }, |
| 5919 | | .{ std.elf.DT_NULL, 0 }, |
| 5907 | if (elf.dynamic.soname != .empty) { |
| 5908 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(elf.dynamic.soname) }; |
| 5909 | dynamic_index += 1; |
| 5910 | } |
| 5911 | if (elf.dynamic.rpath != .empty) { |
| 5912 | dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(elf.dynamic.rpath) }; |
| 5913 | dynamic_index += 1; |
| 5914 | } |
| 5915 | if (elf.dynamic.flags != 0) { |
| 5916 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, elf.dynamic.flags }; |
| 5917 | dynamic_index += 1; |
| 5918 | } |
| 5919 | if (elf.dynamic.flags_1 != 0) { |
| 5920 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, elf.dynamic.flags_1 }; |
| 5921 | dynamic_index += 1; |
| 5922 | } |
| 5923 | if (comp.config.output_mode == .Exe) { |
| 5924 | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; |
| 5925 | dynamic_index += 1; |
| 5926 | } |
| 5927 | if (elf.textrel_count > 0) { |
| 5928 | dynamic_entries[dynamic_index] = .{ std.elf.DT_TEXTREL, 0 }; |
| 5929 | dynamic_index += 1; |
| 5930 | } |
| 5931 | if (elf.shndx.init_array != .UNDEF) { |
| 5932 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5933 | .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) }, |
| 5934 | .{ std.elf.DT_INIT_ARRAYSZ, @intCast(elf.shndx.init_array.size(elf)) }, |
| 5920 | 5935 | }; |
| 5921 | | dynamic_index += 8; |
| 5922 | | assert(dynamic_index == dynamic_len); |
| 5923 | | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 5924 | | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| 5925 | | |
| 5926 | | break :indices .{ |
| 5927 | | .init_array = init_array_index, |
| 5928 | | .fini_array = fini_array_index, |
| 5929 | | .preinit_array = preinit_array_index, |
| 5930 | | .jmprel = jmprel_index, |
| 5931 | | .pltgot = pltgot_index, |
| 5936 | dynamic_index += 2; |
| 5937 | } |
| 5938 | if (elf.shndx.fini_array != .UNDEF) { |
| 5939 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5940 | .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) }, |
| 5941 | .{ std.elf.DT_FINI_ARRAYSZ, @intCast(elf.shndx.fini_array.size(elf)) }, |
| 5932 | 5942 | }; |
| 5933 | | }; |
| 5943 | dynamic_index += 2; |
| 5944 | } |
| 5945 | if (elf.shndx.preinit_array != .UNDEF) { |
| 5946 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5947 | .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) }, |
| 5948 | .{ std.elf.DT_PREINIT_ARRAYSZ, @intCast(elf.shndx.preinit_array.size(elf)) }, |
| 5949 | }; |
| 5950 | dynamic_index += 2; |
| 5951 | } |
| 5952 | if (use_plt) { |
| 5953 | // The `DT_PLTGOT` entry usually points to `.got.plt`, but on targets where that |
| 5954 | // section does not exist it instead points to `.plt`. |
| 5955 | const pltgot_shndx: Section.Index = switch (elf.targetPltInfo().got_plt != null) { |
| 5956 | true => elf.shndx.got_plt, |
| 5957 | false => elf.shndx.plt, |
| 5958 | }; |
| 5959 | dynamic_entries[dynamic_index..][0..4].* = .{ |
| 5960 | .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) }, |
| 5961 | .{ std.elf.DT_PLTGOT, @intCast(pltgot_shndx.vaddr(elf)) }, |
| 5962 | .{ std.elf.DT_PLTRELSZ, @intCast(elf.shndx.rela_plt.size(elf)) }, |
| 5963 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, |
| 5964 | }; |
| 5965 | dynamic_index += 4; |
| 5966 | } |
| 5934 | 5967 | |
| 5935 | | const dsorel: SymbolReloc.Type = switch (ct_class) { |
| 5936 | | .NONE, _ => comptime unreachable, |
| 5937 | | .@"32" => .dsorel32, |
| 5938 | | .@"64" => .dsorel64, |
| 5968 | dynamic_entries[dynamic_index..][0..8].* = .{ |
| 5969 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, |
| 5970 | .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) }, |
| 5971 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| 5972 | .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) }, |
| 5973 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, |
| 5974 | .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) }, |
| 5975 | .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) }, |
| 5976 | .{ std.elf.DT_NULL, 0 }, |
| 5939 | 5977 | }; |
| 5978 | dynamic_index += 8; |
| 5940 | 5979 | |
| 5941 | | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 5942 | | try elf.ensureUnusedRelocCapacity(dynamic_ni, 8); |
| 5943 | | if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5944 | | dynamic_ni, |
| 5945 | | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5946 | | .local(elf.shndx.init_array.get(elf).lsi), |
| 5947 | | 0, |
| 5948 | | dsorel, |
| 5949 | | ); |
| 5950 | | if (dynamic_indices.fini_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5951 | | dynamic_ni, |
| 5952 | | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5953 | | .local(elf.shndx.fini_array.get(elf).lsi), |
| 5954 | | 0, |
| 5955 | | dsorel, |
| 5956 | | ); |
| 5957 | | if (dynamic_indices.preinit_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5958 | | dynamic_ni, |
| 5959 | | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5960 | | .local(elf.shndx.preinit_array.get(elf).lsi), |
| 5961 | | 0, |
| 5962 | | dsorel, |
| 5963 | | ); |
| 5964 | | if (dynamic_indices.jmprel) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5965 | | dynamic_ni, |
| 5966 | | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5967 | | .local(elf.shndx.rela_plt.get(elf).lsi), |
| 5968 | | 0, |
| 5969 | | dsorel, |
| 5970 | | ); |
| 5971 | | if (dynamic_indices.pltgot) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5972 | | dynamic_ni, |
| 5973 | | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5974 | | .local((if (got_plt) elf.shndx.got_plt else elf.shndx.plt).get(elf).lsi), |
| 5975 | | 0, |
| 5976 | | dsorel, |
| 5977 | | ); |
| 5978 | | try elf.addSymbolRelocAssumeCapacity( |
| 5979 | | dynamic_ni, |
| 5980 | | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 8) + 1), |
| 5981 | | .local(elf.shndx.rela_dyn.get(elf).lsi), |
| 5982 | | 0, |
| 5983 | | dsorel, |
| 5984 | | ); |
| 5985 | | try elf.addSymbolRelocAssumeCapacity( |
| 5986 | | dynamic_ni, |
| 5987 | | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1), |
| 5988 | | .local(elf.shndx.dynsym.get(elf).lsi), |
| 5989 | | 0, |
| 5990 | | dsorel, |
| 5991 | | ); |
| 5992 | | try elf.addSymbolRelocAssumeCapacity( |
| 5993 | | dynamic_ni, |
| 5994 | | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1), |
| 5995 | | .local(elf.shndx.dynstr.get(elf).lsi), |
| 5996 | | 0, |
| 5997 | | dsorel, |
| 5998 | | ); |
| 5980 | assert(dynamic_index == dynamic_entries.len); |
| 5981 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 5982 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| 5999 | 5983 | }, |
| 6000 | | }; |
| 5984 | } |
| 6001 | 5985 | } |
| 6002 | 5986 | |
| 6003 | 5987 | fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| ... | ... | @@ -6017,7 +6001,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 6017 | 6001 | .PROGBITS => assert(opts.size > 0), |
| 6018 | 6002 | else => {}, |
| 6019 | 6003 | } |
| 6020 | | if (opts.flags.ALLOC and elf.ehdrField(.type) != .REL) { |
| 6004 | if (opts.flags.ALLOC and elf.ehdrType() != .REL) { |
| 6021 | 6005 | assert(elf.getNode(segment_ni) == .segment); |
| 6022 | 6006 | } |
| 6023 | 6007 | const gpa = elf.base.comp.gpa; |
| ... | ... | @@ -6054,8 +6038,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 6054 | 6038 | }, |
| 6055 | 6039 | }; |
| 6056 | 6040 | try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size); |
| 6057 | | const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrField(.type)) { |
| 6058 | | .NONE, .CORE, _ => unreachable, |
| 6041 | const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) { |
| 6059 | 6042 | .REL => elf.ni.file, |
| 6060 | 6043 | .EXEC, .DYN => segment_ni, |
| 6061 | 6044 | }, .{ |
| ... | ... | @@ -6106,8 +6089,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 6106 | 6089 | try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 6107 | 6090 | try elf.got_relocs.ensureUnusedCapacity(gpa, len); |
| 6108 | 6091 | const class = elf.identClass(); |
| 6109 | | switch (elf.ehdrField(.type)) { |
| 6110 | | .NONE, .CORE, _ => unreachable, |
| 6092 | switch (elf.ehdrType()) { |
| 6111 | 6093 | .REL => { |
| 6112 | 6094 | const shndx = elf.getNodeShndx(node); |
| 6113 | 6095 | if (shndx.get(elf).rela.shndx == .UNDEF) { |
| ... | ... | @@ -6166,10 +6148,9 @@ fn addRelocAssumeCapacity( |
| 6166 | 6148 | target: Symbol.Id, |
| 6167 | 6149 | addend: i64, |
| 6168 | 6150 | @"type": MachineRelocType, |
| 6169 | | ) Error!void { |
| 6151 | ) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void { |
| 6170 | 6152 | assert(node != .none); |
| 6171 | | switch (elf.ehdrField(.type)) { |
| 6172 | | .NONE, .CORE, _ => unreachable, |
| 6153 | switch (elf.ehdrType()) { |
| 6173 | 6154 | .REL => { |
| 6174 | 6155 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| 6175 | 6156 | const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ |
| ... | ... | @@ -6187,161 +6168,113 @@ fn addRelocAssumeCapacity( |
| 6187 | 6168 | const target_ptr = target.index(elf).ptr(elf); |
| 6188 | 6169 | const next = target_ptr.first_target_reloc; |
| 6189 | 6170 | target_ptr.first_target_reloc = ri; |
| 6190 | | break :next next; |
| 6191 | | }; |
| 6192 | | if (next != .none) { |
| 6193 | | next.get(elf).prev = ri; |
| 6194 | | } |
| 6195 | | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 6196 | | .node = node, |
| 6197 | | .offset = offset, |
| 6198 | | .type = .write_rela, |
| 6199 | | .target = target, |
| 6200 | | .addend = addend, |
| 6201 | | .next = next, |
| 6202 | | .prev = .none, |
| 6203 | | .rela_index = rela_index.toOptional(), |
| 6204 | | }); |
| 6205 | | }, |
| 6206 | | |
| 6207 | | .DYN, .EXEC => switch (elf.ehdrField(.machine)) { |
| 6208 | | else => |machine| @panic(@tagName(machine)), |
| 6209 | | .X86_64 => switch (@"type".X86_64) { |
| 6210 | | _, |
| 6211 | | .NONE, |
| 6212 | | .COPY, |
| 6213 | | .GLOB_DAT, |
| 6214 | | .JUMP_SLOT, |
| 6215 | | .RELATIVE64, |
| 6216 | | .RELATIVE, |
| 6217 | | .IRELATIVE, |
| 6218 | | .@"16", |
| 6219 | | .PC16, |
| 6220 | | .@"8", |
| 6221 | | .PC8, |
| 6222 | | .DTPMOD64, |
| 6223 | | .GOTPLT64, |
| 6224 | | => @panic("TODO: error for illegal or unsupported input relocation"), |
| 6225 | | |
| 6226 | | // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt |
| 6227 | | .GOTPC32_TLSDESC => @panic("TODO: R_X86_64_GOTPC32_TLSDESC"), |
| 6228 | | .TLSDESC_CALL => @panic("TODO: R_X86_64_TLSDESC_CALL"), |
| 6229 | | .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"), |
| 6230 | | |
| 6231 | | // Relocations targeting a symbol |
| 6232 | | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), |
| 6233 | | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), |
| 6234 | | .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s), |
| 6235 | | .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), |
| 6236 | | .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), |
| 6237 | | .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32), |
| 6238 | | .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64), |
| 6239 | | .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32), |
| 6240 | | .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64), |
| 6241 | | .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32), |
| 6242 | | .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64), |
| 6243 | | .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32), |
| 6244 | | .GOTPC64 => { |
| 6245 | | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 6246 | | try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64); |
| 6247 | | }, |
| 6248 | | .GOTPC32 => { |
| 6249 | | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 6250 | | try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32); |
| 6251 | | }, |
| 6171 | break :next next; |
| 6172 | }; |
| 6173 | if (next != .none) { |
| 6174 | next.get(elf).prev = ri; |
| 6175 | } |
| 6176 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 6177 | .node = node, |
| 6178 | .offset = offset, |
| 6179 | .type = undefined, |
| 6180 | .target = target, |
| 6181 | .addend = addend, |
| 6182 | .next = next, |
| 6183 | .prev = .none, |
| 6184 | .rela_index = rela_index.toOptional(), |
| 6185 | .result = .ok, |
| 6186 | }); |
| 6187 | }, |
| 6252 | 6188 | |
| 6253 | | // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the |
| 6254 | | // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm |
| 6255 | | // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which |
| 6256 | | // need to be re-applied whenever the GOT moves. |
| 6257 | | .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base |
| 6258 | | .PLTOFF64 => @panic("TODO: R_X86_64_PLTOFF64"), // offset of PLT entry from GOT base (yes, I know, the name is stupid) |
| 6259 | | |
| 6260 | | // Relocations targeting a GOT entry |
| 6261 | | .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset64), |
| 6262 | | .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset32), |
| 6263 | | .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64), |
| 6264 | | .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 6265 | | // TODO: the next two are relaxable to non-GOT relocations, but I haven't figured |
| 6266 | | // out how to represent relaxations yet. If we want to remove a `GotReloc` and add a |
| 6267 | | // `SymbolReloc` at some point, we can't do that in `GotReloc.apply`, because that |
| 6268 | | // function must be idempotent to ensure reproducible binaries. I think we would |
| 6269 | | // need to do that as soon as the operation is known to be relaxable (e.g. because |
| 6270 | | // we found a defininition for a non-preemptible symbol). |
| 6271 | | .GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 6272 | | .REX_GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32), |
| 6273 | | |
| 6274 | | .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .rel32), |
| 6275 | | .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32), |
| 6276 | | .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32), |
| 6189 | .DYN, .EXEC => switch (elf.ehdrMachine()) { |
| 6190 | .AARCH64 => switch (@"type".AARCH64) { |
| 6191 | .NONE => {}, |
| 6192 | _ => return error.UnknownRelocation, |
| 6193 | else => return error.UnimplementedRelocation, |
| 6277 | 6194 | }, |
| 6278 | | .LOONGARCH => switch (@"type".LOONGARCH) { |
| 6279 | | else => std.debug.panic("TODO: unsupported input relocation, {t}", .{@"type".LOONGARCH}), |
| 6280 | | _, |
| 6281 | | .NONE, |
| 6195 | .LOONGARCH => rel_type: switch (@"type".LARCH) { |
| 6196 | .NONE => {}, |
| 6197 | _ => return error.UnknownRelocation, |
| 6198 | |
| 6282 | 6199 | .COPY, |
| 6283 | 6200 | .JUMP_SLOT, |
| 6284 | 6201 | .RELATIVE, |
| 6285 | 6202 | .IRELATIVE, |
| 6286 | | => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".LOONGARCH}), |
| 6287 | | |
| 6288 | | .RELAX => {}, // TODO: relaxation is not yet implemented |
| 6289 | | |
| 6290 | | // Relocations targeting a symbol |
| 6291 | | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), |
| 6292 | | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), |
| 6293 | | .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), |
| 6294 | | .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), |
| 6295 | | |
| 6296 | | .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_abs32_lo12), |
| 6297 | | .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel32_hi20), |
| 6298 | | .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_hi12), |
| 6299 | | .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_lo20), |
| 6300 | | |
| 6301 | | .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel18), |
| 6302 | | .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel23), |
| 6303 | | .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel28), |
| 6304 | | .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_call_rel38), |
| 6305 | | |
| 6306 | | // Relocations targeting a TLS symbol |
| 6307 | | .TLS_LE_LO12, .TLS_LE_LO12_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_lo12), |
| 6308 | | .TLS_LE_HI20, .TLS_LE_HI20_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_hi20), |
| 6309 | | .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_lo20), |
| 6310 | | .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_hi12), |
| 6311 | | .TLS_LE_ADD_R => {}, // TODO: relaxation is not yet implemented |
| 6312 | | |
| 6313 | | // Relocations targeting a GOT entry |
| 6314 | | .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12), |
| 6315 | | .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel32_hi20), |
| 6316 | | .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_lo20), |
| 6317 | | .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_hi12), |
| 6318 | | |
| 6319 | | .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12), |
| 6320 | | .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_hi20), |
| 6321 | | .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_lo20), |
| 6322 | | .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_hi12), |
| 6203 | => return error.NonStaticRelocation, |
| 6204 | |
| 6205 | else => return error.UnimplementedRelocation, |
| 6206 | |
| 6207 | // These relocations signal that certain relaxations are legal, but this linker does |
| 6208 | // not yet implement relaxation, so these are ignored. |
| 6209 | .RELAX, .TLS_LE_ADD_R => {}, |
| 6210 | |
| 6211 | // Relaxable versions of other relocations. Since we don't yet implement relaxation, |
| 6212 | // just use the handling for the non-relaxable versions. |
| 6213 | .TLS_LE_LO12_R => continue :rel_type .TLS_LE_LO12, |
| 6214 | .TLS_LE_HI20_R => continue :rel_type .TLS_LE_HI20, |
| 6215 | |
| 6216 | // zig fmt: off |
| 6217 | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6218 | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6219 | .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6220 | .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 6221 | .ABS_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6222 | .ABS_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })), |
| 6223 | .ABS64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 6224 | .ABS64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 6225 | .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6226 | .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala_hi20)), |
| 6227 | .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_lo20)), |
| 6228 | .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_hi12)), |
| 6229 | |
| 6230 | .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[25:10]", .cast = .signed, .shift = .@"2_exact" })), |
| 6231 | .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b21)), |
| 6232 | .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b26)), |
| 6233 | .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_call36)), |
| 6234 | |
| 6235 | .TLS_LE_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6236 | .TLS_LE_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })), |
| 6237 | .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 6238 | .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 6239 | |
| 6240 | .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6241 | .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala_hi20)), |
| 6242 | .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_lo20)), |
| 6243 | .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_hi12)), |
| 6244 | .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6245 | .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })), |
| 6246 | .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 6247 | .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 6248 | // zig fmt: on |
| 6249 | }, |
| 6250 | .PPC64 => switch (@"type".PPC64) { |
| 6251 | .NONE => {}, |
| 6252 | _ => return error.UnknownRelocation, |
| 6253 | else => return error.UnimplementedRelocation, |
| 6254 | }, |
| 6255 | .RISCV => switch (@"type".RISCV) { |
| 6256 | .NONE => {}, |
| 6257 | _ => return error.UnknownRelocation, |
| 6258 | else => return error.UnimplementedRelocation, |
| 6323 | 6259 | }, |
| 6324 | 6260 | .SPARCV9 => switch (@"type".SPARC) { |
| 6325 | | _, |
| 6326 | | .NONE, |
| 6261 | .NONE => {}, |
| 6262 | _ => return error.UnknownRelocation, |
| 6263 | |
| 6327 | 6264 | .COPY, |
| 6328 | 6265 | .GLOB_DAT, |
| 6329 | 6266 | .JMP_SLOT, |
| 6330 | 6267 | .RELATIVE, |
| 6331 | 6268 | .IRELATIVE, |
| 6332 | | => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".SPARC}), |
| 6269 | => return error.NonStaticRelocation, |
| 6333 | 6270 | |
| 6334 | | inline .WDISP22, |
| 6271 | .WDISP22, |
| 6335 | 6272 | .HI22, |
| 6336 | | .@"22", |
| 6337 | | .@"13", |
| 6338 | 6273 | .LO10, |
| 6339 | 6274 | .HIPLT22, |
| 6340 | 6275 | .LOPLT10, |
| 6341 | 6276 | .PCPLT22, |
| 6342 | 6277 | .PCPLT10, |
| 6343 | | .@"10", |
| 6344 | | .@"11", |
| 6345 | 6278 | .OLO10, |
| 6346 | 6279 | .HH22, |
| 6347 | 6280 | .HM10, |
| ... | ... | @@ -6351,62 +6284,24 @@ fn addRelocAssumeCapacity( |
| 6351 | 6284 | .PC_LM22, |
| 6352 | 6285 | .WDISP16, |
| 6353 | 6286 | .WDISP19, |
| 6354 | | .@"7", |
| 6355 | | .@"5", |
| 6356 | | .@"6", |
| 6357 | 6287 | .HIX22, |
| 6358 | 6288 | .LOX10, |
| 6359 | 6289 | .REGISTER, |
| 6360 | | .TLS_GD_HI22, |
| 6361 | | .TLS_GD_LO10, |
| 6362 | 6290 | .TLS_IE_HI22, |
| 6363 | 6291 | .TLS_IE_LO10, |
| 6364 | 6292 | .TLS_DTPMOD32, |
| 6365 | 6293 | .TLS_DTPMOD64, |
| 6366 | 6294 | .H34, |
| 6367 | 6295 | .WDISP10, |
| 6368 | | => |t| @panic("TODO: " ++ @tagName(t)), |
| 6369 | | |
| 6370 | | // Relocations targeting a symbol |
| 6371 | | .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs8), |
| 6372 | | .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs16), |
| 6373 | | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), |
| 6374 | | .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel8), |
| 6375 | | .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel16), |
| 6376 | | .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), |
| 6377 | | .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_wdisp30), |
| 6378 | | .PC10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_pc10), |
| 6379 | | .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_pc22), |
| 6380 | | .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_wplt30), |
| 6381 | | .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), |
| 6382 | | .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltabs32), |
| 6383 | | .PCPLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32), |
| 6384 | | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), |
| 6385 | | .DISP64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), |
| 6386 | | .PLT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltabs64), |
| 6387 | | .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_h44), |
| 6388 | | .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_m44), |
| 6389 | | .L44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_l44), |
| 6390 | | .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), |
| 6391 | | .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs16), |
| 6392 | | .TLS_GD_CALL, .TLS_LDM_CALL => try elf.addSymbolRelocAssumeCapacity(node, offset, try elf.externSymbolInner(.{ |
| 6393 | | .lib_name = null, |
| 6394 | | .name = "__tls_get_addr", |
| 6395 | | .type = .FUNC, |
| 6396 | | }), addend, .sparc_wplt30), |
| 6397 | | .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32), |
| 6398 | | .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64), |
| 6399 | | |
| 6400 | | // Relocations targeting a TLS symbol |
| 6401 | | .TLS_LDO_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_ldo_hix22), |
| 6402 | | .TLS_LDO_LOX10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_ldo_lox10), |
| 6403 | | .TLS_LE_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_le_hix22), |
| 6404 | | .TLS_LE_LOX10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_le_lox10), |
| 6405 | | .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32), |
| 6406 | | .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64), |
| 6407 | | .TLS_TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32), |
| 6408 | | .TLS_TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64), |
| 6409 | | // We currently do no relaxation, so nothing to do for these. |
| 6296 | => return error.UnimplementedRelocation, |
| 6297 | |
| 6298 | // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though. |
| 6299 | .GOTDATA_HIX22 => return error.UnimplementedRelocation, |
| 6300 | .GOTDATA_LOX10 => return error.UnimplementedRelocation, |
| 6301 | |
| 6302 | // These relocations signal that certain relaxations are legal, but this linker does |
| 6303 | // not yet implement relaxation, so these are ignored. |
| 6304 | .GOTDATA_OP, |
| 6410 | 6305 | .TLS_GD_ADD, |
| 6411 | 6306 | .TLS_LDM_ADD, |
| 6412 | 6307 | .TLS_LDO_ADD, |
| ... | ... | @@ -6415,19 +6310,180 @@ fn addRelocAssumeCapacity( |
| 6415 | 6310 | .TLS_IE_ADD, |
| 6416 | 6311 | => {}, |
| 6417 | 6312 | |
| 6418 | | // Relocations targeting a GOT entry |
| 6419 | | .GOT10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_10), |
| 6420 | | .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_13), |
| 6421 | | .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_22), |
| 6422 | | .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_hi22), |
| 6423 | | .TLS_LDM_LO10 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_lo10), |
| 6424 | | // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though. |
| 6425 | | .GOTDATA_HIX22 => @panic("TODO: R_SPARC_GOTDATA_HIX22"), |
| 6426 | | .GOTDATA_LOX10 => @panic("TODO: R_SPARC_GOTDATA_LOX10"), |
| 6427 | | .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_hix22), |
| 6428 | | .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_lox10), |
| 6429 | | // We currently do no relaxation, so nothing to do for this one. |
| 6430 | | .GOTDATA_OP => {}, |
| 6313 | // zig fmt: off |
| 6314 | .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })), |
| 6315 | .@"16", .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })), |
| 6316 | .@"32", .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6317 | .@"64", .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6318 | |
| 6319 | .@"5" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[4:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6320 | .@"6" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[5:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6321 | .@"7" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[6:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6322 | .@"10" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6323 | .@"11" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[10:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6324 | .@"13" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6325 | .@"22" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6326 | |
| 6327 | .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })), |
| 6328 | .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })), |
| 6329 | .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6330 | .DISP64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 6331 | |
| 6332 | .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6333 | .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6334 | |
| 6335 | .PCPLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6336 | .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6337 | .PLT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6338 | |
| 6339 | .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })), |
| 6340 | .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })), |
| 6341 | .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[21:0]", .cast = .signed, .shift = .@"10" })), |
| 6342 | .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })), |
| 6343 | .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"12" })), |
| 6344 | |
| 6345 | .TLS_LDO_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 6346 | .TLS_LE_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_le_hix22)), |
| 6347 | .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6348 | .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6349 | .TLS_TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6350 | .TLS_TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 6351 | |
| 6352 | .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6353 | .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 6354 | .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_lox10)), |
| 6355 | .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_hix22)), |
| 6356 | .TLS_GD_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 6357 | .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 6358 | // zig fmt: on |
| 6359 | |
| 6360 | .TLS_GD_CALL, .TLS_LDM_CALL => { |
| 6361 | const callee_sym = try elf.externSymbolInner(.{ |
| 6362 | .lib_name = null, |
| 6363 | .name = "__tls_get_addr", |
| 6364 | .type = .FUNC, |
| 6365 | }); |
| 6366 | try elf.addSymbolRelocAssumeCapacity(node, offset, callee_sym, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })); |
| 6367 | }, |
| 6368 | |
| 6369 | // The following relocations are all represented by the ABI as writing to a 13 bit |
| 6370 | // field (32[12:0]), but masking out some bits of the value. To simplify our logic |
| 6371 | // for applying relocations, we instead [un]set any fixed bits right now, then model |
| 6372 | // the relocation as only writing to a smaller 10--12 bit field. |
| 6373 | // TODO: because we flush input sections lazily, we can't actually write these bits |
| 6374 | // immediately---we'll instead have to queue the writes somehow. |
| 6375 | .PC10 => { |
| 6376 | // TODO: 32[12:10] = 0b000 |
| 6377 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6378 | }, |
| 6379 | .L44 => { |
| 6380 | // TODO: 32[12:12] = 0b0 |
| 6381 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" })); |
| 6382 | }, |
| 6383 | .TLS_LDO_LOX10 => { |
| 6384 | // TODO: 32[12:10] = 0b000 |
| 6385 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6386 | }, |
| 6387 | .TLS_LE_LOX10 => { |
| 6388 | // TODO: 32[12:10] = 0b111 |
| 6389 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6390 | }, |
| 6391 | .GOT10 => { |
| 6392 | // TODO: 32[12:10] = 0b000 |
| 6393 | elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6394 | }, |
| 6395 | .TLS_GD_LO10 => { |
| 6396 | // TODO: 32[12:10] = 0b000 |
| 6397 | elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6398 | }, |
| 6399 | .TLS_LDM_LO10 => { |
| 6400 | // TODO: 32[12:10] = 0b000 |
| 6401 | elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6402 | }, |
| 6403 | }, |
| 6404 | .X86_64 => rel_type: switch (@"type".X86_64) { |
| 6405 | .NONE => {}, |
| 6406 | _ => return error.UnknownRelocation, |
| 6407 | |
| 6408 | .COPY, |
| 6409 | .GLOB_DAT, |
| 6410 | .JUMP_SLOT, |
| 6411 | .RELATIVE64, |
| 6412 | .RELATIVE, |
| 6413 | .IRELATIVE, |
| 6414 | .DTPMOD64, |
| 6415 | => return error.NonStaticRelocation, |
| 6416 | |
| 6417 | // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt |
| 6418 | .GOTPC32_TLSDESC => return error.UnimplementedRelocation, |
| 6419 | .TLSDESC_CALL => return error.UnimplementedRelocation, |
| 6420 | .TLSDESC => return error.UnimplementedRelocation, |
| 6421 | |
| 6422 | // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the |
| 6423 | // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm |
| 6424 | // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which |
| 6425 | // need to be re-applied whenever the GOT moves. |
| 6426 | .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base |
| 6427 | .PLTOFF64 => return error.UnimplementedRelocation, // offset of PLT entry from GOT base (yes, I know, the name is stupid) |
| 6428 | |
| 6429 | // TODO: figure out how to do relaxations. Perhaps we want to remove a `GotReloc` |
| 6430 | // and replace it with a `SymbolReloc` when a relaxation becomes possible, but we'd |
| 6431 | // need to bear in mind whether incremental updates might make a relaxation |
| 6432 | // impossible again or something like that. Relaxations seem kind of hostile to |
| 6433 | // incremental compilation, so perhaps we just only support them in non-incremental |
| 6434 | // compilations and just apply them in flush or something. |
| 6435 | |
| 6436 | // Relaxable versions of other relocations. Since we don't yet implement relaxation, |
| 6437 | // just use the handling for the non-relaxable versions. |
| 6438 | .GOTPCRELX, .REX_GOTPCRELX => continue :rel_type .GOTPCREL, |
| 6439 | |
| 6440 | // This relocation was a historical attempt to help linkers optimize uses of symbols |
| 6441 | // which have both GOT entries and PLT entries, by encouraging the linker to create |
| 6442 | // a `.got.plt` entry instead of a `.got` entry. This makes no sense, because the |
| 6443 | // linker already has sufficient knowledge to do that optimization, while compilers |
| 6444 | // actually do *not* have sufficient knowledge (since the PLT and GOT relocations |
| 6445 | // may not be in the same compilation unit). This relocation has since been removed |
| 6446 | // from the psABI, but just in case it appears, we can easily support it by just |
| 6447 | // disregarding the PLT stuff and lowering to a normal GOT entry. |
| 6448 | // |
| 6449 | // More details: https://sourceware.org/pipermail/binutils/2014-November/086548.html |
| 6450 | .GOTPLT64 => continue :rel_type .GOT64, |
| 6451 | |
| 6452 | // zig fmt: off |
| 6453 | .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })), |
| 6454 | .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })), |
| 6455 | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6456 | .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6457 | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6458 | .PC8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })), |
| 6459 | .PC16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })), |
| 6460 | .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6461 | .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 6462 | .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6463 | .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6464 | .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6465 | .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6466 | .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6467 | .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6468 | .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 6469 | |
| 6470 | .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6471 | .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6472 | .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6473 | .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 6474 | .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6475 | .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6476 | .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6477 | // zig fmt: on |
| 6478 | |
| 6479 | .GOTPC64 => { |
| 6480 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 6481 | try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })); |
| 6482 | }, |
| 6483 | .GOTPC32 => { |
| 6484 | const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi); |
| 6485 | try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })); |
| 6486 | }, |
| 6431 | 6487 | }, |
| 6432 | 6488 | }, |
| 6433 | 6489 | } |
| ... | ... | @@ -6440,10 +6496,12 @@ fn addSymbolRelocAssumeCapacity( |
| 6440 | 6496 | addend: i64, |
| 6441 | 6497 | @"type": SymbolReloc.Type, |
| 6442 | 6498 | ) Error!void { |
| 6443 | | assert(elf.ehdrField(.type) != .REL); |
| 6499 | assert(elf.ehdrType() != .REL); |
| 6444 | 6500 | assert(node != .none); |
| 6445 | 6501 | |
| 6446 | 6502 | const rela_index: Section.RelaIndex.Optional = r: { |
| 6503 | if (elf.shndx.dynamic == .UNDEF) break :r .none; |
| 6504 | |
| 6447 | 6505 | // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to |
| 6448 | 6506 | // determine the vaddr of `node`. |
| 6449 | 6507 | const node_vaddr: u64 = switch (elf.getNode(node)) { |
| ... | ... | @@ -6461,162 +6519,51 @@ fn addSymbolRelocAssumeCapacity( |
| 6461 | 6519 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 6462 | 6520 | }; |
| 6463 | 6521 | |
| 6464 | | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 6465 | | else => |machine| @panic(@tagName(machine)), |
| 6466 | | .X86_64 => .{ .X86_64 = switch (@"type") { |
| 6467 | | .write_rela => unreachable, |
| 6468 | | .dsorel64, .dsorel32 => { |
| 6469 | | assert(target.unwrap() == .local); |
| 6470 | | break :r .none; |
| 6471 | | }, |
| 6472 | | .abs64 => .@"64", |
| 6473 | | .abs32 => .@"32", |
| 6474 | | .abs16 => unreachable, |
| 6475 | | .abs8 => unreachable, |
| 6476 | | .abs32s => .@"32S", |
| 6477 | | .rel64 => .PC64, |
| 6478 | | .rel32 => .PC32, |
| 6479 | | .rel16 => unreachable, |
| 6480 | | .rel8 => unreachable, |
| 6481 | | .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none, |
| 6482 | | .dtpoff64 => .DTPOFF64, |
| 6483 | | .dtpoff32 => .DTPOFF32, |
| 6484 | | .tpoff64 => .TPOFF64, |
| 6485 | | .tpoff32 => .TPOFF32, |
| 6486 | | .size64 => .SIZE64, |
| 6487 | | .size32 => .SIZE32, |
| 6488 | | |
| 6489 | | .larch_abs32_lo12, |
| 6490 | | .larch_rel32_hi20, |
| 6491 | | .larch_rel64_lo20, |
| 6492 | | .larch_rel64_hi12, |
| 6493 | | .larch_branch_rel18, |
| 6494 | | .larch_branch_rel23, |
| 6495 | | .larch_branch_rel28, |
| 6496 | | .larch_call_rel38, |
| 6497 | | .larch_tpoff32_lo12, |
| 6498 | | .larch_tpoff32_hi20, |
| 6499 | | .larch_tpoff64_lo20, |
| 6500 | | .larch_tpoff64_hi12, |
| 6501 | | => unreachable, |
| 6502 | | |
| 6503 | | .sparc_wdisp30, |
| 6504 | | .sparc_pc10, |
| 6505 | | .sparc_pc22, |
| 6506 | | .sparc_wplt30, |
| 6507 | | .sparc_h44, |
| 6508 | | .sparc_m44, |
| 6509 | | .sparc_l44, |
| 6510 | | .sparc_ldo_hix22, |
| 6511 | | .sparc_ldo_lox10, |
| 6512 | | .sparc_le_hix22, |
| 6513 | | .sparc_le_lox10, |
| 6514 | | => unreachable, |
| 6515 | | } }, |
| 6516 | | .LOONGARCH => .{ .LOONGARCH = switch (@"type") { |
| 6517 | | .write_rela => unreachable, |
| 6518 | | .dsorel64, .dsorel32 => { |
| 6519 | | assert(target.unwrap() == .local); |
| 6520 | | break :r .none; |
| 6521 | | }, |
| 6522 | | .abs64 => .@"64", |
| 6523 | | .abs32 => .@"32", |
| 6524 | | .abs32s => unreachable, |
| 6525 | | .abs16 => unreachable, |
| 6526 | | .abs8 => unreachable, |
| 6527 | | .rel64 => .@"64_PCREL", |
| 6528 | | .rel32 => .@"32_PCREL", |
| 6529 | | .rel16 => unreachable, |
| 6530 | | .rel8 => unreachable, |
| 6531 | | .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none, |
| 6532 | | .dtpoff64 => .TLS_DTPREL64, |
| 6533 | | .dtpoff32 => .TLS_DTPREL32, |
| 6534 | | .tpoff64 => .TLS_TPREL64, |
| 6535 | | .tpoff32 => .TLS_TPREL32, |
| 6536 | | .size64 => unreachable, |
| 6537 | | .size32 => unreachable, |
| 6538 | | |
| 6539 | | .larch_abs32_lo12 => .PCALA_LO12, |
| 6540 | | .larch_rel32_hi20 => .PCALA_HI20, |
| 6541 | | .larch_rel64_lo20 => .PCALA64_LO20, |
| 6542 | | .larch_rel64_hi12 => .PCALA64_HI12, |
| 6543 | | .larch_branch_rel18 => .B16, |
| 6544 | | .larch_branch_rel23 => .B21, |
| 6545 | | .larch_branch_rel28 => .B26, |
| 6546 | | .larch_call_rel38 => .CALL36, |
| 6547 | | .larch_tpoff32_lo12 => .TLS_LE_LO12, |
| 6548 | | .larch_tpoff32_hi20 => .TLS_LE_HI20, |
| 6549 | | .larch_tpoff64_lo20 => .TLS_LE64_LO20, |
| 6550 | | .larch_tpoff64_hi12 => .TLS_LE64_HI12, |
| 6551 | | |
| 6552 | | .sparc_wdisp30, |
| 6553 | | .sparc_pc10, |
| 6554 | | .sparc_pc22, |
| 6555 | | .sparc_wplt30, |
| 6556 | | .sparc_h44, |
| 6557 | | .sparc_m44, |
| 6558 | | .sparc_l44, |
| 6559 | | .sparc_ldo_hix22, |
| 6560 | | .sparc_ldo_lox10, |
| 6522 | // If this is `true`, we will try to create a copy relocation for the target symbol if it is |
| 6523 | // not locally defined. If the relocation value is always computed from the target symbol's |
| 6524 | // value (even for an external target symbol), and if the target symbol might be of type |
| 6525 | // STT_OBJECT, this should probably be `true`. |
| 6526 | const try_copy_reloc: bool = switch (@"type".target) { |
| 6527 | .rel, .abs => true, |
| 6528 | |
| 6529 | .pltrel, |
| 6530 | .pltabs, |
| 6531 | .dtpoff, |
| 6532 | .tpoff, |
| 6533 | .size, |
| 6534 | => false, |
| 6535 | |
| 6536 | .special => switch (@"type".action.special) { |
| 6537 | .larch_pcala_hi20, |
| 6538 | .larch_pcala64_lo20, |
| 6539 | .larch_pcala64_hi12, |
| 6540 | => true, |
| 6541 | |
| 6542 | .larch_b21, |
| 6543 | .larch_b26, |
| 6544 | .larch_call36, |
| 6561 | 6545 | .sparc_le_hix22, |
| 6562 | | .sparc_le_lox10, |
| 6563 | | => unreachable, |
| 6564 | | } }, |
| 6565 | | .SPARCV9 => .{ .SPARC = switch (@"type") { |
| 6566 | | .write_rela => unreachable, |
| 6567 | | .dsorel64, .dsorel32 => { |
| 6568 | | assert(target.unwrap() == .local); |
| 6569 | | break :r .none; |
| 6570 | | }, |
| 6571 | | .abs64 => .@"64", |
| 6572 | | .abs32 => .@"32", |
| 6573 | | .abs32s => unreachable, |
| 6574 | | .abs16 => .@"16", |
| 6575 | | .abs8 => .@"8", |
| 6576 | | .rel64 => .DISP64, |
| 6577 | | .rel32 => .DISP32, |
| 6578 | | .rel16 => .DISP16, |
| 6579 | | .rel8 => .DISP8, |
| 6580 | | .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none, |
| 6581 | | .dtpoff64 => .TLS_DTPOFF64, |
| 6582 | | .dtpoff32 => .TLS_DTPOFF32, |
| 6583 | | .tpoff64 => .TLS_TPOFF64, |
| 6584 | | .tpoff32 => .TLS_TPOFF32, |
| 6585 | | .size64 => .SIZE64, |
| 6586 | | .size32 => .SIZE32, |
| 6587 | | |
| 6588 | | .larch_abs32_lo12, |
| 6589 | | .larch_rel32_hi20, |
| 6590 | | .larch_rel64_lo20, |
| 6591 | | .larch_rel64_hi12, |
| 6592 | | .larch_branch_rel18, |
| 6593 | | .larch_branch_rel23, |
| 6594 | | .larch_branch_rel28, |
| 6595 | | .larch_call_rel38, |
| 6596 | | .larch_tpoff32_lo12, |
| 6597 | | .larch_tpoff32_hi20, |
| 6598 | | .larch_tpoff64_lo20, |
| 6599 | | .larch_tpoff64_hi12, |
| 6600 | | => unreachable, |
| 6601 | | |
| 6602 | | .sparc_wdisp30 => .WDISP30, |
| 6603 | | .sparc_pc10 => .PC10, |
| 6604 | | .sparc_pc22 => .PC22, |
| 6605 | | .sparc_wplt30 => .WPLT30, |
| 6606 | | .sparc_h44 => .H44, |
| 6607 | | .sparc_m44 => .M44, |
| 6608 | | .sparc_l44 => .L44, |
| 6609 | | .sparc_ldo_hix22 => .TLS_LDO_HIX22, |
| 6610 | | .sparc_ldo_lox10 => .TLS_LDO_LOX10, |
| 6611 | | .sparc_le_hix22 => .TLS_LE_HIX22, |
| 6612 | | .sparc_le_lox10 => .TLS_LE_LOX10, |
| 6613 | | } }, |
| 6546 | => false, |
| 6547 | }, |
| 6614 | 6548 | }; |
| 6615 | 6549 | |
| 6616 | | class: switch (elf.classifySymbolValue(target)) { |
| 6550 | classify: switch (elf.classifySymbolValue(target)) { |
| 6617 | 6551 | .static => break :r .none, |
| 6618 | 6552 | .static_relative => { |
| 6619 | | if (!@"type".isAbsAddr(elf)) break :r .none; |
| 6553 | switch (@"type".target) { |
| 6554 | // Only relocations which resolve to absolute addresses require runtime |
| 6555 | // `R_*_RELATIVE` relocations. |
| 6556 | .special, |
| 6557 | .pltrel, |
| 6558 | .rel, |
| 6559 | .dtpoff, |
| 6560 | .tpoff, |
| 6561 | .size, |
| 6562 | => break :r .none, |
| 6563 | |
| 6564 | .abs, .pltabs => {}, |
| 6565 | } |
| 6566 | if (!@"type".action.simple.dest.isAddr(elf)) break :r .none; |
| 6620 | 6567 | switch (elf.nodeWantsDsoRelocation(node)) { |
| 6621 | 6568 | .no => break :r .none, |
| 6622 | 6569 | .yes => {}, |
| ... | ... | @@ -6629,30 +6576,47 @@ fn addSymbolRelocAssumeCapacity( |
| 6629 | 6576 | .addend = 0, |
| 6630 | 6577 | }).toOptional(); |
| 6631 | 6578 | }, |
| 6632 | | .dynamic => dso_reloc: switch (elf.nodeWantsDsoRelocation(node)) { |
| 6633 | | .no => break :r .none, |
| 6634 | | .yes_textrel => if (try elf.maybeAddCopyRelocation(target.unwrap().global)) { |
| 6635 | | // We were able to use a copy relocation on this symbol to avoid a text relocation, |
| 6636 | | // which is apparently considered a good thing despite copy relocations being an |
| 6637 | | // abomination. (This is necessary for correctness in some cases, because e.g. a |
| 6638 | | // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to |
| 6639 | | // the DSOs being loaded too far apart.) |
| 6640 | | switch (elf.classifySymbolValue(target)) { |
| 6641 | | .dynamic => unreachable, // we just added a copy relocation |
| 6642 | | .static => continue :class .static, |
| 6643 | | .static_relative => continue :class .static_relative, |
| 6644 | | } |
| 6645 | | } else { |
| 6646 | | // At least for now, our only choice is a text relocation. |
| 6647 | | elf.textrel_count += 1; |
| 6648 | | continue :dso_reloc .yes; |
| 6649 | | }, |
| 6650 | | .yes => break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 6651 | | .type = rela_type, |
| 6579 | .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) { |
| 6580 | switch (elf.classifySymbolValue(target)) { |
| 6581 | .static => continue :classify .static, |
| 6582 | .static_relative => continue :classify .static_relative, |
| 6583 | .dynamic => unreachable, // we just added a copy relocation |
| 6584 | } |
| 6585 | } else { |
| 6586 | const dynamic_reloc_type: MachineRelocType = switch (@"type".target) { |
| 6587 | // PLT relocations targeting dynamic symbols actually target that symbol's PLT |
| 6588 | // entry, so we should emit an `R_*_RELATIVE` relocation instead. |
| 6589 | .pltabs => continue :classify .static_relative, |
| 6590 | // ...although PC-relative PLT relocations don't even need that! |
| 6591 | .pltrel => break :r .none, |
| 6592 | // Weird sizes or computations are not supported as runtime relocations. |
| 6593 | .special => break :r .none, |
| 6594 | // Relative addresses are not supported as runtime relocations. |
| 6595 | .rel => break :r .none, |
| 6596 | |
| 6597 | // On the few targets supporting size relocations, they are valid at runtime. |
| 6598 | .size => switch (@"type".action.simple.dest) { |
| 6599 | .@"32" => MachineRelocType.size32(elf) orelse break :r .none, |
| 6600 | .@"64" => MachineRelocType.size64(elf) orelse break :r .none, |
| 6601 | else => break :r .none, |
| 6602 | }, |
| 6603 | // Absolute addresses and TLS offsets can be lowered at runtime provided they |
| 6604 | // are address-sized. |
| 6605 | .dtpoff => if (@"type".action.simple.dest.isAddr(elf)) .dtpOff(elf) else break :r .none, |
| 6606 | .tpoff => if (@"type".action.simple.dest.isAddr(elf)) .tpOff(elf) else break :r .none, |
| 6607 | .abs => if (@"type".action.simple.dest.isAddr(elf)) .absAddr(elf) else break :r .none, |
| 6608 | }; |
| 6609 | switch (elf.nodeWantsDsoRelocation(node)) { |
| 6610 | .no => break :r .none, |
| 6611 | .yes => {}, |
| 6612 | .yes_textrel => elf.textrel_count += 1, |
| 6613 | } |
| 6614 | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 6615 | .type = dynamic_reloc_type, |
| 6652 | 6616 | .offset = node_vaddr + offset, |
| 6653 | 6617 | .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index, |
| 6654 | 6618 | .addend = addend, |
| 6655 | | }).toOptional(), |
| 6619 | }).toOptional(); |
| 6656 | 6620 | }, |
| 6657 | 6621 | } |
| 6658 | 6622 | }; |
| ... | ... | @@ -6673,8 +6637,9 @@ fn addSymbolRelocAssumeCapacity( |
| 6673 | 6637 | .next = next, |
| 6674 | 6638 | .prev = .none, |
| 6675 | 6639 | .rela_index = rela_index, |
| 6640 | .result = .ok, |
| 6676 | 6641 | }); |
| 6677 | | if (@"type".dependsOnTlsSize()) { |
| 6642 | if (@"type".dependsOnTlsSize(elf)) { |
| 6678 | 6643 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 6679 | 6644 | } |
| 6680 | 6645 | |
| ... | ... | @@ -6689,7 +6654,7 @@ fn addGotRelocAssumeCapacity( |
| 6689 | 6654 | addend: i64, |
| 6690 | 6655 | @"type": GotReloc.Type, |
| 6691 | 6656 | ) void { |
| 6692 | | assert(elf.ehdrField(.type) != .REL); |
| 6657 | assert(elf.ehdrType() != .REL); |
| 6693 | 6658 | switch (elf.getNode(node)) { |
| 6694 | 6659 | .input_section, |
| 6695 | 6660 | .nav, |
| ... | ... | @@ -6742,6 +6707,7 @@ fn addGotRelocAssumeCapacity( |
| 6742 | 6707 | .target = target, |
| 6743 | 6708 | .addend = addend, |
| 6744 | 6709 | .type = @"type", |
| 6710 | .result = .ok, |
| 6745 | 6711 | }); |
| 6746 | 6712 | } |
| 6747 | 6713 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| ... | ... | @@ -6768,23 +6734,17 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6768 | 6734 | const sym_value = sym_id.value(elf); |
| 6769 | 6735 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; |
| 6770 | 6736 | } |
| 6771 | | const reloc_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 6772 | | else => |machine| @panic(@tagName(machine)), |
| 6773 | | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, |
| 6774 | | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_TPOFF64 else .TLS_TPOFF32 }, |
| 6775 | | .X86_64 => .{ .X86_64 = .TPOFF64 }, |
| 6776 | | }; |
| 6777 | 6737 | break :val switch (sym_id.unwrap()) { |
| 6778 | 6738 | // For global symbols, just target the right dynsym with no addend. |
| 6779 | 6739 | .global => |name| .{ .reloc = .{ |
| 6780 | | .type = reloc_type, |
| 6740 | .type = .tpOff(elf), |
| 6781 | 6741 | .dynsym_index = elf.globalByName(name).?.dynsym_index, |
| 6782 | 6742 | .addend = 0, |
| 6783 | 6743 | } }, |
| 6784 | 6744 | // For local symbols, target the null symbol (index 0) so we get the offset to the |
| 6785 | 6745 | // base of our TLS block, and then use `addend` to offset to the right symbol. |
| 6786 | 6746 | .local => .{ .reloc = .{ |
| 6787 | | .type = reloc_type, |
| 6747 | .type = .tpOff(elf), |
| 6788 | 6748 | .dynsym_index = 0, |
| 6789 | 6749 | .addend = @intCast(sym_id.value(elf)), |
| 6790 | 6750 | } }, |
| ... | ... | @@ -6807,7 +6767,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6807 | 6767 | .static => .{ .unsigned = sym.value(elf) }, |
| 6808 | 6768 | .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative` |
| 6809 | 6769 | .dynamic => .{ .reloc = .{ |
| 6810 | | .type = .dtpOffAddr(elf), |
| 6770 | .type = .dtpOff(elf), |
| 6811 | 6771 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 6812 | 6772 | .addend = 0, |
| 6813 | 6773 | } }, |
| ... | ... | @@ -6818,12 +6778,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6818 | 6778 | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| 6819 | 6779 | }, |
| 6820 | 6780 | .dynamic => .{ .reloc = .{ |
| 6821 | | .type = switch (elf.ehdrField(.machine)) { |
| 6822 | | else => |machine| @panic(@tagName(machine)), |
| 6823 | | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 6824 | | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 6825 | | .X86_64 => .{ .X86_64 = .DTPMOD64 }, |
| 6826 | | }, |
| 6781 | .type = .dtpMod(elf), |
| 6827 | 6782 | .dynsym_index = switch (elf.classifySymbolValue(sym)) { |
| 6828 | 6783 | .static, .static_relative => 0, |
| 6829 | 6784 | .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| ... | ... | @@ -6837,12 +6792,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6837 | 6792 | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| 6838 | 6793 | }, |
| 6839 | 6794 | .dynamic => .{ .reloc = .{ |
| 6840 | | .type = switch (elf.ehdrField(.machine)) { |
| 6841 | | else => |machine| @panic(@tagName(machine)), |
| 6842 | | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 6843 | | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 6844 | | .X86_64 => .{ .X86_64 = .DTPMOD64 }, |
| 6845 | | }, |
| 6795 | .type = .dtpMod(elf), |
| 6846 | 6796 | .dynsym_index = 0, |
| 6847 | 6797 | .addend = 0, |
| 6848 | 6798 | } }, |
| ... | ... | @@ -7113,18 +7063,30 @@ pub fn flush( |
| 7113 | 7063 | for (elf.globals.strong_undef.keys()) |name| { |
| 7114 | 7064 | if (elf.dso_globals.contains(name)) continue; |
| 7115 | 7065 | any_undef = true; |
| 7116 | | comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)}); |
| 7066 | diags.addError("undefined global symbol '{s}'", .{name.slice(elf)}); |
| 7117 | 7067 | } |
| 7118 | 7068 | if (any_undef) return error.AlreadyReported; |
| 7119 | 7069 | } |
| 7120 | 7070 | |
| 7121 | | elf.updateDynamicTextrel() catch |err| switch (err) { |
| 7071 | elf.prepareDynamic() catch |err| switch (err) { |
| 7122 | 7072 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), |
| 7123 | 7073 | else => |e| return e, |
| 7124 | 7074 | }; |
| 7125 | 7075 | |
| 7126 | 7076 | while (try elf.idle(tid)) {} |
| 7127 | 7077 | |
| 7078 | // We've done the final `idle` loop, so everything is at its final place in the file. We have a |
| 7079 | // few more things to check and write now that addresses and offsets are finalized. |
| 7080 | |
| 7081 | if (elf.overflowed_reloc_count > 0) { |
| 7082 | diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count}); |
| 7083 | } |
| 7084 | if (elf.misaligned_reloc_count > 0) { |
| 7085 | diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count}); |
| 7086 | } |
| 7087 | |
| 7088 | elf.flushDynamic(); |
| 7089 | |
| 7128 | 7090 | const entry_addr: u64 = entry: { |
| 7129 | 7091 | const sym_name_slice: []const u8 = name: switch (elf.options.entry) { |
| 7130 | 7092 | .default => switch (comp.config.output_mode) { |
| ... | ... | @@ -7151,44 +7113,6 @@ pub fn flush( |
| 7151 | 7113 | else => |e| return e, |
| 7152 | 7114 | }; |
| 7153 | 7115 | } |
| 7154 | | fn updateDynamicTextrel(elf: *Elf) Error!void { |
| 7155 | | if (elf.shndx.dynamic == .UNDEF) return; |
| 7156 | | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; |
| 7157 | | switch (elf.shdrPtr(elf.shndx.dynamic)) { |
| 7158 | | inline else => |shdr, class| if (elf.textrel_count > 0) { |
| 7159 | | const cur_size = elf.targetLoad(&shdr.size); |
| 7160 | | const cur_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 7161 | | dynamic_ni.slice(&elf.mf)[0..@intCast(cur_size)], |
| 7162 | | )); |
| 7163 | | const has_textrel: bool = for (cur_entries) |*entry| { |
| 7164 | | if (elf.targetLoad(&entry[0]) == std.elf.DT_TEXTREL) { |
| 7165 | | break true; |
| 7166 | | } |
| 7167 | | } else false; |
| 7168 | | if (!has_textrel) { |
| 7169 | | // Add a DT_TEXTREL entry before the final DT_NULL entry. |
| 7170 | | const new_size = cur_size + @sizeOf([2]class.ElfN().Addr); |
| 7171 | | try elf.ensureNodeSize(dynamic_ni, new_size); |
| 7172 | | elf.targetStore(&shdr.size, new_size); |
| 7173 | | const new_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( |
| 7174 | | dynamic_ni.slice(&elf.mf)[0..@intCast(new_size)], |
| 7175 | | )); |
| 7176 | | const write_entries = new_entries[new_entries.len - 2 ..][0..2]; |
| 7177 | | assert(elf.targetLoad(&write_entries[0][0]) == std.elf.DT_NULL); |
| 7178 | | write_entries.* = .{ |
| 7179 | | .{ std.elf.DT_TEXTREL, 0 }, |
| 7180 | | .{ std.elf.DT_NULL, 0 }, |
| 7181 | | }; |
| 7182 | | if (elf.targetEndian() != native_endian) { |
| 7183 | | std.mem.byteSwapAllElements([2]class.ElfN().Addr, write_entries); |
| 7184 | | } |
| 7185 | | } |
| 7186 | | } else { |
| 7187 | | // TODO: remove the DT_TEXTREL entry if there is one, because it's not necessary any |
| 7188 | | // more. It won't cause any issues having it there, it's just inefficient. |
| 7189 | | }, |
| 7190 | | } |
| 7191 | | } |
| 7192 | 7116 | |
| 7193 | 7117 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7194 | 7118 | const comp = elf.base.comp; |
| ... | ... | @@ -7220,7 +7144,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7220 | 7144 | const sym_id: Symbol.Id = .global(global_name); |
| 7221 | 7145 | const sym = global.symtab_index.ptr(elf); |
| 7222 | 7146 | |
| 7223 | | switch (elf.ehdrField(.type)) { |
| 7147 | switch (elf.ehdrType()) { |
| 7224 | 7148 | .REL => { |
| 7225 | 7149 | // Index in `.symtab` has changed. Relocatables are easy, we just need to update |
| 7226 | 7150 | // all of the output relocations. |
| ... | ... | @@ -7238,7 +7162,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7238 | 7162 | // For other `ET_*` values, the index in `.dynsym` has changed. There are a few |
| 7239 | 7163 | // places we might have emitted output relocations, depending on whether or not the |
| 7240 | 7164 | // symbol's value is statically known. |
| 7241 | | else => switch (elf.classifySymbolValue(sym_id)) { |
| 7165 | .EXEC, .DYN => switch (elf.classifySymbolValue(sym_id)) { |
| 7242 | 7166 | .static, .static_relative => { |
| 7243 | 7167 | // Since the symbol value is statically known, we definitely aren't emitting |
| 7244 | 7168 | // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they |
| ... | ... | @@ -7807,10 +7731,202 @@ fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void { |
| 7807 | 7731 | }, |
| 7808 | 7732 | } |
| 7809 | 7733 | } |
| 7734 | fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void { |
| 7735 | const target_endian = elf.targetEndian(); |
| 7736 | |
| 7737 | // We use the existing free-list tracking of the `.rela.plt` section to also behave as a |
| 7738 | // free-list for the PLT itself---see `pltEntryIsDead` for details. |
| 7739 | const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{ |
| 7740 | .type = .jumpSlot(elf), |
| 7741 | .offset = 0, // populated later |
| 7742 | .raw_sym_index = dynsym_index, |
| 7743 | .addend = 0, |
| 7744 | })); |
| 7745 | |
| 7746 | // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`. |
| 7747 | const got_plt_section: Section.Index, const got_plt_offset: u64 = got_plt: { |
| 7748 | const plt = elf.targetPltInfo(); |
| 7749 | break :got_plt if (plt.got_plt) |got_plt| .{ |
| 7750 | elf.shndx.got_plt, |
| 7751 | elf.targetPtrSize() * (got_plt.header_entries + plt_index), |
| 7752 | } else .{ |
| 7753 | elf.shndx.plt, |
| 7754 | plt.entry_size * (plt.header_entries + plt_index), |
| 7755 | }; |
| 7756 | }; |
| 7757 | |
| 7758 | // Now that we know the index, we can set the relocation's offset. |
| 7759 | elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset); |
| 7760 | |
| 7761 | if (plt_index < elf.plt.count()) { |
| 7762 | // We reused a free entry, so we're already done! |
| 7763 | elf.plt.setKey(plt_index, global_name); |
| 7764 | return; |
| 7765 | } |
| 7766 | |
| 7767 | // We added a new entry, so we now need to extend the PLT sections. |
| 7768 | assert(plt_index == elf.plt.count()); |
| 7769 | elf.plt.putAssumeCapacityNoClobber(global_name, {}); |
| 7770 | |
| 7771 | switch (elf.ehdrMachine()) { |
| 7772 | .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)), |
| 7773 | .X86_64 => { |
| 7774 | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 7775 | const plt_addr = plt_addr: switch (elf.shdrPtr(elf.shndx.plt)) { |
| 7776 | inline else => |shdr| { |
| 7777 | const old_size = 16 * (1 + plt_index); |
| 7778 | assert(elf.targetLoad(&shdr.size) == old_size); |
| 7779 | elf.targetStore(&shdr.size, old_size + 16); |
| 7780 | const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16]; |
| 7781 | @memcpy(plt_slice, &[16]u8{ |
| 7782 | 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 |
| 7783 | 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0 |
| 7784 | 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0 |
| 7785 | 0x66, 0x90, // xchg %ax,%ax |
| 7786 | }); |
| 7787 | std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian); |
| 7788 | std.mem.writeInt( |
| 7789 | i32, |
| 7790 | plt_slice[10..][0..4], |
| 7791 | -@as(i32, @intCast(old_size + 14)), |
| 7792 | target_endian, |
| 7793 | ); |
| 7794 | break :plt_addr elf.targetLoad(&shdr.addr) + old_size; |
| 7795 | }, |
| 7796 | }; |
| 7797 | |
| 7798 | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; |
| 7799 | switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 7800 | inline else => |shdr, class| { |
| 7801 | assert(elf.targetLoad(&shdr.size) == got_plt_offset); |
| 7802 | elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr))); |
| 7803 | std.mem.writeInt( |
| 7804 | class.ElfN().Addr, |
| 7805 | got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)], |
| 7806 | @intCast(plt_addr), |
| 7807 | target_endian, |
| 7808 | ); |
| 7809 | }, |
| 7810 | } |
| 7811 | |
| 7812 | const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni; |
| 7813 | switch (elf.shdrPtr(elf.shndx.plt_sec)) { |
| 7814 | inline else => |shdr| { |
| 7815 | const old_size = 16 * plt_index; |
| 7816 | elf.targetStore(&shdr.size, old_size + 16); |
| 7817 | const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..][0..16]; |
| 7818 | @memcpy(plt_sec_slice, &[16]u8{ |
| 7819 | 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 |
| 7820 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip) |
| 7821 | 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1) |
| 7822 | }); |
| 7823 | std.mem.writeInt( |
| 7824 | i32, |
| 7825 | plt_sec_slice[6..][0..4], |
| 7826 | @intCast(@as(i64, @bitCast( |
| 7827 | (got_plt_section.vaddr(elf) + got_plt_offset) -% (elf.targetLoad(&shdr.addr) + old_size + 10), |
| 7828 | ))), |
| 7829 | target_endian, |
| 7830 | ); |
| 7831 | }, |
| 7832 | } |
| 7833 | }, |
| 7834 | .LOONGARCH => { |
| 7835 | // add a .PLT entry, writing the template |
| 7836 | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 7837 | const plt_addr, const plt_slice = plt_entry: switch (elf.shdrPtr(elf.shndx.plt)) { |
| 7838 | inline else => |shdr| { |
| 7839 | const old_size = 16 * (1 + plt_index); |
| 7840 | assert(elf.targetLoad(&shdr.size) == old_size); |
| 7841 | elf.targetStore(&shdr.size, old_size + 16); |
| 7842 | const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16]; |
| 7843 | @memcpy(plt_slice, source: switch (elf.identClass()) { |
| 7844 | .NONE, _ => unreachable, |
| 7845 | inline .@"32", .@"64" => |elf_class| { |
| 7846 | const ld_byte = if (elf_class == .@"64") 0xc0 else 0x80; |
| 7847 | break :source &[16]u8{ |
| 7848 | 0x1a, 0x00, 0x00, 0x0f, // pcalau12i $t3, %pc_hi20(func@.got.plt) |
| 7849 | 0x28, ld_byte, 0x01, 0xef, // ld.w/d $t3, $t3, %lo12(func@.got.plt) |
| 7850 | 0x4c, 0x00, 0x01, 0xed, // jirl $t1, $t3, 0 |
| 7851 | 0x00, 0x2a, 0x00, 0x00, // break |
| 7852 | }; |
| 7853 | }, |
| 7854 | }); |
| 7855 | break :plt_entry .{ elf.targetLoad(&shdr.addr) + old_size, plt_slice }; |
| 7856 | }, |
| 7857 | }; |
| 7858 | |
| 7859 | // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry |
| 7860 | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; |
| 7861 | switch (elf.shdrPtr(elf.shndx.got_plt)) { |
| 7862 | inline else => |shdr, class| { |
| 7863 | assert(elf.targetLoad(&shdr.size) == got_plt_offset); |
| 7864 | elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr))); |
| 7865 | std.mem.writeInt( |
| 7866 | class.ElfN().Addr, |
| 7867 | got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)], |
| 7868 | @intCast(plt_addr), |
| 7869 | target_endian, |
| 7870 | ); |
| 7871 | }, |
| 7872 | } |
| 7873 | |
| 7874 | // relocate the PLT entry to point to the .GOT.PLT entry |
| 7875 | const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset; |
| 7876 | // TODO: handle overflow gracefully |
| 7877 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]); |
| 7878 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]); |
| 7879 | elf.targetStore(inst0, .{ |
| 7880 | .b0_4 = elf.targetLoad(inst0).b0_4, |
| 7881 | .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr), |
| 7882 | .b25_31 = elf.targetLoad(inst0).b25_31, |
| 7883 | }); |
| 7884 | elf.targetStore(inst1, .{ |
| 7885 | .b0_9 = elf.targetLoad(inst1).b0_9, |
| 7886 | .k12 = @truncate(got_plt_abs), |
| 7887 | .b22_31 = elf.targetLoad(inst1).b22_31, |
| 7888 | }); |
| 7889 | }, |
| 7890 | .SPARCV9 => { |
| 7891 | // add a .PLT entry, writing the template |
| 7892 | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 7893 | switch (elf.shdrPtr(elf.shndx.plt)) { |
| 7894 | inline else => |shdr| { |
| 7895 | assert(elf.targetLoad(&shdr.size) == got_plt_offset); |
| 7896 | elf.targetStore(&shdr.size, @intCast(got_plt_offset + 32)); |
| 7897 | const Inst = packed union(u32) { |
| 7898 | raw: u32, |
| 7899 | imm22: packed struct { imm: u22, op: u10 }, |
| 7900 | disp19: packed struct { disp: u19, op: u13 }, |
| 7901 | }; |
| 7902 | const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32])); |
| 7903 | @memcpy(plt_slice, &[8]Inst{ |
| 7904 | // sethi (. - .plt[0]), %g1 |
| 7905 | .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000000011 } }, |
| 7906 | // ba,a %xcc, .plt[1] |
| 7907 | .{ .disp19 = .{ .disp = @truncate((got_plt_offset + 4 - 32) >> 2), .op = 0b1100001101000 } }, |
| 7908 | // nop |
| 7909 | .{ .raw = 0x0100_0000 }, |
| 7910 | // nop |
| 7911 | .{ .raw = 0x0100_0000 }, |
| 7912 | // nop |
| 7913 | .{ .raw = 0x0100_0000 }, |
| 7914 | // nop |
| 7915 | .{ .raw = 0x0100_0000 }, |
| 7916 | // nop |
| 7917 | .{ .raw = 0x0100_0000 }, |
| 7918 | // nop |
| 7919 | .{ .raw = 0x0100_0000 }, |
| 7920 | }); |
| 7921 | }, |
| 7922 | } |
| 7923 | }, |
| 7924 | } |
| 7925 | } |
| 7810 | 7926 | fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void { |
| 7811 | 7927 | const target_endian = elf.targetEndian(); |
| 7812 | | switch (elf.ehdrField(.machine)) { |
| 7813 | | else => |machine| @panic(@tagName(machine)), |
| 7928 | switch (elf.ehdrMachine()) { |
| 7929 | .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)), |
| 7814 | 7930 | .X86_64 => { |
| 7815 | 7931 | switch (which) { |
| 7816 | 7932 | .plt => return, |
| ... | ... | @@ -7912,8 +8028,20 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad |
| 7912 | 8028 | |
| 7913 | 8029 | const got_plt_abs: u64 = got_plt_addr + got_plt_offset; |
| 7914 | 8030 | // TODO: handle overflow gracefully |
| 7915 | | link.loongarch.writeJ20(target_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr + plt_offset)); |
| 7916 | | link.loongarch.writeK12(target_slice[4..8], @truncate(got_plt_abs)); |
| 8031 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]); |
| 8032 | const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]); |
| 8033 | |
| 8034 | elf.targetStore(inst0, .{ |
| 8035 | .b0_4 = elf.targetLoad(inst0).b0_4, |
| 8036 | .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr + plt_offset), |
| 8037 | .b25_31 = elf.targetLoad(inst0).b25_31, |
| 8038 | }); |
| 8039 | |
| 8040 | elf.targetStore(inst1, .{ |
| 8041 | .b0_9 = elf.targetLoad(inst1).b0_9, |
| 8042 | .k12 = @truncate(got_plt_abs), |
| 8043 | .b22_31 = elf.targetLoad(inst1).b22_31, |
| 8044 | }); |
| 7917 | 8045 | } |
| 7918 | 8046 | }, |
| 7919 | 8047 | } |
| ... | ... | @@ -7986,10 +8114,10 @@ fn updateExportsInner( |
| 7986 | 8114 | .size = @intCast(size), |
| 7987 | 8115 | .type = @"type", |
| 7988 | 8116 | .bind = switch (@"export".opts.linkage) { |
| 7989 | | .internal => @panic("TODO internal linkage"), |
| 7990 | 8117 | .strong => .strong, |
| 7991 | 8118 | .weak => .weak, |
| 7992 | | .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): link_once is not supported", .{}), |
| 8119 | .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}), |
| 8120 | .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}), |
| 7993 | 8121 | }, |
| 7994 | 8122 | .visibility = switch (@"export".opts.visibility) { |
| 7995 | 8123 | .default => .DEFAULT, |
| ... | ... | @@ -8152,3 +8280,19 @@ fn ensureNodeSize( |
| 8152 | 8280 | const new_size = need_size + need_size / MappedFile.growth_factor; |
| 8153 | 8281 | try node.resize(&elf.mf, gpa, new_size); |
| 8154 | 8282 | } |
| 8283 | |
| 8284 | /// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a |
| 8285 | /// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`. |
| 8286 | fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 { |
| 8287 | const index = switch (sym.unwrap()) { |
| 8288 | .local => return null, |
| 8289 | .global => |name| elf.plt.getIndex(name) orelse return null, |
| 8290 | }; |
| 8291 | if (elf.pltEntryIsDead(index)) return null; |
| 8292 | const plt = elf.targetPltInfo(); |
| 8293 | if (plt.plt_sec) |plt_sec| { |
| 8294 | return elf.shndx.plt_sec.vaddr(elf) +% index * plt_sec.entry_size; |
| 8295 | } else { |
| 8296 | return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size; |
| 8297 | } |
| 8298 | } |