Commit 513389bc5b

Andrew Kelley <andrew@ziglang.org>
2025-09-25 01:44:45
CLI: exploit std.mem.chompPrefix
fixes a bug in how -fstructured-cfg and -fno-structured-cfg are handled.
1 parent ac4d79e
Changed files (1)
src/main.zig
@@ -1037,10 +1037,9 @@ fn buildOutputType(
 
             var file_ext: ?Compilation.FileExt = null;
             args_loop: while (args_iter.next()) |arg| {
-                if (mem.startsWith(u8, arg, "@")) {
+                if (mem.chompPrefix(u8, arg, "@")) |resp_file_path| {
                     // This is a "compiler response file". We must parse the file and treat its
                     // contents as command line parameters.
-                    const resp_file_path = arg[1..];
                     args_iter.resp_file = initArgIteratorResponseFile(arena, resp_file_path) catch |err| {
                         fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) });
                     };
@@ -1077,8 +1076,8 @@ fn buildOutputType(
                             .key = key,
                             .value = value,
                         });
-                    } else if (mem.startsWith(u8, arg, "-M")) {
-                        var it = mem.splitScalar(u8, arg["-M".len..], '=');
+                    } else if (mem.chompPrefix(u8, arg, "-M")) |rest| {
+                        var it = mem.splitScalar(u8, rest, '=');
                         const mod_name = it.first();
                         const root_src_orig = if (it.peek() != null) it.rest() else null;
                         try handleModArg(
@@ -1111,8 +1110,8 @@ fn buildOutputType(
                         }
                     } else if (mem.eql(u8, arg, "-rcincludes")) {
                         rc_includes = parseRcIncludes(args_iter.nextOrFatal());
-                    } else if (mem.startsWith(u8, arg, "-rcincludes=")) {
-                        rc_includes = parseRcIncludes(arg["-rcincludes=".len..]);
+                    } else if (mem.chompPrefix(u8, arg, "-rcincludes=")) |rest| {
+                        rc_includes = parseRcIncludes(rest);
                     } else if (mem.eql(u8, arg, "-rcflags")) {
                         extra_rcflags.shrinkRetainingCapacity(0);
                         while (true) {
@@ -1122,9 +1121,9 @@ fn buildOutputType(
                             if (mem.eql(u8, next_arg, "--")) break;
                             try extra_rcflags.append(arena, next_arg);
                         }
-                    } else if (mem.startsWith(u8, arg, "-fstructured-cfg")) {
+                    } else if (mem.eql(u8, arg, "-fstructured-cfg")) {
                         mod_opts.structured_cfg = true;
-                    } else if (mem.startsWith(u8, arg, "-fno-structured-cfg")) {
+                    } else if (mem.eql(u8, arg, "-fno-structured-cfg")) {
                         mod_opts.structured_cfg = false;
                     } else if (mem.eql(u8, arg, "--color")) {
                         const next_arg = args_iter.next() orelse {
@@ -1133,8 +1132,7 @@ fn buildOutputType(
                         color = std.meta.stringToEnum(Color, next_arg) orelse {
                             fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
                         };
-                    } else if (mem.startsWith(u8, arg, "-j")) {
-                        const str = arg["-j".len..];
+                    } else if (mem.chompPrefix(u8, arg, "-j")) |str| {
                         const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {
                             fatal("unable to parse jobs count '{s}': {s}", .{
                                 str, @errorName(err),
@@ -1148,8 +1146,8 @@ fn buildOutputType(
                         subsystem = try parseSubSystem(args_iter.nextOrFatal());
                     } else if (mem.eql(u8, arg, "-O")) {
                         mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal());
-                    } else if (mem.startsWith(u8, arg, "-fentry=")) {
-                        entry = .{ .named = arg["-fentry=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-fentry=")) |rest| {
+                        entry = .{ .named = rest };
                     } else if (mem.eql(u8, arg, "--force_undefined")) {
                         try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {});
                     } else if (mem.eql(u8, arg, "--discard-all")) {
@@ -1176,8 +1174,7 @@ fn buildOutputType(
                         try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true });
                     } else if (mem.eql(u8, arg, "-install_name")) {
                         install_name = args_iter.nextOrFatal();
-                    } else if (mem.startsWith(u8, arg, "--compress-debug-sections=")) {
-                        const param = arg["--compress-debug-sections=".len..];
+                    } else if (mem.chompPrefix(u8, arg, "--compress-debug-sections=")) |param| {
                         linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, param) orelse {
                             fatal("expected --compress-debug-sections=[none|zlib|zstd], found '{s}'", .{param});
                         };
@@ -1275,8 +1272,8 @@ fn buildOutputType(
                         try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
                     } else if (mem.eql(u8, arg, "-I")) {
                         try cssan.addIncludePath(arena, &cc_argv, .I, arg, args_iter.nextOrFatal(), false);
-                    } else if (mem.startsWith(u8, arg, "--embed-dir=")) {
-                        try cssan.addIncludePath(arena, &cc_argv, .embed_dir, arg, arg["--embed-dir=".len..], true);
+                    } else if (mem.chompPrefix(u8, arg, "--embed-dir=")) |rest| {
+                        try cssan.addIncludePath(arena, &cc_argv, .embed_dir, arg, rest, true);
                     } else if (mem.eql(u8, arg, "-isystem")) {
                         try cssan.addIncludePath(arena, &cc_argv, .isystem, arg, args_iter.nextOrFatal(), false);
                     } else if (mem.eql(u8, arg, "-iwithsysroot")) {
@@ -1303,14 +1300,14 @@ fn buildOutputType(
                         target_mcpu = args_iter.nextOrFatal();
                     } else if (mem.eql(u8, arg, "-mcmodel")) {
                         mod_opts.code_model = parseCodeModel(args_iter.nextOrFatal());
-                    } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
-                        mod_opts.code_model = parseCodeModel(arg["-mcmodel=".len..]);
-                    } else if (mem.startsWith(u8, arg, "-ofmt=")) {
-                        create_module.object_format = arg["-ofmt=".len..];
-                    } else if (mem.startsWith(u8, arg, "-mcpu=")) {
-                        target_mcpu = arg["-mcpu=".len..];
-                    } else if (mem.startsWith(u8, arg, "-O")) {
-                        mod_opts.optimize_mode = parseOptimizeMode(arg["-O".len..]);
+                    } else if (mem.chompPrefix(u8, arg, "-mcmodel=")) |rest| {
+                        mod_opts.code_model = parseCodeModel(rest);
+                    } else if (mem.chompPrefix(u8, arg, "-ofmt=")) |rest| {
+                        create_module.object_format = rest;
+                    } else if (mem.chompPrefix(u8, arg, "-mcpu=")) |rest| {
+                        target_mcpu = rest;
+                    } else if (mem.chompPrefix(u8, arg, "-O")) |rest| {
+                        mod_opts.optimize_mode = parseOptimizeMode(rest);
                     } else if (mem.eql(u8, arg, "--dynamic-linker")) {
                         create_module.dynamic_linker = args_iter.nextOrFatal();
                     } else if (mem.eql(u8, arg, "--sysroot")) {
@@ -1408,8 +1405,7 @@ fn buildOutputType(
                         create_module.opts.pie = false;
                     } else if (mem.eql(u8, arg, "-flto")) {
                         create_module.opts.lto = .full;
-                    } else if (mem.startsWith(u8, arg, "-flto=")) {
-                        const mode = arg["-flto=".len..];
+                    } else if (mem.chompPrefix(u8, arg, "-flto=")) |mode| {
                         if (mem.eql(u8, mode, "full")) {
                             create_module.opts.lto = .full;
                         } else if (mem.eql(u8, mode, "thin")) {
@@ -1443,8 +1439,7 @@ fn buildOutputType(
                         mod_opts.omit_frame_pointer = false;
                     } else if (mem.eql(u8, arg, "-fsanitize-c")) {
                         mod_opts.sanitize_c = .full;
-                    } else if (mem.startsWith(u8, arg, "-fsanitize-c=")) {
-                        const mode = arg["-fsanitize-c=".len..];
+                    } else if (mem.chompPrefix(u8, arg, "-fsanitize-c=")) |mode| {
                         if (mem.eql(u8, mode, "trap")) {
                             mod_opts.sanitize_c = .trap;
                         } else if (mem.eql(u8, mode, "full")) {
@@ -1492,8 +1487,7 @@ fn buildOutputType(
                         create_module.opts.san_cov_trace_pc_guard = false;
                     } else if (mem.eql(u8, arg, "-freference-trace")) {
                         reference_trace = 256;
-                    } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
-                        const num = arg["-freference-trace=".len..];
+                    } else if (mem.chompPrefix(u8, arg, "-freference-trace=")) |num| {
                         reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
                             fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
                         };
@@ -1507,51 +1501,51 @@ fn buildOutputType(
                         create_module.opts.rdynamic = true;
                     } else if (mem.eql(u8, arg, "-fsoname")) {
                         soname = .yes_default_value;
-                    } else if (mem.startsWith(u8, arg, "-fsoname=")) {
-                        soname = .{ .yes = arg["-fsoname=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-fsoname=")) |rest| {
+                        soname = .{ .yes = rest };
                     } else if (mem.eql(u8, arg, "-fno-soname")) {
                         soname = .no;
                     } else if (mem.eql(u8, arg, "-femit-bin")) {
                         emit_bin = .yes_default_path;
-                    } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
-                        emit_bin = .{ .yes = arg["-femit-bin=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-femit-bin=")) |rest| {
+                        emit_bin = .{ .yes = rest };
                     } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
                         emit_bin = .no;
                     } else if (mem.eql(u8, arg, "-femit-h")) {
                         emit_h = .yes_default_path;
-                    } else if (mem.startsWith(u8, arg, "-femit-h=")) {
-                        emit_h = .{ .yes = arg["-femit-h=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-femit-h=")) |rest| {
+                        emit_h = .{ .yes = rest };
                     } else if (mem.eql(u8, arg, "-fno-emit-h")) {
                         emit_h = .no;
                     } else if (mem.eql(u8, arg, "-femit-asm")) {
                         emit_asm = .yes_default_path;
-                    } else if (mem.startsWith(u8, arg, "-femit-asm=")) {
-                        emit_asm = .{ .yes = arg["-femit-asm=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-femit-asm=")) |rest| {
+                        emit_asm = .{ .yes = rest };
                     } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
                         emit_asm = .no;
                     } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
                         emit_llvm_ir = .yes_default_path;
-                    } else if (mem.startsWith(u8, arg, "-femit-llvm-ir=")) {
-                        emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-femit-llvm-ir=")) |rest| {
+                        emit_llvm_ir = .{ .yes = rest };
                     } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
                         emit_llvm_ir = .no;
                     } else if (mem.eql(u8, arg, "-femit-llvm-bc")) {
                         emit_llvm_bc = .yes_default_path;
-                    } else if (mem.startsWith(u8, arg, "-femit-llvm-bc=")) {
-                        emit_llvm_bc = .{ .yes = arg["-femit-llvm-bc=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-femit-llvm-bc=")) |rest| {
+                        emit_llvm_bc = .{ .yes = rest };
                     } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) {
                         emit_llvm_bc = .no;
                     } else if (mem.eql(u8, arg, "-femit-docs")) {
                         emit_docs = .yes_default_path;
-                    } else if (mem.startsWith(u8, arg, "-femit-docs=")) {
-                        emit_docs = .{ .yes = arg["-femit-docs=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-femit-docs=")) |rest| {
+                        emit_docs = .{ .yes = rest };
                     } else if (mem.eql(u8, arg, "-fno-emit-docs")) {
                         emit_docs = .no;
                     } else if (mem.eql(u8, arg, "-femit-implib")) {
                         emit_implib = .yes_default_path;
                         emit_implib_arg_provided = true;
-                    } else if (mem.startsWith(u8, arg, "-femit-implib=")) {
-                        emit_implib = .{ .yes = arg["-femit-implib=".len..] };
+                    } else if (mem.chompPrefix(u8, arg, "-femit-implib=")) |rest| {
+                        emit_implib = .{ .yes = rest };
                         emit_implib_arg_provided = true;
                     } else if (mem.eql(u8, arg, "-fno-emit-implib")) {
                         emit_implib = .no;
@@ -1601,8 +1595,7 @@ fn buildOutputType(
                         mod_opts.no_builtin = false;
                     } else if (mem.eql(u8, arg, "-fno-builtin")) {
                         mod_opts.no_builtin = true;
-                    } else if (mem.startsWith(u8, arg, "-fopt-bisect-limit=")) {
-                        const next_arg = arg["-fopt-bisect-limit=".len..];
+                    } else if (mem.chompPrefix(u8, arg, "-fopt-bisect-limit=")) |next_arg| {
                         llvm_opt_bisect_limit = std.fmt.parseInt(c_int, next_arg, 0) catch |err|
                             fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
                     } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
@@ -1645,10 +1638,10 @@ fn buildOutputType(
                             linker_z_relro = true;
                         } else if (mem.eql(u8, z_arg, "norelro")) {
                             linker_z_relro = false;
-                        } else if (mem.startsWith(u8, z_arg, "common-page-size=")) {
-                            linker_z_common_page_size = parseIntSuffix(z_arg, "common-page-size=".len);
-                        } else if (mem.startsWith(u8, z_arg, "max-page-size=")) {
-                            linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);
+                        } else if (mem.chompPrefix(u8, z_arg, "common-page-size=")) |rest| {
+                            linker_z_common_page_size = parseIntSuffix(rest, 0);
+                        } else if (mem.chompPrefix(u8, z_arg, "max-page-size=")) |rest| {
+                            linker_z_max_page_size = parseIntSuffix(rest, 0);
                         } else {
                             fatal("unsupported linker extension flag: -z {s}", .{z_arg});
                         }
@@ -1669,16 +1662,16 @@ fn buildOutputType(
                         linker_import_table = true;
                     } else if (mem.eql(u8, arg, "--export-table")) {
                         linker_export_table = true;
-                    } else if (mem.startsWith(u8, arg, "--initial-memory=")) {
-                        linker_initial_memory = parseIntSuffix(arg, "--initial-memory=".len);
-                    } else if (mem.startsWith(u8, arg, "--max-memory=")) {
-                        linker_max_memory = parseIntSuffix(arg, "--max-memory=".len);
+                    } else if (mem.chompPrefix(u8, arg, "--initial-memory=")) |rest| {
+                        linker_initial_memory = parseIntSuffix(rest, 0);
+                    } else if (mem.chompPrefix(u8, arg, "--max-memory=")) |rest| {
+                        linker_max_memory = parseIntSuffix(rest, 0);
                     } else if (mem.eql(u8, arg, "--shared-memory")) {
                         create_module.opts.shared_memory = true;
-                    } else if (mem.startsWith(u8, arg, "--global-base=")) {
-                        linker_global_base = parseIntSuffix(arg, "--global-base=".len);
-                    } else if (mem.startsWith(u8, arg, "--export=")) {
-                        try linker_export_symbol_names.append(arena, arg["--export=".len..]);
+                    } else if (mem.chompPrefix(u8, arg, "--global-base=")) |rest| {
+                        linker_global_base = parseIntSuffix(rest, 0);
+                    } else if (mem.chompPrefix(u8, arg, "--export=")) |rest| {
+                        try linker_export_symbol_names.append(arena, rest);
                     } else if (mem.eql(u8, arg, "-Bsymbolic")) {
                         linker_bind_global_refs_locally = true;
                     } else if (mem.eql(u8, arg, "--gc-sections")) {
@@ -1687,8 +1680,7 @@ fn buildOutputType(
                         linker_gc_sections = false;
                     } else if (mem.eql(u8, arg, "--build-id")) {
                         build_id = .fast;
-                    } else if (mem.startsWith(u8, arg, "--build-id=")) {
-                        const style = arg["--build-id=".len..];
+                    } else if (mem.chompPrefix(u8, arg, "--build-id=")) |style| {
                         build_id = std.zig.BuildId.parse(style) catch |err| {
                             fatal("unable to parse --build-id style '{s}': {s}", .{
                                 style, @errorName(err),
@@ -1712,26 +1704,26 @@ fn buildOutputType(
                         verbose_generic_instances = true;
                     } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
                         verbose_llvm_ir = "-";
-                    } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) {
-                        verbose_llvm_ir = arg["--verbose-llvm-ir=".len..];
-                    } else if (mem.startsWith(u8, arg, "--verbose-llvm-bc=")) {
-                        verbose_llvm_bc = arg["--verbose-llvm-bc=".len..];
+                    } else if (mem.chompPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| {
+                        verbose_llvm_ir = rest;
+                    } else if (mem.chompPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| {
+                        verbose_llvm_bc = rest;
                     } else if (mem.eql(u8, arg, "--verbose-cimport")) {
                         verbose_cimport = true;
                     } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
                         verbose_llvm_cpu_features = true;
-                    } else if (mem.startsWith(u8, arg, "-T")) {
-                        linker_script = arg[2..];
-                    } else if (mem.startsWith(u8, arg, "-L")) {
-                        try create_module.lib_dir_args.append(arena, arg[2..]);
-                    } else if (mem.startsWith(u8, arg, "-F")) {
-                        try create_module.framework_dirs.append(arena, arg[2..]);
-                    } else if (mem.startsWith(u8, arg, "-l")) {
+                    } else if (mem.chompPrefix(u8, arg, "-T")) |rest| {
+                        linker_script = rest;
+                    } else if (mem.chompPrefix(u8, arg, "-L")) |rest| {
+                        try create_module.lib_dir_args.append(arena, rest);
+                    } else if (mem.chompPrefix(u8, arg, "-F")) |rest| {
+                        try create_module.framework_dirs.append(arena, rest);
+                    } else if (mem.chompPrefix(u8, arg, "-l")) |name| {
                         // We don't know whether this library is part of libc
                         // or libc++ until we resolve the target, so we append
                         // to the list for now.
                         try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
-                            .name = arg["-l".len..],
+                            .name = name,
                             .query = .{
                                 .needed = false,
                                 .weak = false,
@@ -1740,9 +1732,9 @@ fn buildOutputType(
                                 .allow_so_scripts = allow_so_scripts,
                             },
                         } });
-                    } else if (mem.startsWith(u8, arg, "-needed-l")) {
+                    } else if (mem.chompPrefix(u8, arg, "-needed-l")) |name| {
                         try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
-                            .name = arg["-needed-l".len..],
+                            .name = name,
                             .query = .{
                                 .needed = true,
                                 .weak = false,
@@ -1751,9 +1743,9 @@ fn buildOutputType(
                                 .allow_so_scripts = allow_so_scripts,
                             },
                         } });
-                    } else if (mem.startsWith(u8, arg, "-weak-l")) {
+                    } else if (mem.chompPrefix(u8, arg, "-weak-l")) |name| {
                         try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
-                            .name = arg["-weak-l".len..],
+                            .name = name,
                             .query = .{
                                 .needed = false,
                                 .weak = true,
@@ -1764,13 +1756,10 @@ fn buildOutputType(
                         } });
                     } else if (mem.startsWith(u8, arg, "-D")) {
                         try cc_argv.append(arena, arg);
-                    } else if (mem.startsWith(u8, arg, "-I")) {
-                        try cssan.addIncludePath(arena, &cc_argv, .I, arg, arg[2..], true);
-                    } else if (mem.startsWith(u8, arg, "-x")) {
-                        const lang = if (arg.len == "-x".len)
-                            args_iter.nextOrFatal()
-                        else
-                            arg["-x".len..];
+                    } else if (mem.chompPrefix(u8, arg, "-I")) |rest| {
+                        try cssan.addIncludePath(arena, &cc_argv, .I, arg, rest, true);
+                    } else if (mem.chompPrefix(u8, arg, "-x")) |rest| {
+                        const lang = if (rest.len == 0) args_iter.nextOrFatal() else rest;
                         if (mem.eql(u8, lang, "none")) {
                             file_ext = null;
                         } else if (Compilation.LangToExt.get(lang)) |got_ext| {
@@ -1778,8 +1767,8 @@ fn buildOutputType(
                         } else {
                             fatal("language not recognized: '{s}'", .{lang});
                         }
-                    } else if (mem.startsWith(u8, arg, "-mexec-model=")) {
-                        create_module.opts.wasi_exec_model = parseWasiExecModel(arg["-mexec-model=".len..]);
+                    } else if (mem.chompPrefix(u8, arg, "-mexec-model=")) |rest| {
+                        create_module.opts.wasi_exec_model = parseWasiExecModel(rest);
                     } else if (mem.eql(u8, arg, "-municode")) {
                         mingw_unicode_entry_point = true;
                     } else {
@@ -2457,8 +2446,8 @@ fn buildOutputType(
                     linker_enable_new_dtags = false;
                 } else if (mem.eql(u8, arg, "-O")) {
                     linker_optimization = linker_args_it.nextOrFatal();
-                } else if (mem.startsWith(u8, arg, "-O")) {
-                    linker_optimization = arg["-O".len..];
+                } else if (mem.chompPrefix(u8, arg, "-O")) |rest| {
+                    linker_optimization = rest;
                 } else if (mem.eql(u8, arg, "-pagezero_size")) {
                     const next_arg = linker_args_it.nextOrFatal();
                     pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
@@ -2540,11 +2529,8 @@ fn buildOutputType(
                     linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, arg1) orelse {
                         fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{arg1});
                     };
-                } else if (mem.startsWith(u8, arg, "-z")) {
-                    var z_arg = arg[2..];
-                    if (z_arg.len == 0) {
-                        z_arg = linker_args_it.nextOrFatal();
-                    }
+                } else if (mem.chompPrefix(u8, arg, "-z")) |z_rest| {
+                    const z_arg = if (z_rest.len == 0) linker_args_it.nextOrFatal() else z_rest;
                     if (mem.eql(u8, z_arg, "nodelete")) {
                         linker_z_nodelete = true;
                     } else if (mem.eql(u8, z_arg, "notext")) {
@@ -2567,12 +2553,12 @@ fn buildOutputType(
                         linker_z_relro = true;
                     } else if (mem.eql(u8, z_arg, "norelro")) {
                         linker_z_relro = false;
-                    } else if (mem.startsWith(u8, z_arg, "stack-size=")) {
-                        stack_size = parseStackSize(z_arg["stack-size=".len..]);
-                    } else if (mem.startsWith(u8, z_arg, "common-page-size=")) {
-                        linker_z_common_page_size = parseIntSuffix(z_arg, "common-page-size=".len);
-                    } else if (mem.startsWith(u8, z_arg, "max-page-size=")) {
-                        linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);
+                    } else if (mem.chompPrefix(u8, z_arg, "stack-size=")) |rest| {
+                        stack_size = parseStackSize(rest);
+                    } else if (mem.chompPrefix(u8, z_arg, "common-page-size=")) |rest| {
+                        linker_z_common_page_size = parseIntSuffix(rest, 0);
+                    } else if (mem.chompPrefix(u8, z_arg, "max-page-size=")) |rest| {
+                        linker_z_max_page_size = parseIntSuffix(rest, 0);
                     } else {
                         fatal("unsupported linker extension flag: -z {s}", .{z_arg});
                     }
@@ -2686,9 +2672,9 @@ fn buildOutputType(
                             .allow_so_scripts = allow_so_scripts,
                         },
                     } });
-                } else if (mem.startsWith(u8, arg, "-weak-l")) {
+                } else if (mem.chompPrefix(u8, arg, "-weak-l")) |rest| {
                     try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
-                        .name = arg["-weak-l".len..],
+                        .name = rest,
                         .query = .{
                             .weak = true,
                             .needed = false,
@@ -3783,8 +3769,7 @@ fn createModule(
                 try mcpu_buffer.appendSlice(cli_mod.target_mcpu orelse "baseline");
 
                 for (create_module.llvm_m_args.items) |llvm_m_arg| {
-                    if (mem.startsWith(u8, llvm_m_arg, "mno-")) {
-                        const llvm_name = llvm_m_arg["mno-".len..];
+                    if (mem.chompPrefix(u8, llvm_m_arg, "mno-")) |llvm_name| {
                         const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
                             fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
                                 @tagName(cpu_arch), llvm_name,
@@ -3792,8 +3777,7 @@ fn createModule(
                         };
                         try mcpu_buffer.append('-');
                         try mcpu_buffer.appendSlice(zig_name);
-                    } else if (mem.startsWith(u8, llvm_m_arg, "m")) {
-                        const llvm_name = llvm_m_arg["m".len..];
+                    } else if (mem.chompPrefix(u8, llvm_m_arg, "m")) |llvm_name| {
                         const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
                             fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
                                 @tagName(cpu_arch), llvm_name,
@@ -4980,9 +4964,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
                     reference_trace = 256;
                 } else if (mem.eql(u8, arg, "--fetch")) {
                     fetch_only = true;
-                } else if (mem.startsWith(u8, arg, "--fetch=")) {
+                } else if (mem.chompPrefix(u8, arg, "--fetch=")) |sub_arg| {
                     fetch_only = true;
-                    const sub_arg = arg["--fetch=".len..];
                     fetch_mode = std.meta.stringToEnum(Package.Fetch.JobQueue.Mode, sub_arg) orelse
                         fatal("expected [needed|all] after '--fetch=', found '{s}'", .{
                             sub_arg,
@@ -4993,8 +4976,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
                     system_pkg_dir_path = args[i];
                     try child_argv.append("--system");
                     continue;
-                } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
-                    const num = arg["-freference-trace=".len..];
+                } else if (mem.chompPrefix(u8, arg, "-freference-trace=")) |num| {
                     reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
                         fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
                     };
@@ -5044,10 +5026,10 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
                     verbose_generic_instances = true;
                 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
                     verbose_llvm_ir = "-";
-                } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) {
-                    verbose_llvm_ir = arg["--verbose-llvm-ir=".len..];
-                } else if (mem.startsWith(u8, arg, "--verbose-llvm-bc=")) {
-                    verbose_llvm_bc = arg["--verbose-llvm-bc=".len..];
+                } else if (mem.chompPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| {
+                    verbose_llvm_ir = rest;
+                } else if (mem.chompPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| {
+                    verbose_llvm_bc = rest;
                 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
                     verbose_cimport = true;
                 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
@@ -5060,8 +5042,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
                     };
                     try child_argv.appendSlice(&.{ arg, args[i] });
                     continue;
-                } else if (mem.startsWith(u8, arg, "-j")) {
-                    const str = arg["-j".len..];
+                } else if (mem.chompPrefix(u8, arg, "-j")) |str| {
                     const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {
                         fatal("unable to parse jobs count '{s}': {s}", .{
                             str, @errorName(err),
@@ -6966,12 +6947,12 @@ fn cmdFetch(
                     debug_hash = true;
                 } else if (mem.eql(u8, arg, "--save")) {
                     save = .{ .yes = null };
-                } else if (mem.startsWith(u8, arg, "--save=")) {
-                    save = .{ .yes = arg["--save=".len..] };
+                } else if (mem.chompPrefix(u8, arg, "--save=")) |rest| {
+                    save = .{ .yes = rest };
                 } else if (mem.eql(u8, arg, "--save-exact")) {
                     save = .{ .exact = null };
-                } else if (mem.startsWith(u8, arg, "--save-exact=")) {
-                    save = .{ .exact = arg["--save-exact=".len..] };
+                } else if (mem.chompPrefix(u8, arg, "--save-exact=")) |rest| {
+                    save = .{ .exact = rest };
                 } else {
                     fatal("unrecognized parameter: '{s}'", .{arg});
                 }