Merge pull request #9069 from ziglang/zld-common-syms

zig ld: add common (tentative) symbols support
This commit is contained in:
Jakub Konka
2021-06-11 10:18:43 +02:00
committed by GitHub
8 changed files with 338 additions and 129 deletions

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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(&sect.inner.segname),
parseName(&sect.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, &reg.base);
tent.base.alias = &reg.base;
if (tent.base.got_index) |idx| {
self.got_entries.items[idx] = &reg.base;
}
if (tent.base.stubs_index) |idx| {
self.stubs.items[idx] = &reg.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,
};

View File

@@ -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", .{});

View File

@@ -0,0 +1,6 @@
int i;
int j;
int add_to_i_and_j(int x) {
return x + i + j;
}

View File

@@ -0,0 +1,6 @@
long i;
int j = 2;
void incr_i() {
i++;
}

View 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);
}

View 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);
}