commit fd83f1e6567d8dc8866ec4be7512b51625564627
parent da4ace5a31d7aba4dc1236405908b350ca25897e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 19 May 2026 18:23:06 -0700
Broaden driver compatibility
Diffstat:
8 files changed, 1147 insertions(+), 131 deletions(-)
diff --git a/doc/HOSTED.md b/doc/HOSTED.md
@@ -0,0 +1,220 @@
+# Hosted Linking Plan
+
+This document describes the planned split between compiler support runtime
+linking and hosted C library linking in the `cfree cc` driver.
+
+## Goals
+
+- Keep freestanding links freestanding by default.
+- Always provide compiler runtime support needed by code generated by cfree.
+- Add hosted C runtime and libc inputs only when the user explicitly asks for
+ libc, currently by passing `-lc`.
+- Keep platform-specific hosted knowledge isolated in a small, pluggable driver
+ module. No platform paths should be scattered through `cc.c`, `ld.c`, or
+ linker internals.
+- Make every platform path come from an explicit support layout or configured
+ sysroot/SDK, not from implicit host filesystem probing.
+
+## Conceptual Split
+
+There are two distinct runtime classes.
+
+### Compiler Runtime
+
+`libcfree_rt.a` is compiler support code: integer helpers, soft-float helpers,
+memory helpers, atomics, coroutine/runtime helpers, and similar functions that
+generated code or compiler lowering may require.
+
+This is not hosted libc. It should be linked automatically by `cfree cc` during
+final link, independent of `-lc`.
+
+Rules:
+
+- `cfree cc` adds `libcfree_rt.a` for link actions.
+- `cfree cc -c`, `-E`, dependency-only, and token-dump actions do not add it.
+- `cfree ld` remains explicit and does not auto-add compiler runtime unless a
+ future deliberate option such as `--compiler-rt=auto` is added.
+- The runtime archive is placed late in the ordered link inputs, after user
+ objects and libraries, so demand-loaded helpers satisfy unresolved references.
+
+### Hosted C Runtime And Libc
+
+Hosted linking means startup files, hosted ABI shim objects, libc, and dynamic
+loader settings.
+
+This is opt-in. Passing `-lc` requests hosted libc expansion. Without `-lc`,
+`cfree cc` must not add crt objects, hosted shims, libc, or platform dynamic
+loader policy.
+
+Rules:
+
+- `-lc` is recognized specially by the `cc` driver.
+- Other `-lfoo` inputs keep normal ordered library resolution.
+- `-nostdlib` and `-nodefaultlibs` should suppress hosted expansion. The exact
+ policy can be either strict diagnostic or "treat `-lc` as a normal library";
+ the preferred initial behavior is to avoid implicit hosted inputs.
+- Hosted inputs must preserve link order:
+ startup objects before user objects, libc/runtime support after user objects,
+ and trailing finalization objects last.
+
+## Distribution Layout
+
+The intended distribution model is a single directory with the `cfree` binary at
+the root and a sibling support directory:
+
+```text
+cfree
+support/
+ rt/
+ include/
+ lib/
+```
+
+The driver should be able to locate this support root from the installed binary
+path. A command-line override such as `--support-dir DIR` should be available
+for tests, development trees, and unusual packaging.
+
+## Runtime Build Cache
+
+`cfree cc` should compile and cache `libcfree_rt.a` for the target on demand.
+
+Proposed cache layout:
+
+```text
+support/cache/<target-triple>/
+ libcfree_rt.a
+ obj/
+```
+
+The cache builder belongs in a dedicated driver module, for example
+`driver/runtime.c` with `driver/runtime.h`.
+
+Responsibilities:
+
+- Resolve the support directory.
+- Map a `CfreeTarget` to a stable target cache key/triple.
+- Check for an existing runtime archive.
+- Build missing or stale runtime objects for the target.
+- Archive them into `libcfree_rt.a`.
+- Return a concrete path to the archive for insertion into ordered link inputs.
+
+Failure should produce diagnostics that name the target and the support path:
+
+```text
+cc: compiler runtime for aarch64-linux not found and could not be built
+cc: support dir: /path/to/support
+```
+
+## Hosted Resolver
+
+Hosted platform policy belongs in a separate driver module, for example
+`driver/hosted.c` with `driver/hosted.h`.
+
+`cc.c` should not know about `libSystem`, musl crt names, glibc loader names, or
+hosted shim paths. It should only pass target, configured roots, and the current
+ordered input list to the hosted resolver.
+
+The hosted resolver returns concrete ordered additions and link options.
+
+Conceptual profile shape:
+
+```c
+typedef struct HostedProfile {
+ const char* name;
+ CfreeOSKind os;
+ CfreeObjFmt obj;
+ const char* crt1;
+ const char* crti;
+ const char* crtn;
+ const char* libc;
+ const char* libc_nonshared;
+ const char* hosted_shim;
+ const char* interp;
+ bool static_link;
+ bool pie;
+} HostedProfile;
+```
+
+The actual implementation may use a richer structured representation, but the
+important property is locality: adding or changing hosted platform support
+should mean editing `driver/hosted.c`, not the compiler driver or linker.
+
+## Initial Hosted Profiles
+
+### macOS / libSystem
+
+When `-lc` is present for a Darwin/Mach-O target:
+
+- Add `libcfree_hosted_macos.a`.
+- Add the configured SDK/sysroot `libSystem` stub or dylib.
+- Do not add libSystem without `-lc`.
+
+The SDK path should come from explicit configuration, for example `--sysroot` or
+an equivalent support profile setting. The driver should not silently search
+arbitrary host SDK locations.
+
+### Linux / musl
+
+When `-lc` is present for a Linux/ELF target and the configured sysroot contains
+musl static libc:
+
+- Prefer static musl if `libc.a` is available.
+- Add startup and finalization objects in normal musl order:
+
+```text
+crt1.o crti.o user-inputs hosted-shim libc.a libcfree_rt.a crtn.o
+```
+
+For a future dynamic musl profile:
+
+```text
+Scrt1.o crti.o user-inputs hosted-shim libc.so libcfree_rt.a crtn.o
+```
+
+and set the musl interpreter from the profile.
+
+### Linux / glibc
+
+glibc hosted linking is dynamic-only initially.
+
+When `-lc` is present for a Linux/ELF target and the configured sysroot contains
+glibc:
+
+```text
+Scrt1.o crti.o user-inputs hosted-shim libc.so.6 libc_nonshared.a
+libcfree_rt.a crtn.o
+```
+
+The profile must set the dynamic loader explicitly, for example:
+
+```text
+/lib/ld-linux-aarch64.so.1
+/lib64/ld-linux-x86-64.so.2
+/lib/ld-linux-riscv64-lp64d.so.1
+```
+
+These values belong in the hosted profile table.
+
+## Linker Boundary
+
+The linker should consume explicit ordered inputs and explicit link options.
+It should not infer hosted policy.
+
+Existing linker defaults for platform loaders or system libraries should be
+treated as compatibility fallbacks only. The driver should pass explicit
+interpreter and DSO inputs when hosted linking is requested.
+
+## Test Strategy
+
+Start with focused driver tests before broad libc conformance runs.
+
+- No `-lc`: no hosted inputs are added.
+- Link mode through `cfree cc` adds `libcfree_rt.a` automatically.
+- `-lc` on macOS expands through the macOS hosted profile.
+- `-lc` on Linux/musl chooses static musl when `libc.a` exists.
+- `-lc` on Linux/glibc chooses dynamic glibc and sets the profile interpreter.
+- Missing sysroot/support files diagnose the missing concrete file and profile.
+- Ordered inputs preserve normal linker behavior around archives and libc.
+
+The existing `test/libc` sysroot harnesses are good end-to-end validation once
+the small resolver tests are green.
diff --git a/driver/ar.c b/driver/ar.c
@@ -25,6 +25,8 @@
* Each member is opened with cfree_obj_open and its globally-
* defined symbols (CFREE_SB_GLOBAL with section != NONE) are
* indexed; non-object members contribute no symbols.
+ * u update only if member is newer. Accepted as a compatibility
+ * modifier and currently treated as a no-op.
* v verbose. With t, list each member as "<size>\t<name>" instead
* of name-only. With x, print "x - <name>" per extracted member.
* With p, prepend a "<archive>(<name>):\n" header to every member
@@ -78,6 +80,8 @@ void driver_help_ar(void) {
" rcs, crs). Globally-defined symbols (CFREE_SB_GLOBAL with\n"
" a defined section) of each object member are indexed; non-\n"
" object members contribute no symbols.\n"
+ " u Update-if-newer compatibility modifier; accepted as a\n"
+ " no-op.\n"
" v Verbose. With t list <size>\\t<name>; with x/r/c print one\n"
" line per affected member (\"x - name\", \"a - name\", or\n"
" \"r - name\" for replacements); with p force the per-member\n"
@@ -638,6 +642,8 @@ int driver_ar(int argc, char** argv) {
case 'v':
has_v = 1;
break;
+ case 'u':
+ break;
case 't':
do_list = 1;
break;
diff --git a/driver/cc.c b/driver/cc.c
@@ -1,13 +1,13 @@
-#include <stdint.h>
-
-#include "cflags.h"
-#include "driver.h"
#include "lang/c/c.h"
-#include "lib_resolve.h"
#include <cfree/compile.h>
#include <cfree/core.h>
#include <cfree/link.h>
+#include <stdint.h>
+
+#include "cflags.h"
+#include "driver.h"
+#include "lib_resolve.h"
/* `cfree cc` — C compiler driver. With -c produces a single object;
* without -c compiles all C sources, links any .o/.a inputs alongside, and
@@ -48,6 +48,46 @@ typedef enum CcDepMode {
CC_DEP_MMD,
} CcDepMode;
+typedef enum CcLinkItemKind {
+ CC_LINK_SOURCE_FILE,
+ CC_LINK_SOURCE_MEMORY,
+ CC_LINK_OBJECT,
+ CC_LINK_ARCHIVE,
+ CC_LINK_DSO,
+ CC_LINK_LIB,
+} CcLinkItemKind;
+
+typedef struct CcLinkItem {
+ uint8_t kind; /* CcLinkItemKind */
+ uint8_t pad[3];
+ uint32_t index;
+} CcLinkItem;
+
+typedef struct CcArchiveInput {
+ const char* path;
+ int owned;
+ size_t owned_size;
+ uint8_t whole_archive;
+ uint8_t link_mode;
+ uint8_t group_id;
+ uint8_t pad;
+} CcArchiveInput;
+
+typedef struct CcDsoInput {
+ const char* path;
+ int owned;
+ size_t owned_size;
+} CcDsoInput;
+
+typedef struct CcPendingLib {
+ const char* name;
+ uint8_t whole_archive;
+ uint8_t link_mode;
+ uint8_t group_id;
+ uint8_t resolved_kind; /* CcLinkItemKind: ARCHIVE or DSO */
+ uint32_t resolved_index;
+} CcPendingLib;
+
typedef struct CcOptions {
DriverEnv* env;
size_t argv_bound; /* upper bound on per-array list size */
@@ -63,6 +103,8 @@ typedef struct CcOptions {
int target_set; /* did -target appear */
const char* output_path; /* -o */
int output_path_set;
+ char* owned_output_path;
+ size_t owned_output_path_size;
const char* entry; /* -e */
const char* linker_script; /* -T path */
@@ -87,6 +129,7 @@ typedef struct CcOptions {
/* Positional inputs split by suffix. */
const char** source_files; /* .c paths */
+ CfreeLanguage* source_langs;
uint32_t nsource_files;
CfreeSourceInput* source_memory; /* "-" stdin slurp */
uint32_t nsource_memory;
@@ -94,23 +137,22 @@ typedef struct CcOptions {
size_t stdin_size;
const char** object_files; /* .o/.obj paths */
uint32_t nobject_files;
- const char** archives; /* .a paths + resolved -l names */
+ CcArchiveInput* archives; /* .a paths + resolved -l names */
uint32_t narchives;
- /* Sub-list of `archives[]` whose buffers are heap-owned (resolved -l).
- * Their (path, size) sit in parallel arrays for free(). */
- char** owned_archives;
- size_t* owned_archive_sizes;
- uint32_t nowned_archives;
- /* DSO inputs (.so / .dylib / .tbd). Always heap-owned by cc. */
- char** dsos;
- size_t* dso_sizes;
+ CcDsoInput* dsos;
uint32_t ndsos;
/* -L search paths (argv-borrowed). */
const char** lib_search_paths;
uint32_t nlib_search_paths;
/* Pending -l names (resolved at end-of-parse). */
- const char** pending_libs;
+ CcPendingLib* pending_libs;
uint32_t npending_libs;
+ CcLinkItem* link_items;
+ uint32_t nlink_items;
+ uint8_t cur_whole_archive;
+ uint8_t cur_link_mode;
+ uint8_t cur_group_id;
+ uint8_t next_group_id;
/* -M family */
int dep_mode; /* CcDepMode */
@@ -127,6 +169,10 @@ typedef struct CcOptions {
const char** rpaths; /* -Wl,-rpath=DIR (repeatable) */
uint32_t nrpaths;
int new_dtags; /* 1=DT_RUNPATH (default), 0=DT_RPATH */
+ int static_link;
+ int pie;
+ int gc_sections;
+ const char* interp_path;
} CcOptions;
static void cc_usage(void) {
@@ -160,21 +206,19 @@ static int cc_alloc_arrays(CcOptions* o, int argc) {
o->argv_bound = bound;
o->source_files =
driver_alloc_zeroed(o->env, bound * sizeof(*o->source_files));
+ o->source_langs =
+ driver_alloc_zeroed(o->env, bound * sizeof(*o->source_langs));
o->source_memory =
driver_alloc_zeroed(o->env, bound * sizeof(*o->source_memory));
o->object_files =
driver_alloc_zeroed(o->env, bound * sizeof(*o->object_files));
o->archives = driver_alloc_zeroed(o->env, bound * sizeof(*o->archives));
- o->owned_archives =
- driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_archives));
- o->owned_archive_sizes =
- driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_archive_sizes));
o->dsos = driver_alloc_zeroed(o->env, bound * sizeof(*o->dsos));
- o->dso_sizes = driver_alloc_zeroed(o->env, bound * sizeof(*o->dso_sizes));
o->lib_search_paths =
driver_alloc_zeroed(o->env, bound * sizeof(*o->lib_search_paths));
o->pending_libs =
driver_alloc_zeroed(o->env, bound * sizeof(*o->pending_libs));
+ o->link_items = driver_alloc_zeroed(o->env, bound * sizeof(*o->link_items));
o->dep_targets = driver_alloc_zeroed(o->env, bound * sizeof(*o->dep_targets));
o->path_map = driver_alloc_zeroed(o->env, bound * sizeof(*o->path_map));
o->owned_path_map_olds =
@@ -182,16 +226,15 @@ static int cc_alloc_arrays(CcOptions* o, int argc) {
o->owned_path_map_old_sizes =
driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_map_old_sizes));
o->rpaths = driver_alloc_zeroed(o->env, bound * sizeof(*o->rpaths));
- if (!o->source_files || !o->source_memory || !o->object_files ||
- !o->archives || !o->owned_archives || !o->owned_archive_sizes ||
- !o->dsos || !o->dso_sizes ||
- !o->lib_search_paths || !o->pending_libs || !o->dep_targets ||
- !o->path_map || !o->owned_path_map_olds || !o->owned_path_map_old_sizes ||
- !o->rpaths) {
+ if (!o->source_files || !o->source_langs || !o->source_memory ||
+ !o->object_files || !o->archives || !o->dsos || !o->lib_search_paths ||
+ !o->pending_libs || !o->link_items || !o->dep_targets || !o->path_map ||
+ !o->owned_path_map_olds || !o->owned_path_map_old_sizes || !o->rpaths) {
driver_errf(CC_TOOL, "out of memory");
return 1;
}
o->new_dtags = 1;
+ o->cur_link_mode = CFREE_LM_DEFAULT;
if (driver_cflags_init(&o->cf, o->env, argc) != 0) {
driver_errf(CC_TOOL, "out of memory");
return 1;
@@ -202,11 +245,16 @@ static int cc_alloc_arrays(CcOptions* o, int argc) {
static void cc_options_release(CcOptions* o) {
uint32_t i;
size_t bound = o->argv_bound;
- for (i = 0; i < o->nowned_archives; ++i) {
- driver_free(o->env, o->owned_archives[i], o->owned_archive_sizes[i]);
+ for (i = 0; i < o->narchives; ++i) {
+ if (o->archives[i].owned) {
+ driver_free(o->env, (void*)o->archives[i].path,
+ o->archives[i].owned_size);
+ }
}
for (i = 0; i < o->ndsos; ++i) {
- driver_free(o->env, o->dsos[i], o->dso_sizes[i]);
+ if (o->dsos[i].owned) {
+ driver_free(o->env, (void*)o->dsos[i].path, o->dsos[i].owned_size);
+ }
}
for (i = 0; i < o->npath_map; ++i) {
if (o->owned_path_map_olds[i]) {
@@ -217,19 +265,19 @@ static void cc_options_release(CcOptions* o) {
if (o->stdin_buf) driver_free(o->env, o->stdin_buf, o->stdin_size);
if (o->build_id_bytes)
driver_free(o->env, o->build_id_bytes, o->build_id_len);
+ if (o->owned_output_path)
+ driver_free(o->env, o->owned_output_path, o->owned_output_path_size);
driver_cflags_fini(&o->cf, o->env);
driver_free(o->env, o->source_files, bound * sizeof(*o->source_files));
+ driver_free(o->env, o->source_langs, bound * sizeof(*o->source_langs));
driver_free(o->env, o->source_memory, bound * sizeof(*o->source_memory));
driver_free(o->env, o->object_files, bound * sizeof(*o->object_files));
driver_free(o->env, o->archives, bound * sizeof(*o->archives));
- driver_free(o->env, o->owned_archives, bound * sizeof(*o->owned_archives));
- driver_free(o->env, o->owned_archive_sizes,
- bound * sizeof(*o->owned_archive_sizes));
driver_free(o->env, o->dsos, bound * sizeof(*o->dsos));
- driver_free(o->env, o->dso_sizes, bound * sizeof(*o->dso_sizes));
driver_free(o->env, o->lib_search_paths,
bound * sizeof(*o->lib_search_paths));
driver_free(o->env, o->pending_libs, bound * sizeof(*o->pending_libs));
+ driver_free(o->env, o->link_items, bound * sizeof(*o->link_items));
driver_free(o->env, o->dep_targets, bound * sizeof(*o->dep_targets));
driver_free(o->env, o->path_map, bound * sizeof(*o->path_map));
driver_free(o->env, o->owned_path_map_olds,
@@ -239,15 +287,41 @@ static void cc_options_release(CcOptions* o) {
driver_free(o->env, o->rpaths, bound * sizeof(*o->rpaths));
}
+static char* cc_dup_span(DriverEnv* env, const char* s, size_t n) {
+ char* buf = driver_alloc(env, n + 1u);
+ if (!buf) return NULL;
+ driver_memcpy(buf, s, n);
+ buf[n] = '\0';
+ return buf;
+}
+
+static int cc_record_build_id(CcOptions* o, const char* val);
+
/* Parse a single GCC-style -Wl,X[,Y...] pass-through argument. */
static int cc_record_wl(CcOptions* o, const char* arg) {
const char* p = arg;
+ int expect_rpath = 0;
+ int expect_soname = 0;
+ int expect_interp = 0;
while (*p) {
const char* tok = p;
size_t n = 0;
while (p[n] && p[n] != ',') ++n;
p = tok + n + (tok[n] == ',' ? 1 : 0);
+ if (expect_rpath || expect_soname || expect_interp) {
+ char* buf = cc_dup_span(o->env, tok, n);
+ if (!buf) {
+ driver_errf(CC_TOOL, "out of memory");
+ return 1;
+ }
+ if (expect_rpath) o->rpaths[o->nrpaths++] = buf;
+ if (expect_soname) o->soname = buf;
+ if (expect_interp) o->interp_path = buf;
+ expect_rpath = expect_soname = expect_interp = 0;
+ continue;
+ }
+
if (n >= 8 && driver_strneq(tok, "-soname=", 8)) {
char* buf;
size_t bufsz = n - 8 + 1;
@@ -261,6 +335,10 @@ static int cc_record_wl(CcOptions* o, const char* arg) {
o->soname = buf;
continue;
}
+ if (n == 7 && driver_strneq(tok, "-soname", 7)) {
+ expect_soname = 1;
+ continue;
+ }
if (n >= 7 && driver_strneq(tok, "-rpath=", 7)) {
char* buf;
size_t bufsz = n - 7 + 1;
@@ -274,6 +352,22 @@ static int cc_record_wl(CcOptions* o, const char* arg) {
o->rpaths[o->nrpaths++] = buf;
continue;
}
+ if (n == 6 && driver_strneq(tok, "-rpath", 6)) {
+ expect_rpath = 1;
+ continue;
+ }
+ if (n >= 16 && driver_strneq(tok, "-dynamic-linker=", 16)) {
+ o->interp_path = cc_dup_span(o->env, tok + 16, n - 16);
+ if (!o->interp_path) {
+ driver_errf(CC_TOOL, "out of memory");
+ return 1;
+ }
+ continue;
+ }
+ if (n == 15 && driver_strneq(tok, "-dynamic-linker", 15)) {
+ expect_interp = 1;
+ continue;
+ }
if (n == 18 && driver_strneq(tok, "--enable-new-dtags", 18)) {
o->new_dtags = 1;
continue;
@@ -282,18 +376,58 @@ static int cc_record_wl(CcOptions* o, const char* arg) {
o->new_dtags = 0;
continue;
}
+ if (n == 13 && driver_strneq(tok, "--gc-sections", 13)) {
+ o->gc_sections = 1;
+ continue;
+ }
+ if (n == 16 && driver_strneq(tok, "--no-gc-sections", 16)) {
+ o->gc_sections = 0;
+ continue;
+ }
+ if (n >= 11 && driver_strneq(tok, "--build-id=", 11)) {
+ char* buf = cc_dup_span(o->env, tok + 11, n - 11);
+ int rc;
+ if (!buf) {
+ driver_errf(CC_TOOL, "out of memory");
+ return 1;
+ }
+ rc = cc_record_build_id(o, buf);
+ driver_free(o->env, buf, n - 11 + 1u);
+ if (rc != 0) return 1;
+ continue;
+ }
+ if (n == 10 && driver_strneq(tok, "--build-id", 10)) {
+ o->build_id_mode = CFREE_BUILDID_SHA256;
+ continue;
+ }
driver_errf(CC_TOOL, "unsupported -Wl, token: %.*s", (int)n, tok);
return 1;
}
+ if (expect_rpath || expect_soname || expect_interp) {
+ driver_errf(CC_TOOL, "-Wl option requires another comma argument");
+ return 1;
+ }
return 0;
}
static int cc_is_c_source(const char* s) {
- return driver_has_suffix(s, ".c") || driver_has_suffix(s, ".toy") ||
+ return driver_has_suffix(s, ".c") || driver_has_suffix(s, ".S") ||
+ driver_has_suffix(s, ".s") || driver_has_suffix(s, ".toy") ||
driver_has_suffix(s, ".wat") || driver_has_suffix(s, ".wasm");
}
+static int cc_is_dso_input(const char* s) {
+ return driver_has_suffix(s, ".so") || driver_has_suffix(s, ".dylib") ||
+ driver_has_suffix(s, ".tbd");
+}
+
+static void cc_push_link_item(CcOptions* o, uint8_t kind, uint32_t index) {
+ CcLinkItem* it = &o->link_items[o->nlink_items++];
+ it->kind = kind;
+ it->index = index;
+}
+
static int cc_parse_u64(const char* s, uint64_t* out) {
uint64_t v = 0;
int any = 0;
@@ -420,6 +554,10 @@ static int cc_record_mcmodel(CcOptions* o, const char* val) {
return 1;
}
+static void cc_log_ignored(const char* arg) {
+ driver_errf(CC_TOOL, "ignoring accepted compatibility flag: %s", arg);
+}
+
static int cc_record_stdin(CcOptions* o) {
CfreeSourceInput* in;
if (o->stdin_buf) {
@@ -435,21 +573,44 @@ static int cc_record_stdin(CcOptions* o) {
in->bytes.data = o->stdin_buf;
in->bytes.len = o->stdin_size;
in->lang = CFREE_LANG_C;
+ cc_push_link_item(o, CC_LINK_SOURCE_MEMORY, o->nsource_memory - 1u);
return 0;
}
-static int cc_classify_positional(CcOptions* o, const char* a) {
+static CfreeLanguage cc_lang_for_path_or_forced(const char* path,
+ int forced_lang) {
+ if (forced_lang >= 0) return (CfreeLanguage)forced_lang;
+ return cfree_language_for_path(path);
+}
+
+static int cc_classify_positional(CcOptions* o, const char* a,
+ int forced_lang) {
if (driver_streq(a, "-")) return cc_record_stdin(o);
- if (cc_is_c_source(a)) {
+ if (forced_lang >= 0 || cc_is_c_source(a)) {
+ o->source_langs[o->nsource_files] =
+ cc_lang_for_path_or_forced(a, forced_lang);
o->source_files[o->nsource_files++] = a;
+ cc_push_link_item(o, CC_LINK_SOURCE_FILE, o->nsource_files - 1u);
return 0;
}
if (driver_has_suffix(a, ".o") || driver_has_suffix(a, ".obj")) {
o->object_files[o->nobject_files++] = a;
+ cc_push_link_item(o, CC_LINK_OBJECT, o->nobject_files - 1u);
return 0;
}
if (driver_has_suffix(a, ".a")) {
- o->archives[o->narchives++] = a;
+ CcArchiveInput* ar = &o->archives[o->narchives++];
+ ar->path = a;
+ ar->whole_archive = o->cur_whole_archive;
+ ar->link_mode = o->cur_link_mode;
+ ar->group_id = o->cur_group_id;
+ cc_push_link_item(o, CC_LINK_ARCHIVE, o->narchives - 1u);
+ return 0;
+ }
+ if (cc_is_dso_input(a)) {
+ CcDsoInput* d = &o->dsos[o->ndsos++];
+ d->path = a;
+ cc_push_link_item(o, CC_LINK_DSO, o->ndsos - 1u);
return 0;
}
driver_errf(CC_TOOL, "input does not have a recognized suffix: %s", a);
@@ -458,28 +619,36 @@ static int cc_classify_positional(CcOptions* o, const char* a) {
static int cc_resolve_pending_libs(CcOptions* o) {
uint32_t i;
- LibResolveMode mode =
- (o->target.obj == CFREE_OBJ_MACHO) ? LIB_RESOLVE_DYNAMIC_PREFER
- : LIB_RESOLVE_STATIC_ONLY;
for (i = 0; i < o->npending_libs; ++i) {
+ CcPendingLib* pl = &o->pending_libs[i];
char* p;
size_t sz;
LibResolveKind kind;
- if (driver_lib_resolve(o->env, o->pending_libs[i], mode,
- o->lib_search_paths, o->nlib_search_paths, &p, &sz,
- &kind) != 0) {
- driver_errf(CC_TOOL, "library not found: -l%s", o->pending_libs[i]);
+ LibResolveMode mode = (o->static_link || pl->link_mode == CFREE_LM_STATIC)
+ ? LIB_RESOLVE_STATIC_ONLY
+ : LIB_RESOLVE_DYNAMIC_PREFER;
+ if (driver_lib_resolve(o->env, pl->name, mode, o->lib_search_paths,
+ o->nlib_search_paths, &p, &sz, &kind) != 0) {
+ driver_errf(CC_TOOL, "library not found: -l%s", pl->name);
return 1;
}
if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
- o->dsos[o->ndsos] = p;
- o->dso_sizes[o->ndsos] = sz;
- o->ndsos++;
+ CcDsoInput* d = &o->dsos[o->ndsos++];
+ d->path = p;
+ d->owned = 1;
+ d->owned_size = sz;
+ pl->resolved_kind = CC_LINK_DSO;
+ pl->resolved_index = o->ndsos - 1u;
} else {
- o->owned_archives[o->nowned_archives] = p;
- o->owned_archive_sizes[o->nowned_archives] = sz;
- o->nowned_archives++;
- o->archives[o->narchives++] = p;
+ CcArchiveInput* ar = &o->archives[o->narchives++];
+ ar->path = p;
+ ar->owned = 1;
+ ar->owned_size = sz;
+ ar->whole_archive = pl->whole_archive;
+ ar->link_mode = pl->link_mode;
+ ar->group_id = pl->group_id;
+ pl->resolved_kind = CC_LINK_ARCHIVE;
+ pl->resolved_index = o->narchives - 1u;
}
}
return 0;
@@ -494,8 +663,11 @@ static int cc_apply_env(CcOptions* o) {
return 0;
}
+static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o,
+ size_t* out_size);
+
static int cc_parse(int argc, char** argv, CcOptions* o) {
- int x_lang_pinned = 0;
+ int forced_lang = -1;
int i;
if (cc_alloc_arrays(o, argc) != 0) return 1;
@@ -511,15 +683,63 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
if (r > 0) continue;
}
- if (driver_streq(a, "-c")) { o->compile_only = 1; continue; }
- if (driver_streq(a, "-E")) { o->preprocess_only = 1; continue; }
- if (driver_streq(a, "--dump-tokens")) { o->dump_tokens = 1; continue; }
- if (driver_streq(a, "-g")) { o->debug_info = 1; continue; }
- if (driver_streq(a, "-O0")) { o->opt_level = 0; continue; }
- if (driver_streq(a, "-O1")) { o->opt_level = 1; continue; }
- if (driver_streq(a, "-O2")) { o->opt_level = 2; continue; }
+ if (driver_streq(a, "-c")) {
+ o->compile_only = 1;
+ continue;
+ }
+ if (driver_streq(a, "-v") || driver_streq(a, "-###")) {
+ cc_log_ignored(a);
+ continue;
+ }
+ if (driver_streq(a, "-E")) {
+ o->preprocess_only = 1;
+ continue;
+ }
+ if (driver_streq(a, "--dump-tokens")) {
+ o->dump_tokens = 1;
+ continue;
+ }
+ if (driver_streq(a, "-g")) {
+ o->debug_info = 1;
+ continue;
+ }
+ if (driver_streq(a, "-O0")) {
+ o->opt_level = 0;
+ continue;
+ }
+ if (driver_streq(a, "-O1")) {
+ o->opt_level = 1;
+ continue;
+ }
+ if (driver_streq(a, "-O2")) {
+ o->opt_level = 2;
+ continue;
+ }
+ if (driver_streq(a, "-O") || driver_streq(a, "-O3") ||
+ driver_streq(a, "-Os") || driver_streq(a, "-Oz") ||
+ driver_streq(a, "-Ofast")) {
+ o->opt_level = 2;
+ if (!driver_streq(a, "-O")) cc_log_ignored(a);
+ continue;
+ }
- if (driver_streq(a, "-Werror")) { o->warnings_are_errors = 1; continue; }
+ if (driver_streq(a, "-Werror")) {
+ o->warnings_are_errors = 1;
+ continue;
+ }
+ if (driver_strneq(a, "-Werror=", 8)) {
+ o->warnings_are_errors = 1;
+ cc_log_ignored(a);
+ continue;
+ }
+ if (driver_streq(a, "-Wall") || driver_streq(a, "-Wextra") ||
+ driver_streq(a, "-Wpedantic") || driver_streq(a, "-pedantic") ||
+ driver_streq(a, "-pedantic-errors") || driver_streq(a, "-w") ||
+ driver_strneq(a, "-Wno-", 5) ||
+ (driver_strneq(a, "-W", 2) && !driver_strneq(a, "-Wl,", 4))) {
+ cc_log_ignored(a);
+ continue;
+ }
if (driver_strneq(a, "-fmax-errors=", 13)) {
uint64_t v;
if (cc_parse_u64(a + 13, &v) != 0 || v > 0xFFFFFFFFu) {
@@ -529,6 +749,32 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
o->max_errors = (uint32_t)v;
continue;
}
+ if (driver_strneq(a, "-std=", 5) || driver_streq(a, "-ansi")) {
+ cc_log_ignored(a);
+ continue;
+ }
+ if (driver_streq(a, "-ffreestanding") || driver_streq(a, "-fhosted") ||
+ driver_streq(a, "-fno-builtin") || driver_streq(a, "-pipe") ||
+ driver_streq(a, "-pthread") || driver_streq(a, "-nostdlib") ||
+ driver_streq(a, "-nodefaultlibs") || driver_streq(a, "-nostartfiles") ||
+ driver_streq(a, "-nostdinc")) {
+ cc_log_ignored(a);
+ continue;
+ }
+ if (driver_streq(a, "-isysroot") || driver_streq(a, "--sysroot") ||
+ driver_streq(a, "-include") || driver_streq(a, "-iquote") ||
+ driver_streq(a, "-idirafter")) {
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "%s requires an argument", a);
+ return 1;
+ }
+ cc_log_ignored(a);
+ continue;
+ }
+ if (driver_strneq(a, "--sysroot=", 10)) {
+ cc_log_ignored(a);
+ continue;
+ }
if (driver_streq(a, "-fPIC") || driver_streq(a, "-fpic")) {
o->target.pic = CFREE_PIC_PIC;
@@ -538,9 +784,70 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
o->target.pic = CFREE_PIC_PIE;
continue;
}
- if (driver_streq(a, "-static")) { o->target.pic = CFREE_PIC_NONE; continue; }
- if (driver_streq(a, "-pie")) { o->target.pic = CFREE_PIC_PIE; continue; }
- if (driver_streq(a, "-no-pie")) { o->target.pic = CFREE_PIC_NONE; continue; }
+ if (driver_streq(a, "-fno-PIC") || driver_streq(a, "-fno-pic") ||
+ driver_streq(a, "-fno-PIE") || driver_streq(a, "-fno-pie")) {
+ o->target.pic = CFREE_PIC_NONE;
+ continue;
+ }
+ if (driver_streq(a, "-static")) {
+ o->target.pic = CFREE_PIC_NONE;
+ o->static_link = 1;
+ o->cur_link_mode = CFREE_LM_STATIC;
+ continue;
+ }
+ if (driver_streq(a, "-pie")) {
+ o->target.pic = CFREE_PIC_PIE;
+ o->pie = 1;
+ continue;
+ }
+ if (driver_streq(a, "-no-pie")) {
+ o->target.pic = CFREE_PIC_NONE;
+ continue;
+ }
+ if (driver_streq(a, "-Bstatic")) {
+ o->cur_link_mode = CFREE_LM_STATIC;
+ continue;
+ }
+ if (driver_streq(a, "-Bdynamic")) {
+ o->cur_link_mode = CFREE_LM_DYNAMIC;
+ continue;
+ }
+ if (driver_streq(a, "--as-needed")) {
+ o->cur_link_mode = CFREE_LM_AS_NEEDED;
+ continue;
+ }
+ if (driver_streq(a, "--no-as-needed")) {
+ o->cur_link_mode = CFREE_LM_DYNAMIC;
+ continue;
+ }
+ if (driver_streq(a, "--whole-archive")) {
+ o->cur_whole_archive = 1;
+ continue;
+ }
+ if (driver_streq(a, "--no-whole-archive")) {
+ o->cur_whole_archive = 0;
+ continue;
+ }
+ if (driver_streq(a, "--start-group")) {
+ if (o->cur_group_id != 0) {
+ driver_errf(CC_TOOL, "nested --start-group is not supported");
+ return 1;
+ }
+ if (o->next_group_id == UINT8_MAX) {
+ driver_errf(CC_TOOL, "too many --start-group occurrences");
+ return 1;
+ }
+ o->cur_group_id = ++o->next_group_id;
+ continue;
+ }
+ if (driver_streq(a, "--end-group")) {
+ if (o->cur_group_id == 0) {
+ driver_errf(CC_TOOL, "--end-group without --start-group");
+ return 1;
+ }
+ o->cur_group_id = 0;
+ continue;
+ }
if (driver_streq(a, "-shared")) {
o->shared = 1;
@@ -566,24 +873,35 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
}
if (driver_streq(a, "-o")) {
- if (++i >= argc) { driver_errf(CC_TOOL, "-o requires an argument"); return 1; }
- if (o->output_path_set) { driver_errf(CC_TOOL, "duplicate -o"); return 1; }
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "-o 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"); return 1; }
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "-e requires an argument");
+ return 1;
+ }
o->entry = argv[i];
continue;
}
if (driver_streq(a, "-T")) {
- if (++i >= argc) { driver_errf(CC_TOOL, "-T requires an argument"); return 1; }
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "-T requires an argument");
+ return 1;
+ }
o->linker_script = argv[i];
continue;
}
if (driver_streq(a, "-target")) {
- if (++i >= argc) { driver_errf(CC_TOOL, "-target requires an argument"); return 1; }
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "-target requires an argument");
+ return 1;
+ }
if (driver_target_from_triple(argv[i], &o->target) != 0) {
driver_errf(CC_TOOL, "unrecognized target triple: %s", argv[i]);
return 1;
@@ -591,46 +909,128 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
o->target_set = 1;
continue;
}
+ if (driver_strneq(a, "--target=", 9)) {
+ if (driver_target_from_triple(a + 9, &o->target) != 0) {
+ driver_errf(CC_TOOL, "unrecognized target triple: %s", a + 9);
+ return 1;
+ }
+ o->target_set = 1;
+ continue;
+ }
+ if (driver_streq(a, "--target")) {
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "--target requires an argument");
+ return 1;
+ }
+ if (driver_target_from_triple(argv[i], &o->target) != 0) {
+ driver_errf(CC_TOOL, "unrecognized target triple: %s", argv[i]);
+ return 1;
+ }
+ o->target_set = 1;
+ continue;
+ }
+ if (driver_streq(a, "-Xlinker")) {
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "-Xlinker requires an argument");
+ return 1;
+ }
+ if (cc_record_wl(o, argv[i]) != 0) return 1;
+ continue;
+ }
if (driver_streq(a, "-x")) {
- if (++i >= argc) { driver_errf(CC_TOOL, "-x requires an argument"); return 1; }
- if (!driver_streq(argv[i], "c")) {
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "-x requires an argument");
+ return 1;
+ }
+ if (driver_streq(argv[i], "none")) {
+ forced_lang = -1;
+ continue;
+ }
+ if (driver_streq(argv[i], "c")) {
+ forced_lang = CFREE_LANG_C;
+ continue;
+ }
+ if (driver_streq(argv[i], "assembler") ||
+ driver_streq(argv[i], "assembler-with-cpp")) {
+ forced_lang = CFREE_LANG_ASM;
+ continue;
+ }
+ {
driver_errf(CC_TOOL, "unsupported -x language: %s", argv[i]);
return 1;
}
- x_lang_pinned = 1;
- continue;
}
if (driver_strneq(a, "-L", 2)) {
const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
- if (!dir) { driver_errf(CC_TOOL, "-L requires an argument"); return 1; }
+ if (!dir) {
+ driver_errf(CC_TOOL, "-L requires an argument");
+ return 1;
+ }
o->lib_search_paths[o->nlib_search_paths++] = dir;
continue;
}
if (driver_strneq(a, "-l", 2)) {
const char* name = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
- if (!name) { driver_errf(CC_TOOL, "-l requires an argument"); return 1; }
- o->pending_libs[o->npending_libs++] = name;
+ if (!name) {
+ driver_errf(CC_TOOL, "-l requires an argument");
+ return 1;
+ }
+ {
+ CcPendingLib* pl = &o->pending_libs[o->npending_libs++];
+ pl->name = name;
+ pl->whole_archive = o->cur_whole_archive;
+ pl->link_mode = o->cur_link_mode;
+ pl->group_id = o->cur_group_id;
+ cc_push_link_item(o, CC_LINK_LIB, o->npending_libs - 1u);
+ }
continue;
}
- if (driver_streq(a, "-M")) { o->dep_mode = CC_DEP_M; continue; }
- if (driver_streq(a, "-MM")) { o->dep_mode = CC_DEP_MM; continue; }
- if (driver_streq(a, "-MD")) { o->dep_mode = CC_DEP_MD; continue; }
- if (driver_streq(a, "-MMD")) { o->dep_mode = CC_DEP_MMD; continue; }
- if (driver_streq(a, "-MP")) { o->dep_phony = 1; continue; }
+ if (driver_streq(a, "-M")) {
+ o->dep_mode = CC_DEP_M;
+ continue;
+ }
+ if (driver_streq(a, "-MM")) {
+ o->dep_mode = CC_DEP_MM;
+ continue;
+ }
+ if (driver_streq(a, "-MD")) {
+ o->dep_mode = CC_DEP_MD;
+ continue;
+ }
+ if (driver_streq(a, "-MMD")) {
+ o->dep_mode = CC_DEP_MMD;
+ continue;
+ }
+ if (driver_streq(a, "-MP")) {
+ o->dep_phony = 1;
+ continue;
+ }
if (driver_streq(a, "-MF")) {
- if (++i >= argc) { driver_errf(CC_TOOL, "-MF requires an argument"); return 1; }
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "-MF requires an argument");
+ return 1;
+ }
o->dep_file = argv[i];
continue;
}
if (driver_streq(a, "-MT") || driver_streq(a, "-MQ")) {
- if (++i >= argc) { driver_errf(CC_TOOL, "%s requires an argument", a); return 1; }
+ if (++i >= argc) {
+ driver_errf(CC_TOOL, "%s requires an argument", a);
+ return 1;
+ }
o->dep_targets[o->ndep_targets++] = argv[i];
continue;
}
+ if (driver_streq(a, "--")) {
+ for (++i; i < argc; ++i) {
+ if (cc_classify_positional(o, argv[i], forced_lang) != 0) return 1;
+ }
+ break;
+ }
if (driver_streq(a, "-")) {
- if (cc_classify_positional(o, a) != 0) return 1;
+ if (cc_classify_positional(o, a, forced_lang) != 0) return 1;
continue;
}
if (a[0] == '-' && a[1] != '\0') {
@@ -638,16 +1038,16 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
return 1;
}
- if (cc_classify_positional(o, a) != 0) return 1;
+ if (cc_classify_positional(o, a, forced_lang) != 0) return 1;
}
- (void)x_lang_pinned;
if (cc_apply_env(o) != 0) return 1;
if (cc_resolve_pending_libs(o) != 0) return 1;
{
uint32_t total_sources = o->nsource_files + o->nsource_memory;
- uint32_t total_link = o->nobject_files + o->narchives;
+ uint32_t total_link =
+ o->nobject_files + o->narchives + o->ndsos + o->npending_libs;
if (total_sources == 0 && total_link == 0) {
driver_errf(CC_TOOL, "no input files");
@@ -662,30 +1062,41 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
driver_errf(CC_TOOL, "--dump-tokens is incompatible with -c/-E");
return 1;
}
- if (o->shared && (o->compile_only || o->preprocess_only || o->dump_tokens)) {
+ if (o->shared &&
+ (o->compile_only || o->preprocess_only || o->dump_tokens)) {
driver_errf(CC_TOOL, "-shared is incompatible with -c/-E/--dump-tokens");
return 1;
}
- if (!o->shared && (o->soname || o->nrpaths)) {
- driver_errf(CC_TOOL, "-Wl,-soname / -Wl,-rpath require -shared");
+ if (!o->shared && o->soname) {
+ driver_errf(CC_TOOL, "-Wl,-soname requires -shared");
+ return 1;
+ }
+ if (o->cur_group_id != 0) {
+ driver_errf(CC_TOOL, "missing --end-group");
return 1;
}
if (o->compile_only) {
- if (total_sources != 1 || total_link != 0) {
- driver_errf(CC_TOOL, "-c requires exactly one C source and no .o/.a inputs");
+ if (total_sources == 0 || total_link != 0) {
+ driver_errf(CC_TOOL, "-c requires source inputs and no link inputs");
+ return 1;
+ }
+ if (o->output_path && total_sources > 1) {
+ driver_errf(CC_TOOL, "-o cannot be used with -c and multiple sources");
return 1;
}
}
if (o->preprocess_only) {
if (total_sources != 1 || total_link != 0) {
- driver_errf(CC_TOOL, "-E requires exactly one C source and no .o/.a inputs");
+ driver_errf(CC_TOOL,
+ "-E requires exactly one C source and no .o/.a inputs");
return 1;
}
}
if (o->dump_tokens) {
if (total_sources != 1 || total_link != 0) {
- driver_errf(CC_TOOL,
- "--dump-tokens requires exactly one C source and no .o/.a inputs");
+ driver_errf(
+ CC_TOOL,
+ "--dump-tokens requires exactly one C source and no .o/.a inputs");
return 1;
}
}
@@ -706,10 +1117,27 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
driver_errf(CC_TOOL, "-MD/-MMD currently requires -c");
return 1;
}
- if (!o->output_path && !dep_only) {
- driver_errf(CC_TOOL, "-o is required");
+ if (dep_with_compile && total_sources != 1) {
+ driver_errf(CC_TOOL, "-MD/-MMD currently requires exactly one source");
return 1;
}
+ if (!o->output_path && !dep_only) {
+ if (o->compile_only) {
+ if (total_sources == 1) {
+ o->owned_output_path =
+ cc_dep_default_target(o->env, o, &o->owned_output_path_size);
+ if (!o->owned_output_path) {
+ driver_errf(CC_TOOL, "out of memory");
+ return 1;
+ }
+ o->output_path = o->owned_output_path;
+ }
+ } else if (o->preprocess_only) {
+ /* stdout */
+ } else {
+ o->output_path = "a.out";
+ }
+ }
}
}
return 0;
@@ -749,10 +1177,18 @@ static int cc_preprocess(DriverEnv* env, const CcOptions* o,
if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
- if (ctx.file_io->open_writer(ctx.file_io->user, o->output_path, &writer) !=
- CFREE_OK) {
- driver_errf(CC_TOOL, "failed to open output: %s", o->output_path);
- goto out;
+ if (o->output_path) {
+ if (ctx.file_io->open_writer(ctx.file_io->user, o->output_path, &writer) !=
+ CFREE_OK) {
+ driver_errf(CC_TOOL, "failed to open output: %s", o->output_path);
+ goto out;
+ }
+ } else {
+ writer = driver_stdout_writer(env);
+ if (!writer) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
}
if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
@@ -760,8 +1196,8 @@ static int cc_preprocess(DriverEnv* env, const CcOptions* o,
goto out;
}
- rc = cfree_c_preprocess(compiler, pp_opts, &input, writer) == CFREE_OK ? 0
- : 1;
+ rc =
+ cfree_c_preprocess(compiler, pp_opts, &input, writer) == CFREE_OK ? 0 : 1;
out:
if (compiler) driver_compiler_free(compiler);
@@ -914,11 +1350,17 @@ static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o,
size_t slash = 0;
size_t k;
for (k = srclen; k > 0; --k) {
- if (src[k - 1] == '.') { dot = k - 1; break; }
+ if (src[k - 1] == '.') {
+ dot = k - 1;
+ break;
+ }
if (src[k - 1] == '/') break;
}
for (k = dot; k > 0; --k) {
- if (src[k - 1] == '/') { slash = k; break; }
+ if (src[k - 1] == '/') {
+ slash = k;
+ break;
+ }
}
{
size_t name_len = dot - slash;
@@ -935,13 +1377,50 @@ static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o,
}
}
+static char* cc_default_obj_path_for_name(DriverEnv* env, const char* src,
+ size_t* out_size) {
+ size_t srclen = driver_strlen(src);
+ size_t dot = srclen;
+ size_t slash = 0;
+ size_t k;
+ char* buf;
+ for (k = srclen; k > 0; --k) {
+ if (src[k - 1] == '.') {
+ dot = k - 1;
+ break;
+ }
+ if (src[k - 1] == '/') break;
+ }
+ for (k = dot; k > 0; --k) {
+ if (src[k - 1] == '/') {
+ slash = k;
+ break;
+ }
+ }
+ {
+ size_t name_len = dot - slash;
+ size_t bufsz = name_len + 3;
+ buf = driver_alloc(env, bufsz);
+ if (!buf) return NULL;
+ driver_memcpy(buf, src + slash, name_len);
+ buf[name_len] = '.';
+ buf[name_len + 1] = 'o';
+ buf[name_len + 2] = '\0';
+ *out_size = bufsz;
+ return buf;
+ }
+}
+
static char* cc_dep_default_path(DriverEnv* env, const char* out_path,
size_t* out_size) {
size_t len = driver_strlen(out_path);
size_t dot = len;
size_t k;
for (k = len; k > 0; --k) {
- if (out_path[k - 1] == '.') { dot = k - 1; break; }
+ if (out_path[k - 1] == '.') {
+ dot = k - 1;
+ break;
+ }
if (out_path[k - 1] == '/') break;
}
{
@@ -1055,7 +1534,10 @@ static int cc_dep_finish(DriverEnv* env, const CfreeContext* ctx,
ntargets = o->ndep_targets;
if (ntargets == 0) {
owned_target = cc_dep_default_target(env, o, &owned_target_size);
- if (!owned_target) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ if (!owned_target) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
one_target[0] = owned_target;
targets = one_target;
ntargets = 1;
@@ -1063,7 +1545,8 @@ static int cc_dep_finish(DriverEnv* env, const CfreeContext* ctx,
dep_w = cc_dep_open_writer(env, ctx, o, &owned_path, &owned_size);
if (!dep_w) {
- driver_errf(CC_TOOL, "failed to open dep output: %s",
+ driver_errf(
+ CC_TOOL, "failed to open dep output: %s",
o->dep_file ? o->dep_file : (owned_path ? owned_path : "<stdout>"));
goto out;
}
@@ -1107,7 +1590,10 @@ static int cc_run_deps_only(DriverEnv* env, const CcOptions* o,
if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
discard = cc_discard_writer_new(env);
- if (!discard) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ if (!discard) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
driver_errf(CC_TOOL, "failed to initialize compiler");
@@ -1125,8 +1611,9 @@ out:
return rc;
}
-static int cc_run_compile_obj(DriverEnv* env, const CcOptions* o,
- const CfreePreprocessOptions* pp) {
+static int cc_run_compile_one(DriverEnv* env, const CcOptions* o,
+ const CfreePreprocessOptions* pp, int is_memory,
+ uint32_t index, const char* out_path) {
CfreeContext ctx = driver_env_to_context(env);
CfreeCompiler* compiler = NULL;
CfreeWriter* obj_w = NULL;
@@ -1136,11 +1623,23 @@ static int cc_run_compile_obj(DriverEnv* env, const CcOptions* o,
int loaded = 0;
int rc = 1;
- if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
+ if (is_memory) {
+ input = o->source_memory[index].bytes;
+ } else {
+ if (ctx.file_io->read_all(ctx.file_io->user, o->source_files[index], &fd) !=
+ CFREE_OK) {
+ driver_errf(CC_TOOL, "failed to read: %s", o->source_files[index]);
+ goto out;
+ }
+ loaded = 1;
+ input.name = o->source_files[index];
+ input.data = fd.data;
+ input.len = fd.size;
+ }
- if (ctx.file_io->open_writer(ctx.file_io->user, o->output_path, &obj_w) !=
+ if (ctx.file_io->open_writer(ctx.file_io->user, out_path, &obj_w) !=
CFREE_OK) {
- driver_errf(CC_TOOL, "failed to open output: %s", o->output_path);
+ driver_errf(CC_TOOL, "failed to open output: %s", out_path);
goto out;
}
@@ -1151,7 +1650,8 @@ static int cc_run_compile_obj(DriverEnv* env, const CcOptions* o,
cc_fill_c_opts(o, pp, &copts);
{
- CfreeLanguage lang = cfree_language_for_path(o->source_files[0]);
+ CfreeLanguage lang =
+ is_memory ? o->source_memory[index].lang : o->source_langs[index];
CfreeStatus st;
if (lang == CFREE_LANG_C) {
st = cfree_compile_c_obj_emit(compiler, &copts, &input, obj_w);
@@ -1184,6 +1684,35 @@ out:
return rc;
}
+static int cc_run_compile_obj(DriverEnv* env, const CcOptions* o,
+ const CfreePreprocessOptions* pp) {
+ return cc_run_compile_one(env, o, pp, o->nsource_memory == 1, 0,
+ o->output_path);
+}
+
+static int cc_run_compile_objs(DriverEnv* env, const CcOptions* o,
+ const CfreePreprocessOptions* pp) {
+ uint32_t i;
+ if (o->output_path) return cc_run_compile_obj(env, o, pp);
+ for (i = 0; i < o->nsource_files; ++i) {
+ size_t out_size = 0;
+ char* out = cc_default_obj_path_for_name(env, o->source_files[i], &out_size);
+ int rc;
+ if (!out) {
+ driver_errf(CC_TOOL, "out of memory");
+ return 1;
+ }
+ rc = cc_run_compile_one(env, o, pp, 0, i, out);
+ driver_free(env, out, out_size);
+ if (rc != 0) return rc;
+ }
+ for (i = 0; i < o->nsource_memory; ++i) {
+ const char* out = "<stdin>.o";
+ if (cc_run_compile_one(env, o, pp, 1, i, out) != 0) return 1;
+ }
+ return 0;
+}
+
/* exe path: compile every C source via a single CfreeCompiler, load
* .o/.a/script inputs, and call cfree_link_exe or cfree_link_shared. The
* compiler owns the per-source CfreeObjBuilders for the lifetime of the
@@ -1203,6 +1732,7 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
CfreeBytes* obj_in = NULL;
CfreeLinkArchiveInput* arch_in = NULL;
CfreeBytes* dso_in = NULL;
+ CfreeLinkInputOrder* order = NULL;
CfreeObjBuilder** objs = NULL;
CfreeLinkScript* script = NULL;
CfreeCCompileOptions copts;
@@ -1216,29 +1746,50 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
}
if (o->nsource_files) {
- src_bytes =
- driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_bytes));
+ src_bytes = driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_bytes));
src_lf = driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_lf));
- if (!src_bytes || !src_lf) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ if (!src_bytes || !src_lf) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
}
if (nsrc) {
objs = driver_alloc_zeroed(env, nsrc * sizeof(*objs));
- if (!objs) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ if (!objs) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
}
if (o->nobject_files) {
obj_lf = driver_alloc_zeroed(env, o->nobject_files * sizeof(*obj_lf));
obj_in = driver_alloc_zeroed(env, o->nobject_files * sizeof(*obj_in));
- if (!obj_lf || !obj_in) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ if (!obj_lf || !obj_in) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
}
if (o->narchives) {
arch_lf = driver_alloc_zeroed(env, o->narchives * sizeof(*arch_lf));
arch_in = driver_alloc_zeroed(env, o->narchives * sizeof(*arch_in));
- if (!arch_lf || !arch_in) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ if (!arch_lf || !arch_in) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
}
if (o->ndsos) {
dso_lf = driver_alloc_zeroed(env, o->ndsos * sizeof(*dso_lf));
dso_in = driver_alloc_zeroed(env, o->ndsos * sizeof(*dso_in));
- if (!dso_lf || !dso_in) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ if (!dso_lf || !dso_in) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
+ }
+ if (o->nlink_items) {
+ order = driver_alloc_zeroed(env, o->nlink_items * sizeof(*order));
+ if (!order) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
}
for (i = 0; i < o->nsource_files; ++i) {
@@ -1253,15 +1804,16 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
goto out;
}
for (i = 0; i < o->narchives; ++i) {
- if (driver_load_bytes(io, CC_TOOL, o->archives[i], &arch_lf[i],
+ if (driver_load_bytes(io, CC_TOOL, o->archives[i].path, &arch_lf[i],
&arch_in[i].bytes) != 0)
goto out;
- arch_in[i].link_mode = CFREE_LM_DEFAULT;
- arch_in[i].whole_archive = 0;
- arch_in[i].group_id = 0;
+ arch_in[i].link_mode = o->archives[i].link_mode;
+ arch_in[i].whole_archive = o->archives[i].whole_archive;
+ arch_in[i].group_id = o->archives[i].group_id;
}
for (i = 0; i < o->ndsos; ++i) {
- if (driver_load_bytes(io, CC_TOOL, o->dsos[i], &dso_lf[i], &dso_in[i]) != 0)
+ if (driver_load_bytes(io, CC_TOOL, o->dsos[i].path, &dso_lf[i],
+ &dso_in[i]) != 0)
goto out;
}
@@ -1285,7 +1837,7 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
cc_fill_c_opts(o, pp, &copts);
for (i = 0; i < o->nsource_files; ++i) {
- CfreeLanguage lang = cfree_language_for_path(o->source_files[i]);
+ CfreeLanguage lang = o->source_langs[i];
CfreeStatus st;
if (lang == CFREE_LANG_C) {
st = cfree_compile_c_obj(compiler, &copts, &src_bytes[i], &objs[i]);
@@ -1340,6 +1892,45 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
inputs.build_id_mode = o->build_id_mode;
inputs.build_id_bytes = o->build_id_bytes;
inputs.build_id_len = o->build_id_len;
+ if (order) {
+ uint32_t oi;
+ for (oi = 0; oi < o->nlink_items; ++oi) {
+ const CcLinkItem* item = &o->link_items[oi];
+ CfreeLinkInputOrder* dst = &order[oi];
+ switch ((CcLinkItemKind)item->kind) {
+ case CC_LINK_SOURCE_FILE:
+ dst->kind = CFREE_LINK_INPUT_OBJ;
+ dst->index = item->index;
+ break;
+ case CC_LINK_SOURCE_MEMORY:
+ dst->kind = CFREE_LINK_INPUT_OBJ;
+ dst->index = o->nsource_files + item->index;
+ break;
+ case CC_LINK_OBJECT:
+ dst->kind = CFREE_LINK_INPUT_OBJ_BYTES;
+ dst->index = item->index;
+ break;
+ case CC_LINK_ARCHIVE:
+ dst->kind = CFREE_LINK_INPUT_ARCHIVE;
+ dst->index = item->index;
+ break;
+ case CC_LINK_DSO:
+ dst->kind = CFREE_LINK_INPUT_DSO;
+ dst->index = item->index;
+ break;
+ case CC_LINK_LIB: {
+ const CcPendingLib* pl = &o->pending_libs[item->index];
+ dst->kind = (pl->resolved_kind == CC_LINK_DSO)
+ ? CFREE_LINK_INPUT_DSO
+ : CFREE_LINK_INPUT_ARCHIVE;
+ dst->index = pl->resolved_index;
+ break;
+ }
+ }
+ }
+ inputs.order = order;
+ inputs.norder = o->nlink_items;
+ }
if (o->shared) {
CfreeSharedLinkOptions z = {0};
@@ -1355,12 +1946,16 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
s.nrpaths = o->nrpaths;
}
s.allow_undefined = 1;
+ s.gc_sections = o->gc_sections;
rc = cfree_link_shared(compiler, &s, out_w) == CFREE_OK ? 0 : 1;
} else {
CfreeExeLinkOptions z = {0};
CfreeExeLinkOptions link_opts;
link_opts = z;
link_opts.inputs = inputs;
+ link_opts.gc_sections = o->gc_sections;
+ link_opts.pie = o->pie;
+ link_opts.interp_path = o->interp_path;
rc = cfree_link_exe(compiler, &link_opts, out_w) == CFREE_OK ? 0 : 1;
}
}
@@ -1369,8 +1964,7 @@ out:
if (out_w) cfree_writer_close(out_w);
if (rc == 0 && o->output_path) {
if (driver_mark_executable_output(o->output_path) != 0) {
- driver_errf(CC_TOOL, "failed to set executable mode: %s",
- o->output_path);
+ driver_errf(CC_TOOL, "failed to set executable mode: %s", o->output_path);
rc = 1;
}
}
@@ -1393,6 +1987,7 @@ out:
if (arch_lf) driver_free(env, arch_lf, o->narchives * sizeof(*arch_lf));
if (dso_in) driver_free(env, dso_in, o->ndsos * sizeof(*dso_in));
if (dso_lf) driver_free(env, dso_lf, o->ndsos * sizeof(*dso_lf));
+ if (order) driver_free(env, order, o->nlink_items * sizeof(*order));
if (obj_in) driver_free(env, obj_in, o->nobject_files * sizeof(*obj_in));
if (obj_lf) driver_free(env, obj_lf, o->nobject_files * sizeof(*obj_lf));
if (src_lf) driver_free(env, src_lf, o->nsource_files * sizeof(*src_lf));
@@ -1431,7 +2026,7 @@ int driver_cc(int argc, char** argv) {
} else if (co.dep_mode == CC_DEP_M || co.dep_mode == CC_DEP_MM) {
rc = cc_run_deps_only(&env, &co, &pp);
} else if (co.compile_only) {
- rc = cc_run_compile_obj(&env, &co, &pp);
+ rc = cc_run_compile_objs(&env, &co, &pp);
} else {
rc = cc_run_link_exe(&env, &co, &pp);
}
diff --git a/driver/env.c b/driver/env.c
@@ -1578,11 +1578,28 @@ out:
return ok;
}
+static int *driver_hosted_errno_location(void) { return &errno; }
+static FILE *driver_hosted_stdin(void) { return stdin; }
+static FILE *driver_hosted_stdout(void) { return stdout; }
+static FILE *driver_hosted_stderr(void) { return stderr; }
+
void *driver_dlsym_resolver(void *user, const char *name) {
void *p;
(void)user;
if (!name)
return NULL;
+ if (strcmp(name, "__cfree_errno_location") == 0 ||
+ (name[0] == '_' && strcmp(name + 1, "__cfree_errno_location") == 0))
+ return (void *)&driver_hosted_errno_location;
+ if (strcmp(name, "__cfree_stdin") == 0 ||
+ (name[0] == '_' && strcmp(name + 1, "__cfree_stdin") == 0))
+ return (void *)&driver_hosted_stdin;
+ if (strcmp(name, "__cfree_stdout") == 0 ||
+ (name[0] == '_' && strcmp(name + 1, "__cfree_stdout") == 0))
+ return (void *)&driver_hosted_stdout;
+ if (strcmp(name, "__cfree_stderr") == 0 ||
+ (name[0] == '_' && strcmp(name + 1, "__cfree_stderr") == 0))
+ return (void *)&driver_hosted_stderr;
p = dlsym(RTLD_DEFAULT, name);
/* On Mach-O hosts the linker hands us C names with a leading underscore
* (obj_format_c_mangle), but dlsym(RTLD_DEFAULT) expects the
diff --git a/src/api/compile.c b/src/api/compile.c
@@ -36,7 +36,8 @@ CfreeLanguage cfree_language_for_path(const char* path) {
if (path[i] == '/') return CFREE_LANG_C;
if (path[i] == '.') {
const char* ext = path + i + 1;
- if (ext[0] == 's' && ext[1] == '\0') return CFREE_LANG_ASM;
+ if ((ext[0] == 's' || ext[0] == 'S') && ext[1] == '\0')
+ return CFREE_LANG_ASM;
if (ext[0] == 't' && ext[1] == 'o' && ext[2] == 'y' && ext[3] == '\0')
return CFREE_LANG_TOY;
if (ext[0] == 'w' && ext[1] == 'a' && ext[2] == 't' && ext[3] == '\0')
diff --git a/test/ar/cases/04-rcu-compat.expected b/test/ar/cases/04-rcu-compat.expected
@@ -0,0 +1,2 @@
+a.o
+b.o
diff --git a/test/ar/cases/04-rcu-compat.sh b/test/ar/cases/04-rcu-compat.sh
@@ -0,0 +1,8 @@
+# Lua and many traditional makefiles use `ar rcu`. cfree does not track
+# member mtimes for update-if-newer semantics, but it should accept `u`
+# as a compatibility modifier.
+
+printf 'aaaa' > a.o
+printf 'bbbb' > b.o
+"$CFREE" ar rcu lib.a a.o b.o
+"$CFREE" ar t lib.a
diff --git a/test/driver/run.sh b/test/driver/run.sh
@@ -29,6 +29,10 @@ int main(void) { return 0; }
int _start(void) { return 0; }
SRC
+cat > "$work/other.c" <<'SRC'
+int other(void) { return 0; }
+SRC
+
pass=0
fail=0
@@ -55,6 +59,21 @@ else
fail=$((fail + 1))
fi
+rm -f "$work/a.out"
+if (cd "$work" && umask 077 && "$CFREE" cc main.c) \
+ > "$work/cc-link-default.out" 2> "$work/cc-link-default.err"; then
+ if [ -f "$work/a.out" ]; then
+ check_mode "cc-link-default-output" "$work/a.out" 700
+ else
+ printf 'FAIL %s (a.out not created)\n' "cc-link-default-output"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL cc-link-default-output (cfree cc failed)\n'
+ sed 's/^/ | /' "$work/cc-link-default.err"
+ fail=$((fail + 1))
+fi
+
if (umask 077; "$CFREE" cc -c "$work/main.c" -o "$work/main.o") \
> "$work/cc-c.out" 2> "$work/cc-c.err"; then
: > "$work/ld-exe"
@@ -73,6 +92,154 @@ else
fail=$((fail + 1))
fi
+rm -f "$work/main.o"
+if (cd "$work" && "$CFREE" cc -c main.c) \
+ > "$work/cc-c-default.out" 2> "$work/cc-c-default.err"; then
+ if [ -f "$work/main.o" ]; then
+ printf 'PASS %s\n' "cc-c-default-output"
+ pass=$((pass + 1))
+ else
+ printf 'FAIL %s (main.o not created)\n' "cc-c-default-output"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL cc-c-default-output (cfree cc -c failed)\n'
+ sed 's/^/ | /' "$work/cc-c-default.err"
+ fail=$((fail + 1))
+fi
+
+rm -f "$work/main.o" "$work/other.o"
+if (cd "$work" && "$CFREE" cc -c main.c other.c) \
+ > "$work/cc-c-multi.out" 2> "$work/cc-c-multi.err"; then
+ if [ -f "$work/main.o" ] && [ -f "$work/other.o" ]; then
+ printf 'PASS %s\n' "cc-c-multi-default-output"
+ pass=$((pass + 1))
+ else
+ printf 'FAIL %s (expected main.o and other.o)\n' "cc-c-multi-default-output"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL cc-c-multi-default-output (cfree cc -c failed)\n'
+ sed 's/^/ | /' "$work/cc-c-multi.err"
+ fail=$((fail + 1))
+fi
+
+if "$CFREE" cc -E "$work/main.c" > "$work/cc-E-default.out" 2> "$work/cc-E-default.err"; then
+ if [ -s "$work/cc-E-default.out" ]; then
+ printf 'PASS %s\n' "cc-E-default-stdout"
+ pass=$((pass + 1))
+ else
+ printf 'FAIL %s (stdout was empty)\n' "cc-E-default-stdout"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL cc-E-default-stdout (cfree cc -E failed)\n'
+ sed 's/^/ | /' "$work/cc-E-default.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" &&
+ grep -q "ignoring accepted compatibility flag: -std=c11" "$work/cc-ignored.err"; then
+ printf 'PASS %s\n' "cc-ignored-compat-flags"
+ pass=$((pass + 1))
+ else
+ printf 'FAIL %s (missing ignore log)\n' "cc-ignored-compat-flags"
+ sed 's/^/ | /' "$work/cc-ignored.err"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL cc-ignored-compat-flags (cfree cc failed)\n'
+ sed 's/^/ | /' "$work/cc-ignored.err"
+ fail=$((fail + 1))
+fi
+
+cat > "$work/run-errno.c" <<'SRC'
+#include <errno.h>
+int main(void) {
+ errno = 42;
+ return errno;
+}
+SRC
+
+"$CFREE" run -I"$repo_root/rt/include" -I"$repo_root/rt/include/libc" \
+ "$work/run-errno.c" > "$work/run-errno.out" 2> "$work/run-errno.err"
+run_errno_status=$?
+if [ "$run_errno_status" -eq 42 ]; then
+ printf 'PASS %s\n' "run-hosted-errno-shim"
+ pass=$((pass + 1))
+else
+ printf 'FAIL %s (status %s, want 42)\n' \
+ "run-hosted-errno-shim" "$run_errno_status"
+ sed 's/^/ | /' "$work/run-errno.err"
+ fail=$((fail + 1))
+fi
+
+cat > "$work/run-main.c" <<'SRC'
+int add42(int);
+int main(void) { return add42(0); }
+SRC
+cat > "$work/run-lib.c" <<'SRC'
+int add42(int x) { return x + 42; }
+SRC
+
+if "$CFREE" cc -I"$repo_root/rt/include" -I"$repo_root/rt/include/libc" \
+ -c "$work/run-lib.c" -o "$work/run-lib.o" \
+ > "$work/run-lib.out" 2> "$work/run-lib.err" &&
+ "$CFREE" ar rcs "$work/librun.a" "$work/run-lib.o" \
+ > "$work/run-ar.out" 2> "$work/run-ar.err"; then
+ "$CFREE" run -I"$repo_root/rt/include" -I"$repo_root/rt/include/libc" \
+ "$work/run-main.c" "$work/librun.a" \
+ > "$work/run-archive.out" 2> "$work/run-archive.err"
+ run_status=$?
+ if [ "$run_status" -eq 42 ]; then
+ printf 'PASS %s\n' "run-source-archive-demand"
+ pass=$((pass + 1))
+ else
+ printf 'FAIL %s (status %s, want 42)\n' \
+ "run-source-archive-demand" "$run_status"
+ sed 's/^/ | /' "$work/run-archive.err"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL run-source-archive-demand (setup failed)\n'
+ sed 's/^/ | /' "$work/run-lib.err" "$work/run-ar.err"
+ fail=$((fail + 1))
+fi
+
+cat > "$work/order-main.c" <<'SRC'
+int foo(void);
+int _start(void) { return foo(); }
+SRC
+cat > "$work/order-foo.c" <<'SRC'
+int foo(void) { return 0; }
+SRC
+
+if "$CFREE" cc -target x86_64-linux -c "$work/order-main.c" -o "$work/order-main.o" \
+ > "$work/order-main.out" 2> "$work/order-main.err" &&
+ "$CFREE" cc -target x86_64-linux -c "$work/order-foo.c" -o "$work/order-foo.o" \
+ > "$work/order-foo.out" 2> "$work/order-foo.err" &&
+ "$CFREE" ar rc "$work/libfoo.a" "$work/order-foo.o" \
+ > "$work/order-ar.out" 2> "$work/order-ar.err"; then
+ if "$CFREE" cc -target x86_64-linux -L"$work" "$work/order-main.o" -lfoo \
+ -o "$work/order-right" > "$work/order-right.out" 2> "$work/order-right.err" &&
+ ! "$CFREE" cc -target x86_64-linux -L"$work" -lfoo "$work/order-main.o" \
+ -o "$work/order-wrong" > "$work/order-wrong.out" 2> "$work/order-wrong.err"; then
+ printf 'PASS %s\n' "cc-link-archive-order"
+ pass=$((pass + 1))
+ else
+ printf 'FAIL %s (archive order was not enforced)\n' "cc-link-archive-order"
+ sed 's/^/ right| /' "$work/order-right.err"
+ sed 's/^/ wrong| /' "$work/order-wrong.err"
+ fail=$((fail + 1))
+ fi
+else
+ printf 'FAIL cc-link-archive-order (setup failed)\n'
+ sed 's/^/ | /' "$work/order-main.err" "$work/order-foo.err" "$work/order-ar.err"
+ fail=$((fail + 1))
+fi
+
total=$((pass + fail))
if [ "$fail" -gt 0 ]; then
printf '\ndriver: %d/%d passed\n' "$pass" "$total"