mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
Merge remote-tracking branch 'origin/master' into stage2-whole-file-astgen
I want the updated Drone CI stuff to get the CI green.
This commit is contained in:
commit
a9cd9b021b
@ -567,6 +567,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
|
||||
"${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/Symbol.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
|
||||
|
||||
@ -7,28 +7,28 @@ platform:
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
image: ziglang/static-base:llvm12-aarch64-2
|
||||
commands:
|
||||
- ./ci/drone/linux_script_build
|
||||
|
||||
- name: test-1
|
||||
depends_on:
|
||||
- build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
image: ziglang/static-base:llvm12-aarch64-2
|
||||
commands:
|
||||
- ./ci/drone/linux_script_test 1
|
||||
|
||||
- name: test-2
|
||||
depends_on:
|
||||
- build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
image: ziglang/static-base:llvm12-aarch64-2
|
||||
commands:
|
||||
- ./ci/drone/linux_script_test 2
|
||||
|
||||
- name: test-3
|
||||
depends_on:
|
||||
- build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
image: ziglang/static-base:llvm12-aarch64-2
|
||||
commands:
|
||||
- ./ci/drone/linux_script_test 3
|
||||
|
||||
@ -38,7 +38,7 @@ steps:
|
||||
- test-1
|
||||
- test-2
|
||||
- test-3
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
image: ziglang/static-base:llvm12-aarch64-2
|
||||
environment:
|
||||
SRHT_OAUTH_TOKEN:
|
||||
from_secret: SRHT_OAUTH_TOKEN
|
||||
|
||||
@ -71,6 +71,38 @@ pub const source_version_command = extern struct {
|
||||
version: u64,
|
||||
};
|
||||
|
||||
/// The build_version_command contains the min OS version on which this
|
||||
/// binary was built to run for its platform. The list of known platforms and
|
||||
/// tool values following it.
|
||||
pub const build_version_command = extern struct {
|
||||
/// LC_BUILD_VERSION
|
||||
cmd: u32,
|
||||
|
||||
/// sizeof(struct build_version_command) plus
|
||||
/// ntools * sizeof(struct build_version_command)
|
||||
cmdsize: u32,
|
||||
|
||||
/// platform
|
||||
platform: u32,
|
||||
|
||||
/// X.Y.Z is encoded in nibbles xxxx.yy.zz
|
||||
minos: u32,
|
||||
|
||||
/// X.Y.Z is encoded in nibbles xxxx.yy.zz
|
||||
sdk: u32,
|
||||
|
||||
/// number of tool entries following this
|
||||
ntools: u32,
|
||||
};
|
||||
|
||||
pub const build_tool_version = extern struct {
|
||||
/// enum for the tool
|
||||
tool: u32,
|
||||
|
||||
/// version number of the tool
|
||||
version: u32,
|
||||
};
|
||||
|
||||
/// The entry_point_command is a replacement for thread_command.
|
||||
/// It is used for main executables to specify the location (file offset)
|
||||
/// of main(). If -stack_size was used at link time, the stacksize
|
||||
@ -484,6 +516,19 @@ pub const dylib = extern struct {
|
||||
compatibility_version: u32,
|
||||
};
|
||||
|
||||
/// The rpath_command contains a path which at runtime should be added to the current
|
||||
/// run path used to find @rpath prefixed dylibs.
|
||||
pub const rpath_command = extern struct {
|
||||
/// LC_RPATH
|
||||
cmd: u32,
|
||||
|
||||
/// includes string
|
||||
cmdsize: u32,
|
||||
|
||||
/// path to add to run path
|
||||
path: u32,
|
||||
};
|
||||
|
||||
/// The segment load command indicates that a part of this file is to be
|
||||
/// mapped into the task's address space. The size of this segment in memory,
|
||||
/// vmsize, maybe equal to or larger than the amount to map from this file,
|
||||
|
||||
@ -548,7 +548,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
|
||||
const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
|
||||
const target = self.base.options.target;
|
||||
const stack_size = self.base.options.stack_size_override orelse 16777216;
|
||||
const stack_size = self.base.options.stack_size_override orelse 0;
|
||||
const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
|
||||
|
||||
const id_symlink_basename = "lld.id";
|
||||
@ -675,41 +675,168 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
zld.deinit();
|
||||
}
|
||||
zld.arch = target.cpu.arch;
|
||||
zld.stack_size = stack_size;
|
||||
|
||||
// Positional arguments to the linker such as object files and static archives.
|
||||
var positionals = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
try positionals.appendSlice(self.base.options.objects);
|
||||
|
||||
var input_files = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer input_files.deinit();
|
||||
// Positional arguments to the linker such as object files.
|
||||
try input_files.appendSlice(self.base.options.objects);
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try input_files.append(entry.key.status.success.object_path);
|
||||
try positionals.append(entry.key.status.success.object_path);
|
||||
}
|
||||
|
||||
if (module_obj_path) |p| {
|
||||
try input_files.append(p);
|
||||
try positionals.append(p);
|
||||
}
|
||||
try input_files.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
|
||||
try positionals.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
|
||||
// libc++ dep
|
||||
if (self.base.options.link_libcpp) {
|
||||
try input_files.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try input_files.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try positionals.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// Shared libraries.
|
||||
var shared_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.items();
|
||||
for (system_libs) |entry| {
|
||||
const link_lib = entry.key;
|
||||
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
||||
// (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
|
||||
// case we want to avoid prepending "-l".
|
||||
// TODO I think they should go as an input file instead of via shared_libs.
|
||||
if (Compilation.classifyFileExt(link_lib) == .shared_library) {
|
||||
try shared_libs.append(link_lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for (search_lib_names.items) |l_name| {
|
||||
// TODO text-based API, or .tbd files.
|
||||
const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.dylib", .{l_name});
|
||||
|
||||
var found = false;
|
||||
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 dylib file exists.
|
||||
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer tmp.close();
|
||||
|
||||
try shared_libs.append(full_path);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rpaths
|
||||
var rpath_table = std.StringArrayHashMap(void).init(arena);
|
||||
for (self.base.options.rpath_list) |rpath| {
|
||||
if (rpath_table.contains(rpath)) continue;
|
||||
try rpath_table.putNoClobber(rpath, {});
|
||||
}
|
||||
|
||||
var rpaths = std.ArrayList([]const u8).init(arena);
|
||||
try rpaths.ensureCapacity(rpath_table.count());
|
||||
for (rpath_table.items()) |entry| {
|
||||
rpaths.appendAssumeCapacity(entry.key);
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
var argv = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
try argv.append("zig");
|
||||
try argv.append("ld");
|
||||
|
||||
try argv.appendSlice(input_files.items);
|
||||
if (self.base.options.syslibroot) |syslibroot| {
|
||||
try argv.append("-syslibroot");
|
||||
try argv.append(syslibroot);
|
||||
}
|
||||
|
||||
for (rpaths.items) |rpath| {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(rpath);
|
||||
}
|
||||
|
||||
try argv.appendSlice(positionals.items);
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
for (search_lib_names.items) |l_name| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
|
||||
}
|
||||
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
try zld.link(input_files.items, full_out_path, .{
|
||||
.stack_size = self.base.options.stack_size_override,
|
||||
try zld.link(positionals.items, full_out_path, .{
|
||||
.shared_libs = shared_libs.items,
|
||||
.rpaths = rpaths.items,
|
||||
});
|
||||
|
||||
break :outer;
|
||||
@ -2044,28 +2171,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
}
|
||||
if (self.libsystem_cmd_index == null) {
|
||||
self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
|
||||
u64,
|
||||
@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
|
||||
@sizeOf(u64),
|
||||
));
|
||||
// TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
|
||||
// In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
|
||||
const min_version = 0x0;
|
||||
var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
|
||||
.cmd = macho.LC_LOAD_DYLIB,
|
||||
.cmdsize = cmdsize,
|
||||
.dylib = .{
|
||||
.name = @sizeOf(macho.dylib_command),
|
||||
.timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
|
||||
.current_version = min_version,
|
||||
.compatibility_version = min_version,
|
||||
},
|
||||
});
|
||||
dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
|
||||
mem.set(u8, dylib_cmd.data, 0);
|
||||
mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
|
||||
|
||||
var dylib_cmd = try createLoadDylibCommand(self.base.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0);
|
||||
errdefer dylib_cmd.deinit(self.base.allocator);
|
||||
|
||||
try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
|
||||
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
|
||||
185
src/link/MachO/Dylib.zig
Normal file
185
src/link/MachO/Dylib.zig
Normal file
@ -0,0 +1,185 @@
|
||||
const Dylib = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.dylib);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Symbol = @import("Symbol.zig");
|
||||
|
||||
usingnamespace @import("commands.zig");
|
||||
|
||||
allocator: *Allocator,
|
||||
arch: ?std.Target.Cpu.Arch = null,
|
||||
header: ?macho.mach_header_64 = null,
|
||||
file: ?fs.File = null,
|
||||
name: ?[]const u8 = null,
|
||||
|
||||
ordinal: ?u16 = null,
|
||||
|
||||
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
|
||||
|
||||
symtab_cmd_index: ?u16 = null,
|
||||
dysymtab_cmd_index: ?u16 = null,
|
||||
id_cmd_index: ?u16 = null,
|
||||
|
||||
id: ?Id = null,
|
||||
|
||||
symbols: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
|
||||
|
||||
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) Dylib {
|
||||
return .{ .allocator = allocator };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Dylib) void {
|
||||
for (self.load_commands.items) |*lc| {
|
||||
lc.deinit(self.allocator);
|
||||
}
|
||||
self.load_commands.deinit(self.allocator);
|
||||
|
||||
for (self.symbols.items()) |entry| {
|
||||
entry.value.deinit(self.allocator);
|
||||
self.allocator.destroy(entry.value);
|
||||
}
|
||||
self.symbols.deinit(self.allocator);
|
||||
|
||||
if (self.name) |name| {
|
||||
self.allocator.free(name);
|
||||
}
|
||||
|
||||
if (self.id) |*id| {
|
||||
id.deinit(self.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn closeFile(self: Dylib) void {
|
||||
if (self.file) |file| {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(self: *Dylib) !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;
|
||||
}
|
||||
|
||||
const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
|
||||
macho.CPU_TYPE_ARM64 => .aarch64,
|
||||
macho.CPU_TYPE_X86_64 => .x86_64,
|
||||
else => |value| {
|
||||
log.err("unsupported cpu architecture 0x{x}", .{value});
|
||||
return error.UnsupportedCpuArchitecture;
|
||||
},
|
||||
};
|
||||
if (this_arch != self.arch.?) {
|
||||
log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch });
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
try self.readLoadCommands(reader);
|
||||
try self.parseId();
|
||||
try self.parseSymbols();
|
||||
}
|
||||
|
||||
pub fn readLoadCommands(self: *Dylib, reader: anytype) !void {
|
||||
try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
|
||||
|
||||
var i: u16 = 0;
|
||||
while (i < self.header.?.ncmds) : (i += 1) {
|
||||
var cmd = try LoadCommand.read(self.allocator, reader);
|
||||
switch (cmd.cmd()) {
|
||||
macho.LC_SYMTAB => {
|
||||
self.symtab_cmd_index = i;
|
||||
},
|
||||
macho.LC_DYSYMTAB => {
|
||||
self.dysymtab_cmd_index = i;
|
||||
},
|
||||
macho.LC_ID_DYLIB => {
|
||||
self.id_cmd_index = i;
|
||||
},
|
||||
else => {
|
||||
log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
|
||||
},
|
||||
}
|
||||
self.load_commands.appendAssumeCapacity(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
pub 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,
|
||||
};
|
||||
return;
|
||||
};
|
||||
const id_cmd = self.load_commands.items[index].Dylib;
|
||||
const dylib = id_cmd.inner.dylib;
|
||||
|
||||
// TODO should we compare the name from the dylib's id with the user-specified one?
|
||||
const dylib_name = @ptrCast([*:0]const u8, id_cmd.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
|
||||
const name = try self.allocator.dupe(u8, mem.spanZ(dylib_name));
|
||||
|
||||
self.id = .{
|
||||
.name = name,
|
||||
.timestamp = dylib.timestamp,
|
||||
.current_version = dylib.current_version,
|
||||
.compatibility_version = dylib.compatibility_version,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parseSymbols(self: *Dylib) !void {
|
||||
const index = self.symtab_cmd_index orelse return;
|
||||
const symtab_cmd = self.load_commands.items[index].Symtab;
|
||||
|
||||
var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
|
||||
defer self.allocator.free(symtab);
|
||||
_ = try self.file.?.preadAll(symtab, symtab_cmd.symoff);
|
||||
const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
|
||||
|
||||
var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize);
|
||||
defer self.allocator.free(strtab);
|
||||
_ = try self.file.?.preadAll(strtab, symtab_cmd.stroff);
|
||||
|
||||
for (slice) |sym| {
|
||||
const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx));
|
||||
|
||||
if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue;
|
||||
|
||||
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,
|
||||
},
|
||||
.dylib = self,
|
||||
};
|
||||
|
||||
try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Dylib = @import("Dylib.zig");
|
||||
const Object = @import("Object.zig");
|
||||
|
||||
pub const Type = enum {
|
||||
@ -43,7 +44,7 @@ pub const Regular = struct {
|
||||
/// Whether the symbol is a weak ref.
|
||||
weak_ref: bool,
|
||||
|
||||
/// File where to locate this symbol.
|
||||
/// Object file where to locate this symbol.
|
||||
file: *Object,
|
||||
|
||||
/// Debug stab if defined.
|
||||
@ -78,8 +79,8 @@ pub const Regular = struct {
|
||||
pub const Proxy = struct {
|
||||
base: Symbol,
|
||||
|
||||
/// Dylib ordinal.
|
||||
dylib: u16,
|
||||
/// Dylib where to locate this symbol.
|
||||
dylib: ?*Dylib = null,
|
||||
|
||||
pub const base_type: Symbol.Type = .proxy;
|
||||
};
|
||||
|
||||
@ -15,6 +15,7 @@ const reloc = @import("reloc.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
const Archive = @import("Archive.zig");
|
||||
const CodeSignature = @import("CodeSignature.zig");
|
||||
const Dylib = @import("Dylib.zig");
|
||||
const Object = @import("Object.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const Trie = @import("Trie.zig");
|
||||
@ -35,6 +36,7 @@ stack_size: u64 = 0,
|
||||
|
||||
objects: std.ArrayListUnmanaged(*Object) = .{},
|
||||
archives: std.ArrayListUnmanaged(*Archive) = .{},
|
||||
dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
|
||||
|
||||
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
|
||||
|
||||
@ -151,10 +153,18 @@ pub fn deinit(self: *Zld) void {
|
||||
}
|
||||
self.archives.deinit(self.allocator);
|
||||
|
||||
for (self.dylibs.items) |dylib| {
|
||||
dylib.deinit();
|
||||
self.allocator.destroy(dylib);
|
||||
}
|
||||
self.dylibs.deinit(self.allocator);
|
||||
|
||||
self.mappings.deinit(self.allocator);
|
||||
self.unhandled_sections.deinit(self.allocator);
|
||||
|
||||
self.globals.deinit(self.allocator);
|
||||
self.imports.deinit(self.allocator);
|
||||
self.unresolved.deinit(self.allocator);
|
||||
self.strtab.deinit(self.allocator);
|
||||
|
||||
{
|
||||
@ -177,7 +187,8 @@ pub fn closeFiles(self: Zld) void {
|
||||
}
|
||||
|
||||
const LinkArgs = struct {
|
||||
stack_size: ?u64 = null,
|
||||
shared_libs: []const []const u8,
|
||||
rpaths: []const []const u8,
|
||||
};
|
||||
|
||||
pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {
|
||||
@ -214,10 +225,11 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
|
||||
.read = true,
|
||||
.mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,
|
||||
});
|
||||
self.stack_size = args.stack_size orelse 0;
|
||||
|
||||
try self.populateMetadata();
|
||||
try self.addRpaths(args.rpaths);
|
||||
try self.parseInputFiles(files);
|
||||
try self.parseDylibs(args.shared_libs);
|
||||
try self.resolveSymbols();
|
||||
try self.resolveStubsAndGotEntries();
|
||||
try self.updateMetadata();
|
||||
@ -235,6 +247,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
kind: enum {
|
||||
object,
|
||||
archive,
|
||||
dylib,
|
||||
},
|
||||
file: fs.File,
|
||||
name: []const u8,
|
||||
@ -242,7 +255,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
var classified = std.ArrayList(Input).init(self.allocator);
|
||||
defer classified.deinit();
|
||||
|
||||
// First, classify input files as either object or archive.
|
||||
// First, classify input files: object, archive or dylib.
|
||||
for (files) |file_name| {
|
||||
const file = try fs.cwd().openFile(file_name, .{});
|
||||
const full_path = full_path: {
|
||||
@ -283,6 +296,22 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
continue;
|
||||
}
|
||||
|
||||
try_dylib: {
|
||||
const header = try file.reader().readStruct(macho.mach_header_64);
|
||||
if (header.filetype != macho.MH_DYLIB) {
|
||||
try file.seekTo(0);
|
||||
break :try_dylib;
|
||||
}
|
||||
|
||||
try file.seekTo(0);
|
||||
try classified.append(.{
|
||||
.kind = .dylib,
|
||||
.file = file,
|
||||
.name = full_path,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
log.debug("unexpected input file of unknown type '{s}'", .{file_name});
|
||||
}
|
||||
|
||||
@ -311,10 +340,71 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
try archive.parse();
|
||||
try self.archives.append(self.allocator, archive);
|
||||
},
|
||||
.dylib => {
|
||||
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.file;
|
||||
|
||||
const ordinal = @intCast(u16, self.dylibs.items.len);
|
||||
dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem
|
||||
|
||||
// TODO Defer parsing of the dylibs until they are actually needed
|
||||
try dylib.parse();
|
||||
try self.dylibs.append(self.allocator, dylib);
|
||||
|
||||
// Add LC_LOAD_DYLIB command
|
||||
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 });
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parseDylibs(self: *Zld, shared_libs: []const []const u8) !void {
|
||||
for (shared_libs) |lib| {
|
||||
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);
|
||||
dylib.file = try fs.cwd().openFile(lib, .{});
|
||||
|
||||
const ordinal = @intCast(u16, self.dylibs.items.len);
|
||||
dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem
|
||||
|
||||
// TODO Defer parsing of the dylibs until they are actually needed
|
||||
try dylib.parse();
|
||||
try self.dylibs.append(self.allocator, dylib);
|
||||
|
||||
// Add LC_LOAD_DYLIB command
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
fn mapAndUpdateSections(
|
||||
self: *Zld,
|
||||
object_id: u16,
|
||||
@ -1398,35 +1488,51 @@ fn resolveSymbols(self: *Zld) !void {
|
||||
// Third pass, resolve symbols in dynamic libraries.
|
||||
// TODO Implement libSystem as a hard-coded library, or ship with
|
||||
// a libSystem.B.tbd definition file?
|
||||
try self.imports.ensureCapacity(self.allocator, self.unresolved.count());
|
||||
var unresolved = std.ArrayList(*Symbol).init(self.allocator);
|
||||
defer unresolved.deinit();
|
||||
|
||||
try unresolved.ensureCapacity(self.unresolved.count());
|
||||
for (self.unresolved.items()) |entry| {
|
||||
const proxy = try self.allocator.create(Symbol.Proxy);
|
||||
errdefer self.allocator.destroy(proxy);
|
||||
|
||||
proxy.* = .{
|
||||
.base = .{
|
||||
.@"type" = .proxy,
|
||||
.name = try self.allocator.dupe(u8, entry.key),
|
||||
},
|
||||
.dylib = 0,
|
||||
};
|
||||
|
||||
self.imports.putAssumeCapacityNoClobber(proxy.base.name, &proxy.base);
|
||||
entry.value.alias = &proxy.base;
|
||||
unresolved.appendAssumeCapacity(entry.value);
|
||||
}
|
||||
self.unresolved.clearAndFree(self.allocator);
|
||||
|
||||
// If there are any undefs left, flag an error.
|
||||
if (self.unresolved.count() > 0) {
|
||||
for (self.unresolved.items()) |entry| {
|
||||
log.err("undefined reference to symbol '{s}'", .{entry.key});
|
||||
log.err(" | referenced in {s}", .{
|
||||
entry.value.cast(Symbol.Unresolved).?.file.name.?,
|
||||
});
|
||||
var has_undefined = false;
|
||||
while (unresolved.popOrNull()) |undef| {
|
||||
var found = false;
|
||||
for (self.dylibs.items) |dylib| {
|
||||
const proxy = dylib.symbols.get(undef.name) orelse continue;
|
||||
try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
|
||||
undef.alias = proxy;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// TODO we currently hardcode all unresolved symbols to libSystem
|
||||
const proxy = try self.allocator.create(Symbol.Proxy);
|
||||
errdefer self.allocator.destroy(proxy);
|
||||
|
||||
proxy.* = .{
|
||||
.base = .{
|
||||
.@"type" = .proxy,
|
||||
.name = try self.allocator.dupe(u8, undef.name),
|
||||
},
|
||||
.dylib = null, // TODO null means libSystem
|
||||
};
|
||||
|
||||
try self.imports.putNoClobber(self.allocator, proxy.base.name, &proxy.base);
|
||||
undef.alias = &proxy.base;
|
||||
|
||||
// log.err("undefined reference to symbol '{s}'", .{undef.name});
|
||||
// log.err(" | referenced in {s}", .{
|
||||
// undef.cast(Symbol.Unresolved).?.file.name.?,
|
||||
// });
|
||||
// has_undefined = true;
|
||||
}
|
||||
return error.UndefinedSymbolReference;
|
||||
}
|
||||
|
||||
if (has_undefined) return error.UndefinedSymbolReference;
|
||||
|
||||
// Finally put dyld_stub_binder as an Import
|
||||
const dyld_stub_binder = try self.allocator.create(Symbol.Proxy);
|
||||
errdefer self.allocator.destroy(dyld_stub_binder);
|
||||
@ -1436,7 +1542,7 @@ fn resolveSymbols(self: *Zld) !void {
|
||||
.@"type" = .proxy,
|
||||
.name = try self.allocator.dupe(u8, "dyld_stub_binder"),
|
||||
},
|
||||
.dylib = 0,
|
||||
.dylib = null, // TODO null means libSystem
|
||||
};
|
||||
|
||||
try self.imports.putNoClobber(
|
||||
@ -1997,27 +2103,10 @@ fn populateMetadata(self: *Zld) !void {
|
||||
|
||||
if (self.libsystem_cmd_index == null) {
|
||||
self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
|
||||
u64,
|
||||
@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
|
||||
@sizeOf(u64),
|
||||
));
|
||||
// TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
|
||||
// In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
|
||||
const min_version = 0x0;
|
||||
var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
|
||||
.cmd = macho.LC_LOAD_DYLIB,
|
||||
.cmdsize = cmdsize,
|
||||
.dylib = .{
|
||||
.name = @sizeOf(macho.dylib_command),
|
||||
.timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
|
||||
.current_version = min_version,
|
||||
.compatibility_version = min_version,
|
||||
},
|
||||
});
|
||||
dylib_cmd.data = try self.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
|
||||
mem.set(u8, dylib_cmd.data, 0);
|
||||
mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
|
||||
|
||||
var dylib_cmd = try createLoadDylibCommand(self.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0);
|
||||
errdefer dylib_cmd.deinit(self.allocator);
|
||||
|
||||
try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
|
||||
}
|
||||
|
||||
@ -2080,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn addRpaths(self: *Zld, rpaths: []const []const u8) !void {
|
||||
for (rpaths) |rpath| {
|
||||
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
|
||||
u64,
|
||||
@sizeOf(macho.rpath_command) + rpath.len,
|
||||
@sizeOf(u64),
|
||||
));
|
||||
var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{
|
||||
.cmd = macho.LC_RPATH,
|
||||
.cmdsize = cmdsize,
|
||||
.path = @sizeOf(macho.rpath_command),
|
||||
});
|
||||
rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
|
||||
mem.set(u8, rpath_cmd.data, 0);
|
||||
mem.copy(u8, rpath_cmd.data, rpath);
|
||||
try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd });
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(self: *Zld) !void {
|
||||
try self.writeStubHelperCommon();
|
||||
try self.resolveRelocsAndWriteSections();
|
||||
@ -2303,7 +2411,10 @@ fn writeBindInfoTable(self: *Zld) !void {
|
||||
|
||||
for (self.got_entries.items) |sym| {
|
||||
if (sym.cast(Symbol.Proxy)) |proxy| {
|
||||
const dylib_ordinal = proxy.dylib + 1;
|
||||
const dylib_ordinal = ordinal: {
|
||||
const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem
|
||||
break :ordinal dylib.ordinal.?;
|
||||
};
|
||||
try pointers.append(.{
|
||||
.offset = base_offset + proxy.base.got_index.? * @sizeOf(u64),
|
||||
.segment_id = segment_id,
|
||||
@ -2322,7 +2433,10 @@ fn writeBindInfoTable(self: *Zld) !void {
|
||||
|
||||
const sym = self.imports.get("__tlv_bootstrap") orelse unreachable;
|
||||
const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
|
||||
const dylib_ordinal = proxy.dylib + 1;
|
||||
const dylib_ordinal = ordinal: {
|
||||
const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem
|
||||
break :ordinal dylib.ordinal.?;
|
||||
};
|
||||
|
||||
try pointers.append(.{
|
||||
.offset = base_offset,
|
||||
@ -2364,7 +2478,11 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
|
||||
|
||||
for (self.stubs.items) |sym| {
|
||||
const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
|
||||
const dylib_ordinal = proxy.dylib + 1;
|
||||
const dylib_ordinal = ordinal: {
|
||||
const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem
|
||||
break :ordinal dylib.ordinal.?;
|
||||
};
|
||||
|
||||
pointers.appendAssumeCapacity(.{
|
||||
.offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
|
||||
.segment_id = segment_id,
|
||||
@ -2664,11 +2782,15 @@ fn writeSymbolTable(self: *Zld) !void {
|
||||
|
||||
for (self.imports.items()) |entry| {
|
||||
const sym = entry.value;
|
||||
const ordinal = ordinal: {
|
||||
const dylib = sym.cast(Symbol.Proxy).?.dylib orelse break :ordinal 1; // TODO handle libSystem
|
||||
break :ordinal dylib.ordinal.?;
|
||||
};
|
||||
try undefs.append(.{
|
||||
.n_strx = try self.makeString(sym.name),
|
||||
.n_type = macho.N_UNDF | macho.N_EXT,
|
||||
.n_sect = 0,
|
||||
.n_desc = macho.N_SYMBOL_RESOLVER | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
|
||||
.n_desc = (ordinal * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
|
||||
.n_value = 0,
|
||||
});
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ pub const LoadCommand = union(enum) {
|
||||
SourceVersion: macho.source_version_command,
|
||||
Uuid: macho.uuid_command,
|
||||
LinkeditData: macho.linkedit_data_command,
|
||||
Rpath: GenericCommandWithData(macho.rpath_command),
|
||||
Unknown: GenericCommandWithData(macho.load_command),
|
||||
|
||||
pub fn read(allocator: *Allocator, reader: anytype) !LoadCommand {
|
||||
@ -38,7 +39,9 @@ pub const LoadCommand = union(enum) {
|
||||
macho.LC_SEGMENT_64 => LoadCommand{
|
||||
.Segment = try SegmentCommand.read(allocator, stream.reader()),
|
||||
},
|
||||
macho.LC_DYLD_INFO, macho.LC_DYLD_INFO_ONLY => LoadCommand{
|
||||
macho.LC_DYLD_INFO,
|
||||
macho.LC_DYLD_INFO_ONLY,
|
||||
=> LoadCommand{
|
||||
.DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command),
|
||||
},
|
||||
macho.LC_SYMTAB => LoadCommand{
|
||||
@ -47,16 +50,27 @@ pub const LoadCommand = union(enum) {
|
||||
macho.LC_DYSYMTAB => LoadCommand{
|
||||
.Dysymtab = try stream.reader().readStruct(macho.dysymtab_command),
|
||||
},
|
||||
macho.LC_ID_DYLINKER, macho.LC_LOAD_DYLINKER, macho.LC_DYLD_ENVIRONMENT => LoadCommand{
|
||||
macho.LC_ID_DYLINKER,
|
||||
macho.LC_LOAD_DYLINKER,
|
||||
macho.LC_DYLD_ENVIRONMENT,
|
||||
=> LoadCommand{
|
||||
.Dylinker = try GenericCommandWithData(macho.dylinker_command).read(allocator, stream.reader()),
|
||||
},
|
||||
macho.LC_ID_DYLIB, macho.LC_LOAD_WEAK_DYLIB, macho.LC_LOAD_DYLIB, macho.LC_REEXPORT_DYLIB => LoadCommand{
|
||||
macho.LC_ID_DYLIB,
|
||||
macho.LC_LOAD_WEAK_DYLIB,
|
||||
macho.LC_LOAD_DYLIB,
|
||||
macho.LC_REEXPORT_DYLIB,
|
||||
=> LoadCommand{
|
||||
.Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()),
|
||||
},
|
||||
macho.LC_MAIN => LoadCommand{
|
||||
.Main = try stream.reader().readStruct(macho.entry_point_command),
|
||||
},
|
||||
macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => LoadCommand{
|
||||
macho.LC_VERSION_MIN_MACOSX,
|
||||
macho.LC_VERSION_MIN_IPHONEOS,
|
||||
macho.LC_VERSION_MIN_WATCHOS,
|
||||
macho.LC_VERSION_MIN_TVOS,
|
||||
=> LoadCommand{
|
||||
.VersionMin = try stream.reader().readStruct(macho.version_min_command),
|
||||
},
|
||||
macho.LC_SOURCE_VERSION => LoadCommand{
|
||||
@ -65,9 +79,15 @@ pub const LoadCommand = union(enum) {
|
||||
macho.LC_UUID => LoadCommand{
|
||||
.Uuid = try stream.reader().readStruct(macho.uuid_command),
|
||||
},
|
||||
macho.LC_FUNCTION_STARTS, macho.LC_DATA_IN_CODE, macho.LC_CODE_SIGNATURE => LoadCommand{
|
||||
macho.LC_FUNCTION_STARTS,
|
||||
macho.LC_DATA_IN_CODE,
|
||||
macho.LC_CODE_SIGNATURE,
|
||||
=> LoadCommand{
|
||||
.LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command),
|
||||
},
|
||||
macho.LC_RPATH => LoadCommand{
|
||||
.Rpath = try GenericCommandWithData(macho.rpath_command).read(allocator, stream.reader()),
|
||||
},
|
||||
else => LoadCommand{
|
||||
.Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()),
|
||||
},
|
||||
@ -87,6 +107,7 @@ pub const LoadCommand = union(enum) {
|
||||
.Segment => |x| x.write(writer),
|
||||
.Dylinker => |x| x.write(writer),
|
||||
.Dylib => |x| x.write(writer),
|
||||
.Rpath => |x| x.write(writer),
|
||||
.Unknown => |x| x.write(writer),
|
||||
};
|
||||
}
|
||||
@ -104,6 +125,7 @@ pub const LoadCommand = union(enum) {
|
||||
.Segment => |x| x.inner.cmd,
|
||||
.Dylinker => |x| x.inner.cmd,
|
||||
.Dylib => |x| x.inner.cmd,
|
||||
.Rpath => |x| x.inner.cmd,
|
||||
.Unknown => |x| x.inner.cmd,
|
||||
};
|
||||
}
|
||||
@ -121,6 +143,7 @@ pub const LoadCommand = union(enum) {
|
||||
.Segment => |x| x.inner.cmdsize,
|
||||
.Dylinker => |x| x.inner.cmdsize,
|
||||
.Dylib => |x| x.inner.cmdsize,
|
||||
.Rpath => |x| x.inner.cmdsize,
|
||||
.Unknown => |x| x.inner.cmdsize,
|
||||
};
|
||||
}
|
||||
@ -130,6 +153,7 @@ pub const LoadCommand = union(enum) {
|
||||
.Segment => |*x| x.deinit(allocator),
|
||||
.Dylinker => |*x| x.deinit(allocator),
|
||||
.Dylib => |*x| x.deinit(allocator),
|
||||
.Rpath => |*x| x.deinit(allocator),
|
||||
.Unknown => |*x| x.deinit(allocator),
|
||||
else => {},
|
||||
};
|
||||
@ -153,6 +177,7 @@ pub const LoadCommand = union(enum) {
|
||||
.Segment => |x| x.eql(other.Segment),
|
||||
.Dylinker => |x| x.eql(other.Dylinker),
|
||||
.Dylib => |x| x.eql(other.Dylib),
|
||||
.Rpath => |x| x.eql(other.Rpath),
|
||||
.Unknown => |x| x.eql(other.Unknown),
|
||||
};
|
||||
}
|
||||
@ -282,6 +307,37 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn createLoadDylibCommand(
|
||||
allocator: *Allocator,
|
||||
name: []const u8,
|
||||
timestamp: u32,
|
||||
current_version: u32,
|
||||
compatibility_version: u32,
|
||||
) !GenericCommandWithData(macho.dylib_command) {
|
||||
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
|
||||
u64,
|
||||
@sizeOf(macho.dylib_command) + name.len,
|
||||
@sizeOf(u64),
|
||||
));
|
||||
|
||||
var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
|
||||
.cmd = macho.LC_LOAD_DYLIB,
|
||||
.cmdsize = cmdsize,
|
||||
.dylib = .{
|
||||
.name = @sizeOf(macho.dylib_command),
|
||||
.timestamp = timestamp,
|
||||
.current_version = current_version,
|
||||
.compatibility_version = compatibility_version,
|
||||
},
|
||||
});
|
||||
dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
|
||||
|
||||
mem.set(u8, dylib_cmd.data, 0);
|
||||
mem.copy(u8, dylib_cmd.data, name);
|
||||
|
||||
return dylib_cmd;
|
||||
}
|
||||
|
||||
fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void {
|
||||
var stream = io.fixedBufferStream(buffer);
|
||||
var given = try LoadCommand.read(allocator, stream.reader());
|
||||
|
||||
@ -2445,6 +2445,10 @@ fn transInitListExpr(
|
||||
var qual_type = qt.getTypePtr();
|
||||
const source_loc = @ptrCast(*const clang.Expr, expr).getBeginLoc();
|
||||
|
||||
if (qualTypeWasDemotedToOpaque(c, qt)) {
|
||||
return fail(c, error.UnsupportedTranslation, source_loc, "Cannot initialize opaque type", .{});
|
||||
}
|
||||
|
||||
if (qual_type.isRecordType()) {
|
||||
return maybeSuppressResult(c, scope, used, try transInitListExprRecord(
|
||||
c,
|
||||
|
||||
@ -9,10 +9,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
cases.add("test/standalone/main_return_error/error_u8.zig");
|
||||
cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
|
||||
cases.addBuildFile("test/standalone/main_pkg_path/build.zig");
|
||||
if (std.Target.current.os.tag != .macos) {
|
||||
// TODO zld cannot link shared libraries yet.
|
||||
cases.addBuildFile("test/standalone/shared_library/build.zig");
|
||||
}
|
||||
cases.addBuildFile("test/standalone/shared_library/build.zig");
|
||||
cases.addBuildFile("test/standalone/mix_o_files/build.zig");
|
||||
cases.addBuildFile("test/standalone/global_linkage/build.zig");
|
||||
cases.addBuildFile("test/standalone/static_c_lib/build.zig");
|
||||
|
||||
@ -3513,4 +3513,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ asm (".globl func\n\t.type func, @function\n\tfunc:\n\t.cfi_startproc\n\tmovl $42, %eax\n\tret\n\t.cfi_endproc");
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("Demote function that initializes opaque struct",
|
||||
\\struct my_struct {
|
||||
\\ unsigned a: 15;
|
||||
\\ unsigned: 2;
|
||||
\\ unsigned b: 15;
|
||||
\\};
|
||||
\\void initialize(void) {
|
||||
\\ struct my_struct S = {.a = 1, .b = 2};
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\warning: Cannot initialize opaque type
|
||||
,
|
||||
\\warning: unable to translate function, demoted to extern
|
||||
\\pub extern fn initialize() void;
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user