commit 0f52c64c72c5244f3d33abc9ff3edfb5998d3f89
parent eb9194365b806f918ea225520271669c9137f379
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 21 May 2026 06:55:45 -0700
Expand driver flag compatibility
Diffstat:
| M | driver/cc.c | | | 135 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
| M | driver/cflags.c | | | 15 | +++++++++++++++ |
| M | driver/driver.h | | | 4 | ++++ |
| M | driver/ld.c | | | 130 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
| M | driver/objdump.c | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
| M | driver/target.c | | | 60 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | test/driver/run.sh | | | 97 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 491 insertions(+), 7 deletions(-)
diff --git a/driver/cc.c b/driver/cc.c
@@ -50,6 +50,19 @@ typedef enum CcDepMode {
CC_DEP_MMD,
} CcDepMode;
+typedef enum CcProbeKind {
+ CC_PROBE_NONE = 0,
+ CC_PROBE_PRINT_SEARCH_DIRS,
+ CC_PROBE_PRINT_FILE_NAME,
+ CC_PROBE_PRINT_PROG_NAME,
+ CC_PROBE_PRINT_LIBGCC_FILE_NAME,
+ CC_PROBE_PRINT_MULTI_OS_DIRECTORY,
+ CC_PROBE_PRINT_RESOURCE_DIR,
+ CC_PROBE_DUMPMACHINE,
+ CC_PROBE_DUMPVERSION,
+ CC_PROBE_DUMPSPECS,
+} CcProbeKind;
+
typedef enum CcLinkItemKind {
CC_LINK_SOURCE_FILE,
CC_LINK_SOURCE_MEMORY,
@@ -113,6 +126,8 @@ typedef struct CcOptions {
const char* linker_script; /* -T path */
const char* sysroot; /* --sysroot / -isysroot */
const char* support_dir; /* --support-dir */
+ int probe_kind; /* CcProbeKind */
+ const char* probe_arg; /* -print-file-name= / -print-prog-name= */
/* -ffile-prefix-map=old=new entries; old is heap-owned (split out), new
* aliases argv. */
@@ -637,6 +652,63 @@ static void cc_log_ignored(const char* arg) {
driver_errf(CC_TOOL, "ignoring accepted compatibility flag: %s", arg);
}
+static int cc_set_probe(CcOptions* o, int kind, const char* arg) {
+ if (o->probe_kind != CC_PROBE_NONE) {
+ driver_errf(CC_TOOL, "only one compiler-information probe is supported");
+ return 1;
+ }
+ o->probe_kind = kind;
+ o->probe_arg = arg;
+ return 0;
+}
+
+static int cc_run_probe(CcOptions* o) {
+ char triple[64];
+ if (driver_target_to_triple(o->target, triple, sizeof(triple)) != 0) {
+ driver_errf(CC_TOOL, "failed to render target triple");
+ return 1;
+ }
+
+ switch (o->probe_kind) {
+ case CC_PROBE_PRINT_SEARCH_DIRS:
+ driver_printf("install: %s\n", o->support_dir ? o->support_dir : "");
+ driver_printf("programs: =\n");
+ driver_printf("libraries: =");
+ if (o->sysroot) driver_printf("%s/lib", o->sysroot);
+ driver_printf("\n");
+ return 0;
+ case CC_PROBE_PRINT_FILE_NAME:
+ driver_printf("%s\n", o->probe_arg ? o->probe_arg : "");
+ return 0;
+ case CC_PROBE_PRINT_PROG_NAME:
+ driver_printf("%s\n", o->probe_arg ? o->probe_arg : "");
+ return 0;
+ case CC_PROBE_PRINT_LIBGCC_FILE_NAME:
+ driver_printf("libcfree_rt.a\n");
+ return 0;
+ case CC_PROBE_PRINT_MULTI_OS_DIRECTORY:
+ driver_printf(".\n");
+ return 0;
+ case CC_PROBE_PRINT_RESOURCE_DIR:
+ driver_printf("%s\n", o->support_dir ? o->support_dir : "");
+ return 0;
+ case CC_PROBE_DUMPMACHINE:
+ driver_printf("%s\n", triple);
+ return 0;
+ case CC_PROBE_DUMPVERSION:
+ driver_printf("0\n");
+ return 0;
+ case CC_PROBE_DUMPSPECS:
+ driver_printf("*cfree:\n");
+ driver_printf("%%{!S:%%{!E:%%{!c:%%{!r:link}}}}\n");
+ return 0;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int cc_record_stdin(CcOptions* o) {
CfreeSourceInput* in;
if (o->stdin_buf) {
@@ -934,8 +1006,45 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
o->support_dir = a + 14;
continue;
}
- if (driver_streq(a, "-include") || driver_streq(a, "-iquote") ||
- driver_streq(a, "-idirafter")) {
+ if (driver_streq(a, "-print-search-dirs")) {
+ if (cc_set_probe(o, CC_PROBE_PRINT_SEARCH_DIRS, NULL) != 0) return 1;
+ continue;
+ }
+ if (driver_strneq(a, "-print-file-name=", 17)) {
+ if (cc_set_probe(o, CC_PROBE_PRINT_FILE_NAME, a + 17) != 0) return 1;
+ continue;
+ }
+ if (driver_strneq(a, "-print-prog-name=", 17)) {
+ if (cc_set_probe(o, CC_PROBE_PRINT_PROG_NAME, a + 17) != 0) return 1;
+ continue;
+ }
+ if (driver_streq(a, "-print-libgcc-file-name")) {
+ if (cc_set_probe(o, CC_PROBE_PRINT_LIBGCC_FILE_NAME, NULL) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_streq(a, "-print-multi-os-directory")) {
+ if (cc_set_probe(o, CC_PROBE_PRINT_MULTI_OS_DIRECTORY, NULL) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_streq(a, "-print-resource-dir")) {
+ if (cc_set_probe(o, CC_PROBE_PRINT_RESOURCE_DIR, NULL) != 0) return 1;
+ continue;
+ }
+ if (driver_streq(a, "-dumpmachine")) {
+ if (cc_set_probe(o, CC_PROBE_DUMPMACHINE, NULL) != 0) return 1;
+ continue;
+ }
+ if (driver_streq(a, "-dumpversion")) {
+ if (cc_set_probe(o, CC_PROBE_DUMPVERSION, NULL) != 0) return 1;
+ continue;
+ }
+ if (driver_streq(a, "-dumpspecs")) {
+ if (cc_set_probe(o, CC_PROBE_DUMPSPECS, NULL) != 0) return 1;
+ continue;
+ }
+ if (driver_streq(a, "-include")) {
if (++i >= argc) {
driver_errf(CC_TOOL, "%s requires an argument", a);
return 1;
@@ -1049,6 +1158,20 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
o->output_path_set = 1;
continue;
}
+ if (driver_strneq(a, "--output=", 9)) {
+ o->output_path = a + 9;
+ o->output_path_set = 1;
+ continue;
+ }
+ if (driver_streq(a, "--output")) {
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "--output requires an argument");
+ return 1;
+ }
+ o->output_path = argv[i];
+ o->output_path_set = 1;
+ continue;
+ }
if (driver_streq(a, "-e")) {
if (++i >= argc) {
driver_errf(CC_TOOL, "-e requires an argument");
@@ -1213,6 +1336,8 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
if (cc_classify_positional(o, a, forced_lang) != 0) return 1;
}
+ if (o->probe_kind != CC_PROBE_NONE) return 0;
+
if (cc_apply_env(o) != 0) return 1;
if (cc_resolve_pending_libs(o) != 0) return 1;
@@ -2203,6 +2328,12 @@ int driver_cc(int argc, char** argv) {
driver_env_fini(&env);
return 2;
}
+ if (co.probe_kind != CC_PROBE_NONE) {
+ rc = cc_run_probe(&co);
+ cc_options_release(&co);
+ driver_env_fini(&env);
+ return rc;
+ }
link_action = !co.compile_only && !co.preprocess_only && !co.dump_tokens &&
co.dep_mode != CC_DEP_M && co.dep_mode != CC_DEP_MM;
diff --git a/driver/cflags.c b/driver/cflags.c
@@ -110,6 +110,21 @@ int driver_cflags_try_consume(DriverCflags* cf, DriverEnv* env,
return 1;
}
+ if (driver_strneq(a, "-iquote", 7)) {
+ const char* dir = cflags_pull_value(tool, argc, argv, i, 7, "-iquote");
+ if (!dir) return -1;
+ cf->include_dirs[cf->ninclude_dirs++] = dir;
+ return 1;
+ }
+
+ if (driver_strneq(a, "-idirafter", 10)) {
+ const char* dir =
+ cflags_pull_value(tool, argc, argv, i, 10, "-idirafter");
+ if (!dir) return -1;
+ cf->system_include_dirs[cf->nsystem_include_dirs++] = dir;
+ return 1;
+ }
+
if (driver_strneq(a, "-D", 2)) {
const char* arg = cflags_pull_value(tool, argc, argv, i, 2, "-D");
if (!arg) return -1;
diff --git a/driver/driver.h b/driver/driver.h
@@ -142,6 +142,10 @@ CfreeTarget driver_host_target(void);
* unrecognized arch or NULL inputs. */
int driver_target_from_triple(const char *triple, CfreeTarget *out);
+/* Render a canonical driver target triple into `buf`. Returns 0 on success,
+ * nonzero when `buf` is too small. */
+int driver_target_to_triple(CfreeTarget target, char *buf, size_t cap);
+
/* ----------------------------------------------------------------------
* Host-shim helpers
*
diff --git a/driver/ld.c b/driver/ld.c
@@ -107,6 +107,7 @@ typedef struct LdOptions {
int new_dtags; /* 1=DT_RUNPATH (default), 0=DT_RPATH */
int export_dynamic; /* -E / --export-dynamic */
int gc_sections; /* --gc-sections / --no-gc-sections */
+ int allow_undefined; /* shared output undefined-symbol policy */
/* --build-id state */
uint8_t build_id_mode; /* CfreeBuildIdMode */
@@ -190,6 +191,8 @@ void driver_help_ld(void) {
" --no-gc-sections Disable section GC (default)\n"
" -E, --export-dynamic Promote defined globals into dynsym\n"
" (no-op for -shared; recorded for exe)\n"
+ " --no-undefined Reject unresolved symbols in -shared output\n"
+ " -z defs Same as --no-undefined\n"
"\n"
"BUILD ID\n"
" --build-id Same as --build-id=sha256\n"
@@ -221,6 +224,7 @@ static int ld_alloc_arrays(LdOptions* o, int argc) {
return 1;
}
o->new_dtags = 1;
+ o->allow_undefined = 1;
return 0;
}
@@ -382,6 +386,28 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
o->output_seen = 1;
continue;
}
+ if ((val = arg_eq_value(a, "--output")) != NULL) {
+ if (o->output_seen) {
+ driver_errf(LD_TOOL, "-o specified more than once");
+ return 1;
+ }
+ o->output_path = val;
+ o->output_seen = 1;
+ continue;
+ }
+ if (driver_streq(a, "--output")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--output requires an argument");
+ return 1;
+ }
+ if (o->output_seen) {
+ driver_errf(LD_TOOL, "-o specified more than once");
+ return 1;
+ }
+ o->output_path = argv[i];
+ o->output_seen = 1;
+ continue;
+ }
if (driver_streq(a, "-e")) {
if (++i >= argc) {
driver_errf(LD_TOOL, "-e requires an argument");
@@ -390,6 +416,18 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
o->entry = argv[i];
continue;
}
+ if ((val = arg_eq_value(a, "--entry")) != NULL) {
+ o->entry = val;
+ continue;
+ }
+ if (driver_streq(a, "--entry")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--entry requires an argument");
+ return 1;
+ }
+ o->entry = argv[i];
+ continue;
+ }
if (driver_streq(a, "-T")) {
if (++i >= argc) {
driver_errf(LD_TOOL, "-T requires an argument");
@@ -398,6 +436,18 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
o->script_path = argv[i];
continue;
}
+ if ((val = arg_eq_value(a, "--script")) != NULL) {
+ o->script_path = val;
+ continue;
+ }
+ if (driver_streq(a, "--script")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--script requires an argument");
+ return 1;
+ }
+ o->script_path = argv[i];
+ continue;
+ }
if (driver_strneq(a, "-L", 2)) {
const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
@@ -408,6 +458,18 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
o->lib_dirs[o->nlib_dirs++] = dir;
continue;
}
+ if ((val = arg_eq_value(a, "--library-path")) != NULL) {
+ o->lib_dirs[o->nlib_dirs++] = val;
+ continue;
+ }
+ if (driver_streq(a, "--library-path")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--library-path requires an argument");
+ return 1;
+ }
+ o->lib_dirs[o->nlib_dirs++] = argv[i];
+ continue;
+ }
if (driver_strneq(a, "-l", 2)) {
const char* name = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
@@ -435,6 +497,48 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
}
continue;
}
+ if ((val = arg_eq_value(a, "--library")) != NULL) {
+ char* resolved;
+ size_t resolved_size;
+ LibResolveKind kind;
+ LibResolveMode mode =
+ (o->cur_link_mode == CFREE_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY
+ : LIB_RESOLVE_DYNAMIC_PREFER;
+ if (driver_lib_resolve(o->env, val, mode, o->lib_dirs, o->nlib_dirs,
+ &resolved, &resolved_size, &kind) != 0) {
+ driver_errf(LD_TOOL, "cannot find -l%s", val);
+ return 1;
+ }
+ if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
+ ld_push_dso(o, resolved, 1, resolved_size);
+ } else {
+ ld_push_archive(o, resolved, 1, resolved_size);
+ }
+ continue;
+ }
+ if (driver_streq(a, "--library")) {
+ char* resolved;
+ size_t resolved_size;
+ LibResolveKind kind;
+ LibResolveMode mode;
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--library requires an argument");
+ return 1;
+ }
+ mode = (o->cur_link_mode == CFREE_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY
+ : LIB_RESOLVE_DYNAMIC_PREFER;
+ if (driver_lib_resolve(o->env, argv[i], mode, o->lib_dirs, o->nlib_dirs,
+ &resolved, &resolved_size, &kind) != 0) {
+ driver_errf(LD_TOOL, "cannot find -l%s", argv[i]);
+ return 1;
+ }
+ if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
+ ld_push_dso(o, resolved, 1, resolved_size);
+ } else {
+ ld_push_archive(o, resolved, 1, resolved_size);
+ }
+ continue;
+ }
if (driver_streq(a, "-static")) {
o->target.pic = CFREE_PIC_NONE;
@@ -530,6 +634,26 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
o->export_dynamic = 1;
continue;
}
+ if (driver_streq(a, "--no-undefined")) {
+ o->allow_undefined = 0;
+ continue;
+ }
+ if (driver_streq(a, "--allow-shlib-undefined")) {
+ o->allow_undefined = 1;
+ continue;
+ }
+ if (driver_streq(a, "-z")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "-z requires an argument");
+ return 1;
+ }
+ if (driver_streq(argv[i], "defs")) {
+ o->allow_undefined = 0;
+ continue;
+ }
+ driver_errf(LD_TOOL, "unsupported -z option: %s", argv[i]);
+ return 1;
+ }
if (driver_streq(a, "--whole-archive")) {
o->cur_whole_archive = 1;
@@ -881,9 +1005,9 @@ static int ld_run_link(LdOptions* o) {
shared_opts.rpaths = o->rpaths;
shared_opts.nrpaths = o->nrpaths;
}
- /* allow_undefined defaults to 1 for shared output (the produced
- * object resolves against its loader at runtime). */
- shared_opts.allow_undefined = 1;
+ /* By default shared output may resolve symbols against its loader at
+ * runtime; --no-undefined / -z defs tightens that policy. */
+ shared_opts.allow_undefined = o->allow_undefined ? 1 : 0;
shared_opts.gc_sections = o->gc_sections;
(void)o->export_dynamic;
rc = cfree_link_shared(compiler, &shared_opts, writer) == CFREE_OK ? 0 : 1;
diff --git a/driver/objdump.c b/driver/objdump.c
@@ -17,6 +17,7 @@
#define MAX_J_FILTERS 16
typedef struct ObjdumpOpts {
+ int f; /* -f: file header */
int h; /* -h: section headers */
int t; /* -t: symbol table */
int d; /* -d: disasm exec sections */
@@ -52,6 +53,7 @@ void driver_help_objdump(void) {
" symbol table), matching GNU objdump's default-ish behaviour.\n"
"\n"
"OPERATIONS (any combination)\n"
+ " -f Print the file header\n"
" -h Print section headers (idx, name, size, align,\n"
" flags). NOTE: this is the GNU objdump meaning of\n"
" -h — it does NOT print this help; use --help.\n"
@@ -60,6 +62,7 @@ void driver_help_objdump(void) {
" -D Disassemble all sections\n"
" -r Print relocation records\n"
" -s Print section contents as a hex+ASCII dump\n"
+ " -x Aggregate: -f -h -r -t\n"
"\n"
"FILTERS\n"
" -j NAME Restrict output to the named section. Repeatable;\n"
@@ -336,6 +339,11 @@ static void dump_disasm(const CfreeDisasmContext* dctx, CfreeObjFile* f,
const ObjdumpOpts* opts) {
uint32_t nsec = cfree_obj_nsections(f);
uint32_t i;
+ CfreeDisasmContext file_dctx;
+
+ if (!dctx) return;
+ file_dctx = *dctx;
+ file_dctx.target = cfree_obj_target(f);
for (i = 0; i < nsec; ++i) {
CfreeObjSecInfo sec;
@@ -356,7 +364,7 @@ static void dump_disasm(const CfreeDisasmContext* dctx, CfreeObjFile* f,
driver_printf("Disassembly of section %s:\n\n",
sec.name[0] ? sec.name : "(anon)");
- if (cfree_disasm_iter_new(dctx, data, len, 0, f, &dis) != CFREE_OK)
+ if (cfree_disasm_iter_new(&file_dctx, data, len, 0, f, &dis) != CFREE_OK)
continue;
for (;;) {
CfreeIterResult r = cfree_disasm_iter_next(dis, &insn);
@@ -446,6 +454,9 @@ static int parse_short_flags(const char* arg, ObjdumpOpts* o) {
const char* p;
for (p = arg + 1; *p; ++p) {
switch (*p) {
+ case 'f':
+ o->f = 1;
+ break;
case 'h':
o->h = 1;
break;
@@ -464,6 +475,12 @@ static int parse_short_flags(const char* arg, ObjdumpOpts* o) {
case 's':
o->s = 1;
break;
+ case 'x':
+ o->f = 1;
+ o->h = 1;
+ o->r = 1;
+ o->t = 1;
+ break;
default:
driver_errf(OBJDUMP_TOOL, "unknown flag: -%c", *p);
return -1;
@@ -472,6 +489,41 @@ static int parse_short_flags(const char* arg, ObjdumpOpts* o) {
return 0;
}
+static int parse_long_flag(const char* arg, ObjdumpOpts* o) {
+ if (driver_streq(arg, "--file-headers")) {
+ o->f = 1;
+ return 1;
+ }
+ if (driver_streq(arg, "--section-headers")) {
+ o->h = 1;
+ return 1;
+ }
+ if (driver_streq(arg, "--syms")) {
+ o->t = 1;
+ return 1;
+ }
+ if (driver_streq(arg, "--reloc")) {
+ o->r = 1;
+ return 1;
+ }
+ if (driver_streq(arg, "--full-contents")) {
+ o->s = 1;
+ return 1;
+ }
+ if (driver_streq(arg, "--disassemble")) {
+ o->d = 1;
+ return 1;
+ }
+ if (driver_streq(arg, "--all-headers")) {
+ o->f = 1;
+ o->h = 1;
+ o->r = 1;
+ o->t = 1;
+ return 1;
+ }
+ return 0;
+}
+
int driver_objdump(int argc, char** argv) {
DriverEnv env;
ObjdumpOpts opts = {0};
@@ -511,6 +563,7 @@ int driver_objdump(int argc, char** argv) {
opts.j[opts.nj++] = argv[++i];
continue;
}
+ if (parse_long_flag(a, &opts)) continue;
if (parse_short_flags(a, &opts) != 0) {
objdump_usage();
rc = 2;
@@ -518,7 +571,7 @@ int driver_objdump(int argc, char** argv) {
}
}
- saw_op = opts.h || opts.t || opts.d || opts.D || opts.r || opts.s;
+ saw_op = opts.f || opts.h || opts.t || opts.d || opts.D || opts.r || opts.s;
if (!saw_op) { /* Default = -h -t (matches the prior behavior). */
opts.h = 1;
opts.t = 1;
diff --git a/driver/target.c b/driver/target.c
@@ -1,5 +1,6 @@
#include <stddef.h>
#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include "driver.h"
@@ -119,3 +120,62 @@ int driver_target_from_triple(const char* triple, CfreeTarget* out) {
*out = t;
return 0;
}
+
+int driver_target_to_triple(CfreeTarget target, char* buf, size_t cap) {
+ const char* arch;
+ const char* os;
+ int n;
+ if (!buf || cap == 0) return 1;
+
+ switch (target.arch) {
+ case CFREE_ARCH_X86_64:
+ arch = "x86_64";
+ break;
+ case CFREE_ARCH_X86_32:
+ arch = "i386";
+ break;
+ case CFREE_ARCH_ARM_64:
+ arch = "aarch64";
+ break;
+ case CFREE_ARCH_ARM_32:
+ arch = "arm";
+ break;
+ case CFREE_ARCH_RV64:
+ arch = "riscv64";
+ break;
+ case CFREE_ARCH_RV32:
+ arch = "riscv32";
+ break;
+ case CFREE_ARCH_WASM:
+ arch = target.ptr_size == 8 ? "wasm64" : "wasm32";
+ break;
+ default:
+ arch = "unknown";
+ break;
+ }
+
+ switch (target.os) {
+ case CFREE_OS_LINUX:
+ os = "linux";
+ break;
+ case CFREE_OS_MACOS:
+ os = "apple-darwin";
+ break;
+ case CFREE_OS_WINDOWS:
+ os = "windows";
+ break;
+ case CFREE_OS_FREEBSD:
+ os = "freebsd";
+ break;
+ case CFREE_OS_WASI:
+ os = "wasi";
+ break;
+ case CFREE_OS_FREESTANDING:
+ default:
+ os = "elf";
+ break;
+ }
+
+ n = snprintf(buf, cap, "%s-%s", arch, os);
+ return n < 0 || (size_t)n >= cap;
+}
diff --git a/test/driver/run.sh b/test/driver/run.sh
@@ -179,6 +179,28 @@ else
fail=$((fail + 1))
fi
+if "$CFREE" cc -target riscv64-linux -dumpmachine \
+ > "$work/cc-dumpmachine.out" 2> "$work/cc-dumpmachine.err" &&
+ [ "$(cat "$work/cc-dumpmachine.out")" = "riscv64-linux" ]; then
+ printf 'PASS %s\n' "cc-dumpmachine-probe"
+ pass=$((pass + 1))
+else
+ printf 'FAIL %s\n' "cc-dumpmachine-probe"
+ sed 's/^/ | /' "$work/cc-dumpmachine.err"
+ fail=$((fail + 1))
+fi
+
+if "$CFREE" cc -print-file-name=crt1.o \
+ > "$work/cc-print-file-name.out" 2> "$work/cc-print-file-name.err" &&
+ [ "$(cat "$work/cc-print-file-name.out")" = "crt1.o" ]; then
+ printf 'PASS %s\n' "cc-print-file-name-probe"
+ pass=$((pass + 1))
+else
+ printf 'FAIL %s\n' "cc-print-file-name-probe"
+ sed 's/^/ | /' "$work/cc-print-file-name.err"
+ fail=$((fail + 1))
+fi
+
if "$CFREE" cc -Wall -Wextra -std=c11 -ffreestanding -c "$work/main.c" \
-o "$work/ignored.o" > "$work/cc-ignored.out" 2> "$work/cc-ignored.err"; then
if grep -q "ignoring accepted compatibility flag: -Wall" "$work/cc-ignored.err" &&
@@ -196,6 +218,36 @@ else
fail=$((fail + 1))
fi
+if "$CFREE" cc -c "$work/main.c" --output="$work/cc-long-output.o" \
+ > "$work/cc-long-output.out" 2> "$work/cc-long-output.err" &&
+ [ -f "$work/cc-long-output.o" ]; then
+ printf 'PASS %s\n' "cc-long-output"
+ pass=$((pass + 1))
+else
+ printf 'FAIL %s\n' "cc-long-output"
+ sed 's/^/ | /' "$work/cc-long-output.err"
+ fail=$((fail + 1))
+fi
+
+mkdir -p "$work/quote-inc"
+cat > "$work/quote-inc/q.h" <<'SRC'
+#define Q_VALUE 7
+SRC
+cat > "$work/quote-include.c" <<'SRC'
+#include "q.h"
+int q(void) { return Q_VALUE; }
+SRC
+if "$CFREE" cc -c "$work/quote-include.c" -iquote "$work/quote-inc" \
+ -o "$work/quote-include.o" \
+ > "$work/quote-include.out" 2> "$work/quote-include.err"; then
+ printf 'PASS %s\n' "cc-iquote-include"
+ pass=$((pass + 1))
+else
+ printf 'FAIL %s\n' "cc-iquote-include"
+ sed 's/^/ | /' "$work/quote-include.err"
+ fail=$((fail + 1))
+fi
+
cat > "$work/implicit-header.c" <<'SRC'
#include <stddef.h>
#include <stdint.h>
@@ -237,6 +289,51 @@ else
fail=$((fail + 1))
fi
+if "$CFREE" ld "$work/main.o" --output="$work/ld-long-output" \
+ > "$work/ld-long-output.out" 2> "$work/ld-long-output.err" &&
+ [ -f "$work/ld-long-output" ]; then
+ printf 'PASS %s\n' "ld-long-output"
+ pass=$((pass + 1))
+else
+ printf 'FAIL %s\n' "ld-long-output"
+ sed 's/^/ | /' "$work/ld-long-output.err"
+ fail=$((fail + 1))
+fi
+
+cat > "$work/shared-undef.c" <<'SRC'
+int missing(void);
+int f(void) { return missing(); }
+SRC
+if "$CFREE" cc -target x86_64-linux -fPIC -c "$work/shared-undef.c" \
+ -o "$work/shared-undef.o" > "$work/shared-undef-cc.out" \
+ 2> "$work/shared-undef-cc.err"; then
+ if ! "$CFREE" ld -shared --no-undefined "$work/shared-undef.o" \
+ -o "$work/shared-undef.so" > "$work/shared-undef-ld.out" \
+ 2> "$work/shared-undef-ld.err"; then
+ printf 'PASS %s\n' "ld-no-undefined"
+ pass=$((pass + 1))
+ else
+ printf 'FAIL %s (link unexpectedly succeeded)\n' "ld-no-undefined"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL %s (setup compile failed)\n' "ld-no-undefined"
+ sed 's/^/ | /' "$work/shared-undef-cc.err"
+ fail=$((fail + 1))
+fi
+
+if "$CFREE" objdump -x "$work/main.o" \
+ > "$work/objdump-x.out" 2> "$work/objdump-x.err" &&
+ grep -q "Sections:" "$work/objdump-x.out" &&
+ grep -q "SYMBOL TABLE:" "$work/objdump-x.out"; then
+ printf 'PASS %s\n' "objdump-x-aggregate"
+ pass=$((pass + 1))
+else
+ printf 'FAIL %s\n' "objdump-x-aggregate"
+ sed 's/^/ | /' "$work/objdump-x.err"
+ fail=$((fail + 1))
+fi
+
cat > "$work/run-main.c" <<'SRC'
int add42(int);
int main(void) { return add42(0); }