aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-07-07 10:36:41 +0200
committerJakub Konka <kubkon@jakubkonka.com>2021-07-15 18:49:47 +0200
commit555b66c25567ab23402e3792bdbe81b7a4e98803 (patch)
tree7f4dab9ab3c66caf1c4057b46d5dc068f1104bbc /src
parentdbd2eb7c7f9267e8ae508d0995c1d4c5a3b46309 (diff)
downloadzig-555b66c25567ab23402e3792bdbe81b7a4e98803.tar.gz
zig-555b66c25567ab23402e3792bdbe81b7a4e98803.zip
zld: move should_rebase logic into Symbol
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO/Object.zig58
-rw-r--r--src/link/MachO/Symbol.zig21
-rw-r--r--src/link/MachO/Zld.zig129
-rw-r--r--src/link/MachO/reloc.zig55
4 files changed, 111 insertions, 152 deletions
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 6e8925b648..9925611243 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -339,6 +339,7 @@ const TextBlockParser = struct {
zld: *Zld,
nlists: []NlistWithIndex,
index: u32 = 0,
+ match: Zld.MatchingSection,
fn peek(self: *TextBlockParser) ?NlistWithIndex {
return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
@@ -405,6 +406,8 @@ const TextBlockParser = struct {
const senior_nlist = aliases.pop();
const senior_sym = self.zld.locals.items[senior_nlist.index];
assert(senior_sym.payload == .regular);
+ senior_sym.payload.regular.segment_id = self.match.seg;
+ senior_sym.payload.regular.section_id = self.match.sect;
const start_addr = senior_nlist.nlist.n_value - self.section.addr;
const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size;
@@ -417,6 +420,11 @@ const TextBlockParser = struct {
try out.ensureTotalCapacity(aliases.items.len);
for (aliases.items) |alias| {
out.appendAssumeCapacity(alias.index);
+
+ const sym = self.zld.locals.items[alias.index];
+ const reg = &sym.payload.regular;
+ reg.segment_id = self.match.seg;
+ reg.section_id = self.match.sect;
}
break :blk out.toOwnedSlice();
} else null;
@@ -439,6 +447,18 @@ const TextBlockParser = struct {
try self.object.parseRelocs(self.zld, relocs, block, start_addr);
}
+ const is_zerofill = blk: {
+ const tseg = self.zld.load_commands.items[self.match.seg].Segment;
+ const tsect = tseg.sections.items[self.match.sect];
+ const tsect_type = sectionType(tsect);
+ break :blk tsect_type == macho.S_ZEROFILL or
+ tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or
+ tsect_type == macho.S_THREAD_LOCAL_VARIABLES;
+ };
+ if (is_zerofill) {
+ mem.set(u8, block.code, 0);
+ }
+
self.index += 1;
return block;
@@ -511,28 +531,16 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
.object = self,
.zld = zld,
.nlists = filtered_nlists,
+ .match = match,
};
while (try parser.next()) |block| {
- {
- const sym = zld.locals.items[block.local_sym_index];
- const reg = &sym.payload.regular;
- if (reg.file) |file| {
- if (file != self) {
- log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
- continue;
- }
- }
- reg.segment_id = match.seg;
- reg.section_id = match.sect;
- }
-
- if (block.aliases) |aliases| {
- for (aliases) |alias| {
- const sym = zld.locals.items[alias];
- const reg = &sym.payload.regular;
- reg.segment_id = match.seg;
- reg.section_id = match.sect;
+ const sym = zld.locals.items[block.local_sym_index];
+ const reg = &sym.payload.regular;
+ if (reg.file) |file| {
+ if (file != self) {
+ log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
+ continue;
}
}
@@ -587,6 +595,18 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
try self.parseRelocs(zld, relocs, block, 0);
}
+ const is_zerofill = blk: {
+ const tseg = zld.load_commands.items[match.seg].Segment;
+ const tsect = tseg.sections.items[match.sect];
+ const tsect_type = sectionType(tsect);
+ break :blk tsect_type == macho.S_ZEROFILL or
+ tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or
+ tsect_type == macho.S_THREAD_LOCAL_VARIABLES;
+ };
+ if (is_zerofill) {
+ mem.set(u8, block.code, 0);
+ }
+
if (zld.last_text_block) |last| {
last.next = block;
block.prev = last;
diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig
index 16cd0c9ecc..5f437dc209 100644
--- a/src/link/MachO/Symbol.zig
+++ b/src/link/MachO/Symbol.zig
@@ -2,6 +2,7 @@ const Symbol = @This();
const std = @import("std");
const assert = std.debug.assert;
+const commands = @import("commands.zig");
const macho = std.macho;
const mem = std.mem;
@@ -57,6 +58,8 @@ pub const Regular = struct {
local_sym_index: u32 = 0,
+ should_rebase: bool = false,
+
pub const Linkage = enum {
translation_unit,
linkage_unit,
@@ -74,6 +77,9 @@ pub const Regular = struct {
if (self.weak_ref) {
try std.fmt.format(writer, ".weak_ref, ", .{});
}
+ if (self.should_rebase) {
+ try std.fmt.format(writer, ".should_rebase, ", .{});
+ }
if (self.file) |file| {
try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
}
@@ -108,8 +114,8 @@ pub const Proxy = struct {
/// Dynamic binding info - spots within the final
/// executable where this proxy is referenced from.
bind_info: std.ArrayListUnmanaged(struct {
- segment_id: u16,
- address: u64,
+ local_sym_index: u32,
+ offset: u32,
}) = .{},
/// Dylib where to locate this symbol.
@@ -198,6 +204,17 @@ pub fn isTemp(symbol: Symbol) bool {
return false;
}
+pub fn needsTlvOffset(self: Symbol, zld: *Zld) bool {
+ if (self.payload != .regular) return false;
+
+ const reg = self.payload.regular;
+ const seg = zld.load_command.items[reg.segment_id].Segment;
+ const sect = seg.sections.items[reg.section_id];
+ const sect_type = commands.sectionType(sect);
+
+ return sect_type == macho.S_THREAD_LOCAL_VARIABLES;
+}
+
pub fn asNlist(symbol: *Symbol, strtab: *StringTable) !macho.nlist_64 {
const n_strx = try strtab.getOrPut(symbol.name);
const nlist = nlist: {
diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig
index 69f8821cb7..2f28f20253 100644
--- a/src/link/MachO/Zld.zig
+++ b/src/link/MachO/Zld.zig
@@ -107,8 +107,6 @@ locals: std.ArrayListUnmanaged(*Symbol) = .{},
imports: std.ArrayListUnmanaged(*Symbol) = .{},
globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
-threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
-local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
stubs: std.ArrayListUnmanaged(*Symbol) = .{},
got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
@@ -197,8 +195,6 @@ pub fn init(allocator: *Allocator) !Zld {
}
pub fn deinit(self: *Zld) void {
- self.threadlocal_offsets.deinit(self.allocator);
- self.local_rebases.deinit(self.allocator);
self.stubs.deinit(self.allocator);
self.got_entries.deinit(self.allocator);
@@ -225,8 +221,6 @@ pub fn deinit(self: *Zld) void {
}
self.dylibs.deinit(self.allocator);
- self.globals.deinit(self.allocator);
-
for (self.imports.items) |sym| {
sym.deinit(self.allocator);
self.allocator.destroy(sym);
@@ -239,6 +233,7 @@ pub fn deinit(self: *Zld) void {
}
self.locals.deinit(self.allocator);
+ self.globals.deinit(self.allocator);
self.strtab.deinit();
}
@@ -290,7 +285,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
// try self.allocateDataSegment();
// self.allocateLinkeditSegment();
// try self.allocateSymbols();
- // try self.allocateProxyBindAddresses();
// try self.flush();
}
@@ -449,7 +443,7 @@ fn updateMetadata(self: *Zld) !void {
}
}
-const MatchingSection = struct {
+pub const MatchingSection = struct {
seg: u16,
sect: u16,
};
@@ -1140,31 +1134,6 @@ fn allocateSymbols(self: *Zld) !void {
}
}
-fn allocateProxyBindAddresses(self: *Zld) !void {
- for (self.objects.items) |object| {
- for (object.sections.items) |sect| {
- const relocs = sect.relocs orelse continue;
-
- for (relocs) |rel| {
- if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased
- if (rel.target != .symbol) continue;
-
- const sym = object.symbols.items[rel.target.symbol];
- if (sym.payload != .proxy) continue;
-
- const target_map = sect.target_map orelse continue;
- const target_seg = self.load_commands.items[target_map.segment_id].Segment;
- const target_sect = target_seg.sections.items[target_map.section_id];
-
- try sym.payload.proxy.bind_info.append(self.allocator, .{
- .segment_id = target_map.segment_id,
- .address = target_sect.addr + target_map.offset + rel.offset,
- });
- }
- }
- }
-}
-
fn writeStubHelperCommon(self: *Zld) !void {
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
@@ -1748,72 +1717,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
args.source_source_sect_addr = sect.inner.addr;
args.source_target_sect_addr = source_sect.inner.addr;
}
-
- const sect_type = sectionType(target_sect);
- const should_rebase = rebase: {
- if (!unsigned.is_64bit) break :rebase false;
-
- // TODO actually, a check similar to what dyld is doing, that is, verifying
- // that the segment is writable should be enough here.
- const is_right_segment = blk: {
- if (self.data_segment_cmd_index) |idx| {
- if (target_map.segment_id == idx) {
- break :blk true;
- }
- }
- if (self.data_const_segment_cmd_index) |idx| {
- if (target_map.segment_id == idx) {
- break :blk true;
- }
- }
- break :blk false;
- };
-
- if (!is_right_segment) break :rebase false;
- if (sect_type != macho.S_LITERAL_POINTERS and
- sect_type != macho.S_REGULAR)
- {
- break :rebase false;
- }
- if (rel.target == .symbol) {
- const sym = object.symbols.items[rel.target.symbol];
- if (sym.payload == .proxy) {
- break :rebase false;
- }
- }
-
- break :rebase true;
- };
-
- if (should_rebase) {
- try self.local_rebases.append(self.allocator, .{
- .offset = source_addr - target_seg.inner.vmaddr,
- .segment_id = target_map.segment_id,
- });
- }
-
- // TLV is handled via a separate offset mechanism.
- // Calculate the offset to the initializer.
- if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
- // TODO we don't want to save offset to tlv_bootstrap
- if (mem.eql(u8, object.symbols.items[rel.target.symbol].name, "__tlv_bootstrap")) break :tlv;
-
- const base_addr = blk: {
- if (self.tlv_data_section_index) |index| {
- const tlv_data = target_seg.sections.items[index];
- break :blk tlv_data.addr;
- } else {
- const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
- break :blk tlv_bss.addr;
- }
- };
- // 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, .{
- .source_addr = args.source_addr,
- .offset = args.target_addr - base_addr,
- });
- }
},
.got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
@@ -1839,34 +1742,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
try rel.resolve(args);
}
}
-
- log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{
- segname,
- sectname,
- object.name,
- target_sect_off,
- target_sect_off + sect.code.len,
- });
-
- if (sectionType(target_sect) == macho.S_ZEROFILL or
- sectionType(target_sect) == macho.S_THREAD_LOCAL_ZEROFILL or
- sectionType(target_sect) == macho.S_THREAD_LOCAL_VARIABLES)
- {
- log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
- segmentName(target_sect),
- sectionName(target_sect),
- target_sect_off,
- target_sect_off + sect.code.len,
- });
-
- // Zero-out the space
- var zeroes = try self.allocator.alloc(u8, sect.code.len);
- defer self.allocator.free(zeroes);
- mem.set(u8, zeroes, 0);
- try self.file.?.pwriteAll(zeroes, target_sect_off);
- } else {
- try self.file.?.pwriteAll(sect.code, target_sect_off);
- }
}
}
}
diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig
index 4693e89787..ce95b26252 100644
--- a/src/link/MachO/reloc.zig
+++ b/src/link/MachO/reloc.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const aarch64 = @import("../../codegen/aarch64.zig");
const assert = std.debug.assert;
+const commands = @import("commands.zig");
const log = std.log.scoped(.reloc);
const macho = std.macho;
const math = std.math;
@@ -567,14 +568,60 @@ pub const Parser = struct {
const index = @intCast(u32, self.zld.got_entries.items.len);
out_rel.target.got_index = index;
try self.zld.got_entries.append(self.zld.allocator, out_rel.target);
+
log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index });
- }
+ } else if (out_rel.payload == .unsigned) {
+ const sym = out_rel.target;
+ switch (sym.payload) {
+ .proxy => {
+ try sym.payload.proxy.bind_info.append(self.zld.allocator, .{
+ .local_sym_index = self.block.local_sym_index,
+ .offset = out_rel.offset,
+ });
+ },
+ else => {
+ const source_sym = self.zld.locals.items[self.block.local_sym_index];
+ const source_reg = &source_sym.payload.regular;
+ const seg = self.zld.load_commands.items[source_reg.segment_id].Segment;
+ const sect = seg.sections.items[source_reg.section_id];
+ const sect_type = commands.sectionType(sect);
+
+ const should_rebase = rebase: {
+ if (!out_rel.payload.unsigned.is_64bit) break :rebase false;
+
+ // TODO actually, a check similar to what dyld is doing, that is, verifying
+ // that the segment is writable should be enough here.
+ const is_right_segment = blk: {
+ if (self.zld.data_segment_cmd_index) |idx| {
+ if (source_reg.segment_id == idx) {
+ break :blk true;
+ }
+ }
+ if (self.zld.data_const_segment_cmd_index) |idx| {
+ if (source_reg.segment_id == idx) {
+ break :blk true;
+ }
+ }
+ break :blk false;
+ };
+
+ if (!is_right_segment) break :rebase false;
+ if (sect_type != macho.S_LITERAL_POINTERS and
+ sect_type != macho.S_REGULAR)
+ {
+ break :rebase false;
+ }
- if (out_rel.payload == .branch) {
+ break :rebase true;
+ };
+ source_reg.should_rebase = should_rebase;
+ },
+ }
+ } else if (out_rel.payload == .branch) blk: {
const sym = out_rel.target;
- if (sym.stubs_index != null) continue;
- if (sym.payload != .proxy) continue;
+ if (sym.stubs_index != null) break :blk;
+ if (sym.payload != .proxy) break :blk;
const index = @intCast(u32, self.zld.stubs.items.len);
sym.stubs_index = index;