| author | |
| committer | |
| log | d1446565a1449d1f79edb848bbd8db21ce05c70b |
| tree | b3f846e70f76ca2699bc6e38897e244f0e759d5d |
| parent | 04a8f217c651f56a2e185d87dd40c0a87ee78bc5 |
6 files changed, 2189 insertions(+), 407 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -591,6 +591,7 @@ set(ZIG_STAGE2_SOURCES |
| 591 | 591 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Atom.zig" |
| 592 | 592 | "${CMAKE_SOURCE_DIR}/src/link/Elf/LinkerDefined.zig" |
| 593 | 593 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Object.zig" |
| 594 | "${CMAKE_SOURCE_DIR}/src/link/Elf/SharedObject.zig" | |
| 594 | 595 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Symbol.zig" |
| 595 | 596 | "${CMAKE_SOURCE_DIR}/src/link/Elf/ZigModule.zig" |
| 596 | 597 | "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig" |
src/link/Elf.zig+582-65| ... | ... | @@ -14,6 +14,7 @@ files: std.MultiArrayList(File.Entry) = .{}, |
| 14 | 14 | zig_module_index: ?File.Index = null, |
| 15 | 15 | linker_defined_index: ?File.Index = null, |
| 16 | 16 | objects: std.ArrayListUnmanaged(File.Index) = .{}, |
| 17 | shared_objects: std.ArrayListUnmanaged(File.Index) = .{}, | |
| 17 | 18 | |
| 18 | 19 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 19 | 20 | /// Same order as in the file. |
| ... | ... | @@ -61,10 +62,34 @@ default_sym_version: elf.Elf64_Versym, |
| 61 | 62 | shstrtab: StringTable(.strtab) = .{}, |
| 62 | 63 | /// .strtab buffer |
| 63 | 64 | strtab: StringTable(.strtab) = .{}, |
| 64 | ||
| 65 | /// Representation of the GOT table as committed to the file. | |
| 65 | /// Dynamic symbol table. Only populated and emitted when linking dynamically. | |
| 66 | dynsym: DynsymSection = .{}, | |
| 67 | /// .dynstrtab buffer | |
| 68 | dynstrtab: StringTable(.dynstrtab) = .{}, | |
| 69 | /// Version symbol table. Only populated and emitted when linking dynamically. | |
| 70 | versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, | |
| 71 | /// .verneed section | |
| 72 | verneed: VerneedSection = .{}, | |
| 73 | /// .got section | |
| 66 | 74 | got: GotSection = .{}, |
| 75 | /// .rela.dyn section | |
| 67 | 76 | rela_dyn: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, |
| 77 | /// .dynamic section | |
| 78 | dynamic: DynamicSection = .{}, | |
| 79 | /// .hash section | |
| 80 | hash: HashSection = .{}, | |
| 81 | /// .gnu.hash section | |
| 82 | gnu_hash: GnuHashSection = .{}, | |
| 83 | /// .plt section | |
| 84 | plt: PltSection = .{}, | |
| 85 | /// .got.plt section | |
| 86 | got_plt: GotPltSection = .{}, | |
| 87 | /// .plt.got section | |
| 88 | plt_got: PltGotSection = .{}, | |
| 89 | /// .copyrel section | |
| 90 | copy_rel: CopyRelSection = .{}, | |
| 91 | /// .rela.plt section | |
| 92 | rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, | |
| 68 | 93 | |
| 69 | 94 | /// Tracked section headers |
| 70 | 95 | text_section_index: ?u16 = null, |
| ... | ... | @@ -73,18 +98,30 @@ data_section_index: ?u16 = null, |
| 73 | 98 | bss_section_index: ?u16 = null, |
| 74 | 99 | tdata_section_index: ?u16 = null, |
| 75 | 100 | tbss_section_index: ?u16 = null, |
| 101 | debug_info_section_index: ?u16 = null, | |
| 102 | debug_abbrev_section_index: ?u16 = null, | |
| 103 | debug_str_section_index: ?u16 = null, | |
| 104 | debug_aranges_section_index: ?u16 = null, | |
| 105 | debug_line_section_index: ?u16 = null, | |
| 106 | ||
| 107 | copy_rel_section_index: ?u16 = null, | |
| 108 | dynamic_section_index: ?u16 = null, | |
| 109 | dynstrtab_section_index: ?u16 = null, | |
| 110 | dynsymtab_section_index: ?u16 = null, | |
| 76 | 111 | eh_frame_section_index: ?u16 = null, |
| 77 | 112 | eh_frame_hdr_section_index: ?u16 = null, |
| 78 | dynamic_section_index: ?u16 = null, | |
| 113 | hash_section_index: ?u16 = null, | |
| 114 | gnu_hash_section_index: ?u16 = null, | |
| 79 | 115 | got_section_index: ?u16 = null, |
| 80 | 116 | got_plt_section_index: ?u16 = null, |
| 117 | interp_section_index: ?u16 = null, | |
| 81 | 118 | plt_section_index: ?u16 = null, |
| 119 | plt_got_section_index: ?u16 = null, | |
| 82 | 120 | rela_dyn_section_index: ?u16 = null, |
| 83 | debug_info_section_index: ?u16 = null, | |
| 84 | debug_abbrev_section_index: ?u16 = null, | |
| 85 | debug_str_section_index: ?u16 = null, | |
| 86 | debug_aranges_section_index: ?u16 = null, | |
| 87 | debug_line_section_index: ?u16 = null, | |
| 121 | rela_plt_section_index: ?u16 = null, | |
| 122 | versym_section_index: ?u16 = null, | |
| 123 | verneed_section_index: ?u16 = null, | |
| 124 | ||
| 88 | 125 | shstrtab_section_index: ?u16 = null, |
| 89 | 126 | strtab_section_index: ?u16 = null, |
| 90 | 127 | symtab_section_index: ?u16 = null, |
| ... | ... | @@ -316,10 +353,11 @@ pub fn deinit(self: *Elf) void { |
| 316 | 353 | .zig_module => data.zig_module.deinit(gpa), |
| 317 | 354 | .linker_defined => data.linker_defined.deinit(gpa), |
| 318 | 355 | .object => data.object.deinit(gpa), |
| 319 | // .shared_object => data.shared_object.deinit(gpa), | |
| 356 | .shared_object => data.shared_object.deinit(gpa), | |
| 320 | 357 | }; |
| 321 | 358 | self.files.deinit(gpa); |
| 322 | 359 | self.objects.deinit(gpa); |
| 360 | self.shared_objects.deinit(gpa); | |
| 323 | 361 | |
| 324 | 362 | self.shdrs.deinit(gpa); |
| 325 | 363 | self.phdr_to_shdr_table.deinit(gpa); |
| ... | ... | @@ -333,7 +371,6 @@ pub fn deinit(self: *Elf) void { |
| 333 | 371 | self.symbols.deinit(gpa); |
| 334 | 372 | self.symbols_extra.deinit(gpa); |
| 335 | 373 | self.symbols_free_list.deinit(gpa); |
| 336 | self.got.deinit(gpa); | |
| 337 | 374 | self.resolver.deinit(gpa); |
| 338 | 375 | self.start_stop_indexes.deinit(gpa); |
| 339 | 376 | |
| ... | ... | @@ -369,7 +406,19 @@ pub fn deinit(self: *Elf) void { |
| 369 | 406 | self.comdat_groups.deinit(gpa); |
| 370 | 407 | self.comdat_groups_owners.deinit(gpa); |
| 371 | 408 | self.comdat_groups_table.deinit(gpa); |
| 409 | ||
| 410 | self.got.deinit(gpa); | |
| 411 | self.plt.deinit(gpa); | |
| 412 | self.plt_got.deinit(gpa); | |
| 413 | self.dynsym.deinit(gpa); | |
| 414 | self.dynstrtab.deinit(gpa); | |
| 415 | self.dynamic.deinit(gpa); | |
| 416 | self.hash.deinit(gpa); | |
| 417 | self.versym.deinit(gpa); | |
| 418 | self.verneed.deinit(gpa); | |
| 419 | self.copy_rel.deinit(gpa); | |
| 372 | 420 | self.rela_dyn.deinit(gpa); |
| 421 | self.rela_plt.deinit(gpa); | |
| 373 | 422 | } |
| 374 | 423 | |
| 375 | 424 | pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { |
| ... | ... | @@ -1268,6 +1317,24 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1268 | 1317 | try dw.flushModule(self.base.options.module.?); |
| 1269 | 1318 | } |
| 1270 | 1319 | |
| 1320 | // Dedup shared objects | |
| 1321 | { | |
| 1322 | var seen_dsos = std.StringHashMap(void).init(gpa); | |
| 1323 | defer seen_dsos.deinit(); | |
| 1324 | try seen_dsos.ensureTotalCapacity(@as(u32, @intCast(self.shared_objects.items.len))); | |
| 1325 | ||
| 1326 | var i: usize = 0; | |
| 1327 | while (i < self.shared_objects.items.len) { | |
| 1328 | const index = self.shared_objects.items[i]; | |
| 1329 | const shared_object = self.file(index).?.shared_object; | |
| 1330 | const soname = shared_object.soname(); | |
| 1331 | const gop = seen_dsos.getOrPutAssumeCapacity(soname); | |
| 1332 | if (gop.found_existing) { | |
| 1333 | _ = self.shared_objects.orderedRemove(i); | |
| 1334 | } else i += 1; | |
| 1335 | } | |
| 1336 | } | |
| 1337 | ||
| 1271 | 1338 | // If we haven't already, create a linker-generated input file comprising of |
| 1272 | 1339 | // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. |
| 1273 | 1340 | if (self.linker_defined_index == null) { |
| ... | ... | @@ -1677,6 +1744,7 @@ fn resolveSymbols(self: *Elf) void { |
| 1677 | 1744 | if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self); |
| 1678 | 1745 | // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). |
| 1679 | 1746 | for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); |
| 1747 | for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); | |
| 1680 | 1748 | |
| 1681 | 1749 | // Mark live objects. |
| 1682 | 1750 | self.markLive(); |
| ... | ... | @@ -1684,6 +1752,7 @@ fn resolveSymbols(self: *Elf) void { |
| 1684 | 1752 | // Reset state of all globals after marking live objects. |
| 1685 | 1753 | if (self.zig_module_index) |index| self.file(index).?.resetGlobals(self); |
| 1686 | 1754 | for (self.objects.items) |index| self.file(index).?.resetGlobals(self); |
| 1755 | for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self); | |
| 1687 | 1756 | |
| 1688 | 1757 | // Prune dead objects and shared objects. |
| 1689 | 1758 | var i: usize = 0; |
| ... | ... | @@ -1693,6 +1762,13 @@ fn resolveSymbols(self: *Elf) void { |
| 1693 | 1762 | _ = self.objects.orderedRemove(i); |
| 1694 | 1763 | } else i += 1; |
| 1695 | 1764 | } |
| 1765 | i = 0; | |
| 1766 | while (i < self.shared_objects.items.len) { | |
| 1767 | const index = self.shared_objects.items[i]; | |
| 1768 | if (!self.file(index).?.isAlive()) { | |
| 1769 | _ = self.shared_objects.orderedRemove(i); | |
| 1770 | } else i += 1; | |
| 1771 | } | |
| 1696 | 1772 | |
| 1697 | 1773 | // Dedup comdat groups. |
| 1698 | 1774 | for (self.objects.items) |index| { |
| ... | ... | @@ -1728,6 +1804,7 @@ fn resolveSymbols(self: *Elf) void { |
| 1728 | 1804 | // Re-resolve the symbols. |
| 1729 | 1805 | if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self); |
| 1730 | 1806 | for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); |
| 1807 | for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); | |
| 1731 | 1808 | } |
| 1732 | 1809 | |
| 1733 | 1810 | /// Traverses all objects and shared objects marking any object referenced by |
| ... | ... | @@ -1740,6 +1817,10 @@ fn markLive(self: *Elf) void { |
| 1740 | 1817 | const file_ptr = self.file(index).?; |
| 1741 | 1818 | if (file_ptr.isAlive()) file_ptr.markLive(self); |
| 1742 | 1819 | } |
| 1820 | for (self.shared_objects.items) |index| { | |
| 1821 | const file_ptr = self.file(index).?; | |
| 1822 | if (file_ptr.isAlive()) file_ptr.markLive(self); | |
| 1823 | } | |
| 1743 | 1824 | } |
| 1744 | 1825 | |
| 1745 | 1826 | fn markEhFrameAtomsDead(self: *Elf) void { |
| ... | ... | @@ -1759,10 +1840,10 @@ fn markImportsExports(self: *Elf) void { |
| 1759 | 1840 | const file_ptr = global.file(elf_file) orelse continue; |
| 1760 | 1841 | const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other)); |
| 1761 | 1842 | if (vis == .HIDDEN) continue; |
| 1762 | // if (file == .shared and !global.isAbs(self)) { | |
| 1763 | // global.flags.import = true; | |
| 1764 | // continue; | |
| 1765 | // } | |
| 1843 | if (file_ptr == .shared_object and !global.isAbs(elf_file)) { | |
| 1844 | global.flags.import = true; | |
| 1845 | continue; | |
| 1846 | } | |
| 1766 | 1847 | if (file_ptr.index() == file_index) { |
| 1767 | 1848 | global.flags.@"export" = true; |
| 1768 | 1849 | if (elf_file.isDynLib() and vis != .PROTECTED) { |
| ... | ... | @@ -1773,6 +1854,17 @@ fn markImportsExports(self: *Elf) void { |
| 1773 | 1854 | } |
| 1774 | 1855 | }.mark; |
| 1775 | 1856 | |
| 1857 | if (!self.isDynLib()) { | |
| 1858 | for (self.shared_objects.items) |index| { | |
| 1859 | for (self.file(index).?.globals()) |global_index| { | |
| 1860 | const global = self.symbol(global_index); | |
| 1861 | const file_ptr = global.file(self) orelse continue; | |
| 1862 | const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other)); | |
| 1863 | if (file_ptr != .shared_object and vis != .HIDDEN) global.flags.@"export" = true; | |
| 1864 | } | |
| 1865 | } | |
| 1866 | } | |
| 1867 | ||
| 1776 | 1868 | if (self.zig_module_index) |index| { |
| 1777 | 1869 | mark(self, index); |
| 1778 | 1870 | } |
| ... | ... | @@ -1820,12 +1912,51 @@ fn scanRelocs(self: *Elf) !void { |
| 1820 | 1912 | |
| 1821 | 1913 | try self.reportUndefined(&undefs); |
| 1822 | 1914 | |
| 1823 | for (self.symbols.items, 0..) |*sym, sym_index| { | |
| 1915 | for (self.symbols.items, 0..) |*sym, i| { | |
| 1916 | const index = @as(u32, @intCast(i)); | |
| 1917 | if (!sym.isLocal() and !sym.flags.has_dynamic) { | |
| 1918 | log.debug("'{s}' is non-local", .{sym.name(self)}); | |
| 1919 | try self.dynsym.addSymbol(index, self); | |
| 1920 | } | |
| 1824 | 1921 | if (sym.flags.needs_got) { |
| 1825 | 1922 | log.debug("'{s}' needs GOT", .{sym.name(self)}); |
| 1826 | _ = try self.got.addGotSymbol(@intCast(sym_index), self); | |
| 1827 | sym.flags.has_got = true; | |
| 1923 | _ = try self.got.addGotSymbol(index, self); | |
| 1924 | } | |
| 1925 | if (sym.flags.needs_plt) { | |
| 1926 | if (sym.flags.is_canonical) { | |
| 1927 | log.debug("'{s}' needs CPLT", .{sym.name(self)}); | |
| 1928 | sym.flags.@"export" = true; | |
| 1929 | try self.plt.addSymbol(index, self); | |
| 1930 | } else if (sym.flags.needs_got) { | |
| 1931 | log.debug("'{s}' needs PLTGOT", .{sym.name(self)}); | |
| 1932 | try self.plt_got.addSymbol(index, self); | |
| 1933 | } else { | |
| 1934 | log.debug("'{s}' needs PLT", .{sym.name(self)}); | |
| 1935 | try self.plt.addSymbol(index, self); | |
| 1936 | } | |
| 1937 | } | |
| 1938 | if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) { | |
| 1939 | log.debug("'{s}' needs COPYREL", .{sym.name(self)}); | |
| 1940 | try self.copy_rel.addSymbol(index, self); | |
| 1941 | } | |
| 1942 | if (sym.flags.needs_tlsgd) { | |
| 1943 | log.debug("'{s}' needs TLSGD", .{sym.name(self)}); | |
| 1944 | try self.got.addTlsGdSymbol(index, self); | |
| 1828 | 1945 | } |
| 1946 | if (sym.flags.needs_gottp) { | |
| 1947 | log.debug("'{s}' needs GOTTP", .{sym.name(self)}); | |
| 1948 | try self.got.addGotTpSymbol(index, self); | |
| 1949 | } | |
| 1950 | if (sym.flags.needs_tlsdesc) { | |
| 1951 | log.debug("'{s}' needs TLSDESC", .{sym.name(self)}); | |
| 1952 | try self.dynsym.addSymbol(index, self); | |
| 1953 | try self.got.addTlsDescSymbol(index, self); | |
| 1954 | } | |
| 1955 | } | |
| 1956 | ||
| 1957 | if (self.got.flags.needs_tlsld) { | |
| 1958 | log.debug("program needs TLSLD", .{}); | |
| 1959 | try self.got.addTlsLdSymbol(self); | |
| 1829 | 1960 | } |
| 1830 | 1961 | } |
| 1831 | 1962 | |
| ... | ... | @@ -2567,12 +2698,12 @@ fn writeHeader(self: *Elf) !void { |
| 2567 | 2698 | |
| 2568 | 2699 | assert(index == 16); |
| 2569 | 2700 | |
| 2570 | const elf_type = switch (self.base.options.effectiveOutputMode()) { | |
| 2571 | .Exe => elf.ET.EXEC, | |
| 2572 | .Obj => elf.ET.REL, | |
| 2701 | const elf_type: elf.ET = switch (self.base.options.effectiveOutputMode()) { | |
| 2702 | .Exe => if (self.base.options.pic) .DYN else .EXEC, | |
| 2703 | .Obj => .REL, | |
| 2573 | 2704 | .Lib => switch (self.base.options.link_mode) { |
| 2574 | .Static => elf.ET.REL, | |
| 2575 | .Dynamic => elf.ET.DYN, | |
| 2705 | .Static => @as(elf.ET, .REL), | |
| 2706 | .Dynamic => .DYN, | |
| 2576 | 2707 | }, |
| 2577 | 2708 | }; |
| 2578 | 2709 | mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(elf_type), endian); |
| ... | ... | @@ -2819,8 +2950,8 @@ fn updateDeclCode( |
| 2819 | 2950 | esym.st_value = atom_ptr.value; |
| 2820 | 2951 | |
| 2821 | 2952 | log.debug(" (writing new offset table entry)", .{}); |
| 2822 | const extra = sym.extra(self).?; | |
| 2823 | try self.got.writeEntry(self, extra.got); | |
| 2953 | // const extra = sym.extra(self).?; | |
| 2954 | // try self.got.writeEntry(self, extra.got); | |
| 2824 | 2955 | } |
| 2825 | 2956 | } else if (code.len < old_size) { |
| 2826 | 2957 | atom_ptr.shrink(self); |
| ... | ... | @@ -2834,7 +2965,8 @@ fn updateDeclCode( |
| 2834 | 2965 | |
| 2835 | 2966 | sym.flags.needs_got = true; |
| 2836 | 2967 | const gop = try sym.getOrCreateGotEntry(sym_index, self); |
| 2837 | try self.got.writeEntry(self, gop.index); | |
| 2968 | _ = gop; | |
| 2969 | // try self.got.writeEntry(self, gop.index); | |
| 2838 | 2970 | } |
| 2839 | 2971 | |
| 2840 | 2972 | if (self.base.child_pid) |pid| { |
| ... | ... | @@ -3070,7 +3202,8 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. |
| 3070 | 3202 | |
| 3071 | 3203 | local_sym.flags.needs_got = true; |
| 3072 | 3204 | const gop = try local_sym.getOrCreateGotEntry(symbol_index, self); |
| 3073 | try self.got.writeEntry(self, gop.index); | |
| 3205 | _ = gop; | |
| 3206 | // try self.got.writeEntry(self, gop.index); | |
| 3074 | 3207 | |
| 3075 | 3208 | const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; |
| 3076 | 3209 | const file_offset = self.shdrs.items[output_section_index].sh_offset + section_offset; |
| ... | ... | @@ -3480,13 +3613,145 @@ fn initSections(self: *Elf) !void { |
| 3480 | 3613 | } |
| 3481 | 3614 | } |
| 3482 | 3615 | |
| 3483 | if (self.got.entries.items.len > 0 and self.got_section_index == null) { | |
| 3616 | if (self.got.entries.items.len > 0) { | |
| 3484 | 3617 | self.got_section_index = try self.addSection(.{ |
| 3485 | 3618 | .name = ".got", |
| 3486 | 3619 | .type = elf.SHT_PROGBITS, |
| 3487 | 3620 | .flags = elf.SHF_ALLOC | elf.SHF_WRITE, |
| 3488 | 3621 | .addralign = ptr_size, |
| 3489 | 3622 | }); |
| 3623 | self.got_plt_section_index = try self.addSection(.{ | |
| 3624 | .name = ".got.plt", | |
| 3625 | .type = elf.SHT_PROGBITS, | |
| 3626 | .flags = elf.SHF_ALLOC | elf.SHF_WRITE, | |
| 3627 | .addralign = @alignOf(u64), | |
| 3628 | }); | |
| 3629 | } | |
| 3630 | ||
| 3631 | const needs_rela_dyn = blk: { | |
| 3632 | if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or | |
| 3633 | self.copy_rel.symbols.items.len > 0) break :blk true; | |
| 3634 | for (self.objects.items) |index| { | |
| 3635 | if (self.file(index).?.object.num_dynrelocs > 0) break :blk true; | |
| 3636 | } | |
| 3637 | break :blk false; | |
| 3638 | }; | |
| 3639 | if (needs_rela_dyn) { | |
| 3640 | self.rela_dyn_section_index = try self.addSection(.{ | |
| 3641 | .name = ".rela.dyn", | |
| 3642 | .type = elf.SHT_RELA, | |
| 3643 | .flags = elf.SHF_ALLOC, | |
| 3644 | .addralign = @alignOf(elf.Elf64_Rela), | |
| 3645 | .entsize = @sizeOf(elf.Elf64_Rela), | |
| 3646 | }); | |
| 3647 | } | |
| 3648 | ||
| 3649 | if (self.plt.symbols.items.len > 0) { | |
| 3650 | self.plt_section_index = try self.addSection(.{ | |
| 3651 | .name = ".plt", | |
| 3652 | .type = elf.SHT_PROGBITS, | |
| 3653 | .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, | |
| 3654 | .addralign = 16, | |
| 3655 | }); | |
| 3656 | self.rela_plt_section_index = try self.addSection(.{ | |
| 3657 | .name = ".rela.plt", | |
| 3658 | .type = elf.SHT_RELA, | |
| 3659 | .flags = elf.SHF_ALLOC, | |
| 3660 | .addralign = @alignOf(elf.Elf64_Rela), | |
| 3661 | .entsize = @sizeOf(elf.Elf64_Rela), | |
| 3662 | }); | |
| 3663 | } | |
| 3664 | ||
| 3665 | if (self.plt_got.symbols.items.len > 0) { | |
| 3666 | self.plt_got_section_index = try self.addSection(.{ | |
| 3667 | .name = ".plt.got", | |
| 3668 | .type = elf.SHT_PROGBITS, | |
| 3669 | .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, | |
| 3670 | .addralign = 16, | |
| 3671 | }); | |
| 3672 | } | |
| 3673 | ||
| 3674 | if (self.copy_rel.symbols.items.len > 0) { | |
| 3675 | self.copy_rel_section_index = try self.addSection(.{ | |
| 3676 | .name = ".copyrel", | |
| 3677 | .type = elf.SHT_NOBITS, | |
| 3678 | .flags = elf.SHF_ALLOC | elf.SHF_WRITE, | |
| 3679 | }); | |
| 3680 | } | |
| 3681 | ||
| 3682 | const needs_interp = blk: { | |
| 3683 | // On Ubuntu with musl-gcc, we get a weird combo of options looking like this: | |
| 3684 | // -dynamic-linker=<path> -static | |
| 3685 | // In this case, if we do generate .interp section and segment, we will get | |
| 3686 | // a segfault in the dynamic linker trying to load a binary that is static | |
| 3687 | // and doesn't contain .dynamic section. | |
| 3688 | if (self.isStatic() and !self.base.options.pie) break :blk false; | |
| 3689 | break :blk self.base.options.dynamic_linker != null; | |
| 3690 | }; | |
| 3691 | if (needs_interp) { | |
| 3692 | self.interp_section_index = try self.addSection(.{ | |
| 3693 | .name = ".interp", | |
| 3694 | .type = elf.SHT_PROGBITS, | |
| 3695 | .flags = elf.SHF_ALLOC, | |
| 3696 | .addralign = 1, | |
| 3697 | }); | |
| 3698 | } | |
| 3699 | ||
| 3700 | if (self.isDynLib() or self.shared_objects.items.len > 0 or self.base.options.pie) { | |
| 3701 | self.dynstrtab_section_index = try self.addSection(.{ | |
| 3702 | .name = ".dynstr", | |
| 3703 | .flags = elf.SHF_ALLOC, | |
| 3704 | .type = elf.SHT_STRTAB, | |
| 3705 | .entsize = 1, | |
| 3706 | .addralign = 1, | |
| 3707 | }); | |
| 3708 | self.dynamic_section_index = try self.addSection(.{ | |
| 3709 | .name = ".dynamic", | |
| 3710 | .flags = elf.SHF_ALLOC | elf.SHF_WRITE, | |
| 3711 | .type = elf.SHT_DYNAMIC, | |
| 3712 | .entsize = @sizeOf(elf.Elf64_Dyn), | |
| 3713 | .addralign = @alignOf(elf.Elf64_Dyn), | |
| 3714 | }); | |
| 3715 | self.dynsymtab_section_index = try self.addSection(.{ | |
| 3716 | .name = ".dynsym", | |
| 3717 | .flags = elf.SHF_ALLOC, | |
| 3718 | .type = elf.SHT_DYNSYM, | |
| 3719 | .addralign = @alignOf(elf.Elf64_Sym), | |
| 3720 | .entsize = @sizeOf(elf.Elf64_Sym), | |
| 3721 | }); | |
| 3722 | self.hash_section_index = try self.addSection(.{ | |
| 3723 | .name = ".hash", | |
| 3724 | .flags = elf.SHF_ALLOC, | |
| 3725 | .type = elf.SHT_HASH, | |
| 3726 | .addralign = 4, | |
| 3727 | .entsize = 4, | |
| 3728 | }); | |
| 3729 | self.gnu_hash_section_index = try self.addSection(.{ | |
| 3730 | .name = ".gnu.hash", | |
| 3731 | .flags = elf.SHF_ALLOC, | |
| 3732 | .type = elf.SHT_GNU_HASH, | |
| 3733 | .addralign = 8, | |
| 3734 | }); | |
| 3735 | ||
| 3736 | const needs_versions = for (self.dynsym.entries.items) |entry| { | |
| 3737 | const sym = self.symbol(entry.symbol_index); | |
| 3738 | if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true; | |
| 3739 | } else false; | |
| 3740 | if (needs_versions) { | |
| 3741 | self.versym_section_index = try self.addSection(.{ | |
| 3742 | .name = ".gnu.version", | |
| 3743 | .flags = elf.SHF_ALLOC, | |
| 3744 | .type = elf.SHT_GNU_VERSYM, | |
| 3745 | .addralign = @alignOf(elf.Elf64_Versym), | |
| 3746 | .entsize = @sizeOf(elf.Elf64_Versym), | |
| 3747 | }); | |
| 3748 | self.verneed_section_index = try self.addSection(.{ | |
| 3749 | .name = ".gnu.version_r", | |
| 3750 | .flags = elf.SHF_ALLOC, | |
| 3751 | .type = elf.SHT_GNU_VERNEED, | |
| 3752 | .addralign = @alignOf(elf.Elf64_Verneed), | |
| 3753 | }); | |
| 3754 | } | |
| 3490 | 3755 | } |
| 3491 | 3756 | |
| 3492 | 3757 | if (self.symtab_section_index == null) { |
| ... | ... | @@ -3665,6 +3930,20 @@ fn sortSections(self: *Elf) !void { |
| 3665 | 3930 | &self.symtab_section_index, |
| 3666 | 3931 | &self.strtab_section_index, |
| 3667 | 3932 | &self.shstrtab_section_index, |
| 3933 | &self.interp_section_index, | |
| 3934 | &self.dynamic_section_index, | |
| 3935 | &self.dynsymtab_section_index, | |
| 3936 | &self.dynstrtab_section_index, | |
| 3937 | &self.hash_section_index, | |
| 3938 | &self.gnu_hash_section_index, | |
| 3939 | &self.plt_section_index, | |
| 3940 | &self.got_plt_section_index, | |
| 3941 | &self.plt_got_section_index, | |
| 3942 | &self.rela_dyn_section_index, | |
| 3943 | &self.rela_plt_section_index, | |
| 3944 | &self.copy_rel_section_index, | |
| 3945 | &self.versym_section_index, | |
| 3946 | &self.verneed_section_index, | |
| 3668 | 3947 | }) |maybe_index| { |
| 3669 | 3948 | if (maybe_index.*) |*index| { |
| 3670 | 3949 | index.* = backlinks[index.*]; |
| ... | ... | @@ -3675,6 +3954,47 @@ fn sortSections(self: *Elf) !void { |
| 3675 | 3954 | const shdr = &self.shdrs.items[index]; |
| 3676 | 3955 | shdr.sh_link = self.strtab_section_index.?; |
| 3677 | 3956 | } |
| 3957 | ||
| 3958 | if (self.dynamic_section_index) |index| { | |
| 3959 | const shdr = &self.shdrs.items[index]; | |
| 3960 | shdr.sh_link = self.dynstrtab_section_index.?; | |
| 3961 | } | |
| 3962 | ||
| 3963 | if (self.dynsymtab_section_index) |index| { | |
| 3964 | const shdr = &self.shdrs.items[index]; | |
| 3965 | shdr.sh_link = self.dynstrtab_section_index.?; | |
| 3966 | } | |
| 3967 | ||
| 3968 | if (self.hash_section_index) |index| { | |
| 3969 | const shdr = &self.shdrs.items[index]; | |
| 3970 | shdr.sh_link = self.dynsymtab_section_index.?; | |
| 3971 | } | |
| 3972 | ||
| 3973 | if (self.gnu_hash_section_index) |index| { | |
| 3974 | const shdr = &self.shdrs.items[index]; | |
| 3975 | shdr.sh_link = self.dynsymtab_section_index.?; | |
| 3976 | } | |
| 3977 | ||
| 3978 | if (self.versym_section_index) |index| { | |
| 3979 | const shdr = &self.shdrs.items[index]; | |
| 3980 | shdr.sh_link = self.dynsymtab_section_index.?; | |
| 3981 | } | |
| 3982 | ||
| 3983 | if (self.verneed_section_index) |index| { | |
| 3984 | const shdr = &self.shdrs.items[index]; | |
| 3985 | shdr.sh_link = self.dynstrtab_section_index.?; | |
| 3986 | } | |
| 3987 | ||
| 3988 | if (self.rela_dyn_section_index) |index| { | |
| 3989 | const shdr = &self.shdrs.items[index]; | |
| 3990 | shdr.sh_link = self.dynsymtab_section_index orelse 0; | |
| 3991 | } | |
| 3992 | ||
| 3993 | if (self.rela_plt_section_index) |index| { | |
| 3994 | const shdr = &self.shdrs.items[index]; | |
| 3995 | shdr.sh_link = self.dynsymtab_section_index.?; | |
| 3996 | shdr.sh_info = self.plt_section_index.?; | |
| 3997 | } | |
| 3678 | 3998 | } |
| 3679 | 3999 | |
| 3680 | 4000 | fn updateSectionSizes(self: *Elf) !void { |
| ... | ... | @@ -3683,21 +4003,77 @@ fn updateSectionSizes(self: *Elf) !void { |
| 3683 | 4003 | } |
| 3684 | 4004 | |
| 3685 | 4005 | if (self.eh_frame_section_index) |index| { |
| 3686 | const shdr = &self.shdrs.items[index]; | |
| 3687 | shdr.sh_size = try eh_frame.calcEhFrameSize(self); | |
| 3688 | shdr.sh_addralign = @alignOf(u64); | |
| 4006 | self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); | |
| 3689 | 4007 | } |
| 3690 | 4008 | |
| 3691 | 4009 | if (self.eh_frame_hdr_section_index) |index| { |
| 3692 | const shdr = &self.shdrs.items[index]; | |
| 3693 | shdr.sh_size = eh_frame.calcEhFrameHdrSize(self); | |
| 3694 | shdr.sh_addralign = @alignOf(u32); | |
| 4010 | self.shdrs.items[index].sh_size = eh_frame.calcEhFrameHdrSize(self); | |
| 3695 | 4011 | } |
| 3696 | 4012 | |
| 3697 | 4013 | if (self.got_section_index) |index| { |
| 3698 | 4014 | self.shdrs.items[index].sh_size = self.got.size(self); |
| 3699 | 4015 | } |
| 3700 | 4016 | |
| 4017 | if (self.plt_section_index) |index| { | |
| 4018 | self.shdrs.items[index].sh_size = self.plt.size(); | |
| 4019 | } | |
| 4020 | ||
| 4021 | if (self.got_plt_section_index) |index| { | |
| 4022 | self.shdrs.items[index].sh_size = self.got_plt.size(self); | |
| 4023 | } | |
| 4024 | ||
| 4025 | if (self.plt_got_section_index) |index| { | |
| 4026 | self.shdrs.items[index].sh_size = self.plt_got.size(); | |
| 4027 | } | |
| 4028 | ||
| 4029 | if (self.rela_dyn_section_index) |shndx| { | |
| 4030 | var num = self.got.numRela(self) + self.copy_rel.numRela(); | |
| 4031 | for (self.objects.items) |index| { | |
| 4032 | num += self.file(index).?.object.num_dynrelocs; | |
| 4033 | } | |
| 4034 | self.shdrs.items[shndx].sh_size = num * @sizeOf(elf.Elf64_Rela); | |
| 4035 | } | |
| 4036 | ||
| 4037 | if (self.rela_plt_section_index) |index| { | |
| 4038 | self.shdrs.items[index].sh_size = self.plt.numRela() * @sizeOf(elf.Elf64_Rela); | |
| 4039 | } | |
| 4040 | ||
| 4041 | if (self.copy_rel_section_index) |index| { | |
| 4042 | try self.copy_rel.updateSectionSize(index, self); | |
| 4043 | } | |
| 4044 | ||
| 4045 | if (self.interp_section_index) |index| { | |
| 4046 | self.shdrs.items[index].sh_size = self.base.options.dynamic_linker.?.len + 1; | |
| 4047 | } | |
| 4048 | ||
| 4049 | if (self.hash_section_index) |index| { | |
| 4050 | self.shdrs.items[index].sh_size = self.hash.size(); | |
| 4051 | } | |
| 4052 | ||
| 4053 | if (self.gnu_hash_section_index) |index| { | |
| 4054 | self.shdrs.items[index].sh_size = self.gnu_hash.size(); | |
| 4055 | } | |
| 4056 | ||
| 4057 | if (self.dynamic_section_index) |index| { | |
| 4058 | self.shdrs.items[index].sh_size = self.dynamic.size(self); | |
| 4059 | } | |
| 4060 | ||
| 4061 | if (self.dynsymtab_section_index) |index| { | |
| 4062 | self.shdrs.items[index].sh_size = self.dynsym.size(); | |
| 4063 | } | |
| 4064 | ||
| 4065 | if (self.dynstrtab_section_index) |index| { | |
| 4066 | self.shdrs.items[index].sh_size = self.dynstrtab.buffer.items.len; | |
| 4067 | } | |
| 4068 | ||
| 4069 | if (self.versym_section_index) |index| { | |
| 4070 | self.shdrs.items[index].sh_size = self.versym.items.len * @sizeOf(elf.Elf64_Versym); | |
| 4071 | } | |
| 4072 | ||
| 4073 | if (self.verneed_section_index) |index| { | |
| 4074 | self.shdrs.items[index].sh_size = self.verneed.size(); | |
| 4075 | } | |
| 4076 | ||
| 3701 | 4077 | if (self.symtab_section_index != null) { |
| 3702 | 4078 | try self.updateSymtabSize(); |
| 3703 | 4079 | } |
| ... | ... | @@ -3708,13 +4084,17 @@ fn updateSectionSizes(self: *Elf) !void { |
| 3708 | 4084 | if (self.got_section_index) |_| { |
| 3709 | 4085 | try self.got.updateStrtab(self); |
| 3710 | 4086 | } |
| 4087 | if (self.plt_section_index) |_| { | |
| 4088 | try self.plt.updateStrtab(self); | |
| 4089 | } | |
| 4090 | if (self.plt_got_section_index) |_| { | |
| 4091 | try self.plt_got.updateStrtab(self); | |
| 4092 | } | |
| 3711 | 4093 | self.shdrs.items[index].sh_size = self.strtab.buffer.items.len; |
| 3712 | // try self.growNonAllocSection(index, self.strtab.buffer.items.len, 1, false); | |
| 3713 | 4094 | } |
| 3714 | 4095 | |
| 3715 | 4096 | if (self.shstrtab_section_index) |index| { |
| 3716 | 4097 | self.shdrs.items[index].sh_size = self.shstrtab.buffer.items.len; |
| 3717 | // try self.growNonAllocSection(index, self.shstrtab.buffer.items.len, 1, false); | |
| 3718 | 4098 | } |
| 3719 | 4099 | } |
| 3720 | 4100 | |
| ... | ... | @@ -3729,18 +4109,18 @@ fn initPhdrs(self: *Elf) !void { |
| 3729 | 4109 | }); |
| 3730 | 4110 | |
| 3731 | 4111 | // Add INTERP phdr if required |
| 3732 | // if (self.interp_sect_index) |index| { | |
| 3733 | // const shdr = self.sections.items(.shdr)[index]; | |
| 3734 | // _ = try self.addPhdr(.{ | |
| 3735 | // .type = elf.PT_INTERP, | |
| 3736 | // .flags = elf.PF_R, | |
| 3737 | // .@"align" = 1, | |
| 3738 | // .offset = shdr.sh_offset, | |
| 3739 | // .addr = shdr.sh_addr, | |
| 3740 | // .filesz = shdr.sh_size, | |
| 3741 | // .memsz = shdr.sh_size, | |
| 3742 | // }); | |
| 3743 | // } | |
| 4112 | if (self.interp_section_index) |index| { | |
| 4113 | const shdr = self.shdrs.items[index]; | |
| 4114 | _ = try self.addPhdr(.{ | |
| 4115 | .type = elf.PT_INTERP, | |
| 4116 | .flags = elf.PF_R, | |
| 4117 | .@"align" = 1, | |
| 4118 | .offset = shdr.sh_offset, | |
| 4119 | .addr = shdr.sh_addr, | |
| 4120 | .filesz = shdr.sh_size, | |
| 4121 | .memsz = shdr.sh_size, | |
| 4122 | }); | |
| 4123 | } | |
| 3744 | 4124 | |
| 3745 | 4125 | // Add LOAD phdrs |
| 3746 | 4126 | const slice = self.shdrs.items; |
| ... | ... | @@ -3806,18 +4186,18 @@ fn initPhdrs(self: *Elf) !void { |
| 3806 | 4186 | } |
| 3807 | 4187 | |
| 3808 | 4188 | // Add DYNAMIC phdr |
| 3809 | // if (self.dynamic_sect_index) |index| { | |
| 3810 | // const shdr = self.sections.items(.shdr)[index]; | |
| 3811 | // _ = try self.addPhdr(.{ | |
| 3812 | // .type = elf.PT_DYNAMIC, | |
| 3813 | // .flags = elf.PF_R | elf.PF_W, | |
| 3814 | // .@"align" = shdr.sh_addralign, | |
| 3815 | // .offset = shdr.sh_offset, | |
| 3816 | // .addr = shdr.sh_addr, | |
| 3817 | // .memsz = shdr.sh_size, | |
| 3818 | // .filesz = shdr.sh_size, | |
| 3819 | // }); | |
| 3820 | // } | |
| 4189 | if (self.dynamic_section_index) |index| { | |
| 4190 | const shdr = self.shdrs.items[index]; | |
| 4191 | _ = try self.addPhdr(.{ | |
| 4192 | .type = elf.PT_DYNAMIC, | |
| 4193 | .flags = elf.PF_R | elf.PF_W, | |
| 4194 | .@"align" = shdr.sh_addralign, | |
| 4195 | .offset = shdr.sh_offset, | |
| 4196 | .addr = shdr.sh_addr, | |
| 4197 | .memsz = shdr.sh_size, | |
| 4198 | .filesz = shdr.sh_size, | |
| 4199 | }); | |
| 4200 | } | |
| 3821 | 4201 | |
| 3822 | 4202 | // Add PT_GNU_EH_FRAME phdr if required. |
| 3823 | 4203 | if (self.eh_frame_hdr_section_index) |index| { |
| ... | ... | @@ -4113,6 +4493,16 @@ fn updateSymtabSize(self: *Elf) !void { |
| 4113 | 4493 | sizes.nlocals += self.got.output_symtab_size.nlocals; |
| 4114 | 4494 | } |
| 4115 | 4495 | |
| 4496 | if (self.plt_section_index) |_| { | |
| 4497 | self.plt.updateSymtabSize(self); | |
| 4498 | sizes.nlocals += self.plt.output_symtab_size.nlocals; | |
| 4499 | } | |
| 4500 | ||
| 4501 | if (self.plt_got_section_index) |_| { | |
| 4502 | self.plt_got.updateSymtabSize(self); | |
| 4503 | sizes.nlocals += self.plt_got.output_symtab_size.nlocals; | |
| 4504 | } | |
| 4505 | ||
| 4116 | 4506 | if (self.linker_defined_index) |index| { |
| 4117 | 4507 | const linker_defined = self.file(index).?.linker_defined; |
| 4118 | 4508 | linker_defined.updateSymtabSize(self); |
| ... | ... | @@ -4127,18 +4517,70 @@ fn updateSymtabSize(self: *Elf) !void { |
| 4127 | 4517 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 4128 | 4518 | .p64 => @sizeOf(elf.Elf64_Sym), |
| 4129 | 4519 | }; |
| 4130 | // const sym_align: u16 = switch (self.ptr_width) { | |
| 4131 | // .p32 => @alignOf(elf.Elf32_Sym), | |
| 4132 | // .p64 => @alignOf(elf.Elf64_Sym), | |
| 4133 | // }; | |
| 4134 | 4520 | const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; |
| 4135 | 4521 | shdr.sh_size = needed_size; |
| 4136 | // try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, false); | |
| 4137 | 4522 | } |
| 4138 | 4523 | |
| 4139 | 4524 | fn writeSyntheticSections(self: *Elf) !void { |
| 4140 | 4525 | const gpa = self.base.allocator; |
| 4141 | 4526 | |
| 4527 | if (self.interp_section_index) |shndx| { | |
| 4528 | const shdr = self.shdrs.items[shndx]; | |
| 4529 | var buffer = try gpa.alloc(u8, shdr.sh_size); | |
| 4530 | defer gpa.free(buffer); | |
| 4531 | const dylinker = self.base.options.dynamic_linker.?; | |
| 4532 | @memcpy(buffer[0..dylinker.len], dylinker); | |
| 4533 | buffer[dylinker.len] = 0; | |
| 4534 | try self.base.file.?.pwriteAll(buffer, shdr.sh_offset); | |
| 4535 | } | |
| 4536 | ||
| 4537 | if (self.hash_section_index) |shndx| { | |
| 4538 | const shdr = self.shdrs.items[shndx]; | |
| 4539 | try self.base.file.?.pwriteAll(self.hash.buffer.items, shdr.sh_offset); | |
| 4540 | } | |
| 4541 | ||
| 4542 | if (self.gnu_hash_section_index) |shndx| { | |
| 4543 | const shdr = self.shdrs.items[shndx]; | |
| 4544 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size()); | |
| 4545 | defer buffer.deinit(); | |
| 4546 | try self.gnu_hash.write(self, buffer.writer()); | |
| 4547 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | |
| 4548 | } | |
| 4549 | ||
| 4550 | if (self.versym_section_index) |shndx| { | |
| 4551 | const shdr = self.shdrs.items[shndx]; | |
| 4552 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset); | |
| 4553 | } | |
| 4554 | ||
| 4555 | if (self.verneed_section_index) |shndx| { | |
| 4556 | const shdr = self.shdrs.items[shndx]; | |
| 4557 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size()); | |
| 4558 | defer buffer.deinit(); | |
| 4559 | try self.verneed.write(buffer.writer()); | |
| 4560 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | |
| 4561 | } | |
| 4562 | ||
| 4563 | if (self.dynamic_section_index) |shndx| { | |
| 4564 | const shdr = self.shdrs.items[shndx]; | |
| 4565 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self)); | |
| 4566 | defer buffer.deinit(); | |
| 4567 | try self.dynamic.write(self, buffer.writer()); | |
| 4568 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | |
| 4569 | } | |
| 4570 | ||
| 4571 | if (self.dynsymtab_section_index) |shndx| { | |
| 4572 | const shdr = self.shdrs.items[shndx]; | |
| 4573 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size()); | |
| 4574 | defer buffer.deinit(); | |
| 4575 | try self.dynsym.write(self, buffer.writer()); | |
| 4576 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | |
| 4577 | } | |
| 4578 | ||
| 4579 | if (self.dynstrtab_section_index) |shndx| { | |
| 4580 | const shdr = self.shdrs.items[shndx]; | |
| 4581 | try self.base.file.?.pwriteAll(self.dynstrtab.buffer.items, shdr.sh_offset); | |
| 4582 | } | |
| 4583 | ||
| 4142 | 4584 | if (self.eh_frame_section_index) |shndx| { |
| 4143 | 4585 | const shdr = self.shdrs.items[shndx]; |
| 4144 | 4586 | var buffer = try std.ArrayList(u8).initCapacity(gpa, shdr.sh_size); |
| ... | ... | @@ -4163,6 +4605,44 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 4163 | 4605 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); |
| 4164 | 4606 | } |
| 4165 | 4607 | |
| 4608 | if (self.rela_dyn_section_index) |shndx| { | |
| 4609 | const shdr = self.shdrs.items[shndx]; | |
| 4610 | try self.got.addRela(self); | |
| 4611 | try self.copy_rel.addRela(self); | |
| 4612 | self.sortRelaDyn(); | |
| 4613 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset); | |
| 4614 | } | |
| 4615 | ||
| 4616 | if (self.plt_section_index) |shndx| { | |
| 4617 | const shdr = self.shdrs.items[shndx]; | |
| 4618 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size()); | |
| 4619 | defer buffer.deinit(); | |
| 4620 | try self.plt.write(self, buffer.writer()); | |
| 4621 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | |
| 4622 | } | |
| 4623 | ||
| 4624 | if (self.got_plt_section_index) |shndx| { | |
| 4625 | const shdr = self.shdrs.items[shndx]; | |
| 4626 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self)); | |
| 4627 | defer buffer.deinit(); | |
| 4628 | try self.got_plt.write(self, buffer.writer()); | |
| 4629 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | |
| 4630 | } | |
| 4631 | ||
| 4632 | if (self.plt_got_section_index) |shndx| { | |
| 4633 | const shdr = self.shdrs.items[shndx]; | |
| 4634 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size()); | |
| 4635 | defer buffer.deinit(); | |
| 4636 | try self.plt_got.write(self, buffer.writer()); | |
| 4637 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | |
| 4638 | } | |
| 4639 | ||
| 4640 | if (self.rela_plt_section_index) |shndx| { | |
| 4641 | const shdr = self.shdrs.items[shndx]; | |
| 4642 | try self.plt.addRela(self); | |
| 4643 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset); | |
| 4644 | } | |
| 4645 | ||
| 4166 | 4646 | if (self.shstrtab_section_index) |index| { |
| 4167 | 4647 | const shdr = self.shdrs.items[index]; |
| 4168 | 4648 | try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shdr.sh_offset); |
| ... | ... | @@ -4213,11 +4693,27 @@ fn writeSymtab(self: *Elf) !void { |
| 4213 | 4693 | ctx.iglobal += object.output_symtab_size.nglobals; |
| 4214 | 4694 | } |
| 4215 | 4695 | |
| 4696 | for (self.shared_objects.items) |index| { | |
| 4697 | const shared_object = self.file(index).?.shared_object; | |
| 4698 | shared_object.writeSymtab(self, ctx); | |
| 4699 | ctx.iglobal += shared_object.output_symtab_size.nglobals; | |
| 4700 | } | |
| 4701 | ||
| 4216 | 4702 | if (self.got_section_index) |_| { |
| 4217 | 4703 | try self.got.writeSymtab(self, ctx); |
| 4218 | 4704 | ctx.ilocal += self.got.output_symtab_size.nlocals; |
| 4219 | 4705 | } |
| 4220 | 4706 | |
| 4707 | if (self.plt_section_index) |_| { | |
| 4708 | try self.plt.writeSymtab(self, ctx); | |
| 4709 | ctx.ilocal += self.plt.output_symtab_size.nlocals; | |
| 4710 | } | |
| 4711 | ||
| 4712 | if (self.plt_got_section_index) |_| { | |
| 4713 | try self.plt_got.writeSymtab(self, ctx); | |
| 4714 | ctx.ilocal += self.plt_got.output_symtab_size.nlocals; | |
| 4715 | } | |
| 4716 | ||
| 4221 | 4717 | if (self.linker_defined_index) |index| { |
| 4222 | 4718 | const linker_defined = self.file(index).?.linker_defined; |
| 4223 | 4719 | linker_defined.writeSymtab(self, ctx); |
| ... | ... | @@ -4576,7 +5072,7 @@ pub fn isStatic(self: Elf) bool { |
| 4576 | 5072 | } |
| 4577 | 5073 | |
| 4578 | 5074 | pub fn isDynLib(self: Elf) bool { |
| 4579 | return self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic; | |
| 5075 | return self.base.options.effectiveOutputMode() == .Lib and self.base.options.link_mode == .Dynamic; | |
| 4580 | 5076 | } |
| 4581 | 5077 | |
| 4582 | 5078 | fn addPhdr(self: *Elf, opts: struct { |
| ... | ... | @@ -4723,6 +5219,7 @@ pub fn file(self: *Elf, index: File.Index) ?File { |
| 4723 | 5219 | .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined }, |
| 4724 | 5220 | .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module }, |
| 4725 | 5221 | .object => .{ .object = &self.files.items(.data)[index].object }, |
| 5222 | .shared_object => .{ .shared_object = &self.files.items(.data)[index].shared_object }, | |
| 4726 | 5223 | }; |
| 4727 | 5224 | } |
| 4728 | 5225 | |
| ... | ... | @@ -5077,6 +5574,16 @@ fn fmtDumpState( |
| 5077 | 5574 | }); |
| 5078 | 5575 | } |
| 5079 | 5576 | |
| 5577 | for (self.shared_objects.items) |index| { | |
| 5578 | const shared_object = self.file(index).?.shared_object; | |
| 5579 | try writer.print("shared_object({d}) : ", .{index}); | |
| 5580 | try writer.print("{s}", .{shared_object.path}); | |
| 5581 | try writer.print(" : needed({})", .{shared_object.needed}); | |
| 5582 | if (!shared_object.alive) try writer.writeAll(" : [*]"); | |
| 5583 | try writer.writeByte('\n'); | |
| 5584 | try writer.print("{}\n", .{shared_object.fmtSymtab(self)}); | |
| 5585 | } | |
| 5586 | ||
| 5080 | 5587 | if (self.linker_defined_index) |index| { |
| 5081 | 5588 | const linker_defined = self.file(index).?.linker_defined; |
| 5082 | 5589 | try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); |
| ... | ... | @@ -5227,10 +5734,16 @@ const Archive = @import("Elf/Archive.zig"); |
| 5227 | 5734 | pub const Atom = @import("Elf/Atom.zig"); |
| 5228 | 5735 | const Cache = std.Build.Cache; |
| 5229 | 5736 | const Compilation = @import("../Compilation.zig"); |
| 5737 | const CopyRelSection = synthetic_sections.CopyRelSection; | |
| 5738 | const DynamicSection = synthetic_sections.DynamicSection; | |
| 5739 | const DynsymSection = synthetic_sections.DynsymSection; | |
| 5230 | 5740 | const Dwarf = @import("Dwarf.zig"); |
| 5231 | 5741 | const Elf = @This(); |
| 5232 | 5742 | const File = @import("Elf/file.zig").File; |
| 5743 | const GnuHashSection = synthetic_sections.GnuHashSection; | |
| 5233 | 5744 | const GotSection = synthetic_sections.GotSection; |
| 5745 | const GotPltSection = synthetic_sections.GotPltSection; | |
| 5746 | const HashSection = synthetic_sections.HashSection; | |
| 5234 | 5747 | const LinkerDefined = @import("Elf/LinkerDefined.zig"); |
| 5235 | 5748 | const Liveness = @import("../Liveness.zig"); |
| 5236 | 5749 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| ... | ... | @@ -5238,10 +5751,14 @@ const Module = @import("../Module.zig"); |
| 5238 | 5751 | const Object = @import("Elf/Object.zig"); |
| 5239 | 5752 | const InternPool = @import("../InternPool.zig"); |
| 5240 | 5753 | const Package = @import("../Package.zig"); |
| 5754 | const PltSection = synthetic_sections.PltSection; | |
| 5755 | const PltGotSection = synthetic_sections.PltGotSection; | |
| 5756 | const SharedObject = @import("Elf/SharedObject.zig"); | |
| 5241 | 5757 | const Symbol = @import("Elf/Symbol.zig"); |
| 5242 | 5758 | const StringTable = @import("strtab.zig").StringTable; |
| 5243 | 5759 | const TableSection = @import("table_section.zig").TableSection; |
| 5244 | 5760 | const Type = @import("../type.zig").Type; |
| 5245 | 5761 | const TypedValue = @import("../TypedValue.zig"); |
| 5246 | 5762 | const Value = @import("../value.zig").Value; |
| 5763 | const VerneedSection = synthetic_sections.VerneedSection; | |
| 5247 | 5764 | const ZigModule = @import("Elf/ZigModule.zig"); |
src/link/Elf/SharedObject.zig created+361| ... | ... | @@ -0,0 +1,361 @@ |
| 1 | path: []const u8, | |
| 2 | data: []const u8, | |
| 3 | index: File.Index, | |
| 4 | ||
| 5 | header: ?elf.Elf64_Ehdr = null, | |
| 6 | shdrs: std.ArrayListUnmanaged(ElfShdr) = .{}, | |
| 7 | symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, | |
| 8 | strtab: []const u8 = &[0]u8{}, | |
| 9 | /// Version symtab contains version strings of the symbols if present. | |
| 10 | versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, | |
| 11 | verstrings: std.ArrayListUnmanaged(u32) = .{}, | |
| 12 | ||
| 13 | dynamic_sect_index: ?u16 = null, | |
| 14 | versym_sect_index: ?u16 = null, | |
| 15 | verdef_sect_index: ?u16 = null, | |
| 16 | ||
| 17 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | |
| 18 | aliases: ?std.ArrayListUnmanaged(u32) = null, | |
| 19 | ||
| 20 | needed: bool, | |
| 21 | alive: bool, | |
| 22 | ||
| 23 | output_symtab_size: Elf.SymtabSize = .{}, | |
| 24 | ||
| 25 | pub fn isSharedObject(file: std.fs.File) bool { | |
| 26 | const reader = file.reader(); | |
| 27 | const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; | |
| 28 | defer file.seekTo(0) catch {}; | |
| 29 | if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false; | |
| 30 | if (header.e_ident[elf.EI_VERSION] != 1) return false; | |
| 31 | if (header.e_type != elf.ET.DYN) return false; | |
| 32 | return true; | |
| 33 | } | |
| 34 | ||
| 35 | pub fn deinit(self: *SharedObject, allocator: Allocator) void { | |
| 36 | self.versyms.deinit(allocator); | |
| 37 | self.verstrings.deinit(allocator); | |
| 38 | self.symbols.deinit(allocator); | |
| 39 | if (self.aliases) |*aliases| aliases.deinit(allocator); | |
| 40 | self.shdrs.deinit(allocator); | |
| 41 | } | |
| 42 | ||
| 43 | pub fn parse(self: *SharedObject, elf_file: *Elf) !void { | |
| 44 | const gpa = elf_file.base.allocator; | |
| 45 | var stream = std.io.fixedBufferStream(self.data); | |
| 46 | const reader = stream.reader(); | |
| 47 | ||
| 48 | self.header = try reader.readStruct(elf.Elf64_Ehdr); | |
| 49 | ||
| 50 | var dynsym_index: ?u16 = null; | |
| 51 | const shdrs = @as( | |
| 52 | [*]align(1) const elf.Elf64_Shdr, | |
| 53 | @ptrCast(self.data.ptr + self.header.?.e_shoff), | |
| 54 | )[0..self.header.?.e_shnum]; | |
| 55 | try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len); | |
| 56 | ||
| 57 | for (shdrs, 0..) |shdr, i| { | |
| 58 | self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr)); | |
| 59 | switch (shdr.sh_type) { | |
| 60 | elf.SHT_DYNSYM => dynsym_index = @as(u16, @intCast(i)), | |
| 61 | elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)), | |
| 62 | elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)), | |
| 63 | elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)), | |
| 64 | else => {}, | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | if (dynsym_index) |index| { | |
| 69 | const shdr = self.shdrs.items[index]; | |
| 70 | const symtab = self.shdrContents(index); | |
| 71 | const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym)); | |
| 72 | self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms]; | |
| 73 | self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link))); | |
| 74 | } | |
| 75 | ||
| 76 | try self.parseVersions(elf_file); | |
| 77 | try self.initSymtab(elf_file); | |
| 78 | } | |
| 79 | ||
| 80 | fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { | |
| 81 | const gpa = elf_file.base.allocator; | |
| 82 | ||
| 83 | try self.verstrings.resize(gpa, 2); | |
| 84 | self.verstrings.items[elf.VER_NDX_LOCAL] = 0; | |
| 85 | self.verstrings.items[elf.VER_NDX_GLOBAL] = 0; | |
| 86 | ||
| 87 | if (self.verdef_sect_index) |shndx| { | |
| 88 | const verdefs = self.shdrContents(shndx); | |
| 89 | const nverdefs = self.verdefNum(); | |
| 90 | try self.verstrings.resize(gpa, self.verstrings.items.len + nverdefs); | |
| 91 | ||
| 92 | var i: u32 = 0; | |
| 93 | var offset: u32 = 0; | |
| 94 | while (i < nverdefs) : (i += 1) { | |
| 95 | const verdef = @as(*align(1) const elf.Elf64_Verdef, @ptrCast(verdefs.ptr + offset)).*; | |
| 96 | defer offset += verdef.vd_next; | |
| 97 | if (verdef.vd_flags == elf.VER_FLG_BASE) continue; // Skip BASE entry | |
| 98 | const vda_name = if (verdef.vd_cnt > 0) | |
| 99 | @as(*align(1) const elf.Elf64_Verdaux, @ptrCast(verdefs.ptr + offset + verdef.vd_aux)).vda_name | |
| 100 | else | |
| 101 | 0; | |
| 102 | self.verstrings.items[verdef.vd_ndx] = vda_name; | |
| 103 | } | |
| 104 | } | |
| 105 | ||
| 106 | try self.versyms.ensureTotalCapacityPrecise(gpa, self.symtab.len); | |
| 107 | ||
| 108 | if (self.versym_sect_index) |shndx| { | |
| 109 | const versyms_raw = self.shdrContents(shndx); | |
| 110 | const nversyms = @divExact(versyms_raw.len, @sizeOf(elf.Elf64_Versym)); | |
| 111 | const versyms = @as([*]align(1) const elf.Elf64_Versym, @ptrCast(versyms_raw.ptr))[0..nversyms]; | |
| 112 | for (versyms) |ver| { | |
| 113 | const normalized_ver = if (ver & elf.VERSYM_VERSION >= self.verstrings.items.len - 1) | |
| 114 | elf.VER_NDX_GLOBAL | |
| 115 | else | |
| 116 | ver; | |
| 117 | self.versyms.appendAssumeCapacity(normalized_ver); | |
| 118 | } | |
| 119 | } else for (0..self.symtab.len) |_| { | |
| 120 | self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL); | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | fn initSymtab(self: *SharedObject, elf_file: *Elf) !void { | |
| 125 | const gpa = elf_file.base.allocator; | |
| 126 | ||
| 127 | try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len); | |
| 128 | ||
| 129 | for (self.symtab, 0..) |sym, i| { | |
| 130 | const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0; | |
| 131 | const name = self.getString(sym.st_name); | |
| 132 | // We need to garble up the name so that we don't pick this symbol | |
| 133 | // during symbol resolution. Thank you GNU! | |
| 134 | const off = if (hidden) blk: { | |
| 135 | const full_name = try std.fmt.allocPrint(gpa, "{s}@{s}", .{ | |
| 136 | name, | |
| 137 | self.versionString(self.versyms.items[i]), | |
| 138 | }); | |
| 139 | defer gpa.free(full_name); | |
| 140 | break :blk try elf_file.strtab.insert(gpa, full_name); | |
| 141 | } else try elf_file.strtab.insert(gpa, name); | |
| 142 | const gop = try elf_file.getOrCreateGlobal(off); | |
| 143 | self.symbols.addOneAssumeCapacity().* = gop.index; | |
| 144 | } | |
| 145 | } | |
| 146 | ||
| 147 | pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void { | |
| 148 | for (self.globals(), 0..) |index, i| { | |
| 149 | const esym_index = @as(u32, @intCast(i)); | |
| 150 | const this_sym = self.symtab[esym_index]; | |
| 151 | ||
| 152 | if (this_sym.st_shndx == elf.SHN_UNDEF) continue; | |
| 153 | ||
| 154 | const global = elf_file.symbol(index); | |
| 155 | if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) { | |
| 156 | global.value = this_sym.st_value; | |
| 157 | global.atom_index = 0; | |
| 158 | global.esym_index = esym_index; | |
| 159 | global.version_index = self.versyms.items[esym_index]; | |
| 160 | global.file_index = self.index; | |
| 161 | } | |
| 162 | } | |
| 163 | } | |
| 164 | ||
| 165 | pub fn resetGlobals(self: *SharedObject, elf_file: *Elf) void { | |
| 166 | for (self.globals()) |index| { | |
| 167 | const global = elf_file.symbol(index); | |
| 168 | const off = global.name_offset; | |
| 169 | global.* = .{}; | |
| 170 | global.name_offset = off; | |
| 171 | } | |
| 172 | } | |
| 173 | ||
| 174 | pub fn markLive(self: *SharedObject, elf_file: *Elf) void { | |
| 175 | for (self.globals(), 0..) |index, i| { | |
| 176 | const sym = self.symtab[i]; | |
| 177 | if (sym.st_shndx != elf.SHN_UNDEF) continue; | |
| 178 | ||
| 179 | const global = elf_file.symbol(index); | |
| 180 | const file = global.file(elf_file) orelse continue; | |
| 181 | const should_drop = switch (file) { | |
| 182 | .shared_object => |sh| !sh.needed and sym.st_bind() == elf.STB_WEAK, | |
| 183 | else => false, | |
| 184 | }; | |
| 185 | if (!should_drop and !file.isAlive()) { | |
| 186 | file.setAlive(); | |
| 187 | file.markLive(elf_file); | |
| 188 | } | |
| 189 | } | |
| 190 | } | |
| 191 | ||
| 192 | pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void { | |
| 193 | for (self.globals()) |global_index| { | |
| 194 | const global = elf_file.symbol(global_index); | |
| 195 | if (global.file(elf_file)) |file| if (file.index() != self.index) continue; | |
| 196 | if (global.isLocal()) continue; | |
| 197 | global.flags.output_symtab = true; | |
| 198 | self.output_symtab_size.nglobals += 1; | |
| 199 | } | |
| 200 | } | |
| 201 | ||
| 202 | pub fn writeSymtab(self: *SharedObject, elf_file: *Elf, ctx: anytype) void { | |
| 203 | var iglobal = ctx.iglobal; | |
| 204 | for (self.globals()) |global_index| { | |
| 205 | const global = elf_file.symbol(global_index); | |
| 206 | if (global.file(elf_file)) |file| if (file.index() != self.index) continue; | |
| 207 | if (!global.flags.output_symtab) continue; | |
| 208 | global.setOutputSym(elf_file, &ctx.symtab[iglobal]); | |
| 209 | iglobal += 1; | |
| 210 | } | |
| 211 | } | |
| 212 | ||
| 213 | pub fn globals(self: SharedObject) []const Symbol.Index { | |
| 214 | return self.symbols.items; | |
| 215 | } | |
| 216 | ||
| 217 | pub fn shdrContents(self: SharedObject, index: u16) []const u8 { | |
| 218 | const shdr = self.shdrs.items[index]; | |
| 219 | return self.data[shdr.sh_offset..][0..shdr.sh_size]; | |
| 220 | } | |
| 221 | ||
| 222 | pub fn getString(self: SharedObject, off: u32) [:0]const u8 { | |
| 223 | assert(off < self.strtab.len); | |
| 224 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0); | |
| 225 | } | |
| 226 | ||
| 227 | pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 { | |
| 228 | const off = self.verstrings.items[index & elf.VERSYM_VERSION]; | |
| 229 | return self.getString(off); | |
| 230 | } | |
| 231 | ||
| 232 | pub fn asFile(self: *SharedObject) File { | |
| 233 | return .{ .shared_object = self }; | |
| 234 | } | |
| 235 | ||
| 236 | fn dynamicTable(self: *SharedObject) []align(1) const elf.Elf64_Dyn { | |
| 237 | const shndx = self.dynamic_sect_index orelse return &[0]elf.Elf64_Dyn{}; | |
| 238 | const raw = self.shdrContents(shndx); | |
| 239 | const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn)); | |
| 240 | return @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num]; | |
| 241 | } | |
| 242 | ||
| 243 | fn verdefNum(self: *SharedObject) u32 { | |
| 244 | const entries = self.dynamicTable(); | |
| 245 | for (entries) |entry| switch (entry.d_tag) { | |
| 246 | elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)), | |
| 247 | else => {}, | |
| 248 | }; | |
| 249 | return 0; | |
| 250 | } | |
| 251 | ||
| 252 | pub fn soname(self: *SharedObject) []const u8 { | |
| 253 | const entries = self.dynamicTable(); | |
| 254 | for (entries) |entry| switch (entry.d_tag) { | |
| 255 | elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))), | |
| 256 | else => {}, | |
| 257 | }; | |
| 258 | return std.fs.path.basename(self.path); | |
| 259 | } | |
| 260 | ||
| 261 | pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { | |
| 262 | assert(self.aliases == null); | |
| 263 | ||
| 264 | const SortAlias = struct { | |
| 265 | pub fn lessThan(ctx: *Elf, lhs: Symbol.Index, rhs: Symbol.Index) bool { | |
| 266 | const lhs_sym = ctx.symbol(lhs).elfSym(ctx); | |
| 267 | const rhs_sym = ctx.symbol(rhs).elfSym(ctx); | |
| 268 | return lhs_sym.st_value < rhs_sym.st_value; | |
| 269 | } | |
| 270 | }; | |
| 271 | ||
| 272 | const gpa = elf_file.base.allocator; | |
| 273 | var aliases = std.ArrayList(Symbol.Index).init(gpa); | |
| 274 | defer aliases.deinit(); | |
| 275 | try aliases.ensureTotalCapacityPrecise(self.globals().len); | |
| 276 | ||
| 277 | for (self.globals()) |index| { | |
| 278 | const global = elf_file.symbol(index); | |
| 279 | const global_file = global.file(elf_file) orelse continue; | |
| 280 | if (global_file.index() != self.index) continue; | |
| 281 | aliases.appendAssumeCapacity(index); | |
| 282 | } | |
| 283 | ||
| 284 | std.mem.sort(u32, aliases.items, elf_file, SortAlias.lessThan); | |
| 285 | ||
| 286 | self.aliases = aliases.moveToUnmanaged(); | |
| 287 | } | |
| 288 | ||
| 289 | pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u32 { | |
| 290 | assert(self.aliases != null); | |
| 291 | ||
| 292 | const symbol = elf_file.symbol(index).elfSym(elf_file); | |
| 293 | const aliases = self.aliases.?; | |
| 294 | ||
| 295 | const start = for (aliases.items, 0..) |alias, i| { | |
| 296 | const alias_sym = elf_file.symbol(alias).elfSym(elf_file); | |
| 297 | if (symbol.st_value == alias_sym.st_value) break i; | |
| 298 | } else aliases.items.len; | |
| 299 | ||
| 300 | const end = for (aliases.items[start..], 0..) |alias, i| { | |
| 301 | const alias_sym = elf_file.symbol(alias).elfSym(elf_file); | |
| 302 | if (symbol.st_value < alias_sym.st_value) break i + start; | |
| 303 | } else aliases.items.len; | |
| 304 | ||
| 305 | return aliases.items[start..end]; | |
| 306 | } | |
| 307 | ||
| 308 | pub fn format( | |
| 309 | self: SharedObject, | |
| 310 | comptime unused_fmt_string: []const u8, | |
| 311 | options: std.fmt.FormatOptions, | |
| 312 | writer: anytype, | |
| 313 | ) !void { | |
| 314 | _ = self; | |
| 315 | _ = unused_fmt_string; | |
| 316 | _ = options; | |
| 317 | _ = writer; | |
| 318 | @compileError("do not format shared objects directly"); | |
| 319 | } | |
| 320 | ||
| 321 | pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { | |
| 322 | return .{ .data = .{ | |
| 323 | .shared = self, | |
| 324 | .elf_file = elf_file, | |
| 325 | } }; | |
| 326 | } | |
| 327 | ||
| 328 | const FormatContext = struct { | |
| 329 | shared: SharedObject, | |
| 330 | elf_file: *Elf, | |
| 331 | }; | |
| 332 | ||
| 333 | fn formatSymtab( | |
| 334 | ctx: FormatContext, | |
| 335 | comptime unused_fmt_string: []const u8, | |
| 336 | options: std.fmt.FormatOptions, | |
| 337 | writer: anytype, | |
| 338 | ) !void { | |
| 339 | _ = unused_fmt_string; | |
| 340 | _ = options; | |
| 341 | const shared = ctx.shared; | |
| 342 | try writer.writeAll(" globals\n"); | |
| 343 | for (shared.symbols.items) |index| { | |
| 344 | const global = ctx.elf_file.symbol(index); | |
| 345 | try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); | |
| 346 | } | |
| 347 | } | |
| 348 | ||
| 349 | const SharedObject = @This(); | |
| 350 | ||
| 351 | const std = @import("std"); | |
| 352 | const assert = std.debug.assert; | |
| 353 | const elf = std.elf; | |
| 354 | const log = std.log.scoped(.elf); | |
| 355 | const mem = std.mem; | |
| 356 | ||
| 357 | const Allocator = mem.Allocator; | |
| 358 | const Elf = @import("../Elf.zig"); | |
| 359 | const ElfShdr = @import("Object.zig").ElfShdr; | |
| 360 | const File = @import("file.zig").File; | |
| 361 | const Symbol = @import("Symbol.zig"); |
src/link/Elf/Symbol.zig+75-47| ... | ... | @@ -32,7 +32,7 @@ extra_index: u32 = 0, |
| 32 | 32 | |
| 33 | 33 | pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { |
| 34 | 34 | const file_ptr = symbol.file(elf_file).?; |
| 35 | // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; | |
| 35 | if (file_ptr == .shared_object) return symbol.elfSym(elf_file).st_shndx == elf.SHN_ABS; | |
| 36 | 36 | return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.outputShndx() == null and |
| 37 | 37 | file_ptr != .linker_defined; |
| 38 | 38 | } |
| ... | ... | @@ -52,8 +52,8 @@ pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { |
| 52 | 52 | |
| 53 | 53 | pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { |
| 54 | 54 | const s_sym = symbol.elfSym(elf_file); |
| 55 | // const file_ptr = symbol.file(elf_file).?; | |
| 56 | // if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC; | |
| 55 | const file_ptr = symbol.file(elf_file).?; | |
| 56 | if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared_object) return elf.STT_FUNC; | |
| 57 | 57 | return s_sym.st_type(); |
| 58 | 58 | } |
| 59 | 59 | |
| ... | ... | @@ -74,7 +74,7 @@ pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { |
| 74 | 74 | switch (file_ptr) { |
| 75 | 75 | .zig_module => |x| return x.elfSym(symbol.esym_index).*, |
| 76 | 76 | .linker_defined => |x| return x.symtab.items[symbol.esym_index], |
| 77 | .object => |x| return x.symtab[symbol.esym_index], | |
| 77 | inline else => |x| return x.symtab[symbol.esym_index], | |
| 78 | 78 | } |
| 79 | 79 | } |
| 80 | 80 | |
| ... | ... | @@ -88,23 +88,18 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { |
| 88 | 88 | return file_ptr.symbolRank(sym, in_archive); |
| 89 | 89 | } |
| 90 | 90 | |
| 91 | pub fn address(symbol: Symbol, opts: struct { | |
| 92 | plt: bool = true, | |
| 93 | }, elf_file: *Elf) u64 { | |
| 94 | _ = elf_file; | |
| 95 | _ = opts; | |
| 96 | // if (symbol.flags.copy_rel) { | |
| 97 | // return elf_file.sectionAddress(elf_file.copy_rel_sect_index.?) + symbol.value; | |
| 98 | // } | |
| 99 | // if (symbol.flags.plt and opts.plt) { | |
| 100 | // const extra = symbol.getExtra(elf_file).?; | |
| 101 | // if (!symbol.flags.is_canonical and symbol.flags.got) { | |
| 102 | // // We have a non-lazy bound function pointer, use that! | |
| 103 | // return elf_file.getPltGotEntryAddress(extra.plt_got); | |
| 104 | // } | |
| 105 | // // Lazy-bound function it is! | |
| 106 | // return elf_file.getPltEntryAddress(extra.plt); | |
| 107 | // } | |
| 91 | pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf) u64 { | |
| 92 | if (symbol.flags.has_copy_rel) { | |
| 93 | return symbol.copyRelAddress(elf_file); | |
| 94 | } | |
| 95 | if (symbol.flags.has_plt and opts.plt) { | |
| 96 | if (!symbol.flags.is_canonical and symbol.flags.has_got) { | |
| 97 | // We have a non-lazy bound function pointer, use that! | |
| 98 | return symbol.pltGotAddress(elf_file); | |
| 99 | } | |
| 100 | // Lazy-bound function it is! | |
| 101 | return symbol.pltAddress(elf_file); | |
| 102 | } | |
| 108 | 103 | return symbol.value; |
| 109 | 104 | } |
| 110 | 105 | |
| ... | ... | @@ -115,6 +110,33 @@ pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { |
| 115 | 110 | return entry.address(elf_file); |
| 116 | 111 | } |
| 117 | 112 | |
| 113 | pub fn pltGotAddress(symbol: Symbol, elf_file: *Elf) u64 { | |
| 114 | if (!(symbol.flags.has_plt and symbol.flags.has_got and !symbol.flags.is_canonical)) return 0; | |
| 115 | const extras = symbol.extra(elf_file).?; | |
| 116 | const shdr = elf_file.shdrs.items[elf_file.plt_got_section_index.?]; | |
| 117 | return shdr.sh_addr + extras.plt_got * 16; | |
| 118 | } | |
| 119 | ||
| 120 | pub fn pltAddress(symbol: Symbol, elf_file: *Elf) u64 { | |
| 121 | if (!symbol.flags.has_plt) return 0; | |
| 122 | const extras = symbol.extra(elf_file).?; | |
| 123 | const shdr = elf_file.shdrs.items[elf_file.plt_section_index.?]; | |
| 124 | return shdr.sh_addr + extras.plt * 16 + PltSection.preamble_size; | |
| 125 | } | |
| 126 | ||
| 127 | pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) u64 { | |
| 128 | if (!symbol.flags.has_plt) return 0; | |
| 129 | const extras = symbol.extra(elf_file).?; | |
| 130 | const shdr = elf_file.shdrs.items[elf_file.got_plt_section_index.?]; | |
| 131 | return shdr.sh_addr + extras.plt * 8 + GotPltSection.preamble_size; | |
| 132 | } | |
| 133 | ||
| 134 | pub fn copyRelAddress(symbol: Symbol, elf_file: *Elf) u64 { | |
| 135 | if (!symbol.flags.has_copy_rel) return 0; | |
| 136 | const shdr = elf_file.shdrs.items[elf_file.copy_rel_section_index.?]; | |
| 137 | return shdr.sh_addr + symbol.value; | |
| 138 | } | |
| 139 | ||
| 118 | 140 | const GetOrCreateGotEntryResult = struct { |
| 119 | 141 | found_existing: bool, |
| 120 | 142 | index: GotSection.Index, |
| ... | ... | @@ -149,17 +171,18 @@ pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 { |
| 149 | 171 | return entry.address(elf_file); |
| 150 | 172 | } |
| 151 | 173 | |
| 152 | // pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 { | |
| 153 | // const file = symbol.getFile(elf_file) orelse return 0; | |
| 154 | // const shared = file.shared; | |
| 155 | // const s_sym = symbol.getSourceSymbol(elf_file); | |
| 156 | // const shdr = shared.getShdrs()[s_sym.st_shndx]; | |
| 157 | // const alignment = @max(1, shdr.sh_addralign); | |
| 158 | // return if (s_sym.st_value == 0) | |
| 159 | // alignment | |
| 160 | // else | |
| 161 | // @min(alignment, try std.math.powi(u64, 2, @ctz(s_sym.st_value))); | |
| 162 | // } | |
| 174 | pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 { | |
| 175 | const file_ptr = symbol.file(elf_file) orelse return 0; | |
| 176 | assert(file_ptr == .shared_object); | |
| 177 | const shared_object = file_ptr.shared_object; | |
| 178 | const esym = symbol.elfSym(elf_file); | |
| 179 | const shdr = shared_object.shdrs.items[esym.st_shndx]; | |
| 180 | const alignment = @max(1, shdr.sh_addralign); | |
| 181 | return if (esym.st_value == 0) | |
| 182 | alignment | |
| 183 | else | |
| 184 | @min(alignment, try std.math.powi(u64, 2, @ctz(esym.st_value))); | |
| 185 | } | |
| 163 | 186 | |
| 164 | 187 | pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void { |
| 165 | 188 | symbol.extra_index = try elf_file.addSymbolExtra(extras); |
| ... | ... | @@ -183,22 +206,22 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { |
| 183 | 206 | const st_bind: u8 = blk: { |
| 184 | 207 | if (symbol.isLocal()) break :blk 0; |
| 185 | 208 | if (symbol.flags.weak) break :blk elf.STB_WEAK; |
| 186 | // if (file_ptr == .shared) break :blk elf.STB_GLOBAL; | |
| 209 | if (file_ptr == .shared_object) break :blk elf.STB_GLOBAL; | |
| 187 | 210 | break :blk esym.st_bind(); |
| 188 | 211 | }; |
| 189 | 212 | const st_shndx = blk: { |
| 190 | // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?; | |
| 191 | // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; | |
| 213 | if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; | |
| 214 | if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; | |
| 192 | 215 | if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) |
| 193 | 216 | break :blk elf.SHN_ABS; |
| 194 | 217 | break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; |
| 195 | 218 | }; |
| 196 | 219 | const st_value = blk: { |
| 197 | // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file); | |
| 198 | // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) { | |
| 199 | // if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file); | |
| 200 | // break :blk 0; | |
| 201 | // } | |
| 220 | if (symbol.flags.has_copy_rel) break :blk symbol.address(.{}, elf_file); | |
| 221 | if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) { | |
| 222 | if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file); | |
| 223 | break :blk 0; | |
| 224 | } | |
| 202 | 225 | if (st_shndx == elf.SHN_ABS) break :blk symbol.value; |
| 203 | 226 | const shdr = &elf_file.shdrs.items[st_shndx]; |
| 204 | 227 | if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined) |
| ... | ... | @@ -254,9 +277,10 @@ fn formatName( |
| 254 | 277 | switch (symbol.version_index & elf.VERSYM_VERSION) { |
| 255 | 278 | elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {}, |
| 256 | 279 | else => { |
| 257 | unreachable; | |
| 258 | // const shared = symbol.getFile(elf_file).?.shared; | |
| 259 | // try writer.print("@{s}", .{shared.getVersionString(symbol.version_index)}); | |
| 280 | const file_ptr = symbol.file(elf_file).?; | |
| 281 | assert(file_ptr == .shared_object); | |
| 282 | const shared_object = file_ptr.shared_object; | |
| 283 | try writer.print("@{s}", .{shared_object.versionString(symbol.version_index)}); | |
| 260 | 284 | }, |
| 261 | 285 | } |
| 262 | 286 | } |
| ... | ... | @@ -312,9 +336,12 @@ pub const Flags = packed struct { |
| 312 | 336 | /// Whether this symbol is weak. |
| 313 | 337 | weak: bool = false, |
| 314 | 338 | |
| 315 | /// Whether the symbol makes into the output symtab or not. | |
| 339 | /// Whether the symbol makes into the output symtab. | |
| 316 | 340 | output_symtab: bool = false, |
| 317 | 341 | |
| 342 | /// Whether the symbol has entry in dynamic symbol table. | |
| 343 | has_dynamic: bool = false, | |
| 344 | ||
| 318 | 345 | /// Whether the symbol contains GOT indirection. |
| 319 | 346 | needs_got: bool = false, |
| 320 | 347 | has_got: bool = false, |
| ... | ... | @@ -328,7 +355,6 @@ pub const Flags = packed struct { |
| 328 | 355 | /// Whether the symbol contains COPYREL directive. |
| 329 | 356 | needs_copy_rel: bool = false, |
| 330 | 357 | has_copy_rel: bool = false, |
| 331 | has_dynamic: bool = false, | |
| 332 | 358 | |
| 333 | 359 | /// Whether the symbol contains TLSGD indirection. |
| 334 | 360 | needs_tlsgd: bool = false, |
| ... | ... | @@ -365,8 +391,10 @@ const Atom = @import("Atom.zig"); |
| 365 | 391 | const Elf = @import("../Elf.zig"); |
| 366 | 392 | const File = @import("file.zig").File; |
| 367 | 393 | const GotSection = synthetic_sections.GotSection; |
| 394 | const GotPltSection = synthetic_sections.GotPltSection; | |
| 368 | 395 | const LinkerDefined = @import("LinkerDefined.zig"); |
| 369 | // const Object = @import("Object.zig"); | |
| 370 | // const SharedObject = @import("SharedObject.zig"); | |
| 396 | const Object = @import("Object.zig"); | |
| 397 | const PltSection = synthetic_sections.PltSection; | |
| 398 | const SharedObject = @import("SharedObject.zig"); | |
| 371 | 399 | const Symbol = @This(); |
| 372 | 400 | const ZigModule = @import("ZigModule.zig"); |
src/link/Elf/file.zig+7-6| ... | ... | @@ -2,7 +2,7 @@ pub const File = union(enum) { |
| 2 | 2 | zig_module: *ZigModule, |
| 3 | 3 | linker_defined: *LinkerDefined, |
| 4 | 4 | object: *Object, |
| 5 | // shared_object: *SharedObject, | |
| 5 | shared_object: *SharedObject, | |
| 6 | 6 | |
| 7 | 7 | pub fn index(file: File) Index { |
| 8 | 8 | return switch (file) { |
| ... | ... | @@ -26,7 +26,7 @@ pub const File = union(enum) { |
| 26 | 26 | .zig_module => |x| try writer.print("{s}", .{x.path}), |
| 27 | 27 | .linker_defined => try writer.writeAll("(linker defined)"), |
| 28 | 28 | .object => |x| try writer.print("{}", .{x.fmtPath()}), |
| 29 | // .shared_object => |x| try writer.writeAll(x.path), | |
| 29 | .shared_object => |x| try writer.writeAll(x.path), | |
| 30 | 30 | } |
| 31 | 31 | } |
| 32 | 32 | |
| ... | ... | @@ -49,8 +49,7 @@ pub const File = union(enum) { |
| 49 | 49 | pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 { |
| 50 | 50 | const base: u3 = blk: { |
| 51 | 51 | if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5; |
| 52 | // if (file == .shared or in_archive) break :blk switch (sym.st_bind()) { | |
| 53 | if (in_archive) break :blk switch (sym.st_bind()) { | |
| 52 | if (file == .shared_object or in_archive) break :blk switch (sym.st_bind()) { | |
| 54 | 53 | elf.STB_GLOBAL => 3, |
| 55 | 54 | else => 4, |
| 56 | 55 | }; |
| ... | ... | @@ -92,6 +91,7 @@ pub const File = union(enum) { |
| 92 | 91 | pub fn atoms(file: File) []const Atom.Index { |
| 93 | 92 | return switch (file) { |
| 94 | 93 | .linker_defined => unreachable, |
| 94 | .shared_object => unreachable, | |
| 95 | 95 | .zig_module => |x| x.atoms.items, |
| 96 | 96 | .object => |x| x.atoms.items, |
| 97 | 97 | }; |
| ... | ... | @@ -100,6 +100,7 @@ pub const File = union(enum) { |
| 100 | 100 | pub fn locals(file: File) []const Symbol.Index { |
| 101 | 101 | return switch (file) { |
| 102 | 102 | .linker_defined => unreachable, |
| 103 | .shared_object => unreachable, | |
| 103 | 104 | inline else => |x| x.locals(), |
| 104 | 105 | }; |
| 105 | 106 | } |
| ... | ... | @@ -117,7 +118,7 @@ pub const File = union(enum) { |
| 117 | 118 | zig_module: ZigModule, |
| 118 | 119 | linker_defined: LinkerDefined, |
| 119 | 120 | object: Object, |
| 120 | // shared_object: SharedObject, | |
| 121 | shared_object: SharedObject, | |
| 121 | 122 | }; |
| 122 | 123 | }; |
| 123 | 124 | |
| ... | ... | @@ -129,6 +130,6 @@ const Atom = @import("Atom.zig"); |
| 129 | 130 | const Elf = @import("../Elf.zig"); |
| 130 | 131 | const LinkerDefined = @import("LinkerDefined.zig"); |
| 131 | 132 | const Object = @import("Object.zig"); |
| 132 | // const SharedObject = @import("SharedObject.zig"); | |
| 133 | const SharedObject = @import("SharedObject.zig"); | |
| 133 | 134 | const Symbol = @import("Symbol.zig"); |
| 134 | 135 | const ZigModule = @import("ZigModule.zig"); |
src/link/Elf/synthetic_sections.zig+1163-289| ... | ... | @@ -1,3 +1,225 @@ |
| 1 | pub const DynamicSection = struct { | |
| 2 | soname: ?u32 = null, | |
| 3 | needed: std.ArrayListUnmanaged(u32) = .{}, | |
| 4 | rpath: u32 = 0, | |
| 5 | ||
| 6 | pub fn deinit(dt: *DynamicSection, allocator: Allocator) void { | |
| 7 | dt.needed.deinit(allocator); | |
| 8 | } | |
| 9 | ||
| 10 | pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void { | |
| 11 | const gpa = elf_file.base.allocator; | |
| 12 | const off = try elf_file.dynstrtab.insert(gpa, shared.getSoname()); | |
| 13 | try dt.needed.append(gpa, off); | |
| 14 | } | |
| 15 | ||
| 16 | pub fn setRpath(dt: *DynamicSection, rpath_list: []const []const u8, elf_file: *Elf) !void { | |
| 17 | if (rpath_list.len == 0) return; | |
| 18 | const gpa = elf_file.base.allocator; | |
| 19 | var rpath = std.ArrayList(u8).init(gpa); | |
| 20 | defer rpath.deinit(); | |
| 21 | for (rpath_list, 0..) |path, i| { | |
| 22 | if (i > 0) try rpath.append(':'); | |
| 23 | try rpath.appendSlice(path); | |
| 24 | } | |
| 25 | dt.rpath = try elf_file.dynstrtab.insert(gpa, rpath.items); | |
| 26 | } | |
| 27 | ||
| 28 | pub fn setSoname(dt: *DynamicSection, soname: []const u8, elf_file: *Elf) !void { | |
| 29 | dt.soname = try elf_file.dynstrtab.insert(elf_file.base.allocator, soname); | |
| 30 | } | |
| 31 | ||
| 32 | fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 { | |
| 33 | _ = dt; | |
| 34 | var flags: u64 = 0; | |
| 35 | if (elf_file.base.options.z_now) { | |
| 36 | flags |= elf.DF_BIND_NOW; | |
| 37 | } | |
| 38 | for (elf_file.got.entries.items) |entry| switch (entry.tag) { | |
| 39 | .gottp => { | |
| 40 | flags |= elf.DF_STATIC_TLS; | |
| 41 | break; | |
| 42 | }, | |
| 43 | else => {}, | |
| 44 | }; | |
| 45 | if (elf_file.has_text_reloc) { | |
| 46 | flags |= elf.DF_TEXTREL; | |
| 47 | } | |
| 48 | return if (flags > 0) flags else null; | |
| 49 | } | |
| 50 | ||
| 51 | fn getFlags1(dt: DynamicSection, elf_file: *Elf) ?u64 { | |
| 52 | _ = dt; | |
| 53 | var flags_1: u64 = 0; | |
| 54 | if (elf_file.base.options.z_now) { | |
| 55 | flags_1 |= elf.DF_1_NOW; | |
| 56 | } | |
| 57 | if (elf_file.base.options.pie) { | |
| 58 | flags_1 |= elf.DF_1_PIE; | |
| 59 | } | |
| 60 | // if (elf_file.base.options.z_nodlopen) { | |
| 61 | // flags_1 |= elf.DF_1_NOOPEN; | |
| 62 | // } | |
| 63 | return if (flags_1 > 0) flags_1 else null; | |
| 64 | } | |
| 65 | ||
| 66 | pub fn size(dt: DynamicSection, elf_file: *Elf) usize { | |
| 67 | var nentries: usize = 0; | |
| 68 | nentries += dt.needed.items.len; // NEEDED | |
| 69 | if (dt.soname != null) nentries += 1; // SONAME | |
| 70 | if (dt.rpath > 0) nentries += 1; // RUNPATH | |
| 71 | if (elf_file.sectionByName(".init") != null) nentries += 1; // INIT | |
| 72 | if (elf_file.sectionByName(".fini") != null) nentries += 1; // FINI | |
| 73 | if (elf_file.sectionByName(".init_array") != null) nentries += 2; // INIT_ARRAY | |
| 74 | if (elf_file.sectionByName(".fini_array") != null) nentries += 2; // FINI_ARRAY | |
| 75 | if (elf_file.rela_dyn_section_index != null) nentries += 3; // RELA | |
| 76 | if (elf_file.rela_plt_section_index != null) nentries += 3; // JMPREL | |
| 77 | if (elf_file.got_plt_section_index != null) nentries += 1; // PLTGOT | |
| 78 | nentries += 1; // HASH | |
| 79 | if (elf_file.gnu_hash_section_index != null) nentries += 1; // GNU_HASH | |
| 80 | if (elf_file.has_text_reloc) nentries += 1; // TEXTREL | |
| 81 | nentries += 1; // SYMTAB | |
| 82 | nentries += 1; // SYMENT | |
| 83 | nentries += 1; // STRTAB | |
| 84 | nentries += 1; // STRSZ | |
| 85 | if (elf_file.versym_section_index != null) nentries += 1; // VERSYM | |
| 86 | if (elf_file.verneed_section_index != null) nentries += 2; // VERNEED | |
| 87 | if (dt.getFlags(elf_file) != null) nentries += 1; // FLAGS | |
| 88 | if (dt.getFlags1(elf_file) != null) nentries += 1; // FLAGS_1 | |
| 89 | if (!elf_file.isDynLib()) nentries += 1; // DEBUG | |
| 90 | nentries += 1; // NULL | |
| 91 | return nentries * @sizeOf(elf.Elf64_Dyn); | |
| 92 | } | |
| 93 | ||
| 94 | pub fn write(dt: DynamicSection, elf_file: *Elf, writer: anytype) !void { | |
| 95 | // NEEDED | |
| 96 | for (dt.needed.items) |off| { | |
| 97 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_NEEDED, .d_val = off }); | |
| 98 | } | |
| 99 | ||
| 100 | if (dt.soname) |off| { | |
| 101 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_SONAME, .d_val = off }); | |
| 102 | } | |
| 103 | ||
| 104 | // RUNPATH | |
| 105 | // TODO add option in Options to revert to old RPATH tag | |
| 106 | if (dt.rpath > 0) { | |
| 107 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RUNPATH, .d_val = dt.rpath }); | |
| 108 | } | |
| 109 | ||
| 110 | // INIT | |
| 111 | if (elf_file.sectionByName(".init")) |shndx| { | |
| 112 | const addr = elf_file.shdrs.items[shndx].sh_addr; | |
| 113 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_INIT, .d_val = addr }); | |
| 114 | } | |
| 115 | ||
| 116 | // FINI | |
| 117 | if (elf_file.sectionByName(".fini")) |shndx| { | |
| 118 | const addr = elf_file.shdrs.items[shndx].sh_addr; | |
| 119 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FINI, .d_val = addr }); | |
| 120 | } | |
| 121 | ||
| 122 | // INIT_ARRAY | |
| 123 | if (elf_file.sectionByName(".init_array")) |shndx| { | |
| 124 | const shdr = elf_file.shdrs.items[shndx]; | |
| 125 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_INIT_ARRAY, .d_val = shdr.sh_addr }); | |
| 126 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_INIT_ARRAYSZ, .d_val = shdr.sh_size }); | |
| 127 | } | |
| 128 | ||
| 129 | // FINI_ARRAY | |
| 130 | if (elf_file.sectionByName(".fini_array")) |shndx| { | |
| 131 | const shdr = elf_file.shdrs.items[shndx]; | |
| 132 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FINI_ARRAY, .d_val = shdr.sh_addr }); | |
| 133 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FINI_ARRAYSZ, .d_val = shdr.sh_size }); | |
| 134 | } | |
| 135 | ||
| 136 | // RELA | |
| 137 | if (elf_file.rela_dyn_section_index) |shndx| { | |
| 138 | const shdr = elf_file.shdrs.items[shndx]; | |
| 139 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RELA, .d_val = shdr.sh_addr }); | |
| 140 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RELASZ, .d_val = shdr.sh_size }); | |
| 141 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RELAENT, .d_val = shdr.sh_entsize }); | |
| 142 | } | |
| 143 | ||
| 144 | // JMPREL | |
| 145 | if (elf_file.rela_plt_section_index) |shndx| { | |
| 146 | const shdr = elf_file.shdrs.items[shndx]; | |
| 147 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_JMPREL, .d_val = shdr.sh_addr }); | |
| 148 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_PLTRELSZ, .d_val = shdr.sh_size }); | |
| 149 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_PLTREL, .d_val = elf.DT_RELA }); | |
| 150 | } | |
| 151 | ||
| 152 | // PLTGOT | |
| 153 | if (elf_file.got_plt_section_index) |shndx| { | |
| 154 | const addr = elf_file.shdrs.items[shndx].sh_addr; | |
| 155 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_PLTGOT, .d_val = addr }); | |
| 156 | } | |
| 157 | ||
| 158 | { | |
| 159 | assert(elf_file.hash_section_index != null); | |
| 160 | const addr = elf_file.shdrs.items[elf_file.hash_section_index.?].sh_addr; | |
| 161 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_HASH, .d_val = addr }); | |
| 162 | } | |
| 163 | ||
| 164 | if (elf_file.gnu_hash_section_index) |shndx| { | |
| 165 | const addr = elf_file.shdrs.items[shndx].sh_addr; | |
| 166 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_GNU_HASH, .d_val = addr }); | |
| 167 | } | |
| 168 | ||
| 169 | // TEXTREL | |
| 170 | if (elf_file.has_text_reloc) { | |
| 171 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_TEXTREL, .d_val = 0 }); | |
| 172 | } | |
| 173 | ||
| 174 | // SYMTAB + SYMENT | |
| 175 | { | |
| 176 | assert(elf_file.dynsymtab_section_index != null); | |
| 177 | const shdr = elf_file.shdrs.items[elf_file.dynsymtab_section_index.?]; | |
| 178 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_SYMTAB, .d_val = shdr.sh_addr }); | |
| 179 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_SYMENT, .d_val = shdr.sh_entsize }); | |
| 180 | } | |
| 181 | ||
| 182 | // STRTAB + STRSZ | |
| 183 | { | |
| 184 | assert(elf_file.dynstrtab_section_index != null); | |
| 185 | const shdr = elf_file.shdrs.items[elf_file.dynstrtab_section_index.?]; | |
| 186 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_STRTAB, .d_val = shdr.sh_addr }); | |
| 187 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_STRSZ, .d_val = shdr.sh_size }); | |
| 188 | } | |
| 189 | ||
| 190 | // VERSYM | |
| 191 | if (elf_file.versym_section_index) |shndx| { | |
| 192 | const addr = elf_file.shdrs.items[shndx].sh_addr; | |
| 193 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_VERSYM, .d_val = addr }); | |
| 194 | } | |
| 195 | ||
| 196 | // VERNEED + VERNEEDNUM | |
| 197 | if (elf_file.verneed_section_index) |shndx| { | |
| 198 | const addr = elf_file.shdrs.items[shndx].sh_addr; | |
| 199 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_VERNEED, .d_val = addr }); | |
| 200 | try writer.writeStruct(elf.Elf64_Dyn{ | |
| 201 | .d_tag = elf.DT_VERNEEDNUM, | |
| 202 | .d_val = elf_file.verneed.verneed.items.len, | |
| 203 | }); | |
| 204 | } | |
| 205 | ||
| 206 | // FLAGS | |
| 207 | if (dt.getFlags(elf_file)) |flags| { | |
| 208 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FLAGS, .d_val = flags }); | |
| 209 | } | |
| 210 | // FLAGS_1 | |
| 211 | if (dt.getFlags1(elf_file)) |flags_1| { | |
| 212 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FLAGS_1, .d_val = flags_1 }); | |
| 213 | } | |
| 214 | ||
| 215 | // DEBUG | |
| 216 | if (!elf_file.isDynLib()) try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_DEBUG, .d_val = 0 }); | |
| 217 | ||
| 218 | // NULL | |
| 219 | try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_NULL, .d_val = 0 }); | |
| 220 | } | |
| 221 | }; | |
| 222 | ||
| 1 | 223 | pub const GotSection = struct { |
| 2 | 224 | entries: std.ArrayListUnmanaged(Entry) = .{}, |
| 3 | 225 | output_symtab_size: Elf.SymtabSize = .{}, |
| ... | ... | @@ -72,44 +294,57 @@ pub const GotSection = struct { |
| 72 | 294 | return index; |
| 73 | 295 | } |
| 74 | 296 | |
| 75 | // pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 76 | // const index = got.next_index; | |
| 77 | // const symbol = elf_file.getSymbol(sym_index); | |
| 78 | // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true; | |
| 79 | // if (symbol.getExtra(elf_file)) |extra| { | |
| 80 | // var new_extra = extra; | |
| 81 | // new_extra.tlsgd = index; | |
| 82 | // symbol.setExtra(new_extra, elf_file); | |
| 83 | // } else try symbol.addExtra(.{ .tlsgd = index }, elf_file); | |
| 84 | // try got.symbols.append(elf_file.base.allocator, .{ .tlsgd = sym_index }); | |
| 85 | // got.next_index += 2; | |
| 86 | // } | |
| 87 | ||
| 88 | // pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 89 | // const index = got.next_index; | |
| 90 | // const symbol = elf_file.getSymbol(sym_index); | |
| 91 | // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true; | |
| 92 | // if (symbol.getExtra(elf_file)) |extra| { | |
| 93 | // var new_extra = extra; | |
| 94 | // new_extra.gottp = index; | |
| 95 | // symbol.setExtra(new_extra, elf_file); | |
| 96 | // } else try symbol.addExtra(.{ .gottp = index }, elf_file); | |
| 97 | // try got.symbols.append(elf_file.base.allocator, .{ .gottp = sym_index }); | |
| 98 | // got.next_index += 1; | |
| 99 | // } | |
| 100 | ||
| 101 | // pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 102 | // const index = got.next_index; | |
| 103 | // const symbol = elf_file.getSymbol(sym_index); | |
| 104 | // got.needs_rela = true; | |
| 105 | // if (symbol.getExtra(elf_file)) |extra| { | |
| 106 | // var new_extra = extra; | |
| 107 | // new_extra.tlsdesc = index; | |
| 108 | // symbol.setExtra(new_extra, elf_file); | |
| 109 | // } else try symbol.addExtra(.{ .tlsdesc = index }, elf_file); | |
| 110 | // try got.symbols.append(elf_file.base.allocator, .{ .tlsdesc = sym_index }); | |
| 111 | // got.next_index += 2; | |
| 112 | // } | |
| 297 | pub fn addTlsLdSymbol(got: *GotSection, elf_file: *Elf) !void { | |
| 298 | assert(got.flags.needs_tlsld); | |
| 299 | const index = try got.allocateEntry(elf_file.base.allocator); | |
| 300 | const entry = &got.entries.items[index]; | |
| 301 | entry.tag = .tlsld; | |
| 302 | entry.symbol_index = undefined; // unused | |
| 303 | got.flags.needs_rela = true; | |
| 304 | got.tlsld_index = index; | |
| 305 | } | |
| 306 | ||
| 307 | pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 308 | const index = try got.allocateEntry(elf_file.base.allocator); | |
| 309 | const entry = &got.entries.items[index]; | |
| 310 | entry.tag = .tlsgd; | |
| 311 | entry.symbol_index = sym_index; | |
| 312 | const symbol = elf_file.symbol(sym_index); | |
| 313 | if (symbol.flags.import or elf_file.isDynLib()) got.flags.needs_rela = true; | |
| 314 | if (symbol.extra(elf_file)) |extra| { | |
| 315 | var new_extra = extra; | |
| 316 | new_extra.tlsgd = index; | |
| 317 | symbol.setExtra(new_extra, elf_file); | |
| 318 | } else try symbol.addExtra(.{ .tlsgd = index }, elf_file); | |
| 319 | } | |
| 320 | ||
| 321 | pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 322 | const index = try got.allocateEntry(elf_file.base.allocator); | |
| 323 | const entry = &got.entries.items[index]; | |
| 324 | entry.tag = .gottp; | |
| 325 | entry.symbol_index = sym_index; | |
| 326 | const symbol = elf_file.symbol(sym_index); | |
| 327 | if (symbol.flags.import or elf_file.isDynLib()) got.flags.needs_rela = true; | |
| 328 | if (symbol.extra(elf_file)) |extra| { | |
| 329 | var new_extra = extra; | |
| 330 | new_extra.gottp = index; | |
| 331 | symbol.setExtra(new_extra, elf_file); | |
| 332 | } else try symbol.addExtra(.{ .gottp = index }, elf_file); | |
| 333 | } | |
| 334 | ||
| 335 | pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 336 | const index = try got.allocateEntry(elf_file.base.allocator); | |
| 337 | const entry = &got.entries.items[index]; | |
| 338 | entry.tag = .tlsdesc; | |
| 339 | entry.symbol_index = sym_index; | |
| 340 | const symbol = elf_file.symbol(sym_index); | |
| 341 | got.flags.needs_rela = true; | |
| 342 | if (symbol.extra(elf_file)) |extra| { | |
| 343 | var new_extra = extra; | |
| 344 | new_extra.tlsdesc = index; | |
| 345 | symbol.setExtra(new_extra, elf_file); | |
| 346 | } else try symbol.addExtra(.{ .tlsdesc = index }, elf_file); | |
| 347 | } | |
| 113 | 348 | |
| 114 | 349 | pub fn size(got: GotSection, elf_file: *Elf) usize { |
| 115 | 350 | var s: usize = 0; |
| ... | ... | @@ -119,260 +354,207 @@ pub const GotSection = struct { |
| 119 | 354 | return s; |
| 120 | 355 | } |
| 121 | 356 | |
| 122 | pub fn writeEntry(got: *GotSection, elf_file: *Elf, index: Index) !void { | |
| 123 | const entry_size: u16 = elf_file.archPtrWidthBytes(); | |
| 124 | // if (got.dirty) { | |
| 125 | // const needed_size = got.size(elf_file); | |
| 126 | // try elf_file.growAllocSection(elf_file.got_section_index.?, needed_size); | |
| 127 | // got.dirty = false; | |
| 128 | // } | |
| 357 | pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { | |
| 358 | const is_dyn_lib = elf_file.isDynLib(); | |
| 359 | const apply_relocs = true; // TODO add user option for this | |
| 360 | ||
| 361 | for (got.entries.items) |entry| { | |
| 362 | const symbol = elf_file.symbol(entry.symbol_index); | |
| 363 | switch (entry.tag) { | |
| 364 | .got => { | |
| 365 | const value = blk: { | |
| 366 | const value = symbol.address(.{ .plt = false }, elf_file); | |
| 367 | if (symbol.flags.import) break :blk 0; | |
| 368 | if (symbol.isIFunc(elf_file)) | |
| 369 | break :blk if (apply_relocs) value else 0; | |
| 370 | if (elf_file.base.options.pic and !symbol.isAbs(elf_file)) | |
| 371 | break :blk if (apply_relocs) value else 0; | |
| 372 | break :blk value; | |
| 373 | }; | |
| 374 | try writeInt(value, elf_file, writer); | |
| 375 | }, | |
| 376 | .tlsld => { | |
| 377 | try writeInt(if (is_dyn_lib) @as(u64, 0) else 1, elf_file, writer); | |
| 378 | try writeInt(0, elf_file, writer); | |
| 379 | }, | |
| 380 | .tlsgd => { | |
| 381 | if (symbol.flags.import) { | |
| 382 | try writeInt(0, elf_file, writer); | |
| 383 | try writeInt(0, elf_file, writer); | |
| 384 | } else { | |
| 385 | try writeInt(if (is_dyn_lib) @as(u64, 0) else 1, elf_file, writer); | |
| 386 | const offset = symbol.address(.{}, elf_file) - elf_file.dtpAddress(); | |
| 387 | try writeInt(offset, elf_file, writer); | |
| 388 | } | |
| 389 | }, | |
| 390 | .gottp => { | |
| 391 | if (symbol.flags.import) { | |
| 392 | try writeInt(0, elf_file, writer); | |
| 393 | } else if (is_dyn_lib) { | |
| 394 | const offset = if (apply_relocs) | |
| 395 | symbol.address(.{}, elf_file) - elf_file.tlsAddress() | |
| 396 | else | |
| 397 | 0; | |
| 398 | try writeInt(offset, elf_file, writer); | |
| 399 | } else { | |
| 400 | const offset = @as(i64, @intCast(symbol.address(.{}, elf_file))) - | |
| 401 | @as(i64, @intCast(elf_file.tpAddress())); | |
| 402 | try writeInt(offset, elf_file, writer); | |
| 403 | } | |
| 404 | }, | |
| 405 | .tlsdesc => { | |
| 406 | try writeInt(0, elf_file, writer); | |
| 407 | try writeInt(0, elf_file, writer); | |
| 408 | }, | |
| 409 | } | |
| 410 | } | |
| 411 | } | |
| 412 | ||
| 413 | fn writeInt(value: anytype, elf_file: *Elf, writer: anytype) !void { | |
| 414 | const entry_size = elf_file.archPtrWidthBytes(); | |
| 129 | 415 | const endian = elf_file.base.options.target.cpu.arch.endian(); |
| 130 | const entry = got.entries.items[index]; | |
| 131 | const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?]; | |
| 132 | const off = shdr.sh_offset + @as(u64, entry_size) * entry.cell_index; | |
| 133 | const vaddr = shdr.sh_addr + @as(u64, entry_size) * entry.cell_index; | |
| 134 | const value = elf_file.symbol(entry.symbol_index).value; | |
| 135 | 416 | switch (entry_size) { |
| 136 | 2 => { | |
| 137 | var buf: [2]u8 = undefined; | |
| 138 | std.mem.writeInt(u16, &buf, @as(u16, @intCast(value)), endian); | |
| 139 | try elf_file.base.file.?.pwriteAll(&buf, off); | |
| 140 | }, | |
| 141 | 4 => { | |
| 142 | var buf: [4]u8 = undefined; | |
| 143 | std.mem.writeInt(u32, &buf, @as(u32, @intCast(value)), endian); | |
| 144 | try elf_file.base.file.?.pwriteAll(&buf, off); | |
| 145 | }, | |
| 146 | 8 => { | |
| 147 | var buf: [8]u8 = undefined; | |
| 148 | std.mem.writeInt(u64, &buf, value, endian); | |
| 149 | try elf_file.base.file.?.pwriteAll(&buf, off); | |
| 150 | ||
| 151 | if (elf_file.base.child_pid) |pid| { | |
| 152 | switch (builtin.os.tag) { | |
| 153 | .linux => { | |
| 154 | var local_vec: [1]std.os.iovec_const = .{.{ | |
| 155 | .iov_base = &buf, | |
| 156 | .iov_len = buf.len, | |
| 157 | }}; | |
| 158 | var remote_vec: [1]std.os.iovec_const = .{.{ | |
| 159 | .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))), | |
| 160 | .iov_len = buf.len, | |
| 161 | }}; | |
| 162 | const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); | |
| 163 | switch (std.os.errno(rc)) { | |
| 164 | .SUCCESS => assert(rc == buf.len), | |
| 165 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), | |
| 166 | } | |
| 167 | }, | |
| 168 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | |
| 169 | } | |
| 170 | } | |
| 171 | }, | |
| 417 | 2 => try writer.writeInt(u16, @intCast(value), endian), | |
| 418 | 4 => try writer.writeInt(u32, @intCast(value), endian), | |
| 419 | 8 => try writer.writeInt(u64, @intCast(value), endian), | |
| 172 | 420 | else => unreachable, |
| 173 | 421 | } |
| 174 | 422 | } |
| 175 | 423 | |
| 176 | pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { | |
| 177 | const entry_size: u16 = elf_file.archPtrWidthBytes(); | |
| 178 | const endian = elf_file.base.options.target.cpu.arch.endian(); | |
| 424 | pub fn addRela(got: GotSection, elf_file: *Elf) !void { | |
| 425 | const is_dyn_lib = elf_file.isDynLib(); | |
| 426 | try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file)); | |
| 427 | ||
| 179 | 428 | for (got.entries.items) |entry| { |
| 180 | const value = elf_file.symbol(entry.symbol_index).value; | |
| 181 | switch (entry_size) { | |
| 182 | 2 => try writer.writeInt(u16, @intCast(value), endian), | |
| 183 | 4 => try writer.writeInt(u32, @intCast(value), endian), | |
| 184 | 8 => try writer.writeInt(u64, @intCast(value), endian), | |
| 185 | else => unreachable, | |
| 429 | const symbol = switch (entry.tag) { | |
| 430 | .tlsld => null, | |
| 431 | inline else => elf_file.symbol(entry.symbol_index), | |
| 432 | }; | |
| 433 | const extra = if (symbol) |s| s.extra(elf_file).? else null; | |
| 434 | ||
| 435 | switch (entry.tag) { | |
| 436 | .got => { | |
| 437 | const offset = symbol.?.gotAddress(elf_file); | |
| 438 | if (symbol.?.flags.import) { | |
| 439 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 440 | .offset = offset, | |
| 441 | .sym = extra.?.dynamic, | |
| 442 | .type = elf.R_X86_64_GLOB_DAT, | |
| 443 | }); | |
| 444 | continue; | |
| 445 | } | |
| 446 | if (symbol.?.isIFunc(elf_file)) { | |
| 447 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 448 | .offset = offset, | |
| 449 | .type = elf.R_X86_64_IRELATIVE, | |
| 450 | .addend = @intCast(symbol.?.address(.{ .plt = false }, elf_file)), | |
| 451 | }); | |
| 452 | continue; | |
| 453 | } | |
| 454 | if (elf_file.base.options.pic and !symbol.?.isAbs(elf_file)) { | |
| 455 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 456 | .offset = offset, | |
| 457 | .type = elf.R_X86_64_RELATIVE, | |
| 458 | .addend = @intCast(symbol.?.address(.{ .plt = false }, elf_file)), | |
| 459 | }); | |
| 460 | } | |
| 461 | }, | |
| 462 | ||
| 463 | .tlsld => { | |
| 464 | if (is_dyn_lib) { | |
| 465 | const offset = entry.address(elf_file); | |
| 466 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 467 | .offset = offset, | |
| 468 | .type = elf.R_X86_64_DTPMOD64, | |
| 469 | }); | |
| 470 | } | |
| 471 | }, | |
| 472 | ||
| 473 | .tlsgd => { | |
| 474 | const offset = symbol.?.tlsGdAddress(elf_file); | |
| 475 | if (symbol.?.flags.import) { | |
| 476 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 477 | .offset = offset, | |
| 478 | .sym = extra.?.dynamic, | |
| 479 | .type = elf.R_X86_64_DTPMOD64, | |
| 480 | }); | |
| 481 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 482 | .offset = offset + 8, | |
| 483 | .sym = extra.?.dynamic, | |
| 484 | .type = elf.R_X86_64_DTPOFF64, | |
| 485 | }); | |
| 486 | } else if (is_dyn_lib) { | |
| 487 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 488 | .offset = offset, | |
| 489 | .sym = extra.?.dynamic, | |
| 490 | .type = elf.R_X86_64_DTPMOD64, | |
| 491 | }); | |
| 492 | } | |
| 493 | }, | |
| 494 | ||
| 495 | .gottp => { | |
| 496 | const offset = symbol.?.gotTpAddress(elf_file); | |
| 497 | if (symbol.?.flags.import) { | |
| 498 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 499 | .offset = offset, | |
| 500 | .sym = extra.?.dynamic, | |
| 501 | .type = elf.R_X86_64_TPOFF64, | |
| 502 | }); | |
| 503 | } else if (is_dyn_lib) { | |
| 504 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 505 | .offset = offset, | |
| 506 | .type = elf.R_X86_64_TPOFF64, | |
| 507 | .addend = @intCast(symbol.?.address(.{}, elf_file) - elf_file.tlsAddress()), | |
| 508 | }); | |
| 509 | } | |
| 510 | }, | |
| 511 | ||
| 512 | .tlsdesc => { | |
| 513 | const offset = symbol.?.tlsDescAddress(elf_file); | |
| 514 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 515 | .offset = offset, | |
| 516 | .sym = extra.?.dynamic, | |
| 517 | .type = elf.R_X86_64_TLSDESC, | |
| 518 | }); | |
| 519 | }, | |
| 186 | 520 | } |
| 187 | 521 | } |
| 188 | 522 | } |
| 189 | 523 | |
| 190 | // pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { | |
| 191 | // const is_shared = elf_file.options.output_mode == .lib; | |
| 192 | // const apply_relocs = elf_file.options.apply_dynamic_relocs; | |
| 193 | ||
| 194 | // for (got.symbols.items) |sym| { | |
| 195 | // const symbol = elf_file.getSymbol(sym.getIndex()); | |
| 196 | // switch (sym) { | |
| 197 | // .got => { | |
| 198 | // const value: u64 = blk: { | |
| 199 | // const value = symbol.getAddress(.{ .plt = false }, elf_file); | |
| 200 | // if (symbol.flags.import) break :blk 0; | |
| 201 | // if (symbol.isIFunc(elf_file)) | |
| 202 | // break :blk if (apply_relocs) value else 0; | |
| 203 | // if (elf_file.options.pic and !symbol.isAbs(elf_file)) | |
| 204 | // break :blk if (apply_relocs) value else 0; | |
| 205 | // break :blk value; | |
| 206 | // }; | |
| 207 | // try writer.writeIntLittle(u64, value); | |
| 208 | // }, | |
| 209 | ||
| 210 | // .tlsgd => { | |
| 211 | // if (symbol.flags.import) { | |
| 212 | // try writer.writeIntLittle(u64, 0); | |
| 213 | // try writer.writeIntLittle(u64, 0); | |
| 214 | // } else { | |
| 215 | // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1); | |
| 216 | // const offset = symbol.getAddress(.{}, elf_file) - elf_file.getDtpAddress(); | |
| 217 | // try writer.writeIntLittle(u64, offset); | |
| 218 | // } | |
| 219 | // }, | |
| 220 | ||
| 221 | // .gottp => { | |
| 222 | // if (symbol.flags.import) { | |
| 223 | // try writer.writeIntLittle(u64, 0); | |
| 224 | // } else if (is_shared) { | |
| 225 | // const offset = if (apply_relocs) | |
| 226 | // symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress() | |
| 227 | // else | |
| 228 | // 0; | |
| 229 | // try writer.writeIntLittle(u64, offset); | |
| 230 | // } else { | |
| 231 | // const offset = @as(i64, @intCast(symbol.getAddress(.{}, elf_file))) - | |
| 232 | // @as(i64, @intCast(elf_file.getTpAddress())); | |
| 233 | // try writer.writeIntLittle(u64, @as(u64, @bitCast(offset))); | |
| 234 | // } | |
| 235 | // }, | |
| 236 | ||
| 237 | // .tlsdesc => { | |
| 238 | // try writer.writeIntLittle(u64, 0); | |
| 239 | // try writer.writeIntLittle(u64, 0); | |
| 240 | // }, | |
| 241 | // } | |
| 242 | // } | |
| 243 | ||
| 244 | // if (got.emit_tlsld) { | |
| 245 | // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1); | |
| 246 | // try writer.writeIntLittle(u64, 0); | |
| 247 | // } | |
| 248 | // } | |
| 249 | ||
| 250 | // pub fn addRela(got: GotSection, elf_file: *Elf) !void { | |
| 251 | // const is_shared = elf_file.options.output_mode == .lib; | |
| 252 | // try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file)); | |
| 253 | ||
| 254 | // for (got.symbols.items) |sym| { | |
| 255 | // const symbol = elf_file.getSymbol(sym.getIndex()); | |
| 256 | // const extra = symbol.getExtra(elf_file).?; | |
| 257 | ||
| 258 | // switch (sym) { | |
| 259 | // .got => { | |
| 260 | // const offset = symbol.gotAddress(elf_file); | |
| 261 | ||
| 262 | // if (symbol.flags.import) { | |
| 263 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 264 | // .offset = offset, | |
| 265 | // .sym = extra.dynamic, | |
| 266 | // .type = elf.R_X86_64_GLOB_DAT, | |
| 267 | // }); | |
| 268 | // continue; | |
| 269 | // } | |
| 270 | ||
| 271 | // if (symbol.isIFunc(elf_file)) { | |
| 272 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 273 | // .offset = offset, | |
| 274 | // .type = elf.R_X86_64_IRELATIVE, | |
| 275 | // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)), | |
| 276 | // }); | |
| 277 | // continue; | |
| 278 | // } | |
| 279 | ||
| 280 | // if (elf_file.options.pic and !symbol.isAbs(elf_file)) { | |
| 281 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 282 | // .offset = offset, | |
| 283 | // .type = elf.R_X86_64_RELATIVE, | |
| 284 | // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)), | |
| 285 | // }); | |
| 286 | // } | |
| 287 | // }, | |
| 288 | ||
| 289 | // .tlsgd => { | |
| 290 | // const offset = symbol.getTlsGdAddress(elf_file); | |
| 291 | // if (symbol.flags.import) { | |
| 292 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 293 | // .offset = offset, | |
| 294 | // .sym = extra.dynamic, | |
| 295 | // .type = elf.R_X86_64_DTPMOD64, | |
| 296 | // }); | |
| 297 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 298 | // .offset = offset + 8, | |
| 299 | // .sym = extra.dynamic, | |
| 300 | // .type = elf.R_X86_64_DTPOFF64, | |
| 301 | // }); | |
| 302 | // } else if (is_shared) { | |
| 303 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 304 | // .offset = offset, | |
| 305 | // .sym = extra.dynamic, | |
| 306 | // .type = elf.R_X86_64_DTPMOD64, | |
| 307 | // }); | |
| 308 | // } | |
| 309 | // }, | |
| 310 | ||
| 311 | // .gottp => { | |
| 312 | // const offset = symbol.getGotTpAddress(elf_file); | |
| 313 | // if (symbol.flags.import) { | |
| 314 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 315 | // .offset = offset, | |
| 316 | // .sym = extra.dynamic, | |
| 317 | // .type = elf.R_X86_64_TPOFF64, | |
| 318 | // }); | |
| 319 | // } else if (is_shared) { | |
| 320 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 321 | // .offset = offset, | |
| 322 | // .type = elf.R_X86_64_TPOFF64, | |
| 323 | // .addend = @intCast(symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress()), | |
| 324 | // }); | |
| 325 | // } | |
| 326 | // }, | |
| 327 | ||
| 328 | // .tlsdesc => { | |
| 329 | // const offset = symbol.getTlsDescAddress(elf_file); | |
| 330 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 331 | // .offset = offset, | |
| 332 | // .sym = extra.dynamic, | |
| 333 | // .type = elf.R_X86_64_TLSDESC, | |
| 334 | // }); | |
| 335 | // }, | |
| 336 | // } | |
| 337 | // } | |
| 338 | ||
| 339 | // if (is_shared and got.emit_tlsld) { | |
| 340 | // const offset = elf_file.getTlsLdAddress(); | |
| 341 | // elf_file.addRelaDynAssumeCapacity(.{ | |
| 342 | // .offset = offset, | |
| 343 | // .type = elf.R_X86_64_DTPMOD64, | |
| 344 | // }); | |
| 345 | // } | |
| 346 | // } | |
| 347 | ||
| 348 | // pub fn numRela(got: GotSection, elf_file: *Elf) usize { | |
| 349 | // const is_shared = elf_file.options.output_mode == .lib; | |
| 350 | // var num: usize = 0; | |
| 351 | // for (got.symbols.items) |sym| { | |
| 352 | // const symbol = elf_file.symbol(sym.index()); | |
| 353 | // switch (sym) { | |
| 354 | // .got => if (symbol.flags.import or | |
| 355 | // symbol.isIFunc(elf_file) or (elf_file.options.pic and !symbol.isAbs(elf_file))) | |
| 356 | // { | |
| 357 | // num += 1; | |
| 358 | // }, | |
| 359 | ||
| 360 | // .tlsgd => if (symbol.flags.import) { | |
| 361 | // num += 2; | |
| 362 | // } else if (is_shared) { | |
| 363 | // num += 1; | |
| 364 | // }, | |
| 365 | ||
| 366 | // .gottp => if (symbol.flags.import or is_shared) { | |
| 367 | // num += 1; | |
| 368 | // }, | |
| 369 | ||
| 370 | // .tlsdesc => num += 1, | |
| 371 | // } | |
| 372 | // } | |
| 373 | // if (is_shared and got.emit_tlsld) num += 1; | |
| 374 | // return num; | |
| 375 | // } | |
| 524 | pub fn numRela(got: GotSection, elf_file: *Elf) usize { | |
| 525 | const is_dyn_lib = elf_file.isDynLib(); | |
| 526 | var num: usize = 0; | |
| 527 | for (got.entries.items) |entry| { | |
| 528 | const symbol = switch (entry.tag) { | |
| 529 | .tlsld => null, | |
| 530 | inline else => elf_file.symbol(entry.symbol_index), | |
| 531 | }; | |
| 532 | switch (entry.tag) { | |
| 533 | .got => if (symbol.?.flags.import or | |
| 534 | symbol.?.isIFunc(elf_file) or (elf_file.base.options.pic and !symbol.?.isAbs(elf_file))) | |
| 535 | { | |
| 536 | num += 1; | |
| 537 | }, | |
| 538 | ||
| 539 | .tlsld => if (is_dyn_lib) { | |
| 540 | num += 1; | |
| 541 | }, | |
| 542 | ||
| 543 | .tlsgd => if (symbol.?.flags.import) { | |
| 544 | num += 2; | |
| 545 | } else if (is_dyn_lib) { | |
| 546 | num += 1; | |
| 547 | }, | |
| 548 | ||
| 549 | .gottp => if (symbol.?.flags.import or is_dyn_lib) { | |
| 550 | num += 1; | |
| 551 | }, | |
| 552 | ||
| 553 | .tlsdesc => num += 1, | |
| 554 | } | |
| 555 | } | |
| 556 | return num; | |
| 557 | } | |
| 376 | 558 | |
| 377 | 559 | pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void { |
| 378 | 560 | _ = elf_file; |
| ... | ... | @@ -382,8 +564,11 @@ pub const GotSection = struct { |
| 382 | 564 | pub fn updateStrtab(got: GotSection, elf_file: *Elf) !void { |
| 383 | 565 | const gpa = elf_file.base.allocator; |
| 384 | 566 | for (got.entries.items) |entry| { |
| 385 | const symbol = elf_file.symbol(entry.symbol_index); | |
| 386 | const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol.name(elf_file), @tagName(entry.tag) }); | |
| 567 | const symbol_name = switch (entry.tag) { | |
| 568 | .tlsld => "", | |
| 569 | inline else => elf_file.symbol(entry.symbol_index).name(elf_file), | |
| 570 | }; | |
| 571 | const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) }); | |
| 387 | 572 | defer gpa.free(name); |
| 388 | 573 | _ = try elf_file.strtab.insert(gpa, name); |
| 389 | 574 | } |
| ... | ... | @@ -392,14 +577,18 @@ pub const GotSection = struct { |
| 392 | 577 | pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void { |
| 393 | 578 | const gpa = elf_file.base.allocator; |
| 394 | 579 | for (got.entries.items, ctx.ilocal..) |entry, ilocal| { |
| 395 | const symbol = elf_file.symbol(entry.symbol_index); | |
| 396 | const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol.name(elf_file), @tagName(entry.tag) }); | |
| 580 | const symbol = switch (entry.tag) { | |
| 581 | .tlsld => null, | |
| 582 | inline else => elf_file.symbol(entry.symbol_index), | |
| 583 | }; | |
| 584 | const symbol_name = switch (entry.tag) { | |
| 585 | .tlsld => "", | |
| 586 | inline else => symbol.?.name(elf_file), | |
| 587 | }; | |
| 588 | const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) }); | |
| 397 | 589 | defer gpa.free(name); |
| 398 | 590 | const st_name = try elf_file.strtab.insert(gpa, name); |
| 399 | const st_value = switch (entry.tag) { | |
| 400 | .got => symbol.gotAddress(elf_file), | |
| 401 | else => unreachable, | |
| 402 | }; | |
| 591 | const st_value = entry.address(elf_file); | |
| 403 | 592 | const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes(); |
| 404 | 593 | ctx.symtab[ilocal] = .{ |
| 405 | 594 | .st_name = st_name, |
| ... | ... | @@ -443,12 +632,697 @@ pub const GotSection = struct { |
| 443 | 632 | } |
| 444 | 633 | }; |
| 445 | 634 | |
| 635 | pub const PltSection = struct { | |
| 636 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | |
| 637 | output_symtab_size: Elf.SymtabSize = .{}, | |
| 638 | ||
| 639 | pub const preamble_size = 32; | |
| 640 | ||
| 641 | pub fn deinit(plt: *PltSection, allocator: Allocator) void { | |
| 642 | plt.symbols.deinit(allocator); | |
| 643 | } | |
| 644 | ||
| 645 | pub fn addSymbol(plt: *PltSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 646 | const index = @as(u32, @intCast(plt.symbols.items.len)); | |
| 647 | const symbol = elf_file.symbol(sym_index); | |
| 648 | if (symbol.extra(elf_file)) |extra| { | |
| 649 | var new_extra = extra; | |
| 650 | new_extra.plt = index; | |
| 651 | symbol.setExtra(new_extra, elf_file); | |
| 652 | } else try symbol.addExtra(.{ .plt = index }, elf_file); | |
| 653 | try plt.symbols.append(elf_file.base.allocator, sym_index); | |
| 654 | } | |
| 655 | ||
| 656 | pub fn size(plt: PltSection) usize { | |
| 657 | return preamble_size + plt.symbols.items.len * 16; | |
| 658 | } | |
| 659 | ||
| 660 | pub fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void { | |
| 661 | const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr; | |
| 662 | const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr; | |
| 663 | var preamble = [_]u8{ | |
| 664 | 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 | |
| 665 | 0x41, 0x53, // push r11 | |
| 666 | 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1] | |
| 667 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2] | |
| 668 | }; | |
| 669 | var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4; | |
| 670 | mem.writeIntLittle(i32, preamble[8..][0..4], @as(i32, @intCast(disp))); | |
| 671 | disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4; | |
| 672 | mem.writeIntLittle(i32, preamble[14..][0..4], @as(i32, @intCast(disp))); | |
| 673 | try writer.writeAll(&preamble); | |
| 674 | try writer.writeByteNTimes(0xcc, preamble_size - preamble.len); | |
| 675 | ||
| 676 | for (plt.symbols.items, 0..) |sym_index, i| { | |
| 677 | const sym = elf_file.symbol(sym_index); | |
| 678 | const target_addr = sym.gotPltAddress(elf_file); | |
| 679 | const source_addr = sym.pltAddress(elf_file); | |
| 680 | disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4; | |
| 681 | var entry = [_]u8{ | |
| 682 | 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 | |
| 683 | 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N | |
| 684 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N] | |
| 685 | }; | |
| 686 | mem.writeIntLittle(i32, entry[6..][0..4], @as(i32, @intCast(i))); | |
| 687 | mem.writeIntLittle(i32, entry[12..][0..4], @as(i32, @intCast(disp))); | |
| 688 | try writer.writeAll(&entry); | |
| 689 | } | |
| 690 | } | |
| 691 | ||
| 692 | pub fn addRela(plt: PltSection, elf_file: *Elf) !void { | |
| 693 | try elf_file.rela_plt.ensureUnusedCapacity(elf_file.base.allocator, plt.numRela()); | |
| 694 | for (plt.symbols.items) |sym_index| { | |
| 695 | const sym = elf_file.symbol(sym_index); | |
| 696 | assert(sym.flags.import); | |
| 697 | const extra = sym.extra(elf_file).?; | |
| 698 | const r_offset = sym.gotPltAddress(elf_file); | |
| 699 | const r_sym: u64 = extra.dynamic; | |
| 700 | const r_type: u32 = elf.R_X86_64_JUMP_SLOT; | |
| 701 | elf_file.rela_plt.appendAssumeCapacity(.{ | |
| 702 | .r_offset = r_offset, | |
| 703 | .r_info = (r_sym << 32) | r_type, | |
| 704 | .r_addend = 0, | |
| 705 | }); | |
| 706 | } | |
| 707 | } | |
| 708 | ||
| 709 | pub fn numRela(plt: PltSection) usize { | |
| 710 | return plt.symbols.items.len; | |
| 711 | } | |
| 712 | ||
| 713 | pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void { | |
| 714 | _ = elf_file; | |
| 715 | plt.output_symtab_size.nlocals = @as(u32, @intCast(plt.symbols.items.len)); | |
| 716 | } | |
| 717 | ||
| 718 | pub fn updateStrtab(plt: PltSection, elf_file: *Elf) !void { | |
| 719 | const gpa = elf_file.base.allocator; | |
| 720 | for (plt.symbols.items) |sym_index| { | |
| 721 | const sym = elf_file.symbol(sym_index); | |
| 722 | const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)}); | |
| 723 | defer gpa.free(name); | |
| 724 | _ = try elf_file.strtab.insert(gpa, name); | |
| 725 | } | |
| 726 | } | |
| 727 | ||
| 728 | pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) !void { | |
| 729 | const gpa = elf_file.base.allocator; | |
| 730 | ||
| 731 | var ilocal = ctx.ilocal; | |
| 732 | for (plt.symbols.items) |sym_index| { | |
| 733 | const sym = elf_file.symbol(sym_index); | |
| 734 | const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)}); | |
| 735 | defer gpa.free(name); | |
| 736 | const st_name = try elf_file.strtab.insert(gpa, name); | |
| 737 | ctx.symtab[ilocal] = .{ | |
| 738 | .st_name = st_name, | |
| 739 | .st_info = elf.STT_FUNC, | |
| 740 | .st_other = 0, | |
| 741 | .st_shndx = elf_file.plt_section_index.?, | |
| 742 | .st_value = sym.pltAddress(elf_file), | |
| 743 | .st_size = 16, | |
| 744 | }; | |
| 745 | ilocal += 1; | |
| 746 | } | |
| 747 | } | |
| 748 | }; | |
| 749 | ||
| 750 | pub const GotPltSection = struct { | |
| 751 | pub const preamble_size = 24; | |
| 752 | ||
| 753 | pub fn size(got_plt: GotPltSection, elf_file: *Elf) usize { | |
| 754 | _ = got_plt; | |
| 755 | return preamble_size + elf_file.plt.symbols.items.len * 8; | |
| 756 | } | |
| 757 | ||
| 758 | pub fn write(got_plt: GotPltSection, elf_file: *Elf, writer: anytype) !void { | |
| 759 | _ = got_plt; | |
| 760 | { | |
| 761 | // [0]: _DYNAMIC | |
| 762 | const symbol = elf_file.symbol(elf_file.dynamic_index.?); | |
| 763 | try writer.writeIntLittle(u64, symbol.value); | |
| 764 | } | |
| 765 | // [1]: 0x0 | |
| 766 | // [2]: 0x0 | |
| 767 | try writer.writeIntLittle(u64, 0x0); | |
| 768 | try writer.writeIntLittle(u64, 0x0); | |
| 769 | if (elf_file.plt_section_index) |shndx| { | |
| 770 | const plt_addr = elf_file.shdrs.items[shndx].sh_addr; | |
| 771 | for (0..elf_file.plt.symbols.items.len) |_| { | |
| 772 | // [N]: .plt | |
| 773 | try writer.writeIntLittle(u64, plt_addr); | |
| 774 | } | |
| 775 | } | |
| 776 | } | |
| 777 | }; | |
| 778 | ||
| 779 | pub const PltGotSection = struct { | |
| 780 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | |
| 781 | output_symtab_size: Elf.SymtabSize = .{}, | |
| 782 | ||
| 783 | pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void { | |
| 784 | plt_got.symbols.deinit(allocator); | |
| 785 | } | |
| 786 | ||
| 787 | pub fn addSymbol(plt_got: *PltGotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 788 | const index = @as(u32, @intCast(plt_got.symbols.items.len)); | |
| 789 | const symbol = elf_file.symbol(sym_index); | |
| 790 | if (symbol.extra(elf_file)) |extra| { | |
| 791 | var new_extra = extra; | |
| 792 | new_extra.plt_got = index; | |
| 793 | symbol.setExtra(new_extra, elf_file); | |
| 794 | } else try symbol.addExtra(.{ .plt_got = index }, elf_file); | |
| 795 | try plt_got.symbols.append(elf_file.base.allocator, sym_index); | |
| 796 | } | |
| 797 | ||
| 798 | pub fn size(plt_got: PltGotSection) usize { | |
| 799 | return plt_got.symbols.items.len * 16; | |
| 800 | } | |
| 801 | ||
| 802 | pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: anytype) !void { | |
| 803 | for (plt_got.symbols.items) |sym_index| { | |
| 804 | const sym = elf_file.symbol(sym_index); | |
| 805 | const target_addr = sym.gotAddress(elf_file); | |
| 806 | const source_addr = sym.pltGotAddress(elf_file); | |
| 807 | const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 6)) - 4; | |
| 808 | var entry = [_]u8{ | |
| 809 | 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 | |
| 810 | 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got[N] | |
| 811 | 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, | |
| 812 | }; | |
| 813 | mem.writeIntLittle(i32, entry[6..][0..4], @as(i32, @intCast(disp))); | |
| 814 | try writer.writeAll(&entry); | |
| 815 | } | |
| 816 | } | |
| 817 | ||
| 818 | pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void { | |
| 819 | _ = elf_file; | |
| 820 | plt_got.output_symtab_size.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); | |
| 821 | } | |
| 822 | ||
| 823 | pub fn updateStrtab(plt_got: PltGotSection, elf_file: *Elf) !void { | |
| 824 | const gpa = elf_file.base.allocator; | |
| 825 | for (plt_got.symbols.items) |sym_index| { | |
| 826 | const sym = elf_file.symbol(sym_index); | |
| 827 | const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)}); | |
| 828 | defer gpa.free(name); | |
| 829 | _ = try elf_file.strtab.insert(gpa, name); | |
| 830 | } | |
| 831 | } | |
| 832 | ||
| 833 | pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) !void { | |
| 834 | const gpa = elf_file.base.allocator; | |
| 835 | var ilocal = ctx.ilocal; | |
| 836 | for (plt_got.symbols.items) |sym_index| { | |
| 837 | const sym = elf_file.symbol(sym_index); | |
| 838 | const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)}); | |
| 839 | defer gpa.free(name); | |
| 840 | const st_name = try elf_file.strtab.insert(gpa, name); | |
| 841 | ctx.symtab[ilocal] = .{ | |
| 842 | .st_name = st_name, | |
| 843 | .st_info = elf.STT_FUNC, | |
| 844 | .st_other = 0, | |
| 845 | .st_shndx = elf_file.plt_got_section_index.?, | |
| 846 | .st_value = sym.pltGotAddress(elf_file), | |
| 847 | .st_size = 16, | |
| 848 | }; | |
| 849 | ilocal += 1; | |
| 850 | } | |
| 851 | } | |
| 852 | }; | |
| 853 | ||
| 854 | pub const CopyRelSection = struct { | |
| 855 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | |
| 856 | ||
| 857 | pub fn deinit(copy_rel: *CopyRelSection, allocator: Allocator) void { | |
| 858 | copy_rel.symbols.deinit(allocator); | |
| 859 | } | |
| 860 | ||
| 861 | pub fn addSymbol(copy_rel: *CopyRelSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 862 | const index = @as(u32, @intCast(copy_rel.symbols.items.len)); | |
| 863 | const symbol = elf_file.symbol(sym_index); | |
| 864 | symbol.flags.import = true; | |
| 865 | symbol.flags.@"export" = true; | |
| 866 | symbol.flags.has_copy_rel = true; | |
| 867 | symbol.flags.weak = false; | |
| 868 | ||
| 869 | if (symbol.extra(elf_file)) |extra| { | |
| 870 | var new_extra = extra; | |
| 871 | new_extra.copy_rel = index; | |
| 872 | symbol.setExtra(new_extra, elf_file); | |
| 873 | } else try symbol.addExtra(.{ .copy_rel = index }, elf_file); | |
| 874 | try copy_rel.symbols.append(elf_file.base.allocator, sym_index); | |
| 875 | ||
| 876 | const shared_object = symbol.file(elf_file).?.shared_object; | |
| 877 | if (shared_object.aliases == null) { | |
| 878 | try shared_object.initSymbolAliases(elf_file); | |
| 879 | } | |
| 880 | ||
| 881 | const aliases = shared_object.symbolAliases(sym_index, elf_file); | |
| 882 | for (aliases) |alias| { | |
| 883 | if (alias == sym_index) continue; | |
| 884 | const alias_sym = elf_file.symbol(alias); | |
| 885 | alias_sym.flags.import = true; | |
| 886 | alias_sym.flags.@"export" = true; | |
| 887 | alias_sym.flags.has_copy_rel = true; | |
| 888 | alias_sym.flags.needs_copy_rel = true; | |
| 889 | alias_sym.flags.weak = false; | |
| 890 | try elf_file.dynsym.addSymbol(alias, elf_file); | |
| 891 | } | |
| 892 | } | |
| 893 | ||
| 894 | pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u16, elf_file: *Elf) !void { | |
| 895 | const shdr = &elf_file.shdrs.items[shndx]; | |
| 896 | for (copy_rel.symbols.items) |sym_index| { | |
| 897 | const symbol = elf_file.symbol(sym_index); | |
| 898 | const shared_object = symbol.file(elf_file).?.shared_object; | |
| 899 | const alignment = try symbol.dsoAlignment(elf_file); | |
| 900 | symbol.value = mem.alignForward(u64, shdr.sh_size, alignment); | |
| 901 | shdr.sh_addralign = @max(shdr.sh_addralign, alignment); | |
| 902 | shdr.sh_size = symbol.value + symbol.elfSym(elf_file).st_size; | |
| 903 | ||
| 904 | const aliases = shared_object.symbolAliases(sym_index, elf_file); | |
| 905 | for (aliases) |alias| { | |
| 906 | if (alias == sym_index) continue; | |
| 907 | const alias_sym = elf_file.symbol(alias); | |
| 908 | alias_sym.value = symbol.value; | |
| 909 | } | |
| 910 | } | |
| 911 | } | |
| 912 | ||
| 913 | pub fn addRela(copy_rel: CopyRelSection, elf_file: *Elf) !void { | |
| 914 | try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, copy_rel.numRela()); | |
| 915 | for (copy_rel.symbols.items) |sym_index| { | |
| 916 | const sym = elf_file.symbol(sym_index); | |
| 917 | assert(sym.flags.import and sym.flags.has_copy_rel); | |
| 918 | const extra = sym.extra(elf_file).?; | |
| 919 | elf_file.addRelaDynAssumeCapacity(.{ | |
| 920 | .offset = sym.address(.{}, elf_file), | |
| 921 | .sym = extra.dynamic, | |
| 922 | .type = elf.R_X86_64_COPY, | |
| 923 | }); | |
| 924 | } | |
| 925 | } | |
| 926 | ||
| 927 | pub fn numRela(copy_rel: CopyRelSection) usize { | |
| 928 | return copy_rel.symbols.items.len; | |
| 929 | } | |
| 930 | }; | |
| 931 | ||
| 932 | pub const DynsymSection = struct { | |
| 933 | entries: std.ArrayListUnmanaged(Entry) = .{}, | |
| 934 | ||
| 935 | pub const Entry = struct { | |
| 936 | /// Index of the symbol which gets privilege of getting a dynamic treatment | |
| 937 | symbol_index: Symbol.Index, | |
| 938 | /// Offset into .dynstrtab | |
| 939 | off: u32, | |
| 940 | }; | |
| 941 | ||
| 942 | pub fn deinit(dynsym: *DynsymSection, allocator: Allocator) void { | |
| 943 | dynsym.entries.deinit(allocator); | |
| 944 | } | |
| 945 | ||
| 946 | pub fn addSymbol(dynsym: *DynsymSection, sym_index: Symbol.Index, elf_file: *Elf) !void { | |
| 947 | const gpa = elf_file.base.allocator; | |
| 948 | const index = @as(u32, @intCast(dynsym.entries.items.len + 1)); | |
| 949 | const sym = elf_file.symbol(sym_index); | |
| 950 | sym.flags.has_dynamic = true; | |
| 951 | if (sym.extra(elf_file)) |extra| { | |
| 952 | var new_extra = extra; | |
| 953 | new_extra.dynamic = index; | |
| 954 | sym.setExtra(new_extra, elf_file); | |
| 955 | } else try sym.addExtra(.{ .dynamic = index }, elf_file); | |
| 956 | const off = try elf_file.dynstrtab.insert(gpa, sym.name(elf_file)); | |
| 957 | try dynsym.entries.append(gpa, .{ .symbol_index = sym_index, .off = off }); | |
| 958 | } | |
| 959 | ||
| 960 | pub fn sort(dynsym: *DynsymSection, elf_file: *Elf) void { | |
| 961 | const Sort = struct { | |
| 962 | pub fn lessThan(ctx: *Elf, lhs: Entry, rhs: Entry) bool { | |
| 963 | const lhs_sym = ctx.symbol(lhs.symbol_index); | |
| 964 | const rhs_sym = ctx.symbol(rhs.symbol_index); | |
| 965 | ||
| 966 | if (lhs_sym.flags.@"export" != rhs_sym.flags.@"export") { | |
| 967 | return rhs_sym.flags.@"export"; | |
| 968 | } | |
| 969 | ||
| 970 | // TODO cache hash values | |
| 971 | const nbuckets = ctx.gnu_hash.num_buckets; | |
| 972 | const lhs_hash = GnuHashSection.hasher(lhs_sym.name(ctx)) % nbuckets; | |
| 973 | const rhs_hash = GnuHashSection.hasher(rhs_sym.name(ctx)) % nbuckets; | |
| 974 | ||
| 975 | if (lhs_hash == rhs_hash) | |
| 976 | return lhs_sym.extra(ctx).?.dynamic < rhs_sym.extra(ctx).?.dynamic; | |
| 977 | return lhs_hash < rhs_hash; | |
| 978 | } | |
| 979 | }; | |
| 980 | ||
| 981 | var num_exports: u32 = 0; | |
| 982 | for (dynsym.entries.items) |entry| { | |
| 983 | const sym = elf_file.symbol(entry.symbol_index); | |
| 984 | if (sym.flags.@"export") num_exports += 1; | |
| 985 | } | |
| 986 | ||
| 987 | elf_file.gnu_hash.num_buckets = @divTrunc(num_exports, GnuHashSection.load_factor) + 1; | |
| 988 | ||
| 989 | std.mem.sort(Entry, dynsym.entries.items, elf_file, Sort.lessThan); | |
| 990 | ||
| 991 | for (dynsym.entries.items, 1..) |entry, index| { | |
| 992 | const sym = elf_file.symbol(entry.symbol_index); | |
| 993 | var extra = sym.extra(elf_file).?; | |
| 994 | extra.dynamic = @as(u32, @intCast(index)); | |
| 995 | sym.setExtra(extra, elf_file); | |
| 996 | } | |
| 997 | } | |
| 998 | ||
| 999 | pub fn size(dynsym: DynsymSection) usize { | |
| 1000 | return dynsym.count() * @sizeOf(elf.Elf64_Sym); | |
| 1001 | } | |
| 1002 | ||
| 1003 | pub fn count(dynsym: DynsymSection) u32 { | |
| 1004 | return @as(u32, @intCast(dynsym.entries.items.len + 1)); | |
| 1005 | } | |
| 1006 | ||
| 1007 | pub fn write(dynsym: DynsymSection, elf_file: *Elf, writer: anytype) !void { | |
| 1008 | try writer.writeStruct(Elf.null_sym); | |
| 1009 | for (dynsym.entries.items) |entry| { | |
| 1010 | const sym = elf_file.symbol(entry.symbol_index); | |
| 1011 | var out_sym: elf.Elf64_Sym = Elf.null_sym; | |
| 1012 | sym.setOutputSym(elf_file, &out_sym); | |
| 1013 | out_sym.st_name = entry.off; | |
| 1014 | try writer.writeStruct(out_sym); | |
| 1015 | } | |
| 1016 | } | |
| 1017 | }; | |
| 1018 | ||
| 1019 | pub const HashSection = struct { | |
| 1020 | buffer: std.ArrayListUnmanaged(u8) = .{}, | |
| 1021 | ||
| 1022 | pub fn deinit(hs: *HashSection, allocator: Allocator) void { | |
| 1023 | hs.buffer.deinit(allocator); | |
| 1024 | } | |
| 1025 | ||
| 1026 | pub fn generate(hs: *HashSection, elf_file: *Elf) !void { | |
| 1027 | if (elf_file.dynsym.count() == 1) return; | |
| 1028 | ||
| 1029 | const gpa = elf_file.base.allocator; | |
| 1030 | const nsyms = elf_file.dynsym.count(); | |
| 1031 | ||
| 1032 | var buckets = try gpa.alloc(u32, nsyms); | |
| 1033 | defer gpa.free(buckets); | |
| 1034 | @memset(buckets, 0); | |
| 1035 | ||
| 1036 | var chains = try gpa.alloc(u32, nsyms); | |
| 1037 | defer gpa.free(chains); | |
| 1038 | @memset(chains, 0); | |
| 1039 | ||
| 1040 | for (elf_file.dynsym.entries.items, 1..) |entry, i| { | |
| 1041 | const name = elf_file.dynstrtab.getAssumeExists(entry.off); | |
| 1042 | const hash = hasher(name) % buckets.len; | |
| 1043 | chains[@as(u32, @intCast(i))] = buckets[hash]; | |
| 1044 | buckets[hash] = @as(u32, @intCast(i)); | |
| 1045 | } | |
| 1046 | ||
| 1047 | try hs.buffer.ensureTotalCapacityPrecise(gpa, (2 + nsyms * 2) * 4); | |
| 1048 | hs.buffer.writer(gpa).writeIntLittle(u32, @as(u32, @intCast(nsyms))) catch unreachable; | |
| 1049 | hs.buffer.writer(gpa).writeIntLittle(u32, @as(u32, @intCast(nsyms))) catch unreachable; | |
| 1050 | hs.buffer.writer(gpa).writeAll(mem.sliceAsBytes(buckets)) catch unreachable; | |
| 1051 | hs.buffer.writer(gpa).writeAll(mem.sliceAsBytes(chains)) catch unreachable; | |
| 1052 | } | |
| 1053 | ||
| 1054 | pub inline fn size(hs: HashSection) usize { | |
| 1055 | return hs.buffer.items.len; | |
| 1056 | } | |
| 1057 | ||
| 1058 | pub fn hasher(name: [:0]const u8) u32 { | |
| 1059 | var h: u32 = 0; | |
| 1060 | var g: u32 = 0; | |
| 1061 | for (name) |c| { | |
| 1062 | h = (h << 4) + c; | |
| 1063 | g = h & 0xf0000000; | |
| 1064 | if (g > 0) h ^= g >> 24; | |
| 1065 | h &= ~g; | |
| 1066 | } | |
| 1067 | return h; | |
| 1068 | } | |
| 1069 | }; | |
| 1070 | ||
| 1071 | pub const GnuHashSection = struct { | |
| 1072 | num_buckets: u32 = 0, | |
| 1073 | num_bloom: u32 = 1, | |
| 1074 | num_exports: u32 = 0, | |
| 1075 | ||
| 1076 | pub const load_factor = 8; | |
| 1077 | pub const header_size = 16; | |
| 1078 | pub const bloom_shift = 26; | |
| 1079 | ||
| 1080 | fn getExports(elf_file: *Elf) []const DynsymSection.Entry { | |
| 1081 | const start = for (elf_file.dynsym.entries.items, 0..) |entry, i| { | |
| 1082 | const sym = elf_file.symbol(entry.symbol_index); | |
| 1083 | if (sym.flags.@"export") break i; | |
| 1084 | } else elf_file.dynsym.entries.items.len; | |
| 1085 | return elf_file.dynsym.entries.items[start..]; | |
| 1086 | } | |
| 1087 | ||
| 1088 | inline fn bitCeil(x: u64) u64 { | |
| 1089 | if (@popCount(x) == 1) return x; | |
| 1090 | return @as(u64, @intCast(@as(u128, 1) << (64 - @clz(x)))); | |
| 1091 | } | |
| 1092 | ||
| 1093 | pub fn calcSize(hash: *GnuHashSection, elf_file: *Elf) !void { | |
| 1094 | hash.num_exports = @as(u32, @intCast(getExports(elf_file).len)); | |
| 1095 | if (hash.num_exports > 0) { | |
| 1096 | const num_bits = hash.num_exports * 12; | |
| 1097 | hash.num_bloom = @as(u32, @intCast(bitCeil(@divTrunc(num_bits, 64)))); | |
| 1098 | } | |
| 1099 | } | |
| 1100 | ||
| 1101 | pub fn size(hash: GnuHashSection) usize { | |
| 1102 | return header_size + hash.num_bloom * 8 + hash.num_buckets * 4 + hash.num_exports * 4; | |
| 1103 | } | |
| 1104 | ||
| 1105 | pub fn write(hash: GnuHashSection, elf_file: *Elf, writer: anytype) !void { | |
| 1106 | const exports = getExports(elf_file); | |
| 1107 | const export_off = elf_file.dynsym.count() - hash.num_exports; | |
| 1108 | ||
| 1109 | var counting = std.io.countingWriter(writer); | |
| 1110 | const cwriter = counting.writer(); | |
| 1111 | ||
| 1112 | try cwriter.writeIntLittle(u32, hash.num_buckets); | |
| 1113 | try cwriter.writeIntLittle(u32, export_off); | |
| 1114 | try cwriter.writeIntLittle(u32, hash.num_bloom); | |
| 1115 | try cwriter.writeIntLittle(u32, bloom_shift); | |
| 1116 | ||
| 1117 | const gpa = elf_file.base.allocator; | |
| 1118 | const hashes = try gpa.alloc(u32, exports.len); | |
| 1119 | defer gpa.free(hashes); | |
| 1120 | const indices = try gpa.alloc(u32, exports.len); | |
| 1121 | defer gpa.free(indices); | |
| 1122 | ||
| 1123 | // Compose and write the bloom filter | |
| 1124 | const bloom = try gpa.alloc(u64, hash.num_bloom); | |
| 1125 | defer gpa.free(bloom); | |
| 1126 | @memset(bloom, 0); | |
| 1127 | ||
| 1128 | for (exports, 0..) |entry, i| { | |
| 1129 | const sym = elf_file.symbol(entry.symbol_index); | |
| 1130 | const h = hasher(sym.name(elf_file)); | |
| 1131 | hashes[i] = h; | |
| 1132 | indices[i] = h % hash.num_buckets; | |
| 1133 | const idx = @divTrunc(h, 64) % hash.num_bloom; | |
| 1134 | bloom[idx] |= @as(u64, 1) << @as(u6, @intCast(h % 64)); | |
| 1135 | bloom[idx] |= @as(u64, 1) << @as(u6, @intCast((h >> bloom_shift) % 64)); | |
| 1136 | } | |
| 1137 | ||
| 1138 | try cwriter.writeAll(mem.sliceAsBytes(bloom)); | |
| 1139 | ||
| 1140 | // Fill in the hash bucket indices | |
| 1141 | const buckets = try gpa.alloc(u32, hash.num_buckets); | |
| 1142 | defer gpa.free(buckets); | |
| 1143 | @memset(buckets, 0); | |
| 1144 | ||
| 1145 | for (0..hash.num_exports) |i| { | |
| 1146 | if (buckets[indices[i]] == 0) { | |
| 1147 | buckets[indices[i]] = @as(u32, @intCast(i + export_off)); | |
| 1148 | } | |
| 1149 | } | |
| 1150 | ||
| 1151 | try cwriter.writeAll(mem.sliceAsBytes(buckets)); | |
| 1152 | ||
| 1153 | // Finally, write the hash table | |
| 1154 | const table = try gpa.alloc(u32, hash.num_exports); | |
| 1155 | defer gpa.free(table); | |
| 1156 | @memset(table, 0); | |
| 1157 | ||
| 1158 | for (0..hash.num_exports) |i| { | |
| 1159 | const h = hashes[i]; | |
| 1160 | if (i == exports.len - 1 or indices[i] != indices[i + 1]) { | |
| 1161 | table[i] = h | 1; | |
| 1162 | } else { | |
| 1163 | table[i] = h & ~@as(u32, 1); | |
| 1164 | } | |
| 1165 | } | |
| 1166 | ||
| 1167 | try cwriter.writeAll(mem.sliceAsBytes(table)); | |
| 1168 | ||
| 1169 | assert(counting.bytes_written == hash.size()); | |
| 1170 | } | |
| 1171 | ||
| 1172 | pub fn hasher(name: [:0]const u8) u32 { | |
| 1173 | var h: u32 = 5381; | |
| 1174 | for (name) |c| { | |
| 1175 | h = (h << 5) +% h +% c; | |
| 1176 | } | |
| 1177 | return h; | |
| 1178 | } | |
| 1179 | }; | |
| 1180 | ||
| 1181 | pub const VerneedSection = struct { | |
| 1182 | verneed: std.ArrayListUnmanaged(elf.Elf64_Verneed) = .{}, | |
| 1183 | vernaux: std.ArrayListUnmanaged(elf.Elf64_Vernaux) = .{}, | |
| 1184 | index: elf.Elf64_Versym = elf.VER_NDX_GLOBAL + 1, | |
| 1185 | ||
| 1186 | pub fn deinit(vern: *VerneedSection, allocator: Allocator) void { | |
| 1187 | vern.verneed.deinit(allocator); | |
| 1188 | vern.vernaux.deinit(allocator); | |
| 1189 | } | |
| 1190 | ||
| 1191 | pub fn generate(vern: *VerneedSection, elf_file: *Elf) !void { | |
| 1192 | const dynsyms = elf_file.dynsym.entries.items; | |
| 1193 | var versyms = elf_file.versym.items; | |
| 1194 | ||
| 1195 | const VersionedSymbol = struct { | |
| 1196 | /// Index in the output version table | |
| 1197 | index: usize, | |
| 1198 | /// Index of the defining this symbol version shared object file | |
| 1199 | shared_object: File.Index, | |
| 1200 | /// Version index | |
| 1201 | version_index: elf.Elf64_Versym, | |
| 1202 | ||
| 1203 | fn soname(this: @This(), ctx: *Elf) []const u8 { | |
| 1204 | const shared_object = ctx.file(this.shared_object).?.shared_object; | |
| 1205 | return shared_object.soname(); | |
| 1206 | } | |
| 1207 | ||
| 1208 | fn versionString(this: @This(), ctx: *Elf) [:0]const u8 { | |
| 1209 | const shared_object = ctx.file(this.shared_object).?.shared_object; | |
| 1210 | return shared_object.versionString(this.version_index); | |
| 1211 | } | |
| 1212 | ||
| 1213 | pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool { | |
| 1214 | if (lhs.shared_object == rhs.shared_object) return lhs.version_index < rhs.version_index; | |
| 1215 | return mem.lessThan(u8, lhs.soname(ctx), rhs.soname(ctx)); | |
| 1216 | } | |
| 1217 | }; | |
| 1218 | ||
| 1219 | const gpa = elf_file.base.allocator; | |
| 1220 | var verneed = std.ArrayList(VersionedSymbol).init(gpa); | |
| 1221 | defer verneed.deinit(); | |
| 1222 | try verneed.ensureTotalCapacity(dynsyms.len); | |
| 1223 | ||
| 1224 | for (dynsyms, 1..) |entry, i| { | |
| 1225 | const symbol = elf_file.symbol(entry.symbol_index); | |
| 1226 | if (symbol.flags.import and symbol.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) { | |
| 1227 | const shared_object = symbol.file(elf_file).?.shared_object; | |
| 1228 | verneed.appendAssumeCapacity(.{ | |
| 1229 | .index = i, | |
| 1230 | .shared_object = shared_object.index, | |
| 1231 | .version_index = symbol.version_index, | |
| 1232 | }); | |
| 1233 | } | |
| 1234 | } | |
| 1235 | ||
| 1236 | mem.sort(VersionedSymbol, verneed.items, elf_file, VersionedSymbol.lessThan); | |
| 1237 | ||
| 1238 | var last = verneed.items[0]; | |
| 1239 | var last_verneed = try vern.addVerneed(last.soname(elf_file), elf_file); | |
| 1240 | var last_vernaux = try vern.addVernaux(last_verneed, last.versionString(elf_file), elf_file); | |
| 1241 | versyms[last.index] = last_vernaux.vna_other; | |
| 1242 | ||
| 1243 | for (verneed.items[1..]) |ver| { | |
| 1244 | if (ver.shared_object == last.shared_object) { | |
| 1245 | if (ver.version_index != last.version_index) { | |
| 1246 | last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file); | |
| 1247 | } | |
| 1248 | } else { | |
| 1249 | last_verneed = try vern.addVerneed(ver.soname(elf_file), elf_file); | |
| 1250 | last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file); | |
| 1251 | } | |
| 1252 | last = ver; | |
| 1253 | versyms[ver.index] = last_vernaux.vna_other; | |
| 1254 | } | |
| 1255 | ||
| 1256 | // Fixup offsets | |
| 1257 | var count: usize = 0; | |
| 1258 | var verneed_off: u32 = 0; | |
| 1259 | var vernaux_off: u32 = @as(u32, @intCast(vern.verneed.items.len)) * @sizeOf(elf.Elf64_Verneed); | |
| 1260 | for (vern.verneed.items, 0..) |*vsym, vsym_i| { | |
| 1261 | if (vsym_i < vern.verneed.items.len - 1) vsym.vn_next = @sizeOf(elf.Elf64_Verneed); | |
| 1262 | vsym.vn_aux = vernaux_off - verneed_off; | |
| 1263 | var inner_off: u32 = 0; | |
| 1264 | for (vern.vernaux.items[count..][0..vsym.vn_cnt], 0..) |*vaux, vaux_i| { | |
| 1265 | if (vaux_i < vsym.vn_cnt - 1) vaux.vna_next = @sizeOf(elf.Elf64_Vernaux); | |
| 1266 | inner_off += @sizeOf(elf.Elf64_Vernaux); | |
| 1267 | } | |
| 1268 | vernaux_off += inner_off; | |
| 1269 | verneed_off += @sizeOf(elf.Elf64_Verneed); | |
| 1270 | count += vsym.vn_cnt; | |
| 1271 | } | |
| 1272 | } | |
| 1273 | ||
| 1274 | fn addVerneed(vern: *VerneedSection, soname: []const u8, elf_file: *Elf) !*elf.Elf64_Verneed { | |
| 1275 | const gpa = elf_file.base.allocator; | |
| 1276 | const sym = try vern.verneed.addOne(gpa); | |
| 1277 | sym.* = .{ | |
| 1278 | .vn_version = 1, | |
| 1279 | .vn_cnt = 0, | |
| 1280 | .vn_file = try elf_file.dynstrtab.insert(gpa, soname), | |
| 1281 | .vn_aux = 0, | |
| 1282 | .vn_next = 0, | |
| 1283 | }; | |
| 1284 | return sym; | |
| 1285 | } | |
| 1286 | ||
| 1287 | fn addVernaux( | |
| 1288 | vern: *VerneedSection, | |
| 1289 | verneed_sym: *elf.Elf64_Verneed, | |
| 1290 | version: [:0]const u8, | |
| 1291 | elf_file: *Elf, | |
| 1292 | ) !elf.Elf64_Vernaux { | |
| 1293 | const gpa = elf_file.base.allocator; | |
| 1294 | const sym = try vern.vernaux.addOne(gpa); | |
| 1295 | sym.* = .{ | |
| 1296 | .vna_hash = HashSection.hasher(version), | |
| 1297 | .vna_flags = 0, | |
| 1298 | .vna_other = vern.index, | |
| 1299 | .vna_name = try elf_file.dynstrtab.insert(gpa, version), | |
| 1300 | .vna_next = 0, | |
| 1301 | }; | |
| 1302 | verneed_sym.vn_cnt += 1; | |
| 1303 | vern.index += 1; | |
| 1304 | return sym.*; | |
| 1305 | } | |
| 1306 | ||
| 1307 | pub fn size(vern: VerneedSection) usize { | |
| 1308 | return vern.verneed.items.len * @sizeOf(elf.Elf64_Verneed) + vern.vernaux.items.len * @sizeOf(elf.Elf64_Vernaux); | |
| 1309 | } | |
| 1310 | ||
| 1311 | pub fn write(vern: VerneedSection, writer: anytype) !void { | |
| 1312 | try writer.writeAll(mem.sliceAsBytes(vern.verneed.items)); | |
| 1313 | try writer.writeAll(mem.sliceAsBytes(vern.vernaux.items)); | |
| 1314 | } | |
| 1315 | }; | |
| 1316 | ||
| 446 | 1317 | const assert = std.debug.assert; |
| 447 | 1318 | const builtin = @import("builtin"); |
| 448 | 1319 | const elf = std.elf; |
| 1320 | const mem = std.mem; | |
| 449 | 1321 | const log = std.log.scoped(.link); |
| 450 | 1322 | const std = @import("std"); |
| 451 | 1323 | |
| 452 | 1324 | const Allocator = std.mem.Allocator; |
| 453 | 1325 | const Elf = @import("../Elf.zig"); |
| 1326 | const File = @import("file.zig").File; | |
| 1327 | const SharedObject = @import("SharedObject.zig"); | |
| 454 | 1328 | const Symbol = @import("Symbol.zig"); |