Commit 2c184f9a5f
Changed files (4)
lib
std
build
lib/std/build/CheckObjectStep.zig
@@ -50,7 +50,7 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe
/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
/// they could then be added with this simple program `vmaddr entryoff +`.
const Action = struct {
- tag: enum { match, compute_cmp },
+ tag: enum { match, not_present, compute_cmp },
phrase: []const u8,
expected: ?ComputeCompareExpected = null,
@@ -63,7 +63,7 @@ const Action = struct {
/// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
/// in that order with other letters in between
fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
- assert(act.tag == .match);
+ assert(act.tag == .match or act.tag == .not_present);
var candidate_var: ?struct { name: []const u8, value: u64 } = null;
var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
@@ -202,6 +202,13 @@ const Check = struct {
}) catch unreachable;
}
+ fn notPresent(self: *Check, phrase: []const u8) void {
+ self.actions.append(.{
+ .tag = .not_present,
+ .phrase = self.builder.dupe(phrase),
+ }) catch unreachable;
+ }
+
fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
self.actions.append(.{
.tag = .compute_cmp,
@@ -226,6 +233,15 @@ pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
last.match(phrase);
}
+/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
+/// however ensures there is no matching phrase in the output.
+/// Asserts at least one check already exists.
+pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
+ assert(self.checks.items.len > 0);
+ const last = &self.checks.items[self.checks.items.len - 1];
+ last.notPresent(phrase);
+}
+
/// Creates a new check checking specifically symbol table parsed and dumped from the object
/// file.
/// Issuing this check will force parsing and dumping of the symbol table.
@@ -293,6 +309,21 @@ fn make(step: *Step) !void {
return error.TestFailed;
}
},
+ .not_present => {
+ while (it.next()) |line| {
+ if (try act.match(line, &vars)) {
+ std.debug.print(
+ \\
+ \\========= Expected not to find: ===================
+ \\{s}
+ \\========= But parsed file does contain it: ========
+ \\{s}
+ \\
+ , .{ act.phrase, output });
+ return error.TestFailed;
+ }
+ }
+ },
.compute_cmp => {
const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
error.UnknownVariable => {
test/link/macho/dead_strip/build.zig
@@ -0,0 +1,49 @@
+const std = @import("std");
+const Builder = std.build.Builder;
+const LibExeObjectStep = std.build.LibExeObjStep;
+
+pub fn build(b: *Builder) void {
+ const mode = b.standardReleaseOptions();
+
+ const test_step = b.step("test", "Test the program");
+ test_step.dependOn(b.getInstallStep());
+
+ {
+ // Without -dead_strip, we expect `iAmUnused` symbol present
+ const exe = createScenario(b, mode);
+
+ const check = exe.checkObject(.macho);
+ check.checkInSymtab();
+ check.checkNext("{*} (__TEXT,__text) external _iAmUnused");
+
+ test_step.dependOn(&check.step);
+
+ const run_cmd = exe.run();
+ run_cmd.expectStdOutEqual("Hello!\n");
+ test_step.dependOn(&run_cmd.step);
+ }
+
+ {
+ // With -dead_strip, no `iAmUnused` symbol should be present
+ const exe = createScenario(b, mode);
+ exe.link_gc_sections = true;
+
+ const check = exe.checkObject(.macho);
+ check.checkInSymtab();
+ check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused");
+
+ test_step.dependOn(&check.step);
+
+ const run_cmd = exe.run();
+ run_cmd.expectStdOutEqual("Hello!\n");
+ test_step.dependOn(&run_cmd.step);
+ }
+}
+
+fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
+ const exe = b.addExecutable("test", null);
+ exe.addCSourceFile("main.c", &[0][]const u8{});
+ exe.setBuildMode(mode);
+ exe.linkLibC();
+ return exe;
+}
test/link/macho/dead_strip/main.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+void printMe() {
+ printf("Hello!\n");
+}
+
+int main(int argc, char* argv[]) {
+ printMe();
+ return 0;
+}
+
+void iAmUnused() {
+ printf("YOU SHALL NOT PASS!\n");
+}
test/link.zig
@@ -60,6 +60,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
.build_modes = true,
});
+ cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{
+ .build_modes = false,
+ });
+
cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{
.build_modes = true,
.requires_macos_sdk = true,