| ... | @@ -44,6 +44,12 @@ shndx: struct { | ... | @@ -44,6 +44,12 @@ shndx: struct { |
| 44 | fini_array: Section.Index, | 44 | fini_array: Section.Index, |
| 45 | preinit_array: Section.Index, | 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 | symtab: std.ArrayList(Symbol), | 53 | symtab: std.ArrayList(Symbol), |
| 48 | globals: struct { | 54 | globals: struct { |
| 49 | strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global), | 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,7 +64,7 @@ copied_globals: std.array_hash_map.Auto(String(.strtab), struct { |
| 58 | rela_index: Section.RelaIndex, | 64 | rela_index: Section.RelaIndex, |
| 59 | }), | 65 | }), |
| 60 | /// Key is the name of an undef global for which we would *like* to create a copy relocation | 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 | /// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we | 69 | /// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we |
| 64 | /// will remove it from this map and call `maybeAddCopyRelocation`. | 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,6 +171,9 @@ changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), |
| 165 | /// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`. | 171 | /// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`. |
| 166 | textrel_count: u32, | 172 | textrel_count: u32, |
| 167 | | 173 | |
| | 174 | overflowed_reloc_count: u32, |
| | 175 | misaligned_reloc_count: u32, |
| | 176 | |
| 168 | const_prog_node: std.Progress.Node, | 177 | const_prog_node: std.Progress.Node, |
| 169 | synth_prog_node: std.Progress.Node, | 178 | synth_prog_node: std.Progress.Node, |
| 170 | input_prog_node: std.Progress.Node, | 179 | input_prog_node: std.Progress.Node, |
| ... | @@ -486,6 +495,12 @@ const Section = struct { | ... | @@ -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 | fn flags(s: Index, elf: *Elf) std.elf.SHF { | 504 | fn flags(s: Index, elf: *Elf) std.elf.SHF { |
| 490 | return switch (elf.shdrPtr(s)) { | 505 | return switch (elf.shdrPtr(s)) { |
| 491 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, | 506 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, |
| ... | @@ -769,36 +784,111 @@ const GotReloc = struct { | ... | @@ -769,36 +784,111 @@ const GotReloc = struct { |
| 769 | target: GotKey, | 784 | target: GotKey, |
| 770 | addend: i64, | 785 | addend: i64, |
| 771 | type: GotReloc.Type, | 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 = .{ | 797 | fn special(s: Special) GotReloc.Type { |
| 774 | .node = .none, | 798 | return .{ .target = .special, .action = .{ .special = s } }; |
| 775 | .offset = undefined, | 799 | } |
| 776 | .target = undefined, | 800 | |
| 777 | .addend = undefined, | 801 | target: Target, |
| 778 | .type = undefined, | 802 | action: packed union { |
| 779 | }; | 803 | simple: Simple, |
| | 804 | special: Special, |
| | 805 | }, |
| 780 | | 806 | |
| 781 | const Type = enum(u8) { | 807 | /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there |
| 782 | offset32, | 808 | /// are fewer different kinds of GOT relocation. |
| 783 | offset64, | 809 | const Target = enum(u3) { |
| 784 | rel32, | 810 | /// This is a "special" relocation whose specific type is in the `action.special` field. |
| 785 | rel64, | 811 | special, |
| 786 | | 812 | |
| 787 | larch_rel32_hi20, | 813 | /// Absolute address of the GOT entry. |
| 788 | larch_rel64_lo20, | 814 | abs, |
| 789 | larch_rel64_hi12, | 815 | /// Offset from the relocation itself to the GOT entry ("PC-relative"). |
| 790 | larch_abs32_lo12, | 816 | rel, |
| 791 | larch_abs32_hi20, | 817 | /// Offset from the base of the GOT to the GOT entry. |
| 792 | larch_abs64_lo20, | 818 | offset, |
| 793 | larch_abs64_hi12, | 819 | }; |
| 794 | | 820 | |
| 795 | sparc_10, | 821 | const Simple = SymbolReloc.Type.Simple; |
| 796 | sparc_13, | 822 | |
| 797 | sparc_22, | 823 | /// Like `SymbolReloc.Special`, but for GOT relocations. |
| 798 | sparc_ldm_hi22, | 824 | const Special = enum(u13) { |
| 799 | sparc_ldm_lo10, | 825 | larch_pcala_hi20, |
| 800 | sparc_op_hix22, | 826 | larch_pcala64_lo20, |
| 801 | sparc_op_lox10, | 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 | const Index = enum(u32) { | 894 | const Index = enum(u32) { |
| ... | @@ -810,14 +900,34 @@ const GotReloc = struct { | ... | @@ -810,14 +900,34 @@ const GotReloc = struct { |
| 810 | } | 900 | } |
| 811 | }; | 901 | }; |
| 812 | | 902 | |
| 813 | fn apply(reloc: *const GotReloc, elf: *Elf) void { | 903 | fn apply(reloc: *GotReloc, elf: *Elf) void { |
| 814 | assert(elf.ehdrField(.type) != .REL); | 904 | assert(elf.ehdrType() != .REL); |
| 815 | if (reloc.node == .none) return; // deleted | 905 | if (reloc.node == .none) return; // deleted |
| 816 | if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { | 906 | if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { |
| 817 | // There's no point applying the relocation now, because it will be re-applied by | 907 | // There's no point applying the relocation now, because it will be re-applied by |
| 818 | // `flushMoved` at some point anyway. | 908 | // `flushMoved` at some point anyway. |
| 819 | return; | 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 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { | 931 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 822 | .file => unreachable, | 932 | .file => unreachable, |
| 823 | .ehdr => unreachable, | 933 | .ehdr => unreachable, |
| ... | @@ -834,7 +944,7 @@ const GotReloc = struct { | ... | @@ -834,7 +944,7 @@ const GotReloc = struct { |
| 834 | }; | 944 | }; |
| 835 | const dest_vaddr = node_vaddr + reloc.offset; | 945 | const dest_vaddr = node_vaddr + reloc.offset; |
| 836 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; | 946 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 837 | const target_endian = elf.targetEndian(); | 947 | |
| 838 | const got_vaddr = elf.shndx.got.vaddr(elf); | 948 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 839 | const got_index: u64 = elf.got.getIndex(reloc.target).?; | 949 | const got_index: u64 = elf.got.getIndex(reloc.target).?; |
| 840 | const got_offset: u64 = switch (elf.identClass()) { | 950 | const got_offset: u64 = switch (elf.identClass()) { |
| ... | @@ -842,228 +952,201 @@ const GotReloc = struct { | ... | @@ -842,228 +952,201 @@ const GotReloc = struct { |
| 842 | inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, | 952 | inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, |
| 843 | }; | 953 | }; |
| 844 | const addend: u64 = @bitCast(reloc.addend); | 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 => { | 956 | const target_val: u64 = switch (reloc.type.target) { |
| 872 | assert(elf.ehdrField(.machine) == .LOONGARCH); | 957 | .abs => got_vaddr +% got_offset +% addend, |
| 873 | const target_value = got_vaddr +% got_offset +% addend; | 958 | .rel => got_vaddr +% got_offset +% addend -% dest_vaddr, |
| 874 | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr)); | 959 | .offset => got_offset +% addend, |
| 875 | }, | 960 | .special => return reloc.type.action.special.applyInner( |
| 876 | .larch_rel64_lo20 => { | 961 | elf, |
| 877 | assert(elf.ehdrField(.machine) == .LOONGARCH); | 962 | got_vaddr, |
| 878 | const target_value = got_vaddr +% got_offset +% addend; | 963 | got_offset, |
| 879 | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr)); | 964 | addend, |
| 880 | }, | 965 | dest_vaddr, |
| 881 | .larch_rel64_hi12 => { | 966 | dest_slice, |
| 882 | assert(elf.ehdrField(.machine) == .LOONGARCH); | 967 | ), |
| 883 | const target_value = got_vaddr +% got_offset +% addend; | 968 | }; |
| 884 | link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr)); | 969 | try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); |
| 885 | }, | 970 | } |
| 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 | }, | | |
| 906 | | 971 | |
| 907 | .sparc_10 => { | 972 | fn delete(reloc: *GotReloc, elf: *Elf) void { |
| 908 | const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice)); | 973 | switch (reloc.result) { |
| 909 | var result = elf.targetLoad(dest_ptr); | 974 | .ok => {}, |
| 910 | result.simm13 = @as(u10, @truncate(got_offset)); | 975 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 911 | elf.targetStore(dest_ptr, result); | 976 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 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 | }, | | |
| 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 | pub const MachineRelocType = union { | 989 | pub const MachineRelocType = union { |
| 954 | AARCH64: std.elf.R_AARCH64, | 990 | AARCH64: std.elf.R_AARCH64, |
| 955 | LOONGARCH: std.elf.R_LARCH, | 991 | LARCH: std.elf.R_LARCH, |
| 956 | PPC64: std.elf.R_PPC64, | 992 | PPC64: std.elf.R_PPC64, |
| 957 | RISCV: std.elf.R_RISCV, | 993 | RISCV: std.elf.R_RISCV, |
| 958 | SPARC: std.elf.R_SPARC, | 994 | SPARC: std.elf.R_SPARC, |
| 959 | X86_64: std.elf.R_X86_64, | 995 | X86_64: std.elf.R_X86_64, |
| 960 | | 996 | |
| 961 | pub fn none(elf: *Elf) MachineRelocType { | 997 | pub const Format = struct { |
| 962 | return switch (elf.ehdrField(.machine)) { | 998 | rt: MachineRelocType, |
| 963 | else => unreachable, | 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 | .AARCH64 => .{ .AARCH64 = .NONE }, | 1019 | .AARCH64 => .{ .AARCH64 = .NONE }, |
| 965 | .LOONGARCH => .{ .LOONGARCH = .NONE }, | 1020 | .LOONGARCH => .{ .LARCH = .NONE }, |
| 966 | .PPC64 => .{ .PPC64 = .NONE }, | 1021 | .PPC64 => .{ .PPC64 = .NONE }, |
| 967 | .RISCV => .{ .RISCV = .NONE }, | 1022 | .RISCV => .{ .RISCV = .NONE }, |
| 968 | .SPARCV9 => .{ .SPARC = .NONE }, | 1023 | .SPARCV9 => .{ .SPARC = .NONE }, |
| 969 | .X86_64 => .{ .X86_64 = .NONE }, | 1024 | .X86_64 => .{ .X86_64 = .NONE }, |
| 970 | }; | 1025 | }; |
| 971 | } | 1026 | } |
| 972 | pub fn copy(elf: *Elf) MachineRelocType { | 1027 | pub fn copy(elf: *const Elf) MachineRelocType { |
| 973 | return switch (elf.ehdrField(.machine)) { | 1028 | return switch (elf.ehdrMachine()) { |
| 974 | else => unreachable, | | |
| 975 | .AARCH64 => .{ .AARCH64 = .COPY }, | 1029 | .AARCH64 => .{ .AARCH64 = .COPY }, |
| 976 | .LOONGARCH => .{ .LOONGARCH = .COPY }, | 1030 | .LOONGARCH => .{ .LARCH = .COPY }, |
| 977 | .PPC64 => .{ .PPC64 = .COPY }, | 1031 | .PPC64 => .{ .PPC64 = .COPY }, |
| 978 | .RISCV => .{ .RISCV = .COPY }, | 1032 | .RISCV => .{ .RISCV = .COPY }, |
| 979 | .SPARCV9 => .{ .SPARC = .COPY }, | 1033 | .SPARCV9 => .{ .SPARC = .COPY }, |
| 980 | .X86_64 => .{ .X86_64 = .COPY }, | 1034 | .X86_64 => .{ .X86_64 = .COPY }, |
| 981 | }; | 1035 | }; |
| 982 | } | 1036 | } |
| 983 | pub fn relative(elf: *Elf) MachineRelocType { | 1037 | pub fn relative(elf: *const Elf) MachineRelocType { |
| 984 | return switch (elf.ehdrField(.machine)) { | 1038 | return switch (elf.ehdrMachine()) { |
| 985 | else => unreachable, | | |
| 986 | .AARCH64 => .{ .AARCH64 = .RELATIVE }, | 1039 | .AARCH64 => .{ .AARCH64 = .RELATIVE }, |
| 987 | .LOONGARCH => .{ .LOONGARCH = .RELATIVE }, | 1040 | .LOONGARCH => .{ .LARCH = .RELATIVE }, |
| 988 | .PPC64 => .{ .PPC64 = .RELATIVE }, | 1041 | .PPC64 => .{ .PPC64 = .RELATIVE }, |
| 989 | .RISCV => .{ .RISCV = .RELATIVE }, | 1042 | .RISCV => .{ .RISCV = .RELATIVE }, |
| 990 | .SPARCV9 => .{ .SPARC = .RELATIVE }, | 1043 | .SPARCV9 => .{ .SPARC = .RELATIVE }, |
| 991 | .X86_64 => .{ .X86_64 = .RELATIVE }, | 1044 | .X86_64 => .{ .X86_64 = .RELATIVE }, |
| 992 | }; | 1045 | }; |
| 993 | } | 1046 | } |
| 994 | pub fn jumpSlot(elf: *Elf) MachineRelocType { | 1047 | pub fn jumpSlot(elf: *const Elf) MachineRelocType { |
| 995 | return switch (elf.ehdrField(.machine)) { | 1048 | return switch (elf.ehdrMachine()) { |
| 996 | else => unreachable, | | |
| 997 | .AARCH64 => .{ .AARCH64 = .JUMP_SLOT }, | 1049 | .AARCH64 => .{ .AARCH64 = .JUMP_SLOT }, |
| 998 | .LOONGARCH => .{ .LOONGARCH = .JUMP_SLOT }, | 1050 | .LOONGARCH => .{ .LARCH = .JUMP_SLOT }, |
| 999 | .PPC64 => .{ .PPC64 = .JMP_SLOT }, | 1051 | .PPC64 => .{ .PPC64 = .JMP_SLOT }, |
| 1000 | .RISCV => .{ .RISCV = .JUMP_SLOT }, | 1052 | .RISCV => .{ .RISCV = .JUMP_SLOT }, |
| 1001 | .SPARCV9 => .{ .SPARC = .JMP_SLOT }, | 1053 | .SPARCV9 => .{ .SPARC = .JMP_SLOT }, |
| 1002 | .X86_64 => .{ .X86_64 = .JUMP_SLOT }, | 1054 | .X86_64 => .{ .X86_64 = .JUMP_SLOT }, |
| 1003 | }; | 1055 | }; |
| 1004 | } | 1056 | } |
| 1005 | pub fn globDat(elf: *Elf) MachineRelocType { | 1057 | pub fn globDat(elf: *const Elf) MachineRelocType { |
| 1006 | return switch (elf.ehdrField(.machine)) { | 1058 | return switch (elf.ehdrMachine()) { |
| 1007 | else => unreachable, | | |
| 1008 | .AARCH64 => .{ .AARCH64 = .GLOB_DAT }, | 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 | .PPC64 => .{ .PPC64 = .GLOB_DAT }, | 1061 | .PPC64 => .{ .PPC64 = .GLOB_DAT }, |
| 1011 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, | 1062 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1012 | .SPARCV9 => .{ .SPARC = .GLOB_DAT }, | 1063 | .SPARCV9 => .{ .SPARC = .GLOB_DAT }, |
| 1013 | .X86_64 => .{ .X86_64 = .GLOB_DAT }, | 1064 | .X86_64 => .{ .X86_64 = .GLOB_DAT }, |
| 1014 | }; | 1065 | }; |
| 1015 | } | 1066 | } |
| 1016 | pub fn dtpOffAddr(elf: *Elf) MachineRelocType { | 1067 | pub fn dtpMod(elf: *const Elf) MachineRelocType { |
| 1017 | return switch (elf.ehdrField(.machine)) { | 1068 | return switch (elf.ehdrMachine()) { |
| 1018 | else => unreachable, | 1069 | .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPMOD else .P32_TLS_DTPMOD }, |
| 1019 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, | 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 | .PPC64 => .{ .PPC64 = .DTPREL64 }, | 1081 | .PPC64 => .{ .PPC64 = .DTPREL64 }, |
| 1021 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, | 1082 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, |
| 1022 | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPOFF64 else .TLS_DTPOFF32 }, | 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 { | 1087 | pub fn tpOff(elf: *const Elf) MachineRelocType { |
| 1027 | return switch (elf.ehdrField(.machine)) { | 1088 | return switch (elf.ehdrMachine()) { |
| 1028 | else => unreachable, | 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 | .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .ABS64 else .P32_ABS32 }, | 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 | .PPC64 => .{ .PPC64 = .ADDR64 }, | 1101 | .PPC64 => .{ .PPC64 = .ADDR64 }, |
| 1032 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, | 1102 | .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1033 | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" }, | 1103 | .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1034 | .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" }, | 1104 | .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" }, |
| 1035 | }; | 1105 | }; |
| 1036 | } | 1106 | } |
| 1037 | pub fn sizeAddr(elf: *Elf) MachineRelocType { | 1107 | pub fn size32(elf: *const Elf) ?MachineRelocType { |
| 1038 | return switch (elf.ehdrField(.machine)) { | 1108 | return switch (elf.ehdrMachine()) { |
| 1039 | else => unreachable, | 1109 | .AARCH64, |
| 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, | | |
| 1050 | .LOONGARCH, | 1110 | .LOONGARCH, |
| 1051 | .PPC64, | 1111 | .PPC64, |
| 1052 | .RISCV, | 1112 | .RISCV, |
| 1053 | .X86_64, | 1113 | => null, |
| 1054 | => |machine| @unionInit(MachineRelocType, @tagName(machine), @enumFromInt(int)), | 1114 | |
| | 1115 | .SPARCV9 => .{ .SPARC = .SIZE32 }, |
| | 1116 | .X86_64 => .{ .X86_64 = .SIZE32 }, |
| 1055 | }; | 1117 | }; |
| 1056 | } | 1118 | } |
| 1057 | pub fn unwrap(rt: MachineRelocType, elf: *Elf) u32 { | 1119 | pub fn size64(elf: *const Elf) ?MachineRelocType { |
| 1058 | return switch (elf.ehdrField(.machine)) { | 1120 | return switch (elf.ehdrMachine()) { |
| 1059 | else => unreachable, | 1121 | .AARCH64, |
| 1060 | .SPARCV9 => @intFromEnum(rt.SPARC), | | |
| 1061 | inline .AARCH64, | | |
| 1062 | .LOONGARCH, | 1122 | .LOONGARCH, |
| 1063 | .PPC64, | 1123 | .PPC64, |
| 1064 | .RISCV, | 1124 | .RISCV, |
| 1065 | .X86_64, | 1125 | => null, |
| 1066 | => |machine| @intFromEnum(@field(rt, @tagName(machine))), | 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,6 +1165,8 @@ const SymbolReloc = struct { |
| 1082 | /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`. | 1165 | /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`. |
| 1083 | addend: i64, | 1166 | addend: i64, |
| 1084 | /// Specifies how to apply the relocation. | 1167 | /// Specifies how to apply the relocation. |
| | 1168 | /// |
| | 1169 | /// When emitting a relocatable, this field is `undefined`. |
| 1085 | type: SymbolReloc.Type, | 1170 | type: SymbolReloc.Type, |
| 1086 | /// Forms a linked list of all symbol relocations with the same `target`. This list exists so | 1171 | /// Forms a linked list of all symbol relocations with the same `target`. This list exists so |
| 1087 | /// that all relocations targeting a particular symbol can be re-applied if that symbol moves. | 1172 | /// that all relocations targeting a particular symbol can be re-applied if that symbol moves. |
| ... | @@ -1100,6 +1185,7 @@ const SymbolReloc = struct { | ... | @@ -1100,6 +1185,7 @@ const SymbolReloc = struct { |
| 1100 | /// relocation entry. The entry will be removed if we discover a definition which allows us to | 1185 | /// relocation entry. The entry will be removed if we discover a definition which allows us to |
| 1101 | /// statically resolve the relocation. | 1186 | /// statically resolve the relocation. |
| 1102 | rela_index: Section.RelaIndex.Optional, | 1187 | rela_index: Section.RelaIndex.Optional, |
| | 1188 | result: enum(u8) { ok, overflowed, misaligned }, |
| 1103 | | 1189 | |
| 1104 | /// Determines the section in which this relocation will be placed if it is outstanding. | 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,8 +1196,7 @@ const SymbolReloc = struct { |
| 1110 | /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt` | 1196 | /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt` |
| 1111 | /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries. | 1197 | /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries. |
| 1112 | fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index { | 1198 | fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index { |
| 1113 | const shndx = switch (elf.ehdrField(.type)) { | 1199 | const shndx = switch (elf.ehdrType()) { |
| 1114 | .NONE, .CORE, _ => unreachable, | | |
| 1115 | .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx, | 1200 | .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx, |
| 1116 | .EXEC, .DYN => elf.shndx.rela_dyn, | 1201 | .EXEC, .DYN => elf.shndx.rela_dyn, |
| 1117 | }; | 1202 | }; |
| ... | @@ -1128,151 +1213,439 @@ const SymbolReloc = struct { | ... | @@ -1128,151 +1213,439 @@ const SymbolReloc = struct { |
| 1128 | } | 1213 | } |
| 1129 | }; | 1214 | }; |
| 1130 | | 1215 | |
| 1131 | const Type = enum { | 1216 | /// Instead of using the ELF relocation enums, we have our own internal representation for |
| 1132 | /// This input relocation is being directly forwarded to an `ElfN.Rela` entry in the output | 1217 | /// relocation types. This representation is more compact (requiring only 16 bits), and allows |
| 1133 | /// file. `rela_index` is guaranteed to be populated. The ELF relocation type is available | 1218 | /// sharing a lot of relocation handling between multiple relocs and target architectures. |
| 1134 | /// in the `ElfN.Rela` entry. | 1219 | /// |
| 1135 | /// | 1220 | /// A relocation type can be "simple" or "special". |
| 1136 | /// If we are emitting a relocatable (`ET_REL`), all symbol relocs use this type (since we | 1221 | /// |
| 1137 | /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type. | 1222 | /// "Simple" relocations are designed to cover the majority of cases. They can represent most |
| 1138 | write_rela, | 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. | 1249 | /// Otherwise, the `.simple` field is used. |
| 1143 | dsorel64, | 1250 | action: packed union { |
| 1144 | /// Address relative to the DSO base. Like `.abs32` but does not emit `R_*_RELATIVE` relocs. | 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. | 1257 | /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the |
| 1147 | dsorel32, | 1258 | /// relocation---whether we care about the target symbol's absolute address, its PC-relative |
| 1148 | | 1259 | /// address, its PLT entry, etc. |
| 1149 | abs8, | 1260 | const Target = enum(u3) { |
| 1150 | abs16, | 1261 | /// This is a "special" relocation whose specific type is in the `action.special` field. |
| 1151 | abs32, | 1262 | special, |
| 1152 | abs32s, | 1263 | |
| 1153 | abs64, | 1264 | /// Absolute value of the target symbol. |
| 1154 | rel8, | 1265 | abs, |
| 1155 | rel16, | 1266 | /// Offset from the relocation itself to the target symbol ("PC-relative"). |
| 1156 | rel32, | 1267 | rel, |
| 1157 | rel64, | 1268 | /// Address of the target symbol's PLT entry. |
| 1158 | pltabs32, | 1269 | /// |
| 1159 | pltabs64, | 1270 | /// If the target symbol does not have a PLT entry, equivalent to `.abs`. |
| 1160 | pltrel32, | 1271 | pltabs, |
| 1161 | pltrel64, | 1272 | /// Offset from the relocation itself to the target symbol's PLT entry ("PC-relative"). |
| 1162 | dtpoff32, | 1273 | /// |
| 1163 | dtpoff64, | 1274 | /// If the target symbol does not have a PLT entry, equivalent to `.rel`. |
| 1164 | tpoff32, | 1275 | pltrel, |
| 1165 | tpoff64, | 1276 | /// Offset of the target TLS symbol from the base of this DSO's own TLS region. |
| 1166 | size32, | 1277 | dtpoff, |
| 1167 | size64, | 1278 | /// Offset of the target TLS symbol from the raw thread pointer. |
| 1168 | | 1279 | tpoff, |
| 1169 | larch_abs32_lo12, | 1280 | /// Size of the target symbol. |
| 1170 | larch_rel32_hi20, | 1281 | size, |
| 1171 | larch_rel64_lo20, | 1282 | }; |
| 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, | | |
| 1199 | | 1283 | |
| 1200 | .larch_tpoff32_lo12, | 1284 | /// For a "simple" relocation, after the initial value is computed according to `Target`, a |
| 1201 | .larch_tpoff32_hi20, | 1285 | /// `Simple` value communicates how to shift, truncate, and store that value into memory. |
| 1202 | .larch_tpoff64_lo20, | 1286 | const Simple = packed struct(u13) { |
| 1203 | .larch_tpoff64_hi12, | 1287 | /// The field being written to, represented as a sequence of bits in a backing integer |
| 1204 | => true, | 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, | 1339 | /// After the relocation value is shifted (see `shift`), it is truncated to the size of |
| 1207 | .sparc_le_lox10, | 1340 | /// the bit field (see `dest`). This field specifies whether the linker will check for, |
| 1208 | => true, | 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, | 1353 | /// The relocation value (computed based on the `Target`) gets shifted to the right by |
| 1211 | }; | 1354 | /// this amount. By default, the shifted-out bits can be anything, but tags ending in |
| 1212 | } | 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 { | 1367 | /// Given a value (computed based on the `Target`), applies the shift and truncation |
| 1215 | return switch (elf.identClass()) { | 1368 | /// operations specified by `s`, then writes the result to the start of `dest_slice` as |
| 1216 | .NONE, _ => unreachable, | 1369 | /// specified by `s.dest`. |
| 1217 | .@"32" => switch (t) { | 1370 | fn write( |
| 1218 | .abs32, | 1371 | s: Simple, |
| 1219 | .pltabs32, | 1372 | val: u64, |
| 1220 | => true, | 1373 | dest_slice: []u8, |
| 1221 | else => false, | 1374 | target_endian: std.lang.Endian, |
| 1222 | }, | 1375 | ) error{ RelocationMisaligned, RelocationOverflow }!void { |
| 1223 | .@"64" => switch (t) { | 1376 | const shift: u6, const shift_exact: bool = switch (s.shift) { |
| 1224 | .abs64, | 1377 | .@"0" => .{ 0, false }, |
| 1225 | .pltabs64, | 1378 | .@"2_exact" => .{ 2, true }, |
| 1226 | => true, | 1379 | .@"10" => .{ 10, true }, |
| 1227 | else => false, | 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 { | 1621 | fn apply(reloc: *SymbolReloc, elf: *Elf) void { |
| 1234 | assert(elf.ehdrField(.type) != .REL); | 1622 | assert(elf.ehdrType() != .REL); |
| 1235 | assert(reloc.node != .none); | 1623 | assert(reloc.node != .none); |
| 1236 | | | |
| 1237 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { | 1624 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 1238 | // There's no point applying the relocation now, because it will be re-applied by | 1625 | // There's no point applying the relocation now, because it will be re-applied by |
| 1239 | // `flushMoved` at some point anyway. | 1626 | // `flushMoved` at some point anyway. |
| 1240 | return; | 1627 | return; |
| 1241 | } | 1628 | } |
| 1242 | | 1629 | switch (reloc.result) { |
| 1243 | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { | 1630 | .ok => {}, |
| 1244 | .static => unreachable, | 1631 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1245 | .dynamic => return, // the relocation happens at runtime | 1632 | .misaligned => elf.misaligned_reloc_count -= 1, |
| 1246 | .static_relative => { | 1633 | } |
| 1247 | // We have emitted an R_*_RELATIVE relocation to help lower an abs32/abs64 reloc. | 1634 | if (reloc.applyInner(elf)) { |
| 1248 | // This is a simplified version of the general relocation handling logic, where we | 1635 | @branchHint(.likely); |
| 1249 | // know we're using '.abs64' or '.abs32' (matching the ELF ident class). | 1636 | reloc.result = .ok; |
| 1250 | const value = type: switch (reloc.type) { | 1637 | } else |err| switch (err) { |
| 1251 | .abs32, | 1638 | error.RelocationOverflow => { |
| 1252 | .abs64, | 1639 | reloc.result = .overflowed; |
| 1253 | => reloc.target.value(elf) +% @as(u64, @bitCast(reloc.addend)), | 1640 | elf.overflowed_reloc_count += 1; |
| 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; | | |
| 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 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { | 1649 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 1277 | .file => unreachable, | 1650 | .file => unreachable, |
| 1278 | .ehdr => unreachable, | 1651 | .ehdr => unreachable, |
| ... | @@ -1289,349 +1662,75 @@ const SymbolReloc = struct { | ... | @@ -1289,349 +1662,75 @@ const SymbolReloc = struct { |
| 1289 | }; | 1662 | }; |
| 1290 | const dest_vaddr = node_vaddr + reloc.offset; | 1663 | const dest_vaddr = node_vaddr + reloc.offset; |
| 1291 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; | 1664 | const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 1292 | const target_endian = elf.targetEndian(); | 1665 | |
| 1293 | const sym_value: u64 = reloc.target.value(elf); | 1666 | const addend: u64 = @bitCast(reloc.addend); |
| 1294 | const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) { | 1667 | const target_val: u64 = type: switch (reloc.type.target) { |
| 1295 | inline else => |target_sym| elf.targetLoad(&target_sym.size), | 1668 | .abs => reloc.target.value(elf) +% addend, |
| 1296 | }; | 1669 | .rel => reloc.target.value(elf) +% addend -% dest_vaddr, |
| 1297 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); | 1670 | .pltabs => { |
| 1298 | type: switch (reloc.type) { | 1671 | const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .abs; |
| 1299 | .write_rela => unreachable, | 1672 | break :type plt_entry_addr +% addend; |
| 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 | ); | | |
| 1362 | }, | 1673 | }, |
| 1363 | .pltabs32 => { | 1674 | .pltrel => { |
| 1364 | const plt_index = switch (reloc.target.unwrap()) { | 1675 | const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .rel; |
| 1365 | .local => continue :type .abs32, | 1676 | break :type plt_entry_addr +% addend -% dest_vaddr; |
| 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 | ); | | |
| 1383 | }, | 1677 | }, |
| 1384 | .pltrel64 => { | 1678 | .dtpoff => reloc.target.value(elf) +% addend, |
| 1385 | const plt_index = switch (reloc.target.unwrap()) { | 1679 | .tpoff => switch (elf.targetTlsVariant()) { |
| 1386 | .local => continue :type .rel64, | 1680 | .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend, |
| 1387 | .global => |name| elf.plt.getIndex(name) orelse continue :type .rel64, | 1681 | .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend, |
| 1388 | }; | 1682 | .II => { |
| 1389 | if (elf.pltEntryIsDead(plt_index)) continue :type .rel64; | 1683 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 1390 | const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { | 1684 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 1391 | else => |machine| @panic(@tagName(machine)), | 1685 | inline else => |phdr| tls_size: { |
| 1392 | .SPARCV9 => .{ elf.shndx.plt, 4, 32 }, | 1686 | assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS); |
| 1393 | .X86_64 => .{ elf.shndx.plt_sec, 0, 16 }, | 1687 | break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz); |
| 1394 | }; | 1688 | }, |
| 1395 | const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size; | 1689 | }; |
| 1396 | std.mem.writeInt( | 1690 | break :type reloc.target.value(elf) +% addend -% tls_size; |
| 1397 | i64, | 1691 | }, |
| 1398 | dest_slice[0..8], | | |
| 1399 | @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr), | | |
| 1400 | target_endian, | | |
| 1401 | ); | | |
| 1402 | }, | 1692 | }, |
| 1403 | .pltrel32 => { | 1693 | .size => switch (elf.symPtr(reloc.target.index(elf))) { |
| 1404 | const plt_index = switch (reloc.target.unwrap()) { | 1694 | inline else => |sym| elf.targetLoad(&sym.size), |
| 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 | ); | | |
| 1423 | }, | 1695 | }, |
| 1424 | .size64 => std.mem.writeInt( | 1696 | .special => return reloc.type.action.special.applyInner( |
| 1425 | u64, | 1697 | elf, |
| 1426 | dest_slice[0..8], | 1698 | reloc.target, |
| 1427 | sym_size +% @as(u64, @bitCast(reloc.addend)), | 1699 | addend, |
| 1428 | target_endian, | 1700 | dest_vaddr, |
| 1429 | ), | 1701 | dest_slice, |
| 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, | | |
| 1447 | ), | 1702 | ), |
| 1448 | .tpoff64 => { | 1703 | }; |
| 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 | }, | | |
| 1478 | | 1704 | |
| 1479 | .larch_abs32_lo12 => { | 1705 | // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast |
| 1480 | assert(elf.ehdrField(.machine) == .LOONGARCH); | 1706 | // is required, meaning we can handle it now and return early. |
| 1481 | link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value)); | 1707 | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { |
| 1482 | }, | 1708 | .static => unreachable, |
| 1483 | .larch_rel32_hi20 => { | 1709 | .dynamic => return, // the relocation happens at runtime |
| 1484 | assert(elf.ehdrField(.machine) == .LOONGARCH); | 1710 | .static_relative => { |
| 1485 | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr)); | 1711 | // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address |
| 1486 | }, | 1712 | // relocation. The value computed above is valid, but instead of writing it to the |
| 1487 | .larch_rel64_lo20 => { | 1713 | // destination slice, we actually want to write it to the runtime relocation entry. |
| 1488 | assert(elf.ehdrField(.machine) == .LOONGARCH); | 1714 | switch (elf.identClass()) { |
| 1489 | link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr)); | 1715 | .NONE, _ => unreachable, |
| 1490 | }, | 1716 | .@"32" => assert(reloc.type.action.simple.dest == .@"32"), |
| 1491 | .larch_rel64_hi12 => { | 1717 | .@"64" => assert(reloc.type.action.simple.dest == .@"64"), |
| 1492 | assert(elf.ehdrField(.machine) == .LOONGARCH); | 1718 | } |
| 1493 | link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr)); | 1719 | assert(reloc.type.action.simple.cast == .unsigned); |
| 1494 | }, | 1720 | assert(reloc.type.action.simple.shift == .@"0"); |
| 1495 | // TODO: handle bad alignment and overflow gracefully | 1721 | elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, target_val); |
| 1496 | .larch_branch_rel18 => { | 1722 | return; |
| 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)); | | |
| 1539 | }, | 1723 | }, |
| | 1724 | }; |
| 1540 | | 1725 | |
| 1541 | .sparc_wdisp30 => { | 1726 | try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); |
| 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 | } | | |
| 1628 | } | 1727 | } |
| 1629 | | 1728 | |
| 1630 | fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void { | 1729 | fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void { |
| 1631 | assert(index.get(elf) == reloc); | 1730 | assert(index.get(elf) == reloc); |
| 1632 | | 1731 | |
| 1633 | reloc.deleteOutputRel(elf); | 1732 | reloc.deleteOutputRel(elf); |
| 1634 | if (reloc.type.dependsOnTlsSize()) { | 1733 | if (reloc.type.dependsOnTlsSize(elf)) { |
| 1635 | assert(elf.tls_size_symbol_relocs.swapRemove(index)); | 1734 | assert(elf.tls_size_symbol_relocs.swapRemove(index)); |
| 1636 | } | 1735 | } |
| 1637 | | 1736 | |
| ... | @@ -1647,6 +1746,11 @@ const SymbolReloc = struct { | ... | @@ -1647,6 +1746,11 @@ const SymbolReloc = struct { |
| 1647 | .none => {}, | 1746 | .none => {}, |
| 1648 | else => |next| next.get(elf).prev = reloc.prev, | 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 | reloc.* = undefined; | 1755 | reloc.* = undefined; |
| 1652 | } | 1756 | } |
| ... | @@ -1656,8 +1760,7 @@ const SymbolReloc = struct { | ... | @@ -1656,8 +1760,7 @@ const SymbolReloc = struct { |
| 1656 | fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void { | 1760 | fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void { |
| 1657 | const rela_index = reloc.rela_index.unwrap() orelse return; | 1761 | const rela_index = reloc.rela_index.unwrap() orelse return; |
| 1658 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); | 1762 | reloc.relaSection(elf).relaDeleteOne(elf, rela_index); |
| 1659 | switch (elf.ehdrField(.type)) { | 1763 | switch (elf.ehdrType()) { |
| 1660 | .NONE, .CORE, _ => unreachable, | | |
| 1661 | .REL => {}, | 1764 | .REL => {}, |
| 1662 | .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) { | 1765 | .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) { |
| 1663 | .no => unreachable, // there *was* a dynamic relocation! | 1766 | .no => unreachable, // there *was* a dynamic relocation! |
| ... | @@ -1715,37 +1818,26 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void { | ... | @@ -1715,37 +1818,26 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void { |
| 1715 | try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); | 1818 | try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); |
| 1716 | | 1819 | |
| 1717 | try elf.plt.ensureUnusedCapacity(gpa, len); | 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)) { | 1823 | const plt = elf.targetPltInfo(); |
| 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); | | |
| 1726 | | 1824 | |
| 1727 | // Ensure the `.got.plt` section's node is big enough | 1825 | // Ensure the `.plt` section's node is big enough: |
| 1728 | const got_plt_need_size: usize = elf.targetPtrSize() * (3 + need_plt_capacity); | 1826 | { |
| 1729 | try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size); | 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 | 1831 | // If there is a `.got.plt` section, ensure its node is big enough |
| 1732 | const plt_sec_need_size: usize = 16 * need_plt_capacity; | 1832 | if (plt.got_plt) |got_plt| { |
| 1733 | try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size); | 1833 | const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count); |
| 1734 | }, | 1834 | try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, need_size); |
| 1735 | .LOONGARCH => { | 1835 | } |
| 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); | | |
| 1739 | | 1836 | |
| 1740 | // Ensure the `.got.plt` section's node is big enough | 1837 | // If there is a `.plt.sec` section, ensure its node is big enough |
| 1741 | const got_plt_need_size: usize = elf.targetPtrSize() * (2 + need_plt_capacity); | 1838 | if (plt.plt_sec) |plt_sec| { |
| 1742 | try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size); | 1839 | const need_size: usize = plt_sec.entry_size * need_plt_count; |
| 1743 | }, | 1840 | try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, need_size); |
| 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 | }, | | |
| 1749 | } | 1841 | } |
| 1750 | } | 1842 | } |
| 1751 | /// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at | 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,7 +1900,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L |
| 1808 | const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name)); | 1900 | const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name)); |
| 1809 | elf.globalByName(global_name).?.symtab_index = new_index; | 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 | // This symbol's index is changing, so queue an update of relocs targeting it. | 1904 | // This symbol's index is changing, so queue an update of relocs targeting it. |
| 1813 | elf.changed_symtab_index.putAssumeCapacity(global_name, {}); | 1905 | elf.changed_symtab_index.putAssumeCapacity(global_name, {}); |
| 1814 | } | 1906 | } |
| ... | @@ -1972,7 +2064,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -1972,7 +2064,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 1972 | }; | 2064 | }; |
| 1973 | | 2065 | |
| 1974 | const force_local_bind: bool = switch (opts.visibility) { | 2066 | const force_local_bind: bool = switch (opts.visibility) { |
| 1975 | .HIDDEN, .INTERNAL => elf.ehdrField(.type) != .REL, | 2067 | .HIDDEN, .INTERNAL => elf.ehdrType() != .REL, |
| 1976 | .PROTECTED, .DEFAULT => false, | 2068 | .PROTECTED, .DEFAULT => false, |
| 1977 | }; | 2069 | }; |
| 1978 | | 2070 | |
| ... | @@ -2071,7 +2163,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -2071,7 +2163,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 2071 | } | 2163 | } |
| 2072 | | 2164 | |
| 2073 | switch (@"type") { | 2165 | switch (@"type") { |
| 2074 | .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and | 2166 | .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and |
| 2075 | elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic) | 2167 | elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic) |
| 2076 | { | 2168 | { |
| 2077 | // This STT_FUNC symbol might be defined externally, so it needs a PLT entry. | 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,7 +2332,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi |
| 2240 | // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are | 2332 | // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are |
| 2241 | // putting the global in this state for the first time---let's call it "demoting" the global to | 2333 | // putting the global in this state for the first time---let's call it "demoting" the global to |
| 2242 | // STB_LOCAL---we need to update its bind in the symtab. | 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 | switch (elf.symPtr(global_ptr.symtab_index)) { | 2336 | switch (elf.symPtr(global_ptr.symtab_index)) { |
| 2245 | inline else => |sym, class| { | 2337 | inline else => |sym, class| { |
| 2246 | const old_info = elf.targetLoad(&sym.info); | 2338 | const old_info = elf.targetLoad(&sym.info); |
| ... | @@ -2274,7 +2366,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi | ... | @@ -2274,7 +2366,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi |
| 2274 | /// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF | 2366 | /// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF |
| 2275 | /// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols. | 2367 | /// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols. |
| 2276 | fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { | 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 | switch (elf.shdrPtr(.symtab)) { | 2370 | switch (elf.shdrPtr(.symtab)) { |
| 2279 | inline else => |shdr, class| { | 2371 | inline else => |shdr, class| { |
| 2280 | // `shdr.info` stores the index of the first global symbol. We are going to swap the | 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,240 +2384,64 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 2292 | // The demoted global was not the first global in the symtab, so we need to swap it | 2384 | // The demoted global was not the first global in the symtab, so we need to swap it |
| 2293 | // to its new location. | 2385 | // to its new location. |
| 2294 | | 2386 | |
| 2295 | const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class)); | 2387 | const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class)); |
| 2296 | const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class)); | 2388 | 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 | }; | | |
| 2478 | | 2389 | |
| 2479 | // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry | 2390 | const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name)); |
| 2480 | const got_plt_ni = elf.shndx.got_plt.get(elf).ni; | 2391 | assert(elf.globalByName(this_name).? == global_ptr); |
| 2481 | switch (elf.shdrPtr(elf.shndx.got_plt)) { | 2392 | |
| 2482 | inline else => |shdr, class| { | 2393 | const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name)); |
| 2483 | assert(elf.targetLoad(&shdr.size) == got_plt_offset); | 2394 | const other_global_ptr = elf.globalByName(other_name).?; |
| 2484 | elf.targetStore(&shdr.size, got_plt_offset + @sizeOf(class.ElfN().Addr)); | 2395 | assert(other_global_ptr.symtab_index == dest_index); |
| 2485 | std.mem.writeInt( | 2396 | |
| 2486 | class.ElfN().Addr, | 2397 | // First swap the symtab entries... |
| 2487 | got_plt_ni.slice(&elf.mf)[got_plt_offset..][0..@sizeOf(class.ElfN().Addr)], | 2398 | std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr); |
| 2488 | @intCast(plt_addr), | 2399 | // ...then the `elf.symtab` metadata... |
| 2489 | target_endian, | 2400 | std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf)); |
| 2490 | ); | 2401 | // ...then update the `elf.globals` tracking. |
| 2491 | }, | 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 | 2406 | // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact, |
| 2495 | const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset; | 2407 | // we'll move another symbol into its place just like we did above. |
| 2496 | // TODO: handle overflow gracefully | 2408 | if (global_ptr.dynsym_index != 0) { |
| 2497 | link.loongarch.writeJ20(plt_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr)); | 2409 | const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class)); |
| 2498 | link.loongarch.writeK12(plt_slice[4..8], @truncate(got_plt_abs)); | 2410 | |
| 2499 | }, | 2411 | const ent_size = @sizeOf(class.ElfN().Sym); |
| 2500 | .SPARCV9 => { | 2412 | assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size); |
| 2501 | // add a .PLT entry, writing the template | 2413 | |
| 2502 | const plt_ni = elf.shndx.plt.get(elf).ni; | 2414 | // We're going to decrease the size of `.dynsym`, thereby removing its last index. |
| 2503 | switch (elf.shdrPtr(elf.shndx.plt)) { | 2415 | const old_size = elf.targetLoad(&dynsym_shdr.size); |
| 2504 | inline else => |shdr| { | 2416 | const new_size = old_size - ent_size; |
| 2505 | assert(elf.targetLoad(&shdr.size) == got_plt_offset); | 2417 | const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size)); |
| 2506 | elf.targetStore(&shdr.size, got_plt_offset + 32); | 2418 | |
| 2507 | const plt_slice: []u32 = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[got_plt_offset..][0..32])); | 2419 | const free_dynsym_index = global_ptr.dynsym_index; |
| 2508 | // sethi (. - .plt[0]), %g1 | 2420 | global_ptr.dynsym_index = 0; |
| 2509 | // ba,a %xcc, .plt[1] | 2421 | |
| 2510 | // nop | 2422 | if (free_dynsym_index != remove_dynsym_index) { |
| 2511 | // nop | 2423 | // The demoted global wasn't the last entry, so move whatever entry we just |
| 2512 | // nop | 2424 | // truncated out of dynsym into its place. |
| 2513 | // nop | 2425 | |
| 2514 | // nop | 2426 | const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class)); |
| 2515 | // nop | 2427 | const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class)); |
| 2516 | @memcpy(plt_slice, &([2]u32{ | 2428 | |
| 2517 | // TODO: handle overflow gracefully | 2429 | const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name)); |
| 2518 | @bitCast(link.sparc.reloc.Imm22{ | 2430 | const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf)); |
| 2519 | .imm22 = @truncate(got_plt_offset), | 2431 | const moved_global_ptr = elf.globalByName(moved_name).?; |
| 2520 | .b22_31 = 0b0000000011, | 2432 | |
| 2521 | }), | 2433 | dest_dynsym_ptr.* = src_dynsym_ptr.*; |
| 2522 | @bitCast(link.sparc.reloc.Disp19{ | 2434 | |
| 2523 | .disp19 = @truncate((got_plt_offset + 4 - 32) >> 2), | 2435 | assert(moved_global_ptr.dynsym_index == remove_dynsym_index); |
| 2524 | .b19_31 = 0b1100001101000, | 2436 | moved_global_ptr.dynsym_index = free_dynsym_index; |
| 2525 | }), | 2437 | |
| 2526 | } ++ @as([6]u32, @splat(0x01000000)))); | 2438 | // Since that symbol's dynsym index has changed, we'll have to update any |
| 2527 | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllElements(u32, plt_slice); | 2439 | // relocation entries targeting it. |
| 2528 | }, | 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,7 +2576,7 @@ const Symbol = struct { |
| 2660 | } | 2576 | } |
| 2661 | | 2577 | |
| 2662 | // Re-apply relocations targeting this symbol | 2578 | // Re-apply relocations targeting this symbol |
| 2663 | if (elf.ehdrField(.type) != .REL) { | 2579 | if (elf.ehdrType() != .REL) { |
| 2664 | sym_id.applyTargetRelocs(elf); | 2580 | sym_id.applyTargetRelocs(elf); |
| 2665 | } | 2581 | } |
| 2666 | | 2582 | |
| ... | @@ -2678,7 +2594,7 @@ const Symbol = struct { | ... | @@ -2678,7 +2594,7 @@ const Symbol = struct { |
| 2678 | } | 2594 | } |
| 2679 | | 2595 | |
| 2680 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { | 2596 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 2681 | assert(elf.ehdrField(.type) != .REL); | 2597 | assert(elf.ehdrType() != .REL); |
| 2682 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; | 2598 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 2683 | while (ri != .none) { | 2599 | while (ri != .none) { |
| 2684 | const reloc = ri.get(elf); | 2600 | const reloc = ri.get(elf); |
| ... | @@ -2693,7 +2609,7 @@ const Symbol = struct { | ... | @@ -2693,7 +2609,7 @@ const Symbol = struct { |
| 2693 | /// | 2609 | /// |
| 2694 | /// Asserts we are creating a DSO. | 2610 | /// Asserts we are creating a DSO. |
| 2695 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { | 2611 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 2696 | assert(elf.ehdrField(.type) != .REL); | 2612 | assert(elf.ehdrType() != .REL); |
| 2697 | assert(elf.shndx.dynamic != .UNDEF); | 2613 | assert(elf.shndx.dynamic != .UNDEF); |
| 2698 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; | 2614 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 2699 | while (ri != .none) { | 2615 | while (ri != .none) { |
| ... | @@ -2713,7 +2629,20 @@ const Symbol = struct { | ... | @@ -2713,7 +2629,20 @@ const Symbol = struct { |
| 2713 | const reloc = ri.get(elf); | 2629 | const reloc = ri.get(elf); |
| 2714 | ri = reloc.next; | 2630 | ri = reloc.next; |
| 2715 | assert(reloc.target == sym_id); | 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 | switch (elf.nodeWantsDsoRelocation(reloc.node)) { | 2646 | switch (elf.nodeWantsDsoRelocation(reloc.node)) { |
| 2718 | .no => continue, | 2647 | .no => continue, |
| 2719 | .yes_textrel => elf.textrel_count += 1, | 2648 | .yes_textrel => elf.textrel_count += 1, |
| ... | @@ -2781,8 +2710,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { | ... | @@ -2781,8 +2710,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| 2781 | } { | 2710 | } { |
| 2782 | const comp = elf.base.comp; | 2711 | const comp = elf.base.comp; |
| 2783 | | 2712 | |
| 2784 | const runtime_load_addr = switch (elf.ehdrField(.type)) { | 2713 | const runtime_load_addr = switch (elf.ehdrType()) { |
| 2785 | .NONE, .CORE, _ => unreachable, | | |
| 2786 | .REL => unreachable, | 2714 | .REL => unreachable, |
| 2787 | .DYN => true, | 2715 | .DYN => true, |
| 2788 | .EXEC => false, | 2716 | .EXEC => false, |
| ... | @@ -2933,10 +2861,10 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id { | ... | @@ -2933,10 +2861,10 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id { |
| 2933 | .size = 0, | 2861 | .size = 0, |
| 2934 | .type = opts.type, | 2862 | .type = opts.type, |
| 2935 | .bind = switch (opts.linkage) { | 2863 | .bind = switch (opts.linkage) { |
| 2936 | .internal => @panic("TODO internal extern symbol"), | | |
| 2937 | .strong => .strong, | 2864 | .strong => .strong, |
| 2938 | .weak => .weak, | 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 | .visibility = switch (opts.visibility) { | 2869 | .visibility = switch (opts.visibility) { |
| 2942 | .default => .DEFAULT, | 2870 | .default => .DEFAULT, |
| ... | @@ -2965,6 +2893,9 @@ pub fn addReloc( | ... | @@ -2965,6 +2893,9 @@ pub fn addReloc( |
| 2965 | }; | 2893 | }; |
| 2966 | elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) { | 2894 | elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) { |
| 2967 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), | 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 | else => |e| return e, | 2899 | else => |e| return e, |
| 2969 | }; | 2900 | }; |
| 2970 | } | 2901 | } |
| ... | @@ -3149,23 +3080,6 @@ const StringTable = struct { | ... | @@ -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 | pub fn open( | 3083 | pub fn open( |
| 3170 | arena: std.mem.Allocator, | 3084 | arena: std.mem.Allocator, |
| 3171 | comp: *Compilation, | 3085 | comp: *Compilation, |
| ... | @@ -3212,7 +3126,7 @@ fn create( | ... | @@ -3212,7 +3126,7 @@ fn create( |
| 3212 | .amdpal => .AMDGPU_PAL, | 3126 | .amdpal => .AMDGPU_PAL, |
| 3213 | .mesa3d => .AMDGPU_MESA3D, | 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 | .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC, | 3130 | .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC, |
| 3217 | .Lib => switch (comp.config.link_mode) { | 3131 | .Lib => switch (comp.config.link_mode) { |
| 3218 | .static => .REL, | 3132 | .static => .REL, |
| ... | @@ -3220,7 +3134,9 @@ fn create( | ... | @@ -3220,7 +3134,9 @@ fn create( |
| 3220 | }, | 3134 | }, |
| 3221 | .Obj => .REL, | 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 | const maybe_interp = switch (comp.config.link_mode) { | 3140 | const maybe_interp = switch (comp.config.link_mode) { |
| 3225 | .static => null, | 3141 | .static => null, |
| 3226 | .dynamic => switch (comp.config.output_mode) { | 3142 | .dynamic => switch (comp.config.output_mode) { |
| ... | @@ -3276,6 +3192,12 @@ fn create( | ... | @@ -3276,6 +3192,12 @@ fn create( |
| 3276 | .fini_array = .UNDEF, | 3192 | .fini_array = .UNDEF, |
| 3277 | .preinit_array = .UNDEF, | 3193 | .preinit_array = .UNDEF, |
| 3278 | }, | 3194 | }, |
| | 3195 | .dynamic = .{ |
| | 3196 | .flags = 0, |
| | 3197 | .flags_1 = 0, |
| | 3198 | .rpath = .empty, |
| | 3199 | .soname = .empty, |
| | 3200 | }, |
| 3279 | .symtab = .empty, | 3201 | .symtab = .empty, |
| 3280 | .globals = .{ | 3202 | .globals = .{ |
| 3281 | .strong_def = .empty, | 3203 | .strong_def = .empty, |
| ... | @@ -3310,10 +3232,12 @@ fn create( | ... | @@ -3310,10 +3232,12 @@ fn create( |
| 3310 | .tls_size_symbol_relocs = .empty, | 3232 | .tls_size_symbol_relocs = .empty, |
| 3311 | .section_by_name = .empty, | 3233 | .section_by_name = .empty, |
| 3312 | .changed_symtab_index = .empty, | 3234 | .changed_symtab_index = .empty, |
| | 3235 | .textrel_count = 0, |
| | 3236 | .overflowed_reloc_count = 0, |
| | 3237 | .misaligned_reloc_count = 0, |
| 3313 | .const_prog_node = .none, | 3238 | .const_prog_node = .none, |
| 3314 | .synth_prog_node = .none, | 3239 | .synth_prog_node = .none, |
| 3315 | .input_prog_node = .none, | 3240 | .input_prog_node = .none, |
| 3316 | .textrel_count = 0, | | |
| 3317 | }; | 3241 | }; |
| 3318 | errdefer elf.deinit(); | 3242 | errdefer elf.deinit(); |
| 3319 | | 3243 | |
| ... | @@ -3362,14 +3286,14 @@ fn initHeaders( | ... | @@ -3362,14 +3286,14 @@ fn initHeaders( |
| 3362 | class: std.elf.CLASS, | 3286 | class: std.elf.CLASS, |
| 3363 | data: std.elf.DATA, | 3287 | data: std.elf.DATA, |
| 3364 | osabi: std.elf.OSABI, | 3288 | osabi: std.elf.OSABI, |
| 3365 | @"type": std.elf.ET, | 3289 | @"type": EhdrType, |
| 3366 | machine: std.elf.EM, | 3290 | machine: EhdrMachine, |
| 3367 | maybe_interp: ?[]const u8, | 3291 | maybe_interp: ?[]const u8, |
| 3368 | ) !void { | 3292 | ) Error!void { |
| 3369 | const comp = elf.base.comp; | 3293 | const comp = elf.base.comp; |
| 3370 | const gpa = comp.gpa; | 3294 | const gpa = comp.gpa; |
| | 3295 | |
| 3371 | const have_dynamic_section = switch (@"type") { | 3296 | const have_dynamic_section = switch (@"type") { |
| 3372 | .NONE, .CORE, _ => unreachable, | | |
| 3373 | .REL => false, | 3297 | .REL => false, |
| 3374 | .EXEC => comp.config.link_mode == .dynamic, | 3298 | .EXEC => comp.config.link_mode == .dynamic, |
| 3375 | .DYN => true, | 3299 | .DYN => true, |
| ... | @@ -3380,13 +3304,7 @@ fn initHeaders( | ... | @@ -3380,13 +3304,7 @@ fn initHeaders( |
| 3380 | .@"64" => .@"8", | 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 = | 3307 | const plt: PltInfo = .fromMachine(machine); |
| 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 | }; | | |
| 3390 | | 3308 | |
| 3391 | const shnum: u32 = shnum: { | 3309 | const shnum: u32 = shnum: { |
| 3392 | var shnum: u32 = 1; // reserved ("null") shdr | 3310 | var shnum: u32 = 1; // reserved ("null") shdr |
| ... | @@ -3408,9 +3326,9 @@ fn initHeaders( | ... | @@ -3408,9 +3326,9 @@ fn initHeaders( |
| 3408 | } | 3326 | } |
| 3409 | if (@"type" != .REL) { | 3327 | if (@"type" != .REL) { |
| 3410 | shnum += 1; // .got | 3328 | shnum += 1; // .got |
| 3411 | shnum += @intFromBool(got_plt); // .got.plt | 3329 | shnum += @intFromBool(plt.got_plt != null); // .got.plt |
| 3412 | shnum += 1; // .plt | 3330 | shnum += 1; // .plt |
| 3413 | shnum += @intFromBool(plt_sec); // .plt_sec | 3331 | shnum += @intFromBool(plt.plt_sec != null); // .plt_sec |
| 3414 | } | 3332 | } |
| 3415 | break :shnum shnum; | 3333 | break :shnum shnum; |
| 3416 | }; | 3334 | }; |
| ... | @@ -3427,7 +3345,6 @@ fn initHeaders( | ... | @@ -3427,7 +3345,6 @@ fn initHeaders( |
| 3427 | gnu_stack: u32, | 3345 | gnu_stack: u32, |
| 3428 | }, const phnum: u32 = ph: { | 3346 | }, const phnum: u32 = ph: { |
| 3429 | switch (@"type") { | 3347 | switch (@"type") { |
| 3430 | .NONE, .CORE, _ => unreachable, | | |
| 3431 | .REL => break :ph .{ undefined, 0 }, | 3348 | .REL => break :ph .{ undefined, 0 }, |
| 3432 | .EXEC, .DYN => {}, | 3349 | .EXEC, .DYN => {}, |
| 3433 | } | 3350 | } |
| ... | @@ -3483,9 +3400,9 @@ fn initHeaders( | ... | @@ -3483,9 +3400,9 @@ fn initHeaders( |
| 3483 | try elf.symtab.ensureTotalCapacity(gpa, 1); | 3400 | try elf.symtab.ensureTotalCapacity(gpa, 1); |
| 3484 | elf.nodes.appendAssumeCapacity(.file); | 3401 | elf.nodes.appendAssumeCapacity(.file); |
| 3485 | | 3402 | |
| 3486 | switch (class) { | 3403 | const entsize: struct { ph: u32, sh: u32 } = switch (class) { |
| 3487 | .NONE, _ => unreachable, | 3404 | .NONE, _ => unreachable, |
| 3488 | inline else => |ct_class| { | 3405 | inline else => |ct_class| entsize: { |
| 3489 | const ElfN = ct_class.ElfN(); | 3406 | const ElfN = ct_class.ElfN(); |
| 3490 | assert(elf.ni.ehdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.file, .{ | 3407 | assert(elf.ni.ehdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.file, .{ |
| 3491 | .size = @sizeOf(ElfN.Ehdr), | 3408 | .size = @sizeOf(ElfN.Ehdr), |
| ... | @@ -3502,42 +3419,35 @@ fn initHeaders( | ... | @@ -3502,42 +3419,35 @@ fn initHeaders( |
| 3502 | .osabi = osabi, | 3419 | .osabi = osabi, |
| 3503 | .abiversion = 0, | 3420 | .abiversion = 0, |
| 3504 | }; | 3421 | }; |
| 3505 | ehdr.type = @"type"; | 3422 | ehdr.type = @"type".toElf(); |
| 3506 | ehdr.machine = machine; | 3423 | ehdr.machine = machine.toElf(); |
| 3507 | ehdr.version = 1; | 3424 | ehdr.version = 1; |
| 3508 | ehdr.entry = 0; | 3425 | ehdr.entry = 0; |
| 3509 | ehdr.phoff = 0; | 3426 | ehdr.phoff = 0; |
| 3510 | ehdr.shoff = 0; | 3427 | ehdr.shoff = 0; |
| 3511 | ehdr.flags = switch (machine) { | 3428 | ehdr.flags = switch (machine) { |
| 3512 | .LOONGARCH => e_flags: { | 3429 | .LOONGARCH => .{ .loongarch = .{ |
| 3513 | const target_cpu = &elf.base.comp.getTarget().cpu; | 3430 | .base_abi_modifier = mod: { |
| 3514 | const e_flags: std.elf.loongarch.EFlags = .{ | 3431 | const cpu = comp.getTarget().cpu; |
| 3515 | .base_abi_modifier = if (target_cpu.has(.loongarch, .d)) | 3432 | if (cpu.has(.loongarch, .d)) break :mod .d; |
| 3516 | .d | 3433 | if (cpu.has(.loongarch, .f)) break :mod .f; |
| 3517 | else if (target_cpu.has(.loongarch, .f)) | 3434 | break :mod .s; |
| 3518 | .f | 3435 | }, |
| 3519 | else | 3436 | .abi_extension = .base, |
| 3520 | .s, | 3437 | .abi_version = 1, |
| 3521 | .abi_extension = .base, | 3438 | } }, |
| 3522 | .abi_version = 1, | 3439 | .SPARCV9 => .{ .sparc = .{ |
| 3523 | }; | 3440 | .mm = .rmo, |
| 3524 | break :e_flags @bitCast(e_flags); | 3441 | .ext = .{ |
| 3525 | }, | 3442 | .@"32plus" = false, |
| 3526 | .SPARCV9 => e_flags: { | 3443 | .sun_us1 = false, |
| 3527 | const e_flags: std.elf.sparc.EFlags = .{ | 3444 | .hal_r1 = false, |
| 3528 | .mm = .rmo, | 3445 | .sun_us3 = false, |
| 3529 | .ext = .{ | 3446 | .le_data = false, |
| 3530 | .@"32plus" = false, | 3447 | }, |
| 3531 | .sun_us1 = false, | 3448 | } }, |
| 3532 | .hal_r1 = false, | 3449 | .X86_64 => .{ .int = 0 }, |
| 3533 | .sun_us3 = false, | 3450 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 3534 | .le_data = false, | | |
| 3535 | }, | | |
| 3536 | }; | | |
| 3537 | break :e_flags @bitCast(e_flags); | | |
| 3538 | }, | | |
| 3539 | .X86_64 => 0, | | |
| 3540 | else => @panic(@tagName(machine)), | | |
| 3541 | }; | 3451 | }; |
| 3542 | ehdr.ehsize = @sizeOf(ElfN.Ehdr); | 3452 | ehdr.ehsize = @sizeOf(ElfN.Ehdr); |
| 3543 | ehdr.phentsize = @sizeOf(ElfN.Phdr); | 3453 | ehdr.phentsize = @sizeOf(ElfN.Phdr); |
| ... | @@ -3546,11 +3456,13 @@ fn initHeaders( | ... | @@ -3546,11 +3456,13 @@ fn initHeaders( |
| 3546 | ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection` | 3456 | ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection` |
| 3547 | ehdr.shstrndx = std.elf.SHN_UNDEF; | 3457 | ehdr.shstrndx = std.elf.SHN_UNDEF; |
| 3548 | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); | 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 | assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ | 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 | .alignment = elf.mf.flags.block_size, | 3466 | .alignment = elf.mf.flags.block_size, |
| 3555 | .moved = true, | 3467 | .moved = true, |
| 3556 | .resized = true, | 3468 | .resized = true, |
| ... | @@ -3558,28 +3470,24 @@ fn initHeaders( | ... | @@ -3558,28 +3470,24 @@ fn initHeaders( |
| 3558 | elf.nodes.appendAssumeCapacity(.shdr); | 3470 | elf.nodes.appendAssumeCapacity(.shdr); |
| 3559 | | 3471 | |
| 3560 | const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) { | 3472 | const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) { |
| 3561 | .BPF, | 3473 | .AARCH64 => 0x10000, |
| 3562 | .SPARCV9, | 3474 | .LOONGARCH => 0x4000, |
| 3563 | => 0x100000, | 3475 | .PPC64 => 0x10000, |
| 3564 | .AARCH64, | 3476 | .RISCV => 0x1000, |
| 3565 | .AMDGPU, | 3477 | .SPARCV9 => 0x100000, |
| 3566 | .QDSP6, | 3478 | .X86_64 => 0x1000, |
| 3567 | .MIPS, | 3479 | |
| 3568 | .PPC, | 3480 | //.@"68K" => 0x2000, |
| 3569 | .PPC64, | 3481 | //.AMDGPU => 0x10000, |
| 3570 | .SPARC, | 3482 | //.ARC_COMPACT2 => 0x2000, |
| 3571 | .SPARC32PLUS, | 3483 | //.AVR => 0x1, |
| 3572 | => 0x10000, | 3484 | //.BPF => 0x100000, |
| 3573 | .LOONGARCH, | 3485 | //.MIPS => 0x10000, |
| 3574 | => 0x4000, | 3486 | //.MSP430 => 0x4, |
| 3575 | .ARC_COMPACT2, | 3487 | //.PPC => 0x10000, |
| 3576 | .@"68K", | 3488 | //.QDSP6 => 0x10000, |
| 3577 | => 0x2000, | 3489 | //.SPARC => 0x10000, |
| 3578 | .MSP430, | 3490 | //.SPARC32PLUS => 0x10000, |
| 3579 | => 0x4, | | |
| 3580 | .AVR, | | |
| 3581 | => 0x1, | | |
| 3582 | else => 0x1000, | | |
| 3583 | }); | 3491 | }); |
| 3584 | | 3492 | |
| 3585 | var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: { | 3493 | var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: { |
| ... | @@ -3592,7 +3500,7 @@ fn initHeaders( | ... | @@ -3592,7 +3500,7 @@ fn initHeaders( |
| 3592 | elf.phdrs.items[phndx.rodata] = elf.ni.rodata; | 3500 | elf.phdrs.items[phndx.rodata] = elf.ni.rodata; |
| 3593 | | 3501 | |
| 3594 | assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ | 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 | .alignment = addr_align, | 3504 | .alignment = addr_align, |
| 3597 | .moved = true, | 3505 | .moved = true, |
| 3598 | .resized = true, | 3506 | .resized = true, |
| ... | @@ -3627,16 +3535,16 @@ fn initHeaders( | ... | @@ -3627,16 +3535,16 @@ fn initHeaders( |
| 3627 | | 3535 | |
| 3628 | elf.phdrs.items[phndx.gnu_stack] = .none; | 3536 | elf.phdrs.items[phndx.gnu_stack] = .none; |
| 3629 | | 3537 | |
| 3630 | break :ph_vaddr switch (elf.ehdrField(.type)) { | 3538 | break :ph_vaddr switch (elf.ehdrType()) { |
| 3631 | .NONE, .CORE, _ => unreachable, | | |
| 3632 | .REL, .DYN => 0, | 3539 | .REL, .DYN => 0, |
| 3633 | .EXEC => switch (machine) { | 3540 | .EXEC => switch (machine) { |
| 3634 | .@"386" => 0x400000, | 3541 | .AARCH64, |
| 3635 | .AARCH64, .X86_64 => 0x200000, | 3542 | => 0x200000, |
| 3636 | .PPC, .PPC64 => 0x10000000, | 3543 | .LOONGARCH => 0x10000, |
| 3637 | .S390 => 0x1000000, | 3544 | .PPC64 => 0x10000000, |
| | 3545 | .RISCV => 0x10000, |
| 3638 | .SPARCV9 => 0x100000, | 3546 | .SPARCV9 => 0x100000, |
| 3639 | else => 0x10000, | 3547 | .X86_64 => 0x200000, |
| 3640 | }, | 3548 | }, |
| 3641 | }; | 3549 | }; |
| 3642 | } else undefined; | 3550 | } else undefined; |
| ... | @@ -3870,28 +3778,21 @@ fn initHeaders( | ... | @@ -3870,28 +3778,21 @@ fn initHeaders( |
| 3870 | .type = .PROGBITS, | 3778 | .type = .PROGBITS, |
| 3871 | // Reserve space for the reserved words, populated later. | 3779 | // Reserve space for the reserved words, populated later. |
| 3872 | .size = switch (machine) { | 3780 | .size = switch (machine) { |
| 3873 | else => @panic(@tagName(machine)), | 3781 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 3874 | .X86_64 => 3 * elf.targetPtrSize(), | 3782 | .X86_64 => 3 * elf.targetPtrSize(), |
| 3875 | .LOONGARCH, | 3783 | .LOONGARCH, .SPARCV9 => elf.targetPtrSize(), |
| 3876 | .SPARCV9, | | |
| 3877 | => elf.targetPtrSize(), | | |
| 3878 | }, | 3784 | }, |
| 3879 | .flags = .{ .WRITE = true, .ALLOC = true }, | 3785 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 3880 | .addralign = addr_align, | 3786 | .addralign = addr_align, |
| 3881 | .entsize = @intCast(addr_align.toByteUnits()), | 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 | if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data, | 3790 | if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data, |
| 3885 | .{ | 3791 | .{ |
| 3886 | .name = ".got.plt", | 3792 | .name = ".got.plt", |
| 3887 | .type = .PROGBITS, | 3793 | .type = .PROGBITS, |
| 3888 | .flags = .{ .WRITE = true, .ALLOC = true }, | 3794 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 3889 | .size = switch (machine) { | 3795 | .size = got_plt.header_entries * elf.targetPtrSize(), |
| 3890 | else => @panic(@tagName(machine)), | | |
| 3891 | .@"386" => 3 * 4, | | |
| 3892 | .X86_64 => 3 * 8, | | |
| 3893 | .LOONGARCH => 2 * elf.targetPtrSize(), | | |
| 3894 | }, | | |
| 3895 | .addralign = addr_align, | 3796 | .addralign = addr_align, |
| 3896 | .entsize = @intCast(addr_align.toByteUnits()), | 3797 | .entsize = @intCast(addr_align.toByteUnits()), |
| 3897 | }, | 3798 | }, |
| ... | @@ -3902,19 +3803,16 @@ fn initHeaders( | ... | @@ -3902,19 +3803,16 @@ fn initHeaders( |
| 3902 | .flags = .{ | 3803 | .flags = .{ |
| 3903 | .ALLOC = true, | 3804 | .ALLOC = true, |
| 3904 | .EXECINSTR = true, | 3805 | .EXECINSTR = true, |
| 3905 | .WRITE = switch (machine) { | 3806 | .WRITE = plt.got_plt == null, |
| 3906 | .SPARCV9 => true, | | |
| 3907 | else => false, | | |
| 3908 | }, | | |
| 3909 | }, | 3807 | }, |
| 3910 | .size = init_plt_size, | 3808 | .size = plt.entry_size * plt.header_entries, |
| 3911 | .addralign = plt_align, | 3809 | .addralign = plt.@"align", |
| 3912 | .node_align = elf.mf.flags.block_size, | 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 | .name = ".plt.sec", | 3813 | .name = ".plt.sec", |
| 3916 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, | 3814 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, |
| 3917 | .addralign = plt_align, | 3815 | .addralign = plt.@"align", |
| 3918 | .node_align = elf.mf.flags.block_size, | 3816 | .node_align = elf.mf.flags.block_size, |
| 3919 | }); | 3817 | }); |
| 3920 | if (maybe_interp) |interp| { | 3818 | if (maybe_interp) |interp| { |
| ... | @@ -4005,7 +3903,7 @@ fn initHeaders( | ... | @@ -4005,7 +3903,7 @@ fn initHeaders( |
| 4005 | .type = .RELA, | 3903 | .type = .RELA, |
| 4006 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, | 3904 | .flags = .{ .ALLOC = true, .INFO_LINK = true }, |
| 4007 | .link = elf.shndx.dynsym.toSection().?, | 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 | .addralign = addr_align, | 3907 | .addralign = addr_align, |
| 4010 | .entsize = rela_size, | 3908 | .entsize = rela_size, |
| 4011 | .node_align = elf.mf.flags.block_size, | 3909 | .node_align = elf.mf.flags.block_size, |
| ... | @@ -4019,7 +3917,7 @@ fn initHeaders( | ... | @@ -4019,7 +3917,7 @@ fn initHeaders( |
| 4019 | .node_align = addr_align, | 3917 | .node_align = addr_align, |
| 4020 | }); | 3918 | }); |
| 4021 | switch (machine) { | 3919 | switch (machine) { |
| 4022 | else => @panic(@tagName(machine)), | 3920 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 4023 | .X86_64 => { | 3921 | .X86_64 => { |
| 4024 | const plt_ni = elf.shndx.plt.get(elf).ni; | 3922 | const plt_ni = elf.shndx.plt.get(elf).ni; |
| 4025 | const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi); | 3923 | const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi); |
| ... | @@ -4035,14 +3933,14 @@ fn initHeaders( | ... | @@ -4035,14 +3933,14 @@ fn initHeaders( |
| 4035 | 2, | 3933 | 2, |
| 4036 | got_plt_sym, | 3934 | got_plt_sym, |
| 4037 | 8 * 1 - 4, | 3935 | 8 * 1 - 4, |
| 4038 | .rel32, | 3936 | .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }), |
| 4039 | ); | 3937 | ); |
| 4040 | try elf.addSymbolRelocAssumeCapacity( | 3938 | try elf.addSymbolRelocAssumeCapacity( |
| 4041 | plt_ni, | 3939 | plt_ni, |
| 4042 | 8, | 3940 | 8, |
| 4043 | got_plt_sym, | 3941 | got_plt_sym, |
| 4044 | 8 * 2 - 4, | 3942 | 8 * 2 - 4, |
| 4045 | .rel32, | 3943 | .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }), |
| 4046 | ); | 3944 | ); |
| 4047 | }, | 3945 | }, |
| 4048 | .LOONGARCH => { | 3946 | .LOONGARCH => { |
| ... | @@ -4073,9 +3971,24 @@ fn initHeaders( | ... | @@ -4073,9 +3971,24 @@ fn initHeaders( |
| 4073 | }); | 3971 | }); |
| 4074 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 3972 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 4075 | try elf.ensureUnusedRelocCapacity(plt_ni, 3); | 3973 | try elf.ensureUnusedRelocCapacity(plt_ni, 3); |
| 4076 | try elf.addSymbolRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .larch_rel32_hi20); | 3974 | elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LARCH = .PCALA_HI20 }) catch |err| switch (err) { |
| 4077 | try elf.addSymbolRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .larch_abs32_lo12); | 3975 | error.UnknownRelocation => unreachable, |
| 4078 | try elf.addSymbolRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .larch_abs32_lo12); | 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 | .SPARCV9 => {}, | 3993 | .SPARCV9 => {}, |
| 4081 | } | 3994 | } |
| ... | @@ -4092,7 +4005,7 @@ fn initHeaders( | ... | @@ -4092,7 +4005,7 @@ fn initHeaders( |
| 4092 | | 4005 | |
| 4093 | // Populate reserved GOT words. | 4006 | // Populate reserved GOT words. |
| 4094 | switch (machine) { | 4007 | switch (machine) { |
| 4095 | else => @panic(@tagName(machine)), | 4008 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 4096 | .X86_64 => { | 4009 | .X86_64 => { |
| 4097 | try elf.got.ensureUnusedCapacity(gpa, 3); | 4010 | try elf.got.ensureUnusedCapacity(gpa, 3); |
| 4098 | elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { | 4011 | elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { |
| ... | @@ -4102,9 +4015,7 @@ fn initHeaders( | ... | @@ -4102,9 +4015,7 @@ fn initHeaders( |
| 4102 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none); | 4015 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none); |
| 4103 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none); | 4016 | elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none); |
| 4104 | }, | 4017 | }, |
| 4105 | .LOONGARCH, | 4018 | .LOONGARCH, .SPARCV9 => { |
| 4106 | .SPARCV9, | | |
| 4107 | => { | | |
| 4108 | try elf.got.ensureUnusedCapacity(gpa, 1); | 4019 | try elf.got.ensureUnusedCapacity(gpa, 1); |
| 4109 | elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { | 4020 | elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { |
| 4110 | true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, | 4021 | true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, |
| ... | @@ -4153,8 +4064,17 @@ fn initHeaders( | ... | @@ -4153,8 +4064,17 @@ fn initHeaders( |
| 4153 | .node = elf.shndx.got.get(elf).ni, | 4064 | .node = elf.shndx.got.get(elf).ni, |
| 4154 | .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"), | 4065 | .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"), |
| 4155 | .value = switch (machine) { | 4066 | .value = switch (machine) { |
| 4156 | .QDSP6, .@"386", .X86_64 => elf.shndx.got_plt.vaddr(elf), | 4067 | .AARCH64, |
| 4157 | else => elf.shndx.got.vaddr(elf), | 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 | .size = 0, | 4079 | .size = 0, |
| 4160 | .type = .NOTYPE, | 4080 | .type = .NOTYPE, |
| ... | @@ -4267,6 +4187,29 @@ fn initHeaders( | ... | @@ -4267,6 +4187,29 @@ fn initHeaders( |
| 4267 | const shndx: Section.Index = @enumFromInt(shndx_raw); | 4187 | const shndx: Section.Index = @enumFromInt(shndx_raw); |
| 4268 | elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); | 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 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { | 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,7 +4322,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 4379 | if (ptr.* != .none) { | 4322 | if (ptr.* != .none) { |
| 4380 | for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| { | 4323 | for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| { |
| 4381 | if (reloc.node != ni) break; | 4324 | if (reloc.node != ni) break; |
| 4382 | reloc.* = .deleted; | 4325 | reloc.delete(elf); |
| 4383 | } | 4326 | } |
| 4384 | } | 4327 | } |
| 4385 | ptr.* = @enumFromInt(elf.got_relocs.items.len); | 4328 | ptr.* = @enumFromInt(elf.got_relocs.items.len); |
| ... | @@ -4418,15 +4361,124 @@ fn flushMovedNodeRelocs( | ... | @@ -4418,15 +4361,124 @@ fn flushMovedNodeRelocs( |
| 4418 | fn identClass(elf: *const Elf) std.elf.CLASS { | 4361 | fn identClass(elf: *const Elf) std.elf.CLASS { |
| 4419 | return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]); | 4362 | return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]); |
| 4420 | } | 4363 | } |
| 4421 | fn identData(elf: *const Elf) std.elf.DATA { | 4364 | |
| 4422 | return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]); | 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 | return elf.identClass().size(); | 4415 | return elf.identClass().size(); |
| 4427 | } | 4416 | } |
| 4428 | fn targetEndian(elf: *const Elf) std.lang.Endian { | 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 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { | 4483 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 4432 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; | 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,7 +4487,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi |
| 4435 | return switch (@typeInfo(Child)) { | 4487 | return switch (@typeInfo(Child)) { |
| 4436 | else => @compileError(@typeName(Child)), | 4488 | else => @compileError(@typeName(Child)), |
| 4437 | .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()), | 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 | .@"struct" => |@"struct"| @bitCast( | 4491 | .@"struct" => |@"struct"| @bitCast( |
| 4440 | elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))), | 4492 | elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))), |
| 4441 | ), | 4493 | ), |
| ... | @@ -4475,14 +4527,6 @@ fn ehdrPtr(elf: *Elf) EhdrPtr { | ... | @@ -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 | const PhdrSlice = union(std.elf.CLASS) { | 4531 | const PhdrSlice = union(std.elf.CLASS) { |
| 4488 | NONE: noreturn, | 4532 | NONE: noreturn, |
| ... | @@ -4490,7 +4534,7 @@ const PhdrSlice = union(std.elf.CLASS) { | ... | @@ -4490,7 +4534,7 @@ const PhdrSlice = union(std.elf.CLASS) { |
| 4490 | @"64": []std.elf.Elf64.Phdr, | 4534 | @"64": []std.elf.Elf64.Phdr, |
| 4491 | }; | 4535 | }; |
| 4492 | fn phdrSlice(elf: *Elf) PhdrSlice { | 4536 | fn phdrSlice(elf: *Elf) PhdrSlice { |
| 4493 | assert(elf.ehdrField(.type) != .REL); | 4537 | assert(elf.ehdrType() != .REL); |
| 4494 | const slice = elf.ni.phdr.slice(&elf.mf); | 4538 | const slice = elf.ni.phdr.slice(&elf.mf); |
| 4495 | return switch (elf.identClass()) { | 4539 | return switch (elf.identClass()) { |
| 4496 | .NONE, _ => unreachable, | 4540 | .NONE, _ => unreachable, |
| ... | @@ -4587,8 +4631,7 @@ fn mapInputSection(elf: *Elf, opts: struct { | ... | @@ -4587,8 +4631,7 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 4587 | return error.StripSection; | 4631 | return error.StripSection; |
| 4588 | } | 4632 | } |
| 4589 | | 4633 | |
| 4590 | const name: []const u8 = switch (elf.ehdrField(.type)) { | 4634 | const name: []const u8 = switch (elf.ehdrType()) { |
| 4591 | .NONE, .CORE, _ => unreachable, | | |
| 4592 | .REL => opts.name, | 4635 | .REL => opts.name, |
| 4593 | .EXEC, .DYN => name: { | 4636 | .EXEC, .DYN => name: { |
| 4594 | if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text"; | 4637 | if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text"; |
| ... | @@ -5050,7 +5093,7 @@ fn loadObject( | ... | @@ -5050,7 +5093,7 @@ fn loadObject( |
| 5050 | const ElfN = class.ElfN(); | 5093 | const ElfN = class.ElfN(); |
| 5051 | const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); | 5094 | const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); |
| 5052 | if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{}); | 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 | return diags.failParse(path, "bad machine", .{}); | 5097 | return diags.failParse(path, "bad machine", .{}); |
| 5055 | if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; | 5098 | if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; |
| 5056 | if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size) | 5099 | if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size) |
| ... | @@ -5383,23 +5426,43 @@ fn loadObject( | ... | @@ -5383,23 +5426,43 @@ fn loadObject( |
| 5383 | ); | 5426 | ); |
| 5384 | const target = symmap.items[rel.info.sym - 1]; | 5427 | const target = symmap.items[rel.info.sym - 1]; |
| 5385 | if (target == Symbol.Id.null) { | 5428 | if (target == Symbol.Id.null) { |
| 5386 | // If this is not an SHF_ALLOC section, then let's let this | 5429 | // If this is not an SHF_ALLOC section, then let's not report |
| 5387 | // slide for now, because it probably doesn't affect the final | 5430 | // this for now, because it probably doesn't affect the final |
| 5388 | // binary's functionality for this section to be a bit broken. | 5431 | // binary's functionality for this section to be a bit broken. |
| 5389 | if (!loc_sec.shdr.flags.shf.ALLOC) continue; | 5432 | if (loc_sec.shdr.flags.shf.ALLOC) { |
| 5390 | return diags.failParse( | 5433 | diags.addParseError( |
| 5391 | path, | 5434 | path, |
| 5392 | "unsupported symbol at index {d} required for relocation", | 5435 | "unsupported symbol at index {d} required for relocation", |
| 5393 | .{rel.info.sym}, | 5436 | .{rel.info.sym}, |
| 5394 | ); | 5437 | ); |
| | 5438 | } |
| | 5439 | continue; |
| 5395 | } | 5440 | } |
| 5396 | try elf.addRelocAssumeCapacity( | 5441 | const rt: MachineRelocType = .wrap(rel.info.type, elf); |
| | 5442 | elf.addRelocAssumeCapacity( |
| 5397 | loc_node, | 5443 | loc_node, |
| 5398 | rel.offset - loc_sec.shdr.addr, | 5444 | rel.offset - loc_sec.shdr.addr, |
| 5399 | target, | 5445 | target, |
| 5400 | rel.addend, | 5446 | rel.addend, |
| 5401 | .wrap(rel.info.type, elf), | 5447 | rt, |
| 5402 | ); | 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,7 +5486,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars |
| 5423 | const ElfN = class.ElfN(); | 5486 | const ElfN = class.ElfN(); |
| 5424 | const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); | 5487 | const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); |
| 5425 | if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{}); | 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 | return diags.failParse(path, "bad machine", .{}); | 5490 | return diags.failParse(path, "bad machine", .{}); |
| 5428 | if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff); | 5491 | if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff); |
| 5429 | // We're going to need to know the alignment of every section later. | 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,222 +5845,143 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5782 | .file_symbol = zcu_file_symbol, | 5845 | .file_symbol = zcu_file_symbol, |
| 5783 | }; | 5846 | }; |
| 5784 | } | 5847 | } |
| | 5848 | } |
| 5785 | | 5849 | |
| 5786 | const got_plt = switch (elf.ehdrField(.machine)) { | 5850 | fn prepareDynamic(elf: *Elf) Error!void { |
| 5787 | .SPARCV9 => false, | 5851 | const comp = elf.base.comp; |
| 5788 | else => true, | 5852 | |
| 5789 | }; | 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 | .NONE, _ => unreachable, | 5884 | .NONE, _ => unreachable, |
| 5793 | inline else => |ct_class| { | 5885 | inline else => |class| { |
| 5794 | const ElfN = ct_class.ElfN(); | 5886 | const ElfN = class.ElfN(); |
| 5795 | const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0; | 5887 | |
| 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 | }; | | |
| 5806 | // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries. | 5888 | // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries. |
| 5807 | const use_plt = !(comp.config.output_mode == .Exe and | 5889 | const use_plt = !(comp.config.output_mode == .Exe and |
| 5808 | comp.config.link_mode == .static and | 5890 | comp.config.link_mode == .static and |
| 5809 | comp.config.pie); | 5891 | comp.config.pie); |
| 5810 | const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: { | 5892 | |
| 5811 | break :str try elf.string(.dynstr, soname_slice); | 5893 | const dynamic_size = elf.targetLoad(&@field(elf.shdrPtr(elf.shndx.dynamic), @tagName(class)).size); |
| 5812 | } else null; | 5894 | const dynamic_slice = elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)]; |
| 5813 | const needed_len = elf.needed.count(); | 5895 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(dynamic_slice)); |
| 5814 | const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) + | 5896 | |
| 5815 | @intFromBool(rpath != .empty) + | 5897 | var dynamic_index: usize = 0; |
| 5816 | @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) + | 5898 | |
| 5817 | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + | 5899 | for ( |
| 5818 | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + | 5900 | dynamic_entries[dynamic_index..][0..elf.needed.count()], |
| 5819 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + | 5901 | elf.needed.keys(), |
| 5820 | @as(usize, @intFromBool(use_plt)) * 4 + | 5902 | ) |*dynamic_entry, needed| { |
| 5821 | @intFromBool(comp.config.output_mode == .Exe) + 8; | 5903 | dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) }; |
| 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), | | |
| 5827 | } | 5904 | } |
| | 5905 | dynamic_index += elf.needed.count(); |
| 5828 | | 5906 | |
| 5829 | const dynamic_indices: struct { | 5907 | if (elf.dynamic.soname != .empty) { |
| 5830 | init_array: ?usize, | 5908 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(elf.dynamic.soname) }; |
| 5831 | fini_array: ?usize, | 5909 | dynamic_index += 1; |
| 5832 | preinit_array: ?usize, | 5910 | } |
| 5833 | jmprel: ?usize, | 5911 | if (elf.dynamic.rpath != .empty) { |
| 5834 | pltgot: ?usize, | 5912 | dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(elf.dynamic.rpath) }; |
| 5835 | } = indices: { | 5913 | dynamic_index += 1; |
| 5836 | const sec_dynamic = dynamic_ni.slice(&elf.mf); | 5914 | } |
| 5837 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); | 5915 | if (elf.dynamic.flags != 0) { |
| 5838 | errdefer comptime unreachable; // don't invalidate `dynamic_entries` | 5916 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, elf.dynamic.flags }; |
| 5839 | var dynamic_index: usize = 0; | 5917 | dynamic_index += 1; |
| 5840 | for ( | 5918 | } |
| 5841 | dynamic_entries[dynamic_index..][0..needed_len], | 5919 | if (elf.dynamic.flags_1 != 0) { |
| 5842 | elf.needed.keys(), | 5920 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, elf.dynamic.flags_1 }; |
| 5843 | ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) }; | 5921 | dynamic_index += 1; |
| 5844 | dynamic_index += needed_len; | 5922 | } |
| 5845 | if (soname) |soname_dynstr| { | 5923 | if (comp.config.output_mode == .Exe) { |
| 5846 | dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(soname_dynstr) }; | 5924 | dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 }; |
| 5847 | dynamic_index += 1; | 5925 | dynamic_index += 1; |
| 5848 | } | 5926 | } |
| 5849 | if (rpath != .empty) { | 5927 | if (elf.textrel_count > 0) { |
| 5850 | dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) }; | 5928 | dynamic_entries[dynamic_index] = .{ std.elf.DT_TEXTREL, 0 }; |
| 5851 | dynamic_index += 1; | 5929 | dynamic_index += 1; |
| 5852 | } | 5930 | } |
| 5853 | if (flags != 0) { | 5931 | if (elf.shndx.init_array != .UNDEF) { |
| 5854 | dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags }; | 5932 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5855 | dynamic_index += 1; | 5933 | .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) }, |
| 5856 | } | 5934 | .{ std.elf.DT_INIT_ARRAYSZ, @intCast(elf.shndx.init_array.size(elf)) }, |
| 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 }, | | |
| 5920 | }; | 5935 | }; |
| 5921 | dynamic_index += 8; | 5936 | dynamic_index += 2; |
| 5922 | assert(dynamic_index == dynamic_len); | 5937 | } |
| 5923 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| | 5938 | if (elf.shndx.fini_array != .UNDEF) { |
| 5924 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); | 5939 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5925 | | 5940 | .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) }, |
| 5926 | break :indices .{ | 5941 | .{ std.elf.DT_FINI_ARRAYSZ, @intCast(elf.shndx.fini_array.size(elf)) }, |
| 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, | | |
| 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) { | 5968 | dynamic_entries[dynamic_index..][0..8].* = .{ |
| 5936 | .NONE, _ => comptime unreachable, | 5969 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, |
| 5937 | .@"32" => .dsorel32, | 5970 | .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) }, |
| 5938 | .@"64" => .dsorel64, | 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); | 5980 | assert(dynamic_index == dynamic_entries.len); |
| 5942 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 8); | 5981 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 5943 | if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity( | 5982 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| 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 | ); | | |
| 5999 | }, | 5983 | }, |
| 6000 | }; | 5984 | } |
| 6001 | } | 5985 | } |
| 6002 | | 5986 | |
| 6003 | fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { | 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,7 +6001,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 6017 | .PROGBITS => assert(opts.size > 0), | 6001 | .PROGBITS => assert(opts.size > 0), |
| 6018 | else => {}, | 6002 | else => {}, |
| 6019 | } | 6003 | } |
| 6020 | if (opts.flags.ALLOC and elf.ehdrField(.type) != .REL) { | 6004 | if (opts.flags.ALLOC and elf.ehdrType() != .REL) { |
| 6021 | assert(elf.getNode(segment_ni) == .segment); | 6005 | assert(elf.getNode(segment_ni) == .segment); |
| 6022 | } | 6006 | } |
| 6023 | const gpa = elf.base.comp.gpa; | 6007 | const gpa = elf.base.comp.gpa; |
| ... | @@ -6054,8 +6038,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { | ... | @@ -6054,8 +6038,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 6054 | }, | 6038 | }, |
| 6055 | }; | 6039 | }; |
| 6056 | try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size); | 6040 | try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size); |
| 6057 | const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrField(.type)) { | 6041 | const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) { |
| 6058 | .NONE, .CORE, _ => unreachable, | | |
| 6059 | .REL => elf.ni.file, | 6042 | .REL => elf.ni.file, |
| 6060 | .EXEC, .DYN => segment_ni, | 6043 | .EXEC, .DYN => segment_ni, |
| 6061 | }, .{ | 6044 | }, .{ |
| ... | @@ -6106,8 +6089,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) | ... | @@ -6106,8 +6089,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 6106 | try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); | 6089 | try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 6107 | try elf.got_relocs.ensureUnusedCapacity(gpa, len); | 6090 | try elf.got_relocs.ensureUnusedCapacity(gpa, len); |
| 6108 | const class = elf.identClass(); | 6091 | const class = elf.identClass(); |
| 6109 | switch (elf.ehdrField(.type)) { | 6092 | switch (elf.ehdrType()) { |
| 6110 | .NONE, .CORE, _ => unreachable, | | |
| 6111 | .REL => { | 6093 | .REL => { |
| 6112 | const shndx = elf.getNodeShndx(node); | 6094 | const shndx = elf.getNodeShndx(node); |
| 6113 | if (shndx.get(elf).rela.shndx == .UNDEF) { | 6095 | if (shndx.get(elf).rela.shndx == .UNDEF) { |
| ... | @@ -6166,10 +6148,9 @@ fn addRelocAssumeCapacity( | ... | @@ -6166,10 +6148,9 @@ fn addRelocAssumeCapacity( |
| 6166 | target: Symbol.Id, | 6148 | target: Symbol.Id, |
| 6167 | addend: i64, | 6149 | addend: i64, |
| 6168 | @"type": MachineRelocType, | 6150 | @"type": MachineRelocType, |
| 6169 | ) Error!void { | 6151 | ) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void { |
| 6170 | assert(node != .none); | 6152 | assert(node != .none); |
| 6171 | switch (elf.ehdrField(.type)) { | 6153 | switch (elf.ehdrType()) { |
| 6172 | .NONE, .CORE, _ => unreachable, | | |
| 6173 | .REL => { | 6154 | .REL => { |
| 6174 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; | 6155 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| 6175 | const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ | 6156 | const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ |
| ... | @@ -6187,161 +6168,113 @@ fn addRelocAssumeCapacity( | ... | @@ -6187,161 +6168,113 @@ fn addRelocAssumeCapacity( |
| 6187 | const target_ptr = target.index(elf).ptr(elf); | 6168 | const target_ptr = target.index(elf).ptr(elf); |
| 6188 | const next = target_ptr.first_target_reloc; | 6169 | const next = target_ptr.first_target_reloc; |
| 6189 | target_ptr.first_target_reloc = ri; | 6170 | target_ptr.first_target_reloc = ri; |
| 6190 | break :next next; | 6171 | break :next next; |
| 6191 | }; | 6172 | }; |
| 6192 | if (next != .none) { | 6173 | if (next != .none) { |
| 6193 | next.get(elf).prev = ri; | 6174 | next.get(elf).prev = ri; |
| 6194 | } | 6175 | } |
| 6195 | elf.symbol_relocs.appendAssumeCapacity(.{ | 6176 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 6196 | .node = node, | 6177 | .node = node, |
| 6197 | .offset = offset, | 6178 | .offset = offset, |
| 6198 | .type = .write_rela, | 6179 | .type = undefined, |
| 6199 | .target = target, | 6180 | .target = target, |
| 6200 | .addend = addend, | 6181 | .addend = addend, |
| 6201 | .next = next, | 6182 | .next = next, |
| 6202 | .prev = .none, | 6183 | .prev = .none, |
| 6203 | .rela_index = rela_index.toOptional(), | 6184 | .rela_index = rela_index.toOptional(), |
| 6204 | }); | 6185 | .result = .ok, |
| 6205 | }, | 6186 | }); |
| 6206 | | 6187 | }, |
| 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 | }, | | |
| 6252 | | 6188 | |
| 6253 | // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the | 6189 | .DYN, .EXEC => switch (elf.ehdrMachine()) { |
| 6254 | // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm | 6190 | .AARCH64 => switch (@"type".AARCH64) { |
| 6255 | // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which | 6191 | .NONE => {}, |
| 6256 | // need to be re-applied whenever the GOT moves. | 6192 | _ => return error.UnknownRelocation, |
| 6257 | .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base | 6193 | else => return error.UnimplementedRelocation, |
| 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), | | |
| 6277 | }, | 6194 | }, |
| 6278 | .LOONGARCH => switch (@"type".LOONGARCH) { | 6195 | .LOONGARCH => rel_type: switch (@"type".LARCH) { |
| 6279 | else => std.debug.panic("TODO: unsupported input relocation, {t}", .{@"type".LOONGARCH}), | 6196 | .NONE => {}, |
| 6280 | _, | 6197 | _ => return error.UnknownRelocation, |
| 6281 | .NONE, | 6198 | |
| 6282 | .COPY, | 6199 | .COPY, |
| 6283 | .JUMP_SLOT, | 6200 | .JUMP_SLOT, |
| 6284 | .RELATIVE, | 6201 | .RELATIVE, |
| 6285 | .IRELATIVE, | 6202 | .IRELATIVE, |
| 6286 | => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".LOONGARCH}), | 6203 | => return error.NonStaticRelocation, |
| 6287 | | 6204 | |
| 6288 | .RELAX => {}, // TODO: relaxation is not yet implemented | 6205 | else => return error.UnimplementedRelocation, |
| 6289 | | 6206 | |
| 6290 | // Relocations targeting a symbol | 6207 | // These relocations signal that certain relaxations are legal, but this linker does |
| 6291 | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64), | 6208 | // not yet implement relaxation, so these are ignored. |
| 6292 | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), | 6209 | .RELAX, .TLS_LE_ADD_R => {}, |
| 6293 | .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64), | 6210 | |
| 6294 | .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), | 6211 | // Relaxable versions of other relocations. Since we don't yet implement relaxation, |
| 6295 | | 6212 | // just use the handling for the non-relaxable versions. |
| 6296 | .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_abs32_lo12), | 6213 | .TLS_LE_LO12_R => continue :rel_type .TLS_LE_LO12, |
| 6297 | .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel32_hi20), | 6214 | .TLS_LE_HI20_R => continue :rel_type .TLS_LE_HI20, |
| 6298 | .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_hi12), | 6215 | |
| 6299 | .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_lo20), | 6216 | // zig fmt: off |
| 6300 | | 6217 | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6301 | .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel18), | 6218 | .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6302 | .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel23), | 6219 | .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 6303 | .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel28), | 6220 | .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 6304 | .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_call_rel38), | 6221 | .ABS_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6305 | | 6222 | .ABS_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })), |
| 6306 | // Relocations targeting a TLS symbol | 6223 | .ABS64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 6307 | .TLS_LE_LO12, .TLS_LE_LO12_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_lo12), | 6224 | .ABS64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 6308 | .TLS_LE_HI20, .TLS_LE_HI20_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_hi20), | 6225 | .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6309 | .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_lo20), | 6226 | .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala_hi20)), |
| 6310 | .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_hi12), | 6227 | .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_lo20)), |
| 6311 | .TLS_LE_ADD_R => {}, // TODO: relaxation is not yet implemented | 6228 | .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_hi12)), |
| 6312 | | 6229 | |
| 6313 | // Relocations targeting a GOT entry | 6230 | .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[25:10]", .cast = .signed, .shift = .@"2_exact" })), |
| 6314 | .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12), | 6231 | .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b21)), |
| 6315 | .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel32_hi20), | 6232 | .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b26)), |
| 6316 | .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_lo20), | 6233 | .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_call36)), |
| 6317 | .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_hi12), | 6234 | |
| 6318 | | 6235 | .TLS_LE_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 6319 | .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12), | 6236 | .TLS_LE_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })), |
| 6320 | .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_hi20), | 6237 | .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 6321 | .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_lo20), | 6238 | .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 6322 | .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_hi12), | 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 | .SPARCV9 => switch (@"type".SPARC) { | 6260 | .SPARCV9 => switch (@"type".SPARC) { |
| 6325 | _, | 6261 | .NONE => {}, |
| 6326 | .NONE, | 6262 | _ => return error.UnknownRelocation, |
| | 6263 | |
| 6327 | .COPY, | 6264 | .COPY, |
| 6328 | .GLOB_DAT, | 6265 | .GLOB_DAT, |
| 6329 | .JMP_SLOT, | 6266 | .JMP_SLOT, |
| 6330 | .RELATIVE, | 6267 | .RELATIVE, |
| 6331 | .IRELATIVE, | 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 | .HI22, | 6272 | .HI22, |
| 6336 | .@"22", | | |
| 6337 | .@"13", | | |
| 6338 | .LO10, | 6273 | .LO10, |
| 6339 | .HIPLT22, | 6274 | .HIPLT22, |
| 6340 | .LOPLT10, | 6275 | .LOPLT10, |
| 6341 | .PCPLT22, | 6276 | .PCPLT22, |
| 6342 | .PCPLT10, | 6277 | .PCPLT10, |
| 6343 | .@"10", | | |
| 6344 | .@"11", | | |
| 6345 | .OLO10, | 6278 | .OLO10, |
| 6346 | .HH22, | 6279 | .HH22, |
| 6347 | .HM10, | 6280 | .HM10, |
| ... | @@ -6351,62 +6284,24 @@ fn addRelocAssumeCapacity( | ... | @@ -6351,62 +6284,24 @@ fn addRelocAssumeCapacity( |
| 6351 | .PC_LM22, | 6284 | .PC_LM22, |
| 6352 | .WDISP16, | 6285 | .WDISP16, |
| 6353 | .WDISP19, | 6286 | .WDISP19, |
| 6354 | .@"7", | | |
| 6355 | .@"5", | | |
| 6356 | .@"6", | | |
| 6357 | .HIX22, | 6287 | .HIX22, |
| 6358 | .LOX10, | 6288 | .LOX10, |
| 6359 | .REGISTER, | 6289 | .REGISTER, |
| 6360 | .TLS_GD_HI22, | | |
| 6361 | .TLS_GD_LO10, | | |
| 6362 | .TLS_IE_HI22, | 6290 | .TLS_IE_HI22, |
| 6363 | .TLS_IE_LO10, | 6291 | .TLS_IE_LO10, |
| 6364 | .TLS_DTPMOD32, | 6292 | .TLS_DTPMOD32, |
| 6365 | .TLS_DTPMOD64, | 6293 | .TLS_DTPMOD64, |
| 6366 | .H34, | 6294 | .H34, |
| 6367 | .WDISP10, | 6295 | .WDISP10, |
| 6368 | => |t| @panic("TODO: " ++ @tagName(t)), | 6296 | => return error.UnimplementedRelocation, |
| 6369 | | 6297 | |
| 6370 | // Relocations targeting a symbol | 6298 | // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though. |
| 6371 | .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs8), | 6299 | .GOTDATA_HIX22 => return error.UnimplementedRelocation, |
| 6372 | .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs16), | 6300 | .GOTDATA_LOX10 => return error.UnimplementedRelocation, |
| 6373 | .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32), | 6301 | |
| 6374 | .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel8), | 6302 | // These relocations signal that certain relaxations are legal, but this linker does |
| 6375 | .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel16), | 6303 | // not yet implement relaxation, so these are ignored. |
| 6376 | .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32), | 6304 | .GOTDATA_OP, |
| 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. | | |
| 6410 | .TLS_GD_ADD, | 6305 | .TLS_GD_ADD, |
| 6411 | .TLS_LDM_ADD, | 6306 | .TLS_LDM_ADD, |
| 6412 | .TLS_LDO_ADD, | 6307 | .TLS_LDO_ADD, |
| ... | @@ -6415,19 +6310,180 @@ fn addRelocAssumeCapacity( | ... | @@ -6415,19 +6310,180 @@ fn addRelocAssumeCapacity( |
| 6415 | .TLS_IE_ADD, | 6310 | .TLS_IE_ADD, |
| 6416 | => {}, | 6311 | => {}, |
| 6417 | | 6312 | |
| 6418 | // Relocations targeting a GOT entry | 6313 | // zig fmt: off |
| 6419 | .GOT10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_10), | 6314 | .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })), |
| 6420 | .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_13), | 6315 | .@"16", .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })), |
| 6421 | .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_22), | 6316 | .@"32", .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 6422 | .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_hi22), | 6317 | .@"64", .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 6423 | .TLS_LDM_LO10 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_lo10), | 6318 | |
| 6424 | // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though. | 6319 | .@"5" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[4:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6425 | .GOTDATA_HIX22 => @panic("TODO: R_SPARC_GOTDATA_HIX22"), | 6320 | .@"6" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[5:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6426 | .GOTDATA_LOX10 => @panic("TODO: R_SPARC_GOTDATA_LOX10"), | 6321 | .@"7" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[6:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6427 | .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_hix22), | 6322 | .@"10" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6428 | .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_lox10), | 6323 | .@"11" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[10:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6429 | // We currently do no relaxation, so nothing to do for this one. | 6324 | .@"13" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })), |
| 6430 | .GOTDATA_OP => {}, | 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,10 +6496,12 @@ fn addSymbolRelocAssumeCapacity( |
| 6440 | addend: i64, | 6496 | addend: i64, |
| 6441 | @"type": SymbolReloc.Type, | 6497 | @"type": SymbolReloc.Type, |
| 6442 | ) Error!void { | 6498 | ) Error!void { |
| 6443 | assert(elf.ehdrField(.type) != .REL); | 6499 | assert(elf.ehdrType() != .REL); |
| 6444 | assert(node != .none); | 6500 | assert(node != .none); |
| 6445 | | 6501 | |
| 6446 | const rela_index: Section.RelaIndex.Optional = r: { | 6502 | const rela_index: Section.RelaIndex.Optional = r: { |
| | 6503 | if (elf.shndx.dynamic == .UNDEF) break :r .none; |
| | 6504 | |
| 6447 | // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to | 6505 | // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to |
| 6448 | // determine the vaddr of `node`. | 6506 | // determine the vaddr of `node`. |
| 6449 | const node_vaddr: u64 = switch (elf.getNode(node)) { | 6507 | const node_vaddr: u64 = switch (elf.getNode(node)) { |
| ... | @@ -6461,162 +6519,51 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -6461,162 +6519,51 @@ fn addSymbolRelocAssumeCapacity( |
| 6461 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), | 6519 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 6462 | }; | 6520 | }; |
| 6463 | | 6521 | |
| 6464 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { | 6522 | // If this is `true`, we will try to create a copy relocation for the target symbol if it is |
| 6465 | else => |machine| @panic(@tagName(machine)), | 6523 | // not locally defined. If the relocation value is always computed from the target symbol's |
| 6466 | .X86_64 => .{ .X86_64 = switch (@"type") { | 6524 | // value (even for an external target symbol), and if the target symbol might be of type |
| 6467 | .write_rela => unreachable, | 6525 | // STT_OBJECT, this should probably be `true`. |
| 6468 | .dsorel64, .dsorel32 => { | 6526 | const try_copy_reloc: bool = switch (@"type".target) { |
| 6469 | assert(target.unwrap() == .local); | 6527 | .rel, .abs => true, |
| 6470 | break :r .none; | 6528 | |
| 6471 | }, | 6529 | .pltrel, |
| 6472 | .abs64 => .@"64", | 6530 | .pltabs, |
| 6473 | .abs32 => .@"32", | 6531 | .dtpoff, |
| 6474 | .abs16 => unreachable, | 6532 | .tpoff, |
| 6475 | .abs8 => unreachable, | 6533 | .size, |
| 6476 | .abs32s => .@"32S", | 6534 | => false, |
| 6477 | .rel64 => .PC64, | 6535 | |
| 6478 | .rel32 => .PC32, | 6536 | .special => switch (@"type".action.special) { |
| 6479 | .rel16 => unreachable, | 6537 | .larch_pcala_hi20, |
| 6480 | .rel8 => unreachable, | 6538 | .larch_pcala64_lo20, |
| 6481 | .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none, | 6539 | .larch_pcala64_hi12, |
| 6482 | .dtpoff64 => .DTPOFF64, | 6540 | => true, |
| 6483 | .dtpoff32 => .DTPOFF32, | 6541 | |
| 6484 | .tpoff64 => .TPOFF64, | 6542 | .larch_b21, |
| 6485 | .tpoff32 => .TPOFF32, | 6543 | .larch_b26, |
| 6486 | .size64 => .SIZE64, | 6544 | .larch_call36, |
| 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, | | |
| 6561 | .sparc_le_hix22, | 6545 | .sparc_le_hix22, |
| 6562 | .sparc_le_lox10, | 6546 | => false, |
| 6563 | => unreachable, | 6547 | }, |
| 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 | } }, | | |
| 6614 | }; | 6548 | }; |
| 6615 | | 6549 | |
| 6616 | class: switch (elf.classifySymbolValue(target)) { | 6550 | classify: switch (elf.classifySymbolValue(target)) { |
| 6617 | .static => break :r .none, | 6551 | .static => break :r .none, |
| 6618 | .static_relative => { | 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 | switch (elf.nodeWantsDsoRelocation(node)) { | 6567 | switch (elf.nodeWantsDsoRelocation(node)) { |
| 6621 | .no => break :r .none, | 6568 | .no => break :r .none, |
| 6622 | .yes => {}, | 6569 | .yes => {}, |
| ... | @@ -6629,30 +6576,47 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -6629,30 +6576,47 @@ fn addSymbolRelocAssumeCapacity( |
| 6629 | .addend = 0, | 6576 | .addend = 0, |
| 6630 | }).toOptional(); | 6577 | }).toOptional(); |
| 6631 | }, | 6578 | }, |
| 6632 | .dynamic => dso_reloc: switch (elf.nodeWantsDsoRelocation(node)) { | 6579 | .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) { |
| 6633 | .no => break :r .none, | 6580 | switch (elf.classifySymbolValue(target)) { |
| 6634 | .yes_textrel => if (try elf.maybeAddCopyRelocation(target.unwrap().global)) { | 6581 | .static => continue :classify .static, |
| 6635 | // We were able to use a copy relocation on this symbol to avoid a text relocation, | 6582 | .static_relative => continue :classify .static_relative, |
| 6636 | // which is apparently considered a good thing despite copy relocations being an | 6583 | .dynamic => unreachable, // we just added a copy relocation |
| 6637 | // abomination. (This is necessary for correctness in some cases, because e.g. a | 6584 | } |
| 6638 | // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to | 6585 | } else { |
| 6639 | // the DSOs being loaded too far apart.) | 6586 | const dynamic_reloc_type: MachineRelocType = switch (@"type".target) { |
| 6640 | switch (elf.classifySymbolValue(target)) { | 6587 | // PLT relocations targeting dynamic symbols actually target that symbol's PLT |
| 6641 | .dynamic => unreachable, // we just added a copy relocation | 6588 | // entry, so we should emit an `R_*_RELATIVE` relocation instead. |
| 6642 | .static => continue :class .static, | 6589 | .pltabs => continue :classify .static_relative, |
| 6643 | .static_relative => continue :class .static_relative, | 6590 | // ...although PC-relative PLT relocations don't even need that! |
| 6644 | } | 6591 | .pltrel => break :r .none, |
| 6645 | } else { | 6592 | // Weird sizes or computations are not supported as runtime relocations. |
| 6646 | // At least for now, our only choice is a text relocation. | 6593 | .special => break :r .none, |
| 6647 | elf.textrel_count += 1; | 6594 | // Relative addresses are not supported as runtime relocations. |
| 6648 | continue :dso_reloc .yes; | 6595 | .rel => break :r .none, |
| 6649 | }, | 6596 | |
| 6650 | .yes => break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ | 6597 | // On the few targets supporting size relocations, they are valid at runtime. |
| 6651 | .type = rela_type, | 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 | .offset = node_vaddr + offset, | 6616 | .offset = node_vaddr + offset, |
| 6653 | .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index, | 6617 | .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index, |
| 6654 | .addend = addend, | 6618 | .addend = addend, |
| 6655 | }).toOptional(), | 6619 | }).toOptional(); |
| 6656 | }, | 6620 | }, |
| 6657 | } | 6621 | } |
| 6658 | }; | 6622 | }; |
| ... | @@ -6673,8 +6637,9 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -6673,8 +6637,9 @@ fn addSymbolRelocAssumeCapacity( |
| 6673 | .next = next, | 6637 | .next = next, |
| 6674 | .prev = .none, | 6638 | .prev = .none, |
| 6675 | .rela_index = rela_index, | 6639 | .rela_index = rela_index, |
| | 6640 | .result = .ok, |
| 6676 | }); | 6641 | }); |
| 6677 | if (@"type".dependsOnTlsSize()) { | 6642 | if (@"type".dependsOnTlsSize(elf)) { |
| 6678 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); | 6643 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 6679 | } | 6644 | } |
| 6680 | | 6645 | |
| ... | @@ -6689,7 +6654,7 @@ fn addGotRelocAssumeCapacity( | ... | @@ -6689,7 +6654,7 @@ fn addGotRelocAssumeCapacity( |
| 6689 | addend: i64, | 6654 | addend: i64, |
| 6690 | @"type": GotReloc.Type, | 6655 | @"type": GotReloc.Type, |
| 6691 | ) void { | 6656 | ) void { |
| 6692 | assert(elf.ehdrField(.type) != .REL); | 6657 | assert(elf.ehdrType() != .REL); |
| 6693 | switch (elf.getNode(node)) { | 6658 | switch (elf.getNode(node)) { |
| 6694 | .input_section, | 6659 | .input_section, |
| 6695 | .nav, | 6660 | .nav, |
| ... | @@ -6742,6 +6707,7 @@ fn addGotRelocAssumeCapacity( | ... | @@ -6742,6 +6707,7 @@ fn addGotRelocAssumeCapacity( |
| 6742 | .target = target, | 6707 | .target = target, |
| 6743 | .addend = addend, | 6708 | .addend = addend, |
| 6744 | .type = @"type", | 6709 | .type = @"type", |
| | 6710 | .result = .ok, |
| 6745 | }); | 6711 | }); |
| 6746 | } | 6712 | } |
| 6747 | fn updateGotEntry(elf: *Elf, got_index: usize) void { | 6713 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| ... | @@ -6768,23 +6734,17 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -6768,23 +6734,17 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6768 | const sym_value = sym_id.value(elf); | 6734 | const sym_value = sym_id.value(elf); |
| 6769 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; | 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 | break :val switch (sym_id.unwrap()) { | 6737 | break :val switch (sym_id.unwrap()) { |
| 6778 | // For global symbols, just target the right dynsym with no addend. | 6738 | // For global symbols, just target the right dynsym with no addend. |
| 6779 | .global => |name| .{ .reloc = .{ | 6739 | .global => |name| .{ .reloc = .{ |
| 6780 | .type = reloc_type, | 6740 | .type = .tpOff(elf), |
| 6781 | .dynsym_index = elf.globalByName(name).?.dynsym_index, | 6741 | .dynsym_index = elf.globalByName(name).?.dynsym_index, |
| 6782 | .addend = 0, | 6742 | .addend = 0, |
| 6783 | } }, | 6743 | } }, |
| 6784 | // For local symbols, target the null symbol (index 0) so we get the offset to the | 6744 | // For local symbols, target the null symbol (index 0) so we get the offset to the |
| 6785 | // base of our TLS block, and then use `addend` to offset to the right symbol. | 6745 | // base of our TLS block, and then use `addend` to offset to the right symbol. |
| 6786 | .local => .{ .reloc = .{ | 6746 | .local => .{ .reloc = .{ |
| 6787 | .type = reloc_type, | 6747 | .type = .tpOff(elf), |
| 6788 | .dynsym_index = 0, | 6748 | .dynsym_index = 0, |
| 6789 | .addend = @intCast(sym_id.value(elf)), | 6749 | .addend = @intCast(sym_id.value(elf)), |
| 6790 | } }, | 6750 | } }, |
| ... | @@ -6807,7 +6767,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -6807,7 +6767,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6807 | .static => .{ .unsigned = sym.value(elf) }, | 6767 | .static => .{ .unsigned = sym.value(elf) }, |
| 6808 | .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative` | 6768 | .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative` |
| 6809 | .dynamic => .{ .reloc = .{ | 6769 | .dynamic => .{ .reloc = .{ |
| 6810 | .type = .dtpOffAddr(elf), | 6770 | .type = .dtpOff(elf), |
| 6811 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, | 6771 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 6812 | .addend = 0, | 6772 | .addend = 0, |
| 6813 | } }, | 6773 | } }, |
| ... | @@ -6818,12 +6778,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -6818,12 +6778,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6818 | break :val .{ .unsigned = 1 }; // TLS module ID for executable | 6778 | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| 6819 | }, | 6779 | }, |
| 6820 | .dynamic => .{ .reloc = .{ | 6780 | .dynamic => .{ .reloc = .{ |
| 6821 | .type = switch (elf.ehdrField(.machine)) { | 6781 | .type = .dtpMod(elf), |
| 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 | }, | | |
| 6827 | .dynsym_index = switch (elf.classifySymbolValue(sym)) { | 6782 | .dynsym_index = switch (elf.classifySymbolValue(sym)) { |
| 6828 | .static, .static_relative => 0, | 6783 | .static, .static_relative => 0, |
| 6829 | .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index, | 6784 | .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| ... | @@ -6837,12 +6792,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -6837,12 +6792,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6837 | break :val .{ .unsigned = 1 }; // TLS module ID for executable | 6792 | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| 6838 | }, | 6793 | }, |
| 6839 | .dynamic => .{ .reloc = .{ | 6794 | .dynamic => .{ .reloc = .{ |
| 6840 | .type = switch (elf.ehdrField(.machine)) { | 6795 | .type = .dtpMod(elf), |
| 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 | }, | | |
| 6846 | .dynsym_index = 0, | 6796 | .dynsym_index = 0, |
| 6847 | .addend = 0, | 6797 | .addend = 0, |
| 6848 | } }, | 6798 | } }, |
| ... | @@ -7113,18 +7063,30 @@ pub fn flush( | ... | @@ -7113,18 +7063,30 @@ pub fn flush( |
| 7113 | for (elf.globals.strong_undef.keys()) |name| { | 7063 | for (elf.globals.strong_undef.keys()) |name| { |
| 7114 | if (elf.dso_globals.contains(name)) continue; | 7064 | if (elf.dso_globals.contains(name)) continue; |
| 7115 | any_undef = true; | 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 | if (any_undef) return error.AlreadyReported; | 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 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), | 7072 | error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), |
| 7123 | else => |e| return e, | 7073 | else => |e| return e, |
| 7124 | }; | 7074 | }; |
| 7125 | | 7075 | |
| 7126 | while (try elf.idle(tid)) {} | 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 | const entry_addr: u64 = entry: { | 7090 | const entry_addr: u64 = entry: { |
| 7129 | const sym_name_slice: []const u8 = name: switch (elf.options.entry) { | 7091 | const sym_name_slice: []const u8 = name: switch (elf.options.entry) { |
| 7130 | .default => switch (comp.config.output_mode) { | 7092 | .default => switch (comp.config.output_mode) { |
| ... | @@ -7151,44 +7113,6 @@ pub fn flush( | ... | @@ -7151,44 +7113,6 @@ pub fn flush( |
| 7151 | else => |e| return e, | 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 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | 7117 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7194 | const comp = elf.base.comp; | 7118 | const comp = elf.base.comp; |
| ... | @@ -7220,7 +7144,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | ... | @@ -7220,7 +7144,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7220 | const sym_id: Symbol.Id = .global(global_name); | 7144 | const sym_id: Symbol.Id = .global(global_name); |
| 7221 | const sym = global.symtab_index.ptr(elf); | 7145 | const sym = global.symtab_index.ptr(elf); |
| 7222 | | 7146 | |
| 7223 | switch (elf.ehdrField(.type)) { | 7147 | switch (elf.ehdrType()) { |
| 7224 | .REL => { | 7148 | .REL => { |
| 7225 | // Index in `.symtab` has changed. Relocatables are easy, we just need to update | 7149 | // Index in `.symtab` has changed. Relocatables are easy, we just need to update |
| 7226 | // all of the output relocations. | 7150 | // all of the output relocations. |
| ... | @@ -7238,7 +7162,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | ... | @@ -7238,7 +7162,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7238 | // For other `ET_*` values, the index in `.dynsym` has changed. There are a few | 7162 | // For other `ET_*` values, the index in `.dynsym` has changed. There are a few |
| 7239 | // places we might have emitted output relocations, depending on whether or not the | 7163 | // places we might have emitted output relocations, depending on whether or not the |
| 7240 | // symbol's value is statically known. | 7164 | // symbol's value is statically known. |
| 7241 | else => switch (elf.classifySymbolValue(sym_id)) { | 7165 | .EXEC, .DYN => switch (elf.classifySymbolValue(sym_id)) { |
| 7242 | .static, .static_relative => { | 7166 | .static, .static_relative => { |
| 7243 | // Since the symbol value is statically known, we definitely aren't emitting | 7167 | // Since the symbol value is statically known, we definitely aren't emitting |
| 7244 | // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they | 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,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 | fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void { | 7926 | fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void { |
| 7811 | const target_endian = elf.targetEndian(); | 7927 | const target_endian = elf.targetEndian(); |
| 7812 | switch (elf.ehdrField(.machine)) { | 7928 | switch (elf.ehdrMachine()) { |
| 7813 | else => |machine| @panic(@tagName(machine)), | 7929 | .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)), |
| 7814 | .X86_64 => { | 7930 | .X86_64 => { |
| 7815 | switch (which) { | 7931 | switch (which) { |
| 7816 | .plt => return, | 7932 | .plt => return, |
| ... | @@ -7912,8 +8028,20 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad | ... | @@ -7912,8 +8028,20 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad |
| 7912 | | 8028 | |
| 7913 | const got_plt_abs: u64 = got_plt_addr + got_plt_offset; | 8029 | const got_plt_abs: u64 = got_plt_addr + got_plt_offset; |
| 7914 | // TODO: handle overflow gracefully | 8030 | // TODO: handle overflow gracefully |
| 7915 | link.loongarch.writeJ20(target_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr + plt_offset)); | 8031 | const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]); |
| 7916 | link.loongarch.writeK12(target_slice[4..8], @truncate(got_plt_abs)); | 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,10 +8114,10 @@ fn updateExportsInner( |
| 7986 | .size = @intCast(size), | 8114 | .size = @intCast(size), |
| 7987 | .type = @"type", | 8115 | .type = @"type", |
| 7988 | .bind = switch (@"export".opts.linkage) { | 8116 | .bind = switch (@"export".opts.linkage) { |
| 7989 | .internal => @panic("TODO internal linkage"), | | |
| 7990 | .strong => .strong, | 8117 | .strong => .strong, |
| 7991 | .weak => .weak, | 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 | .visibility = switch (@"export".opts.visibility) { | 8122 | .visibility = switch (@"export".opts.visibility) { |
| 7995 | .default => .DEFAULT, | 8123 | .default => .DEFAULT, |
| ... | @@ -8152,3 +8280,19 @@ fn ensureNodeSize( | ... | @@ -8152,3 +8280,19 @@ fn ensureNodeSize( |
| 8152 | const new_size = need_size + need_size / MappedFile.growth_factor; | 8280 | const new_size = need_size + need_size / MappedFile.growth_factor; |
| 8153 | try node.resize(&elf.mf, gpa, new_size); | 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 | } |