aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Compilation.zig1
-rw-r--r--src/link/MachO/Archive.zig9
-rw-r--r--src/link/MachO/Object.zig194
-rw-r--r--src/link/MachO/Symbol.zig137
-rw-r--r--src/link/MachO/Zld.zig889
-rw-r--r--src/link/MachO/reloc.zig10
-rw-r--r--src/link/MachO/reloc/aarch64.zig18
-rw-r--r--src/link/MachO/reloc/x86_64.zig16
-rw-r--r--src/stage1/parser.cpp11
-rw-r--r--src/stage1/stage1.h5
-rw-r--r--src/stage1/target.cpp16
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 &regular.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();
}