aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-08-04 14:24:00 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-08-04 14:24:00 -0700
commitba70eee8bb81da0d44982a84395aee660635e5ba (patch)
tree40f4ef036ab60572f9b19e94dc84415993ed3fa8 /lib/std
parenta3045b8abbba896da34a02266f2be89dd6c90ecc (diff)
parent616f65df750f53e6334cc5ed2c8f4b5668d573f2 (diff)
downloadzig-ba70eee8bb81da0d44982a84395aee660635e5ba.tar.gz
zig-ba70eee8bb81da0d44982a84395aee660635e5ba.zip
Merge remote-tracking branch 'origin/master' into llvm15
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/build/CheckObjectStep.zig89
-rw-r--r--lib/std/builtin.zig11
-rw-r--r--lib/std/crypto/25519/ed25519.zig27
-rw-r--r--lib/std/fs.zig31
-rw-r--r--lib/std/fs/test.zig5
-rw-r--r--lib/std/macho.zig464
6 files changed, 165 insertions, 462 deletions
diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig
index 0525bbf034..cc0982ec08 100644
--- a/lib/std/build/CheckObjectStep.zig
+++ b/lib/std/build/CheckObjectStep.zig
@@ -283,7 +283,14 @@ fn make(step: *Step) !void {
const gpa = self.builder.allocator;
const src_path = self.source.getPath(self.builder);
- const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
+ const contents = try fs.cwd().readFileAllocOptions(
+ gpa,
+ src_path,
+ self.max_bytes,
+ null,
+ @alignOf(u64),
+ null,
+ );
const output = switch (self.obj_format) {
.macho => try MachODumper.parseAndDump(contents, .{
@@ -370,9 +377,10 @@ const Opts = struct {
};
const MachODumper = struct {
+ const LoadCommandIterator = macho.LoadCommandIterator;
const symtab_label = "symtab";
- fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
+ fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
var stream = std.io.fixedBufferStream(bytes);
const reader = stream.reader();
@@ -385,55 +393,54 @@ const MachODumper = struct {
var output = std.ArrayList(u8).init(gpa);
const writer = output.writer();
- var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);
- try load_commands.ensureTotalCapacity(hdr.ncmds);
-
- var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);
- var imports = std.ArrayList(u16).init(gpa);
-
- var symtab_cmd: ?u16 = null;
- var i: u16 = 0;
- while (i < hdr.ncmds) : (i += 1) {
- var cmd = try macho.LoadCommand.read(gpa, reader);
- load_commands.appendAssumeCapacity(cmd);
+ var symtab: []const macho.nlist_64 = undefined;
+ var strtab: []const u8 = undefined;
+ var sections = std.ArrayList(macho.section_64).init(gpa);
+ var imports = std.ArrayList([]const u8).init(gpa);
+ var it = LoadCommandIterator{
+ .ncmds = hdr.ncmds,
+ .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
+ };
+ var i: usize = 0;
+ while (it.next()) |cmd| {
switch (cmd.cmd()) {
.SEGMENT_64 => {
- const seg = cmd.segment;
- for (seg.sections.items) |_, j| {
- try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });
+ const seg = cmd.cast(macho.segment_command_64).?;
+ try sections.ensureUnusedCapacity(seg.nsects);
+ for (cmd.getSections()) |sect| {
+ sections.appendAssumeCapacity(sect);
}
},
- .SYMTAB => {
- symtab_cmd = i;
+ .SYMTAB => if (opts.dump_symtab) {
+ const lc = cmd.cast(macho.symtab_command).?;
+ symtab = @ptrCast(
+ [*]const macho.nlist_64,
+ @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
+ )[0..lc.nsyms];
+ strtab = bytes[lc.stroff..][0..lc.strsize];
},
.LOAD_DYLIB,
.LOAD_WEAK_DYLIB,
.REEXPORT_DYLIB,
=> {
- try imports.append(i);
+ try imports.append(cmd.getDylibPathName());
},
else => {},
}
try dumpLoadCommand(cmd, i, writer);
try writer.writeByte('\n');
+
+ i += 1;
}
if (opts.dump_symtab) {
- const cmd = load_commands.items[symtab_cmd.?].symtab;
- try writer.writeAll(symtab_label ++ "\n");
- const strtab = bytes[cmd.stroff..][0..cmd.strsize];
- const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];
- const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab);
-
for (symtab) |sym| {
if (sym.stab()) continue;
const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
if (sym.sect()) {
- const map = sections.items[sym.n_sect - 1];
- const seg = load_commands.items[map.seg].segment;
- const sect = seg.sections.items[map.sect];
+ const sect = sections.items[sym.n_sect - 1];
try writer.print("{x} ({s},{s})", .{
sym.n_value,
sect.segName(),
@@ -455,9 +462,7 @@ const MachODumper = struct {
break :blk "flat lookup";
unreachable;
}
- const import_id = imports.items[@bitCast(u16, ordinal) - 1];
- const import = load_commands.items[import_id].dylib;
- const full_path = mem.sliceTo(import.data, 0);
+ const full_path = imports.items[@bitCast(u16, ordinal) - 1];
const basename = fs.path.basename(full_path);
assert(basename.len > 0);
const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
@@ -481,7 +486,7 @@ const MachODumper = struct {
return output.toOwnedSlice();
}
- fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void {
+ fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
// print header first
try writer.print(
\\LC {d}
@@ -491,8 +496,7 @@ const MachODumper = struct {
switch (lc.cmd()) {
.SEGMENT_64 => {
- // TODO dump section headers
- const seg = lc.segment.inner;
+ const seg = lc.cast(macho.segment_command_64).?;
try writer.writeByte('\n');
try writer.print(
\\segname {s}
@@ -508,7 +512,7 @@ const MachODumper = struct {
seg.filesize,
});
- for (lc.segment.sections.items) |sect| {
+ for (lc.getSections()) |sect| {
try writer.writeByte('\n');
try writer.print(
\\sectname {s}
@@ -531,7 +535,7 @@ const MachODumper = struct {
.LOAD_WEAK_DYLIB,
.REEXPORT_DYLIB,
=> {
- const dylib = lc.dylib.inner.dylib;
+ const dylib = lc.cast(macho.dylib_command).?;
try writer.writeByte('\n');
try writer.print(
\\name {s}
@@ -539,19 +543,20 @@ const MachODumper = struct {
\\current version {x}
\\compatibility version {x}
, .{
- mem.sliceTo(lc.dylib.data, 0),
- dylib.timestamp,
- dylib.current_version,
- dylib.compatibility_version,
+ lc.getDylibPathName(),
+ dylib.dylib.timestamp,
+ dylib.dylib.current_version,
+ dylib.dylib.compatibility_version,
});
},
.MAIN => {
+ const main = lc.cast(macho.entry_point_command).?;
try writer.writeByte('\n');
try writer.print(
\\entryoff {x}
\\stacksize {x}
- , .{ lc.main.entryoff, lc.main.stacksize });
+ , .{ main.entryoff, main.stacksize });
},
.RPATH => {
@@ -559,7 +564,7 @@ const MachODumper = struct {
try writer.print(
\\path {s}
, .{
- mem.sliceTo(lc.rpath.data, 0),
+ lc.getRpathPathName(),
});
},
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index c38eb543ed..047c65439c 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -846,6 +846,17 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
}
}
+pub fn checkNonScalarSentinel(expected: anytype, actual: @TypeOf(expected)) void {
+ if (!std.meta.eql(expected, actual)) {
+ panicSentinelMismatch(expected, actual);
+ }
+}
+
+pub fn panicSentinelMismatch(expected: anytype, actual: @TypeOf(expected)) noreturn {
+ @setCold(true);
+ std.debug.panic("sentinel mismatch: expected {any}, found {any}", .{ expected, actual });
+}
+
pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
@setCold(true);
std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});
diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig
index 83a6c2389e..2a7671863e 100644
--- a/lib/std/crypto/25519/ed25519.zig
+++ b/lib/std/crypto/25519/ed25519.zig
@@ -229,15 +229,14 @@ pub const Ed25519 = struct {
blind_secret_key: BlindSecretKey,
};
- /// Blind an existing key pair with a blinding seed.
- pub fn blind(key_pair: Ed25519.KeyPair, blind_seed: [blind_seed_length]u8) !BlindKeyPair {
+ /// Blind an existing key pair with a blinding seed and a context.
+ pub fn blind(key_pair: Ed25519.KeyPair, blind_seed: [blind_seed_length]u8, ctx: []const u8) !BlindKeyPair {
var h: [Sha512.digest_length]u8 = undefined;
Sha512.hash(key_pair.secret_key[0..32], &h, .{});
Curve.scalar.clamp(h[0..32]);
const scalar = Curve.scalar.reduce(h[0..32].*);
- var blind_h: [Sha512.digest_length]u8 = undefined;
- Sha512.hash(blind_seed[0..], &blind_h, .{});
+ const blind_h = blindCtx(blind_seed, ctx);
const blind_factor = Curve.scalar.reduce(blind_h[0..32].*);
const blind_scalar = Curve.scalar.mul(scalar, blind_factor);
@@ -259,9 +258,8 @@ pub const Ed25519 = struct {
}
/// Recover a public key from a blind version of it.
- pub fn unblindPublicKey(blind_public_key: [public_length]u8, blind_seed: [blind_seed_length]u8) ![public_length]u8 {
- var blind_h: [Sha512.digest_length]u8 = undefined;
- Sha512.hash(&blind_seed, &blind_h, .{});
+ pub fn unblindPublicKey(blind_public_key: [public_length]u8, blind_seed: [blind_seed_length]u8, ctx: []const u8) ![public_length]u8 {
+ const blind_h = blindCtx(blind_seed, ctx);
const inv_blind_factor = Scalar.fromBytes(blind_h[0..32].*).invert().toBytes();
const public_key = try (try Curve.fromBytes(blind_public_key)).mul(inv_blind_factor);
return public_key.toBytes();
@@ -297,6 +295,17 @@ pub const Ed25519 = struct {
mem.copy(u8, sig[32..], s[0..]);
return sig;
}
+
+ /// Compute a blind context from a blinding seed and a context.
+ fn blindCtx(blind_seed: [blind_seed_length]u8, ctx: []const u8) [Sha512.digest_length]u8 {
+ var blind_h: [Sha512.digest_length]u8 = undefined;
+ var hx = Sha512.init(.{});
+ hx.update(&blind_seed);
+ hx.update(&[1]u8{0});
+ hx.update(ctx);
+ hx.final(&blind_h);
+ return blind_h;
+ }
};
};
@@ -458,7 +467,7 @@ test "ed25519 with blind keys" {
crypto.random.bytes(&blind);
// Blind the key pair
- const blind_kp = try BlindKeySignatures.blind(kp, blind);
+ const blind_kp = try BlindKeySignatures.blind(kp, blind, "ctx");
// Sign a message and check that it can be verified with the blind public key
const msg = "test";
@@ -466,6 +475,6 @@ test "ed25519 with blind keys" {
try Ed25519.verify(sig, msg, blind_kp.blind_public_key);
// Unblind the public key
- const pk = try BlindKeySignatures.unblindPublicKey(blind_kp.blind_public_key, blind);
+ const pk = try BlindKeySignatures.unblindPublicKey(blind_kp.blind_public_key, blind, "ctx");
try std.testing.expectEqualSlices(u8, &pk, &kp.public_key);
}
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index c96a118399..0968e16812 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -595,6 +595,19 @@ pub const IterableDir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
pub fn next(self: *Self) Error!?Entry {
+ return self.nextLinux() catch |err| switch (err) {
+ // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration.
+ // This matches the behavior of non-Linux UNIX platforms.
+ error.DirNotFound => null,
+ else => |e| return e,
+ };
+ }
+
+ pub const ErrorLinux = error{DirNotFound} || IteratorError;
+
+ /// Implementation of `next` that can return `error.DirNotFound` if the directory being
+ /// iterated was deleted during iteration (this error is Linux specific).
+ pub fn nextLinux(self: *Self) ErrorLinux!?Entry {
start_over: while (true) {
if (self.index >= self.end_index) {
if (self.first_iter) {
@@ -607,7 +620,7 @@ pub const IterableDir = struct {
.BADF => unreachable, // Dir is invalid or was opened without iteration ability
.FAULT => unreachable,
.NOTDIR => unreachable,
- .NOENT => return null, // The directory being iterated was deleted during iteration.
+ .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
.INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
else => |err| return os.unexpectedErrno(err),
}
@@ -729,6 +742,20 @@ pub const IterableDir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
pub fn next(self: *Self) Error!?Entry {
+ return self.nextWasi() catch |err| switch (err) {
+ // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration.
+ // This matches the behavior of non-Linux UNIX platforms.
+ error.DirNotFound => null,
+ else => |e| return e,
+ };
+ }
+
+ pub const ErrorWasi = error{DirNotFound} || IteratorError;
+
+ /// Implementation of `next` that can return platform-dependent errors depending on the host platform.
+ /// When the host platform is Linux, `error.DirNotFound` can be returned if the directory being
+ /// iterated was deleted during iteration.
+ pub fn nextWasi(self: *Self) ErrorWasi!?Entry {
// We intentinally use fd_readdir even when linked with libc,
// since its implementation is exactly the same as below,
// and we avoid the code complexity here.
@@ -742,7 +769,7 @@ pub const IterableDir = struct {
.FAULT => unreachable,
.NOTDIR => unreachable,
.INVAL => unreachable,
- .NOENT => return null, // The directory being iterated was deleted during iteration.
+ .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
.NOTCAPABLE => return error.AccessDenied,
else => |err| return os.unexpectedErrno(err),
}
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index bedec7d4ad..538ce1bf5e 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" {
// Now, when we try to iterate, the next call should return null immediately.
const entry = try iterator.next();
try std.testing.expect(entry == null);
+
+ // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
+ if (builtin.os.tag == .linux) {
+ try std.testing.expectError(error.DirNotFound, iterator.nextLinux());
+ }
}
fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {
diff --git a/lib/std/macho.zig b/lib/std/macho.zig
index cd4bfa37fb..1955a00334 100644
--- a/lib/std/macho.zig
+++ b/lib/std/macho.zig
@@ -780,7 +780,7 @@ pub const section_64 = extern struct {
return parseName(&sect.segname);
}
- pub fn type_(sect: section_64) u8 {
+ pub fn @"type"(sect: section_64) u8 {
return @truncate(u8, sect.flags & 0xff);
}
@@ -793,6 +793,11 @@ pub const section_64 = extern struct {
return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
}
+ pub fn isZerofill(sect: section_64) bool {
+ const tt = sect.@"type"();
+ return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
+ }
+
pub fn isDebug(sect: section_64) bool {
return sect.attrs() & S_ATTR_DEBUG != 0;
}
@@ -1835,429 +1840,70 @@ pub const data_in_code_entry = extern struct {
kind: u16,
};
-/// A Zig wrapper for all known MachO load commands.
-/// Provides interface to read and write the load command data to a buffer.
-pub const LoadCommand = union(enum) {
- segment: SegmentCommand,
- dyld_info_only: dyld_info_command,
- symtab: symtab_command,
- dysymtab: dysymtab_command,
- dylinker: GenericCommandWithData(dylinker_command),
- dylib: GenericCommandWithData(dylib_command),
- main: entry_point_command,
- version_min: version_min_command,
- source_version: source_version_command,
- build_version: GenericCommandWithData(build_version_command),
- uuid: uuid_command,
- linkedit_data: linkedit_data_command,
- rpath: GenericCommandWithData(rpath_command),
- unknown: GenericCommandWithData(load_command),
-
- pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
- const header = try reader.readStruct(load_command);
- var buffer = try allocator.alloc(u8, header.cmdsize);
- defer allocator.free(buffer);
- mem.copy(u8, buffer, mem.asBytes(&header));
- try reader.readNoEof(buffer[@sizeOf(load_command)..]);
- var stream = io.fixedBufferStream(buffer);
-
- return switch (header.cmd) {
- .SEGMENT_64 => LoadCommand{
- .segment = try SegmentCommand.read(allocator, stream.reader()),
- },
- .DYLD_INFO, .DYLD_INFO_ONLY => LoadCommand{
- .dyld_info_only = try stream.reader().readStruct(dyld_info_command),
- },
- .SYMTAB => LoadCommand{
- .symtab = try stream.reader().readStruct(symtab_command),
- },
- .DYSYMTAB => LoadCommand{
- .dysymtab = try stream.reader().readStruct(dysymtab_command),
- },
- .ID_DYLINKER, .LOAD_DYLINKER, .DYLD_ENVIRONMENT => LoadCommand{
- .dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
- },
- .ID_DYLIB, .LOAD_WEAK_DYLIB, .LOAD_DYLIB, .REEXPORT_DYLIB => LoadCommand{
- .dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
- },
- .MAIN => LoadCommand{
- .main = try stream.reader().readStruct(entry_point_command),
- },
- .VERSION_MIN_MACOSX, .VERSION_MIN_IPHONEOS, .VERSION_MIN_WATCHOS, .VERSION_MIN_TVOS => LoadCommand{
- .version_min = try stream.reader().readStruct(version_min_command),
- },
- .SOURCE_VERSION => LoadCommand{
- .source_version = try stream.reader().readStruct(source_version_command),
- },
- .BUILD_VERSION => LoadCommand{
- .build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
- },
- .UUID => LoadCommand{
- .uuid = try stream.reader().readStruct(uuid_command),
- },
- .FUNCTION_STARTS, .DATA_IN_CODE, .CODE_SIGNATURE => LoadCommand{
- .linkedit_data = try stream.reader().readStruct(linkedit_data_command),
- },
- .RPATH => LoadCommand{
- .rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
- },
- else => LoadCommand{
- .unknown = try GenericCommandWithData(load_command).read(allocator, stream.reader()),
- },
- };
- }
-
- pub fn write(self: LoadCommand, writer: anytype) !void {
- return switch (self) {
- .dyld_info_only => |x| writeStruct(x, writer),
- .symtab => |x| writeStruct(x, writer),
- .dysymtab => |x| writeStruct(x, writer),
- .main => |x| writeStruct(x, writer),
- .version_min => |x| writeStruct(x, writer),
- .source_version => |x| writeStruct(x, writer),
- .uuid => |x| writeStruct(x, writer),
- .linkedit_data => |x| writeStruct(x, writer),
- .segment => |x| x.write(writer),
- .dylinker => |x| x.write(writer),
- .dylib => |x| x.write(writer),
- .rpath => |x| x.write(writer),
- .build_version => |x| x.write(writer),
- .unknown => |x| x.write(writer),
- };
- }
-
- pub fn cmd(self: LoadCommand) LC {
- return switch (self) {
- .dyld_info_only => |x| x.cmd,
- .symtab => |x| x.cmd,
- .dysymtab => |x| x.cmd,
- .main => |x| x.cmd,
- .version_min => |x| x.cmd,
- .source_version => |x| x.cmd,
- .uuid => |x| x.cmd,
- .linkedit_data => |x| x.cmd,
- .segment => |x| x.inner.cmd,
- .dylinker => |x| x.inner.cmd,
- .dylib => |x| x.inner.cmd,
- .rpath => |x| x.inner.cmd,
- .build_version => |x| x.inner.cmd,
- .unknown => |x| x.inner.cmd,
- };
- }
-
- pub fn cmdsize(self: LoadCommand) u32 {
- return switch (self) {
- .dyld_info_only => |x| x.cmdsize,
- .symtab => |x| x.cmdsize,
- .dysymtab => |x| x.cmdsize,
- .main => |x| x.cmdsize,
- .version_min => |x| x.cmdsize,
- .source_version => |x| x.cmdsize,
- .linkedit_data => |x| x.cmdsize,
- .uuid => |x| x.cmdsize,
- .segment => |x| x.inner.cmdsize,
- .dylinker => |x| x.inner.cmdsize,
- .dylib => |x| x.inner.cmdsize,
- .rpath => |x| x.inner.cmdsize,
- .build_version => |x| x.inner.cmdsize,
- .unknown => |x| x.inner.cmdsize,
- };
- }
-
- pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
- return switch (self.*) {
- .segment => |*x| x.deinit(allocator),
- .dylinker => |*x| x.deinit(allocator),
- .dylib => |*x| x.deinit(allocator),
- .rpath => |*x| x.deinit(allocator),
- .build_version => |*x| x.deinit(allocator),
- .unknown => |*x| x.deinit(allocator),
- else => {},
- };
- }
-
- fn writeStruct(command: anytype, writer: anytype) !void {
- return writer.writeAll(mem.asBytes(&command));
- }
-
- pub fn eql(self: LoadCommand, other: LoadCommand) bool {
- if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
- return switch (self) {
- .dyld_info_only => |x| meta.eql(x, other.dyld_info_only),
- .symtab => |x| meta.eql(x, other.symtab),
- .dysymtab => |x| meta.eql(x, other.dysymtab),
- .main => |x| meta.eql(x, other.main),
- .version_min => |x| meta.eql(x, other.version_min),
- .source_version => |x| meta.eql(x, other.source_version),
- .build_version => |x| x.eql(other.build_version),
- .uuid => |x| meta.eql(x, other.uuid),
- .linkedit_data => |x| meta.eql(x, other.linkedit_data),
- .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),
- };
- }
-};
-
-/// A Zig wrapper for segment_command_64.
-/// Encloses the extern struct together with a list of sections for this segment.
-pub const SegmentCommand = struct {
- inner: segment_command_64,
- sections: std.ArrayListUnmanaged(section_64) = .{},
+pub const LoadCommandIterator = struct {
+ ncmds: usize,
+ buffer: []align(@alignOf(u64)) const u8,
+ index: usize = 0,
- pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {
- const inner = try reader.readStruct(segment_command_64);
- var segment = SegmentCommand{
- .inner = inner,
- };
- try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
+ pub const LoadCommand = struct {
+ hdr: load_command,
+ data: []const u8,
- var i: usize = 0;
- while (i < inner.nsects) : (i += 1) {
- const sect = try reader.readStruct(section_64);
- segment.sections.appendAssumeCapacity(sect);
+ pub fn cmd(lc: LoadCommand) LC {
+ return lc.hdr.cmd;
}
- return segment;
- }
-
- pub fn write(self: SegmentCommand, writer: anytype) !void {
- try writer.writeAll(mem.asBytes(&self.inner));
- for (self.sections.items) |sect| {
- try writer.writeAll(mem.asBytes(&sect));
- }
- }
-
- pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
- self.sections.deinit(allocator);
- }
-
- pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
- if (!meta.eql(self.inner, other.inner)) return false;
- const lhs = self.sections.items;
- const rhs = other.sections.items;
- var i: usize = 0;
- while (i < self.inner.nsects) : (i += 1) {
- if (!meta.eql(lhs[i], rhs[i])) return false;
+ pub fn cmdsize(lc: LoadCommand) u32 {
+ return lc.hdr.cmdsize;
}
- return true;
- }
-};
-
-pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
- return .{ .inner = cmd };
-}
-/// A Zig wrapper for a generic load command with variable-length data.
-pub fn GenericCommandWithData(comptime Cmd: type) type {
- return struct {
- inner: Cmd,
- /// This field remains undefined until `read` is called.
- data: []u8 = undefined,
-
- const Self = @This();
-
- pub fn read(allocator: Allocator, reader: anytype) !Self {
- const inner = try reader.readStruct(Cmd);
- var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
- errdefer allocator.free(data);
- try reader.readNoEof(data);
- return Self{
- .inner = inner,
- .data = data,
- };
+ pub fn cast(lc: LoadCommand, comptime Cmd: type) ?Cmd {
+ if (lc.data.len < @sizeOf(Cmd)) return null;
+ return @ptrCast(*const Cmd, @alignCast(@alignOf(Cmd), &lc.data[0])).*;
}
- pub fn write(self: Self, writer: anytype) !void {
- try writer.writeAll(mem.asBytes(&self.inner));
- try writer.writeAll(self.data);
+ /// Asserts LoadCommand is of type segment_command_64.
+ pub fn getSections(lc: LoadCommand) []const section_64 {
+ const segment_lc = lc.cast(segment_command_64).?;
+ if (segment_lc.nsects == 0) return &[0]section_64{};
+ const data = lc.data[@sizeOf(segment_command_64)..];
+ const sections = @ptrCast(
+ [*]const section_64,
+ @alignCast(@alignOf(section_64), &data[0]),
+ )[0..segment_lc.nsects];
+ return sections;
}
- pub fn deinit(self: *Self, allocator: Allocator) void {
- allocator.free(self.data);
+ /// Asserts LoadCommand is of type dylib_command.
+ pub fn getDylibPathName(lc: LoadCommand) []const u8 {
+ const dylib_lc = lc.cast(dylib_command).?;
+ const data = lc.data[dylib_lc.dylib.name..];
+ return mem.sliceTo(data, 0);
}
- pub fn eql(self: Self, other: Self) bool {
- if (!meta.eql(self.inner, other.inner)) return false;
- return mem.eql(u8, self.data, other.data);
+ /// Asserts LoadCommand is of type rpath_command.
+ pub fn getRpathPathName(lc: LoadCommand) []const u8 {
+ const rpath_lc = lc.cast(rpath_command).?;
+ const data = lc.data[rpath_lc.path..];
+ return mem.sliceTo(data, 0);
}
};
-}
-
-pub fn createLoadDylibCommand(
- allocator: Allocator,
- cmd_id: LC,
- name: []const u8,
- timestamp: u32,
- current_version: u32,
- compatibility_version: u32,
-) !GenericCommandWithData(dylib_command) {
- assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
- const cmdsize = @intCast(u32, mem.alignForwardGeneric(
- u64,
- @sizeOf(dylib_command) + name.len + 1, // +1 for nul
- @sizeOf(u64),
- ));
-
- var dylib_cmd = emptyGenericCommandWithData(dylib_command{
- .cmd = cmd_id,
- .cmdsize = cmdsize,
- .dylib = .{
- .name = @sizeOf(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());
- defer given.deinit(allocator);
- try testing.expect(expected.eql(given));
-}
-
-fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
- var stream = io.fixedBufferStream(buffer);
- try cmd.write(stream.writer());
- try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
-}
-
-fn makeStaticString(bytes: []const u8) [16]u8 {
- var buf = [_]u8{0} ** 16;
- assert(bytes.len <= buf.len);
- mem.copy(u8, &buf, bytes);
- return buf;
-}
-
-test "read-write segment command" {
- // TODO compiling for macOS from big-endian arch
- if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
-
- var gpa = testing.allocator;
- const in_buffer = &[_]u8{
- 0x19, 0x00, 0x00, 0x00, // cmd
- 0x98, 0x00, 0x00, 0x00, // cmdsize
- 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
- 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
- 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
- 0x07, 0x00, 0x00, 0x00, // maxprot
- 0x05, 0x00, 0x00, 0x00, // initprot
- 0x01, 0x00, 0x00, 0x00, // nsects
- 0x00, 0x00, 0x00, 0x00, // flags
- 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
- 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
- 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
- 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
- 0x00, 0x40, 0x00, 0x00, // offset
- 0x02, 0x00, 0x00, 0x00, // alignment
- 0x00, 0x00, 0x00, 0x00, // reloff
- 0x00, 0x00, 0x00, 0x00, // nreloc
- 0x00, 0x04, 0x00, 0x80, // flags
- 0x00, 0x00, 0x00, 0x00, // reserved1
- 0x00, 0x00, 0x00, 0x00, // reserved2
- 0x00, 0x00, 0x00, 0x00, // reserved3
- };
- var cmd = SegmentCommand{
- .inner = .{
- .cmdsize = 152,
- .segname = makeStaticString("__TEXT"),
- .vmaddr = 4294967296,
- .vmsize = 294912,
- .filesize = 294912,
- .maxprot = PROT.READ | PROT.WRITE | PROT.EXEC,
- .initprot = PROT.EXEC | PROT.READ,
- .nsects = 1,
- },
- };
- try cmd.sections.append(gpa, .{
- .sectname = makeStaticString("__text"),
- .segname = makeStaticString("__TEXT"),
- .addr = 4294983680,
- .size = 448,
- .offset = 16384,
- .@"align" = 2,
- .flags = S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
- });
- defer cmd.deinit(gpa);
- try testRead(gpa, in_buffer, LoadCommand{ .segment = cmd });
-
- var out_buffer: [in_buffer.len]u8 = undefined;
- try testWrite(&out_buffer, LoadCommand{ .segment = cmd }, in_buffer);
-}
-test "read-write generic command with data" {
- // TODO compiling for macOS from big-endian arch
- if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
-
- var gpa = testing.allocator;
- const in_buffer = &[_]u8{
- 0x0c, 0x00, 0x00, 0x00, // cmd
- 0x20, 0x00, 0x00, 0x00, // cmdsize
- 0x18, 0x00, 0x00, 0x00, // name
- 0x02, 0x00, 0x00, 0x00, // timestamp
- 0x00, 0x00, 0x00, 0x00, // current_version
- 0x00, 0x00, 0x00, 0x00, // compatibility_version
- 0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
- };
- var cmd = GenericCommandWithData(dylib_command){
- .inner = .{
- .cmd = .LOAD_DYLIB,
- .cmdsize = 32,
- .dylib = .{
- .name = 24,
- .timestamp = 2,
- .current_version = 0,
- .compatibility_version = 0,
- },
- },
- };
- cmd.data = try gpa.alloc(u8, 8);
- defer gpa.free(cmd.data);
- cmd.data[0] = 0x2f;
- cmd.data[1] = 0x75;
- cmd.data[2] = 0x73;
- cmd.data[3] = 0x72;
- cmd.data[4] = 0x0;
- cmd.data[5] = 0x0;
- cmd.data[6] = 0x0;
- cmd.data[7] = 0x0;
- try testRead(gpa, in_buffer, LoadCommand{ .dylib = cmd });
-
- var out_buffer: [in_buffer.len]u8 = undefined;
- try testWrite(&out_buffer, LoadCommand{ .dylib = cmd }, in_buffer);
-}
+ pub fn next(it: *LoadCommandIterator) ?LoadCommand {
+ if (it.index >= it.ncmds) return null;
-test "read-write C struct command" {
- // TODO compiling for macOS from big-endian arch
- if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
+ const hdr = @ptrCast(
+ *const load_command,
+ @alignCast(@alignOf(load_command), &it.buffer[0]),
+ ).*;
+ const cmd = LoadCommand{
+ .hdr = hdr,
+ .data = it.buffer[0..hdr.cmdsize],
+ };
- var gpa = testing.allocator;
- const in_buffer = &[_]u8{
- 0x28, 0x00, 0x00, 0x80, // cmd
- 0x18, 0x00, 0x00, 0x00, // cmdsize
- 0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
- };
- const cmd = .{
- .cmd = .MAIN,
- .cmdsize = 24,
- .entryoff = 16644,
- .stacksize = 0,
- };
- try testRead(gpa, in_buffer, LoadCommand{ .main = cmd });
+ it.buffer = @alignCast(@alignOf(u64), it.buffer[hdr.cmdsize..]);
+ it.index += 1;
- var out_buffer: [in_buffer.len]u8 = undefined;
- try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
-}
+ return cmd;
+ }
+};