authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-05 15:55:00+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-05 15:55:00+02:00
logd9fffd431a89ed4104bcc0b2165bfb9917cdd82b
tree4b17125cfe074a98c32789c6315ca52db2e0234c
parent02451bdebfe2685c283f6955488d978047e4e9d8

elf: start porting abstraction of input file


5 files changed, 979 insertions(+), 292 deletions(-)

src/link/Elf.zig+331-292
...@@ -6,6 +6,10 @@ ptr_width: PtrWidth,...@@ -6,6 +6,10 @@ ptr_width: PtrWidth,
6/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.6/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
7llvm_object: ?*LlvmObject = null,7llvm_object: ?*LlvmObject = null,
88
9files: std.MutliArrayList(File.Entry) = .{},
10zig_module_index: ?File.Index = null,
11linker_defined_index: ?File.Index = null,
12
9/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.13/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
10/// Same order as in the file.14/// Same order as in the file.
11sections: std.MultiArrayList(Section) = .{},15sections: std.MultiArrayList(Section) = .{},
...@@ -51,16 +55,12 @@ debug_line_section_index: ?u16 = null,...@@ -51,16 +55,12 @@ debug_line_section_index: ?u16 = null,
51shstrtab_section_index: ?u16 = null,55shstrtab_section_index: ?u16 = null,
52strtab_section_index: ?u16 = null,56strtab_section_index: ?u16 = null,
5357
54/// The same order as in the file. ELF requires global symbols to all be after the58symbols: std.ArrayListUnmanaged(Symbol) = .{},
55/// local symbols, they cannot be mixed. So we must buffer all the global symbols and59globals: std.ArrayListUnmanaged(Symbol.Index) = .{},
56/// write them at the end. These are only the local symbols. The length of this array
57/// is the value used for sh_info in the .symtab section.
58locals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
59globals: std.ArrayListUnmanaged(u32) = .{},
60resolver: std.StringHashMapUnmanaged(u32) = .{},60resolver: std.StringHashMapUnmanaged(u32) = .{},
61unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},61unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
6262
63locals_free_list: std.ArrayListUnmanaged(u32) = .{},63symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
64globals_free_list: std.ArrayListUnmanaged(u32) = .{},64globals_free_list: std.ArrayListUnmanaged(u32) = .{},
6565
66got_table: TableSection(u32) = .{},66got_table: TableSection(u32) = .{},
...@@ -77,7 +77,7 @@ debug_aranges_section_dirty: bool = false,...@@ -77,7 +77,7 @@ debug_aranges_section_dirty: bool = false,
77debug_info_header_dirty: bool = false,77debug_info_header_dirty: bool = false,
78debug_line_header_dirty: bool = false,78debug_line_header_dirty: bool = false,
7979
80error_flags: File.ErrorFlags = File.ErrorFlags{},80error_flags: link.File.ErrorFlags = link.File.ErrorFlags{},
8181
82/// Table of tracked LazySymbols.82/// Table of tracked LazySymbols.
83lazy_syms: LazySymbolTable = .{},83lazy_syms: LazySymbolTable = .{},
...@@ -153,9 +153,11 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -153,9 +153,11 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
153 self.shdr_table_dirty = true;153 self.shdr_table_dirty = true;
154154
155 // Index 0 is always a null symbol.155 // Index 0 is always a null symbol.
156 try self.locals.append(allocator, null_sym);156 try self.symbols.append(allocator, .{});
157 // Allocate atom index 0 to null atom157 // Allocate atom index 0 to null atom
158 try self.atoms.append(allocator, .{});158 try self.atoms.append(allocator, .{});
159 // Append null file at index 0
160 try self.files.append(allocator, .null);
159 // There must always be a null section in index 0161 // There must always be a null section in index 0
160 try self.sections.append(allocator, .{162 try self.sections.append(allocator, .{
161 .shdr = .{163 .shdr = .{
...@@ -222,6 +224,15 @@ pub fn deinit(self: *Elf) void {...@@ -222,6 +224,15 @@ pub fn deinit(self: *Elf) void {
222224
223 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);225 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
224226
227 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
228 .null => {},
229 .zig_module => data.zig_module.deinit(gpa),
230 .linker_defined => data.linker_defined.deinit(gpa),
231 // .object => data.object.deinit(gpa),
232 // .shared_object => data.shared_object.deinit(gpa),
233 };
234 self.files.deinit(gpa);
235
225 for (self.sections.items(.free_list)) |*free_list| {236 for (self.sections.items(.free_list)) |*free_list| {
226 free_list.deinit(gpa);237 free_list.deinit(gpa);
227 }238 }
...@@ -230,10 +241,10 @@ pub fn deinit(self: *Elf) void {...@@ -230,10 +241,10 @@ pub fn deinit(self: *Elf) void {
230 self.program_headers.deinit(gpa);241 self.program_headers.deinit(gpa);
231 self.shstrtab.deinit(gpa);242 self.shstrtab.deinit(gpa);
232 self.strtab.deinit(gpa);243 self.strtab.deinit(gpa);
233 self.locals.deinit(gpa);244 self.symbols.deinit(gpa);
245 self.symbols_free_list.deinit(gpa);
234 self.globals.deinit(gpa);246 self.globals.deinit(gpa);
235 self.globals_free_list.deinit(gpa);247 self.globals_free_list.deinit(gpa);
236 self.locals_free_list.deinit(gpa);
237 self.got_table.deinit(gpa);248 self.got_table.deinit(gpa);
238 self.unresolved.deinit(gpa);249 self.unresolved.deinit(gpa);
239250
...@@ -278,7 +289,7 @@ pub fn deinit(self: *Elf) void {...@@ -278,7 +289,7 @@ pub fn deinit(self: *Elf) void {
278 }289 }
279}290}
280291
281pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {292pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
282 assert(self.llvm_object == null);293 assert(self.llvm_object == null);
283294
284 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);295 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
...@@ -653,7 +664,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -653,7 +664,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
653 .sh_size = file_size,664 .sh_size = file_size,
654 // The section header index of the associated string table.665 // The section header index of the associated string table.
655 .sh_link = self.strtab_section_index.?,666 .sh_link = self.strtab_section_index.?,
656 .sh_info = @as(u32, @intCast(self.locals.items.len)),667 .sh_info = @as(u32, @intCast(self.symbols.items.len)),
657 .sh_addralign = min_align,668 .sh_addralign = min_align,
658 .sh_entsize = each_size,669 .sh_entsize = each_size,
659 },670 },
...@@ -818,7 +829,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -818,7 +829,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
818829
819 {830 {
820 // Iterate over symbols, populating free_list and last_text_block.831 // Iterate over symbols, populating free_list and last_text_block.
821 if (self.locals.items.len != 1) {832 if (self.symbols.items.len != 1) {
822 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");833 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
823 }834 }
824 // We are starting with an empty file. The default values are correct, null and empty list.835 // We are starting with an empty file. The default values are correct, null and empty list.
...@@ -975,280 +986,294 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -975,280 +986,294 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
975 // corresponds to the Zig source code.986 // corresponds to the Zig source code.
976 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;987 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
977988
978 if (self.lazy_syms.getPtr(.none)) |metadata| {989 self.zig_module_index = blk: {
979 // Most lazy symbols can be updated on first use, but990 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
980 // anyerror needs to wait for everything to be flushed.991 self.files.set(index, .{ .zig_module = .{ .index = index } });
981 if (metadata.text_state != .unused) self.updateLazySymbolAtom(992 break :blk index;
982 File.LazySymbol.initDecl(.code, null, module),993 };
983 metadata.text_atom,
984 self.text_section_index.?,
985 ) catch |err| return switch (err) {
986 error.CodegenFail => error.FlushFailure,
987 else => |e| e,
988 };
989 if (metadata.rodata_state != .unused) self.updateLazySymbolAtom(
990 File.LazySymbol.initDecl(.const_data, null, module),
991 metadata.rodata_atom,
992 self.rodata_section_index.?,
993 ) catch |err| return switch (err) {
994 error.CodegenFail => error.FlushFailure,
995 else => |e| e,
996 };
997 }
998 for (self.lazy_syms.values()) |*metadata| {
999 if (metadata.text_state != .unused) metadata.text_state = .flushed;
1000 if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
1001 }
1002
1003 const target_endian = self.base.options.target.cpu.arch.endian();
1004 const foreign_endian = target_endian != builtin.cpu.arch.endian();
1005
1006 if (self.dwarf) |*dw| {
1007 try dw.flushModule(module);
1008 }
1009
1010 {
1011 var it = self.relocs.iterator();
1012 while (it.next()) |entry| {
1013 const atom_index = entry.key_ptr.*;
1014 const relocs = entry.value_ptr.*;
1015 const atom_ptr = self.atom(atom_index);
1016 const source_sym = atom_ptr.symbol(self);
1017 const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx];
1018
1019 log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)});
1020
1021 for (relocs.items) |*reloc| {
1022 const target_sym = self.locals.items[reloc.target];
1023 const target_vaddr = target_sym.st_value + reloc.addend;
1024
1025 if (target_vaddr == reloc.prev_vaddr) continue;
1026
1027 const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;
1028 const file_offset = source_shdr.sh_offset + section_offset;
1029
1030 log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{
1031 reloc.offset,
1032 target_vaddr,
1033 self.strtab.get(target_sym.st_name),
1034 });
1035
1036 switch (self.ptr_width) {
1037 .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset),
1038 .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset),
1039 }
1040
1041 reloc.prev_vaddr = target_vaddr;
1042 }
1043 }
1044 }
1045
1046 try self.writeSymbols();
1047
1048 if (build_options.enable_logging) {
1049 self.logSymtab();
1050 }
1051
1052 if (self.dwarf) |*dw| {
1053 if (self.debug_abbrev_section_dirty) {
1054 try dw.writeDbgAbbrev();
1055 if (!self.shdr_table_dirty) {
1056 // Then it won't get written with the others and we need to do it.
1057 try self.writeSectHeader(self.debug_abbrev_section_index.?);
1058 }
1059 self.debug_abbrev_section_dirty = false;
1060 }
1061
1062 if (self.debug_info_header_dirty) {
1063 // Currently only one compilation unit is supported, so the address range is simply
1064 // identical to the main program header virtual address and memory size.
1065 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1066 const low_pc = text_phdr.p_vaddr;
1067 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
1068 try dw.writeDbgInfoHeader(module, low_pc, high_pc);
1069 self.debug_info_header_dirty = false;
1070 }
1071
1072 if (self.debug_aranges_section_dirty) {
1073 // Currently only one compilation unit is supported, so the address range is simply
1074 // identical to the main program header virtual address and memory size.
1075 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1076 try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
1077 if (!self.shdr_table_dirty) {
1078 // Then it won't get written with the others and we need to do it.
1079 try self.writeSectHeader(self.debug_aranges_section_index.?);
1080 }
1081 self.debug_aranges_section_dirty = false;
1082 }
1083
1084 if (self.debug_line_header_dirty) {
1085 try dw.writeDbgLineHeader();
1086 self.debug_line_header_dirty = false;
1087 }
1088 }
1089
1090 if (self.phdr_table_dirty) {
1091 const phsize: u64 = switch (self.ptr_width) {
1092 .p32 => @sizeOf(elf.Elf32_Phdr),
1093 .p64 => @sizeOf(elf.Elf64_Phdr),
1094 };
1095
1096 const phdr_table_index = self.phdr_table_index.?;
1097 const phdr_table = &self.program_headers.items[phdr_table_index];
1098 const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?];
1099
1100 const allocated_size = self.allocatedSize(phdr_table.p_offset);
1101 const needed_size = self.program_headers.items.len * phsize;
1102
1103 if (needed_size > allocated_size) {
1104 phdr_table.p_offset = 0; // free the space
1105 phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align)));
1106 }
1107
1108 phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align);
1109 const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
1110 phdr_table_load.p_filesz = load_align_offset + needed_size;
1111 phdr_table_load.p_memsz = load_align_offset + needed_size;
1112
1113 phdr_table.p_filesz = needed_size;
1114 phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
1115 phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
1116 phdr_table.p_memsz = needed_size;
1117
1118 switch (self.ptr_width) {
1119 .p32 => {
1120 const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1121 defer gpa.free(buf);
1122
1123 for (buf, 0..) |*phdr, i| {
1124 phdr.* = progHeaderTo32(self.program_headers.items[i]);
1125 if (foreign_endian) {
1126 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
1127 }
1128 }
1129 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
1130 },
1131 .p64 => {
1132 const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1133 defer gpa.free(buf);
1134
1135 for (buf, 0..) |*phdr, i| {
1136 phdr.* = self.program_headers.items[i];
1137 if (foreign_endian) {
1138 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
1139 }
1140 }
1141 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
1142 },
1143 }
1144
1145 // We don't actually care if the phdr load section overlaps, only the phdr section matters.
1146 phdr_table_load.p_offset = 0;
1147 phdr_table_load.p_filesz = 0;
1148
1149 self.phdr_table_dirty = false;
1150 }
1151
1152 {
1153 const shdr_index = self.shstrtab_section_index.?;
1154 if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1155 try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);
1156 const shstrtab_sect = self.sections.items(.shdr)[shdr_index];
1157 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset);
1158 self.shstrtab_dirty = false;
1159 }
1160 }
1161
1162 {
1163 const shdr_index = self.strtab_section_index.?;
1164 if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1165 try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false);
1166 const strtab_sect = self.sections.items(.shdr)[shdr_index];
1167 try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset);
1168 self.strtab_dirty = false;
1169 }
1170 }
1171
1172 if (self.dwarf) |dwarf| {
1173 const shdr_index = self.debug_str_section_index.?;
1174 if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1175 try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
1176 const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];
1177 try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);
1178 self.debug_strtab_dirty = false;
1179 }
1180 }
1181
1182 if (self.shdr_table_dirty) {
1183 const shsize: u64 = switch (self.ptr_width) {
1184 .p32 => @sizeOf(elf.Elf32_Shdr),
1185 .p64 => @sizeOf(elf.Elf64_Shdr),
1186 };
1187 const shalign: u16 = switch (self.ptr_width) {
1188 .p32 => @alignOf(elf.Elf32_Shdr),
1189 .p64 => @alignOf(elf.Elf64_Shdr),
1190 };
1191 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1192 const needed_size = self.sections.slice().len * shsize;
1193
1194 if (needed_size > allocated_size) {
1195 self.shdr_table_offset = null; // free the space
1196 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
1197 }
1198994
1199 switch (self.ptr_width) {995 self.linker_defined = blk: {
1200 .p32 => {996 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1201 const slice = self.sections.slice();997 self.files.set(index, .{ .linker_defined = .{} });
1202 const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len);998 break :blk index;
1203 defer gpa.free(buf);999 };
12041000
1205 for (buf, 0..) |*shdr, i| {1001 std.debug.print("{}\n", .{self.dumpState()});
1206 shdr.* = sectHeaderTo32(slice.items(.shdr)[i]);1002
1207 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });1003 // if (self.lazy_syms.getPtr(.none)) |metadata| {
1208 if (foreign_endian) {1004 // // Most lazy symbols can be updated on first use, but
1209 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);1005 // // anyerror needs to wait for everything to be flushed.
1210 }1006 // if (metadata.text_state != .unused) self.updateLazySymbolAtom(
1211 }1007 // link.File.LazySymbol.initDecl(.code, null, module),
1212 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);1008 // metadata.text_atom,
1213 },1009 // self.text_section_index.?,
1214 .p64 => {1010 // ) catch |err| return switch (err) {
1215 const slice = self.sections.slice();1011 // error.CodegenFail => error.FlushFailure,
1216 const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len);1012 // else => |e| e,
1217 defer gpa.free(buf);1013 // };
12181014 // if (metadata.rodata_state != .unused) self.updateLazySymbolAtom(
1219 for (buf, 0..) |*shdr, i| {1015 // link.File.LazySymbol.initDecl(.const_data, null, module),
1220 shdr.* = slice.items(.shdr)[i];1016 // metadata.rodata_atom,
1221 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });1017 // self.rodata_section_index.?,
1222 if (foreign_endian) {1018 // ) catch |err| return switch (err) {
1223 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);1019 // error.CodegenFail => error.FlushFailure,
1224 }1020 // else => |e| e,
1225 }1021 // };
1226 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);1022 // }
1227 },1023 // for (self.lazy_syms.values()) |*metadata| {
1228 }1024 // if (metadata.text_state != .unused) metadata.text_state = .flushed;
1229 self.shdr_table_dirty = false;1025 // if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
1230 }1026 // }
1231 if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) {1027
1232 log.debug("flushing. no_entry_point_found = true", .{});1028 // const target_endian = self.base.options.target.cpu.arch.endian();
1233 self.error_flags.no_entry_point_found = true;1029 // const foreign_endian = target_endian != builtin.cpu.arch.endian();
1234 } else {1030
1235 log.debug("flushing. no_entry_point_found = false", .{});1031 // if (self.dwarf) |*dw| {
1236 self.error_flags.no_entry_point_found = false;1032 // try dw.flushModule(module);
1237 try self.writeElfHeader();1033 // }
1238 }1034
12391035 // {
1240 // The point of flush() is to commit changes, so in theory, nothing should1036 // var it = self.relocs.iterator();
1241 // be dirty after this. However, it is possible for some things to remain1037 // while (it.next()) |entry| {
1242 // dirty because they fail to be written in the event of compile errors,1038 // const atom_index = entry.key_ptr.*;
1243 // such as debug_line_header_dirty and debug_info_header_dirty.1039 // const relocs = entry.value_ptr.*;
1244 assert(!self.debug_abbrev_section_dirty);1040 // const atom_ptr = self.atom(atom_index);
1245 assert(!self.debug_aranges_section_dirty);1041 // const source_sym = atom_ptr.symbol(self);
1246 assert(!self.phdr_table_dirty);1042 // const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx];
1247 assert(!self.shdr_table_dirty);1043
1248 assert(!self.shstrtab_dirty);1044 // log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)});
1249 assert(!self.strtab_dirty);1045
1250 assert(!self.debug_strtab_dirty);1046 // for (relocs.items) |*reloc| {
1251 assert(!self.got_table_count_dirty);1047 // const target_sym = self.locals.items[reloc.target];
1048 // const target_vaddr = target_sym.st_value + reloc.addend;
1049
1050 // if (target_vaddr == reloc.prev_vaddr) continue;
1051
1052 // const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;
1053 // const file_offset = source_shdr.sh_offset + section_offset;
1054
1055 // log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{
1056 // reloc.offset,
1057 // target_vaddr,
1058 // self.strtab.get(target_sym.st_name),
1059 // });
1060
1061 // switch (self.ptr_width) {
1062 // .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset),
1063 // .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset),
1064 // }
1065
1066 // reloc.prev_vaddr = target_vaddr;
1067 // }
1068 // }
1069 // }
1070
1071 // try self.writeSymbols();
1072
1073 // if (build_options.enable_logging) {
1074 // self.logSymtab();
1075 // }
1076
1077 // if (self.dwarf) |*dw| {
1078 // if (self.debug_abbrev_section_dirty) {
1079 // try dw.writeDbgAbbrev();
1080 // if (!self.shdr_table_dirty) {
1081 // // Then it won't get written with the others and we need to do it.
1082 // try self.writeSectHeader(self.debug_abbrev_section_index.?);
1083 // }
1084 // self.debug_abbrev_section_dirty = false;
1085 // }
1086
1087 // if (self.debug_info_header_dirty) {
1088 // // Currently only one compilation unit is supported, so the address range is simply
1089 // // identical to the main program header virtual address and memory size.
1090 // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1091 // const low_pc = text_phdr.p_vaddr;
1092 // const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
1093 // try dw.writeDbgInfoHeader(module, low_pc, high_pc);
1094 // self.debug_info_header_dirty = false;
1095 // }
1096
1097 // if (self.debug_aranges_section_dirty) {
1098 // // Currently only one compilation unit is supported, so the address range is simply
1099 // // identical to the main program header virtual address and memory size.
1100 // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1101 // try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
1102 // if (!self.shdr_table_dirty) {
1103 // // Then it won't get written with the others and we need to do it.
1104 // try self.writeSectHeader(self.debug_aranges_section_index.?);
1105 // }
1106 // self.debug_aranges_section_dirty = false;
1107 // }
1108
1109 // if (self.debug_line_header_dirty) {
1110 // try dw.writeDbgLineHeader();
1111 // self.debug_line_header_dirty = false;
1112 // }
1113 // }
1114
1115 // if (self.phdr_table_dirty) {
1116 // const phsize: u64 = switch (self.ptr_width) {
1117 // .p32 => @sizeOf(elf.Elf32_Phdr),
1118 // .p64 => @sizeOf(elf.Elf64_Phdr),
1119 // };
1120
1121 // const phdr_table_index = self.phdr_table_index.?;
1122 // const phdr_table = &self.program_headers.items[phdr_table_index];
1123 // const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?];
1124
1125 // const allocated_size = self.allocatedSize(phdr_table.p_offset);
1126 // const needed_size = self.program_headers.items.len * phsize;
1127
1128 // if (needed_size > allocated_size) {
1129 // phdr_table.p_offset = 0; // free the space
1130 // phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align)));
1131 // }
1132
1133 // phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align);
1134 // const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
1135 // phdr_table_load.p_filesz = load_align_offset + needed_size;
1136 // phdr_table_load.p_memsz = load_align_offset + needed_size;
1137
1138 // phdr_table.p_filesz = needed_size;
1139 // phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
1140 // phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
1141 // phdr_table.p_memsz = needed_size;
1142
1143 // switch (self.ptr_width) {
1144 // .p32 => {
1145 // const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1146 // defer gpa.free(buf);
1147
1148 // for (buf, 0..) |*phdr, i| {
1149 // phdr.* = progHeaderTo32(self.program_headers.items[i]);
1150 // if (foreign_endian) {
1151 // mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
1152 // }
1153 // }
1154 // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
1155 // },
1156 // .p64 => {
1157 // const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1158 // defer gpa.free(buf);
1159
1160 // for (buf, 0..) |*phdr, i| {
1161 // phdr.* = self.program_headers.items[i];
1162 // if (foreign_endian) {
1163 // mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
1164 // }
1165 // }
1166 // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
1167 // },
1168 // }
1169
1170 // // We don't actually care if the phdr load section overlaps, only the phdr section matters.
1171 // phdr_table_load.p_offset = 0;
1172 // phdr_table_load.p_filesz = 0;
1173
1174 // self.phdr_table_dirty = false;
1175 // }
1176
1177 // {
1178 // const shdr_index = self.shstrtab_section_index.?;
1179 // if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1180 // try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);
1181 // const shstrtab_sect = self.sections.items(.shdr)[shdr_index];
1182 // try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset);
1183 // self.shstrtab_dirty = false;
1184 // }
1185 // }
1186
1187 // {
1188 // const shdr_index = self.strtab_section_index.?;
1189 // if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1190 // try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false);
1191 // const strtab_sect = self.sections.items(.shdr)[shdr_index];
1192 // try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset);
1193 // self.strtab_dirty = false;
1194 // }
1195 // }
1196
1197 // if (self.dwarf) |dwarf| {
1198 // const shdr_index = self.debug_str_section_index.?;
1199 // if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1200 // try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
1201 // const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];
1202 // try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);
1203 // self.debug_strtab_dirty = false;
1204 // }
1205 // }
1206
1207 // if (self.shdr_table_dirty) {
1208 // const shsize: u64 = switch (self.ptr_width) {
1209 // .p32 => @sizeOf(elf.Elf32_Shdr),
1210 // .p64 => @sizeOf(elf.Elf64_Shdr),
1211 // };
1212 // const shalign: u16 = switch (self.ptr_width) {
1213 // .p32 => @alignOf(elf.Elf32_Shdr),
1214 // .p64 => @alignOf(elf.Elf64_Shdr),
1215 // };
1216 // const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1217 // const needed_size = self.sections.slice().len * shsize;
1218
1219 // if (needed_size > allocated_size) {
1220 // self.shdr_table_offset = null; // free the space
1221 // self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
1222 // }
1223
1224 // switch (self.ptr_width) {
1225 // .p32 => {
1226 // const slice = self.sections.slice();
1227 // const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len);
1228 // defer gpa.free(buf);
1229
1230 // for (buf, 0..) |*shdr, i| {
1231 // shdr.* = sectHeaderTo32(slice.items(.shdr)[i]);
1232 // log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1233 // if (foreign_endian) {
1234 // mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
1235 // }
1236 // }
1237 // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1238 // },
1239 // .p64 => {
1240 // const slice = self.sections.slice();
1241 // const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len);
1242 // defer gpa.free(buf);
1243
1244 // for (buf, 0..) |*shdr, i| {
1245 // shdr.* = slice.items(.shdr)[i];
1246 // log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1247 // if (foreign_endian) {
1248 // mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
1249 // }
1250 // }
1251 // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1252 // },
1253 // }
1254 // self.shdr_table_dirty = false;
1255 // }
1256 // if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) {
1257 // log.debug("flushing. no_entry_point_found = true", .{});
1258 // self.error_flags.no_entry_point_found = true;
1259 // } else {
1260 // log.debug("flushing. no_entry_point_found = false", .{});
1261 // self.error_flags.no_entry_point_found = false;
1262 // try self.writeElfHeader();
1263 // }
1264
1265 // // The point of flush() is to commit changes, so in theory, nothing should
1266 // // be dirty after this. However, it is possible for some things to remain
1267 // // dirty because they fail to be written in the event of compile errors,
1268 // // such as debug_line_header_dirty and debug_info_header_dirty.
1269 // assert(!self.debug_abbrev_section_dirty);
1270 // assert(!self.debug_aranges_section_dirty);
1271 // assert(!self.phdr_table_dirty);
1272 // assert(!self.shdr_table_dirty);
1273 // assert(!self.shstrtab_dirty);
1274 // assert(!self.strtab_dirty);
1275 // assert(!self.debug_strtab_dirty);
1276 // assert(!self.got_table_count_dirty);
1252}1277}
12531278
1254fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {1279fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
...@@ -2347,7 +2372,7 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {...@@ -2347,7 +2372,7 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
2347 }2372 }
2348}2373}
23492374
2350pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Index {2375pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Atom.Index {
2351 const mod = self.base.options.module.?;2376 const mod = self.base.options.module.?;
2352 const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));2377 const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));
2353 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();2378 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
...@@ -2559,7 +2584,7 @@ pub fn updateDecl(...@@ -2559,7 +2584,7 @@ pub fn updateDecl(
2559 self: *Elf,2584 self: *Elf,
2560 mod: *Module,2585 mod: *Module,
2561 decl_index: Module.Decl.Index,2586 decl_index: Module.Decl.Index,
2562) File.UpdateDeclError!void {2587) link.File.UpdateDeclError!void {
2563 if (build_options.skip_non_native and builtin.object_format != .elf) {2588 if (build_options.skip_non_native and builtin.object_format != .elf) {
2564 @panic("Attempted to compile for object format that was disabled by build configuration");2589 @panic("Attempted to compile for object format that was disabled by build configuration");
2565 }2590 }
...@@ -2634,7 +2659,7 @@ pub fn updateDecl(...@@ -2634,7 +2659,7 @@ pub fn updateDecl(
26342659
2635fn updateLazySymbolAtom(2660fn updateLazySymbolAtom(
2636 self: *Elf,2661 self: *Elf,
2637 sym: File.LazySymbol,2662 sym: link.File.LazySymbol,
2638 atom_index: Atom.Index,2663 atom_index: Atom.Index,
2639 shdr_index: u16,2664 shdr_index: u16,
2640) !void {2665) !void {
...@@ -2776,7 +2801,7 @@ pub fn updateDeclExports(...@@ -2776,7 +2801,7 @@ pub fn updateDeclExports(
2776 mod: *Module,2801 mod: *Module,
2777 decl_index: Module.Decl.Index,2802 decl_index: Module.Decl.Index,
2778 exports: []const *Module.Export,2803 exports: []const *Module.Export,
2779) File.UpdateDeclExportsError!void {2804) link.File.UpdateDeclExportsError!void {
2780 if (build_options.skip_non_native and builtin.object_format != .elf) {2805 if (build_options.skip_non_native and builtin.object_format != .elf) {
2781 @panic("Attempted to compile for object format that was disabled by build configuration");2806 @panic("Attempted to compile for object format that was disabled by build configuration");
2782 }2807 }
...@@ -3420,6 +3445,18 @@ pub fn atomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index {...@@ -3420,6 +3445,18 @@ pub fn atomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index {
3420 return self.atom_by_index_table.get(sym_index);3445 return self.atom_by_index_table.get(sym_index);
3421}3446}
34223447
3448fn dumpState(self: *Elf ) std.fmt.Formatter(fmtDumpState) {
3449 return .{ .data = self };
3450}
3451
3452fn fmtDumpState(self: *Elf,
3453 comptime unused_fmt_string: []const u8,
3454 options: std.fmt.FormatOptions,
3455 writer: anytype,
3456) !void {
3457
3458}
3459
3423pub const null_sym = elf.Elf64_Sym{3460pub const null_sym = elf.Elf64_Sym{
3424 .st_name = 0,3461 .st_name = 0,
3425 .st_info = 0,3462 .st_info = 0,
...@@ -3431,7 +3468,7 @@ pub const null_sym = elf.Elf64_Sym{...@@ -3431,7 +3468,7 @@ pub const null_sym = elf.Elf64_Sym{
34313468
3432const default_entry_addr = 0x8000000;3469const default_entry_addr = 0x8000000;
34333470
3434pub const base_tag: File.Tag = .elf;3471pub const base_tag: link.File.Tag = .elf;
34353472
3436const Section = struct {3473const Section = struct {
3437 shdr: elf.Elf64_Shdr,3474 shdr: elf.Elf64_Shdr,
...@@ -3506,7 +3543,8 @@ pub const Atom = @import("Elf/Atom.zig");...@@ -3506,7 +3543,8 @@ pub const Atom = @import("Elf/Atom.zig");
3506const Cache = std.Build.Cache;3543const Cache = std.Build.Cache;
3507const Compilation = @import("../Compilation.zig");3544const Compilation = @import("../Compilation.zig");
3508const Dwarf = @import("Dwarf.zig");3545const Dwarf = @import("Dwarf.zig");
3509const File = link.File;3546const File = @import("Elf/File.zig");
3547const LinkerDefined = @import("Elf/LinkerDefined.zig");
3510const Liveness = @import("../Liveness.zig");3548const Liveness = @import("../Liveness.zig");
3511const LlvmObject = @import("../codegen/llvm.zig").Object;3549const LlvmObject = @import("../codegen/llvm.zig").Object;
3512const Module = @import("../Module.zig");3550const Module = @import("../Module.zig");
...@@ -3517,3 +3555,4 @@ const TableSection = @import("table_section.zig").TableSection;...@@ -3517,3 +3555,4 @@ const TableSection = @import("table_section.zig").TableSection;
3517const Type = @import("../type.zig").Type;3555const Type = @import("../type.zig").Type;
3518const TypedValue = @import("../TypedValue.zig");3556const TypedValue = @import("../TypedValue.zig");
3519const Value = @import("../value.zig").Value;3557const Value = @import("../value.zig").Value;
3558const ZigModule = @import("Elf/ZigModule.zig");
src/link/Elf/LinkerDefined.zig created+132
...@@ -0,0 +1,132 @@
1index: File.Index,
2symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
3symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
4alive: bool = true,
5
6// output_symtab_size: Elf.SymtabSize = .{},
7
8pub fn deinit(self: *LinkerDefined, allocator: Allocator) void {
9 self.symtab.deinit(allocator);
10 self.symbols.deinit(allocator);
11}
12
13pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 {
14 const gpa = elf_file.base.allocator;
15 try self.symtab.ensureUnusedCapacity(gpa, 1);
16 try self.symbols.ensureUnusedCapacity(gpa, 1);
17 self.symtab.appendAssumeCapacity(.{
18 .st_name = try elf_file.strtab.insert(gpa, name),
19 .st_info = elf.STB_GLOBAL << 4,
20 .st_other = @intFromEnum(elf.STV.HIDDEN),
21 .st_shndx = elf.SHN_ABS,
22 .st_value = 0,
23 .st_size = 0,
24 });
25 const off = try elf_file.internString("{s}", .{name});
26 const gop = try elf_file.getOrCreateGlobal(off);
27 self.symbols.addOneAssumeCapacity().* = gop.index;
28 return gop.index;
29}
30
31pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
32 for (self.symbols.items, 0..) |index, i| {
33 const sym_idx = @as(u32, @intCast(i));
34 const this_sym = self.symtab.items[sym_idx];
35
36 if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
37
38 const global = elf_file.symbol(index);
39 if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
40 global.* = .{
41 .value = 0,
42 .name = global.name,
43 .atom = 0,
44 .file = self.index,
45 .sym_idx = sym_idx,
46 .ver_idx = elf_file.default_sym_version,
47 };
48 }
49 }
50}
51
52// pub fn resetGlobals(self: *LinkerDefined, elf_file: *Elf) void {
53// for (self.symbols.items) |index| {
54// const global = elf_file.getSymbol(index);
55// const name = global.name;
56// global.* = .{};
57// global.name = name;
58// }
59// }
60
61// pub fn calcSymtabSize(self: *InternalObject, elf_file: *Elf) !void {
62// if (elf_file.options.strip_all) return;
63
64// for (self.getGlobals()) |global_index| {
65// const global = elf_file.getSymbol(global_index);
66// if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue;
67// global.flags.output_symtab = true;
68// self.output_symtab_size.nlocals += 1;
69// self.output_symtab_size.strsize += @as(u32, @intCast(global.getName(elf_file).len + 1));
70// }
71// }
72
73// pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: Elf.WriteSymtabCtx) !void {
74// if (elf_file.options.strip_all) return;
75
76// const gpa = elf_file.base.allocator;
77
78// var ilocal = ctx.ilocal;
79// for (self.getGlobals()) |global_index| {
80// const global = elf_file.getSymbol(global_index);
81// if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue;
82// if (!global.flags.output_symtab) continue;
83// const st_name = try ctx.strtab.insert(gpa, global.getName(elf_file));
84// ctx.symtab[ilocal] = global.asElfSym(st_name, elf_file);
85// ilocal += 1;
86// }
87// }
88
89pub fn asFile(self: *LinkerDefined) File {
90 return .{ .linker_defined = self };
91}
92
93pub inline fn getGlobals(self: *LinkerDefined) []const u32 {
94 return self.symbols.items;
95}
96
97pub fn fmtSymtab(self: *InternalObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
98 return .{ .data = .{
99 .self = self,
100 .elf_file = elf_file,
101 } };
102}
103
104const FormatContext = struct {
105 self: *InternalObject,
106 elf_file: *Elf,
107};
108
109fn formatSymtab(
110 ctx: FormatContext,
111 comptime unused_fmt_string: []const u8,
112 options: std.fmt.FormatOptions,
113 writer: anytype,
114) !void {
115 _ = unused_fmt_string;
116 _ = options;
117 try writer.writeAll(" globals\n");
118 for (ctx.self.getGlobals()) |index| {
119 const global = ctx.elf_file.getSymbol(index);
120 try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
121 }
122}
123
124const std = @import("std");
125const elf = std.elf;
126
127const Allocator = std.mem.Allocator;
128const Elf = @import("../Elf.zig");
129const File = @import("file.zig").File;
130const LinkerDefined = @This();
131// const Object = @import("Object.zig");
132const Symbol = @import("Symbol.zig");
src/link/Elf/Symbol.zig created+337
...@@ -0,0 +1,337 @@
1//! Represents a defined symbol.
2
3/// Allocated address value of this symbol.
4value: u64 = 0,
5
6/// Offset into the linker's string table.
7name_offset: u32 = 0,
8
9/// Index of file where this symbol is defined.
10file_index: File.Index = 0,
11
12/// Index of atom containing this symbol.
13/// Index of 0 means there is no associated atom with this symbol.
14/// Use `atom` to get the pointer to the atom.
15atom_index: Atom.Index = 0,
16
17/// Assigned output section index for this atom.
18output_section_index: u16 = 0,
19
20/// Index of the source symbol this symbol references.
21/// Use `getSourceSymbol` to pull the source symbol from the relevant file.
22symbol_index: Index = 0,
23
24/// Index of the source version symbol this symbol references if any.
25/// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL.
26version_index: elf.Elf64_Versym = elf.VER_NDX_LOCAL,
27
28/// Misc flags for the symbol packaged as packed struct for compression.
29flags: Flags = .{},
30
31extra_index: u32 = 0,
32
33pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
34 const file_ptr = symbol.file(elf_file).?;
35 if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS;
36 return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.shndx == 0
37 and file_ptr != .linker_defined and file_ptr != .zig_module;
38}
39
40pub fn isLocal(symbol: Symbol) bool {
41 return !(symbol.flags.import or symbol.flags.@"export");
42}
43
44pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {
45 return symbol.@"type"(elf_file) == elf.STT_GNU_IFUNC;
46}
47
48pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {
49 const file_ptr = symbol.file(elf_file).?;
50 const s_sym = symbol.sourceSymbol(elf_file);
51 if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC;
52 return s_sym.st_type();
53}
54
55pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
56 return elf_file.strtab.getAssumeExists(symbol.name);
57}
58
59pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
60 return elf_file.atom(symbol.atom);
61}
62
63pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
64 return elf_file.file(symbol.file);
65}
66
67pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
68 const file_ptr = symbol.file(elf_file).?;
69 return switch (file_ptr) {
70 .linker_defined, .zig_module => |x| x.symtab.items[symbol.sym_idx],
71 inline else => |x| x.symtab[symbol.sym_idx],
72 };
73}
74
75pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
76 const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32);
77 const sym = symbol.sourceSymbol(elf_file);
78 const in_archive = switch (file) {
79 // .object => |x| !x.alive,
80 else => false,
81 };
82 return file_ptr.symbolRank(sym, in_archive);
83}
84
85pub fn address(symbol: Symbol, opts: struct {
86 plt: bool = true,
87}, elf_file: *Elf) u64 {
88 // if (symbol.flags.copy_rel) {
89 // return elf_file.sectionAddress(elf_file.copy_rel_sect_index.?) + symbol.value;
90 // }
91 // if (symbol.flags.plt and opts.plt) {
92 // const extra = symbol.getExtra(elf_file).?;
93 // if (!symbol.flags.is_canonical and symbol.flags.got) {
94 // // We have a non-lazy bound function pointer, use that!
95 // return elf_file.getPltGotEntryAddress(extra.plt_got);
96 // }
97 // // Lazy-bound function it is!
98 // return elf_file.getPltEntryAddress(extra.plt);
99 // }
100 return symbol.value;
101}
102
103pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 {
104 if (!symbol.flags.got) return 0;
105 const extra = symbol.extra(elf_file).?;
106 return elf_file.gotEntryAddress(extra.got);
107}
108
109// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
110// if (!symbol.flags.tlsgd) return 0;
111// const extra = symbol.getExtra(elf_file).?;
112// return elf_file.getGotEntryAddress(extra.tlsgd);
113// }
114
115// pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {
116// if (!symbol.flags.gottp) return 0;
117// const extra = symbol.getExtra(elf_file).?;
118// return elf_file.getGotEntryAddress(extra.gottp);
119// }
120
121// pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {
122// if (!symbol.flags.tlsdesc) return 0;
123// const extra = symbol.getExtra(elf_file).?;
124// return elf_file.getGotEntryAddress(extra.tlsdesc);
125// }
126
127// pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 {
128// const file = symbol.getFile(elf_file) orelse return 0;
129// const shared = file.shared;
130// const s_sym = symbol.getSourceSymbol(elf_file);
131// const shdr = shared.getShdrs()[s_sym.st_shndx];
132// const alignment = @max(1, shdr.sh_addralign);
133// return if (s_sym.st_value == 0)
134// alignment
135// else
136// @min(alignment, try std.math.powi(u64, 2, @ctz(s_sym.st_value)));
137// }
138
139pub fn addExtra(symbol: *Symbol, extra: Extra, elf_file: *Elf) !void {
140 symbol.extra = try elf_file.addSymbolExtra(extra);
141}
142
143pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra {
144 return elf_file.symbolExtra(symbol.extra);
145}
146
147pub fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void {
148 elf_file.setSymbolExtra(symbol.extra, extra);
149}
150
151pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym {
152 const file_ptr = symbol.file(elf_file).?;
153 const s_sym = symbol.sourceSymbol(elf_file);
154 const st_type = symbol.@"type"(elf_file);
155 const st_bind: u8 = blk: {
156 if (symbol.isLocal()) break :blk 0;
157 if (symbol.flags.weak) break :blk elf.STB_WEAK;
158 // if (file_ptr == .shared) break :blk elf.STB_GLOBAL;
159 break :blk s_sym.st_bind();
160 };
161 const st_shndx = blk: {
162 // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?;
163 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
164 if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module)
165 break :blk elf.SHN_ABS;
166 break :blk symbol.shndx;
167 };
168 const st_value = blk: {
169 // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file);
170 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) {
171 // if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file);
172 // break :blk 0;
173 // }
174 // if (st_shndx == elf.SHN_ABS) break :blk symbol.value;
175 // const shdr = &elf_file.sections.items(.shdr)[st_shndx];
176 // if (Elf.shdrIsTls(shdr)) break :blk symbol.value - elf_file.getTlsAddress();
177 break :blk symbol.value;
178 };
179 return elf.Elf64_Sym{
180 .st_name = st_name,
181 .st_info = (st_bind << 4) | st_type,
182 .st_other = s_sym.st_other,
183 .st_shndx = st_shndx,
184 .st_value = st_value,
185 .st_size = s_sym.st_size,
186 };
187}
188
189pub fn format(
190 symbol: Symbol,
191 comptime unused_fmt_string: []const u8,
192 options: std.fmt.FormatOptions,
193 writer: anytype,
194) !void {
195 _ = symbol;
196 _ = unused_fmt_string;
197 _ = options;
198 _ = writer;
199 @compileError("do not format symbols directly");
200}
201
202const FormatContext = struct {
203 symbol: Symbol,
204 elf_file: *Elf,
205};
206
207pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(formatName) {
208 return .{ .data = .{
209 .symbol = symbol,
210 .elf_file = elf_file,
211 } };
212}
213
214fn formatName(
215 ctx: FormatContext,
216 comptime unused_fmt_string: []const u8,
217 options: std.fmt.FormatOptions,
218 writer: anytype,
219) !void {
220 _ = options;
221 _ = unused_fmt_string;
222 const elf_file = ctx.elf_file;
223 const symbol = ctx.symbol;
224 try writer.writeAll(symbol.getName(elf_file));
225 switch (symbol.ver_idx & elf.VERSYM_VERSION) {
226 elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {},
227 else => {
228 unreachable;
229 // const shared = symbol.getFile(elf_file).?.shared;
230 // try writer.print("@{s}", .{shared.getVersionString(symbol.ver_idx)});
231 },
232 }
233}
234
235pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(format2) {
236 return .{ .data = .{
237 .symbol = symbol,
238 .elf_file = elf_file,
239 } };
240}
241
242fn format2(
243 ctx: FormatContext,
244 comptime unused_fmt_string: []const u8,
245 options: std.fmt.FormatOptions,
246 writer: anytype,
247) !void {
248 _ = options;
249 _ = unused_fmt_string;
250 const symbol = ctx.symbol;
251 try writer.print("%{d} : {s} : @{x}", .{ symbol.sym_idx, symbol.fmtName(ctx.elf_file), symbol.value });
252 if (symbol.getFile(ctx.elf_file)) |file| {
253 if (symbol.isAbs(ctx.elf_file)) {
254 if (symbol.getSourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) {
255 try writer.writeAll(" : undef");
256 } else {
257 try writer.writeAll(" : absolute");
258 }
259 } else if (symbol.shndx != 0) {
260 try writer.print(" : sect({d})", .{symbol.shndx});
261 }
262 if (symbol.getAtom(ctx.elf_file)) |atom| {
263 try writer.print(" : atom({d})", .{atom.atom_index});
264 }
265 var buf: [2]u8 = .{'_'} ** 2;
266 if (symbol.flags.@"export") buf[0] = 'E';
267 if (symbol.flags.import) buf[1] = 'I';
268 try writer.print(" : {s}", .{&buf});
269 if (symbol.flags.weak) try writer.writeAll(" : weak");
270 switch (file) {
271 .internal => |x| try writer.print(" : internal({d})", .{x.index}),
272 .object => |x| try writer.print(" : object({d})", .{x.index}),
273 .shared => |x| try writer.print(" : shared({d})", .{x.index}),
274 }
275 } else try writer.writeAll(" : unresolved");
276}
277
278pub const Flags = packed struct {
279 /// Whether the symbol is imported at runtime.
280 import: bool = false,
281
282 /// Whether the symbol is exported at runtime.
283 @"export": bool = false,
284
285 /// Whether this symbol is weak.
286 weak: bool = false,
287
288 /// Whether the symbol makes into the output symtab or not.
289 output_symtab: bool = false,
290
291 /// Whether the symbol contains GOT indirection.
292 got: bool = false,
293
294 /// Whether the symbol contains PLT indirection.
295 plt: bool = false,
296 /// Whether the PLT entry is canonical.
297 is_canonical: bool = false,
298
299 /// Whether the symbol contains COPYREL directive.
300 copy_rel: bool = false,
301 has_copy_rel: bool = false,
302 has_dynamic: bool = false,
303
304 /// Whether the symbol contains TLSGD indirection.
305 tlsgd: bool = false,
306
307 /// Whether the symbol contains GOTTP indirection.
308 gottp: bool = false,
309
310 /// Whether the symbol contains TLSDESC indirection.
311 tlsdesc: bool = false,
312};
313
314pub const Extra = struct {
315 got: u32 = 0,
316 plt: u32 = 0,
317 plt_got: u32 = 0,
318 dynamic: u32 = 0,
319 copy_rel: u32 = 0,
320 tlsgd: u32 = 0,
321 gottp: u32 = 0,
322 tlsdesc: u32 = 0,
323};
324
325pub const Index = u32;
326
327const std = @import("std");
328const assert = std.debug.assert;
329const elf = std.elf;
330
331const Atom = @import("Atom.zig");
332const Elf = @import("../Elf.zig");
333const File = @import("file.zig").File;
334const InternalObject = @import("InternalObject.zig");
335const Object = @import("Object.zig");
336const SharedObject = @import("SharedObject.zig");
337const Symbol = @This();
src/link/Elf/ZigModule.zig created+69
...@@ -0,0 +1,69 @@
1index: File.Index,
2elf_locals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
3locals: std.ArrayListUnmanaged(Symbol.Index) = .{},
4elf_globals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
5globals: std.ArrayListUnmanaged(Symbol.Index) = .{},
6alive: bool = true,
7
8// output_symtab_size: Elf.SymtabSize = .{},
9
10pub fn deinit(self: *ZigModule, allocator: Allocator) void {
11 self.elf_locals.deinit(allocator);
12 self.locals.deinit(allocator);
13 self.elf_globals.deinit(allocator);
14 self.globals.deinit(allocator);
15}
16
17pub fn asFile(self: *ZigModule) File {
18 return .{ .zig_module = self };
19}
20
21pub fn getLocals(self: *ZigModule) []const Symbol.Index {
22 return self.locals.items;
23}
24
25pub fn getGlobals(self: *ZigModule) []const Symbol.Index {
26 return self.globals.items;
27}
28
29pub fn fmtSymtab(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
30 return .{ .data = .{
31 .self = self,
32 .elf_file = elf_file,
33 } };
34}
35
36const FormatContext = struct {
37 self: *ZigModule,
38 elf_file: *Elf,
39};
40
41fn formatSymtab(
42 ctx: FormatContext,
43 comptime unused_fmt_string: []const u8,
44 options: std.fmt.FormatOptions,
45 writer: anytype,
46) !void {
47 _ = unused_fmt_string;
48 _ = options;
49 try writer.writeAll(" locals\n");
50 for (ctx.self.getLocals()) |index| {
51 const local = ctx.elf_file.symbol(index);
52 try writer.print(" {}\n", .{local.fmt(ctx.elf_file)});
53 }
54 try writer.writeAll(" globals\n");
55 for (ctx.self.getGlobals()) |index| {
56 const global = ctx.elf_file.getSymbol(index);
57 try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
58 }
59}
60
61const std = @import("std");
62const elf = std.elf;
63
64const Allocator = std.mem.Allocator;
65const Elf = @import("../Elf.zig");
66const File = @import("file.zig").File;
67const ZigModule = @This();
68// const Object = @import("Object.zig");
69const Symbol = @import("Symbol.zig");
src/link/Elf/file.zig created+110
...@@ -0,0 +1,110 @@
1pub const File = union(enum) {
2 zig_module: *ZigModule,
3 linker_defined: *LinkerDefined,
4 // object: *Object,
5 // shared_object: *SharedObject,
6
7 pub fn index(file: File) Index {
8 return switch (file) {
9 inline else => |x| x.index,
10 };
11 }
12
13 pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) {
14 return .{ .data = file };
15 }
16
17 fn formatPath(
18 file: File,
19 comptime unused_fmt_string: []const u8,
20 options: std.fmt.FormatOptions,
21 writer: anytype,
22 ) !void {
23 _ = unused_fmt_string;
24 _ = options;
25 switch (file) {
26 .zig_module => try writer.writeAll("(zig module)"),
27 .linker_defined => try writer.writeAll("(linker defined)"),
28 .object => |x| try writer.print("{}", .{x.fmtPath()}),
29 .shared_object => |x| try writer.writeAll(x.path),
30 }
31 }
32
33 pub fn resolveSymbols(file: File, elf_file: *Elf) void {
34 switch (file) {
35 .zig_module => unreachable, // handled separately
36 inline else => |x| x.resolveSymbols(elf_file),
37 }
38 }
39
40 // pub fn resetGlobals(file: File, elf_file: *Elf) void {
41 // switch (file) {
42 // inline else => |x| x.resetGlobals(elf_file),
43 // }
44 // }
45
46 pub fn isAlive(file: File) bool {
47 return switch (file) {
48 .zig_module => true,
49 .linker_defined => true,
50 inline else => |x| x.alive,
51 };
52 }
53
54 /// Encodes symbol rank so that the following ordering applies:
55 /// * strong defined
56 /// * weak defined
57 /// * strong in lib (dso/archive)
58 /// * weak in lib (dso/archive)
59 /// * common
60 /// * common in lib (archive)
61 /// * unclaimed
62 pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 {
63 const base: u3 = blk: {
64 if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5;
65 if (file == .shared or in_archive) break :blk switch (sym.st_bind()) {
66 elf.STB_GLOBAL => 3,
67 else => 4,
68 };
69 break :blk switch (sym.st_bind()) {
70 elf.STB_GLOBAL => 1,
71 else => 2,
72 };
73 };
74 return (@as(u32, base) << 24) + file.index();
75 }
76
77 pub fn setAlive(file: File) void {
78 switch (file) {
79 .zig_module, .linker_defined => {},
80 inline else => |x| x.alive = true,
81 }
82 }
83
84 pub fn markLive(file: File, elf_file: *Elf) void {
85 switch (file) {
86 .zig_module, .linker_defined => {},
87 inline else => |x| x.markLive(elf_file),
88 }
89 }
90
91 pub const Index = u32;
92
93 pub const Entry = union(enum) {
94 null: void,
95 zig_module: ZigModule,
96 linker_defined: LinkerDefined,
97 // object: Object,
98 // shared_object: SharedObject,
99 };
100};
101
102const std = @import("std");
103const elf = std.elf;
104
105const Allocator = std.mem.Allocator;
106const Elf = @import("../Elf.zig");
107const LinkerDefined = @import("LinkerDefined.zig");
108// const Object = @import("Object.zig");
109// const SharedObject = @import("SharedObject.zig");
110const ZigModule = @import("ZigModule.zig");