diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Compilation.zig | 1 | ||||
| -rw-r--r-- | src/link/MachO/Archive.zig | 9 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 194 | ||||
| -rw-r--r-- | src/link/MachO/Symbol.zig | 137 | ||||
| -rw-r--r-- | src/link/MachO/Zld.zig | 889 | ||||
| -rw-r--r-- | src/link/MachO/reloc.zig | 10 | ||||
| -rw-r--r-- | src/link/MachO/reloc/aarch64.zig | 18 | ||||
| -rw-r--r-- | src/link/MachO/reloc/x86_64.zig | 16 | ||||
| -rw-r--r-- | src/stage1/parser.cpp | 11 | ||||
| -rw-r--r-- | src/stage1/stage1.h | 5 | ||||
| -rw-r--r-- | src/stage1/target.cpp | 16 |
11 files changed, 704 insertions, 602 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index 7ff7ef1374..58d6f41858 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -232,7 +232,6 @@ pub const CObject = struct { pub fn destroy(em: *ErrorMsg, gpa: *Allocator) void { gpa.free(em.msg); gpa.destroy(em); - em.* = undefined; } }; diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 5a0b9609ad..702a807a4d 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -16,7 +16,7 @@ allocator: *Allocator, arch: ?std.Target.Cpu.Arch = null, file: ?fs.File = null, header: ?ar_hdr = null, -name: ?[]u8 = null, +name: ?[]const u8 = null, /// Parsed table of contents. /// Each symbol name points to a list of all definition @@ -195,7 +195,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { } /// Caller owns the Object instance. -pub fn parseObject(self: Archive, offset: u32) !Object { +pub fn parseObject(self: Archive, offset: u32) !*Object { var reader = self.file.?.reader(); try reader.context.seekTo(offset); @@ -217,7 +217,10 @@ pub fn parseObject(self: Archive, offset: u32) !Object { break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_name }); }; - var object = Object.init(self.allocator); + var object = try self.allocator.create(Object); + errdefer self.allocator.destroy(object); + + object.* = Object.init(self.allocator); object.arch = self.arch.?; object.file = try fs.cwd().openFile(self.name.?, .{}); object.name = name; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 6703a5bfb7..4d2ade7aad 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -22,7 +22,7 @@ arch: ?std.Target.Cpu.Arch = null, header: ?macho.mach_header_64 = null, file: ?fs.File = null, file_offset: ?u32 = null, -name: ?[]u8 = null, +name: ?[]const u8 = null, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, sections: std.ArrayListUnmanaged(Section) = .{}, @@ -43,17 +43,13 @@ dwarf_debug_str_index: ?u16 = null, dwarf_debug_line_index: ?u16 = null, dwarf_debug_ranges_index: ?u16 = null, -symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, -strtab: std.ArrayListUnmanaged(u8) = .{}, +symbols: std.ArrayListUnmanaged(*Symbol) = .{}, +initializers: std.ArrayListUnmanaged(*Symbol) = .{}, +data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, -locals: std.StringArrayHashMapUnmanaged(Symbol) = .{}, -stabs: std.ArrayListUnmanaged(Stab) = .{}, tu_path: ?[]const u8 = null, tu_mtime: ?u64 = null, -initializers: std.ArrayListUnmanaged(CppStatic) = .{}, -data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, - pub const Section = struct { inner: macho.section_64, code: []u8, @@ -71,23 +67,6 @@ pub const Section = struct { } }; -const CppStatic = struct { - symbol: u32, - target_addr: u64, -}; - -const Stab = struct { - tag: Tag, - symbol: u32, - size: ?u64 = null, - - const Tag = enum { - function, - global, - static, - }; -}; - const DebugInfo = struct { inner: dwarf.DwarfInfo, debug_info: []u8, @@ -169,14 +148,12 @@ pub fn deinit(self: *Object) void { } self.sections.deinit(self.allocator); - for (self.locals.items()) |*entry| { - entry.value.deinit(self.allocator); + for (self.symbols.items) |sym| { + sym.deinit(self.allocator); + self.allocator.destroy(sym); } - self.locals.deinit(self.allocator); + self.symbols.deinit(self.allocator); - self.symtab.deinit(self.allocator); - self.strtab.deinit(self.allocator); - self.stabs.deinit(self.allocator); self.data_in_code_entries.deinit(self.allocator); self.initializers.deinit(self.allocator); @@ -222,9 +199,9 @@ pub fn parse(self: *Object) !void { } try self.readLoadCommands(reader); + try self.parseSymbols(); try self.parseSections(); - if (self.symtab_cmd_index != null) try self.parseSymtab(); - if (self.data_in_code_cmd_index != null) try self.readDataInCode(); + try self.parseDataInCode(); try self.parseInitializers(); try self.parseDebugInfo(); } @@ -298,9 +275,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { } pub fn parseSections(self: *Object) !void { - log.debug("parsing sections in {s}", .{self.name.?}); const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; + log.debug("parsing sections in {s}", .{self.name.?}); + try self.sections.ensureCapacity(self.allocator, seg.sections.items.len); for (seg.sections.items) |sect| { @@ -327,6 +305,7 @@ pub fn parseSections(self: *Object) !void { self.arch.?, section.code, mem.bytesAsSlice(macho.relocation_info, raw_relocs), + self.symbols.items, ); } @@ -344,60 +323,86 @@ pub fn parseInitializers(self: *Object) !void { const relocs = section.relocs orelse unreachable; try self.initializers.ensureCapacity(self.allocator, relocs.len); for (relocs) |rel| { - self.initializers.appendAssumeCapacity(.{ - .symbol = rel.target.symbol, - .target_addr = undefined, - }); + self.initializers.appendAssumeCapacity(rel.target.symbol); } - mem.reverse(CppStatic, self.initializers.items); - - for (self.initializers.items) |initializer| { - const sym = self.symtab.items[initializer.symbol]; - const sym_name = self.getString(sym.n_strx); - log.debug(" | {s}", .{sym_name}); - } + mem.reverse(*Symbol, self.initializers.items); } -pub fn parseSymtab(self: *Object) !void { - const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab; +pub fn parseSymbols(self: *Object) !void { + const index = self.symtab_cmd_index orelse return; + const symtab_cmd = self.load_commands.items[index].Symtab; var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); defer self.allocator.free(symtab); - _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); - try self.symtab.appendSlice(self.allocator, slice); var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); defer self.allocator.free(strtab); - _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); - try self.strtab.appendSlice(self.allocator, strtab); - for (self.symtab.items) |sym, sym_id| { - if (Symbol.isStab(sym) or Symbol.isUndef(sym)) continue; + for (slice) |sym| { + const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); - const sym_name = self.getString(sym.n_strx); - const tag: Symbol.Tag = tag: { - if (Symbol.isLocal(sym)) { - if (self.arch.? == .aarch64 and mem.startsWith(u8, sym_name, "l")) continue; - break :tag .local; + if (Symbol.isStab(sym)) { + log.err("stab {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } + if (Symbol.isIndr(sym)) { + log.err("indirect symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } + if (Symbol.isAbs(sym)) { + log.err("absolute symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } + + const name = try self.allocator.dupe(u8, sym_name); + const symbol: *Symbol = symbol: { + if (Symbol.isSect(sym)) { + const linkage: Symbol.Regular.Linkage = linkage: { + if (!Symbol.isExt(sym)) break :linkage .translation_unit; + if (Symbol.isWeakDef(sym) or Symbol.isPext(sym)) break :linkage .linkage_unit; + break :linkage .global; + }; + const regular = try self.allocator.create(Symbol.Regular); + errdefer self.allocator.destroy(regular); + regular.* = .{ + .base = .{ + .@"type" = .regular, + .name = name, + }, + .linkage = linkage, + .address = sym.n_value, + .section = sym.n_sect - 1, + .weak_ref = Symbol.isWeakRef(sym), + .file = self, + }; + break :symbol ®ular.base; } - if (Symbol.isWeakDef(sym)) { - break :tag .weak; + + if (sym.n_value != 0) { + log.err("common symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + // const comm_size = sym.n_value; + // const comm_align = (sym.n_desc >> 8) & 0x0f; + // log.warn("Common symbol: size 0x{x}, align 0x{x}", .{ comm_size, comm_align }); } - break :tag .strong; + + const undef = try self.allocator.create(Symbol.Unresolved); + errdefer self.allocator.destroy(undef); + undef.* = .{ + .base = .{ + .@"type" = .unresolved, + .name = name, + }, + .file = self, + }; + break :symbol &undef.base; }; - const name = try self.allocator.dupe(u8, sym_name); - try self.locals.putNoClobber(self.allocator, name, .{ - .tag = tag, - .name = name, - .address = 0, - .section = 0, - .index = @intCast(u32, sym_id), - }); + try self.symbols.append(self.allocator, symbol); } } @@ -429,38 +434,31 @@ pub fn parseDebugInfo(self: *Object) !void { break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000)); }; - for (self.locals.items()) |entry, index| { - const local = entry.value; - const source_sym = self.symtab.items[local.index.?]; - const size = blk: for (debug_info.inner.func_list.items) |func| { - if (func.pc_range) |range| { - if (source_sym.n_value >= range.start and source_sym.n_value < range.end) { - break :blk range.end - range.start; + for (self.symbols.items) |sym| { + if (sym.cast(Symbol.Regular)) |reg| { + const size: u64 = blk: for (debug_info.inner.func_list.items) |func| { + if (func.pc_range) |range| { + if (reg.address >= range.start and reg.address < range.end) { + break :blk range.end - range.start; + } } - } - } else null; - const tag: Stab.Tag = tag: { - if (size != null) break :tag .function; - switch (local.tag) { - .weak, .strong => break :tag .global, - else => break :tag .static, - } - }; - - try self.stabs.append(self.allocator, .{ - .tag = tag, - .size = size, - .symbol = @intCast(u32, index), - }); + } else 0; + + reg.stab = .{ + .kind = kind: { + if (size > 0) break :kind .function; + switch (reg.linkage) { + .translation_unit => break :kind .static, + else => break :kind .global, + } + }, + .size = size, + }; + } } } -pub fn getString(self: *const Object, str_off: u32) []const u8 { - assert(str_off < self.strtab.items.len); - return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off)); -} - -pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { +fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; const sect = seg.sections.items[index]; var buffer = try allocator.alloc(u8, sect.size); @@ -468,7 +466,7 @@ pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { return buffer; } -pub fn readDataInCode(self: *Object) !void { +pub fn parseDataInCode(self: *Object) !void { const index = self.data_in_code_cmd_index orelse return; const data_in_code = self.load_commands.items[index].LinkeditData; diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 9e6c2bf68a..f928c807a3 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -2,31 +2,113 @@ const Symbol = @This(); const std = @import("std"); const macho = std.macho; +const mem = std.mem; -const Allocator = std.mem.Allocator; +const Allocator = mem.Allocator; +const Object = @import("Object.zig"); -pub const Tag = enum { - local, - weak, - strong, - import, - undef, +pub const Type = enum { + regular, + proxy, + unresolved, }; -tag: Tag, +/// Symbol type. +@"type": Type, + +/// Symbol name. Owned slice. name: []u8, -address: u64, -section: u8, -/// Index of file where to locate this symbol. -/// Depending on context, this is either an object file, or a dylib. -file: ?u16 = null, +/// Alias of. +alias: ?*Symbol = null, + +/// Index in GOT table for indirection. +got_index: ?u32 = null, + +/// Index in stubs table for late binding. +stubs_index: ?u32 = null, + +pub const Regular = struct { + base: Symbol, + + /// Linkage type. + linkage: Linkage, + + /// Symbol address. + address: u64, + + /// Section ID where the symbol resides. + section: u8, + + /// Whether the symbol is a weak ref. + weak_ref: bool, + + /// File where to locate this symbol. + file: *Object, + + /// Debug stab if defined. + stab: ?struct { + /// Stab kind + kind: enum { + function, + global, + static, + }, + + /// Size of the stab. + size: u64, + } = null, + + pub const base_type: Symbol.Type = .regular; + + pub const Linkage = enum { + translation_unit, + linkage_unit, + global, + }; + + pub fn isTemp(regular: *Regular) bool { + if (regular.linkage == .translation_unit) { + return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L"); + } + return false; + } +}; + +pub const Proxy = struct { + base: Symbol, -/// Index of this symbol within the file's symbol table. -index: ?u32 = null, + /// Dylib ordinal. + dylib: u16, -pub fn deinit(self: *Symbol, allocator: *Allocator) void { - allocator.free(self.name); + pub const base_type: Symbol.Type = .proxy; +}; + +pub const Unresolved = struct { + base: Symbol, + + /// File where this symbol was referenced. + file: *Object, + + pub const base_type: Symbol.Type = .unresolved; +}; + +pub fn deinit(base: *Symbol, allocator: *Allocator) void { + allocator.free(base.name); +} + +pub fn cast(base: *Symbol, comptime T: type) ?*T { + if (base.@"type" != T.base_type) { + return null; + } + return @fieldParentPtr(T, "base", base); +} + +pub fn getTopmostAlias(base: *Symbol) *Symbol { + if (base.alias) |alias| { + return alias.getTopmostAlias(); + } + return base; } pub fn isStab(sym: macho.nlist_64) bool { @@ -51,21 +133,20 @@ pub fn isUndf(sym: macho.nlist_64) bool { return type_ == macho.N_UNDF; } -pub fn isWeakDef(sym: macho.nlist_64) bool { - return (sym.n_desc & macho.N_WEAK_DEF) != 0; +pub fn isIndr(sym: macho.nlist_64) bool { + const type_ = macho.N_TYPE & sym.n_type; + return type_ == macho.N_INDR; } -/// Symbol is local if it is defined and not an extern. -pub fn isLocal(sym: macho.nlist_64) bool { - return isSect(sym) and !isExt(sym); +pub fn isAbs(sym: macho.nlist_64) bool { + const type_ = macho.N_TYPE & sym.n_type; + return type_ == macho.N_ABS; } -/// Symbol is global if it is defined and an extern. -pub fn isGlobal(sym: macho.nlist_64) bool { - return isSect(sym) and isExt(sym); +pub fn isWeakDef(sym: macho.nlist_64) bool { + return (sym.n_desc & macho.N_WEAK_DEF) != 0; } -/// Symbol is undefined if it is not defined and an extern. -pub fn isUndef(sym: macho.nlist_64) bool { - return isUndf(sym) and isExt(sym); +pub fn isWeakRef(sym: macho.nlist_64) bool { + return (sym.n_desc & macho.N_WEAK_REF) != 0; } diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index a585b1fd1e..4d19da1e97 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -29,8 +29,8 @@ page_size: ?u16 = null, file: ?fs.File = null, out_path: ?[]const u8 = null, -objects: std.ArrayListUnmanaged(Object) = .{}, -archives: std.ArrayListUnmanaged(Archive) = .{}, +objects: std.ArrayListUnmanaged(*Object) = .{}, +archives: std.ArrayListUnmanaged(*Archive) = .{}, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, @@ -58,6 +58,7 @@ stubs_section_index: ?u16 = null, stub_helper_section_index: ?u16 = null, text_const_section_index: ?u16 = null, cstring_section_index: ?u16 = null, +ustring_section_index: ?u16 = null, // __DATA_CONST segment sections got_section_index: ?u16 = null, @@ -74,28 +75,30 @@ data_section_index: ?u16 = null, bss_section_index: ?u16 = null, common_section_index: ?u16 = null, -symtab: std.StringArrayHashMapUnmanaged(Symbol) = .{}, +globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, +imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, +unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, + strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, -threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, +threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, -stubs: std.StringArrayHashMapUnmanaged(u32) = .{}, -got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, +stubs: std.ArrayListUnmanaged(*Symbol) = .{}, +got_entries: std.ArrayListUnmanaged(*Symbol) = .{}, stub_helper_stubs_start_off: ?u64 = null, mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, -const GotEntry = struct { - tag: enum { - local, - import, - }, - index: u32, - target_addr: u64, - file: u16, +const TlvOffset = struct { + source_addr: u64, + offset: u64, + + fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool { + return a.source_addr < b.source_addr; + } }; const MappingKey = struct { @@ -124,15 +127,7 @@ pub fn init(allocator: *Allocator) Zld { pub fn deinit(self: *Zld) void { self.threadlocal_offsets.deinit(self.allocator); self.local_rebases.deinit(self.allocator); - - for (self.stubs.items()) |entry| { - self.allocator.free(entry.key); - } self.stubs.deinit(self.allocator); - - for (self.got_entries.items()) |entry| { - self.allocator.free(entry.key); - } self.got_entries.deinit(self.allocator); for (self.load_commands.items) |*lc| { @@ -140,23 +135,22 @@ pub fn deinit(self: *Zld) void { } self.load_commands.deinit(self.allocator); - for (self.objects.items) |*object| { + for (self.objects.items) |object| { object.deinit(); + self.allocator.destroy(object); } self.objects.deinit(self.allocator); - for (self.archives.items) |*archive| { + for (self.archives.items) |archive| { archive.deinit(); + self.allocator.destroy(archive); } self.archives.deinit(self.allocator); self.mappings.deinit(self.allocator); self.unhandled_sections.deinit(self.allocator); - for (self.symtab.items()) |*entry| { - entry.value.deinit(self.allocator); - } - self.symtab.deinit(self.allocator); + self.globals.deinit(self.allocator); self.strtab.deinit(self.allocator); { @@ -224,10 +218,6 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { try self.allocateDataSegment(); self.allocateLinkeditSegment(); try self.allocateSymbols(); - try self.allocateStubsAndGotEntries(); - try self.allocateCppStatics(); - try self.writeStubHelperCommon(); - try self.resolveRelocsAndWriteSections(); try self.flush(); } @@ -291,17 +281,23 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { for (classified.items) |input| { switch (input.kind) { .object => { - var object = Object.init(self.allocator); + const object = try self.allocator.create(Object); + errdefer self.allocator.destroy(object); + + object.* = Object.init(self.allocator); object.arch = self.arch.?; - object.name = try self.allocator.dupe(u8, input.name); + object.name = input.name; object.file = input.file; try object.parse(); try self.objects.append(self.allocator, object); }, .archive => { - var archive = Archive.init(self.allocator); + const archive = try self.allocator.create(Archive); + errdefer self.allocator.destroy(archive); + + archive.* = Archive.init(self.allocator); archive.arch = self.arch.?; - archive.name = try self.allocator.dupe(u8, input.name); + archive.name = input.name; archive.file = input.file; try archive.parse(); try self.archives.append(self.allocator, archive); @@ -367,23 +363,43 @@ fn updateMetadata(self: *Zld) !void { switch (flags) { macho.S_REGULAR, macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { if (mem.eql(u8, segname, "__TEXT")) { - if (self.text_const_section_index != null) continue; - - self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__const"), - .segname = makeStaticString("__TEXT"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + if (mem.eql(u8, sectname, "__ustring")) { + if (self.ustring_section_index != null) continue; + + self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__ustring"), + .segname = makeStaticString("__TEXT"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_REGULAR, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } else { + if (self.text_const_section_index != null) continue; + + self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__const"), + .segname = makeStaticString("__TEXT"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_REGULAR, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } } else if (mem.eql(u8, segname, "__DATA")) { if (!mem.eql(u8, sectname, "__const")) continue; if (self.data_const_section_index != null) continue; @@ -599,6 +615,50 @@ fn updateMetadata(self: *Zld) !void { }, 0); } } + + tlv_align: { + const has_tlv = + self.tlv_section_index != null or + self.tlv_data_section_index != null or + self.tlv_bss_section_index != null; + + if (!has_tlv) break :tlv_align; + + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + + if (self.tlv_section_index) |index| { + const sect = &seg.sections.items[index]; + sect.@"align" = 3; // __thread_vars is always 8byte aligned + } + + // Apparently __tlv_data and __tlv_bss need to have matching alignment, so fix it up. + // <rdar://problem/24221680> All __thread_data and __thread_bss sections must have same alignment + // https://github.com/apple-opensource/ld64/blob/e28c028b20af187a16a7161d89e91868a450cadc/src/ld/ld.cpp#L1172 + const data_align: u32 = data: { + if (self.tlv_data_section_index) |index| { + const sect = &seg.sections.items[index]; + break :data sect.@"align"; + } + break :tlv_align; + }; + const bss_align: u32 = bss: { + if (self.tlv_bss_section_index) |index| { + const sect = &seg.sections.items[index]; + break :bss sect.@"align"; + } + break :tlv_align; + }; + const max_align = math.max(data_align, bss_align); + + if (self.tlv_data_section_index) |index| { + const sect = &seg.sections.items[index]; + sect.@"align" = max_align; + } + if (self.tlv_bss_section_index) |index| { + const sect = &seg.sections.items[index]; + sect.@"align" = max_align; + } + } } const MatchingSection = struct { @@ -674,10 +734,17 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }, macho.S_REGULAR => { if (mem.eql(u8, segname, "__TEXT")) { - break :blk .{ - .seg = self.text_segment_cmd_index.?, - .sect = self.text_const_section_index.?, - }; + if (mem.eql(u8, sectname, "__ustring")) { + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.ustring_section_index.?, + }; + } else { + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.text_const_section_index.?, + }; + } } else if (mem.eql(u8, segname, "__DATA")) { if (mem.eql(u8, sectname, "__data")) { break :blk .{ @@ -723,6 +790,7 @@ fn sortSections(self: *Zld) !void { &self.stub_helper_section_index, &self.text_const_section_index, &self.cstring_section_index, + &self.ustring_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -805,7 +873,7 @@ fn sortSections(self: *Zld) !void { fn allocateTextSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const nstubs = @intCast(u32, self.stubs.items().len); + const nstubs = @intCast(u32, self.stubs.items.len); const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; seg.inner.fileoff = 0; @@ -856,7 +924,7 @@ fn allocateTextSegment(self: *Zld) !void { fn allocateDataConstSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const nentries = @intCast(u32, self.got_entries.items().len); + const nentries = @intCast(u32, self.got_entries.items.len); const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; @@ -871,7 +939,7 @@ fn allocateDataConstSegment(self: *Zld) !void { fn allocateDataSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const nstubs = @intCast(u32, self.stubs.items().len); + const nstubs = @intCast(u32, self.stubs.items.len); const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; @@ -914,95 +982,46 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64) !void { } fn allocateSymbols(self: *Zld) !void { - for (self.objects.items) |*object, object_id| { - for (object.locals.items()) |*entry| { - const source_sym = object.symtab.items[entry.value.index.?]; - const source_sect_id = source_sym.n_sect - 1; + for (self.objects.items) |object, object_id| { + for (object.symbols.items) |sym| { + const reg = sym.cast(Symbol.Regular) orelse continue; // TODO I am more and more convinced we should store the mapping as part of the Object struct. const target_mapping = self.mappings.get(.{ .object_id = @intCast(u16, object_id), - .source_sect_id = source_sect_id, + .source_sect_id = reg.section, }) orelse { if (self.unhandled_sections.get(.{ .object_id = @intCast(u16, object_id), - .source_sect_id = source_sect_id, + .source_sect_id = reg.section, }) != null) continue; - log.err("section not mapped for symbol '{s}'", .{entry.value.name}); + log.err("section not mapped for symbol '{s}'", .{sym.name}); return error.SectionNotMappedForSymbol; }; const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[source_sect_id]; + const source_sect = source_seg.sections.items[reg.section]; const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; const target_addr = target_sect.addr + target_mapping.offset; - const n_value = source_sym.n_value - source_sect.addr + target_addr; + const address = reg.address - source_sect.addr + target_addr; - log.debug("resolving local symbol '{s}' at 0x{x}", .{ entry.value.name, n_value }); + log.debug("resolving symbol '{s}' at 0x{x}", .{ sym.name, address }); // TODO there might be a more generic way of doing this. - var n_sect: u8 = 0; + var section: u8 = 0; for (self.load_commands.items) |cmd, cmd_id| { if (cmd != .Segment) break; if (cmd_id == target_mapping.target_seg_id) { - n_sect += @intCast(u8, target_mapping.target_sect_id) + 1; + section += @intCast(u8, target_mapping.target_sect_id) + 1; break; } - n_sect += @intCast(u8, cmd.Segment.sections.items.len); - } - - entry.value.address = n_value; - entry.value.section = n_sect; - } - } - - for (self.symtab.items()) |*entry| { - if (entry.value.tag == .import) continue; - - const object_id = entry.value.file orelse unreachable; - const object = self.objects.items[object_id]; - const local = object.locals.get(entry.key) orelse unreachable; - - log.debug("resolving {} symbol '{s}' at 0x{x}", .{ entry.value.tag, entry.key, local.address }); - - entry.value.address = local.address; - entry.value.section = local.section; - } -} - -fn allocateStubsAndGotEntries(self: *Zld) !void { - for (self.got_entries.items()) |*entry| { - if (entry.value.tag == .import) continue; - - const object = self.objects.items[entry.value.file]; - entry.value.target_addr = target_addr: { - if (object.locals.get(entry.key)) |local| { - break :target_addr local.address; + section += @intCast(u8, cmd.Segment.sections.items.len); } - const global = self.symtab.get(entry.key) orelse unreachable; - break :target_addr global.address; - }; - - log.debug("resolving GOT entry '{s}' at 0x{x}", .{ - entry.key, - entry.value.target_addr, - }); - } -} -fn allocateCppStatics(self: *Zld) !void { - for (self.objects.items) |*object| { - for (object.initializers.items) |*initializer| { - const sym = object.symtab.items[initializer.symbol]; - const sym_name = object.getString(sym.n_strx); - initializer.target_addr = object.locals.get(sym_name).?.address; - - log.debug("resolving C++ initializer '{s}' at 0x{x}", .{ - sym_name, - initializer.target_addr, - }); + reg.address = address; + reg.section = section; } } } @@ -1037,8 +1056,8 @@ fn writeStubHelperCommon(self: *Zld) !void { code[9] = 0xff; code[10] = 0x25; { - const dyld_stub_binder = self.got_entries.get("dyld_stub_binder").?; - const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64)); + const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; + const addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); mem.writeIntLittle(u32, code[11..], displacement); } @@ -1081,9 +1100,9 @@ fn writeStubHelperCommon(self: *Zld) !void { code[10] = 0xbf; code[11] = 0xa9; binder_blk_outer: { - const dyld_stub_binder = self.got_entries.get("dyld_stub_binder").?; + const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; const this_addr = stub_helper.addr + 3 * @sizeOf(u32); - const target_addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64)); + const target_addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); binder_blk: { const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk; const literal = math.cast(u18, displacement) catch |_| break :binder_blk; @@ -1134,8 +1153,9 @@ fn writeStubHelperCommon(self: *Zld) !void { } }; - for (self.stubs.items()) |entry| { - const index = entry.value; + for (self.stubs.items) |sym| { + // TODO weak bound pointers + const index = sym.stubs_index orelse unreachable; try self.writeLazySymbolPointer(index); try self.writeStub(index); try self.writeStubInStubHelper(index); @@ -1274,145 +1294,151 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void { try self.file.?.pwriteAll(code, stub_off); } -fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { - const object = self.objects.items[object_id]; +fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { log.debug("resolving symbols in '{s}'", .{object.name}); - for (object.symtab.items) |sym, sym_id| { - if (Symbol.isLocal(sym)) { - // If symbol is local to CU, we don't put it in the global symbol table. - continue; - } else if (Symbol.isGlobal(sym)) { - const sym_name = object.getString(sym.n_strx); - const is_weak = Symbol.isWeakDef(sym) or Symbol.isPext(sym); - const global = self.symtab.getEntry(sym_name) orelse { + for (object.symbols.items) |sym| { + if (sym.cast(Symbol.Regular)) |reg| { + if (reg.linkage == .translation_unit) continue; // Symbol local to TU. + + if (self.unresolved.swapRemove(sym.name)) |entry| { + // Create link to the global. + entry.value.alias = sym; + } + const entry = self.globals.getEntry(sym.name) orelse { // Put new global symbol into the symbol table. - const name = try self.allocator.dupe(u8, sym_name); - try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = if (is_weak) .weak else .strong, - .name = name, - .address = 0, - .section = 0, - .file = object_id, - .index = @intCast(u32, sym_id), - }); + try self.globals.putNoClobber(self.allocator, sym.name, sym); continue; }; - - switch (global.value.tag) { - .weak => { - if (is_weak) continue; // Nothing to do for weak symbol. + const g_sym = entry.value; + const g_reg = g_sym.cast(Symbol.Regular) orelse unreachable; + + switch (g_reg.linkage) { + .translation_unit => unreachable, + .linkage_unit => { + if (reg.linkage == .linkage_unit) { + // Create link to the first encountered linkage_unit symbol. + sym.alias = g_sym; + continue; + } }, - .strong => { - if (!is_weak) { - log.debug("strong symbol '{s}' defined multiple times", .{sym_name}); + .global => { + if (reg.linkage == .global) { + log.debug("symbol '{s}' defined multiple times", .{reg.base.name}); return error.MultipleSymbolDefinitions; } + sym.alias = g_sym; continue; }, - else => {}, } - global.value.tag = if (is_weak) .weak else .strong; - global.value.file = object_id; - global.value.index = @intCast(u32, sym_id); - } else if (Symbol.isUndef(sym)) { - const sym_name = object.getString(sym.n_strx); - if (self.symtab.contains(sym_name)) continue; // Nothing to do if we already found a definition. - - const name = try self.allocator.dupe(u8, sym_name); - try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = .undef, - .name = name, - .address = 0, - .section = 0, - }); + g_sym.alias = sym; + entry.value = sym; + } else if (sym.cast(Symbol.Unresolved)) |und| { + if (self.globals.get(sym.name)) |g_sym| { + sym.alias = g_sym; + continue; + } + if (self.unresolved.get(sym.name)) |u_sym| { + sym.alias = u_sym; + continue; + } + try self.unresolved.putNoClobber(self.allocator, sym.name, sym); } else unreachable; } } fn resolveSymbols(self: *Zld) !void { // First pass, resolve symbols in provided objects. - for (self.objects.items) |object, object_id| { - try self.resolveSymbolsInObject(@intCast(u16, object_id)); + for (self.objects.items) |object| { + try self.resolveSymbolsInObject(object); } // Second pass, resolve symbols in static libraries. var next_sym: usize = 0; - var nsyms: usize = self.symtab.items().len; - while (next_sym < nsyms) : (next_sym += 1) { - const sym = self.symtab.items()[next_sym]; - if (sym.value.tag != .undef) continue; + while (true) { + if (next_sym == self.unresolved.count()) break; - const sym_name = sym.value.name; + const entry = self.unresolved.items()[next_sym]; + const sym = entry.value; + + var reset: bool = false; for (self.archives.items) |archive| { // Check if the entry exists in a static archive. - const offsets = archive.toc.get(sym_name) orelse { + const offsets = archive.toc.get(sym.name) orelse { // No hit. continue; }; assert(offsets.items.len > 0); const object = try archive.parseObject(offsets.items[0]); - const object_id = @intCast(u16, self.objects.items.len); try self.objects.append(self.allocator, object); - try self.resolveSymbolsInObject(object_id); + try self.resolveSymbolsInObject(object); - nsyms = self.symtab.items().len; + reset = true; break; } + + if (reset) { + next_sym = 0; + } else { + next_sym += 1; + } } // Third pass, resolve symbols in dynamic libraries. // TODO Implement libSystem as a hard-coded library, or ship with // a libSystem.B.tbd definition file? - for (self.symtab.items()) |*entry| { - if (entry.value.tag != .undef) continue; + try self.imports.ensureCapacity(self.allocator, self.unresolved.count()); + for (self.unresolved.items()) |entry| { + const proxy = try self.allocator.create(Symbol.Proxy); + errdefer self.allocator.destroy(proxy); + + proxy.* = .{ + .base = .{ + .@"type" = .proxy, + .name = try self.allocator.dupe(u8, entry.key), + }, + .dylib = 0, + }; - entry.value.tag = .import; - entry.value.file = 0; + self.imports.putAssumeCapacityNoClobber(proxy.base.name, &proxy.base); + entry.value.alias = &proxy.base; } + self.unresolved.clearAndFree(self.allocator); // If there are any undefs left, flag an error. - var has_unresolved = false; - for (self.symtab.items()) |entry| { - if (entry.value.tag != .undef) continue; - - has_unresolved = true; - log.err("undefined reference to symbol '{s}'", .{entry.value.name}); - } - if (has_unresolved) { + if (self.unresolved.count() > 0) { + for (self.unresolved.items()) |entry| { + log.err("undefined reference to symbol '{s}'", .{entry.key}); + log.err(" | referenced in {s}", .{ + entry.value.cast(Symbol.Unresolved).?.file.name.?, + }); + } return error.UndefinedSymbolReference; } // Finally put dyld_stub_binder as an Import - var name = try self.allocator.dupe(u8, "dyld_stub_binder"); - try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = .import, - .name = name, - .address = 0, - .section = 0, - .file = 0, - }); + const dyld_stub_binder = try self.allocator.create(Symbol.Proxy); + errdefer self.allocator.destroy(dyld_stub_binder); - { - log.debug("symtab", .{}); - for (self.symtab.items()) |sym| { - switch (sym.value.tag) { - .weak, .strong => { - log.debug(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); - }, - .import => { - log.debug(" | {s} => libSystem.B.dylib", .{sym.key}); - }, - else => unreachable, - } - } - } + dyld_stub_binder.* = .{ + .base = .{ + .@"type" = .proxy, + .name = try self.allocator.dupe(u8, "dyld_stub_binder"), + }, + .dylib = 0, + }; + + try self.imports.putNoClobber( + self.allocator, + dyld_stub_binder.base.name, + &dyld_stub_binder.base, + ); } fn resolveStubsAndGotEntries(self: *Zld) !void { - for (self.objects.items) |object, object_id| { + for (self.objects.items) |object| { log.debug("resolving stubs and got entries from {s}", .{object.name}); for (object.sections.items) |sect| { @@ -1421,42 +1447,32 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { switch (rel.@"type") { .unsigned => continue, .got_page, .got_page_off, .got_load, .got => { - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - - if (self.got_entries.contains(sym_name)) continue; - - // TODO clean this up - const is_import = self.symtab.get(sym_name).?.tag == .import; - var name = try self.allocator.dupe(u8, sym_name); - const index = @intCast(u32, self.got_entries.items().len); - try self.got_entries.putNoClobber(self.allocator, name, .{ - .tag = if (is_import) .import else .local, - .index = index, - .target_addr = 0, - .file = if (is_import) 0 else @intCast(u16, object_id), - }); + const sym = rel.target.symbol.getTopmostAlias(); + if (sym.got_index != null) continue; + + const index = @intCast(u32, self.got_entries.items.len); + sym.got_index = index; + try self.got_entries.append(self.allocator, sym); - log.debug(" | found GOT entry {s}: {}", .{ sym_name, self.got_entries.get(sym_name) }); + log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); }, else => { if (rel.target != .symbol) continue; - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - - if (!Symbol.isUndef(sym)) continue; + const sym = rel.target.symbol.getTopmostAlias(); + assert(sym.@"type" != .unresolved); - const in_globals = self.symtab.get(sym_name) orelse unreachable; + if (sym.stubs_index != null) continue; + if (sym.@"type" != .proxy) continue; + // if (sym.cast(Symbol.Regular)) |reg| { + // if (!reg.weak_ref) continue; + // } - if (in_globals.tag != .import) continue; - if (self.stubs.contains(sym_name)) continue; + const index = @intCast(u32, self.stubs.items.len); + sym.stubs_index = index; + try self.stubs.append(self.allocator, sym); - var name = try self.allocator.dupe(u8, sym_name); - const index = @intCast(u32, self.stubs.items().len); - try self.stubs.putNoClobber(self.allocator, name, index); - - log.debug(" | found stub {s}: {}", .{ sym_name, self.stubs.get(sym_name) }); + log.debug(" | found stub {s}: {*}", .{ sym.name, sym }); }, } } @@ -1464,16 +1480,12 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { } // Finally, put dyld_stub_binder as the final GOT entry - var name = try self.allocator.dupe(u8, "dyld_stub_binder"); - const index = @intCast(u32, self.got_entries.items().len); - try self.got_entries.putNoClobber(self.allocator, name, .{ - .tag = .import, - .index = index, - .target_addr = 0, - .file = 0, - }); + const sym = self.imports.get("dyld_stub_binder") orelse unreachable; + const index = @intCast(u32, self.got_entries.items.len); + sym.got_index = index; + try self.got_entries.append(self.allocator, sym); - log.debug(" | found GOT entry dyld_stub_binder: {}", .{self.got_entries.get("dyld_stub_binder")}); + log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); } fn resolveRelocsAndWriteSections(self: *Zld) !void { @@ -1547,11 +1559,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { // TLV is handled via a separate offset mechanism. // Calculate the offset to the initializer. if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - // TODO we don't want to save offset to tlv_bootstrap - if (mem.eql(u8, sym_name, "__tlv_bootstrap")) break :tlv; + if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv; const base_addr = blk: { if (self.tlv_data_section_index) |index| { @@ -1564,16 +1573,17 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { }; // Since we require TLV data to always preceed TLV bss section, we calculate // offsets wrt to the former if it is defined; otherwise, wrt to the latter. - try self.threadlocal_offsets.append(self.allocator, args.target_addr - base_addr); + try self.threadlocal_offsets.append(self.allocator, .{ + .source_addr = args.source_addr, + .offset = args.target_addr - base_addr, + }); } }, .got_page, .got_page_off, .got_load, .got => { const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const got = dc_seg.sections.items[self.got_section_index.?]; - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - const entry = self.got_entries.get(sym_name) orelse unreachable; - args.target_addr = got.addr + entry.index * @sizeOf(u64); + const final = rel.target.symbol.getTopmostAlias(); + args.target_addr = got.addr + final.got_index.? * @sizeOf(u64); }, else => |tt| { if (tt == .signed and rel.target == .section) { @@ -1620,58 +1630,27 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { } fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) !u64 { - const object = self.objects.items[object_id]; const target_addr = blk: { switch (target) { - .symbol => |sym_id| { - const sym = object.symtab.items[sym_id]; - const sym_name = object.getString(sym.n_strx); - - if (Symbol.isSect(sym)) { - log.debug(" | local symbol '{s}'", .{sym_name}); - if (object.locals.get(sym_name)) |local| { - break :blk local.address; - } - // For temp locals, i.e., symbols prefixed with l... we relocate - // based on section addressing. - const source_sect_id = sym.n_sect - 1; - const target_mapping = self.mappings.get(.{ - .object_id = object_id, - .source_sect_id = source_sect_id, - }) orelse unreachable; - - const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[source_sect_id]; - const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; - const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; - const target_addr = target_sect.addr + target_mapping.offset; - break :blk sym.n_value - source_sect.addr + target_addr; - } else if (self.symtab.get(sym_name)) |global| { - switch (global.tag) { - .weak, .strong => { - log.debug(" | global symbol '{s}'", .{sym_name}); - break :blk global.address; - }, - .import => { - if (self.stubs.get(sym_name)) |index| { - log.debug(" | symbol stub '{s}'", .{sym_name}); - const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const stubs = segment.sections.items[self.stubs_section_index.?]; - break :blk stubs.addr + index * stubs.reserved2; - } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) { - log.debug(" | symbol '__tlv_bootstrap'", .{}); - const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const tlv = segment.sections.items[self.tlv_section_index.?]; - break :blk tlv.addr; - } else { - log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); - return error.FailedToResolveRelocationTarget; - } - }, - else => unreachable, + .symbol => |sym| { + const final = sym.getTopmostAlias(); + if (final.cast(Symbol.Regular)) |reg| { + log.debug(" | regular '{s}'", .{sym.name}); + break :blk reg.address; + } else if (final.cast(Symbol.Proxy)) |proxy| { + if (mem.eql(u8, sym.name, "__tlv_bootstrap")) { + log.debug(" | symbol '__tlv_bootstrap'", .{}); + const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const tlv = segment.sections.items[self.tlv_section_index.?]; + break :blk tlv.addr; } + + log.debug(" | symbol stub '{s}'", .{sym.name}); + const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = segment.sections.items[self.stubs_section_index.?]; + break :blk stubs.addr + proxy.base.stubs_index.? * stubs.reserved2; } else { - log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); + log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name}); return error.FailedToResolveRelocationTarget; } }, @@ -2091,6 +2070,9 @@ fn populateMetadata(self: *Zld) !void { } fn flush(self: *Zld) !void { + try self.writeStubHelperCommon(); + try self.resolveRelocsAndWriteSections(); + if (self.common_section_index) |index| { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const sect = &seg.sections.items[index]; @@ -2120,10 +2102,12 @@ fn flush(self: *Zld) !void { var stream = std.io.fixedBufferStream(buffer); var writer = stream.writer(); + std.sort.sort(TlvOffset, self.threadlocal_offsets.items, {}, TlvOffset.cmp); + const seek_amt = 2 * @sizeOf(u64); - while (self.threadlocal_offsets.popOrNull()) |offset| { + for (self.threadlocal_offsets.items) |tlv| { try writer.context.seekBy(seek_amt); - try writer.writeIntLittle(u64, offset); + try writer.writeIntLittle(u64, tlv.offset); } try self.file.?.pwriteAll(buffer, sect.offset); @@ -2136,10 +2120,10 @@ fn flush(self: *Zld) !void { var initializers = std.ArrayList(u64).init(self.allocator); defer initializers.deinit(); - // TODO sort the initializers globally for (self.objects.items) |object| { for (object.initializers.items) |initializer| { - try initializers.append(initializer.target_addr); + const address = initializer.cast(Symbol.Regular).?.address; + try initializers.append(address); } } @@ -2193,14 +2177,15 @@ fn writeGotEntries(self: *Zld) !void { const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[self.got_section_index.?]; - var buffer = try self.allocator.alloc(u8, self.got_entries.items().len * @sizeOf(u64)); + var buffer = try self.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64)); defer self.allocator.free(buffer); var stream = std.io.fixedBufferStream(buffer); var writer = stream.writer(); - for (self.got_entries.items()) |entry| { - try writer.writeIntLittle(u64, entry.value.target_addr); + for (self.got_entries.items) |sym| { + const address: u64 = if (sym.cast(Symbol.Regular)) |reg| reg.address else 0; + try writer.writeIntLittle(u64, address); } log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len }); @@ -2213,7 +2198,8 @@ fn setEntryPoint(self: *Zld) !void { // entrypoint. For now, assume default of `_main`. const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text = seg.sections.items[self.text_section_index.?]; - const entry_sym = self.symtab.get("_main") orelse return error.MissingMainEntrypoint; + const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; + const entry_sym = sym.cast(Symbol.Regular) orelse unreachable; const ec = &self.load_commands.items[self.main_cmd_index.?].Main; ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr); } @@ -2226,24 +2212,21 @@ fn writeRebaseInfoTable(self: *Zld) !void { pointers.appendSliceAssumeCapacity(self.local_rebases.items); if (self.got_section_index) |idx| { - // TODO this should be cleaned up! const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - for (self.got_entries.items()) |entry| { - if (entry.value.tag == .import) continue; - + for (self.got_entries.items) |sym| { + if (sym.@"type" == .proxy) continue; try pointers.append(.{ - .offset = base_offset + entry.value.index * @sizeOf(u64), + .offset = base_offset + sym.got_index.? * @sizeOf(u64), .segment_id = segment_id, }); } } if (self.mod_init_func_section_index) |idx| { - // TODO audit and investigate this. const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; const base_offset = sect.addr - seg.inner.vmaddr; @@ -2267,10 +2250,10 @@ fn writeRebaseInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_segment_cmd_index.?); - try pointers.ensureCapacity(pointers.items.len + self.stubs.items().len); - for (self.stubs.items()) |entry| { + try pointers.ensureCapacity(pointers.items.len + self.stubs.items.len); + for (self.stubs.items) |sym| { pointers.appendAssumeCapacity(.{ - .offset = base_offset + entry.value * @sizeOf(u64), + .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), .segment_id = segment_id, }); } @@ -2306,21 +2289,16 @@ fn writeBindInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - for (self.got_entries.items()) |entry| { - if (entry.value.tag == .local) continue; - - const dylib_ordinal = dylib_ordinal: { - const sym = self.symtab.get(entry.key) orelse continue; // local indirection - if (sym.tag != .import) continue; // local indirection - break :dylib_ordinal sym.file.? + 1; - }; - - try pointers.append(.{ - .offset = base_offset + entry.value.index * @sizeOf(u64), - .segment_id = segment_id, - .dylib_ordinal = dylib_ordinal, - .name = entry.key, - }); + for (self.got_entries.items) |sym| { + if (sym.cast(Symbol.Proxy)) |proxy| { + const dylib_ordinal = proxy.dylib + 1; + try pointers.append(.{ + .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), + .segment_id = segment_id, + .dylib_ordinal = dylib_ordinal, + .name = proxy.base.name, + }); + } } } @@ -2330,14 +2308,15 @@ fn writeBindInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_segment_cmd_index.?); - const sym = self.symtab.get("__tlv_bootstrap") orelse unreachable; - const dylib_ordinal = sym.file.? + 1; + const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; + const proxy = sym.cast(Symbol.Proxy) orelse unreachable; + const dylib_ordinal = proxy.dylib + 1; try pointers.append(.{ .offset = base_offset, .segment_id = segment_id, .dylib_ordinal = dylib_ordinal, - .name = "__tlv_bootstrap", + .name = proxy.base.name, }); } @@ -2369,20 +2348,16 @@ fn writeLazyBindInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_segment_cmd_index.?); - try pointers.ensureCapacity(self.stubs.items().len); - - for (self.stubs.items()) |entry| { - const dylib_ordinal = dylib_ordinal: { - const sym = self.symtab.get(entry.key) orelse unreachable; - assert(sym.tag == .import); - break :dylib_ordinal sym.file.? + 1; - }; + try pointers.ensureCapacity(self.stubs.items.len); + for (self.stubs.items) |sym| { + const proxy = sym.cast(Symbol.Proxy) orelse unreachable; + const dylib_ordinal = proxy.dylib + 1; pointers.appendAssumeCapacity(.{ - .offset = base_offset + entry.value * @sizeOf(u64), + .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), .segment_id = segment_id, .dylib_ordinal = dylib_ordinal, - .name = entry.key, + .name = sym.name, }); } } @@ -2451,7 +2426,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { else => {}, } } - assert(self.stubs.items().len <= offsets.items.len); + assert(self.stubs.items.len <= offsets.items.len); const stub_size: u4 = switch (self.arch.?) { .x86_64 => 10, @@ -2464,9 +2439,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { else => unreachable, }; var buf: [@sizeOf(u32)]u8 = undefined; - for (self.stubs.items()) |entry| { - const placeholder_off = self.stub_helper_stubs_start_off.? + entry.value * stub_size + off; - mem.writeIntLittle(u32, &buf, offsets.items[entry.value]); + for (self.stubs.items) |sym| { + const index = sym.stubs_index orelse unreachable; + const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; + mem.writeIntLittle(u32, &buf, offsets.items[index]); try self.file.?.pwriteAll(&buf, placeholder_off); } } @@ -2478,12 +2454,13 @@ fn writeExportInfo(self: *Zld) !void { const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; // TODO export items for dylibs - const sym = self.symtab.get("_main") orelse return error.MissingMainEntrypoint; - assert(sym.address >= text_segment.inner.vmaddr); + const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; + const reg = sym.cast(Symbol.Regular) orelse unreachable; + assert(reg.address >= text_segment.inner.vmaddr); try trie.put(.{ - .name = "_main", - .vmaddr_offset = sym.address - text_segment.inner.vmaddr, + .name = sym.name, + .vmaddr_offset = reg.address - text_segment.inner.vmaddr, .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, }); @@ -2511,7 +2488,7 @@ fn writeDebugInfo(self: *Zld) !void { var stabs = std.ArrayList(macho.nlist_64).init(self.allocator); defer stabs.deinit(); - for (self.objects.items) |object, object_id| { + for (self.objects.items) |object| { const tu_path = object.tu_path orelse continue; const tu_mtime = object.tu_mtime orelse continue; const dirname = std.fs.path.dirname(tu_path) orelse "./"; @@ -2540,39 +2517,42 @@ fn writeDebugInfo(self: *Zld) !void { .n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work }); - for (object.stabs.items) |stab| { - const entry = object.locals.items()[stab.symbol]; - const sym = entry.value; + for (object.symbols.items) |sym| { + if (sym.@"type" != .regular) continue; + const reg = sym.cast(Symbol.Regular) orelse unreachable; - switch (stab.tag) { + if (reg.isTemp() or reg.stab == null) continue; + const stab = reg.stab orelse unreachable; + + switch (stab.kind) { .function => { try stabs.append(.{ .n_strx = 0, .n_type = macho.N_BNSYM, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = sym.address, + .n_value = reg.address, }); try stabs.append(.{ .n_strx = try self.makeString(sym.name), .n_type = macho.N_FUN, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = sym.address, + .n_value = reg.address, }); try stabs.append(.{ .n_strx = 0, .n_type = macho.N_FUN, .n_sect = 0, .n_desc = 0, - .n_value = stab.size.?, + .n_value = stab.size, }); try stabs.append(.{ .n_strx = 0, .n_type = macho.N_ENSYM, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = stab.size.?, + .n_value = stab.size, }); }, .global => { @@ -2588,9 +2568,9 @@ fn writeDebugInfo(self: *Zld) !void { try stabs.append(.{ .n_strx = try self.makeString(sym.name), .n_type = macho.N_STSYM, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = sym.address, + .n_value = reg.address, }); }, } @@ -2626,27 +2606,6 @@ fn writeDebugInfo(self: *Zld) !void { dysymtab.nlocalsym = symtab.nsyms; } -fn populateStringTable(self: *Zld) !void { - for (self.objects.items) |*object| { - for (object.symtab.items) |*sym| { - switch (sym.tag) { - .undef, .import => continue, - else => {}, - } - const sym_name = object.getString(sym.inner.n_strx); - const n_strx = try self.makeString(sym_name); - sym.inner.n_strx = n_strx; - } - } - - for (self.symtab.items()) |*entry| { - if (entry.value.tag != .import) continue; - - const n_strx = try self.makeString(entry.key); - entry.value.inner.n_strx = n_strx; - } -} - fn writeSymbolTable(self: *Zld) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; @@ -2654,56 +2613,52 @@ fn writeSymbolTable(self: *Zld) !void { var locals = std.ArrayList(macho.nlist_64).init(self.allocator); defer locals.deinit(); + var exports = std.ArrayList(macho.nlist_64).init(self.allocator); + defer exports.deinit(); + for (self.objects.items) |object| { - for (object.locals.items()) |entry| { - const sym = entry.value; - if (sym.tag != .local) continue; - - try locals.append(.{ - .n_strx = try self.makeString(sym.name), - .n_type = macho.N_SECT, - .n_sect = sym.section, - .n_desc = 0, - .n_value = sym.address, - }); + for (object.symbols.items) |sym| { + const final = sym.getTopmostAlias(); + if (final.@"type" != .regular) continue; + + const reg = final.cast(Symbol.Regular) orelse unreachable; + if (reg.isTemp()) continue; + + switch (reg.linkage) { + .translation_unit => { + try locals.append(.{ + .n_strx = try self.makeString(sym.name), + .n_type = macho.N_SECT, + .n_sect = reg.section, + .n_desc = 0, + .n_value = reg.address, + }); + }, + else => { + try exports.append(.{ + .n_strx = try self.makeString(sym.name), + .n_type = macho.N_SECT | macho.N_EXT, + .n_sect = reg.section, + .n_desc = 0, + .n_value = reg.address, + }); + }, + } } } - var exports = std.ArrayList(macho.nlist_64).init(self.allocator); - defer exports.deinit(); - var undefs = std.ArrayList(macho.nlist_64).init(self.allocator); defer undefs.deinit(); - var undefs_ids = std.StringHashMap(u32).init(self.allocator); - defer undefs_ids.deinit(); - - var undef_id: u32 = 0; - for (self.symtab.items()) |entry| { + for (self.imports.items()) |entry| { const sym = entry.value; - switch (sym.tag) { - .weak, .strong => { - try exports.append(.{ - .n_strx = try self.makeString(sym.name), - .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = sym.section, - .n_desc = 0, - .n_value = sym.address, - }); - }, - .import => { - try undefs.append(.{ - .n_strx = try self.makeString(sym.name), - .n_type = macho.N_UNDF | macho.N_EXT, - .n_sect = 0, - .n_desc = macho.N_SYMBOL_RESOLVER | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, - .n_value = 0, - }); - try undefs_ids.putNoClobber(sym.name, undef_id); - undef_id += 1; - }, - else => unreachable, - } + try undefs.append(.{ + .n_strx = try self.makeString(sym.name), + .n_type = macho.N_UNDF | macho.N_EXT, + .n_sect = 0, + .n_desc = macho.N_SYMBOL_RESOLVER | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, + .n_value = 0, + }); } const nlocals = locals.items.len; @@ -2743,8 +2698,8 @@ fn writeSymbolTable(self: *Zld) !void { const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; - const nstubs = @intCast(u32, self.stubs.items().len); - const ngot_entries = @intCast(u32, self.got_entries.items().len); + const nstubs = @intCast(u32, self.stubs.items.len); + const ngot_entries = @intCast(u32, self.got_entries.items.len); dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; @@ -2764,25 +2719,25 @@ fn writeSymbolTable(self: *Zld) !void { var writer = stream.writer(); stubs.reserved1 = 0; - for (self.stubs.items()) |entry| { - const id = undefs_ids.get(entry.key) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + id); + for (self.stubs.items) |sym| { + const id = self.imports.getIndex(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); } got.reserved1 = nstubs; - for (self.got_entries.items()) |entry| { - if (entry.value.tag == .import) { - const id = undefs_ids.get(entry.key) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + id); + for (self.got_entries.items) |sym| { + if (sym.@"type" == .proxy) { + const id = self.imports.getIndex(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); } else { try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); } } la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; - for (self.stubs.items()) |entry| { - const id = undefs_ids.get(entry.key) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + id); + for (self.stubs.items) |sym| { + const id = self.imports.getIndex(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); } try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff); @@ -2979,3 +2934,33 @@ pub fn parseName(name: *const [16]u8) []const u8 { const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; return name[0..len]; } + +fn printSymbols(self: *Zld) void { + log.debug("globals", .{}); + for (self.globals.items()) |entry| { + const sym = entry.value.cast(Symbol.Regular) orelse unreachable; + log.debug(" | {s} @ {*}", .{ sym.base.name, entry.value }); + log.debug(" => alias of {*}", .{sym.base.alias}); + log.debug(" => linkage {s}", .{sym.linkage}); + log.debug(" => defined in {s}", .{sym.file.name.?}); + } + for (self.objects.items) |object| { + log.debug("locals in {s}", .{object.name.?}); + for (object.symbols.items) |sym| { + log.debug(" | {s} @ {*}", .{ sym.name, sym }); + log.debug(" => alias of {*}", .{sym.alias}); + if (sym.cast(Symbol.Regular)) |reg| { + log.debug(" => linkage {s}", .{reg.linkage}); + } else { + log.debug(" => unresolved", .{}); + } + } + } + log.debug("proxies", .{}); + for (self.imports.items()) |entry| { + const sym = entry.value.cast(Symbol.Proxy) orelse unreachable; + log.debug(" | {s} @ {*}", .{ sym.base.name, entry.value }); + log.debug(" => alias of {*}", .{sym.base.alias}); + log.debug(" => defined in libSystem.B.dylib", .{}); + } +} diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig index 57825149d1..1ce9fa2c2d 100644 --- a/src/link/MachO/reloc.zig +++ b/src/link/MachO/reloc.zig @@ -10,6 +10,7 @@ const aarch64 = @import("reloc/aarch64.zig"); const x86_64 = @import("reloc/x86_64.zig"); const Allocator = mem.Allocator; +const Symbol = @import("Symbol.zig"); pub const Relocation = struct { @"type": Type, @@ -75,12 +76,12 @@ pub const Relocation = struct { }; pub const Target = union(enum) { - symbol: u32, + symbol: *Symbol, section: u16, - pub fn from_reloc(reloc: macho.relocation_info) Target { + pub fn from_reloc(reloc: macho.relocation_info, symbols: []*Symbol) Target { return if (reloc.r_extern == 1) .{ - .symbol = reloc.r_symbolnum, + .symbol = symbols[reloc.r_symbolnum], } else .{ .section = @intCast(u16, reloc.r_symbolnum - 1), }; @@ -136,6 +137,7 @@ pub fn parse( arch: std.Target.Cpu.Arch, code: []u8, relocs: []const macho.relocation_info, + symbols: []*Symbol, ) ![]*Relocation { var it = RelocIterator{ .buffer = relocs, @@ -148,6 +150,7 @@ pub fn parse( .it = &it, .code = code, .parsed = std.ArrayList(*Relocation).init(allocator), + .symbols = symbols, }; defer parser.deinit(); try parser.parse(); @@ -160,6 +163,7 @@ pub fn parse( .it = &it, .code = code, .parsed = std.ArrayList(*Relocation).init(allocator), + .symbols = symbols, }; defer parser.deinit(); try parser.parse(); diff --git a/src/link/MachO/reloc/aarch64.zig b/src/link/MachO/reloc/aarch64.zig index d8e7cebddd..c08934d84b 100644 --- a/src/link/MachO/reloc/aarch64.zig +++ b/src/link/MachO/reloc/aarch64.zig @@ -10,6 +10,7 @@ const reloc = @import("../reloc.zig"); const Allocator = mem.Allocator; const Relocation = reloc.Relocation; +const Symbol = @import("../Symbol.zig"); pub const Branch = struct { base: Relocation, @@ -24,7 +25,7 @@ pub const Branch = struct { log.debug(" | displacement 0x{x}", .{displacement}); var inst = branch.inst; - inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); + inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); } }; @@ -188,6 +189,7 @@ pub const Parser = struct { it: *reloc.RelocIterator, code: []u8, parsed: std.ArrayList(*Relocation), + symbols: []*Symbol, addend: ?u32 = null, subtractor: ?Relocation.Target = null, @@ -273,7 +275,7 @@ pub const Parser = struct { var branch = try parser.allocator.create(Branch); errdefer parser.allocator.destroy(branch); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); branch.* = .{ .base = .{ @@ -294,7 +296,7 @@ pub const Parser = struct { assert(rel.r_length == 2); const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; @@ -400,7 +402,7 @@ pub const Parser = struct { aarch64.Instruction.load_store_register, ), inst) }; } - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var page_off = try parser.allocator.create(PageOff); errdefer parser.allocator.destroy(page_off); @@ -437,7 +439,7 @@ pub const Parser = struct { ), inst); assert(parsed_inst.size == 3); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var page_off = try parser.allocator.create(GotPageOff); errdefer parser.allocator.destroy(page_off); @@ -496,7 +498,7 @@ pub const Parser = struct { } }; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var page_off = try parser.allocator.create(TlvpPageOff); errdefer parser.allocator.destroy(page_off); @@ -531,7 +533,7 @@ pub const Parser = struct { assert(rel.r_pcrel == 0); assert(parser.subtractor == null); - parser.subtractor = Relocation.Target.from_reloc(rel); + parser.subtractor = Relocation.Target.from_reloc(rel, parser.symbols); // Verify SUBTRACTOR is followed by UNSIGNED. const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); @@ -554,7 +556,7 @@ pub const Parser = struct { var unsigned = try parser.allocator.create(reloc.Unsigned); errdefer parser.allocator.destroy(unsigned); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const is_64bit: bool = switch (rel.r_length) { 3 => true, 2 => false, diff --git a/src/link/MachO/reloc/x86_64.zig b/src/link/MachO/reloc/x86_64.zig index cdc90aac90..32f83924e8 100644 --- a/src/link/MachO/reloc/x86_64.zig +++ b/src/link/MachO/reloc/x86_64.zig @@ -9,6 +9,7 @@ const reloc = @import("../reloc.zig"); const Allocator = mem.Allocator; const Relocation = reloc.Relocation; +const Symbol = @import("../Symbol.zig"); pub const Branch = struct { base: Relocation, @@ -95,6 +96,7 @@ pub const Parser = struct { it: *reloc.RelocIterator, code: []u8, parsed: std.ArrayList(*Relocation), + symbols: []*Symbol, subtractor: ?Relocation.Target = null, pub fn deinit(parser: *Parser) void { @@ -145,7 +147,7 @@ pub const Parser = struct { var branch = try parser.allocator.create(Branch); errdefer parser.allocator.destroy(branch); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); branch.* = .{ .base = .{ @@ -165,7 +167,7 @@ pub const Parser = struct { assert(rel.r_length == 2); const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const is_extern = rel.r_extern == 1; const offset = @intCast(u32, rel.r_address); @@ -211,7 +213,7 @@ pub const Parser = struct { const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var got_load = try parser.allocator.create(GotLoad); errdefer parser.allocator.destroy(got_load); @@ -237,7 +239,7 @@ pub const Parser = struct { const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var got = try parser.allocator.create(Got); errdefer parser.allocator.destroy(got); @@ -263,7 +265,7 @@ pub const Parser = struct { const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var tlv = try parser.allocator.create(Tlv); errdefer parser.allocator.destroy(tlv); @@ -288,7 +290,7 @@ pub const Parser = struct { assert(rel.r_pcrel == 0); assert(parser.subtractor == null); - parser.subtractor = Relocation.Target.from_reloc(rel); + parser.subtractor = Relocation.Target.from_reloc(rel, parser.symbols); // Verify SUBTRACTOR is followed by UNSIGNED. const next = @intToEnum(macho.reloc_type_x86_64, parser.it.peek().r_type); @@ -311,7 +313,7 @@ pub const Parser = struct { var unsigned = try parser.allocator.create(reloc.Unsigned); errdefer parser.allocator.destroy(unsigned); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const is_64bit: bool = switch (rel.r_length) { 3 => true, 2 => false, diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp index d57277cd51..f152f245b7 100644 --- a/src/stage1/parser.cpp +++ b/src/stage1/parser.cpp @@ -825,7 +825,16 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { AstNode *return_type = nullptr; if (anytype == nullptr) { exmark = eat_token_if(pc, TokenIdBang); - return_type = ast_expect(pc, ast_parse_type_expr); + return_type = ast_parse_type_expr(pc); + if (return_type == nullptr) { + Token *next = peek_token(pc); + ast_error( + pc, + next, + "expected return type (use 'void' to return nothing), found: '%s'", + token_name(next->id) + ); + } } AstNode *res = ast_create_node(pc, NodeTypeFnProto, first); diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h index 59632b9877..6413914f6e 100644 --- a/src/stage1/stage1.h +++ b/src/stage1/stage1.h @@ -56,7 +56,7 @@ enum TargetSubsystem { // ABI warning -// Synchronize with target.cpp::os_list +// Synchronize with std.Target.Os.Tag and target.cpp::os_list enum Os { OsFreestanding, OsAnanas, @@ -94,6 +94,9 @@ enum Os { OsWASI, OsEmscripten, OsUefi, + OsOpenCL, + OsGLSL450, + OsVulkan, OsOther, }; diff --git a/src/stage1/target.cpp b/src/stage1/target.cpp index 6aa3cfcbd0..5a1e18e152 100644 --- a/src/stage1/target.cpp +++ b/src/stage1/target.cpp @@ -122,6 +122,9 @@ static const Os os_list[] = { OsWASI, OsEmscripten, OsUefi, + OsOpenCL, + OsGLSL450, + OsVulkan, OsOther, }; @@ -213,6 +216,9 @@ Os target_os_enum(size_t index) { ZigLLVM_OSType get_llvm_os_type(Os os_type) { switch (os_type) { case OsFreestanding: + case OsOpenCL: + case OsGLSL450: + case OsVulkan: case OsOther: return ZigLLVM_UnknownOS; case OsAnanas: @@ -330,6 +336,9 @@ const char *target_os_name(Os os_type) { case OsHurd: case OsWASI: case OsEmscripten: + case OsOpenCL: + case OsGLSL450: + case OsVulkan: return ZigLLVMGetOSTypeName(get_llvm_os_type(os_type)); } zig_unreachable(); @@ -733,6 +742,9 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsAMDPAL: case OsHermitCore: case OsHurd: + case OsOpenCL: + case OsGLSL450: + case OsVulkan: zig_panic("TODO c type size in bits for this target"); } zig_unreachable(); @@ -999,6 +1011,10 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsWASI: case OsEmscripten: return ZigLLVM_Musl; + case OsOpenCL: + case OsGLSL450: + case OsVulkan: + return ZigLLVM_UnknownEnvironment; } zig_unreachable(); } |
