From 72f2f68938d626299e2bb5aa3b8932a45d4ae778 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 20 Jun 2021 19:21:15 +0200 Subject: zld: parse framework dirs and names --- src/link/MachO.zig | 212 +++++++++++++++++++++++++++++------------------- src/link/MachO/Stub.zig | 37 ++++++++- src/link/tapi.zig | 3 +- 3 files changed, 165 insertions(+), 87 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b7696f6a7c..c16957b97d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -514,6 +514,119 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } } +fn resolvePaths( + arena: *Allocator, + resolved_paths: *std.ArrayList([]const u8), + syslibroot: ?[]const u8, + search_dirs: []const []const u8, + lib_names: []const []const u8, + kind: enum { lib, framework }, +) !void { + var resolved_dirs = std.ArrayList([]const u8).init(arena); + for (search_dirs) |dir| { + if (fs.path.isAbsolute(dir)) { + var candidates = std.ArrayList([]const u8).init(arena); + if (syslibroot) |root| { + const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir }); + try candidates.append(full_path); + } + try candidates.append(dir); + + var found = false; + for (candidates.items) |candidate| { + // Verify that search path actually exists + var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); + + try resolved_dirs.append(candidate); + found = true; + break; + } + + if (!found) { + switch (kind) { + .lib => log.warn("directory not found for '-L{s}'", .{dir}), + .framework => log.warn("directory not found for '-F{s}'", .{dir}), + } + } + } else { + // Verify that search path actually exists + var tmp = fs.cwd().openDir(dir, .{}) catch |err| switch (err) { + error.FileNotFound => { + switch (kind) { + .lib => log.warn("directory not found for '-L{s}'", .{dir}), + .framework => log.warn("directory not found for '-F{s}'", .{dir}), + } + continue; + }, + else => |e| return e, + }; + defer tmp.close(); + + try resolved_dirs.append(dir); + } + } + + // Assume ld64 default: -search_paths_first + // Look in each directory for a dylib (next, tbd), and then for archive + // TODO implement alternative: -search_dylibs_first + const exts = switch (kind) { + .lib => &[_][]const u8{ "dylib", "tbd", "a" }, + .framework => &[_][]const u8{ "dylib", "tbd" }, + }; + + for (lib_names) |lib_name| { + var found = false; + + ext: for (exts) |ext| { + const lib_name_ext = blk: { + switch (kind) { + .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }), + .framework => { + const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name}); + const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext }); + break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn }); + }, + } + }; + + for (resolved_dirs.items) |dir| { + const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext }); + + // Check if the lib file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); + + try resolved_paths.append(full_path); + found = true; + break :ext; + } + } + + if (!found) { + switch (kind) { + .lib => { + log.warn("library not found for '-l{s}'", .{lib_name}); + log.warn("Library search paths:", .{}); + }, + .framework => { + log.warn("framework not found for '-f{s}'", .{lib_name}); + log.warn("Framework search paths:", .{}); + }, + } + for (resolved_dirs.items) |dir| { + log.warn(" {s}", .{dir}); + } + } + } +} + fn linkWithLLD(self: *MachO, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); @@ -700,7 +813,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { } // Shared and static libraries passed via `-l` flag. - var libs = std.ArrayList([]const u8).init(arena); var search_lib_names = std.ArrayList([]const u8).init(arena); const system_libs = self.base.options.system_libs.keys(); @@ -716,84 +828,15 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { try search_lib_names.append(link_lib); } - var search_lib_dirs = std.ArrayList([]const u8).init(arena); - - for (self.base.options.lib_dirs) |path| { - if (fs.path.isAbsolute(path)) { - var candidates = std.ArrayList([]const u8).init(arena); - if (self.base.options.syslibroot) |syslibroot| { - const full_path = try fs.path.join(arena, &[_][]const u8{ syslibroot, path }); - try candidates.append(full_path); - } - try candidates.append(path); - - var found = false; - for (candidates.items) |candidate| { - // Verify that search path actually exists - var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - try search_lib_dirs.append(candidate); - found = true; - break; - } - - if (!found) { - log.warn("directory not found for '-L{s}'", .{path}); - } - } else { - // Verify that search path actually exists - var tmp = fs.cwd().openDir(path, .{}) catch |err| switch (err) { - error.FileNotFound => { - log.warn("directory not found for '-L{s}'", .{path}); - continue; - }, - else => |e| return e, - }; - defer tmp.close(); - - try search_lib_dirs.append(path); - } - } - - // Assume ld64 default: -search_paths_first - // Look in each directory for a dylib (next, tbd), and then for archive - // TODO implement alternative: -search_dylibs_first - const exts = &[_][]const u8{ "dylib", "tbd", "a" }; - - for (search_lib_names.items) |l_name| { - var found = false; - - ext: for (exts) |ext| { - const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ l_name, ext }); - - for (search_lib_dirs.items) |lib_dir| { - const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext }); - - // Check if the lib file exists. - const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - try libs.append(full_path); - found = true; - break :ext; - } - } - - if (!found) { - log.warn("library not found for '-l{s}'", .{l_name}); - log.warn("Library search paths:", .{}); - for (search_lib_dirs.items) |lib_dir| { - log.warn(" {s}", .{lib_dir}); - } - } - } + var libs = std.ArrayList([]const u8).init(arena); + try resolvePaths( + arena, + &libs, + self.base.options.syslibroot, + self.base.options.lib_dirs, + search_lib_names.items, + .lib, + ); // rpaths var rpath_table = std.StringArrayHashMap(void).init(arena); @@ -809,9 +852,14 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { } // frameworks - for (self.base.options.frameworks) |framework| { - log.warn("frameworks not yet supported for '-framework {s}'", .{framework}); - } + try resolvePaths( + arena, + &libs, + self.base.options.syslibroot, + self.base.options.framework_dirs, + self.base.options.frameworks, + .framework, + ); if (self.base.options.verbose_link) { var argv = std.ArrayList([]const u8).init(arena); diff --git a/src/link/MachO/Stub.zig b/src/link/MachO/Stub.zig index 3e1474539d..6111690a7d 100644 --- a/src/link/MachO/Stub.zig +++ b/src/link/MachO/Stub.zig @@ -60,7 +60,7 @@ pub fn parse(self: *Stub) !void { const lib_stub = self.lib_stub orelse return error.EmptyStubFile; if (lib_stub.inner.len == 0) return error.EmptyStubFile; - log.debug("parsing shared library from stub '{s}'", .{self.name.?}); + log.warn("parsing shared library from stub '{s}'", .{self.name.?}); const umbrella_lib = lib_stub.inner[0]; self.id = .{ @@ -84,9 +84,24 @@ pub fn parse(self: *Stub) !void { for (exports) |exp| { if (!hasTarget(exp.targets, target_string)) continue; - for (exp.symbols) |sym_name| { - if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, sym_name, {}); + if (exp.symbols) |symbols| { + for (symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, sym_name, {}); + } + } + + if (exp.objc_classes) |classes| { + for (classes) |sym_name| { + log.warn(" | {s}", .{sym_name}); + const actual_sym_name = try std.fmt.allocPrint( + self.allocator, + "_OBJC_CLASS_$_{s}", + .{sym_name}, + ); + if (self.symbols.contains(actual_sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + } } } } @@ -101,6 +116,20 @@ pub fn parse(self: *Stub) !void { } } } + + if (stub.objc_classes) |classes| { + log.warn(" | objc_classes", .{}); + for (classes) |sym_name| { + log.warn(" | {s}", .{sym_name}); + const actual_sym_name = try std.fmt.allocPrint( + self.allocator, + "_OBJC_METACLASS_$_{s}", + .{sym_name}, + ); + if (self.symbols.contains(actual_sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + } + } } } diff --git a/src/link/tapi.zig b/src/link/tapi.zig index efa7227def..51d51d6ed3 100644 --- a/src/link/tapi.zig +++ b/src/link/tapi.zig @@ -36,7 +36,8 @@ pub const LibStub = struct { }, exports: ?[]const struct { targets: []const []const u8, - symbols: []const []const u8, + symbols: ?[]const []const u8, + objc_classes: ?[]const []const u8, }, reexports: ?[]const struct { targets: []const []const u8, -- cgit v1.2.3 From 852e1ed23cb5cfdb687a52c39fa9c60be3159730 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 21 Jun 2021 11:11:19 +0200 Subject: zld+stage2: refactor creating segments and sections Move the logic into default-init structs part of constructors in `SegmentCommand` struct in `commands.zig` module. --- src/link/MachO.zig | 105 ++-------- src/link/MachO/DebugSymbols.zig | 66 +------ src/link/MachO/Stub.zig | 60 ++++-- src/link/MachO/Zld.zig | 411 +++++----------------------------------- src/link/MachO/commands.zig | 74 +++++++- 5 files changed, 172 insertions(+), 544 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index c16957b97d..cc00cafe3e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1779,18 +1779,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.pagezero_segment_cmd_index == null) { self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__PAGEZERO"), - .vmaddr = 0, + .Segment = SegmentCommand.empty("__PAGEZERO", .{ .vmsize = 0x100000000, // size always set to 4GB - .fileoff = 0, - .filesize = 0, - .maxprot = 0, - .initprot = 0, - .nsects = 0, - .flags = 0, }), }); self.header_dirty = true; @@ -1809,18 +1799,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__TEXT"), + .Segment = SegmentCommand.empty("__TEXT", .{ .vmaddr = 0x100000000, // always starts at 4GB .vmsize = needed_size, - .fileoff = 0, .filesize = needed_size, .maxprot = maxprot, .initprot = initprot, - .nsects = 0, - .flags = 0, }), }); self.header_dirty = true; @@ -1841,19 +1825,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __text section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try text_segment.addSection(self.base.allocator, .{ - .sectname = makeStaticString("__text"), - .segname = makeStaticString("__TEXT"), + try text_segment.addSection(self.base.allocator, "__text", "__TEXT", .{ .addr = text_segment.inner.vmaddr + off, .size = @intCast(u32, needed_size), .offset = @intCast(u32, off), .@"align" = alignment, - .reloff = 0, - .nreloc = 0, .flags = flags, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -1879,19 +1856,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __stubs section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try text_segment.addSection(self.base.allocator, .{ - .sectname = makeStaticString("__stubs"), - .segname = makeStaticString("__TEXT"), + try text_segment.addSection(self.base.allocator, "__stubs", "__TEXT", .{ .addr = text_segment.inner.vmaddr + off, .size = needed_size, .offset = @intCast(u32, off), .@"align" = alignment, - .reloff = 0, - .nreloc = 0, .flags = flags, - .reserved1 = 0, .reserved2 = stub_size, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -1912,19 +1883,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __stub_helper section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try text_segment.addSection(self.base.allocator, .{ - .sectname = makeStaticString("__stub_helper"), - .segname = makeStaticString("__TEXT"), + try text_segment.addSection(self.base.allocator, "__stub_helper", "__TEXT", .{ .addr = text_segment.inner.vmaddr + off, .size = needed_size, .offset = @intCast(u32, off), .@"align" = alignment, - .reloff = 0, - .nreloc = 0, .flags = flags, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -1941,18 +1905,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__DATA_CONST"), + .Segment = SegmentCommand.empty("__DATA_CONST", .{ .vmaddr = address_and_offset.address, .vmsize = needed_size, .fileoff = address_and_offset.offset, .filesize = needed_size, .maxprot = maxprot, .initprot = initprot, - .nsects = 0, - .flags = 0, }), }); self.header_dirty = true; @@ -1969,19 +1928,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __got section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try dc_segment.addSection(self.base.allocator, .{ - .sectname = makeStaticString("__got"), - .segname = makeStaticString("__DATA_CONST"), + try dc_segment.addSection(self.base.allocator, "__got", "__DATA_CONST", .{ .addr = dc_segment.inner.vmaddr + off - dc_segment.inner.fileoff, .size = needed_size, .offset = @intCast(u32, off), .@"align" = 3, // 2^3 = @sizeOf(u64) - .reloff = 0, - .nreloc = 0, .flags = flags, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -1998,18 +1950,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__DATA"), + .Segment = SegmentCommand.empty("__DATA", .{ .vmaddr = address_and_offset.address, .vmsize = needed_size, .fileoff = address_and_offset.offset, .filesize = needed_size, .maxprot = maxprot, .initprot = initprot, - .nsects = 0, - .flags = 0, }), }); self.header_dirty = true; @@ -2026,19 +1973,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __la_symbol_ptr section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try data_segment.addSection(self.base.allocator, .{ - .sectname = makeStaticString("__la_symbol_ptr"), - .segname = makeStaticString("__DATA"), + try data_segment.addSection(self.base.allocator, "__la_symbol_ptr", "__DATA", .{ .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, .size = needed_size, .offset = @intCast(u32, off), .@"align" = 3, // 2^3 = @sizeOf(u64) - .reloff = 0, - .nreloc = 0, .flags = flags, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -2047,26 +1987,17 @@ pub fn populateMissingMetadata(self: *MachO) !void { const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; self.data_section_index = @intCast(u16, data_segment.sections.items.len); - const flags = macho.S_REGULAR; const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. log.debug("found __data section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try data_segment.addSection(self.base.allocator, .{ - .sectname = makeStaticString("__data"), - .segname = makeStaticString("__DATA"), + try data_segment.addSection(self.base.allocator, "__data", "__DATA", .{ .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, .size = needed_size, .offset = @intCast(u32, off), .@"align" = 3, // 2^3 = @sizeOf(u64) - .reloff = 0, - .nreloc = 0, - .flags = flags, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -2081,18 +2012,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __LINKEDIT segment free space at 0x{x}", .{address_and_offset.offset}); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__LINKEDIT"), + .Segment = SegmentCommand.empty("__LINKEDIT", .{ .vmaddr = address_and_offset.address, - .vmsize = 0, .fileoff = address_and_offset.offset, - .filesize = 0, .maxprot = maxprot, .initprot = initprot, - .nsects = 0, - .flags = 0, }), }); self.header_dirty = true; @@ -2450,13 +2374,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, return vaddr; } -pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { - var buf = [_]u8{0} ** 16; - if (bytes.len > buf.len) @compileError("string too long; max 16 bytes"); - mem.copy(u8, &buf, bytes); - return buf; -} - fn makeString(self: *MachO, bytes: []const u8) !u32 { if (self.string_table_directory.get(bytes)) |offset| { log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset }); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 2b10f3307b..c26ce889ae 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -19,7 +19,6 @@ const MachO = @import("../MachO.zig"); const SrcFn = MachO.SrcFn; const TextBlock = MachO.TextBlock; const padToIdeal = MachO.padToIdeal; -const makeStaticString = MachO.makeStaticString; usingnamespace @import("commands.zig"); @@ -212,18 +211,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size }); try self.load_commands.append(allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__DWARF"), + .Segment = SegmentCommand.empty("__DWARF", .{ .vmaddr = vmaddr, .vmsize = needed_size, .fileoff = off, .filesize = needed_size, - .maxprot = 0, - .initprot = 0, - .nsects = 0, - .flags = 0, }), }); self.header_dirty = true; @@ -234,19 +226,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len); assert(self.debug_string_table.items.len == 0); - try dwarf_segment.addSection(allocator, .{ - .sectname = makeStaticString("__debug_str"), - .segname = makeStaticString("__DWARF"), + try dwarf_segment.addSection(allocator, "__debug_str", "__DWARF", .{ .addr = dwarf_segment.inner.vmaddr, .size = @intCast(u32, self.debug_string_table.items.len), .offset = @intCast(u32, dwarf_segment.inner.fileoff), .@"align" = 1, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -262,19 +246,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, .{ - .sectname = makeStaticString("__debug_info"), - .segname = makeStaticString("__DWARF"), + try dwarf_segment.addSection(allocator, "__debug_info", "__DWARF", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), .@"align" = p_align, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -290,19 +266,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, .{ - .sectname = makeStaticString("__debug_abbrev"), - .segname = makeStaticString("__DWARF"), + try dwarf_segment.addSection(allocator, "__debug_abbrev", "__DWARF", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), .@"align" = p_align, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -318,19 +286,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, .{ - .sectname = makeStaticString("__debug_aranges"), - .segname = makeStaticString("__DWARF"), + try dwarf_segment.addSection(allocator, "__debug_aranges", "__DWARF", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), .@"align" = p_align, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -346,19 +306,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, .{ - .sectname = makeStaticString("__debug_line"), - .segname = makeStaticString("__DWARF"), + try dwarf_segment.addSection(allocator, "__debug_line", "__DWARF", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), .@"align" = p_align, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); self.header_dirty = true; self.load_commands_dirty = true; @@ -692,14 +644,10 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void { } fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand { - var cmd = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, + var cmd = SegmentCommand.empty("", .{ .cmdsize = base_cmd.inner.cmdsize, - .segname = undefined, .vmaddr = base_cmd.inner.vmaddr, .vmsize = base_cmd.inner.vmsize, - .fileoff = 0, - .filesize = 0, .maxprot = base_cmd.inner.maxprot, .initprot = base_cmd.inner.initprot, .nsects = base_cmd.inner.nsects, diff --git a/src/link/MachO/Stub.zig b/src/link/MachO/Stub.zig index 6111690a7d..d71c454c79 100644 --- a/src/link/MachO/Stub.zig +++ b/src/link/MachO/Stub.zig @@ -60,7 +60,7 @@ pub fn parse(self: *Stub) !void { const lib_stub = self.lib_stub orelse return error.EmptyStubFile; if (lib_stub.inner.len == 0) return error.EmptyStubFile; - log.warn("parsing shared library from stub '{s}'", .{self.name.?}); + log.debug("parsing shared library from stub '{s}'", .{self.name.?}); const umbrella_lib = lib_stub.inner[0]; self.id = .{ @@ -93,14 +93,26 @@ pub fn parse(self: *Stub) !void { if (exp.objc_classes) |classes| { for (classes) |sym_name| { - log.warn(" | {s}", .{sym_name}); - const actual_sym_name = try std.fmt.allocPrint( - self.allocator, - "_OBJC_CLASS_$_{s}", - .{sym_name}, - ); - if (self.symbols.contains(actual_sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + log.debug(" | {s}", .{sym_name}); + { + const actual_sym_name = try std.fmt.allocPrint( + self.allocator, + "_OBJC_CLASS_$_{s}", + .{sym_name}, + ); + if (self.symbols.contains(actual_sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + } + + { + const actual_sym_name = try std.fmt.allocPrint( + self.allocator, + "_OBJC_METACLASS_$_{s}", + .{sym_name}, + ); + if (self.symbols.contains(actual_sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + } } } } @@ -118,16 +130,28 @@ pub fn parse(self: *Stub) !void { } if (stub.objc_classes) |classes| { - log.warn(" | objc_classes", .{}); + log.debug(" | objc_classes", .{}); for (classes) |sym_name| { - log.warn(" | {s}", .{sym_name}); - const actual_sym_name = try std.fmt.allocPrint( - self.allocator, - "_OBJC_METACLASS_$_{s}", - .{sym_name}, - ); - if (self.symbols.contains(actual_sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + log.debug(" | {s}", .{sym_name}); + { + const actual_sym_name = try std.fmt.allocPrint( + self.allocator, + "_OBJC_CLASS_$_{s}", + .{sym_name}, + ); + if (self.symbols.contains(actual_sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + } + + { + const actual_sym_name = try std.fmt.allocPrint( + self.allocator, + "_OBJC_METACLASS_$_{s}", + .{sym_name}, + ); + if (self.symbols.contains(actual_sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); + } } } } diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 0a9d209c61..3f30379ca5 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -79,6 +79,8 @@ mod_init_func_section_index: ?u16 = null, mod_term_func_section_index: ?u16 = null, data_const_section_index: ?u16 = null, +objc_cfstring_section_index: ?u16 = null, + // __DATA segment sections tlv_section_index: ?u16 = null, tlv_data_section_index: ?u16 = null, @@ -515,39 +517,15 @@ fn updateMetadata(self: *Zld) !void { 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, - }); + try text_seg.addSection(self.allocator, "__const", "__TEXT", .{}); continue; }, macho.S_CSTRING_LITERALS => { if (self.cstring_section_index != null) continue; self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__cstring"), - .segname = makeStaticString("__TEXT"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try text_seg.addSection(self.allocator, "__cstring", "__TEXT", .{ .flags = macho.S_CSTRING_LITERALS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); continue; }, @@ -555,19 +533,8 @@ fn updateMetadata(self: *Zld) !void { if (self.mod_init_func_section_index != null) continue; self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__mod_init_func"), - .segname = makeStaticString("__DATA_CONST"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try data_const_seg.addSection(self.allocator, "__mod_init_func", "__DATA_CONST", .{ .flags = macho.S_MOD_INIT_FUNC_POINTERS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); continue; }, @@ -575,19 +542,8 @@ fn updateMetadata(self: *Zld) !void { if (self.mod_term_func_section_index != null) continue; self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__mod_term_func"), - .segname = makeStaticString("__DATA_CONST"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try data_const_seg.addSection(self.allocator, "__mod_term_func", "__DATA_CONST", .{ .flags = macho.S_MOD_TERM_FUNC_POINTERS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); continue; }, @@ -596,37 +552,15 @@ fn updateMetadata(self: *Zld) !void { if (self.common_section_index != null) continue; 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, + try data_seg.addSection(self.allocator, "__common", "__DATA", .{ .flags = macho.S_ZEROFILL, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); } else { if (self.bss_section_index != null) continue; self.bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__bss"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try data_seg.addSection(self.allocator, "__bss", "__DATA", .{ .flags = macho.S_ZEROFILL, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); } continue; @@ -635,19 +569,8 @@ fn updateMetadata(self: *Zld) !void { if (self.tlv_section_index != null) continue; self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__thread_vars"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try data_seg.addSection(self.allocator, "__thread_vars", "__DATA", .{ .flags = macho.S_THREAD_LOCAL_VARIABLES, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); continue; }, @@ -655,19 +578,8 @@ fn updateMetadata(self: *Zld) !void { if (self.tlv_data_section_index != null) continue; self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__thread_data"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try data_seg.addSection(self.allocator, "__thread_data", "__DATA", .{ .flags = macho.S_THREAD_LOCAL_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); continue; }, @@ -675,19 +587,8 @@ fn updateMetadata(self: *Zld) !void { if (self.tlv_bss_section_index != null) continue; self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__thread_bss"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try data_seg.addSection(self.allocator, "__thread_bss", "__DATA", .{ .flags = macho.S_THREAD_LOCAL_ZEROFILL, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); continue; }, @@ -698,20 +599,7 @@ fn updateMetadata(self: *Zld) !void { if (self.eh_frame_section_index != null) continue; self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__eh_frame"), - .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, - }); + try text_seg.addSection(self.allocator, "__eh_frame", "__TEXT", .{}); continue; } @@ -719,20 +607,7 @@ fn updateMetadata(self: *Zld) !void { if (self.data_const_section_index != null) continue; self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__const"), - .segname = makeStaticString("__DATA_CONST"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); continue; }, macho.S_REGULAR => { @@ -740,19 +615,8 @@ fn updateMetadata(self: *Zld) !void { if (self.text_section_index != null) continue; self.text_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__text"), - .segname = makeStaticString("__TEXT"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, + try text_seg.addSection(self.allocator, "__text", "__TEXT", .{ .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); continue; } @@ -771,56 +635,17 @@ fn updateMetadata(self: *Zld) !void { 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, - }); + try text_seg.addSection(self.allocator, "__ustring", "__TEXT", .{}); } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { if (self.gcc_except_tab_section_index != null) continue; self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__gcc_except_tab"), - .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, - }); + try text_seg.addSection(self.allocator, "__gcc_except_tab", "__TEXT", .{}); } 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, - }); + try text_seg.addSection(self.allocator, "__const", "__TEXT", .{}); } continue; } @@ -829,20 +654,7 @@ fn updateMetadata(self: *Zld) !void { if (self.data_const_section_index != null) continue; self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__const"), - .segname = makeStaticString("__DATA_CONST"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); continue; } @@ -851,38 +663,17 @@ fn updateMetadata(self: *Zld) !void { if (self.data_const_section_index != null) continue; self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__const"), - .segname = makeStaticString("__DATA_CONST"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); + } else if (mem.eql(u8, sectname, "__cfstring")) { + if (self.objc_cfstring_section_index != null) continue; + + self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__cfstring", "__DATA_CONST", .{}); } else { if (self.data_section_index != null) continue; self.data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__data"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + try data_seg.addSection(self.allocator, "__data", "__DATA", .{}); } continue; @@ -932,19 +723,8 @@ fn updateMetadata(self: *Zld) !void { 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, + try data_seg.addSection(self.allocator, "__common", "__DATA", .{ .flags = macho.S_ZEROFILL, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); break :ind self.common_section_index.?; }; @@ -1136,6 +916,11 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { .seg = self.data_const_segment_cmd_index.?, .sect = self.data_const_section_index.?, }; + } else if (mem.eql(u8, sectname, "__cfstring")) { + break :blk .{ + .seg = self.data_const_segment_cmd_index.?, + .sect = self.objc_cfstring_section_index.?, + }; } break :blk .{ .seg = self.data_segment_cmd_index.?, @@ -1200,6 +985,7 @@ fn sortSections(self: *Zld) !void { &self.mod_init_func_section_index, &self.mod_term_func_section_index, &self.data_const_section_index, + &self.objc_cfstring_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -2240,18 +2026,8 @@ fn populateMetadata(self: *Zld) !void { if (self.pagezero_segment_cmd_index == null) { self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__PAGEZERO"), - .vmaddr = 0, + .Segment = SegmentCommand.empty("__PAGEZERO", .{ .vmsize = 0x100000000, // size always set to 4GB - .fileoff = 0, - .filesize = 0, - .maxprot = 0, - .initprot = 0, - .nsects = 0, - .flags = 0, }), }); } @@ -2259,18 +2035,10 @@ fn populateMetadata(self: *Zld) !void { if (self.text_segment_cmd_index == null) { self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__TEXT"), + .Segment = SegmentCommand.empty("__TEXT", .{ .vmaddr = 0x100000000, // always starts at 4GB - .vmsize = 0, - .fileoff = 0, - .filesize = 0, .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, - .nsects = 0, - .flags = 0, }), }); } @@ -2283,19 +2051,9 @@ fn populateMetadata(self: *Zld) !void { .aarch64 => 2, else => unreachable, // unhandled architecture type }; - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__text"), - .segname = makeStaticString("__TEXT"), - .addr = 0, - .size = 0, - .offset = 0, + try text_seg.addSection(self.allocator, "__text", "__TEXT", .{ .@"align" = alignment, - .reloff = 0, - .nreloc = 0, .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); } @@ -2312,19 +2070,10 @@ fn populateMetadata(self: *Zld) !void { .aarch64 => 3 * @sizeOf(u32), else => unreachable, // unhandled architecture type }; - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__stubs"), - .segname = makeStaticString("__TEXT"), - .addr = 0, - .size = 0, - .offset = 0, + try text_seg.addSection(self.allocator, "__stubs", "__TEXT", .{ .@"align" = alignment, - .reloff = 0, - .nreloc = 0, .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved1 = 0, .reserved2 = stub_size, - .reserved3 = 0, }); } @@ -2341,37 +2090,19 @@ fn populateMetadata(self: *Zld) !void { .aarch64 => 6 * @sizeOf(u32), else => unreachable, }; - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__stub_helper"), - .segname = makeStaticString("__TEXT"), - .addr = 0, + try text_seg.addSection(self.allocator, "__stub_helper", "__TEXT", .{ .size = stub_helper_size, - .offset = 0, .@"align" = alignment, - .reloff = 0, - .nreloc = 0, .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); } if (self.data_const_segment_cmd_index == null) { self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__DATA_CONST"), - .vmaddr = 0, - .vmsize = 0, - .fileoff = 0, - .filesize = 0, + .Segment = SegmentCommand.empty("__DATA_CONST", .{ .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, - .nsects = 0, - .flags = 0, }), }); } @@ -2379,37 +2110,18 @@ fn populateMetadata(self: *Zld) !void { if (self.got_section_index == null) { const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; self.got_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__got"), - .segname = makeStaticString("__DATA_CONST"), - .addr = 0, - .size = 0, - .offset = 0, + try data_const_seg.addSection(self.allocator, "__got", "__DATA_CONST", .{ .@"align" = 3, // 2^3 = @sizeOf(u64) - .reloff = 0, - .nreloc = 0, .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); } if (self.data_segment_cmd_index == null) { self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__DATA"), - .vmaddr = 0, - .vmsize = 0, - .fileoff = 0, - .filesize = 0, + .Segment = SegmentCommand.empty("__DATA", .{ .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, - .nsects = 0, - .flags = 0, }), }); } @@ -2417,56 +2129,26 @@ fn populateMetadata(self: *Zld) !void { if (self.la_symbol_ptr_section_index == null) { const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__la_symbol_ptr"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, + try data_seg.addSection(self.allocator, "__la_symbol_ptr", "__DATA", .{ .@"align" = 3, // 2^3 = @sizeOf(u64) - .reloff = 0, - .nreloc = 0, .flags = macho.S_LAZY_SYMBOL_POINTERS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); } if (self.data_section_index == null) { const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; self.data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__data"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, + try data_seg.addSection(self.allocator, "__data", "__DATA", .{ .@"align" = 3, // 2^3 = @sizeOf(u64) - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, }); } if (self.linkedit_segment_cmd_index == null) { self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.allocator, .{ - .Segment = SegmentCommand.empty(.{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__LINKEDIT"), - .vmaddr = 0, - .vmsize = 0, - .fileoff = 0, - .filesize = 0, + .Segment = SegmentCommand.empty("__LINKEDIT", .{ .maxprot = macho.VM_PROT_READ, .initprot = macho.VM_PROT_READ, - .nsects = 0, - .flags = 0, }), }); } @@ -3469,13 +3151,6 @@ fn writeHeader(self: *Zld) !void { try self.file.?.pwriteAll(mem.asBytes(&header), 0); } -pub fn makeStaticString(bytes: []const u8) [16]u8 { - var buf = [_]u8{0} ** 16; - assert(bytes.len <= buf.len); - mem.copy(u8, &buf, bytes); - return buf; -} - fn makeString(self: *Zld, bytes: []const u8) !u32 { if (self.strtab_dir.get(bytes)) |offset| { log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset }); diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index 8bceb64f1e..6ad1964483 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -9,7 +9,6 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const MachO = @import("../MachO.zig"); -const makeStaticString = MachO.makeStaticString; const padToIdeal = MachO.padToIdeal; pub const LoadCommand = union(enum) { @@ -187,12 +186,70 @@ pub const SegmentCommand = struct { inner: macho.segment_command_64, sections: std.ArrayListUnmanaged(macho.section_64) = .{}, - pub fn empty(inner: macho.segment_command_64) SegmentCommand { - return .{ .inner = inner }; + const SegmentOptions = struct { + cmdsize: u32 = @sizeOf(macho.segment_command_64), + vmaddr: u64 = 0, + vmsize: u64 = 0, + fileoff: u64 = 0, + filesize: u64 = 0, + maxprot: macho.vm_prot_t = macho.VM_PROT_NONE, + initprot: macho.vm_prot_t = macho.VM_PROT_NONE, + nsects: u32 = 0, + flags: u32 = 0, + }; + + pub fn empty(comptime segname: []const u8, opts: SegmentOptions) SegmentCommand { + return .{ + .inner = .{ + .cmd = macho.LC_SEGMENT_64, + .cmdsize = opts.cmdsize, + .segname = makeStaticString(segname), + .vmaddr = opts.vmaddr, + .vmsize = opts.vmsize, + .fileoff = opts.fileoff, + .filesize = opts.filesize, + .maxprot = opts.maxprot, + .initprot = opts.initprot, + .nsects = opts.nsects, + .flags = opts.flags, + }, + }; } - pub fn addSection(self: *SegmentCommand, alloc: *Allocator, section: macho.section_64) !void { - try self.sections.append(alloc, section); + const SectionOptions = struct { + addr: u64 = 0, + size: u64 = 0, + offset: u32 = 0, + @"align": u32 = 0, + reloff: u32 = 0, + nreloc: u32 = 0, + flags: u32 = macho.S_REGULAR, + reserved1: u32 = 0, + reserved2: u32 = 0, + reserved3: u32 = 0, + }; + + pub fn addSection( + self: *SegmentCommand, + alloc: *Allocator, + comptime sectname: []const u8, + comptime segname: []const u8, + opts: SectionOptions, + ) !void { + try self.sections.append(alloc, .{ + .sectname = makeStaticString(sectname), + .segname = makeStaticString(segname), + .addr = opts.addr, + .size = opts.size, + .offset = opts.offset, + .@"align" = opts.@"align", + .reloff = opts.reloff, + .nreloc = opts.nreloc, + .flags = opts.flags, + .reserved1 = opts.reserved1, + .reserved2 = opts.reserved2, + .reserved3 = opts.reserved3, + }); self.inner.cmdsize += @sizeOf(macho.section_64); self.inner.nsects += 1; } @@ -338,6 +395,13 @@ pub fn createLoadDylibCommand( return dylib_cmd; } +fn makeStaticString(bytes: []const u8) [16]u8 { + var buf = [_]u8{0} ** 16; + assert(bytes.len <= buf.len); + mem.copy(u8, &buf, bytes); + return buf; +} + fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void { var stream = io.fixedBufferStream(buffer); var given = try LoadCommand.read(allocator, stream.reader()); -- cgit v1.2.3 From 5f9b4cba6da9540d5a2fd03d9b379d0b7eb5937d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 21 Jun 2021 13:21:34 +0200 Subject: zld: clean up logic for matching and mapping sections --- src/link/MachO/Zld.zig | 364 ++++++++++++++++++++----------------------------- 1 file changed, 150 insertions(+), 214 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 3f30379ca5..23b077fd67 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -503,217 +503,22 @@ fn mapAndUpdateSections( fn updateMetadata(self: *Zld) !void { for (self.objects.items) |object| { - 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; - const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - - // Create missing metadata - for (object.sections.items) |sect| { - const segname = sect.segname(); - const sectname = sect.sectname(); - - switch (sect.sectionType()) { - macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS, macho.S_LITERAL_POINTERS => { - 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, "__const", "__TEXT", .{}); - continue; - }, - macho.S_CSTRING_LITERALS => { - if (self.cstring_section_index != null) continue; - - self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__cstring", "__TEXT", .{ - .flags = macho.S_CSTRING_LITERALS, - }); - continue; - }, - macho.S_MOD_INIT_FUNC_POINTERS => { - if (self.mod_init_func_section_index != null) continue; - - self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__mod_init_func", "__DATA_CONST", .{ - .flags = macho.S_MOD_INIT_FUNC_POINTERS, - }); - continue; - }, - macho.S_MOD_TERM_FUNC_POINTERS => { - if (self.mod_term_func_section_index != null) continue; - - self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__mod_term_func", "__DATA_CONST", .{ - .flags = macho.S_MOD_TERM_FUNC_POINTERS, - }); - continue; - }, - macho.S_ZEROFILL => { - if (mem.eql(u8, sectname, "__common")) { - if (self.common_section_index != null) continue; - - self.common_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__common", "__DATA", .{ - .flags = macho.S_ZEROFILL, - }); - } else { - if (self.bss_section_index != null) continue; - - self.bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__bss", "__DATA", .{ - .flags = macho.S_ZEROFILL, - }); - } - continue; - }, - macho.S_THREAD_LOCAL_VARIABLES => { - if (self.tlv_section_index != null) continue; - - self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__thread_vars", "__DATA", .{ - .flags = macho.S_THREAD_LOCAL_VARIABLES, - }); - continue; - }, - macho.S_THREAD_LOCAL_REGULAR => { - if (self.tlv_data_section_index != null) continue; - - self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__thread_data", "__DATA", .{ - .flags = macho.S_THREAD_LOCAL_REGULAR, - }); - continue; - }, - macho.S_THREAD_LOCAL_ZEROFILL => { - if (self.tlv_bss_section_index != null) continue; - - self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__thread_bss", "__DATA", .{ - .flags = macho.S_THREAD_LOCAL_ZEROFILL, - }); - continue; - }, - macho.S_COALESCED => { - if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) { - // TODO I believe __eh_frame is currently part of __unwind_info section - // in the latest ld64 output. - if (self.eh_frame_section_index != null) continue; - - self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__eh_frame", "__TEXT", .{}); - continue; - } - - // TODO audit this: is this the right mapping? - if (self.data_const_section_index != null) continue; - - self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); - continue; - }, - macho.S_REGULAR => { - if (sect.isCode()) { - if (self.text_section_index != null) continue; - - self.text_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__text", "__TEXT", .{ - .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - }); - continue; - } - - if (sect.isDebug()) { - if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { - log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ - sect.flags(), segname, sectname, - }); - } - continue; - } - - if (mem.eql(u8, segname, "__TEXT")) { - 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, "__ustring", "__TEXT", .{}); - } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { - if (self.gcc_except_tab_section_index != null) continue; - - self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__gcc_except_tab", "__TEXT", .{}); - } 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, "__const", "__TEXT", .{}); - } - continue; - } - - if (mem.eql(u8, segname, "__DATA_CONST")) { - if (self.data_const_section_index != null) continue; - - self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); - continue; - } - - if (mem.eql(u8, segname, "__DATA")) { - if (mem.eql(u8, sectname, "__const")) { - if (self.data_const_section_index != null) continue; - - self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); - } else if (mem.eql(u8, sectname, "__cfstring")) { - if (self.objc_cfstring_section_index != null) continue; - - self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__cfstring", "__DATA_CONST", .{}); - } else { - if (self.data_section_index != null) continue; - - self.data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__data", "__DATA", .{}); - } - - continue; - } - - if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) { - log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{ - sect.flags(), segname, sectname, - }); - continue; - } - }, - else => {}, - } - - log.err("{s}: unhandled section type 0x{x} for '{s},{s}'", .{ - object.name.?, - sect.flags(), - segname, - sectname, - }); - return error.UnhandledSection; - } - - // Find ideal section alignment. - for (object.sections.items) |sect| { - if (self.getMatchingSection(sect)) |res| { - const target_seg = &self.load_commands.items[res.seg].Segment; - const target_sect = &target_seg.sections.items[res.sect]; - target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align"); - } - } - - // Update section mappings + // Find ideal section alignment and update section mappings for (object.sections.items) |sect, sect_id| { - if (self.getMatchingSection(sect)) |res| { - try self.mapAndUpdateSections(object, @intCast(u16, sect_id), res.seg, res.sect); + const match = (try self.getMatchingSection(sect)) orelse { + log.debug("{s}: unhandled section type 0x{x} for '{s},{s}'", .{ + object.name.?, + sect.flags(), + sect.segname(), + sect.sectname(), + }); continue; - } - log.debug("section '{s},{s}' will be unmapped", .{ sect.segname(), sect.sectname() }); + }; + const target_seg = &self.load_commands.items[match.seg].Segment; + const target_sect = &target_seg.sections.items[match.sect]; + target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align"); + + try self.mapAndUpdateSections(object, @intCast(u16, sect_id), match.seg, match.sect); } } @@ -798,31 +603,60 @@ const MatchingSection = struct { sect: u16, }; -fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { +fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { + 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; + const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const segname = sect.segname(); const sectname = sect.sectname(); const res: ?MatchingSection = blk: { switch (sect.sectionType()) { macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS, macho.S_LITERAL_POINTERS => { + if (self.text_const_section_index == null) { + self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__const", "__TEXT", .{}); + } + break :blk .{ .seg = self.text_segment_cmd_index.?, .sect = self.text_const_section_index.?, }; }, macho.S_CSTRING_LITERALS => { + if (self.cstring_section_index == null) { + self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__cstring", "__TEXT", .{ + .flags = macho.S_CSTRING_LITERALS, + }); + } + break :blk .{ .seg = self.text_segment_cmd_index.?, .sect = self.cstring_section_index.?, }; }, macho.S_MOD_INIT_FUNC_POINTERS => { + if (self.mod_init_func_section_index == null) { + self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__mod_init_func", "__DATA_CONST", .{ + .flags = macho.S_MOD_INIT_FUNC_POINTERS, + }); + } + break :blk .{ .seg = self.data_const_segment_cmd_index.?, .sect = self.mod_init_func_section_index.?, }; }, macho.S_MOD_TERM_FUNC_POINTERS => { + if (self.mod_term_func_section_index == null) { + self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__mod_term_func", "__DATA_CONST", .{ + .flags = macho.S_MOD_TERM_FUNC_POINTERS, + }); + } + break :blk .{ .seg = self.data_const_segment_cmd_index.?, .sect = self.mod_term_func_section_index.?, @@ -830,11 +664,25 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { }, macho.S_ZEROFILL => { if (mem.eql(u8, sectname, "__common")) { + if (self.common_section_index == null) { + self.common_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__common", "__DATA", .{ + .flags = macho.S_ZEROFILL, + }); + } + break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.common_section_index.?, }; } else { + if (self.bss_section_index == null) { + self.bss_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__bss", "__DATA", .{ + .flags = macho.S_ZEROFILL, + }); + } + break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.bss_section_index.?, @@ -842,18 +690,39 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { } }, macho.S_THREAD_LOCAL_VARIABLES => { + if (self.tlv_section_index == null) { + self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__thread_vars", "__DATA", .{ + .flags = macho.S_THREAD_LOCAL_VARIABLES, + }); + } + break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.tlv_section_index.?, }; }, macho.S_THREAD_LOCAL_REGULAR => { + if (self.tlv_data_section_index == null) { + self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__thread_data", "__DATA", .{ + .flags = macho.S_THREAD_LOCAL_REGULAR, + }); + } + break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.tlv_data_section_index.?, }; }, macho.S_THREAD_LOCAL_ZEROFILL => { + if (self.tlv_bss_section_index == null) { + self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__thread_bss", "__DATA", .{ + .flags = macho.S_THREAD_LOCAL_ZEROFILL, + }); + } + break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.tlv_bss_section_index.?, @@ -861,12 +730,25 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { }, macho.S_COALESCED => { if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) { + // TODO I believe __eh_frame is currently part of __unwind_info section + // in the latest ld64 output. + if (self.eh_frame_section_index == null) { + self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__eh_frame", "__TEXT", .{}); + } + break :blk .{ .seg = self.text_segment_cmd_index.?, .sect = self.eh_frame_section_index.?, }; } + // TODO audit this: is this the right mapping? + if (self.data_const_section_index == null) { + self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); + } + break :blk .{ .seg = self.data_const_segment_cmd_index.?, .sect = self.data_const_section_index.?, @@ -874,6 +756,13 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { }, macho.S_REGULAR => { if (sect.isCode()) { + if (self.text_section_index == null) { + self.text_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__text", "__TEXT", .{ + .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, + }); + } + break :blk .{ .seg = self.text_segment_cmd_index.?, .sect = self.text_section_index.?, @@ -881,21 +770,41 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { } if (sect.isDebug()) { // TODO debug attributes + if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { + log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ + sect.flags(), segname, sectname, + }); + } break :blk null; } if (mem.eql(u8, segname, "__TEXT")) { if (mem.eql(u8, sectname, "__ustring")) { + if (self.ustring_section_index == null) { + self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__ustring", "__TEXT", .{}); + } + break :blk .{ .seg = self.text_segment_cmd_index.?, .sect = self.ustring_section_index.?, }; } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { + if (self.gcc_except_tab_section_index == null) { + self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__gcc_except_tab", "__TEXT", .{}); + } + break :blk .{ .seg = self.text_segment_cmd_index.?, .sect = self.gcc_except_tab_section_index.?, }; } else { + if (self.text_const_section_index == null) { + self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__const", "__TEXT", .{}); + } + break :blk .{ .seg = self.text_segment_cmd_index.?, .sect = self.text_const_section_index.?, @@ -904,6 +813,11 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { } if (mem.eql(u8, segname, "__DATA_CONST")) { + if (self.data_const_section_index == null) { + self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); + } + break :blk .{ .seg = self.data_const_segment_cmd_index.?, .sect = self.data_const_section_index.?, @@ -912,20 +826,42 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection { if (mem.eql(u8, segname, "__DATA")) { if (mem.eql(u8, sectname, "__const")) { + if (self.data_const_section_index == null) { + self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); + } + break :blk .{ .seg = self.data_const_segment_cmd_index.?, .sect = self.data_const_section_index.?, }; } else if (mem.eql(u8, sectname, "__cfstring")) { + if (self.objc_cfstring_section_index == null) { + self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__cfstring", "__DATA_CONST", .{}); + } + break :blk .{ .seg = self.data_const_segment_cmd_index.?, .sect = self.objc_cfstring_section_index.?, }; + } else { + if (self.data_section_index == null) { + self.data_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__data", "__DATA", .{}); + } + + break :blk .{ + .seg = self.data_segment_cmd_index.?, + .sect = self.data_section_index.?, + }; } - break :blk .{ - .seg = self.data_segment_cmd_index.?, - .sect = self.data_section_index.?, - }; + } + + if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) { + log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{ + sect.flags(), segname, sectname, + }); } break :blk null; -- cgit v1.2.3 From a480ae6e37199a3b210a4029f3d3c728e959a21d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 21 Jun 2021 14:16:10 +0200 Subject: zld: handle objc-specific sections --- src/link/MachO/Zld.zig | 139 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 23b077fd67..928af2d07d 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -73,6 +73,11 @@ gcc_except_tab_section_index: ?u16 = null, unwind_info_section_index: ?u16 = null, eh_frame_section_index: ?u16 = null, +objc_methlist_section_index: ?u16 = null, +objc_methname_section_index: ?u16 = null, +objc_methtype_section_index: ?u16 = null, +objc_classname_section_index: ?u16 = null, + // __DATA_CONST segment sections got_section_index: ?u16 = null, mod_init_func_section_index: ?u16 = null, @@ -80,6 +85,8 @@ mod_term_func_section_index: ?u16 = null, data_const_section_index: ?u16 = null, objc_cfstring_section_index: ?u16 = null, +objc_classlist_section_index: ?u16 = null, +objc_imageinfo_section_index: ?u16 = null, // __DATA segment sections tlv_section_index: ?u16 = null, @@ -90,6 +97,11 @@ data_section_index: ?u16 = null, bss_section_index: ?u16 = null, common_section_index: ?u16 = null, +objc_const_section_index: ?u16 = null, +objc_selrefs_section_index: ?u16 = null, +objc_classrefs_section_index: ?u16 = null, +objc_data_section_index: ?u16 = null, + globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, @@ -612,7 +624,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { const res: ?MatchingSection = blk: { switch (sect.sectionType()) { - macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS, macho.S_LITERAL_POINTERS => { + macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { if (self.text_const_section_index == null) { self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); try text_seg.addSection(self.allocator, "__const", "__TEXT", .{}); @@ -624,6 +636,44 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { }; }, macho.S_CSTRING_LITERALS => { + if (mem.eql(u8, sectname, "__objc_methname")) { + // TODO it seems the common values within the sections in objects are deduplicated/merged + // on merging the sections' contents. + if (self.objc_methname_section_index == null) { + self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__objc_methname", "__TEXT", .{ + .flags = macho.S_CSTRING_LITERALS, + }); + } + + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.objc_methname_section_index.?, + }; + } else if (mem.eql(u8, sectname, "__objc_methtype")) { + if (self.objc_methtype_section_index == null) { + self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__objc_methtype", "__TEXT", .{ + .flags = macho.S_CSTRING_LITERALS, + }); + } + + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.objc_methtype_section_index.?, + }; + } else if (mem.eql(u8, sectname, "__objc_classname")) { + if (self.objc_classname_section_index == null) { + self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__objc_classname", "__TEXT", .{}); + } + + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.objc_classname_section_index.?, + }; + } + if (self.cstring_section_index == null) { self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); try text_seg.addSection(self.allocator, "__cstring", "__TEXT", .{ @@ -636,6 +686,24 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { .sect = self.cstring_section_index.?, }; }, + macho.S_LITERAL_POINTERS => { + if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { + if (self.objc_selrefs_section_index == null) { + self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__objc_selrefs", "__DATA", .{ + .flags = macho.S_LITERAL_POINTERS, + }); + } + + break :blk .{ + .seg = self.data_segment_cmd_index.?, + .sect = self.objc_selrefs_section_index.?, + }; + } + + // TODO investigate + break :blk null; + }, macho.S_MOD_INIT_FUNC_POINTERS => { if (self.mod_init_func_section_index == null) { self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); @@ -799,6 +867,16 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { .seg = self.text_segment_cmd_index.?, .sect = self.gcc_except_tab_section_index.?, }; + } else if (mem.eql(u8, sectname, "__objc_methlist")) { + if (self.objc_methlist_section_index == null) { + self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, "__objc_methlist", "__TEXT", .{}); + } + + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.objc_methlist_section_index.?, + }; } else { if (self.text_const_section_index == null) { self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); @@ -845,6 +923,56 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { .seg = self.data_const_segment_cmd_index.?, .sect = self.objc_cfstring_section_index.?, }; + } else if (mem.eql(u8, sectname, "__objc_classlist")) { + if (self.objc_classlist_section_index == null) { + self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__objc_classlist", "__DATA_CONST", .{}); + } + + break :blk .{ + .seg = self.data_const_segment_cmd_index.?, + .sect = self.objc_classlist_section_index.?, + }; + } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { + if (self.objc_imageinfo_section_index == null) { + self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, "__objc_imageinfo", "__DATA_CONST", .{}); + } + + break :blk .{ + .seg = self.data_const_segment_cmd_index.?, + .sect = self.objc_imageinfo_section_index.?, + }; + } else if (mem.eql(u8, sectname, "__objc_const")) { + if (self.objc_const_section_index == null) { + self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__objc_const", "__DATA", .{}); + } + + break :blk .{ + .seg = self.data_segment_cmd_index.?, + .sect = self.objc_const_section_index.?, + }; + } else if (mem.eql(u8, sectname, "__objc_classrefs")) { + if (self.objc_classrefs_section_index == null) { + self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__objc_classrefs", "__DATA", .{}); + } + + break :blk .{ + .seg = self.data_segment_cmd_index.?, + .sect = self.objc_classrefs_section_index.?, + }; + } else if (mem.eql(u8, sectname, "__objc_data")) { + if (self.objc_data_section_index == null) { + self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__objc_data", "__DATA", .{}); + } + + break :blk .{ + .seg = self.data_segment_cmd_index.?, + .sect = self.objc_data_section_index.?, + }; } else { if (self.data_section_index == null) { self.data_section_index = @intCast(u16, data_seg.sections.items.len); @@ -896,6 +1024,9 @@ fn sortSections(self: *Zld) !void { &self.cstring_section_index, &self.ustring_section_index, &self.text_const_section_index, + &self.objc_methname_section_index, + &self.objc_methtype_section_index, + &self.objc_classname_section_index, &self.eh_frame_section_index, }; for (indices) |maybe_index| { @@ -922,6 +1053,8 @@ fn sortSections(self: *Zld) !void { &self.mod_term_func_section_index, &self.data_const_section_index, &self.objc_cfstring_section_index, + &self.objc_classlist_section_index, + &self.objc_imageinfo_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -944,6 +1077,10 @@ fn sortSections(self: *Zld) !void { // __DATA segment const indices = &[_]*?u16{ &self.la_symbol_ptr_section_index, + &self.objc_const_section_index, + &self.objc_selrefs_section_index, + &self.objc_classrefs_section_index, + &self.objc_data_section_index, &self.data_section_index, &self.tlv_section_index, &self.tlv_data_section_index, -- cgit v1.2.3 From d1fcb998484c8dec6e931b51a89ce4b2999254f5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 21 Jun 2021 14:31:20 +0200 Subject: zld+macho: populate segname from SegmentCommand when adding section --- src/link/MachO.zig | 12 +++---- src/link/MachO/DebugSymbols.zig | 10 +++--- src/link/MachO/Zld.zig | 72 ++++++++++++++++++++--------------------- src/link/MachO/commands.zig | 9 +++--- 4 files changed, 52 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index cc00cafe3e..3521874129 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1825,7 +1825,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __text section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try text_segment.addSection(self.base.allocator, "__text", "__TEXT", .{ + try text_segment.addSection(self.base.allocator, "__text", .{ .addr = text_segment.inner.vmaddr + off, .size = @intCast(u32, needed_size), .offset = @intCast(u32, off), @@ -1856,7 +1856,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __stubs section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try text_segment.addSection(self.base.allocator, "__stubs", "__TEXT", .{ + try text_segment.addSection(self.base.allocator, "__stubs", .{ .addr = text_segment.inner.vmaddr + off, .size = needed_size, .offset = @intCast(u32, off), @@ -1883,7 +1883,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __stub_helper section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try text_segment.addSection(self.base.allocator, "__stub_helper", "__TEXT", .{ + try text_segment.addSection(self.base.allocator, "__stub_helper", .{ .addr = text_segment.inner.vmaddr + off, .size = needed_size, .offset = @intCast(u32, off), @@ -1928,7 +1928,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __got section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try dc_segment.addSection(self.base.allocator, "__got", "__DATA_CONST", .{ + try dc_segment.addSection(self.base.allocator, "__got", .{ .addr = dc_segment.inner.vmaddr + off - dc_segment.inner.fileoff, .size = needed_size, .offset = @intCast(u32, off), @@ -1973,7 +1973,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __la_symbol_ptr section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try data_segment.addSection(self.base.allocator, "__la_symbol_ptr", "__DATA", .{ + try data_segment.addSection(self.base.allocator, "__la_symbol_ptr", .{ .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, .size = needed_size, .offset = @intCast(u32, off), @@ -1993,7 +1993,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __data section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - try data_segment.addSection(self.base.allocator, "__data", "__DATA", .{ + try data_segment.addSection(self.base.allocator, "__data", .{ .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, .size = needed_size, .offset = @intCast(u32, off), diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index c26ce889ae..684861ebf5 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -226,7 +226,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len); assert(self.debug_string_table.items.len == 0); - try dwarf_segment.addSection(allocator, "__debug_str", "__DWARF", .{ + try dwarf_segment.addSection(allocator, "__debug_str", .{ .addr = dwarf_segment.inner.vmaddr, .size = @intCast(u32, self.debug_string_table.items.len), .offset = @intCast(u32, dwarf_segment.inner.fileoff), @@ -246,7 +246,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, "__debug_info", "__DWARF", .{ + try dwarf_segment.addSection(allocator, "__debug_info", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), @@ -266,7 +266,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, "__debug_abbrev", "__DWARF", .{ + try dwarf_segment.addSection(allocator, "__debug_abbrev", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), @@ -286,7 +286,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, "__debug_aranges", "__DWARF", .{ + try dwarf_segment.addSection(allocator, "__debug_aranges", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), @@ -306,7 +306,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found dSym __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - try dwarf_segment.addSection(allocator, "__debug_line", "__DWARF", .{ + try dwarf_segment.addSection(allocator, "__debug_line", .{ .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, .size = file_size_hint, .offset = @intCast(u32, off), diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 928af2d07d..31f1eef303 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -540,7 +540,7 @@ fn updateMetadata(self: *Zld) !void { 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, "__common", "__DATA", .{ + try data_seg.addSection(self.allocator, "__common", .{ .flags = macho.S_ZEROFILL, }); break :ind self.common_section_index.?; @@ -627,7 +627,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { if (self.text_const_section_index == null) { self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__const", "__TEXT", .{}); + try text_seg.addSection(self.allocator, "__const", .{}); } break :blk .{ @@ -641,7 +641,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { // on merging the sections' contents. if (self.objc_methname_section_index == null) { self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__objc_methname", "__TEXT", .{ + try text_seg.addSection(self.allocator, "__objc_methname", .{ .flags = macho.S_CSTRING_LITERALS, }); } @@ -653,7 +653,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_methtype")) { if (self.objc_methtype_section_index == null) { self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__objc_methtype", "__TEXT", .{ + try text_seg.addSection(self.allocator, "__objc_methtype", .{ .flags = macho.S_CSTRING_LITERALS, }); } @@ -665,7 +665,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_classname")) { if (self.objc_classname_section_index == null) { self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__objc_classname", "__TEXT", .{}); + try text_seg.addSection(self.allocator, "__objc_classname", .{}); } break :blk .{ @@ -676,7 +676,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { if (self.cstring_section_index == null) { self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__cstring", "__TEXT", .{ + try text_seg.addSection(self.allocator, "__cstring", .{ .flags = macho.S_CSTRING_LITERALS, }); } @@ -690,7 +690,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { if (self.objc_selrefs_section_index == null) { self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__objc_selrefs", "__DATA", .{ + try data_seg.addSection(self.allocator, "__objc_selrefs", .{ .flags = macho.S_LITERAL_POINTERS, }); } @@ -707,7 +707,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { macho.S_MOD_INIT_FUNC_POINTERS => { if (self.mod_init_func_section_index == null) { self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__mod_init_func", "__DATA_CONST", .{ + try data_const_seg.addSection(self.allocator, "__mod_init_func", .{ .flags = macho.S_MOD_INIT_FUNC_POINTERS, }); } @@ -720,7 +720,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { macho.S_MOD_TERM_FUNC_POINTERS => { if (self.mod_term_func_section_index == null) { self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__mod_term_func", "__DATA_CONST", .{ + try data_const_seg.addSection(self.allocator, "__mod_term_func", .{ .flags = macho.S_MOD_TERM_FUNC_POINTERS, }); } @@ -734,7 +734,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { if (mem.eql(u8, sectname, "__common")) { if (self.common_section_index == null) { self.common_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__common", "__DATA", .{ + try data_seg.addSection(self.allocator, "__common", .{ .flags = macho.S_ZEROFILL, }); } @@ -746,7 +746,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else { if (self.bss_section_index == null) { self.bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__bss", "__DATA", .{ + try data_seg.addSection(self.allocator, "__bss", .{ .flags = macho.S_ZEROFILL, }); } @@ -760,7 +760,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { macho.S_THREAD_LOCAL_VARIABLES => { if (self.tlv_section_index == null) { self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__thread_vars", "__DATA", .{ + try data_seg.addSection(self.allocator, "__thread_vars", .{ .flags = macho.S_THREAD_LOCAL_VARIABLES, }); } @@ -773,7 +773,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { macho.S_THREAD_LOCAL_REGULAR => { if (self.tlv_data_section_index == null) { self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__thread_data", "__DATA", .{ + try data_seg.addSection(self.allocator, "__thread_data", .{ .flags = macho.S_THREAD_LOCAL_REGULAR, }); } @@ -786,7 +786,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { macho.S_THREAD_LOCAL_ZEROFILL => { if (self.tlv_bss_section_index == null) { self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__thread_bss", "__DATA", .{ + try data_seg.addSection(self.allocator, "__thread_bss", .{ .flags = macho.S_THREAD_LOCAL_ZEROFILL, }); } @@ -802,7 +802,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { // in the latest ld64 output. if (self.eh_frame_section_index == null) { self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__eh_frame", "__TEXT", .{}); + try text_seg.addSection(self.allocator, "__eh_frame", .{}); } break :blk .{ @@ -814,7 +814,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { // TODO audit this: is this the right mapping? if (self.data_const_section_index == null) { self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); + try data_const_seg.addSection(self.allocator, "__const", .{}); } break :blk .{ @@ -826,7 +826,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { if (sect.isCode()) { if (self.text_section_index == null) { self.text_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__text", "__TEXT", .{ + try text_seg.addSection(self.allocator, "__text", .{ .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, }); } @@ -850,7 +850,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { if (mem.eql(u8, sectname, "__ustring")) { if (self.ustring_section_index == null) { self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__ustring", "__TEXT", .{}); + try text_seg.addSection(self.allocator, "__ustring", .{}); } break :blk .{ @@ -860,7 +860,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { if (self.gcc_except_tab_section_index == null) { self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__gcc_except_tab", "__TEXT", .{}); + try text_seg.addSection(self.allocator, "__gcc_except_tab", .{}); } break :blk .{ @@ -870,7 +870,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_methlist")) { if (self.objc_methlist_section_index == null) { self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__objc_methlist", "__TEXT", .{}); + try text_seg.addSection(self.allocator, "__objc_methlist", .{}); } break :blk .{ @@ -880,7 +880,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else { if (self.text_const_section_index == null) { self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, "__const", "__TEXT", .{}); + try text_seg.addSection(self.allocator, "__const", .{}); } break :blk .{ @@ -893,7 +893,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { if (mem.eql(u8, segname, "__DATA_CONST")) { if (self.data_const_section_index == null) { self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); + try data_const_seg.addSection(self.allocator, "__const", .{}); } break :blk .{ @@ -906,7 +906,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { if (mem.eql(u8, sectname, "__const")) { if (self.data_const_section_index == null) { self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__const", "__DATA_CONST", .{}); + try data_const_seg.addSection(self.allocator, "__const", .{}); } break :blk .{ @@ -916,7 +916,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__cfstring")) { if (self.objc_cfstring_section_index == null) { self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__cfstring", "__DATA_CONST", .{}); + try data_const_seg.addSection(self.allocator, "__cfstring", .{}); } break :blk .{ @@ -926,7 +926,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_classlist")) { if (self.objc_classlist_section_index == null) { self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__objc_classlist", "__DATA_CONST", .{}); + try data_const_seg.addSection(self.allocator, "__objc_classlist", .{}); } break :blk .{ @@ -936,7 +936,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { if (self.objc_imageinfo_section_index == null) { self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__objc_imageinfo", "__DATA_CONST", .{}); + try data_const_seg.addSection(self.allocator, "__objc_imageinfo", .{}); } break :blk .{ @@ -946,7 +946,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_const")) { if (self.objc_const_section_index == null) { self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__objc_const", "__DATA", .{}); + try data_seg.addSection(self.allocator, "__objc_const", .{}); } break :blk .{ @@ -956,7 +956,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_classrefs")) { if (self.objc_classrefs_section_index == null) { self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__objc_classrefs", "__DATA", .{}); + try data_seg.addSection(self.allocator, "__objc_classrefs", .{}); } break :blk .{ @@ -966,7 +966,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else if (mem.eql(u8, sectname, "__objc_data")) { if (self.objc_data_section_index == null) { self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__objc_data", "__DATA", .{}); + try data_seg.addSection(self.allocator, "__objc_data", .{}); } break :blk .{ @@ -976,7 +976,7 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { } else { if (self.data_section_index == null) { self.data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__data", "__DATA", .{}); + try data_seg.addSection(self.allocator, "__data", .{}); } break :blk .{ @@ -2124,7 +2124,7 @@ fn populateMetadata(self: *Zld) !void { .aarch64 => 2, else => unreachable, // unhandled architecture type }; - try text_seg.addSection(self.allocator, "__text", "__TEXT", .{ + try text_seg.addSection(self.allocator, "__text", .{ .@"align" = alignment, .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, }); @@ -2143,7 +2143,7 @@ fn populateMetadata(self: *Zld) !void { .aarch64 => 3 * @sizeOf(u32), else => unreachable, // unhandled architecture type }; - try text_seg.addSection(self.allocator, "__stubs", "__TEXT", .{ + try text_seg.addSection(self.allocator, "__stubs", .{ .@"align" = alignment, .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, .reserved2 = stub_size, @@ -2163,7 +2163,7 @@ fn populateMetadata(self: *Zld) !void { .aarch64 => 6 * @sizeOf(u32), else => unreachable, }; - try text_seg.addSection(self.allocator, "__stub_helper", "__TEXT", .{ + try text_seg.addSection(self.allocator, "__stub_helper", .{ .size = stub_helper_size, .@"align" = alignment, .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, @@ -2183,7 +2183,7 @@ fn populateMetadata(self: *Zld) !void { if (self.got_section_index == null) { const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; self.got_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, "__got", "__DATA_CONST", .{ + try data_const_seg.addSection(self.allocator, "__got", .{ .@"align" = 3, // 2^3 = @sizeOf(u64) .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, }); @@ -2202,7 +2202,7 @@ fn populateMetadata(self: *Zld) !void { if (self.la_symbol_ptr_section_index == null) { const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__la_symbol_ptr", "__DATA", .{ + try data_seg.addSection(self.allocator, "__la_symbol_ptr", .{ .@"align" = 3, // 2^3 = @sizeOf(u64) .flags = macho.S_LAZY_SYMBOL_POINTERS, }); @@ -2211,7 +2211,7 @@ fn populateMetadata(self: *Zld) !void { if (self.data_section_index == null) { const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; self.data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__data", "__DATA", .{ + try data_seg.addSection(self.allocator, "__data", .{ .@"align" = 3, // 2^3 = @sizeOf(u64) }); } diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index 6ad1964483..6958b8d1e6 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -233,12 +233,11 @@ pub const SegmentCommand = struct { self: *SegmentCommand, alloc: *Allocator, comptime sectname: []const u8, - comptime segname: []const u8, opts: SectionOptions, ) !void { - try self.sections.append(alloc, .{ + var section = macho.section_64{ .sectname = makeStaticString(sectname), - .segname = makeStaticString(segname), + .segname = undefined, .addr = opts.addr, .size = opts.size, .offset = opts.offset, @@ -249,7 +248,9 @@ pub const SegmentCommand = struct { .reserved1 = opts.reserved1, .reserved2 = opts.reserved2, .reserved3 = opts.reserved3, - }); + }; + mem.copy(u8, §ion.segname, &self.inner.segname); + try self.sections.append(alloc, section); self.inner.cmdsize += @sizeOf(macho.section_64); self.inner.nsects += 1; } -- cgit v1.2.3 From 0736365fa45fe4f3649a98a63fa82ccf8fc70d40 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 22 Jun 2021 10:29:58 +0200 Subject: zld: fix finding pointers for rebasing --- src/link/MachO/Zld.zig | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 31f1eef303..8a302ea06c 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -1948,29 +1948,52 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { args.source_target_sect_addr = source_sect.inner.addr; } - rebases: { - var hit: bool = false; - if (target_map.segment_id == self.data_segment_cmd_index.?) { - if (self.data_section_index) |index| { - if (index == target_map.section_id) hit = true; + const flags = @truncate(u8, target_sect.flags & 0xff); + 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 (flags != macho.S_LITERAL_POINTERS and + flags != macho.S_REGULAR) + { + break :rebase false; } - if (target_map.segment_id == self.data_const_segment_cmd_index.?) { - if (self.data_const_section_index) |index| { - if (index == target_map.section_id) hit = true; + if (rel.target == .symbol) { + const final = rel.target.symbol.getTopmostAlias(); + if (final.cast(Symbol.Proxy)) |_| { + break :rebase false; } } - if (!hit) break :rebases; + 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 (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { + if (flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { // TODO we don't want to save offset to tlv_bootstrap if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv; -- cgit v1.2.3 From 1ff3ebffa36a6d8f0b2489b7cbb0aceaf9189064 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 22 Jun 2021 11:17:43 +0200 Subject: zld: handle dynamic binding of proxies for objc correctly --- src/link/MachO/Symbol.zig | 5 +++++ src/link/MachO/Zld.zig | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 2286b1ea93..01fcd15984 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -85,6 +85,11 @@ pub const Regular = struct { pub const Proxy = struct { base: Symbol, + bind_info: std.ArrayListUnmanaged(struct { + segment_id: u16, + address: u64, + }) = .{}, + /// Dylib or stub where to locate this symbol. /// null means self-reference. file: ?union(enum) { diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 8a302ea06c..c229fe9742 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -263,6 +263,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L self.allocateLinkeditSegment(); try self.allocateSymbols(); try self.allocateTentativeSymbols(); + try self.allocateProxiesBindAddresses(); try self.flush(); } @@ -1346,6 +1347,30 @@ fn allocateTentativeSymbols(self: *Zld) !void { } } +fn allocateProxiesBindAddresses(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 = rel.target.symbol.getTopmostAlias(); + if (sym.cast(Symbol.Proxy)) |proxy| { + 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 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.?]; @@ -1863,6 +1888,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { const relocs = sect.relocs orelse continue; for (relocs) |rel| { switch (rel.@"type") { + .unsigned => continue, .got_page, .got_page_off, .got_load, .got, .pointer_to_got => { const sym = rel.target.symbol.getTopmostAlias(); if (sym.got_index != null) continue; @@ -2090,7 +2116,10 @@ fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.T const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const stubs = segment.sections.items[self.stubs_section_index.?]; const stubs_index = proxy.base.stubs_index orelse { - log.err("expected stubs index when relocating symbol '{s}'", .{final.name}); + if (proxy.bind_info.items.len > 0) { + break :blk 0; // Dynamically bound by dyld. + } + log.err("expected stubs index or dynamic bind address when relocating symbol '{s}'", .{final.name}); log.err("this is an internal linker error", .{}); return error.FailedToResolveRelocationTarget; }; @@ -2640,6 +2669,20 @@ fn writeBindInfoTable(self: *Zld) !void { } } + for (self.imports.values()) |sym| { + if (sym.cast(Symbol.Proxy)) |proxy| { + for (proxy.bind_info.items) |info| { + const seg = self.load_commands.items[info.segment_id].Segment; + try pointers.append(.{ + .offset = info.address - seg.inner.vmaddr, + .segment_id = info.segment_id, + .dylib_ordinal = proxy.dylibOrdinal(), + .name = proxy.base.name, + }); + } + } + } + if (self.tlv_section_index) |idx| { const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; -- cgit v1.2.3 From 52a9d3f03797f1cd387ec5f2c2a1714e1121cdab Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 22 Jun 2021 13:58:25 +0200 Subject: zld: clean up memory management and refactor --- src/link/MachO/Stub.zig | 62 +++++++++++++++-------------------------------- src/link/MachO/Symbol.zig | 18 ++++++++++++++ src/link/MachO/Zld.zig | 10 +++++--- 3 files changed, 44 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Stub.zig b/src/link/MachO/Stub.zig index d71c454c79..40ed63b673 100644 --- a/src/link/MachO/Stub.zig +++ b/src/link/MachO/Stub.zig @@ -41,6 +41,9 @@ pub fn init(allocator: *Allocator) Stub { } pub fn deinit(self: *Stub) void { + for (self.symbols.keys()) |key| { + self.allocator.free(key); + } self.symbols.deinit(self.allocator); if (self.lib_stub) |*lib_stub| { @@ -56,6 +59,18 @@ pub fn deinit(self: *Stub) void { } } +fn addObjCClassSymbols(self: *Stub, sym_name: []const u8) !void { + const expanded = &[_][]const u8{ + try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), + try std.fmt.allocPrint(self.allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}), + }; + + for (expanded) |sym| { + if (self.symbols.contains(sym)) continue; + try self.symbols.putNoClobber(self.allocator, sym, .{}); + } +} + pub fn parse(self: *Stub) !void { const lib_stub = self.lib_stub orelse return error.EmptyStubFile; if (lib_stub.inner.len == 0) return error.EmptyStubFile; @@ -87,32 +102,13 @@ pub fn parse(self: *Stub) !void { if (exp.symbols) |symbols| { for (symbols) |sym_name| { if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, sym_name, {}); + try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); } } if (exp.objc_classes) |classes| { for (classes) |sym_name| { - log.debug(" | {s}", .{sym_name}); - { - const actual_sym_name = try std.fmt.allocPrint( - self.allocator, - "_OBJC_CLASS_$_{s}", - .{sym_name}, - ); - if (self.symbols.contains(actual_sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); - } - - { - const actual_sym_name = try std.fmt.allocPrint( - self.allocator, - "_OBJC_METACLASS_$_{s}", - .{sym_name}, - ); - if (self.symbols.contains(actual_sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); - } + try self.addObjCClassSymbols(sym_name); } } } @@ -124,34 +120,14 @@ pub fn parse(self: *Stub) !void { for (reexp.symbols) |sym_name| { if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, sym_name, {}); + try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); } } } if (stub.objc_classes) |classes| { - log.debug(" | objc_classes", .{}); for (classes) |sym_name| { - log.debug(" | {s}", .{sym_name}); - { - const actual_sym_name = try std.fmt.allocPrint( - self.allocator, - "_OBJC_CLASS_$_{s}", - .{sym_name}, - ); - if (self.symbols.contains(actual_sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); - } - - { - const actual_sym_name = try std.fmt.allocPrint( - self.allocator, - "_OBJC_METACLASS_$_{s}", - .{sym_name}, - ); - if (self.symbols.contains(actual_sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, actual_sym_name, {}); - } + try self.addObjCClassSymbols(sym_name); } } } diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 01fcd15984..b2e7867ca3 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -74,6 +74,8 @@ pub const Regular = struct { global, }; + pub fn deinit(regular: *Regular, allocator: *Allocator) void {} + 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"); @@ -85,6 +87,8 @@ pub const Regular = struct { pub const Proxy = struct { base: Symbol, + /// Dynamic binding info - spots within the final + /// executable where this proxy is referenced from. bind_info: std.ArrayListUnmanaged(struct { segment_id: u16, address: u64, @@ -99,6 +103,10 @@ pub const Proxy = struct { pub const base_type: Symbol.Type = .proxy; + pub fn deinit(proxy: *Proxy, allocator: *Allocator) void { + proxy.bind_info.deinit(allocator); + } + pub fn dylibOrdinal(proxy: *Proxy) u16 { const file = proxy.file orelse return 0; return switch (file) { @@ -115,6 +123,8 @@ pub const Unresolved = struct { file: *Object, pub const base_type: Symbol.Type = .unresolved; + + pub fn deinit(unresolved: *Unresolved, allocator: *Allocator) void {} }; pub const Tentative = struct { @@ -130,10 +140,18 @@ pub const Tentative = struct { file: *Object, pub const base_type: Symbol.Type = .tentative; + + pub fn deinit(tentative: *Tentative, allocator: *Allocator) void {} }; pub fn deinit(base: *Symbol, allocator: *Allocator) void { allocator.free(base.name); + switch (base.@"type") { + .regular => @fieldParentPtr(Regular, "base", base).deinit(allocator), + .proxy => @fieldParentPtr(Proxy, "base", base).deinit(allocator), + .unresolved => @fieldParentPtr(Unresolved, "base", base).deinit(allocator), + .tentative => @fieldParentPtr(Tentative, "base", base).deinit(allocator), + } } pub fn cast(base: *Symbol, comptime T: type) ?*T { diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index c229fe9742..f2c0fbf718 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -263,7 +263,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L self.allocateLinkeditSegment(); try self.allocateSymbols(); try self.allocateTentativeSymbols(); - try self.allocateProxiesBindAddresses(); + try self.allocateProxyBindAddresses(); try self.flush(); } @@ -1347,7 +1347,7 @@ fn allocateTentativeSymbols(self: *Zld) !void { } } -fn allocateProxiesBindAddresses(self: *Zld) !void { +fn allocateProxyBindAddresses(self: *Zld) !void { for (self.objects.items) |object| { for (object.sections.items) |sect| { const relocs = sect.relocs orelse continue; @@ -1361,6 +1361,7 @@ fn allocateProxiesBindAddresses(self: *Zld) !void { 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 proxy.bind_info.append(self.allocator, .{ .segment_id = target_map.segment_id, .address = target_sect.addr + target_map.offset + rel.offset, @@ -2119,7 +2120,10 @@ fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.T if (proxy.bind_info.items.len > 0) { break :blk 0; // Dynamically bound by dyld. } - log.err("expected stubs index or dynamic bind address when relocating symbol '{s}'", .{final.name}); + log.err( + "expected stubs index or dynamic bind address when relocating symbol '{s}'", + .{final.name}, + ); log.err("this is an internal linker error", .{}); return error.FailedToResolveRelocationTarget; }; -- cgit v1.2.3 From 3f57468c8bd16f33b5ac21cf8eaea2fdb948b999 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 22 Jun 2021 12:47:56 +0200 Subject: Classify .m as ObjC, compile using clang and link with zld --- src/Compilation.zig | 12 ++++++++++-- src/main.zig | 5 +++-- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Compilation.zig b/src/Compilation.zig index ea878056ae..eea4d930c4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2857,7 +2857,7 @@ pub fn addCCArgs( try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); switch (ext) { - .c, .cpp, .h => { + .c, .cpp, .m, .h => { try argv.appendSlice(&[_][]const u8{ "-nostdinc", "-fno-spell-checking", @@ -3148,6 +3148,7 @@ pub const FileExt = enum { c, cpp, h, + m, ll, bc, assembly, @@ -3159,7 +3160,7 @@ pub const FileExt = enum { pub fn clangSupportsDepFile(ext: FileExt) bool { return switch (ext) { - .c, .cpp, .h => true, + .c, .cpp, .h, .m => true, .ll, .bc, @@ -3193,6 +3194,10 @@ pub fn hasCppExt(filename: []const u8) bool { mem.endsWith(u8, filename, ".cxx"); } +pub fn hasObjCExt(filename: []const u8) bool { + return mem.endsWith(u8, filename, ".m"); +} + pub fn hasAsmExt(filename: []const u8) bool { return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); } @@ -3229,6 +3234,8 @@ pub fn classifyFileExt(filename: []const u8) FileExt { return .c; } else if (hasCppExt(filename)) { return .cpp; + } else if (hasObjCExt(filename)) { + return .m; } else if (mem.endsWith(u8, filename, ".ll")) { return .ll; } else if (mem.endsWith(u8, filename, ".bc")) { @@ -3252,6 +3259,7 @@ pub fn classifyFileExt(filename: []const u8) FileExt { test "classifyFileExt" { try std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc")); + try std.testing.expectEqual(FileExt.m, classifyFileExt("foo.m")); try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim")); try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so")); try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1")); diff --git a/src/main.zig b/src/main.zig index f2d00c47bb..1cf62c011b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -290,6 +290,7 @@ const usage_build_generic = \\ .c C source code (requires LLVM extensions) \\ .cpp C++ source code (requires LLVM extensions) \\ Other C++ extensions: .C .cc .cxx + \\ .m Objective-C source code (requires LLVM extensions) \\ \\General Options: \\ -h, --help Print this help and exit @@ -1072,7 +1073,7 @@ fn buildOutputType( .object, .static_library, .shared_library => { try link_objects.append(arg); }, - .assembly, .c, .cpp, .h, .ll, .bc => { + .assembly, .c, .cpp, .h, .ll, .bc, .m => { try c_source_files.append(.{ .src_path = arg, .extra_flags = try arena.dupe([]const u8, extra_cflags.items), @@ -1135,7 +1136,7 @@ fn buildOutputType( .positional => { const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg)); switch (file_ext) { - .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }), + .assembly, .c, .cpp, .ll, .bc, .h, .m => try c_source_files.append(.{ .src_path = it.only_arg }), .unknown, .shared_library, .object, .static_library => { try link_objects.append(it.only_arg); }, -- cgit v1.2.3 From 3cb6b6bd90c3b304bf771b37e974dd943c060e2b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 23 Jun 2021 11:09:45 +0200 Subject: zld: merge Stub with Dylib struct After giving it more thought, it doesn't make sense to separate the two structurally. Instead, there should be two constructors for a Dylib struct: one from binary file, and the other from a stub file. This cleans up a lot of code and opens the way for recursive parsing of re-exports from a dylib which are a hard requirement for native feel when linking frameworks. --- CMakeLists.txt | 1 - src/link/MachO/Dylib.zig | 90 +++++++++++++++- src/link/MachO/Stub.zig | 159 --------------------------- src/link/MachO/Symbol.zig | 15 +-- src/link/MachO/Zld.zig | 267 +++++++++++++++++++++------------------------- 5 files changed, 212 insertions(+), 320 deletions(-) delete mode 100644 src/link/MachO/Stub.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index edd24d282e..f279990e27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -579,7 +579,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" - "${CMAKE_SOURCE_DIR}/src/link/MachO/Stub.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index bcf2433dd7..44b012905e 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -9,10 +9,12 @@ const mem = std.mem; const Allocator = mem.Allocator; const Symbol = @import("Symbol.zig"); +const LibStub = @import("../tapi.zig").LibStub; usingnamespace @import("commands.zig"); allocator: *Allocator, + arch: ?std.Target.Cpu.Arch = null, header: ?macho.mach_header_64 = null, file: ?fs.File = null, @@ -103,7 +105,7 @@ pub fn parse(self: *Dylib) !void { try self.parseSymbols(); } -pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { +fn readLoadCommands(self: *Dylib, reader: anytype) !void { try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); var i: u16 = 0; @@ -127,7 +129,7 @@ pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { } } -pub fn parseId(self: *Dylib) !void { +fn parseId(self: *Dylib) !void { const index = self.id_cmd_index orelse { log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); self.id = .{ @@ -153,7 +155,7 @@ pub fn parseId(self: *Dylib) !void { }; } -pub fn parseSymbols(self: *Dylib) !void { +fn parseSymbols(self: *Dylib) !void { const index = self.symtab_cmd_index orelse return; const symtab_cmd = self.load_commands.items[index].Symtab; @@ -176,6 +178,86 @@ pub fn parseSymbols(self: *Dylib) !void { } } +fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { + const expanded = &[_][]const u8{ + try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), + try std.fmt.allocPrint(self.allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}), + }; + + for (expanded) |sym| { + if (self.symbols.contains(sym)) continue; + try self.symbols.putNoClobber(self.allocator, sym, .{}); + } +} + +pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { + if (lib_stub.inner.len == 0) return error.EmptyStubFile; + + log.debug("parsing shared library from stub '{s}'", .{self.name.?}); + + const umbrella_lib = lib_stub.inner[0]; + self.id = .{ + .name = try self.allocator.dupe(u8, umbrella_lib.install_name), + // TODO parse from the stub + .timestamp = 2, + .current_version = 0, + .compatibility_version = 0, + }; + + const target_string: []const u8 = switch (self.arch.?) { + .aarch64 => "arm64-macos", + .x86_64 => "x86_64-macos", + else => unreachable, + }; + + for (lib_stub.inner) |stub| { + if (!hasTarget(stub.targets, target_string)) continue; + + if (stub.exports) |exports| { + for (exports) |exp| { + if (!hasTarget(exp.targets, target_string)) continue; + + if (exp.symbols) |symbols| { + for (symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); + } + } + + if (exp.objc_classes) |classes| { + for (classes) |sym_name| { + try self.addObjCClassSymbols(sym_name); + } + } + } + } + + if (stub.reexports) |reexports| { + for (reexports) |reexp| { + if (!hasTarget(reexp.targets, target_string)) continue; + + for (reexp.symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); + } + } + } + + if (stub.objc_classes) |classes| { + for (classes) |sym_name| { + try self.addObjCClassSymbols(sym_name); + } + } + } +} + +fn hasTarget(targets: []const []const u8, target: []const u8) bool { + for (targets) |t| { + if (mem.eql(u8, t, target)) return true; + } + return false; +} + pub fn isDylib(file: fs.File) !bool { const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) { error.EndOfStream => return false, @@ -197,7 +279,7 @@ pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol { .@"type" = .proxy, .name = name, }, - .file = .{ .dylib = self }, + .file = self, }; return &proxy.base; diff --git a/src/link/MachO/Stub.zig b/src/link/MachO/Stub.zig deleted file mode 100644 index 40ed63b673..0000000000 --- a/src/link/MachO/Stub.zig +++ /dev/null @@ -1,159 +0,0 @@ -const Stub = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const fs = std.fs; -const log = std.log.scoped(.stub); -const macho = std.macho; -const mem = std.mem; - -const Allocator = mem.Allocator; -const Symbol = @import("Symbol.zig"); -pub const LibStub = @import("../tapi.zig").LibStub; - -allocator: *Allocator, -arch: ?std.Target.Cpu.Arch = null, -lib_stub: ?LibStub = null, -name: ?[]const u8 = null, - -ordinal: ?u16 = null, - -id: ?Id = null, - -/// Parsed symbol table represented as hash map of symbols' -/// names. We can and should defer creating *Symbols until -/// a symbol is referenced by an object file. -symbols: std.StringArrayHashMapUnmanaged(void) = .{}, - -pub const Id = struct { - name: []const u8, - timestamp: u32, - current_version: u32, - compatibility_version: u32, - - pub fn deinit(id: *Id, allocator: *Allocator) void { - allocator.free(id.name); - } -}; - -pub fn init(allocator: *Allocator) Stub { - return .{ .allocator = allocator }; -} - -pub fn deinit(self: *Stub) void { - for (self.symbols.keys()) |key| { - self.allocator.free(key); - } - self.symbols.deinit(self.allocator); - - if (self.lib_stub) |*lib_stub| { - lib_stub.deinit(); - } - - if (self.name) |name| { - self.allocator.free(name); - } - - if (self.id) |*id| { - id.deinit(self.allocator); - } -} - -fn addObjCClassSymbols(self: *Stub, sym_name: []const u8) !void { - const expanded = &[_][]const u8{ - try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), - try std.fmt.allocPrint(self.allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}), - }; - - for (expanded) |sym| { - if (self.symbols.contains(sym)) continue; - try self.symbols.putNoClobber(self.allocator, sym, .{}); - } -} - -pub fn parse(self: *Stub) !void { - const lib_stub = self.lib_stub orelse return error.EmptyStubFile; - if (lib_stub.inner.len == 0) return error.EmptyStubFile; - - log.debug("parsing shared library from stub '{s}'", .{self.name.?}); - - const umbrella_lib = lib_stub.inner[0]; - self.id = .{ - .name = try self.allocator.dupe(u8, umbrella_lib.install_name), - // TODO parse from the stub - .timestamp = 2, - .current_version = 0, - .compatibility_version = 0, - }; - - const target_string: []const u8 = switch (self.arch.?) { - .aarch64 => "arm64-macos", - .x86_64 => "x86_64-macos", - else => unreachable, - }; - - for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; - - if (stub.exports) |exports| { - for (exports) |exp| { - if (!hasTarget(exp.targets, target_string)) continue; - - if (exp.symbols) |symbols| { - for (symbols) |sym_name| { - if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); - } - } - - if (exp.objc_classes) |classes| { - for (classes) |sym_name| { - try self.addObjCClassSymbols(sym_name); - } - } - } - } - - if (stub.reexports) |reexports| { - for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; - - for (reexp.symbols) |sym_name| { - if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); - } - } - } - - if (stub.objc_classes) |classes| { - for (classes) |sym_name| { - try self.addObjCClassSymbols(sym_name); - } - } - } -} - -fn hasTarget(targets: []const []const u8, target: []const u8) bool { - for (targets) |t| { - if (mem.eql(u8, t, target)) return true; - } - return false; -} - -pub fn createProxy(self: *Stub, sym_name: []const u8) !?*Symbol { - if (!self.symbols.contains(sym_name)) return null; - - const name = try self.allocator.dupe(u8, sym_name); - const proxy = try self.allocator.create(Symbol.Proxy); - errdefer self.allocator.destroy(proxy); - - proxy.* = .{ - .base = .{ - .@"type" = .proxy, - .name = name, - }, - .file = .{ .stub = self }, - }; - - return &proxy.base; -} diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index b2e7867ca3..0c6777009d 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -7,7 +7,6 @@ const mem = std.mem; const Allocator = mem.Allocator; const Dylib = @import("Dylib.zig"); const Object = @import("Object.zig"); -const Stub = @import("Stub.zig"); pub const Type = enum { regular, @@ -94,12 +93,9 @@ pub const Proxy = struct { address: u64, }) = .{}, - /// Dylib or stub where to locate this symbol. + /// Dylib where to locate this symbol. /// null means self-reference. - file: ?union(enum) { - dylib: *Dylib, - stub: *Stub, - } = null, + file: ?*Dylib = null, pub const base_type: Symbol.Type = .proxy; @@ -108,11 +104,8 @@ pub const Proxy = struct { } pub fn dylibOrdinal(proxy: *Proxy) u16 { - const file = proxy.file orelse return 0; - return switch (file) { - .dylib => |dylib| dylib.ordinal.?, - .stub => |stub| stub.ordinal.?, - }; + const dylib = proxy.file orelse return 0; + return dylib.ordinal.?; } }; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index f2c0fbf718..b46feced49 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -16,8 +16,8 @@ const Allocator = mem.Allocator; const Archive = @import("Archive.zig"); const CodeSignature = @import("CodeSignature.zig"); const Dylib = @import("Dylib.zig"); +const LibStub = @import("../tapi.zig").LibStub; const Object = @import("Object.zig"); -const Stub = @import("Stub.zig"); const Symbol = @import("Symbol.zig"); const Trie = @import("Trie.zig"); @@ -38,9 +38,8 @@ stack_size: u64 = 0, objects: std.ArrayListUnmanaged(*Object) = .{}, archives: std.ArrayListUnmanaged(*Archive) = .{}, dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, -lib_stubs: std.ArrayListUnmanaged(*Stub) = .{}, -libsystem_stub_index: ?u16 = null, +libsystem_dylib_index: ?u16 = null, next_dylib_ordinal: u16 = 1, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, @@ -134,10 +133,6 @@ const TlvOffset = struct { /// Default path to dyld const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; -const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; -/// TODO this should be inferred from included libSystem.tbd or similar. -const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib"; - pub fn init(allocator: *Allocator) Zld { return .{ .allocator = allocator }; } @@ -171,12 +166,6 @@ pub fn deinit(self: *Zld) void { } self.dylibs.deinit(self.allocator); - for (self.lib_stubs.items) |stub| { - stub.deinit(); - self.allocator.destroy(stub); - } - self.lib_stubs.deinit(self.allocator); - for (self.imports.values()) |proxy| { proxy.deinit(self.allocator); self.allocator.destroy(proxy); @@ -269,20 +258,30 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L fn parseInputFiles(self: *Zld, files: []const []const u8) !void { const Input = struct { - kind: enum { - object, - archive, - dylib, - stub, - }, - origin: union { - file: fs.File, - stub: Stub.LibStub, + kind: union(enum) { + object: fs.File, + archive: fs.File, + dylib: fs.File, + stub: LibStub, }, name: []const u8, + + fn deinit(input: *@This()) void { + switch (input.kind) { + .stub => |*stub| { + stub.deinit(); + }, + else => {}, + } + } }; var classified = std.ArrayList(Input).init(self.allocator); - defer classified.deinit(); + defer { + for (classified.items) |*input| { + input.deinit(); + } + classified.deinit(); + } // First, classify input files: object, archive, dylib or stub (tbd). for (files) |file_name| { @@ -296,8 +295,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try_object: { if (!(try Object.isObject(file))) break :try_object; try classified.append(.{ - .kind = .object, - .origin = .{ .file = file }, + .kind = .{ .object = file }, .name = full_path, }); continue; @@ -306,8 +304,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try_archive: { if (!(try Archive.isArchive(file))) break :try_archive; try classified.append(.{ - .kind = .archive, - .origin = .{ .file = file }, + .kind = .{ .archive = file }, .name = full_path, }); continue; @@ -316,20 +313,18 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try_dylib: { if (!(try Dylib.isDylib(file))) break :try_dylib; try classified.append(.{ - .kind = .dylib, - .origin = .{ .file = file }, + .kind = .{ .dylib = file }, .name = full_path, }); continue; } try_stub: { - var lib_stub = Stub.LibStub.loadFromFile(self.allocator, file) catch { + var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { break :try_stub; }; try classified.append(.{ - .kind = .stub, - .origin = .{ .stub = lib_stub }, + .kind = .{ .stub = lib_stub }, .name = full_path, }); file.close(); @@ -343,53 +338,46 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { // Based on our classification, proceed with parsing. for (classified.items) |input| { switch (input.kind) { - .object => { + .object => |file| { const object = try self.allocator.create(Object); errdefer self.allocator.destroy(object); object.* = Object.init(self.allocator); object.arch = self.arch.?; object.name = input.name; - object.file = input.origin.file; + object.file = file; try object.parse(); try self.objects.append(self.allocator, object); }, - .archive => { + .archive => |file| { const archive = try self.allocator.create(Archive); errdefer self.allocator.destroy(archive); archive.* = Archive.init(self.allocator); archive.arch = self.arch.?; archive.name = input.name; - archive.file = input.origin.file; + archive.file = file; try archive.parse(); try self.archives.append(self.allocator, archive); }, - .dylib => { + .dylib, .stub => { const dylib = try self.allocator.create(Dylib); errdefer self.allocator.destroy(dylib); dylib.* = Dylib.init(self.allocator); dylib.arch = self.arch.?; dylib.name = input.name; - dylib.file = input.origin.file; - try dylib.parse(); - try self.dylibs.append(self.allocator, dylib); - }, - .stub => { - const stub = try self.allocator.create(Stub); - errdefer self.allocator.destroy(stub); - - stub.* = Stub.init(self.allocator); - stub.arch = self.arch.?; - stub.name = input.name; - stub.lib_stub = input.origin.stub; + if (input.kind == .dylib) { + dylib.file = input.kind.dylib; + try dylib.parse(); + } else { + try dylib.parseFromStub(input.kind.stub); + } - try stub.parse(); - try self.lib_stubs.append(self.allocator, stub); + try self.dylibs.append(self.allocator, dylib); }, } } @@ -399,50 +387,64 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { for (libs) |lib| { const file = try fs.cwd().openFile(lib, .{}); - if (try Dylib.isDylib(file)) { - const dylib = try self.allocator.create(Dylib); - errdefer self.allocator.destroy(dylib); + var kind: ?union(enum) { + archive, + dylib, + stub: LibStub, + } = kind: { + if (try Archive.isArchive(file)) break :kind .archive; + if (try Dylib.isDylib(file)) break :kind .dylib; + var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { + break :kind null; + }; + break :kind .{ .stub = lib_stub }; + }; + defer { + if (kind) |*kk| { + switch (kk.*) { + .stub => |*stub| { + stub.deinit(); + }, + else => {}, + } + } + } - dylib.* = Dylib.init(self.allocator); - dylib.arch = self.arch.?; - dylib.name = try self.allocator.dupe(u8, lib); - dylib.file = file; + const unwrapped = kind orelse { + file.close(); + log.warn("unknown filetype for a library: '{s}'", .{lib}); + continue; + }; + switch (unwrapped) { + .archive => { + const archive = try self.allocator.create(Archive); + errdefer self.allocator.destroy(archive); - try dylib.parse(); - try self.dylibs.append(self.allocator, dylib); - } else { - // Try tbd stub file next. - if (Stub.LibStub.loadFromFile(self.allocator, file)) |lib_stub| { - const stub = try self.allocator.create(Stub); - errdefer self.allocator.destroy(stub); - - stub.* = Stub.init(self.allocator); - stub.arch = self.arch.?; - stub.name = try self.allocator.dupe(u8, lib); - stub.lib_stub = lib_stub; - - try stub.parse(); - try self.lib_stubs.append(self.allocator, stub); - } else |_| { - // TODO this entire logic has to be cleaned up. - try file.seekTo(0); - - if (try Archive.isArchive(file)) { - 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, lib); - archive.file = file; - - try archive.parse(); - try self.archives.append(self.allocator, archive); + archive.* = Archive.init(self.allocator); + archive.arch = self.arch.?; + archive.name = try self.allocator.dupe(u8, lib); + archive.file = file; + + try archive.parse(); + try self.archives.append(self.allocator, archive); + }, + .dylib, .stub => { + const dylib = try self.allocator.create(Dylib); + errdefer self.allocator.destroy(dylib); + + dylib.* = Dylib.init(self.allocator); + dylib.arch = self.arch.?; + dylib.name = try self.allocator.dupe(u8, lib); + + if (unwrapped == .dylib) { + dylib.file = file; + try dylib.parse(); } else { - file.close(); - log.warn("unknown filetype for a library: '{s}'", .{lib}); + try dylib.parseFromStub(unwrapped.stub); } - } + + try self.dylibs.append(self.allocator, dylib); + }, } } } @@ -451,24 +453,24 @@ fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { const file = try fs.cwd().openFile(libc_stub_path, .{}); defer file.close(); - var lib_stub = try Stub.LibStub.loadFromFile(self.allocator, file); + var lib_stub = try LibStub.loadFromFile(self.allocator, file); + defer lib_stub.deinit(); - const stub = try self.allocator.create(Stub); - errdefer self.allocator.destroy(stub); + const dylib = try self.allocator.create(Dylib); + errdefer self.allocator.destroy(dylib); - stub.* = Stub.init(self.allocator); - stub.arch = self.arch.?; - stub.name = try self.allocator.dupe(u8, libc_stub_path); - stub.lib_stub = lib_stub; + dylib.* = Dylib.init(self.allocator); + dylib.arch = self.arch.?; + dylib.name = try self.allocator.dupe(u8, libc_stub_path); - try stub.parse(); + try dylib.parseFromStub(lib_stub); - self.libsystem_stub_index = @intCast(u16, self.lib_stubs.items.len); - try self.lib_stubs.append(self.allocator, stub); + self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); + try self.dylibs.append(self.allocator, dylib); // Add LC_LOAD_DYLIB load command. - stub.ordinal = self.next_dylib_ordinal; - const dylib_id = stub.id orelse unreachable; + dylib.ordinal = self.next_dylib_ordinal; + const dylib_id = dylib.id orelse unreachable; var dylib_cmd = try createLoadDylibCommand( self.allocator, dylib_id.name, @@ -1778,25 +1780,16 @@ fn resolveSymbols(self: *Zld) !void { } self.unresolved.clearRetainingCapacity(); - var referenced = std.AutoHashMap(union(enum) { - dylib: *Dylib, - stub: *Stub, - }, void).init(self.allocator); + var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator); defer referenced.deinit(); loop: while (unresolved.popOrNull()) |undef| { const proxy = self.imports.get(undef.name) orelse outer: { const proxy = inner: { - for (self.dylibs.items) |dylib| { + for (self.dylibs.items) |dylib, i| { const proxy = (try dylib.createProxy(undef.name)) orelse continue; - try referenced.put(.{ .dylib = dylib }, {}); - break :inner proxy; - } - for (self.lib_stubs.items) |stub, i| { - const proxy = (try stub.createProxy(undef.name)) orelse continue; - if (self.libsystem_stub_index.? != @intCast(u16, i)) { - // LibSystem gets its load command separately. - try referenced.put(.{ .stub = stub }, {}); + if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately. + try referenced.put(dylib, {}); } break :inner proxy; } @@ -1829,33 +1822,17 @@ fn resolveSymbols(self: *Zld) !void { // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. var it = referenced.iterator(); - while (it.next()) |key| { - var dylib_cmd = blk: { - switch (key.key_ptr.*) { - .dylib => |dylib| { - dylib.ordinal = self.next_dylib_ordinal; - const dylib_id = dylib.id orelse unreachable; - break :blk try createLoadDylibCommand( - self.allocator, - dylib_id.name, - dylib_id.timestamp, - dylib_id.current_version, - dylib_id.compatibility_version, - ); - }, - .stub => |stub| { - stub.ordinal = self.next_dylib_ordinal; - const dylib_id = stub.id orelse unreachable; - break :blk try createLoadDylibCommand( - self.allocator, - dylib_id.name, - dylib_id.timestamp, - dylib_id.current_version, - dylib_id.compatibility_version, - ); - }, - } - }; + while (it.next()) |entry| { + const dylib = entry.key_ptr.*; + dylib.ordinal = self.next_dylib_ordinal; + const dylib_id = dylib.id orelse unreachable; + var dylib_cmd = try createLoadDylibCommand( + self.allocator, + dylib_id.name, + dylib_id.timestamp, + dylib_id.current_version, + dylib_id.compatibility_version, + ); errdefer dylib_cmd.deinit(self.allocator); try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); self.next_dylib_ordinal += 1; @@ -1873,8 +1850,8 @@ fn resolveSymbols(self: *Zld) !void { } // Finally put dyld_stub_binder as an Import - const libsystem_stub = self.lib_stubs.items[self.libsystem_stub_index.?]; - const proxy = (try libsystem_stub.createProxy("dyld_stub_binder")) orelse { + const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?]; + const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse { log.err("undefined reference to symbol 'dyld_stub_binder'", .{}); return error.UndefinedSymbolReference; }; -- cgit v1.2.3 From 5ac5cd9de7c5387e37baa4f287d609c5d2f34564 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 23 Jun 2021 15:11:31 +0200 Subject: zld: naively parse all dylib deps in stubs --- src/link/MachO.zig | 1 + src/link/MachO/Archive.zig | 63 ++++++++----- src/link/MachO/Dylib.zig | 148 +++++++++++++++++++++++++---- src/link/MachO/Object.zig | 52 +++++++--- src/link/MachO/Zld.zig | 230 +++++++++++---------------------------------- src/link/tapi.zig | 3 +- 6 files changed, 263 insertions(+), 234 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3521874129..4ede9c516e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -789,6 +789,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { zld.deinit(); } zld.arch = target.cpu.arch; + zld.syslibroot = self.base.options.syslibroot; zld.stack_size = stack_size; // Positional arguments to the linker such as object files and static archives. diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 48bc33f0fa..22439d300a 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -8,12 +8,13 @@ const macho = std.macho; const mem = std.mem; const Allocator = mem.Allocator; +const Arch = std.Target.Cpu.Arch; const Object = @import("Object.zig"); usingnamespace @import("commands.zig"); allocator: *Allocator, -arch: ?std.Target.Cpu.Arch = null, +arch: ?Arch = null, file: ?fs.File = null, header: ?ar_hdr = null, name: ?[]const u8 = null, @@ -85,10 +86,36 @@ const ar_hdr = extern struct { } }; -pub fn init(allocator: *Allocator) Archive { - return .{ +pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?*Archive { + const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { + error.FileNotFound => return null, + else => |e| return e, + }; + errdefer file.close(); + + const archive = try allocator.create(Archive); + errdefer allocator.destroy(archive); + + const name = try allocator.dupe(u8, path); + errdefer allocator.free(name); + + archive.* = .{ .allocator = allocator, + .arch = arch, + .name = name, + .file = file, + }; + + archive.parse() catch |err| switch (err) { + error.EndOfStream, error.NotArchive => { + archive.deinit(); + allocator.destroy(archive); + return null; + }, + else => |e| return e, }; + + return archive; } pub fn deinit(self: *Archive) void { @@ -116,15 +143,15 @@ pub fn parse(self: *Archive) !void { const magic = try reader.readBytesNoEof(SARMAG); if (!mem.eql(u8, &magic, ARMAG)) { - log.err("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic }); - return error.MalformedArchive; + log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic }); + return error.NotArchive; } self.header = try reader.readStruct(ar_hdr); if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) { - log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag }); - return error.MalformedArchive; + log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag }); + return error.NotArchive; } var embedded_name = try parseName(self.allocator, self.header.?, reader); @@ -222,23 +249,15 @@ pub fn parseObject(self: Archive, offset: u32) !*Object { 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; - object.file_offset = @intCast(u32, try reader.context.getPos()); + object.* = .{ + .allocator = self.allocator, + .arch = self.arch.?, + .file = try fs.cwd().openFile(self.name.?, .{}), + .name = name, + .file_offset = @intCast(u32, try reader.context.getPos()), + }; try object.parse(); - try reader.context.seekTo(0); return object; } - -pub fn isArchive(file: fs.File) !bool { - const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch |err| switch (err) { - error.EndOfStream => return false, - else => |e| return e, - }; - try file.seekTo(0); - return mem.eql(u8, &magic, Archive.ARMAG); -} diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 44b012905e..723277c541 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -8,6 +8,7 @@ const macho = std.macho; const mem = std.mem; const Allocator = mem.Allocator; +const Arch = std.Target.Cpu.Arch; const Symbol = @import("Symbol.zig"); const LibStub = @import("../tapi.zig").LibStub; @@ -15,10 +16,11 @@ usingnamespace @import("commands.zig"); allocator: *Allocator, -arch: ?std.Target.Cpu.Arch = null, +arch: ?Arch = null, header: ?macho.mach_header_64 = null, file: ?fs.File = null, name: ?[]const u8 = null, +syslibroot: ?[]const u8 = null, ordinal: ?u16 = null, @@ -35,6 +37,11 @@ id: ?Id = null, /// a symbol is referenced by an object file. symbols: std.StringArrayHashMapUnmanaged(void) = .{}, +// TODO we should keep track of already parsed dylibs so that +// we don't unnecessarily reparse them again. +// TODO add dylib dep analysis and extraction for .dylib files. +dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, + pub const Id = struct { name: []const u8, timestamp: u32, @@ -46,8 +53,57 @@ pub const Id = struct { } }; -pub fn init(allocator: *Allocator) Dylib { - return .{ .allocator = allocator }; +pub const Error = error{ + OutOfMemory, + EmptyStubFile, + MismatchedCpuArchitecture, + UnsupportedCpuArchitecture, +} || fs.File.OpenError || std.os.PReadError; + +pub fn createAndParseFromPath( + allocator: *Allocator, + arch: Arch, + path: []const u8, + syslibroot: ?[]const u8, + recurse_libs: bool, +) Error!?*Dylib { + const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { + error.FileNotFound => return null, + else => |e| return e, + }; + errdefer file.close(); + + const dylib = try allocator.create(Dylib); + errdefer allocator.destroy(dylib); + + const name = try allocator.dupe(u8, path); + errdefer allocator.free(name); + + dylib.* = .{ + .allocator = allocator, + .arch = arch, + .name = name, + .file = file, + .syslibroot = syslibroot, + }; + + dylib.parse(recurse_libs) catch |err| switch (err) { + error.EndOfStream, error.NotDylib => { + try file.seekTo(0); + + var lib_stub = LibStub.loadFromFile(allocator, file) catch { + dylib.deinit(); + allocator.destroy(dylib); + return null; + }; + defer lib_stub.deinit(); + + try dylib.parseFromStub(lib_stub, recurse_libs); + }, + else => |e| return e, + }; + + return dylib; } pub fn deinit(self: *Dylib) void { @@ -60,6 +116,7 @@ pub fn deinit(self: *Dylib) void { self.allocator.free(key); } self.symbols.deinit(self.allocator); + self.dylibs.deinit(self.allocator); if (self.name) |name| { self.allocator.free(name); @@ -76,15 +133,15 @@ pub fn closeFile(self: Dylib) void { } } -pub fn parse(self: *Dylib) !void { +pub fn parse(self: *Dylib, recurse_libs: bool) !void { log.debug("parsing shared library '{s}'", .{self.name.?}); var reader = self.file.?.reader(); self.header = try reader.readStruct(macho.mach_header_64); if (self.header.?.filetype != macho.MH_DYLIB) { - log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype }); - return error.MalformedDylib; + log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype }); + return error.NotDylib; } const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { @@ -190,7 +247,7 @@ fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { } } -pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { +pub fn parseFromStub(self: *Dylib, lib_stub: LibStub, recurse_libs: bool) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; log.debug("parsing shared library from stub '{s}'", .{self.name.?}); @@ -236,9 +293,17 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { for (reexports) |reexp| { if (!hasTarget(reexp.targets, target_string)) continue; - for (reexp.symbols) |sym_name| { - if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); + if (reexp.symbols) |symbols| { + for (symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); + } + } + + if (reexp.objc_classes) |classes| { + for (classes) |sym_name| { + try self.addObjCClassSymbols(sym_name); + } } } } @@ -249,6 +314,60 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { } } } + + for (lib_stub.inner) |stub| { + if (!hasTarget(stub.targets, target_string)) continue; + + if (stub.reexported_libraries) |reexports| reexports: { + if (!recurse_libs) break :reexports; + + for (reexports) |reexp| { + if (!hasTarget(reexp.targets, target_string)) continue; + + outer: for (reexp.libraries) |lib| { + const dirname = fs.path.dirname(lib) orelse { + log.warn("unable to resolve dependency {s}", .{lib}); + continue; + }; + const filename = fs.path.basename(lib); + const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index| + filename[0..index] + else + filename; + + for (&[_][]const u8{ "dylib", "tbd" }) |ext| { + const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ + without_ext, + ext, + }); + defer self.allocator.free(with_ext); + + const lib_path = if (self.syslibroot) |syslibroot| + try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext }) + else + try fs.path.join(self.allocator, &.{ dirname, with_ext }); + + log.debug("trying dependency at fully resolved path {s}", .{lib_path}); + + const dylib = (try createAndParseFromPath( + self.allocator, + self.arch.?, + lib_path, + self.syslibroot, + true, + )) orelse { + continue; + }; + + try self.dylibs.append(self.allocator, dylib); + continue :outer; + } else { + log.warn("unable to resolve dependency {s}", .{lib}); + } + } + } + } + } } fn hasTarget(targets: []const []const u8, target: []const u8) bool { @@ -258,15 +377,6 @@ fn hasTarget(targets: []const []const u8, target: []const u8) bool { return false; } -pub fn isDylib(file: fs.File) !bool { - const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) { - error.EndOfStream => return false, - else => |e| return e, - }; - try file.seekTo(0); - return header.filetype == macho.MH_DYLIB; -} - pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol { if (!self.symbols.contains(sym_name)) return null; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 64db2fe091..747adaab87 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -11,6 +11,7 @@ const mem = std.mem; const reloc = @import("reloc.zig"); const Allocator = mem.Allocator; +const Arch = std.Target.Cpu.Arch; const Relocation = reloc.Relocation; const Symbol = @import("Symbol.zig"); const parseName = @import("Zld.zig").parseName; @@ -18,7 +19,7 @@ const parseName = @import("Zld.zig").parseName; usingnamespace @import("commands.zig"); allocator: *Allocator, -arch: ?std.Target.Cpu.Arch = null, +arch: ?Arch = null, header: ?macho.mach_header_64 = null, file: ?fs.File = null, file_offset: ?u32 = null, @@ -173,10 +174,36 @@ const DebugInfo = struct { } }; -pub fn init(allocator: *Allocator) Object { - return .{ +pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?*Object { + const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { + error.FileNotFound => return null, + else => |e| return e, + }; + errdefer file.close(); + + const object = try allocator.create(Object); + errdefer allocator.destroy(object); + + const name = try allocator.dupe(u8, path); + errdefer allocator.free(name); + + object.* = .{ .allocator = allocator, + .arch = arch, + .name = name, + .file = file, }; + + object.parse() catch |err| switch (err) { + error.EndOfStream, error.NotObject => { + object.deinit(); + allocator.destroy(object); + return null; + }, + else => |e| return e, + }; + + return object; } pub fn deinit(self: *Object) void { @@ -223,11 +250,15 @@ pub fn parse(self: *Object) !void { self.header = try reader.readStruct(macho.mach_header_64); if (self.header.?.filetype != macho.MH_OBJECT) { - log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_OBJECT, self.header.?.filetype }); - return error.MalformedObject; + log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ + macho.MH_OBJECT, + self.header.?.filetype, + }); + + return error.NotObject; } - const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { + const this_arch: Arch = switch (self.header.?.cputype) { macho.CPU_TYPE_ARM64 => .aarch64, macho.CPU_TYPE_X86_64 => .x86_64, else => |value| { @@ -533,12 +564,3 @@ pub fn parseDataInCode(self: *Object) !void { try self.data_in_code_entries.append(self.allocator, dice); } } - -pub fn isObject(file: fs.File) !bool { - const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) { - error.EndOfStream => return false, - else => |e| return e, - }; - try file.seekTo(0); - return header.filetype == macho.MH_OBJECT; -} diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index b46feced49..a4ee93cf58 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -16,7 +16,6 @@ const Allocator = mem.Allocator; const Archive = @import("Archive.zig"); const CodeSignature = @import("CodeSignature.zig"); const Dylib = @import("Dylib.zig"); -const LibStub = @import("../tapi.zig").LibStub; const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); const Trie = @import("Trie.zig"); @@ -33,6 +32,7 @@ out_path: ?[]const u8 = null, // TODO these args will become obselete once Zld is coalesced with incremental // linker. +syslibroot: ?[]const u8 = null, stack_size: u64 = 0, objects: std.ArrayListUnmanaged(*Object) = .{}, @@ -257,214 +257,90 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L } fn parseInputFiles(self: *Zld, files: []const []const u8) !void { - const Input = struct { - kind: union(enum) { - object: fs.File, - archive: fs.File, - dylib: fs.File, - stub: LibStub, - }, - name: []const u8, - - fn deinit(input: *@This()) void { - switch (input.kind) { - .stub => |*stub| { - stub.deinit(); - }, - else => {}, - } - } - }; - var classified = std.ArrayList(Input).init(self.allocator); - defer { - for (classified.items) |*input| { - input.deinit(); - } - classified.deinit(); - } - - // First, classify input files: object, archive, dylib or stub (tbd). for (files) |file_name| { - const file = try fs.cwd().openFile(file_name, .{}); const full_path = full_path: { var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; const path = try std.fs.realpath(file_name, &buffer); break :full_path try self.allocator.dupe(u8, path); }; - try_object: { - if (!(try Object.isObject(file))) break :try_object; - try classified.append(.{ - .kind = .{ .object = file }, - .name = full_path, - }); - continue; - } - - try_archive: { - if (!(try Archive.isArchive(file))) break :try_archive; - try classified.append(.{ - .kind = .{ .archive = file }, - .name = full_path, - }); + if (try Object.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |object| { + try self.objects.append(self.allocator, object); continue; } - try_dylib: { - if (!(try Dylib.isDylib(file))) break :try_dylib; - try classified.append(.{ - .kind = .{ .dylib = file }, - .name = full_path, - }); + if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |archive| { + try self.archives.append(self.allocator, archive); continue; } - try_stub: { - var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { - break :try_stub; - }; - try classified.append(.{ - .kind = .{ .stub = lib_stub }, - .name = full_path, - }); - file.close(); + if (try Dylib.createAndParseFromPath( + self.allocator, + self.arch.?, + full_path, + self.syslibroot, + true, + )) |dylib| { + try self.dylibs.append(self.allocator, dylib); continue; } - file.close(); log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); } - - // Based on our classification, proceed with parsing. - for (classified.items) |input| { - switch (input.kind) { - .object => |file| { - const object = try self.allocator.create(Object); - errdefer self.allocator.destroy(object); - - object.* = Object.init(self.allocator); - object.arch = self.arch.?; - object.name = input.name; - object.file = file; - - try object.parse(); - try self.objects.append(self.allocator, object); - }, - .archive => |file| { - const archive = try self.allocator.create(Archive); - errdefer self.allocator.destroy(archive); - - archive.* = Archive.init(self.allocator); - archive.arch = self.arch.?; - archive.name = input.name; - archive.file = file; - - try archive.parse(); - try self.archives.append(self.allocator, archive); - }, - .dylib, .stub => { - const dylib = try self.allocator.create(Dylib); - errdefer self.allocator.destroy(dylib); - - dylib.* = Dylib.init(self.allocator); - dylib.arch = self.arch.?; - dylib.name = input.name; - - if (input.kind == .dylib) { - dylib.file = input.kind.dylib; - try dylib.parse(); - } else { - try dylib.parseFromStub(input.kind.stub); - } - - try self.dylibs.append(self.allocator, dylib); - }, - } - } } fn parseLibs(self: *Zld, libs: []const []const u8) !void { - for (libs) |lib| { - const file = try fs.cwd().openFile(lib, .{}); - - var kind: ?union(enum) { - archive, - dylib, - stub: LibStub, - } = kind: { - if (try Archive.isArchive(file)) break :kind .archive; - if (try Dylib.isDylib(file)) break :kind .dylib; - var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { - break :kind null; - }; - break :kind .{ .stub = lib_stub }; - }; - defer { - if (kind) |*kk| { - switch (kk.*) { - .stub => |*stub| { - stub.deinit(); - }, - else => {}, - } + const DylibDeps = struct { + fn bubbleUp(out: *std.ArrayList(*Dylib), next: *Dylib) error{OutOfMemory}!void { + try out.ensureUnusedCapacity(next.dylibs.items.len); + for (next.dylibs.items) |dylib| { + out.appendAssumeCapacity(dylib); + } + for (next.dylibs.items) |dylib| { + try bubbleUp(out, dylib); } } + }; - const unwrapped = kind orelse { - file.close(); - log.warn("unknown filetype for a library: '{s}'", .{lib}); + for (libs) |lib| { + if (try Dylib.createAndParseFromPath( + self.allocator, + self.arch.?, + lib, + self.syslibroot, + true, + )) |dylib| { + try self.dylibs.append(self.allocator, dylib); continue; - }; - switch (unwrapped) { - .archive => { - 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, lib); - archive.file = file; - - try archive.parse(); - try self.archives.append(self.allocator, archive); - }, - .dylib, .stub => { - const dylib = try self.allocator.create(Dylib); - errdefer self.allocator.destroy(dylib); - - dylib.* = Dylib.init(self.allocator); - dylib.arch = self.arch.?; - dylib.name = try self.allocator.dupe(u8, lib); - - if (unwrapped == .dylib) { - dylib.file = file; - try dylib.parse(); - } else { - try dylib.parseFromStub(unwrapped.stub); - } - - try self.dylibs.append(self.allocator, dylib); - }, } - } -} -fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { - const file = try fs.cwd().openFile(libc_stub_path, .{}); - defer file.close(); + if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, lib)) |archive| { + try self.archives.append(self.allocator, archive); + continue; + } - var lib_stub = try LibStub.loadFromFile(self.allocator, file); - defer lib_stub.deinit(); + log.warn("unknown filetype for a library: '{s}'", .{lib}); + } - const dylib = try self.allocator.create(Dylib); - errdefer self.allocator.destroy(dylib); + // Flatten out any parsed dependencies. + var deps = std.ArrayList(*Dylib).init(self.allocator); + defer deps.deinit(); - dylib.* = Dylib.init(self.allocator); - dylib.arch = self.arch.?; - dylib.name = try self.allocator.dupe(u8, libc_stub_path); + for (self.dylibs.items) |dylib| { + try DylibDeps.bubbleUp(&deps, dylib); + } - try dylib.parseFromStub(lib_stub); + try self.dylibs.appendSlice(self.allocator, deps.toOwnedSlice()); +} +fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { + const dylib = (try Dylib.createAndParseFromPath( + self.allocator, + self.arch.?, + libc_stub_path, + self.syslibroot, + false, + )) orelse return error.FailedToParseLibSystem; self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); try self.dylibs.append(self.allocator, dylib); diff --git a/src/link/tapi.zig b/src/link/tapi.zig index 51d51d6ed3..dc696577e8 100644 --- a/src/link/tapi.zig +++ b/src/link/tapi.zig @@ -41,7 +41,8 @@ pub const LibStub = struct { }, reexports: ?[]const struct { targets: []const []const u8, - symbols: []const []const u8, + symbols: ?[]const []const u8, + objc_classes: ?[]const []const u8, }, allowable_clients: ?[]const struct { targets: []const []const u8, -- cgit v1.2.3 From 8669e3d46b24fcd6b6aa1053a16cf5301ed0e87a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Jun 2021 14:39:30 +0200 Subject: zld: when parsing dylibs, allow multiple return values --- src/link/MachO/Dylib.zig | 145 +++++++++++++++++++++++++---------------------- src/link/MachO/Zld.zig | 39 ++++--------- 2 files changed, 90 insertions(+), 94 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 723277c541..515df003b6 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -37,10 +37,7 @@ id: ?Id = null, /// a symbol is referenced by an object file. symbols: std.StringArrayHashMapUnmanaged(void) = .{}, -// TODO we should keep track of already parsed dylibs so that -// we don't unnecessarily reparse them again. -// TODO add dylib dep analysis and extraction for .dylib files. -dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, +dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{}, pub const Id = struct { name: []const u8, @@ -66,7 +63,7 @@ pub fn createAndParseFromPath( path: []const u8, syslibroot: ?[]const u8, recurse_libs: bool, -) Error!?*Dylib { +) Error!?[]*Dylib { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -87,7 +84,7 @@ pub fn createAndParseFromPath( .syslibroot = syslibroot, }; - dylib.parse(recurse_libs) catch |err| switch (err) { + dylib.parse() catch |err| switch (err) { error.EndOfStream, error.NotDylib => { try file.seekTo(0); @@ -98,12 +95,20 @@ pub fn createAndParseFromPath( }; defer lib_stub.deinit(); - try dylib.parseFromStub(lib_stub, recurse_libs); + try dylib.parseFromStub(lib_stub); }, else => |e| return e, }; - return dylib; + var dylibs = std.ArrayList(*Dylib).init(allocator); + defer dylibs.deinit(); + try dylibs.append(dylib); + + if (recurse_libs) { + try dylib.parseDependentLibs(&dylibs); + } + + return dylibs.toOwnedSlice(); } pub fn deinit(self: *Dylib) void { @@ -116,7 +121,11 @@ pub fn deinit(self: *Dylib) void { self.allocator.free(key); } self.symbols.deinit(self.allocator); - self.dylibs.deinit(self.allocator); + + for (self.dependent_libs.keys()) |key| { + self.allocator.free(key); + } + self.dependent_libs.deinit(self.allocator); if (self.name) |name| { self.allocator.free(name); @@ -133,7 +142,7 @@ pub fn closeFile(self: Dylib) void { } } -pub fn parse(self: *Dylib, recurse_libs: bool) !void { +pub fn parse(self: *Dylib) !void { log.debug("parsing shared library '{s}'", .{self.name.?}); var reader = self.file.?.reader(); @@ -235,6 +244,13 @@ fn parseSymbols(self: *Dylib) !void { } } +fn hasTarget(targets: []const []const u8, target: []const u8) bool { + for (targets) |t| { + if (mem.eql(u8, t, target)) return true; + } + return false; +} + fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { const expanded = &[_][]const u8{ try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), @@ -247,7 +263,7 @@ fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { } } -pub fn parseFromStub(self: *Dylib, lib_stub: LibStub, recurse_libs: bool) !void { +pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; log.debug("parsing shared library from stub '{s}'", .{self.name.?}); @@ -270,6 +286,17 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub, recurse_libs: bool) !void for (lib_stub.inner) |stub| { if (!hasTarget(stub.targets, target_string)) continue; + if (stub.reexported_libraries) |reexports| { + for (reexports) |reexp| { + if (!hasTarget(reexp.targets, target_string)) continue; + + try self.dependent_libs.ensureUnusedCapacity(self.allocator, reexp.libraries.len); + for (reexp.libraries) |lib| { + self.dependent_libs.putAssumeCapacity(try self.allocator.dupe(u8, lib), {}); + } + } + } + if (stub.exports) |exports| { for (exports) |exp| { if (!hasTarget(exp.targets, target_string)) continue; @@ -314,69 +341,53 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub, recurse_libs: bool) !void } } } +} - for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; - - if (stub.reexported_libraries) |reexports| reexports: { - if (!recurse_libs) break :reexports; +pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { + outer: for (self.dependent_libs.keys()) |lib| { + const dirname = fs.path.dirname(lib) orelse { + log.warn("unable to resolve dependency {s}", .{lib}); + continue; + }; + const filename = fs.path.basename(lib); + const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index| + filename[0..index] + else + filename; + + for (&[_][]const u8{ "dylib", "tbd" }) |ext| { + const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ + without_ext, + ext, + }); + defer self.allocator.free(with_ext); + + const lib_path = if (self.syslibroot) |syslibroot| + try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext }) + else + try fs.path.join(self.allocator, &.{ dirname, with_ext }); + + log.debug("trying dependency at fully resolved path {s}", .{lib_path}); + + const dylibs = (try createAndParseFromPath( + self.allocator, + self.arch.?, + lib_path, + self.syslibroot, + true, + )) orelse { + continue; + }; - for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + try out.appendSlice(dylibs); - outer: for (reexp.libraries) |lib| { - const dirname = fs.path.dirname(lib) orelse { - log.warn("unable to resolve dependency {s}", .{lib}); - continue; - }; - const filename = fs.path.basename(lib); - const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index| - filename[0..index] - else - filename; - - for (&[_][]const u8{ "dylib", "tbd" }) |ext| { - const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ - without_ext, - ext, - }); - defer self.allocator.free(with_ext); - - const lib_path = if (self.syslibroot) |syslibroot| - try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext }) - else - try fs.path.join(self.allocator, &.{ dirname, with_ext }); - - log.debug("trying dependency at fully resolved path {s}", .{lib_path}); - - const dylib = (try createAndParseFromPath( - self.allocator, - self.arch.?, - lib_path, - self.syslibroot, - true, - )) orelse { - continue; - }; - - try self.dylibs.append(self.allocator, dylib); - continue :outer; - } else { - log.warn("unable to resolve dependency {s}", .{lib}); - } - } - } + continue :outer; + } else { + log.warn("unable to resolve dependency {s}", .{lib}); } } } -fn hasTarget(targets: []const []const u8, target: []const u8) bool { - for (targets) |t| { - if (mem.eql(u8, t, target)) return true; - } - return false; -} - pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol { if (!self.symbols.contains(sym_name)) return null; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index a4ee93cf58..a0c60f1b07 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -280,8 +280,9 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { full_path, self.syslibroot, true, - )) |dylib| { - try self.dylibs.append(self.allocator, dylib); + )) |dylibs| { + defer self.allocator.free(dylibs); + try self.dylibs.appendSlice(self.allocator, dylibs); continue; } @@ -290,18 +291,6 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } fn parseLibs(self: *Zld, libs: []const []const u8) !void { - const DylibDeps = struct { - fn bubbleUp(out: *std.ArrayList(*Dylib), next: *Dylib) error{OutOfMemory}!void { - try out.ensureUnusedCapacity(next.dylibs.items.len); - for (next.dylibs.items) |dylib| { - out.appendAssumeCapacity(dylib); - } - for (next.dylibs.items) |dylib| { - try bubbleUp(out, dylib); - } - } - }; - for (libs) |lib| { if (try Dylib.createAndParseFromPath( self.allocator, @@ -309,8 +298,9 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { lib, self.syslibroot, true, - )) |dylib| { - try self.dylibs.append(self.allocator, dylib); + )) |dylibs| { + defer self.allocator.free(dylibs); + try self.dylibs.appendSlice(self.allocator, dylibs); continue; } @@ -321,26 +311,21 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { log.warn("unknown filetype for a library: '{s}'", .{lib}); } - - // Flatten out any parsed dependencies. - var deps = std.ArrayList(*Dylib).init(self.allocator); - defer deps.deinit(); - - for (self.dylibs.items) |dylib| { - try DylibDeps.bubbleUp(&deps, dylib); - } - - try self.dylibs.appendSlice(self.allocator, deps.toOwnedSlice()); } fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { - const dylib = (try Dylib.createAndParseFromPath( + const dylibs = (try Dylib.createAndParseFromPath( self.allocator, self.arch.?, libc_stub_path, self.syslibroot, false, )) orelse return error.FailedToParseLibSystem; + defer self.allocator.free(dylibs); + + assert(dylibs.len == 1); // More than one dylib output from parsing libSystem! + const dylib = dylibs[0]; + self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); try self.dylibs.append(self.allocator, dylib); -- cgit v1.2.3 From 5e0e7b2cb4b568e244a4b0b4eb51d8faeb3fb226 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Jun 2021 18:43:38 +0200 Subject: zld: exclude libs part of the umbrella when parsing the umbrella lib --- src/link/MachO/Dylib.zig | 50 ++++++++++++++++++++++++++++++++---------------- src/link/MachO/Zld.zig | 3 --- 2 files changed, 34 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 515df003b6..56ad7107d5 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -62,7 +62,6 @@ pub fn createAndParseFromPath( arch: Arch, path: []const u8, syslibroot: ?[]const u8, - recurse_libs: bool, ) Error!?[]*Dylib { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, @@ -102,11 +101,9 @@ pub fn createAndParseFromPath( var dylibs = std.ArrayList(*Dylib).init(allocator); defer dylibs.deinit(); - try dylibs.append(dylib); - if (recurse_libs) { - try dylib.parseDependentLibs(&dylibs); - } + try dylibs.append(dylib); + try dylib.parseDependentLibs(&dylibs); return dylibs.toOwnedSlice(); } @@ -283,18 +280,17 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { else => unreachable, }; - for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; + var umbrella_libs = std.StringHashMap(void).init(self.allocator); + defer umbrella_libs.deinit(); - if (stub.reexported_libraries) |reexports| { - for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + for (lib_stub.inner) |stub, stub_index| { + if (!hasTarget(stub.targets, target_string)) continue; - try self.dependent_libs.ensureUnusedCapacity(self.allocator, reexp.libraries.len); - for (reexp.libraries) |lib| { - self.dependent_libs.putAssumeCapacity(try self.allocator.dupe(u8, lib), {}); - } - } + if (stub_index > 0) { + // TODO I thought that we could switch on presence of `parent-umbrella` map; + // however, turns out `libsystem_notify.dylib` is fully reexported by `libSystem.dylib` + // BUT does not feature a `parent-umbrella` map as the only sublib. Apple's bug perhaps? + try umbrella_libs.put(stub.install_name, .{}); } if (stub.exports) |exports| { @@ -341,6 +337,29 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { } } } + + log.debug("{s}", .{umbrella_lib.install_name}); + + // TODO track which libs were already parsed in different steps + for (lib_stub.inner) |stub| { + if (!hasTarget(stub.targets, target_string)) continue; + + if (stub.reexported_libraries) |reexports| { + for (reexports) |reexp| { + if (!hasTarget(reexp.targets, target_string)) continue; + + for (reexp.libraries) |lib| { + if (umbrella_libs.contains(lib)) { + log.debug(" | {s} <= {s}", .{ lib, umbrella_lib.install_name }); + continue; + } + + log.debug(" | {s}", .{lib}); + try self.dependent_libs.put(self.allocator, try self.allocator.dupe(u8, lib), {}); + } + } + } + } } pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { @@ -374,7 +393,6 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { self.arch.?, lib_path, self.syslibroot, - true, )) orelse { continue; }; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index a0c60f1b07..8fc40decd7 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -279,7 +279,6 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { self.arch.?, full_path, self.syslibroot, - true, )) |dylibs| { defer self.allocator.free(dylibs); try self.dylibs.appendSlice(self.allocator, dylibs); @@ -297,7 +296,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { self.arch.?, lib, self.syslibroot, - true, )) |dylibs| { defer self.allocator.free(dylibs); try self.dylibs.appendSlice(self.allocator, dylibs); @@ -319,7 +317,6 @@ fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { self.arch.?, libc_stub_path, self.syslibroot, - false, )) orelse return error.FailedToParseLibSystem; defer self.allocator.free(dylibs); -- cgit v1.2.3 From ad0be7857745613f53d3902c86d54401bb72696c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Jun 2021 22:19:44 +0200 Subject: zld: parse dylib's id from tbd --- src/link/MachO/Dylib.zig | 91 ++++++++++++++++++++++++++++++++++++++++-------- src/link/tapi.zig | 5 +++ 2 files changed, 82 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 56ad7107d5..737c83f2f9 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -3,8 +3,10 @@ const Dylib = @This(); const std = @import("std"); const assert = std.debug.assert; const fs = std.fs; +const fmt = std.fmt; const log = std.log.scoped(.dylib); const macho = std.macho; +const math = std.math; const mem = std.mem; const Allocator = mem.Allocator; @@ -37,6 +39,7 @@ id: ?Id = null, /// a symbol is referenced by an object file. symbols: std.StringArrayHashMapUnmanaged(void) = .{}, +// TODO add parsing re-exported libs from binary dylibs dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{}, pub const Id = struct { @@ -45,9 +48,72 @@ pub const Id = struct { current_version: u32, compatibility_version: u32, + pub fn default(name: []const u8) Id { + return .{ + .name = name, + .timestamp = 2, + .current_version = 0x10000, + .compatibility_version = 0x10000, + }; + } + pub fn deinit(id: *Id, allocator: *Allocator) void { allocator.free(id.name); } + + const ParseError = fmt.ParseIntError || fmt.BufPrintError; + + pub fn parseCurrentVersion(id: *Id, version: anytype) ParseError!void { + id.current_version = try parseVersion(version); + } + + pub fn parseCompatibilityVersion(id: *Id, version: anytype) ParseError!void { + id.compatibility_version = try parseVersion(version); + } + + fn parseVersion(version: anytype) ParseError!u32 { + const string = blk: { + switch (version) { + .int => |int| { + var out: u32 = 0; + const major = try math.cast(u16, int); + out += @intCast(u32, major) << 16; + return out; + }, + .float => |float| { + var buf: [256]u8 = undefined; + break :blk try fmt.bufPrint(&buf, "{d:.2}", .{float}); + }, + .string => |string| { + break :blk string; + }, + } + }; + + var out: u32 = 0; + var values: [3][]const u8 = undefined; + + var split = mem.split(string, "."); + var i: u4 = 0; + while (split.next()) |value| { + if (i > 2) { + log.warn("malformed version field: {s}", .{string}); + return 0x10000; + } + values[i] = value; + i += 1; + } + + if (values.len > 2) { + out += try fmt.parseInt(u8, values[2], 10); + } + if (values.len > 1) { + out += @intCast(u32, try fmt.parseInt(u8, values[1], 10)) << 8; + } + out += @intCast(u32, try fmt.parseInt(u16, values[0], 10)) << 16; + + return out; + } }; pub const Error = error{ @@ -55,7 +121,7 @@ pub const Error = error{ EmptyStubFile, MismatchedCpuArchitecture, UnsupportedCpuArchitecture, -} || fs.File.OpenError || std.os.PReadError; +} || fs.File.OpenError || std.os.PReadError || Id.ParseError; pub fn createAndParseFromPath( allocator: *Allocator, @@ -195,12 +261,7 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { fn parseId(self: *Dylib) !void { const index = self.id_cmd_index orelse { log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); - self.id = .{ - .name = try self.allocator.dupe(u8, self.name.?), - .timestamp = 2, - .current_version = 0, - .compatibility_version = 0, - }; + self.id = Id.default(try self.allocator.dupe(u8, self.name.?)); return; }; const id_cmd = self.load_commands.items[index].Dylib; @@ -266,13 +327,15 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { log.debug("parsing shared library from stub '{s}'", .{self.name.?}); const umbrella_lib = lib_stub.inner[0]; - self.id = .{ - .name = try self.allocator.dupe(u8, umbrella_lib.install_name), - // TODO parse from the stub - .timestamp = 2, - .current_version = 0, - .compatibility_version = 0, - }; + + var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name)); + if (umbrella_lib.current_version) |version| { + try id.parseCurrentVersion(version); + } + if (umbrella_lib.compatibility_version) |version| { + try id.parseCompatibilityVersion(version); + } + self.id = id; const target_string: []const u8 = switch (self.arch.?) { .aarch64 => "arm64-macos", diff --git a/src/link/tapi.zig b/src/link/tapi.zig index dc696577e8..35193b0eec 100644 --- a/src/link/tapi.zig +++ b/src/link/tapi.zig @@ -26,6 +26,11 @@ pub const LibStub = struct { float: f64, int: u64, }, + compatibility_version: ?union(enum) { + string: []const u8, + float: f64, + int: u64, + }, reexported_libraries: ?[]const struct { targets: []const []const u8, libraries: []const []const u8, -- cgit v1.2.3 From 8fc5b5a0874f16a16dd16206e35157cefba5edc5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Jun 2021 22:40:19 +0200 Subject: zld: put DICE and CodeSig load commands last after `LC_LOAD_DYLIB` commands to match ld64 and make the binaries compatible with Apple tools. --- src/link/MachO/Zld.zig | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 8fc40decd7..f391899159 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -238,7 +238,6 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L }); try self.populateMetadata(); - try self.addRpaths(args.rpaths); try self.parseInputFiles(files); try self.parseLibs(args.libs); try self.parseLibSystem(args.libc_stub_path); @@ -246,6 +245,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L try self.resolveStubsAndGotEntries(); try self.updateMetadata(); try self.sortSections(); + try self.addRpaths(args.rpaths); + try self.addDataInCodeLC(); + try self.addCodeSignatureLC(); try self.allocateTextSegment(); try self.allocateDataConstSegment(); try self.allocateDataSegment(); @@ -2231,24 +2233,28 @@ fn populateMetadata(self: *Zld) !void { std.crypto.random.bytes(&uuid_cmd.uuid); try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd }); } +} - if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) { - self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); +fn addDataInCodeLC(self: *Zld) !void { + if (self.data_in_code_cmd_index == null) { + self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.allocator, .{ .LinkeditData = .{ - .cmd = macho.LC_CODE_SIGNATURE, + .cmd = macho.LC_DATA_IN_CODE, .cmdsize = @sizeOf(macho.linkedit_data_command), .dataoff = 0, .datasize = 0, }, }); } +} - if (self.data_in_code_cmd_index == null and self.arch.? == .x86_64) { - self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len); +fn addCodeSignatureLC(self: *Zld) !void { + if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) { + self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.allocator, .{ .LinkeditData = .{ - .cmd = macho.LC_DATA_IN_CODE, + .cmd = macho.LC_CODE_SIGNATURE, .cmdsize = @sizeOf(macho.linkedit_data_command), .dataoff = 0, .datasize = 0, @@ -2344,9 +2350,7 @@ fn flush(self: *Zld) !void { try self.writeBindInfoTable(); try self.writeLazyBindInfoTable(); try self.writeExportInfo(); - if (self.arch.? == .x86_64) { - try self.writeDataInCode(); - } + try self.writeDataInCode(); { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; -- cgit v1.2.3 From 264f5f4544fc87330d328d1aea96a27934738368 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Jun 2021 23:08:24 +0200 Subject: Apply AST fixes --- src/link/MachO/Symbol.zig | 10 +--------- src/link/tapi/parse.zig | 7 +------ 2 files changed, 2 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 0c6777009d..c58af27672 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -73,8 +73,6 @@ pub const Regular = struct { global, }; - pub fn deinit(regular: *Regular, allocator: *Allocator) void {} - 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"); @@ -116,8 +114,6 @@ pub const Unresolved = struct { file: *Object, pub const base_type: Symbol.Type = .unresolved; - - pub fn deinit(unresolved: *Unresolved, allocator: *Allocator) void {} }; pub const Tentative = struct { @@ -133,17 +129,13 @@ pub const Tentative = struct { file: *Object, pub const base_type: Symbol.Type = .tentative; - - pub fn deinit(tentative: *Tentative, allocator: *Allocator) void {} }; pub fn deinit(base: *Symbol, allocator: *Allocator) void { allocator.free(base.name); switch (base.@"type") { - .regular => @fieldParentPtr(Regular, "base", base).deinit(allocator), .proxy => @fieldParentPtr(Proxy, "base", base).deinit(allocator), - .unresolved => @fieldParentPtr(Unresolved, "base", base).deinit(allocator), - .tentative => @fieldParentPtr(Tentative, "base", base).deinit(allocator), + else => {}, } } diff --git a/src/link/tapi/parse.zig b/src/link/tapi/parse.zig index 1e40ac63dc..0c923f961b 100644 --- a/src/link/tapi/parse.zig +++ b/src/link/tapi/parse.zig @@ -42,7 +42,7 @@ pub const Node = struct { .doc => @fieldParentPtr(Node.Doc, "base", self).deinit(allocator), .map => @fieldParentPtr(Node.Map, "base", self).deinit(allocator), .list => @fieldParentPtr(Node.List, "base", self).deinit(allocator), - .value => @fieldParentPtr(Node.Value, "base", self).deinit(allocator), + .value => {}, } } @@ -180,11 +180,6 @@ pub const Node = struct { pub const base_tag: Node.Tag = .value; - pub fn deinit(self: *Value, allocator: *Allocator) void { - _ = self; - _ = allocator; - } - pub fn format( self: *const Value, comptime fmt: []const u8, -- cgit v1.2.3 From ddd2cd73307c06906a8d120b41fd5ab8864797a1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Jun 2021 23:34:22 +0200 Subject: zld: fix Dylib.Id parsing logic for string values --- src/link/MachO/Dylib.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 737c83f2f9..e91ff30ad2 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -94,20 +94,20 @@ pub const Id = struct { var values: [3][]const u8 = undefined; var split = mem.split(string, "."); - var i: u4 = 0; + var count: u4 = 0; while (split.next()) |value| { - if (i > 2) { + if (count > 2) { log.warn("malformed version field: {s}", .{string}); return 0x10000; } - values[i] = value; - i += 1; + values[count] = value; + count += 1; } - if (values.len > 2) { + if (count > 2) { out += try fmt.parseInt(u8, values[2], 10); } - if (values.len > 1) { + if (count > 1) { out += @intCast(u32, try fmt.parseInt(u8, values[1], 10)) << 8; } out += @intCast(u32, try fmt.parseInt(u16, values[0], 10)) << 16; -- cgit v1.2.3