Merge pull request #9069 from ziglang/zld-common-syms
zig ld: add common (tentative) symbols support
This commit is contained in:
@@ -54,6 +54,11 @@ pub const Section = struct {
|
||||
inner: macho.section_64,
|
||||
code: []u8,
|
||||
relocs: ?[]*Relocation,
|
||||
target_map: ?struct {
|
||||
segment_id: u16,
|
||||
section_id: u16,
|
||||
offset: u32,
|
||||
} = null,
|
||||
|
||||
pub fn deinit(self: *Section, allocator: *Allocator) void {
|
||||
allocator.free(self.code);
|
||||
@@ -346,15 +351,15 @@ pub fn parseSymbols(self: *Object) !void {
|
||||
const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx));
|
||||
|
||||
if (Symbol.isStab(sym)) {
|
||||
log.err("stab {s} in {s}", .{ sym_name, self.name.? });
|
||||
log.err("unhandled symbol type: 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.? });
|
||||
log.err("unhandled symbol type: indirect {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.? });
|
||||
log.err("unhandled symbol type: absolute {s} in {s}", .{ sym_name, self.name.? });
|
||||
return error.UnhandledSymbolType;
|
||||
}
|
||||
|
||||
@@ -383,11 +388,18 @@ pub fn parseSymbols(self: *Object) !void {
|
||||
}
|
||||
|
||||
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 });
|
||||
const tentative = try self.allocator.create(Symbol.Tentative);
|
||||
errdefer self.allocator.destroy(tentative);
|
||||
tentative.* = .{
|
||||
.base = .{
|
||||
.@"type" = .tentative,
|
||||
.name = name,
|
||||
},
|
||||
.size = sym.n_value,
|
||||
.alignment = (sym.n_desc >> 8) & 0x0f,
|
||||
.file = self,
|
||||
};
|
||||
break :symbol &tentative.base;
|
||||
}
|
||||
|
||||
const undef = try self.allocator.create(Symbol.Unresolved);
|
||||
|
||||
@@ -12,6 +12,7 @@ pub const Type = enum {
|
||||
regular,
|
||||
proxy,
|
||||
unresolved,
|
||||
tentative,
|
||||
};
|
||||
|
||||
/// Symbol type.
|
||||
@@ -60,6 +61,10 @@ pub const Regular = struct {
|
||||
size: u64,
|
||||
} = null,
|
||||
|
||||
/// True if symbol was already committed into the final
|
||||
/// symbol table.
|
||||
visited: bool = false,
|
||||
|
||||
pub const base_type: Symbol.Type = .regular;
|
||||
|
||||
pub const Linkage = enum {
|
||||
@@ -94,6 +99,21 @@ pub const Unresolved = struct {
|
||||
pub const base_type: Symbol.Type = .unresolved;
|
||||
};
|
||||
|
||||
pub const Tentative = struct {
|
||||
base: Symbol,
|
||||
|
||||
/// Symbol size.
|
||||
size: u64,
|
||||
|
||||
/// Symbol alignment as power of two.
|
||||
alignment: u16,
|
||||
|
||||
/// File where this symbol was referenced.
|
||||
file: *Object,
|
||||
|
||||
pub const base_type: Symbol.Type = .tentative;
|
||||
};
|
||||
|
||||
pub fn deinit(base: *Symbol, allocator: *Allocator) void {
|
||||
allocator.free(base.name);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,11 @@ common_section_index: ?u16 = null,
|
||||
globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
|
||||
imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
|
||||
unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
|
||||
tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
|
||||
|
||||
/// Offset into __DATA,__common section.
|
||||
/// Set if the linker found tentative definitions in any of the objects.
|
||||
tentative_defs_offset: u64 = 0,
|
||||
|
||||
strtab: std.ArrayListUnmanaged(u8) = .{},
|
||||
strtab_dir: std.StringHashMapUnmanaged(u32) = .{},
|
||||
@@ -95,9 +100,6 @@ got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
|
||||
|
||||
stub_helper_stubs_start_off: ?u64 = null,
|
||||
|
||||
mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
|
||||
unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
|
||||
|
||||
const TlvOffset = struct {
|
||||
source_addr: u64,
|
||||
offset: u64,
|
||||
@@ -107,18 +109,6 @@ const TlvOffset = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const MappingKey = struct {
|
||||
object_id: u16,
|
||||
source_sect_id: u16,
|
||||
};
|
||||
|
||||
pub const SectionMapping = struct {
|
||||
source_sect_id: u16,
|
||||
target_seg_id: u16,
|
||||
target_sect_id: u16,
|
||||
offset: u32,
|
||||
};
|
||||
|
||||
/// Default path to dyld
|
||||
const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
|
||||
|
||||
@@ -159,9 +149,7 @@ pub fn deinit(self: *Zld) void {
|
||||
}
|
||||
self.dylibs.deinit(self.allocator);
|
||||
|
||||
self.mappings.deinit(self.allocator);
|
||||
self.unhandled_sections.deinit(self.allocator);
|
||||
|
||||
self.tentatives.deinit(self.allocator);
|
||||
self.globals.deinit(self.allocator);
|
||||
self.imports.deinit(self.allocator);
|
||||
self.unresolved.deinit(self.allocator);
|
||||
@@ -239,6 +227,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
|
||||
try self.allocateDataSegment();
|
||||
self.allocateLinkeditSegment();
|
||||
try self.allocateSymbols();
|
||||
try self.allocateTentativeSymbols();
|
||||
try self.flush();
|
||||
}
|
||||
|
||||
@@ -407,46 +396,39 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
|
||||
|
||||
fn mapAndUpdateSections(
|
||||
self: *Zld,
|
||||
object_id: u16,
|
||||
object: *Object,
|
||||
source_sect_id: u16,
|
||||
target_seg_id: u16,
|
||||
target_sect_id: u16,
|
||||
) !void {
|
||||
const object = self.objects.items[object_id];
|
||||
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 = &object.sections.items[source_sect_id];
|
||||
const target_seg = &self.load_commands.items[target_seg_id].Segment;
|
||||
const target_sect = &target_seg.sections.items[target_sect_id];
|
||||
|
||||
const alignment = try math.powi(u32, 2, target_sect.@"align");
|
||||
const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment);
|
||||
const size = mem.alignForwardGeneric(u64, source_sect.size, alignment);
|
||||
const key = MappingKey{
|
||||
.object_id = object_id,
|
||||
.source_sect_id = source_sect_id,
|
||||
};
|
||||
try self.mappings.putNoClobber(self.allocator, key, .{
|
||||
.source_sect_id = source_sect_id,
|
||||
.target_seg_id = target_seg_id,
|
||||
.target_sect_id = target_sect_id,
|
||||
.offset = @intCast(u32, offset),
|
||||
});
|
||||
log.debug("{s}: {s},{s} mapped to {s},{s} from 0x{x} to 0x{x}", .{
|
||||
object.name,
|
||||
parseName(&source_sect.segname),
|
||||
parseName(&source_sect.sectname),
|
||||
const size = mem.alignForwardGeneric(u64, source_sect.inner.size, alignment);
|
||||
|
||||
log.debug("{s}: '{s},{s}' mapped to '{s},{s}' from 0x{x} to 0x{x}", .{
|
||||
object.name.?,
|
||||
parseName(&source_sect.inner.segname),
|
||||
parseName(&source_sect.inner.sectname),
|
||||
parseName(&target_sect.segname),
|
||||
parseName(&target_sect.sectname),
|
||||
offset,
|
||||
offset + size,
|
||||
});
|
||||
|
||||
source_sect.target_map = .{
|
||||
.segment_id = target_seg_id,
|
||||
.section_id = target_sect_id,
|
||||
.offset = @intCast(u32, offset),
|
||||
};
|
||||
target_sect.size = offset + size;
|
||||
}
|
||||
|
||||
fn updateMetadata(self: *Zld) !void {
|
||||
for (self.objects.items) |object, id| {
|
||||
const object_id = @intCast(u16, id);
|
||||
for (self.objects.items) |object| {
|
||||
const object_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
|
||||
const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
|
||||
@@ -699,22 +681,59 @@ fn updateMetadata(self: *Zld) !void {
|
||||
for (object_seg.sections.items) |source_sect, sect_id| {
|
||||
const source_sect_id = @intCast(u16, sect_id);
|
||||
if (self.getMatchingSection(source_sect)) |res| {
|
||||
try self.mapAndUpdateSections(object_id, source_sect_id, res.seg, res.sect);
|
||||
try self.mapAndUpdateSections(object, source_sect_id, res.seg, res.sect);
|
||||
continue;
|
||||
}
|
||||
|
||||
const segname = parseName(&source_sect.segname);
|
||||
const sectname = parseName(&source_sect.sectname);
|
||||
|
||||
log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname });
|
||||
|
||||
try self.unhandled_sections.putNoClobber(self.allocator, .{
|
||||
.object_id = object_id,
|
||||
.source_sect_id = source_sect_id,
|
||||
}, 0);
|
||||
log.debug("section '{s},{s}' will be unmapped", .{
|
||||
parseName(&source_sect.segname),
|
||||
parseName(&source_sect.sectname),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have __DATA,__common section if we have tentative definitions.
|
||||
// Update size and alignment of __DATA,__common section.
|
||||
if (self.tentatives.values().len > 0) {
|
||||
const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const common_section_index = self.common_section_index orelse ind: {
|
||||
self.common_section_index = @intCast(u16, data_seg.sections.items.len);
|
||||
try data_seg.addSection(self.allocator, .{
|
||||
.sectname = makeStaticString("__common"),
|
||||
.segname = makeStaticString("__DATA"),
|
||||
.addr = 0,
|
||||
.size = 0,
|
||||
.offset = 0,
|
||||
.@"align" = 0,
|
||||
.reloff = 0,
|
||||
.nreloc = 0,
|
||||
.flags = macho.S_ZEROFILL,
|
||||
.reserved1 = 0,
|
||||
.reserved2 = 0,
|
||||
.reserved3 = 0,
|
||||
});
|
||||
break :ind self.common_section_index.?;
|
||||
};
|
||||
const common_sect = &data_seg.sections.items[common_section_index];
|
||||
|
||||
var max_align: u16 = 0;
|
||||
var added_size: u64 = 0;
|
||||
for (self.tentatives.values()) |sym| {
|
||||
const tent = sym.cast(Symbol.Tentative) orelse unreachable;
|
||||
max_align = math.max(max_align, tent.alignment);
|
||||
added_size += tent.size;
|
||||
}
|
||||
|
||||
common_sect.@"align" = math.max(common_sect.@"align", max_align);
|
||||
|
||||
const alignment = try math.powi(u32, 2, common_sect.@"align");
|
||||
const offset = mem.alignForwardGeneric(u64, common_sect.size, alignment);
|
||||
const size = mem.alignForwardGeneric(u64, added_size, alignment);
|
||||
|
||||
common_sect.size = offset + size;
|
||||
self.tentative_defs_offset = offset;
|
||||
}
|
||||
|
||||
tlv_align: {
|
||||
const has_tlv =
|
||||
self.tlv_section_index != null or
|
||||
@@ -954,18 +973,34 @@ fn sortSections(self: *Zld) !void {
|
||||
}
|
||||
}
|
||||
|
||||
var it = self.mappings.valueIterator();
|
||||
while (it.next()) |mapping| {
|
||||
if (self.text_segment_cmd_index.? == mapping.target_seg_id) {
|
||||
const new_index = text_index_mapping.get(mapping.target_sect_id) orelse unreachable;
|
||||
mapping.target_sect_id = new_index;
|
||||
} else if (self.data_const_segment_cmd_index.? == mapping.target_seg_id) {
|
||||
const new_index = data_const_index_mapping.get(mapping.target_sect_id) orelse unreachable;
|
||||
mapping.target_sect_id = new_index;
|
||||
} else if (self.data_segment_cmd_index.? == mapping.target_seg_id) {
|
||||
const new_index = data_index_mapping.get(mapping.target_sect_id) orelse unreachable;
|
||||
mapping.target_sect_id = new_index;
|
||||
} else unreachable;
|
||||
for (self.objects.items) |object| {
|
||||
for (object.sections.items) |*sect| {
|
||||
const target_map = sect.target_map orelse continue;
|
||||
|
||||
const new_index = blk: {
|
||||
if (self.text_segment_cmd_index.? == target_map.segment_id) {
|
||||
break :blk text_index_mapping.get(target_map.section_id) orelse unreachable;
|
||||
} else if (self.data_const_segment_cmd_index.? == target_map.segment_id) {
|
||||
break :blk data_const_index_mapping.get(target_map.section_id) orelse unreachable;
|
||||
} else if (self.data_segment_cmd_index.? == target_map.segment_id) {
|
||||
break :blk data_index_mapping.get(target_map.section_id) orelse unreachable;
|
||||
} else unreachable;
|
||||
};
|
||||
|
||||
log.debug("remapping in {s}: '{s},{s}': {} => {}", .{
|
||||
object.name.?,
|
||||
parseName(§.inner.segname),
|
||||
parseName(§.inner.sectname),
|
||||
target_map.section_id,
|
||||
new_index,
|
||||
});
|
||||
|
||||
sect.target_map = .{
|
||||
.segment_id = target_map.segment_id,
|
||||
.section_id = new_index,
|
||||
.offset = target_map.offset,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1080,30 +1115,24 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64) !void {
|
||||
}
|
||||
|
||||
fn allocateSymbols(self: *Zld) !void {
|
||||
for (self.objects.items) |object, object_id| {
|
||||
for (self.objects.items) |object| {
|
||||
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 = reg.section,
|
||||
}) orelse {
|
||||
if (self.unhandled_sections.get(.{
|
||||
.object_id = @intCast(u16, object_id),
|
||||
.source_sect_id = reg.section,
|
||||
}) != null) continue;
|
||||
|
||||
log.err("section not mapped for symbol '{s}'", .{sym.name});
|
||||
return error.SectionNotMappedForSymbol;
|
||||
const source_sect = &object.sections.items[reg.section];
|
||||
const target_map = source_sect.target_map orelse {
|
||||
log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{
|
||||
parseName(&source_sect.inner.segname),
|
||||
parseName(&source_sect.inner.sectname),
|
||||
sym.name,
|
||||
});
|
||||
continue;
|
||||
};
|
||||
|
||||
const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
|
||||
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 address = reg.address - source_sect.addr + target_addr;
|
||||
const target_seg = self.load_commands.items[target_map.segment_id].Segment;
|
||||
const target_sect = target_seg.sections.items[target_map.section_id];
|
||||
const target_addr = target_sect.addr + target_map.offset;
|
||||
const address = reg.address - source_sect.inner.addr + target_addr;
|
||||
|
||||
log.debug("resolving symbol '{s}' at 0x{x}", .{ sym.name, address });
|
||||
|
||||
@@ -1111,8 +1140,8 @@ fn allocateSymbols(self: *Zld) !void {
|
||||
var section: u8 = 0;
|
||||
for (self.load_commands.items) |cmd, cmd_id| {
|
||||
if (cmd != .Segment) break;
|
||||
if (cmd_id == target_mapping.target_seg_id) {
|
||||
section += @intCast(u8, target_mapping.target_sect_id) + 1;
|
||||
if (cmd_id == target_map.segment_id) {
|
||||
section += @intCast(u8, target_map.section_id) + 1;
|
||||
break;
|
||||
}
|
||||
section += @intCast(u8, cmd.Segment.sections.items.len);
|
||||
@@ -1124,6 +1153,74 @@ fn allocateSymbols(self: *Zld) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn allocateTentativeSymbols(self: *Zld) !void {
|
||||
if (self.tentatives.values().len == 0) return;
|
||||
|
||||
const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const common_sect = &data_seg.sections.items[self.common_section_index.?];
|
||||
|
||||
const alignment = try math.powi(u32, 2, common_sect.@"align");
|
||||
var base_address: u64 = common_sect.addr + self.tentative_defs_offset;
|
||||
|
||||
log.debug("base address for tentative definitions 0x{x}", .{base_address});
|
||||
|
||||
// TODO there might be a more generic way of doing this.
|
||||
var section: u8 = 0;
|
||||
for (self.load_commands.items) |cmd, cmd_id| {
|
||||
if (cmd != .Segment) break;
|
||||
if (cmd_id == self.data_segment_cmd_index.?) {
|
||||
section += @intCast(u8, self.common_section_index.?) + 1;
|
||||
break;
|
||||
}
|
||||
section += @intCast(u8, cmd.Segment.sections.items.len);
|
||||
}
|
||||
|
||||
// Convert tentative definitions into regular symbols.
|
||||
for (self.tentatives.values()) |sym, i| {
|
||||
const tent = sym.cast(Symbol.Tentative) orelse unreachable;
|
||||
const reg = try self.allocator.create(Symbol.Regular);
|
||||
errdefer self.allocator.destroy(reg);
|
||||
|
||||
reg.* = .{
|
||||
.base = .{
|
||||
.@"type" = .regular,
|
||||
.name = try self.allocator.dupe(u8, tent.base.name),
|
||||
.got_index = tent.base.got_index,
|
||||
.stubs_index = tent.base.stubs_index,
|
||||
},
|
||||
.linkage = .global,
|
||||
.address = base_address,
|
||||
.section = section,
|
||||
.weak_ref = false,
|
||||
.file = tent.file,
|
||||
.stab = .{
|
||||
.kind = .global,
|
||||
.size = 0,
|
||||
},
|
||||
};
|
||||
|
||||
try self.globals.putNoClobber(self.allocator, reg.base.name, ®.base);
|
||||
tent.base.alias = ®.base;
|
||||
|
||||
if (tent.base.got_index) |idx| {
|
||||
self.got_entries.items[idx] = ®.base;
|
||||
}
|
||||
if (tent.base.stubs_index) |idx| {
|
||||
self.stubs.items[idx] = ®.base;
|
||||
}
|
||||
|
||||
const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment);
|
||||
|
||||
log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{
|
||||
tent.base.name,
|
||||
base_address,
|
||||
address,
|
||||
});
|
||||
|
||||
base_address = address;
|
||||
}
|
||||
}
|
||||
|
||||
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.?];
|
||||
@@ -1399,6 +1496,10 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
|
||||
if (sym.cast(Symbol.Regular)) |reg| {
|
||||
if (reg.linkage == .translation_unit) continue; // Symbol local to TU.
|
||||
|
||||
if (self.tentatives.fetchSwapRemove(sym.name)) |kv| {
|
||||
// Create link to the global.
|
||||
kv.value.alias = sym;
|
||||
}
|
||||
if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {
|
||||
// Create link to the global.
|
||||
kv.value.alias = sym;
|
||||
@@ -1432,15 +1533,49 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
|
||||
|
||||
g_sym.alias = sym;
|
||||
sym_ptr.* = sym;
|
||||
} else if (sym.cast(Symbol.Tentative)) |tent| {
|
||||
if (self.globals.get(sym.name)) |g_sym| {
|
||||
sym.alias = g_sym;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {
|
||||
kv.value.alias = sym;
|
||||
}
|
||||
|
||||
const sym_ptr = self.tentatives.getPtr(sym.name) orelse {
|
||||
// Put new tentative definition symbol into symbol table.
|
||||
try self.tentatives.putNoClobber(self.allocator, sym.name, sym);
|
||||
continue;
|
||||
};
|
||||
|
||||
// Compare by size and pick the largest tentative definition.
|
||||
// We model this like a heap where the tentative definition with the
|
||||
// largest size always washes up on top.
|
||||
const t_sym = sym_ptr.*;
|
||||
const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable;
|
||||
|
||||
if (tent.size < t_tent.size) {
|
||||
sym.alias = t_sym;
|
||||
continue;
|
||||
}
|
||||
|
||||
t_sym.alias = sym;
|
||||
sym_ptr.* = sym;
|
||||
} else if (sym.cast(Symbol.Unresolved)) |und| {
|
||||
if (self.globals.get(sym.name)) |g_sym| {
|
||||
sym.alias = g_sym;
|
||||
continue;
|
||||
}
|
||||
if (self.tentatives.get(sym.name)) |t_sym| {
|
||||
sym.alias = t_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;
|
||||
}
|
||||
@@ -1482,7 +1617,6 @@ fn resolveSymbols(self: *Zld) !void {
|
||||
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?
|
||||
@@ -1602,10 +1736,10 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
|
||||
}
|
||||
|
||||
fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||
for (self.objects.items) |object, object_id| {
|
||||
for (self.objects.items) |object| {
|
||||
log.debug("relocating object {s}", .{object.name});
|
||||
|
||||
for (object.sections.items) |sect, source_sect_id| {
|
||||
for (object.sections.items) |sect| {
|
||||
if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or
|
||||
sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue;
|
||||
|
||||
@@ -1614,18 +1748,15 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||
|
||||
log.debug("relocating section '{s},{s}'", .{ segname, sectname });
|
||||
|
||||
// Get mapping
|
||||
const target_mapping = self.mappings.get(.{
|
||||
.object_id = @intCast(u16, object_id),
|
||||
.source_sect_id = @intCast(u16, source_sect_id),
|
||||
}) orelse {
|
||||
log.debug("no mapping for {s},{s}; skipping", .{ segname, sectname });
|
||||
// Get target mapping
|
||||
const target_map = sect.target_map orelse {
|
||||
log.debug("no mapping for '{s},{s}'; skipping", .{ segname, sectname });
|
||||
continue;
|
||||
};
|
||||
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_sect_addr = target_sect.addr + target_mapping.offset;
|
||||
const target_sect_off = target_sect.offset + target_mapping.offset;
|
||||
const target_seg = self.load_commands.items[target_map.segment_id].Segment;
|
||||
const target_sect = target_seg.sections.items[target_map.section_id];
|
||||
const target_sect_addr = target_sect.addr + target_map.offset;
|
||||
const target_sect_off = target_sect.offset + target_map.offset;
|
||||
|
||||
if (sect.relocs) |relocs| {
|
||||
for (relocs) |rel| {
|
||||
@@ -1638,11 +1769,11 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||
|
||||
switch (rel.@"type") {
|
||||
.unsigned => {
|
||||
args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target);
|
||||
args.target_addr = try self.relocTargetAddr(object, rel.target);
|
||||
|
||||
const unsigned = rel.cast(reloc.Unsigned) orelse unreachable;
|
||||
if (unsigned.subtractor) |subtractor| {
|
||||
args.subtractor = try self.relocTargetAddr(@intCast(u16, object_id), subtractor);
|
||||
args.subtractor = try self.relocTargetAddr(object, subtractor);
|
||||
}
|
||||
if (rel.target == .section) {
|
||||
const source_sect = object.sections.items[rel.target.section];
|
||||
@@ -1652,14 +1783,14 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||
|
||||
rebases: {
|
||||
var hit: bool = false;
|
||||
if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
|
||||
if (target_map.segment_id == self.data_segment_cmd_index.?) {
|
||||
if (self.data_section_index) |index| {
|
||||
if (index == target_mapping.target_sect_id) hit = true;
|
||||
if (index == target_map.section_id) hit = true;
|
||||
}
|
||||
}
|
||||
if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) {
|
||||
if (target_map.segment_id == self.data_const_segment_cmd_index.?) {
|
||||
if (self.data_const_section_index) |index| {
|
||||
if (index == target_mapping.target_sect_id) hit = true;
|
||||
if (index == target_map.section_id) hit = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1667,7 +1798,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||
|
||||
try self.local_rebases.append(self.allocator, .{
|
||||
.offset = source_addr - target_seg.inner.vmaddr,
|
||||
.segment_id = target_mapping.target_seg_id,
|
||||
.segment_id = target_map.segment_id,
|
||||
});
|
||||
}
|
||||
// TLV is handled via a separate offset mechanism.
|
||||
@@ -1705,7 +1836,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||
args.source_source_sect_addr = sect.inner.addr;
|
||||
args.source_target_sect_addr = source_sect.inner.addr;
|
||||
}
|
||||
args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target);
|
||||
args.target_addr = try self.relocTargetAddr(object, rel.target);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1744,7 +1875,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) !u64 {
|
||||
fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 {
|
||||
const target_addr = blk: {
|
||||
switch (target) {
|
||||
.symbol => |sym| {
|
||||
@@ -1770,13 +1901,11 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target)
|
||||
}
|
||||
},
|
||||
.section => |sect_id| {
|
||||
const target_mapping = self.mappings.get(.{
|
||||
.object_id = object_id,
|
||||
.source_sect_id = sect_id,
|
||||
}) orelse unreachable;
|
||||
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];
|
||||
break :blk target_sect.addr + target_mapping.offset;
|
||||
const source_sect = object.sections.items[sect_id];
|
||||
const target_map = source_sect.target_map orelse unreachable;
|
||||
const target_seg = self.load_commands.items[target_map.segment_id].Segment;
|
||||
const target_sect = target_seg.sections.items[target_map.section_id];
|
||||
break :blk target_sect.addr + target_map.offset;
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -2646,8 +2775,17 @@ fn writeDebugInfo(self: *Zld) !void {
|
||||
});
|
||||
|
||||
for (object.symbols.items) |sym| {
|
||||
if (sym.@"type" != .regular) continue;
|
||||
const reg = sym.cast(Symbol.Regular) orelse unreachable;
|
||||
const reg = reg: {
|
||||
switch (sym.@"type") {
|
||||
.regular => break :reg sym.cast(Symbol.Regular) orelse unreachable,
|
||||
.tentative => {
|
||||
const final = sym.getTopmostAlias().cast(Symbol.Regular) orelse unreachable;
|
||||
if (object != final.file) continue;
|
||||
break :reg final;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
};
|
||||
|
||||
if (reg.isTemp() or reg.stab == null) continue;
|
||||
const stab = reg.stab orelse unreachable;
|
||||
@@ -2751,6 +2889,7 @@ fn writeSymbolTable(self: *Zld) !void {
|
||||
|
||||
const reg = final.cast(Symbol.Regular) orelse unreachable;
|
||||
if (reg.isTemp()) continue;
|
||||
if (reg.visited) continue;
|
||||
|
||||
switch (reg.linkage) {
|
||||
.translation_unit => {
|
||||
@@ -2772,6 +2911,8 @@ fn writeSymbolTable(self: *Zld) !void {
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
reg.visited = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2901,20 +3042,16 @@ fn writeDataInCode(self: *Zld) !void {
|
||||
|
||||
const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const text_sect = text_seg.sections.items[self.text_section_index.?];
|
||||
for (self.objects.items) |object, object_id| {
|
||||
const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
|
||||
const source_sect = source_seg.sections.items[object.text_section_index.?];
|
||||
const target_mapping = self.mappings.get(.{
|
||||
.object_id = @intCast(u16, object_id),
|
||||
.source_sect_id = object.text_section_index.?,
|
||||
}) orelse continue;
|
||||
for (self.objects.items) |object| {
|
||||
const source_sect = object.sections.items[object.text_section_index.?];
|
||||
const target_map = source_sect.target_map orelse continue;
|
||||
|
||||
try buf.ensureCapacity(
|
||||
buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry),
|
||||
);
|
||||
for (object.data_in_code_entries.items) |dice| {
|
||||
const new_dice: macho.data_in_code_entry = .{
|
||||
.offset = text_sect.offset + target_mapping.offset + dice.offset,
|
||||
.offset = text_sect.offset + target_map.offset + dice.offset,
|
||||
.length = dice.length,
|
||||
.kind = dice.kind,
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{});
|
||||
cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig", .{});
|
||||
cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig", .{});
|
||||
cases.addBuildFile("test/standalone/link_common_symbols/build.zig", .{});
|
||||
cases.addBuildFile("test/standalone/issue_339/build.zig", .{});
|
||||
cases.addBuildFile("test/standalone/issue_8550/build.zig", .{});
|
||||
cases.addBuildFile("test/standalone/issue_794/build.zig", .{});
|
||||
|
||||
6
test/standalone/link_common_symbols/a.c
Normal file
6
test/standalone/link_common_symbols/a.c
Normal file
@@ -0,0 +1,6 @@
|
||||
int i;
|
||||
int j;
|
||||
|
||||
int add_to_i_and_j(int x) {
|
||||
return x + i + j;
|
||||
}
|
||||
6
test/standalone/link_common_symbols/b.c
Normal file
6
test/standalone/link_common_symbols/b.c
Normal file
@@ -0,0 +1,6 @@
|
||||
long i;
|
||||
int j = 2;
|
||||
|
||||
void incr_i() {
|
||||
i++;
|
||||
}
|
||||
16
test/standalone/link_common_symbols/build.zig
Normal file
16
test/standalone/link_common_symbols/build.zig
Normal file
@@ -0,0 +1,16 @@
|
||||
const Builder = @import("std").build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const lib_a = b.addStaticLibrary("a", null);
|
||||
lib_a.addCSourceFiles(&.{ "a.c", "b.c" }, &.{"-fcommon"});
|
||||
lib_a.setBuildMode(mode);
|
||||
|
||||
const test_exe = b.addTest("main.zig");
|
||||
test_exe.setBuildMode(mode);
|
||||
test_exe.linkLibrary(lib_a);
|
||||
|
||||
const test_step = b.step("test", "Test it");
|
||||
test_step.dependOn(&test_exe.step);
|
||||
}
|
||||
11
test/standalone/link_common_symbols/main.zig
Normal file
11
test/standalone/link_common_symbols/main.zig
Normal file
@@ -0,0 +1,11 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
extern fn incr_i() void;
|
||||
extern fn add_to_i_and_j(x: c_int) c_int;
|
||||
|
||||
test "import C common symbols" {
|
||||
incr_i();
|
||||
const res = add_to_i_and_j(2);
|
||||
try expect(res == 5);
|
||||
}
|
||||
Reference in New Issue
Block a user