libmemalloc 0.0.0-alpha
The Magic of Memory Transmutation
Loading...
Searching...
No Matches
c-module-architecture

C Module Architecture

Use this guide when designing a C module, connecting modules, or reviewing a build. It defines who owns state and interfaces, how dependencies cross module boundaries, and how compilation, linking, exports, and lifecycle fit together.

Start with the module model, then use the index to find a boundary or build rule. The appendix brings the rules together in a complete composition and build example.

This guide uses C Code Standard for rule precedence, profiles, style, local safety, and deviations, and Common C Pitfalls for failure scenarios. Apply the three guides together when reviewing an implementation.

Use C Compliance and Assurance for lifecycle evidence and C Formal Verification for module contracts and integration proof campaigns.

On this page

Module ownership and visibility

Headers and declarations

Ports, adapters, and lifecycle

Compilation and isolation

Build stages and linking

Public interfaces and exports

Release artifacts and hardening

Build targets

Boundary tests and release checks

Dependency constraints

Architecture shapes and placement decisions

Artifact modes and merge review

Trust boundaries and capabilities

Compatibility and state contracts

Verification, ownership, and source provenance

Public contracts and verification evidence

Performance ownership and topology


Module model and applicability

Each module owns its implementation, state, inbound API, dependency ports, tests, and export contract. Peer communication uses injected callbacks or adapters. The composition root owns binding and combined lifecycle.

Owner Permitted compile-time knowledge
Module Its own public/internal headers, approved leaf contracts, C/platform headers, and the narrow foundation allowances in CMOD-004 and CMOD-050
Contract header Required leaf types and ABI spellings; no module implementation or convenience aggregation
Adapter Public APIs and contracts it translates; no module internal headers
Composition root (main.c) Any headers needed by the application, including internal, platform, and third-party headers; locally defined helpers and state remain private
Module-owned internal test Its module's internal contract, never another module's internals

An approved foundation library or platform abstraction is a named lower-layer exception, as in the original policy. Record its permitted headers, symbols, and dependencies. Neither the word foundation nor an acyclic graph grants a new exception for a peer provider.

A callback changes where a dependency is bound. It does not remove semantic coupling, erase execution cost, create memory isolation, or authenticate the provider. A native module running in the same address space is not an enforced security sandbox. Use an actual process/hardware protection boundary where the threat model requires one.

The reference implementation uses two externally synchronized peer instances: buffer and emitter. A buffer_sink adapter connects the emitter's write port to BUFFER_append. The checked foundation is a direct dependency of buffer. Neither peer compiles with the other's headers or links the other peer target. Static and ELF shared variants are built separately from the same sources.


Compile-time boundaries

An arrow means that the source on the left may include the public contract on the right. The composition root and adapter know both public APIs. Each peer module knows its own contracts and approved foundations. A callback port belongs to its consumer; the adapter implements that port using the provider's public API.

flowchart LR
    C["Composition root"] --> E["Emitter public API and sink port"]
    C --> B["Buffer public API"]
    C --> A["Buffer sink adapter"]
    A --> E
    A --> B
    EI["Emitter implementation"] --> E
    BI["Buffer implementation"] --> B
    BI --> F["Approved checked-arithmetic foundation"]
    E --> L["Leaf types and contracts"]
    B --> L

These boundaries implement CMOD-004, CMOD-020, CMOD-021, and CMOD-022. A peer implementation has no include or link dependency on the other peer. The foundation allowance must name its permitted interface.


Runtime callback flow

At runtime, the emitter invokes its injected port. The adapter translates that call into the buffer's public operation and returns the provider's result through the port contract. The emitter does not choose a concrete provider or inspect its representation.

sequenceDiagram
    participant C as Composition root
    participant E as Emitter
    participant A as Buffer sink adapter
    participant B as Buffer
    C->>E: Bind sink callback and borrowed context
    C->>E: Send bytes
    E->>A: Invoke sink port
    A->>B: Append through public API
    B-->>A: Append status
    A-->>E: Translate result to port contract
    E-->>C: Return operation status

The synchronous call completes before send returns. The callback context and provider remain alive throughout the call. See CMOD-023, CMOD-024, CMOD-025, and CMOD-026 for context, lifetime, reentry, and error contracts.


Lifecycle that every binding must satisfy

Composition creates providers, creates consumers, and binds borrowed contexts. A consumer may call only while the provider context and function code are alive. Shutdown first prevents new calls, then drains active work, then destroys the consumer, then releases the provider. Partial construction unwinds only completed acquisitions. A runtime unbind or plugin unload must prove quiescence; replacing a function pointer with NULL is not such proof.

In the example, a callback completes synchronously before send returns, and the application destroys emitter before buffer. The busy flag rejects synchronous reentry. External synchronization is a precondition for concurrent callers and teardown; the flag is not an atomic lock. The example does not implement runtime plugin loading, worker threads, SDK installation, or cross-version ABI negotiation.

flowchart TD
    P["Create provider"] --> C["Create consumer"]
    C --> B["Bind callback and borrowed context"]
    B --> R["Run operations"]
    R --> S["Prevent new calls"]
    S --> Q["Drain active work"]
    Q --> D["Destroy consumer"]
    D --> F["Release provider"]

On a construction failure, unwind only the acquisitions that completed. The shutdown order above keeps the provider alive until the consumer can no longer call it. This is the lifecycle obligation in CMOD-021 and CMOD-024; a cleared callback pointer alone does not prove it.


From source to release

Compile each translation unit using its module's declared inputs. Choose the artifact path required by the target: object files, a relocatable module object, a static archive, or a shared library. The final link composes the selected artifacts with adapters and application code.

flowchart LR
    S["Module sources and headers"] --> C["Compile each translation unit"]
    C --> O["Object files"]
    O --> R["Optional relocatable module object"]
    O --> A["Static archive"]
    O --> H["Shared library with export controls"]
    R --> A
    O --> L["Final application link"]
    R --> L
    A --> L
    H --> L
    I["Adapters and composition root"] --> L
    L --> V["Check exports, dependencies, and release metadata"]

Shared-library objects use the target's required position-independent code settings. Export checks inspect the actual artifact and selected link mode. See CMOD-031 through CMOD-039 for build stages and CMOD-061 through CMOD-065 for artifact checks.


Architecture controls


CMOD-001: Minimum Visibility Is the Primary Rule

Class: ARCHITECTURE. Obligation: project requirement.

Give declarations, includes, types, state, callbacks, and exports the smallest scope their owner needs. A one-file helper is static; a cross-TU helper is module internal; a public API exists only for an approved consumer. Minimum visibility is an architecture policy. It is not memory isolation between native modules in the same process. Automatic variable declaration placement follows CSTYLE-107; minimum visibility does not permit nested declarations contrary to that rule.

Related controls: CSTYLE-033, CSTYLE-069.

Complete example: worked sources.

Local examples

Visibility decision flow (not executable):

flowchart TD
    file_scope{"Need the symbol outside one .c file?"}
    module_scope{"Need the symbol outside its module?"}
    peer_scope{"Need another peer module to name the symbol?"}
    link_scope{"Need the final program or external consumer<br/>to link the symbol by name?"}

    file_scope -->|No| private_symbol["Static definition in that .c file"]
    file_scope -->|Yes| module_scope
    module_scope -->|No| internal_symbol["Hidden module-internal symbol"]
    module_scope -->|Yes| peer_scope
    peer_scope -->|No| no_peer_header["Keep it out of peer module headers"]
    peer_scope -->|Yes| adapter["Redesign the boundary around callbacks or an adapter"]
    no_peer_header --> link_scope
    adapter --> link_scope
    link_scope -->|No| hidden_symbol["Hide or localize it"]
    link_scope -->|Yes| public_symbol["Public API/export contract"]

CMOD-002: One Module Owns Each Implementation Detail

Class: ARCHITECTURE. Obligation: project requirement.

Each implementation detail belongs to one module. A module owns:

  • its source files
  • its internal headers
  • its private and internal symbols
  • its state machines
  • its storage layout
  • its allocation policy for module-owned objects
  • its lifecycle rules
  • its inbound API
  • the callback ports that represent dependencies it consumes
  • its module tests
  • its export list A peer module must not modify, inspect, allocate, free, or depend on another module's internal representation. Ownership does not follow directory proximity. A file in another module remains outside the boundary even when both modules live in the same repository. Code in the composition layer may include and bind both public module APIs. Peer module code must not include or name the other module's implementation.

Related controls: CSTYLE-032, CSTYLE-082.

Failure scenarios: CPIT-005.

Complete example: worked sources.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart LR
    subgraph A[Module A ownership]
        AAPI[Public API]
        ACB[Callback ports]
        ASTATE[Private state]
        AINT[Internal code]
    end

    subgraph B[Module B ownership]
        BAPI[Public API]
        BCB[Callback ports]
        BSTATE[Private state]
        BINT[Internal code]
    end

    COMP[Composition root or adapter]

    COMP --> AAPI
    COMP --> BAPI
    COMP --> ACB
    COMP --> BCB

    AINT --- ASTATE
    BINT --- BSTATE

CMOD-003: Peer Modules Communicate Through Callbacks

Class: ARCHITECTURE. Obligation: project requirement.

A peer module must not call another peer module through a named link-time symbol. It receives a compatible function pointer through configuration, creation, binding, or registration and invokes that pointer. The composition root owns the concrete symbol relationship. Use an adapter when DTOs, units, errors, ownership, or execution semantics differ.

The original narrow foundation/platform allowances remain governed by CMOD-004, CMOD-050, and CMOD-062. They do not authorize peer calls or allow a module to reclassify a provider merely to bypass a callback. Callback-only communication is the project's module-boundary policy, not an ISO C requirement.

Related controls: CSTYLE-071.

Failure scenarios: CPIT-026, CPIT-032.

Complete example: worked sources.

Local examples

Noncompliant fragment (do not copy):

#include "frontend.h"

#include <stdlib.h>

#include "backend.h"

int FRONTEND_process(void)
{
    int ret = EXIT_SUCCESS;

    ret = BACKEND_run();
    if (ret != EXIT_SUCCESS)
    {
        goto function_output;
    }

function_output:
    return ret;
}

Contextual C example:

typedef int (*frontend_request_cb_t)(void                         *context,
                                     const frontend_request_dto_t *request,
                                     frontend_reply_dto_t         *reply);

typedef struct FrontendCallbacks
{
        void                 *request_context;
        frontend_request_cb_t request;
} frontend_callbacks_t;

Contextual C example:

int APP_createFrontend(backend_t *backend, frontend_t **frontend)
{
        int ret = EXIT_SUCCESS;

        frontend_callbacks_t callbacks = { 0 };

        if (backend == (backend_t *)(NULL))
        {
                ret = -EINVAL;
                goto function_output;
        }

        if (frontend == (frontend_t **)(NULL))
        {
                ret = -EINVAL;
                goto function_output;
        }

        callbacks.request_context = backend;
        callbacks.request         = BACKEND_handleFrontendRequest;

        ret = FRONTEND_create(frontend, &callbacks);

function_output:
        return ret;
}

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

sequenceDiagram
    participant C as Composition Root
    participant F as Frontend Module
    participant B as Backend Module

    C->>B: create backend
    C->>F: create frontend with callback pointer
    F->>B: invoke injected callback
    B-->>F: return DTO/status

CMOD-004: Compile Modules in Isolation

Class: ARCHITECTURE. Obligation: project requirement.

The build must compile each module without peer-module include paths or peer-module link dependencies. A module may use its own public/internal headers, the C standard library and approved platform headers, the project leaf types.h, approved boundary DTO/callback contracts, module-local generated headers, and platform abstractions owned by the module or an approved foundation.

A module target must not depend on another peer module target. Adapters and the composition root may depend on the public APIs they connect. Application main.c additionally has the unrestricted header selection described in CMOD-021; this does not grant the same scope to module targets. Inspect actual include dependencies as well as compiler arguments so relative includes cannot bypass the boundary. Foundation/runtime symbols need the explicit allowlist required by CMOD-062.

Related controls: CSTYLE-034.

Complete example: worked sources.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart TD
    TYPES[foundation/types.h]
    DTO[boundary DTOs]

    subgraph MA[Module A isolated build]
        AINC[A public and internal headers]
        ASRC[A source files]
        AOBJ[A object files]
        ASRC --> AOBJ
        AINC --> AOBJ
    end

    subgraph MB[Module B isolated build]
        BINC[B public and internal headers]
        BSRC[B source files]
        BOBJ[B object files]
        BSRC --> BOBJ
        BINC --> BOBJ
    end

    TYPES --> AOBJ
    TYPES --> BOBJ
    DTO --> AOBJ
    DTO --> BOBJ

    ADAPTER[Adapter target]
    ROOT[Composition root]

    AOBJ --> ROOT
    BOBJ --> ROOT
    ADAPTER --> ROOT

CMOD-005: Module Directory Layout

Class: ARCHITECTURE. Obligation: project requirement.

Give each module its own source list, public include directory, internal include directory when needed, tests, and build description. Keep adapters and composition outside the peers they connect. The worked project uses modules/<name>/{inc,src} with a local CMakeLists. Directory names are not the authority: the reviewed ownership and dependency graph is.

Complete example: worked sources.

Local examples

Layout example (not executable):

backend/
├── CMakeLists.txt
├── backend.version.map
├── inc/
│   └── backend.h
├── src/
│   ├── backend.c
│   ├── backend_data.c
│   ├── backend_process.c
│   └── inc/
│       ├── backend_internal.h
│       ├── backend_data.h
│       └── backend_process.h
└── tests/
    ├── public/
    └── internal/

Layout example (not executable):

integration/
├── adapters/
│   └── frontend_backend_adapter.c
├── contracts/
│   ├── frontend_request_dto.h
│   └── frontend_reply_dto.h
└── composition/
    └── application_modules.c

CMOD-006: Public, Internal, and Private Visibility

Class: ARCHITECTURE. Obligation: project requirement.

Public declarations form the supported inbound contract. Internal declarations are shared only among translation units and owned tests of the same module. Private declarations stay in one translation unit and use internal linkage. The application composition file follows the private-definition policy and broad include scope in CMOD-021; its hosted main entry is the linkage exception. These source-level classes differ from ELF visibility and dynamic export status; validate each layer rather than inferring one from the spelling of a name.

Complete example: worked sources.

Local examples

Layout example (not executable):

public
internal
private

Layout example (not executable):

<module>/inc/<module>.h

Layout example (not executable):

<module>/src/inc/*.h

Contextual C example:

static int backend_validateState(const backend_t *backend)
{
        int ret = EXIT_SUCCESS;

        if (backend == (const backend_t *)(NULL))
        {
                ret = -EINVAL;
                goto function_output;
        }

function_output:
        return ret;
}


CMOD-007: State Ownership

Class: ARCHITECTURE. Obligation: project requirement.

Keep a module's mutable implementation state in module-owned objects. Peers must not inspect or update its private fields. Use opaque handles and explicitly shared value DTOs. An exposed DTO is a deliberate data contract, not private state made public. Immutable tables may be shared only as an approved contract without transferring authority over module internals.

Complete example: worked sources.

Local examples

Noncompliant fragment (do not copy):

typedef struct Backend backend_t;

Contextual C example:

struct Backend
{
        uint32_t state;
        bool     is_ready;
};


CMOD-008: Memory Ownership Across Boundaries

Class: ARCHITECTURE. Obligation: project requirement.

A boundary must state who owns every pointer and how long the pointed object remains valid. Default callback rules:

  • the caller owns input DTO storage
  • the callee may read input DTO storage only during the callback
  • the callee must not retain an input pointer
  • the caller owns output storage that it passes into the callback
  • the callback writes only within declared capacities
  • pointer ownership does not transfer unless the contract names the transfer
  • a module frees only memory that its own allocator contract owns Prefer DTO values with fixed-width fields, bounded arrays, spans, IDs, or opaque handles over raw ownership-bearing pointers.

Related controls: CSTYLE-082, CSTYLE-083, CSTYLE-084.

Failure scenarios: CPIT-001, CPIT-002, CPIT-003, CPIT-005, CPIT-032, CPIT-033.

Complete example: worked sources.

Local examples

Layout example (not executable):

boundary contract says caller allocates DTO; callee borrows during call and
never frees it

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

callee frees caller-owned DTO because ownership was not specified

CMOD-009: Headers Must Minimize Includes

Class: ARCHITECTURE. Obligation: project requirement.

A header must avoid including another project header. Expose only declarations needed by its public contract and no transitive convenience includes. Prefer forward declarations when only pointers to an incomplete type are needed. A source file includes its own concrete dependencies. Only the leaf-contract exceptions in CMOD-010 may cross header boundaries.

Related controls: CSTYLE-026, CSTYLE-028, CSTYLE-033, CSTYLE-034.

Complete example: worked sources.

Local examples

Contextual C example:

#if !defined(COIL_BACKEND_H)
  #define COIL_BACKEND_H

  #include "types.h"

typedef struct Backend       backend_t;
typedef struct BackendConfig backend_config_t;

int BACKEND_create(backend_t **backend, const backend_config_t *config);

int BACKEND_destroy(backend_t *backend);

#endif

Noncompliant fragment (do not copy):

#include "database.h"
#include "frontend.h"
#include "logger.h"
#include "network.h"
#include "parser.h"


CMOD-010: Allowed Header-to-Header Dependencies

Class: ARCHITECTURE. Obligation: project requirement.

A project header may include another project header only when it is an approved leaf contract. The original categories are types.h, boundary DTO headers with no module dependencies, boundary callback-contract headers with no module dependencies, and required C standard headers used to spell the ABI.

An include exception needs a type-completeness or ABI-spelling reason; convenience does not qualify. A public module header must not include another module's public or internal header. A foundation directory is not blanket permission to include its contents.

The example's compiler_api.h is the explicitly scoped ABI-annotation leaf for the selected platform profile: macros only, no provider API or state. Its approval does not permit a general dependency-aggregating compiler header.

Complete example: worked sources.

Local examples

Layout example (not executable):

public header includes only types.h or a leaf DTO/callback contract needed to
spell ABI

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

public module header includes another peer module public header for
convenience

CMOD-011: types.h Is a Foundation Leaf

Class: ARCHITECTURE. Obligation: project requirement.

A shared types.h, if present, contains only widely used scalar/value type definitions and the minimum standard headers they require. It must contain no service API, callbacks, allocator interface, mutable state, packing policy, or module-dependent feature logic. Do not introduce types.h when direct standard includes and a narrow status header suffice. Test it alone when the project has it.

Related controls: CSTYLE-036, CSTYLE-034.

Complete example: worked sources.

Local examples

Contextual C example:

#if !defined(COIL_TYPES_H)
  #define COIL_TYPES_H

  #include <stdbool.h>
  #include <stddef.h>
  #include <stdint.h>

typedef int32_t  project_status_t;
typedef uint32_t project_id_t;

#endif


CMOD-012: DTO Headers Are Leaf Contracts

Class: ARCHITECTURE. Obligation: project requirement.

Place a deliberately shared DTO in a contract header with no peer implementation dependencies. Specify units, lengths, capacity, nullability, lifetime, and allowed values. A pointer-bearing DTO is an in-process contract; neither fixed-width fields nor a size_t length make it a portable wire or cross-architecture ABI. Use a distinct, explicit serializer for wire/persistent data. No hidden allocation or ownership transfer may be implied by copying a DTO.

Related controls: CSTYLE-036, CSTYLE-063, CSTYLE-082, CSTYLE-083.

Failure scenarios: CPIT-013, CPIT-029, CPIT-032.

Complete example: worked sources.

Local examples

Contextual C example:

#if !defined(COIL_FRONTEND_REQUEST_DTO_H)
  #define COIL_FRONTEND_REQUEST_DTO_H

  #include "types.h"

typedef struct FrontendRequestDto
{
        const uint8_t *payload;
        size_t         payload_size_bytes;

        uint32_t operation;
        uint32_t request_id;
} frontend_request_dto_t;

#endif


CMOD-013: Callback Contract Headers Are Leaf Contracts

Class: ARCHITECTURE. Obligation: project requirement.

The consumer owns the required operation's port unless an explicitly shared protocol has a separate owner. Define the exact function-pointer type, borrowed context, and semantics independently of the concrete provider. The header may include needed DTO/standard leaves. Do not create a v-table with unrelated capabilities to pass a provider object through another name.

Related controls: CSTYLE-071.

Failure scenarios: CPIT-026, CPIT-032.

Complete example: worked sources.

Local examples

Contextual C example:

typedef int (*frontend_storage_read_cb_t)(void *context, uint32_t object_id,
                                          void   *buffer,
                                          size_t  buffer_size_bytes,
                                          size_t *read_size_bytes);

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

backend_read_cb_t
postgres_read_cb_t
redis_read_cb_t

CMOD-014: Public Headers Must Be Self-Contained

Class: ARCHITECTURE. Obligation: project requirement.

Compile every public and contract header as the first include in a fresh translation unit, using only its documented usage requirements. Test each supported configuration that changes the header. A missing prerequisite is not fixed by reordering the consumer's includes. C++ compatibility is separately qualified when the project claims it.

Related controls: CSTYLE-034.

Complete example: worked sources.

Local examples

Contextual C example:

#include "backend.h"


CMOD-015: Internal Headers Stay Inside the Module

Class: ARCHITECTURE. Obligation: project requirement.

A module's internal headers belong below its private include directory, such as <module>/src/inc. They are consumed by the owning implementation and owned internal tests. Application main.c may also include them under the composition exception in CMOD-021. Give that application target the necessary private include paths; do not add those directories to PUBLIC or INTERFACE include paths. Other modules, ordinary adapters, and external consumers continue to use public contracts.

Complete example: worked sources.

Local examples

Layout example (not executable):

<module>/src/inc/*.h

CMOD-016: Include What the Translation Unit Uses

Class: ARCHITECTURE. Obligation: project requirement.

An implementation includes its own public header first and directly includes the declarations it uses. Required standard headers do not become an interface promise merely because another header includes them. Follow CSTYLE-028. Do not solve missing declarations with local hand-written prototypes or casts.

Complete example: worked sources.

Local examples

Contextual C example:

#include <errno.h>
#include <stdlib.h>

#include "backend.h"
#include "backend_internal.h"
#include "backend_process.h"
#include "frontend_request_dto.h"


CMOD-017: Only the C++ Compatibility Block May Use extern

Class: ARCHITECTURE. Obligation: project requirement.

The only permitted explicit extern in project-authored C interfaces is the guarded extern "C" compatibility block specified by CMOD-019. It is required in every project C header intended for C++ inclusion, including internal and contract headers when they support C++ consumers.

Do not write extern on object declarations, function prototypes, or inline definitions. Normal function prototypes already declare external linkage in C. Do not create cross-translation-unit variables: keep state in owned objects or private static storage. A same-module helper may have a normal prototype in an internal header without becoming part of the public API.

There is no additional exception for project-generated declarations or platform adapters. Foreign vendor and system headers keep their upstream declarations; including them is not permission to copy their storage-class conventions into project-authored code. See CMOD-087 for linker symbols.

Related controls: CSTYLE-088, CMOD-019.

Local examples

Noncompliant fragment (do not copy):

extern int g_global_state;
extern int BACKEND_run(void);

Contextual C example:

int BACKEND_run(void);


CMOD-018: No Cross-Module Global Objects

Class: ARCHITECTURE. Obligation: project requirement.

A module must not publish an object for another module to read or write by name. Use a query operation on an opaque handle or an injected port instead. For peer-to-peer flow, composition binds the operation through a callback. The peer must not receive the provider's public symbol as a direct link dependency.

Use leaf value definitions for constants and private static const tables when needed, rather than a cross-module data symbol. Audit exported data as well as functions; adding const does not exempt an external object from this rule.

Related controls: CSTYLE-082, CSTYLE-088.

Failure scenarios: CPIT-005.

Complete example: worked sources.

Local examples

Noncompliant fragment (do not copy):

extern backend_state_t g_backend_state;

Contextual C example:

int BACKEND_getStatus(const backend_t *backend, backend_status_dto_t *status);


CMOD-019: C++ Compatible Headers Own Their Linkage Guards

Class: ARCHITECTURE. Obligation: project requirement.

Every project C header declared compatible with C++ must own its conditional C-linkage block. This applies to public, internal, and leaf-contract headers. The includer must not be required to wrap #include manually. Use the marker and opening/closing form below, inside the include guard. The block is the sole permitted explicit extern under CMOD-017.

Put the header's C function and callback declarations within this block. Ordinary C-compatible types may be there too. Keep any permitted dependency includes outside the block, particularly C++ standard headers, templates, namespaces, and overloads. The block does not authorize new header dependencies under CMOD-009/010 or turn C++ declarations into C-compatible interfaces. Macro-only headers declared C++ compatible retain the same template even if they have no linkage-bearing entities. Unmodified third-party headers retain their upstream compatibility mechanism.

The closing block precedes the include guard's final #endif. Source files compiled as C do not need a blanket compatibility block around their includes or private implementation. A C++ consumer includes the guarded header normally. Test each claimed C/C++ language baseline, repeated inclusion, include order, and a C++ caller linked against an implementation compiled as C. Parsing alone does not demonstrate matching linkage, layout, calling conventions, or exception behavior.

Local examples

Contextual C/C++ header example:

#if !defined(APP_SERVICE_H)
  #define APP_SERVICE_H

/*< C++ Compatibility >*/
#ifdef __cplusplus
extern "C"
{
#endif

int SERVICE_run(void);

/*< C++ Compatibility >*/
#ifdef __cplusplus
}
#endif

#endif /* APP_SERVICE_H */

Noncompliant fragment (do not copy):

/* Unconditional C++ syntax prevents inclusion by a C compiler. */
extern "C" int SERVICE_run(void);


CMOD-087: Linker and Assembly Symbols Stay in a Boundary Adapter

Class: ARCHITECTURE. Apply the scope and requirement words below.

A platform boundary owns linker-script and assembly-defined symbols. Document whether each symbol represents an address, absolute value, size, or object, its alignment, its producer, and the build variants that define it. Keep that representation out of ordinary modules.

Expose a normal function prototype implemented by a platform or assembly accessor when the raw symbol would otherwise require an explicit extern declaration. The accessor implements the selected C calling convention and validates or translates the result as needed. Do not replace an extern object declaration with a tentative definition: that would define storage rather than reference the linker symbol. CMOD-017 allows no separate linker/platform exception.

Local examples

Noncompliant fragment (do not copy):

extern char linker_heap_start[];

Contextual C example:

const unsigned char *PLATFORM_heapStart(void);


CMOD-020: Modules Depend on Ports, Not Providers

Class: ARCHITECTURE. Obligation: project requirement.

A module defines the external operations it needs as callback ports. Each port describes required behavior, not the concrete provider. Composition binds the provider or adapter. Keep scheduling, failure behavior, lifetime, and backpressure explicit.

Foundation and platform dependencies must meet the original lower-layer approval rules in CMOD-050 and the symbol allowlist in CMOD-062. A module cannot replace a peer port with a direct provider dependency by changing its directory name.

Complete example: worked sources.

Local examples

Contextual C example:

typedef struct ParserCallbacks
{
        void            *emit_context;
        void            *read_context;
        parser_emit_cb_t emit;
        parser_read_cb_t read;
} parser_callbacks_t;


CMOD-021: The Composition Root Owns Binding

Class: ARCHITECTURE. Obligation: project requirement.

The composition layer selects implementations, creates module instances, binds ports, and owns startup/unwind/shutdown ordering. It may name several public APIs. It must keep borrowed contexts and provider code alive until their consumers are quiescent. Failure during construction unwinds only resources already acquired, in a valid reverse dependency order.

Application main.c is the composition file, not a reusable module interface. Every helper, type, macro, and state object defined there is private to that translation unit: file-scope helpers and objects use static, local types and macros stay in the file, and no public/internal application header exports them. Imported declarations retain the linkage and ownership of the header that owns them. Hosted main is the sole entry-point exception: use the standard non-static signature without an explicit extern. Do not give a .c file an include guard or include it as a header.

main.c may include any header needed by the application: C standard library, platform, third-party, module public or internal headers, contracts, and adapters. Peer-module include restrictions do not apply to this composition file. List direct dependencies in its documentation and target settings; keep the extra include paths private to the application target. This permission does not make its own declarations public, transfer module ownership, or relax the invariants of imported interfaces. Other source files do not inherit this scope merely by including the same headers.

Complete example: worked sources.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart LR
    ROOT[Composition root]

    ROOT -->|create| A[Module A]
    ROOT -->|create| B[Module B]
    ROOT -->|bind function pointer| A
    ROOT -->|bind function pointer| B

    A -->|callback call| B
    B -->|callback call| A

CMOD-022: Adapters Own Semantic Translation

Class: ARCHITECTURE. Obligation: project requirement.

Use an adapter when two modules do not share the same callback signature, DTO, error model, units, lifecycle, or threading contract. The adapter may:

  • include both public module headers
  • include the specific DTO headers it translates
  • convert one DTO into another
  • map error domains
  • convert units
  • copy data across ownership boundaries
  • schedule or serialize callbacks when the integration contract requires it The adapter must not include either module's internal headers. Keep adapters small. Integration policy belongs in the composition layer, not in module internals.

Complete example: worked sources.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart LR
    A[Module A]
    AD[Adapter]
    B[Module B]

    A -->|callback A contract| AD
    AD -->|public API or bound callback| B
    B -->|result or callback| AD
    AD -->|A DTO| A

CMOD-023: Callback Context Is Opaque

Class: ARCHITECTURE. Obligation: project requirement.

A callback may receive a void *context owned by the binding layer. The consuming module stores and returns that pointer without interpreting its layout. The consuming module must not:

  • cast the context to a peer module struct type
  • free the context
  • modify the pointed object outside the callback contract
  • publish the context through another public interface Only the callback implementation understands the context.

Related controls: CSTYLE-071, CSTYLE-082, CSTYLE-096.

Failure scenarios: CPIT-032.

Complete example: worked sources.

Local examples

Contextual C example:

module->request_context = callbacks->request_context;
ret = module->request(module->request_context, request, reply);

Noncompliant fragment (do not copy):

backend_t *backend = (backend_t *)module->request_context;
backend->private_state = 1;


CMOD-024: Callback Lifetime Must Be Explicit

Class: ARCHITECTURE. Obligation: project requirement.

Copy a callback table or retain it only under an explicit table-lifetime contract. In either case, the target code and borrowed context must outlive all invocations. Before unbind, replacement, destruction, or dynamic unload, exclude new invocations and drain in-flight calls. A clear-to-NULL assignment alone is not safe concurrent teardown. Single-threaded APIs must state that external synchronization covers destruction too.

Related controls: CSTYLE-071, CSTYLE-082, CSTYLE-084.

Failure scenarios: CPIT-001, CPIT-002, CPIT-032.

Complete example: worked sources.

Local examples

Layout example (not executable):

create(config with callbacks) -> use -> destroy

Layout example (not executable):

create -> bind callbacks -> use -> unbind -> destroy

CMOD-025: Callback Reentrancy Is Forbidden by Default

Class: ARCHITECTURE. Obligation: project requirement.

A callback must not re-enter its invoking instance unless that instance's contract defines the behavior. Reject reentry, defer it, or prove a reentrant state machine. A boolean busy flag can reject synchronous reentry in a single-threaded/external-lock contract; it does not provide thread safety. Document whether callback code may request shutdown and when it takes effect.

Related controls: CSTYLE-071, CSTYLE-090.

Complete example: worked sources.

Local examples

Layout example (not executable):

callback execution -> non-reentrant

CMOD-026: Callback Error Semantics Must Match the Port

Class: ARCHITECTURE. Obligation: project requirement.

Specify callback success, error domain, output validity, partial side effects, timeouts, retries, and cancellation. Translate provider-specific failures in the adapter. Do not promise rollback of an external side effect merely because the consumer returns an error. State idempotency before retrying a failed operation. The example's buffer adapter is all-or-nothing; that is not assumed for I/O.

Complete example: worked sources.

Local examples

Layout example (not executable):

port defines negative error namespace and whether partial output is valid;
adapter maps provider errors explicitly

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

callback returns bool while caller interprets it as errno-style integer

CMOD-027: Translation Units Compile Independently

Class: ARCHITECTURE. Obligation: project requirement.

Compile each implementation file with the declarations and target settings it needs. Link external definitions later. A missing function declaration is a compile defect; an unresolved final symbol is a link defect. Function-pointer binding changes dependency representation but does not remove the dependency's semantic, lifetime, or execution-time obligations.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart LR
    C1[backend_data.c plus headers] --> CC1[compiler]
    CC1 --> O1[backend_data.o]

    C2[backend_process.c plus headers] --> CC2[compiler]
    CC2 --> O2[backend_process.o]

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

cc -c backend_data.c -o backend_data.o
cc -c backend_process.c -o backend_process.o


CMOD-028: A Module Build Must Succeed Without Peer Headers

Class: ARCHITECTURE. Obligation: project requirement.

Inspect compile commands and compiler-generated dependency files. Reject relative-path, generated-header, or transitive inclusion of an unapproved peer header, not only explicit -I paths. Keep a reviewed allowlist for contract, foundation, compiler-runtime, and platform paths. Test a peer build without the other peer's directories available.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Layout example (not executable):

-I.../frontend/inc      while compiling backend
-I.../backend/src/inc   while compiling frontend

CMOD-029: A Module Build Must Not Link a Peer Module

Class: ARCHITECTURE. Obligation: project requirement.

A module target must not use a peer module as a private, public, or interface link dependency.

The composition target performs the final combination. Adapters may link or reference the public APIs of the modules they connect.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Noncompliant fragment (do not copy):

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

target_link_libraries(frontend PRIVATE backend)


CMOD-030: Undefined Peer Symbols Are a CI Failure

Class: ARCHITECTURE. Obligation: project requirement.

For a module artifact, identify external references that are not satisfied within that module or an approved dependency. nm -u on an archive can include references resolved by another member of the same archive; subtract the module's own definitions before classifying them as external. Reject every peer-symbol dependency. Also inspect code and relocations where optimization or LTO obscures names; a prefix scan alone is not a complete architectural proof.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

frontend.o
  U BACKEND_run
  T FRONTEND_run

Layout example (not executable):

frontend.o
  T FRONTEND_run

CMOD-031: Tool Ownership by Build Stage

Class: ARCHITECTURE. Obligation: project requirement.

The compiler emits translation units; a partial linker combines relocatable objects; an archiver packages members; a final linker resolves a program or shared library. Header self-containment tests compile a translation unit that includes the header. A linker map, export map, archive index, and dynamic symbol table serve different purposes and are not interchangeable evidence.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Layout example (not executable):

compiler       .c -> .o
linker -r      .o + .o -> module.o
ar             .o + .o -> libmodule.a
linker -shared .o + .o -> libmodule.so / module.dll
final linker   main.o + modules/libs/adapters -> app.elf / app.exe

CMOD-032: Phase 1: Build Object Files

Class: ARCHITECTURE. Obligation: project requirement.

Compile each object with its owning module's include paths, macros, dialect, warning policy, and ABI options. External references may target owned same-module functions and approved foundation/runtime/platform services. Peer references are not allowed. Record actual compile commands; global include paths can conceal an invalid dependency even if the target list looks correct.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Layout example (not executable):

backend_data.c      + headers -> backend_data.o
backend_process.c   + headers -> backend_process.o
frontend_data.c     + headers -> frontend_data.o
frontend_process.c  + headers -> frontend_process.o
main.c              + headers -> main.o

CMOD-033: Phase 2: Build a Relocatable Module Object

Class: ARCHITECTURE. Obligation: project requirement.

Use a partial link such as the platform's relocatable-link mode only when its benefit is explicit. Preserve needed relocation and export information for the final link. Partial linking does not automatically hide internal globals and can change dead-code selection granularity. Qualify LTO and debug behavior before using this representation in a release pipeline.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart TD
    O1[backend_data.o]
    O2[backend_process.o]
    O3[backend_state.o]
    LR[linker -r]
    MO[backend.o]

    O1 --> LR
    O2 --> LR
    O3 --> LR
    LR --> MO

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

cc -r \
    backend_data.o \
    backend_process.o \
    backend_state.o \
    -o backend.o


CMOD-034: Phase 3A: Build a Static Library

Class: ARCHITECTURE. Obligation: project requirement.

A static library is an archive of object files, not a completed link. It may retain internal global symbols and unresolved references between its members. Audit its members and allowed external dependency set. A shared-library version script does not hide symbols inside the archive. The final consumer still needs the archive's approved link dependencies.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Layout example (not executable):

backend_data.o + backend_process.o
        |
        v
       ar
        |
        v
libbackend.a

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

ar rcs libbackend.a backend_data.o backend_process.o


CMOD-035: Phase 3B: Build a Linux Shared Library

Class: ARCHITECTURE. Obligation: project requirement.

For a Linux ELF shared module, compile position-independent code with hidden default visibility and explicitly mark its supported public declarations. Apply an export allowlist at final shared linking and inspect the actual dynamic exports. Reject unresolved symbols in an ordinary, non-instrumented shared build. A host-resolved plugin ABI needs a documented import contract. A sanitizer test profile may instead resolve instrumentation symbols in the final executable; Clang ASan documents why --no-undefined is inappropriate for that shared test build. Neither exception relaxes the ordinary release import/export audit.

Source context: gcc-codegen; ld-version; asan.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

cc \
    -fPIC \
    -fvisibility=hidden \
    -c backend_data.c \
    -o backend_data.pic.o

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

cc -shared \
    backend_data.pic.o \
    backend_process.pic.o \
    -Wl,--version-script=backend.version.map \
    -Wl,-soname,libbackend.so.1 \
    -o libbackend.so.1.0.0


CMOD-036: Phase 3C: Build a Windows Dynamic Library

Class: ARCHITECTURE. Obligation: project requirement.

For Windows DLLs, specify exported functions, import declarations, calling convention, runtime/allocator compatibility, and the import-library contract. Use explicit export annotations or a DEF allowlist, not automatic export of all non-static symbols. Compile separate static and DLL variants when their macros or ABI settings differ. The included compiler adapter sketches these annotations; Windows execution and DLL loading were not tested in this delivery.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Layout example (not executable):

backend objects
      |
      v
shared linker
  |       |
  v       v
backend.dll
libbackend.dll.a or backend.lib

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

x86_64-w64-mingw32-gcc -shared \
    backend_data.o \
    backend_process.o \
    -Wl,--out-implib,libbackend.dll.a \
    -o backend.dll


CMOD-037: Phase 4: Final Application Link

Class: ARCHITECTURE. Obligation: project requirement.

The final composition target links the application entry point, isolated module artifacts, adapters, and approved foundation libraries.

The final link is the first normal stage that needs the complete application symbol graph.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart TD
    MAIN[main.o]
    A[module_a.o or liba.a]
    B[module_b.o or libb.a]
    AD[adapters.o]
    LINK[final linker]
    APP[app.elf or app.exe]
    MAP[app.map]

    MAIN --> LINK
    A --> LINK
    B --> LINK
    AD --> LINK
    LINK --> APP
    LINK --> MAP

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

cc \
    main.o \
    adapters.o \
    backend.o \
    frontend.o \
    -Wl,-Map=app.map \
    -o app.elf


CMOD-038: Static Library Link Order

Class: ARCHITECTURE. Obligation: project requirement.

Static archive resolution can depend on linker semantics and ordering. Represent approved foundation and third-party dependencies in build targets so the final link receives them. Do not conceal peer cycles with linker groups. Link-order workarounds for a foreign library belong to its adapter and must be documented. Callback peers do not make all other archive ordering irrelevant.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

cc main.o libconsumer.a libprovider.a -o app.elf


CMOD-039: Linker Report Map and Export Map Are Different Files

Class: ARCHITECTURE. Obligation: project requirement.

A report map describes link layout and contributions; an export/version map constrains a shared object's exposed symbols and, where configured, versions. Use different filenames and review them for their respective purpose. A nonempty report map is not evidence that an export allowlist was applied.

Source context: cmake-link; cmake-build; cmake-standard.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

-Wl,-Map=app.map

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

-Wl,--version-script=backend.version.map


CMOD-106: Reproducible Build Inputs and Metadata

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

A release profile must define which build inputs may influence the artifact and must remove accidental dependence on wall-clock time, absolute checkout paths, filesystem enumeration order, locale, timezone, archive metadata, random seeds, and host-specific generator output.

The build owner must record the toolchain and dependency versions and must use a stable input order. When generated output needs a source-derived timestamp, use the project reproducible-build mechanism, such as SOURCE_DATE_EPOCH, instead of the build clock. Reproducible Builds documents stable input order, locale control, and SOURCE_DATE_EPOCH for this purpose.reproducible-stable-inputs reproducible-source-date-epoch

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

export LC_ALL=C
export TZ=UTC
export SOURCE_DATE_EPOCH="$(git log -1 --format=%ct)"
cmake -S . -B build -DPROJECT_RELEASE=ON
cmake --build build --target release

Noncompliant fragment (do not copy):

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

date > generated/build_timestamp.txt
find src -type f > generated/source_order.txt


CMOD-040: Private Functions Use static

Class: ARCHITECTURE. Obligation: project requirement.

A function used by one translation unit must use internal linkage. Do not declare a private helper in a header.

Related controls: CSTYLE-003.

Local examples

Contextual C example:

static int backend_parseLine(const char *line)
{
        int ret = EXIT_SUCCESS;

        if (line == (const char *)(NULL))
        {
                ret = -EINVAL;
                goto function_output;
        }

        /* implementation */

function_output:
        return ret;
}


CMOD-041: Module-Internal Cross-TU Functions Stay Hidden

Class: ARCHITECTURE. Obligation: project requirement.

Cross-TU internal functions use external linkage only within their owning module and must stay outside the public dynamic ABI. Keep their prototypes in internal headers and use hidden visibility/export controls for shared variants. A static archive may still carry those names for its own final resolution. Optional LTO/localization must not break relocations, debugging, or ABI checks.

Source context: gcc-codegen; ld-version.

Local examples

Layout example (not executable):

cross-TU module helper lives in internal header and is hidden from final
shared-library export table

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

cross-TU internal helper becomes a public dynamic symbol

CMOD-042: Public Symbols Need an Explicit Export Contract

Class: ARCHITECTURE. Obligation: project requirement.

Maintain an explicit set of exported public symbols for each shared ABI. Compare the produced dynamic symbol table against that set in both directions: no unapproved exports and no missing required exports. ELF visibility attributes and linker scripts must agree. For Windows, inspect PE exports. Do not treat all symbols reported by a generic symbol-table command as dynamic exports.

Source context: gcc-codegen; ld-version.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

BACKEND_1.0 {
    global:
        BACKEND_create;
        BACKEND_destroy;
        BACKEND_handleFrontendRequest;

    local:
        *;
};

CMOD-043: Public Symbol Naming Must Reveal Only the Public API

Class: ARCHITECTURE. Obligation: project requirement.

Public symbols use the uppercase module prefix defined by CSTYLE-003; internal and private helpers use its lowercase form. The export manifest, not the spelling alone, determines the dynamic ABI. Review every new public-looking symbol against the intended API and reject accidental exports. A private helper still needs static.

Related controls: CSTYLE-003.

Local examples

Layout example (not executable):

Public API:
  BACKEND_create
  BACKEND_destroy
  BACKEND_handleFrontendRequest

Internal module API:
  backend_dataLoad
  backend_processStep

Private translation-unit helper:
  static backend_parseLine

CMOD-044: Public API Types Prefer Opaque Handles

Class: ARCHITECTURE. Obligation: project requirement.

Expose incomplete object types when clients should not depend on representation. The owning module creates, initializes, and destroys them using its documented storage contract. An opaque handle does not validate arbitrary pointers and is not a capability security boundary in a shared native address space. Caller-supplied storage requires a separate qualified alignment/lifetime design.

Local examples

Contextual C example:

typedef struct Backend backend_t;


CMOD-107: Public APIs Have a Lifecycle and Deprecation Policy

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

Each public API belongs to a named maturity state such as experimental, unstable, stable, deprecated, or retired. The project must define which states permit source or ABI breaks, how users learn about a change, and how long a stable API remains available after deprecation.

A deprecation must identify the replacement, first deprecated release, earliest removal release, and migration test coverage. Stable API removal requires an approved compatibility decision. Zephyr uses explicit experimental, unstable, stable, deprecated, and retired states and requires a deprecation period for stable APIs.zephyr-api-lifecycle

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

DEVICE_read:
  state: stable
  since: 3.0.0

DEVICE_readLegacy:
  state: deprecated
  deprecated_since: 4.2.0
  earliest_removal: 5.0.0
  replacement: DEVICE_read

Noncompliant fragment (do not copy):

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

DEVICE_readLegacy:
  state: removed
  replacement: null
  migration_window: null


CMOD-045: Release Artifacts Minimize Discoverable Symbols

Class: ARCHITECTURE. Obligation: project requirement.

Minimize unnecessary release exports and metadata without removing required runtime, diagnostics, unwind, or support information. Evaluate hidden visibility, section garbage collection, and LTO under the target profile. They are different mechanisms and none is a secrecy guarantee. Do not enable identical-code folding when the product relies on distinct function addresses unless that reliance is removed or the behavior is qualified.

Source context: gcc-codegen; ld-version.

Local examples

Layout example (not executable):

-fvisibility=hidden
-ffunction-sections
-fdata-sections
-flto
-Wl,--gc-sections
-Wl,--version-script=<module>.version.map

CMOD-046: Ship Stripped Release Binaries

Class: ARCHITECTURE. Obligation: project requirement.

Where deployment allows stripping, retain matching unstripped artifacts or separate debug files in controlled engineering storage. Record build identity so crash addresses can be symbolized against the exact binary. Verify the shipped binary after stripping and signing. Do not strip relocatable libraries as though they were final executables.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

objcopy --only-keep-debug app.elf app.debug
strip --strip-unneeded app.elf


CMOD-047: Do Not Embed Internal Names Without Need

Class: ARCHITECTURE. Obligation: project requirement.

Avoid shipping unnecessary source paths, build-user paths, debug-only names, and unused diagnostic strings. Use qualified compiler path-remapping options to avoid embedding host-specific paths. Preserve diagnostics, unwind data, and identifiers required for safety, support, and incident response. Stable numeric error IDs may replace implementation-specific text when an internal decoder and the product diagnostic contract support that choice.

Local examples

Layout example (not executable):

-ffile-prefix-map=<build-root>=.
-fdebug-prefix-map=<build-root>=.

CMOD-048: Reverse-Engineering Resistance Does Not Replace Security

Class: ARCHITECTURE. Obligation: project requirement.

The project may reduce binary metadata, symbol names, and public ABI surface. The design must still assume that an analyst can inspect machine code, memory, control flow, constants, and external behavior. Do not place secrets, private keys, credentials, trust decisions, or authorization rules behind symbol hiding as the only control.

Local examples

Layout example (not executable):

authorization and key protection remain correct even if attacker knows every
symbol and control-flow edge

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

assume hidden function name prevents attacker from reaching privileged
operation

CMOD-108: Production Binaries Use a Named Hardening Profile

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

Each production target must select a reviewed compiler and linker hardening profile for its operating system, architecture, ABI, and threat model. The profile owns the exact flags and the CI checks that prove the resulting binary contains the requested protections.

A hosted ELF profile should evaluate fortification, stack protection, stack- clash protection, PIE/PIC, non-executable stack metadata, full RELRO, and architecture control-flow or branch protection when the toolchain and target support them. Embedded profiles should map the same goal to MPU/PMP, execute permissions, stack protection, branch protection, and linker-region policy as available. OpenSSF documents current GCC and Clang hardening options and their target constraints.openssf-hardening

Keep flags in one target-owned build profile:

Do not scatter one-off hardening flags across unrelated targets:

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

add_library(project_hardening INTERFACE)

target_compile_options(
    project_hardening
    INTERFACE
        -fstack-protector-strong
        -fstack-clash-protection
)

target_link_options(
    project_hardening
    INTERFACE
        -Wl,-z,relro
        -Wl,-z,now
        -Wl,-z,noexecstack
)

Noncompliant fragment (do not copy):

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

target_compile_options(module_a PRIVATE -fstack-protector-strong)
target_link_options(module_b PRIVATE -Wl,-z,relro)


CMOD-049: One Target Owns One Module Build

Class: ARCHITECTURE. Obligation: project requirement.

Each module CMakeLists owns its source list, public/internal include paths, compile requirements, and artifact variants. Use target-scoped options and usage requirements. Avoid global include/link settings that permit unrelated peer access. Build-interface and install-interface paths need separate handling when packaging an SDK; the example is a build-tree demonstration, not an installer.

Source context: cmake-link; cmake-build; cmake-standard.

Complete example: worked sources.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

add_library(backend_obj OBJECT
    src/backend.c
    src/backend_data.c
    src/backend_process.c
)

target_include_directories(backend_obj
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/inc
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src/inc
        ${PROJECT_SOURCE_DIR}/foundation/inc
        ${PROJECT_SOURCE_DIR}/integration/contracts
)


CMOD-050: Module Targets Do Not Link Peer Modules

Class: ARCHITECTURE. Obligation: project requirement.

A peer module must not appear in target_link_libraries() for another peer module, whether the dependency is PUBLIC, PRIVATE, or INTERFACE.

Allowed module dependencies include approved foundation libraries and platform abstraction libraries that form a lower architectural layer, as in the supplied baseline. Model those lower layers explicitly; they are not a shortcut around callback boundaries. CMake propagation keywords do not grant architectural permission.

Source context: cmake-link; cmake-build; cmake-standard.

Complete example: worked sources.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

add_library(foo_obj OBJECT ${FOO_SOURCES})
# no target_link_libraries(foo_obj bar)

Noncompliant fragment (do not copy):

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

target_link_libraries(foo PRIVATE bar)


CMOD-051: Adapters Are Separate Targets

Class: ARCHITECTURE. Obligation: project requirement.

Build adapters as their own targets with only the public interfaces they translate. The composition root also legitimately sees multiple peer APIs. Do not expose internal include paths through an adapter. Keep a stateless glue adapter small; a stateful scheduler or policy service needs its own lifecycle and tests rather than being hidden as glue.

Source context: cmake-link; cmake-build; cmake-standard.

Complete example: worked sources.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

add_library(frontend_backend_adapter OBJECT
    adapters/frontend_backend_adapter.c
)

target_include_directories(frontend_backend_adapter
    PRIVATE
        ${PROJECT_SOURCE_DIR}/frontend/inc
        ${PROJECT_SOURCE_DIR}/backend/inc
        ${PROJECT_SOURCE_DIR}/integration/contracts
        ${PROJECT_SOURCE_DIR}/foundation/inc
)


CMOD-052: Build Object, Static, and Shared Variants From the Same Sources

Class: ARCHITECTURE. Obligation: project requirement.

Reuse the same source list across object/static/shared variants, but reuse compiled objects only when PIC, export/import definitions, ABI flags, sanitizer, and LTO settings are compatible. The example compiles static and shared objects separately. $<TARGET_OBJECTS:...> alone supplies objects, not every usage requirement; model include/compile/link propagation explicitly or link the object target as intended by the qualified CMake version.

Source context: cmake-link; cmake-build; cmake-standard.

Complete example: worked sources.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

add_library(backend_static STATIC
    $<TARGET_OBJECTS:backend_obj>
)

set_target_properties(backend_static PROPERTIES
    OUTPUT_NAME backend
)


CMOD-053: Shared Targets Apply Export Controls

Class: ARCHITECTURE. Obligation: project requirement.

Attach shared-library export options to the shared target's link step. Mark an export script as a link dependency so edits cause relinking. Check the output with the platform's dynamic-export tool. For versioned releases, add a reviewed SONAME/ABI version policy; the example's unversioned build-tree library does not promise an installable stable SDK.

Source context: cmake-link; cmake-build; cmake-standard.

Complete example: worked sources.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

add_library(backend_shared SHARED
    src/backend.c
    src/backend_data.c
    src/backend_process.c
)

set_target_properties(backend_shared PROPERTIES
    C_VISIBILITY_PRESET hidden
    VISIBILITY_INLINES_HIDDEN YES
    OUTPUT_NAME backend
    VERSION 1.0.0
    SOVERSION 1
)

if(UNIX AND NOT APPLE)
    target_link_options(backend_shared PRIVATE
        "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/backend.version.map"
    )
endif()


CMOD-054: Public API Tests Use Only the Public Boundary

Class: ARCHITECTURE. Obligation: project requirement.

A module-isolated public API test includes that module's public headers, approved foundations, and its port/DTO contracts, never its private headers. Integration tests may include the public interfaces of several modules because the composition layer owns those tests. Label the distinction: passing an integration test is not evidence that a peer compiles without the other peer.

Complete example: worked sources.

Local examples

Contextual C example:

#include "backend.h"
#include "frontend_request_dto.h"


CMOD-055: Internal Tests Belong to the Module

Class: ARCHITECTURE. Obligation: project requirement.

An internal test may include a module internal header only when the module build owns that test target. A test for another module must not use that access path.

Complete example: worked sources.

Local examples

Contextual C example:

#include "backend_internal.h"


CMOD-056: Private Functions Stay Private During Tests

Class: ARCHITECTURE. Obligation: project requirement.

Exercise private helpers through public or owned internal behavior. Do not remove static or install a test-only public symbol to make tests convenient. A behavior that needs independent unit tests may receive a real internal contract without becoming public. Coverage instrumentation must not redefine ownership or production ABI.

Complete example: worked sources.

Local examples

Layout example (not executable):

test private behavior through public/internal observable contract or same-TU
test technique that does not export helper

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

remove static from private helper so unit test can link it

CMOD-057: Callback Ports Need Mocks

Class: ARCHITECTURE. Obligation: project requirement.

Inject controlled port implementations and test success, failure, empty input, capacity edges, missing required callbacks, allowed NULL contexts, retention, and reentry/teardown behavior. Negative tests may exercise documented rejection paths; they must not execute undefined behavior. A mock does not prove the real provider meets timing, thread, hardware, or I/O guarantees.

Complete example: worked sources.

Local examples

Layout example (not executable):

test binds mock callback/context through the same port contract used in
production

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

test links the real peer module just to satisfy a direct peer symbol

CMOD-058: Header Boundary Checks

Class: ARCHITECTURE. Obligation: project requirement.

Compile every public and boundary-contract header as the first include in a probe. Do the same for the selected foundation headers, including types.h if the project uses it. Exercise each supported public compile-definition variant. Reject peer public-header dependencies, unrelated transitive convenience includes, public exposure of internal paths, and adapters including internals. A macro-only probe may add the harmless declaration allowed by CSTYLE-034.

Local examples

Layout example (not executable):

CI compiles each public/leaf contract header alone and rejects transitive
include dependence

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

headers are tested only indirectly through the full application build

CMOD-059: extern Checks

Class: ARCHITECTURE. Obligation: project requirement.

Inspect project-authored C sources and headers for explicit extern. Accept only the balanced, __cplusplus-guarded extern "C" form required by CMOD-019, with its compatibility markers. Reject storage-class declarations, unguarded linkage specifications, and missing closing guards. Do not introduce a platform or generated-code allowlist for additional project-authored uses. Keep unmodified third-party/system source outside this project-authored token check.

Use token-aware inspection with preprocessor context so comments, string literals, and inactive branches do not create false positives or hide violations. A raw grep for extern cannot decide acceptance because the required compatibility block contains that token. Compile C and C++ probes and link a C++ consumer against C objects for every header claiming that compatibility. Include-graph, object-ownership, and export checks remain separate obligations.


CMOD-060: Include-Graph Checks

Class: ARCHITECTURE. Obligation: project requirement.

Generate or inspect the include graph for every qualified variant. Reject peer implementation edges, peer internal headers, and public exposure of private paths in module targets. Classify application main.c separately: its direct includes are permitted by CMOD-021, including internal headers; its private include paths must not propagate to modules. Distinguish a true contract leaf from an umbrella carrying dependencies. Simple literal-include scans are useful regression checks but do not prove all macro-generated or conditionally inactive include paths are valid.

Source context: nm; readelf.

Local examples

Layout example (not executable):

module A header -> module B header
module A source -> module B header
module A -> module B src/inc

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart TD
    TH[types.h]
    DTO[DTO leaf]
    CA[Module A headers]
    CB[Module B headers]

    TH --> CA
    TH --> CB
    DTO --> CA
    DTO --> CB

    CA -. forbidden .-> CB
    CB -. forbidden .-> CA

CMOD-061: Link-Graph Checks

Class: ARCHITECTURE. Obligation: project requirement.

Validate the target graph against the approved peer/foundation/adapter/root classification. Reject peer-to-peer link edges even when they are PRIVATE or hidden behind an INTERFACE target. Test the final link too: an apparently valid graph can still omit required runtime or foundation dependencies.

Source context: nm; readelf.

Local examples

Layout example (not executable):

module_a_obj ----\
module_b_obj -----+--> composition/app
adapter_obj ------/

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

module_a -> module_b
module_b -> module_a

CMOD-062: Undefined-Symbol Checks

Class: ARCHITECTURE. Obligation: project requirement.

Inspect object/archive references and account for definitions supplied inside the same module. Compare remaining external symbols to an exact approved set, including compiler/runtime helpers. Reject peer APIs regardless of naming. A prefix blacklist is a useful additional check, not a substitute for ownership classification. Qualify the symbol tool's handling of LTO objects.

Source context: nm; readelf.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

nm -u backend.o
nm -u libbackend.a
readelf -Ws backend.o


CMOD-063: Public Export Checks

Class: ARCHITECTURE. Obligation: project requirement.

Read ELF dynamic symbols or PE export entries rather than the entire debug or static symbol table. Normalize version suffixes only under the ABI's versioning policy. Compare names and, where ABI stability matters, signatures/layouts/version contracts. This example checks exported names; it does not perform a complete cross-version binary compatibility proof.

Source context: nm; readelf.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

readelf -Ws libbackend.so
objdump -T libbackend.so

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

llvm-readobj --coff-exports backend.dll
llvm-objdump -p backend.dll


CMOD-064: Static Archive Symbol Checks

Class: ARCHITECTURE. Obligation: project requirement.

Inspect archive members, defined global names, and unresolved external names. Separate module-internal resolution from external dependencies. Reject leaked public-looking APIs and unauthorized externally linked data. Internal globals needed between members need not be stripped before final resolution. No archive is declared hidden merely because a version script exists nearby.

Source context: nm; readelf.

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

ar t libbackend.a
nm -g --defined-only libbackend.a


CMOD-065: Release Metadata Checks

Class: ARCHITECTURE. Obligation: project requirement.

Inspect actual release outputs for unneeded debug information, full build paths, extra exports, and sensitive strings. Retain the required diagnostic and symbolization artifacts with the matching build ID. This is separate from secret scanning, signing, source review, and a security assessment. An absence of names does not show an absence of secrets.

Source context: nm; readelf.

Local examples

Layout example (not executable):

CI verifies stripped/debug-prefix-map/reproducibility metadata policy for
release artifact

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

release package contains source paths and full debug metadata by accident

CMOD-109: ABI Compatibility Is a Release Gate

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

A module that promises a stable binary ABI must compare the candidate artifact against the supported baseline before release. The gate must inspect exported symbols and, where the platform exposes enough debug/type metadata, reachable public type changes. A source-compatible header does not prove binary compatibility.

On ELF targets, a tool such as libabigail abidiff can compare two shared libraries and report ABI changes, including removed exported symbols and type changes.libabigail-abidiff

Local examples

Contextual example:

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

abidiff artifacts/baseline/libdevice.so artifacts/candidate/libdevice.so

Noncompliant fragment (do not copy):

Build/configuration excerpt for the named profile; resolve paths and targets in that project.

nm -D artifacts/candidate/libdevice.so > candidate.symbols
# No baseline comparison occurs.


CMOD-066: No Peer Header Dependency

Class: ARCHITECTURE. Obligation: project requirement.

Reject a change when one peer module needs another peer module header to compile. Create a callback port, leaf contract, or adapter instead.

Local examples

Layout example (not executable):

foo public/internal headers reference only foo + approved leaf contracts

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

foo.h includes bar.h to call peer API

CMOD-067: No Peer Symbol Dependency

Class: ARCHITECTURE. Obligation: project requirement.

Reject a change when one peer module object contains a direct undefined reference to another peer module public symbol. Bind the operation as a callback in the composition layer.

Local examples

Layout example (not executable):

foo artifact communicates through injected port and has no BAR_* undefined
symbol

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

foo.o directly references BAR_process

CMOD-068: No Cross-Module Struct Ownership

Class: ARCHITECTURE. Obligation: project requirement.

Reject a change when a module:

  • allocates another module's private struct
  • uses sizeof on another module's opaque type
  • accesses another module's fields
  • embeds another module's private context by value
  • frees another module's object without that module's API contract Use an opaque handle or DTO.

Local examples

Layout example (not executable):

boundary uses DTO/opaque handle; each module allocates/frees its own private
representation

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

module A allocates sizeof(module_b_private_t) and module B later frees it

CMOD-069: No Architecture Through Static Link Order

Class: ARCHITECTURE. Obligation: project requirement.

Document every dependency before link ordering is considered. Reject peer cycles; permit approved acyclic foundation/foreign dependencies with explicit target metadata. Do not use linker groups or whole-archive mode to hide missing architecture decisions. Review their code-size and dead-code effects when they are legitimately needed.

Local examples

Layout example (not executable):

module graph is explicit in composition/binding; static archive order is only
a linker detail for external legacy deps

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

A must precede B in link line because A directly references B, so link order
encodes architecture

CMOD-070: No Header as a Dependency Aggregator

Class: ARCHITECTURE. Obligation: project requirement.

Do not create a module header whose purpose is to include a group of unrelated module headers.

The composition source may include those public headers directly because it owns the integration.

Local examples

Noncompliant fragment (do not copy):

#if !defined(COIL_PROJECT_ALL_H)
#define COIL_PROJECT_ALL_H

#include "backend.h"
#include "frontend.h"
#include "network.h"
#include "storage.h"

#endif


CMOD-071: Compile-Time Dependency Shape

Class: ARCHITECTURE. Obligation: project requirement.

The reference compile graph is buffer -> checked/contracts and emitter -> contracts; neither peer includes or links the other. The buffer sink adapter and composition see both public APIs. Contract dependencies remain acyclic. The graph describes permitted build knowledge, not runtime call order.

Complete example: worked sources.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart TD
    FOUNDATION[Foundation leaf headers and libraries]
    CONTRACTS[DTO and callback contracts]

    MODA[Module A]
    MODB[Module B]
    MODC[Module C]

    ADAPTERS[Adapters]
    COMPOSE[Composition root]
    APP[Final application]

    FOUNDATION --> MODA
    FOUNDATION --> MODB
    FOUNDATION --> MODC

    CONTRACTS --> MODA
    CONTRACTS --> MODB
    CONTRACTS --> MODC

    MODA --> ADAPTERS
    MODB --> ADAPTERS
    MODC --> ADAPTERS

    MODA --> COMPOSE
    MODB --> COMPOSE
    MODC --> COMPOSE
    ADAPTERS --> COMPOSE
    COMPOSE --> APP

CMOD-072: Runtime Communication Shape

Class: ARCHITECTURE. Obligation: project requirement.

The reference runtime path is composition -> emitter -> injected write port -> buffer sink adapter -> buffer. The adapter names the buffer API; the emitter does not. The binder retains the buffer until the emitter is destroyed. A runtime cycle would require a separate reentrancy and scheduling decision.

Complete example: worked sources.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart LR
    A[Module A]
    B[Module B]
    C[Module C]
    X[Adapter]

    A -->|callback| B
    B -->|callback| A
    A -->|callback| X
    X -->|callback or public entry| C

CMOD-073: Progressive Artifact Shape

Class: ARCHITECTURE. Obligation: project requirement.

The example compiles separate static and shared variants from the same owned sources. Both variants use the same public behavioral contract and tests. An object/partial-link packaging mode is described by CMOD-033 and CMOD-077 but is not included in this executed matrix. Artifact packaging does not replace module ownership or ABI qualification.

Complete example: worked sources.

Local examples

Contextual example:

Architecture/data-flow example; arrows do not establish a memory-protection boundary.

flowchart TD
    SRC[Module .c files]
    OBJ[Translation-unit .o files]
    PICK{Artifact mode}
    REL[module.o]
    STA[libmodule.a]
    DYN[libmodule.so or module.dll]
    FINAL[Final linker]
    APP[app.elf or app.exe]

    SRC -->|compiler| OBJ
    OBJ --> PICK
    PICK -->|linker -r| REL
    PICK -->|ar| STA
    PICK -->|shared linker| DYN

    REL --> FINAL
    STA --> FINAL
    DYN --> FINAL
    FINAL --> APP

CMOD-074: Where a Declaration Belongs

Class: ARCHITECTURE. Obligation: project requirement.

Place a one-TU declaration in its source; place a same-module cross-TU contract in an internal header; place a supported inbound operation in the public header; place a consumer dependency port in its contract header. Place integration translation in the adapter. Do not make a declaration public only because an unrelated source wants to use it.

Local examples

Layout example (not executable):

Used by one .c file?
  -> static declaration and definition in that .c file

Used by multiple .c files in one module?
  -> internal header under src/inc

Needed by the composition layer or external consumer?
  -> public module header

Needed by a peer module?
  -> callback port or DTO contract, then composition binding

Needed by two modules with incompatible contracts?
  -> adapter plus explicit DTO translation

CMOD-075: Where a Type Belongs

Class: ARCHITECTURE. Obligation: project requirement.

A private context belongs to its module implementation. An opaque handle belongs to its public header. A shared value DTO belongs to an owned contract leaf; a wire format belongs to a protocol specification and serializer. A platform-specific representation belongs to its adapter. Move a type outward only when its new compatibility and ownership obligations are accepted.

Local examples

Layout example (not executable):

Module implementation detail?
  -> internal header or .c file

Public object identity without public layout?
  -> opaque declaration in module public header

Project-wide scalar foundation type?
  -> types.h after review

Boundary value shared by isolated modules?
  -> leaf DTO contract

Provider-specific implementation type?
  -> provider module only

CMOD-076: Where a Call Belongs

Class: ARCHITECTURE. Obligation: project requirement.

A private helper call stays inside its translation unit or module. A peer capability call uses an injected port. An approved lower-layer utility may be a direct named call. A translation between peer semantics belongs to an adapter. The composition root chooses concrete implementations and owns lifetime order.

Local examples

Layout example (not executable):

Call within one translation unit?
  -> static function

Call across translation units in one module?
  -> internal module function

Call from application/composition into a module?
  -> public API

Call from one peer module toward another capability?
  -> injected callback

Call needs data or semantic conversion?
  -> callback implemented by an adapter

CMOD-077: Object Module Mode

Class: ARCHITECTURE. Obligation: project requirement.

Use a relocatable module object for a modular monolith or embedded application that benefits from one object per module. Audit the module object because a partial link does not hide every internal global symbol on its own.

Local examples

Layout example (not executable):

backend/*.o  -> linker -r -> backend.o
frontend/*.o -> linker -r -> frontend.o
main.o + adapters.o + backend.o + frontend.o -> app.elf

CMOD-078: Static Library Mode

Class: ARCHITECTURE. Obligation: project requirement.

Use a static library for reusable modules that the final application links into one executable. The archive preserves object-level symbol information. Apply symbol and ownership checks before release.

Local examples

Layout example (not executable):

backend/*.o  -> ar -> libbackend.a
frontend/*.o -> ar -> libfrontend.a
main.o + adapters.o + libraries -> app.elf

CMOD-079: Dynamic Library Mode

Class: ARCHITECTURE. Obligation: project requirement.

Use a shared library or DLL when runtime replacement, ABI versioning, process sharing, plugin loading, or deployment boundaries justify the dynamic ABI. Require:

  • hidden default visibility
  • explicit export allowlist
  • ABI version policy
  • symbol audit
  • runtime loader test
  • stripped release artifact when the platform permits it

Local examples

Layout example (not executable):

shared module exports allowlisted public ABI and hides internals

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

shared module exports helpers, peer callbacks, and private data symbols by
default

CMOD-080: Required Checks Before Merge

Class: ARCHITECTURE. Obligation: project requirement.

A merge must meet the applicable profile: clean compilation, required static analysis, header self-containment, approved include/link graph, authorized symbol sets, deterministic contract tests, failure-path tests, and reviewed deviations. Check the restored local policies too: typed NULL, explicit void-pointer conversions, function-entry declarations, single-exit returns, and loop control. Accept explicit extern only in the guarded compatibility block. Reject peer header or direct-symbol edges in module targets; evaluate application main.c under CMOD-021.

Release adds production configuration, dependency inventory/exposure review, artifact integrity, ABI/export review, and target-specific evidence. A skipped or unavailable check is not a pass. The delivery record states which of these checks were executed on the supplied example.


CMOD-081: Core Architecture Summary

Class: ARCHITECTURE. Obligation: project requirement.

The translation unit owns private helpers. The module owns representation, state, lifecycle, public behavior, and required ports. Contract owners own shared value/port schemas. Adapters own conversion. Composition owns binding and system lifetime. Build/release owners qualify artifacts and preserve evidence. Apply minimum visibility throughout without mistaking it for process isolation.

Related controls: CMOD-001, CMOD-081.

Local examples

Layout example (not executable):

translation unit
  owns private static helpers

module
  owns implementation, state, internal API, public inbound API, callback ports

boundary contract
  owns narrow DTO and callback types shared across isolated builds

adapter
  owns semantic conversion between module contracts

composition root
  owns concrete module binding and final application graph

release linker
  owns final symbol resolution, export control, dead-code removal, and link map

Layout example (not executable):

.c + leaf headers
    -> compiler
    -> .o
    -> linker -r / ar / shared linker
    -> module artifact
    -> composition root + adapters + modules
    -> final linker
    -> stripped and audited application artifact

CMOD-082: One Module Owns Each External Trust Boundary

Class: SECURITY / ARCHITECTURE. Obligation: project requirement.

Assign one policy owner to each external ingress or privileged operation. Specify accepted representation/version, byte/work/time budgets, authentication, object-level authorization, validation, interpretation, failure behavior, and audit events. Lower-level modules still validate their own API preconditions. In-process DTOs are not tamper-proof capabilities; a native peer with memory corruption is not contained merely by this architectural boundary.

Related controls: CSTYLE-059, CSTYLE-110, CSTYLE-111, CSTYLE-113.

Failure scenarios: CPIT-094, CPIT-105, CPIT-110, CPIT-111, CPIT-115.

Source context: authorization; validation.

Local examples

Layout example (not executable):

network request -> protocol/input adapter -> validated DTO -> module API
file/update      -> ingress verifier       -> validated artifact -> owner
IPC/CLI          -> boundary adapter       -> validated command -> owner

CMOD-083: Production Security Configuration Is a Controlled Artifact

Class: SECURITY / ARCHITECTURE. Obligation: project requirement.

Production behavior must not depend on a developer remembering to disable an unsafe mode manually.

  • production, test, factory, and development variants are explicit build/runtime profiles
  • production defaults use the least-privileged valid state
  • test/debug bypasses are absent from production when practical; otherwise they require the normal authorization policy and are disabled by default
  • default credentials and embedded production secrets are forbidden
  • security-relevant configuration is validated before activation
  • an invalid or missing security setting does not silently select a permissive fallback
  • release CI verifies the production configuration profile Configuration ownership belongs to a module or composition-layer policy owner; it must not be duplicated across unrelated modules.

Related controls: CSTYLE-107, CSTYLE-114.

Failure scenarios: CPIT-096, CPIT-105, CPIT-116, CPIT-120.

Source context: supply-chain; ssdf.

Local examples

Layout example (not executable):

production profile disables debug/factory bypasses by default and CI verifies
configuration artifact

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

production behavior depends on operator remembering to unset DEBUG_UNLOCK

CMOD-084: Dependency and Artifact Integrity Is Part of Module Ownership

Class: SECURITY / ARCHITECTURE. Obligation: project requirement.

Record each dependency's identity, version, source, license, owner, pin, and shipped exposure. Verify artifacts against a trust root: a hash obtained from the same untrusted source is not independent authenticity evidence. Keep an update/removal path, a vulnerability-review cadence, and provenance/inventory records. Signed firmware/update metadata must include target identity and the product's anti-rollback policy. API wrapping alone does not make an artifact trustworthy.

Related controls: CSTYLE-029, CSTYLE-059, CSTYLE-115.

Failure scenarios: CPIT-101, CPIT-102, CPIT-105, CPIT-117, CPIT-118.

Source context: supply-chain; ssdf.

Local examples

Layout example (not executable):

dependency has owner, pinned version, approved source, SBOM entry,
integrity/provenance check, CVE/KEV review path

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

build downloads latest.tar.gz from mutable URL with no pin/hash/signature

CMOD-085: Privileged Capabilities Stay With Their Security Owner

Class: SECURITY / ARCHITECTURE. Obligation: project requirement.

Give a consumer only the authority its operation needs. The policy owner checks identity, operation, and concrete resource; adapters must not broaden that authority. Avoid passing unrestricted filesystem roots, credentials, or network clients for convenience. Native pointers are not an isolation boundary; use a process, hardware protection, or another enforced boundary where the threat model requires containment between mutually untrusted components.

Related controls: CSTYLE-110, CSTYLE-112.

Failure scenarios: CPIT-105, CPIT-112, CPIT-113, CPIT-114.

Source context: authorization; validation.

Local examples

Layout example (not executable):

request -> auth boundary -> narrow authorized capability -> module

Layout example (not executable):

request -> module receives global credential/root handle -> decides everything

CMOD-086: Runtime Loaders and Plugins Are Explicit Trust Boundaries

Class: SECURITY / ARCHITECTURE. Obligation: project requirement.

Centralize component identity, search roots, permissions, authenticity, ABI/version negotiation, and load/unload policy in a loader owner. Pin the file loaded where the platform permits it, rather than checking one path and later loading a different object. Do not unload code while callbacks, threads, TLS destructors, or objects may still reference it. Test load failure, wrong ABI, rejected paths, and teardown on each supported platform.

Related controls: CSTYLE-115.

Failure scenarios: CPIT-105, CPIT-118, CPIT-121.

Source context: supply-chain; ssdf.

Local examples

Layout example (not executable):

loader uses allowlisted path/origin, validates signature/ABI/capability policy
before activation

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

dlopen() searches current working directory or environment-controlled path for
privileged plugin

CMOD-088: Public Headers May Use a Narrower Language Profile

Class: ARCHITECTURE. Apply the scope and requirement words below.

A public ABI header may target a more conservative C language version and feature subset than private implementation files. The module must declare and test the public-header profile separately.

The public profile must define:

  • minimum C version and compiler families;
  • permitted attributes and compatibility macros;
  • integer, enum, alignment, and calling-convention assumptions;
  • C/C++ bridge ownership when applicable;
  • a self-contained compile matrix for supported consumers.

Implementation-only C23 or compiler features must not leak into a C11 public contract merely because the owning module can compile them.

Local examples

Layout example (not executable):

CI compiles public headers with the declared minimum C language/toolchain
baseline

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

implementation builds with C23, but public headers silently require
unsupported compiler extensions

CMOD-089: Evolvable ABI Objects Use Opaque Construction

Class: ARCHITECTURE. Apply the scope and requirement words below.

A public long-lived object expected to evolve should use an incomplete type, module-owned constructor, and module-owned destructor. Callers must not allocate, copy, or depend on its layout.

The destructor accepts the caller's pointer when clearing that handle is part of the lifecycle contract. ABI review must define version negotiation for public DTOs that cannot remain opaque.

Local examples

Contextual C example:

typedef struct Device device_t;

int DEVICE_create(device_t **device);

void DEVICE_destroy(device_t **device);


CMOD-090: Validate Before Externally Visible Mutation

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

Related pitfalls:

Evaluate every rejectable precondition before the first externally visible state mutation whenever the operation can be structured that way.

After commit begins, a failure must either roll back to the prior valid state or move the object into a documented recoverable state. Do not increment counters, publish pointers, write hardware, consume ownership, or persist partial data and then discover an input condition that could have been checked first.

Local examples

Layout example (not executable):

decode -> validate syntax/range/permission/state/capacity -> commit mutation

CMOD-091: Library Diagnostics Use an Injected Port

Class: ARCHITECTURE. Apply the scope and requirement words below.

Library and core modules must not own stdin, stdout, or stderr. They must not call printf, fprintf, puts, perror, or a platform console directly.

Diagnostics flow through a project logging or diagnostic port whose owner defines severity, formatting, sensitive-data policy, blocking, allocation, thread safety, ISR behavior, and destination. An application adapter may bind that port to a console, structured log, test recorder, or no-op sink.

Local examples

Contextual example:

Consumer-owned callback. No process-wide stdout/stderr ownership or hidden formatting allocation; the logger binder states blocking/reentrancy and secret-redaction policy.

typedef int (*module_log_cb_t)(void *context, uint32_t event_id,
                               int operation_result);


CMOD-092: Synchronization Protocols Hide Raw Atomics

Class: ARCHITECTURE. Apply the scope and requirement words below.

Ordinary module logic must consume semantic synchronization operations instead of constructing memory-order protocols from scattered raw atomics and barriers. The synchronization owner documents:

  • protected state and lifetime;
  • single-writer or multi-writer assumptions;
  • ordering and matching operation;
  • progress, blocking, and fairness;
  • initialization and teardown;
  • architecture and tool validation.

A private helper that requires the caller to hold a lock must use the project's Locked naming suffix and state the exact lock precondition. A public function must not require an unnamed implicit lock state.

Local examples

Contextual example:

Consumer-owned port declaration. Its owner defines full/closed states, memory ordering, lifetime and bounded execution. The consumer does not invent an atomic protocol at the call site.

typedef int (*queue_try_push_cb_t)(void *context, const unsigned char *data,
                                   size_t size_bytes);


CMOD-093: Verification and Hardening Use Named Profiles

Class: ARCHITECTURE. Apply the scope and requirement words below.

Do not force proof-specific or fault-hardening restrictions onto every ordinary module without a product requirement. Define named, cumulative profiles:

A profile may prohibit additional language features, restrict object layout, or require dedicated hardening primitives. Its documentation must state scope, toolchain, proof or threat model, generated-code checks, and conflicts with ordinary performance rules.

Packed structures, encoded booleans, redundant comparisons, volatile barriers, and unusual control flow are not general hardening by themselves. Use only the profile-owned primitive whose machine-code and fault behavior form part of the contract.

Local examples

Layout example (not executable):

general C -> analyzable C -> formally verifiable C
                         \-> fault-hardened C

CMOD-094: Optimized Backends Follow One Owned Architecture

Class: ARCHITECTURE. Apply the scope and requirement words below.

An algorithm owner that provides architecture-specific implementations must own:

  • one portable reference implementation when practical;
  • one stable algorithm contract;
  • target-specific source boundaries;
  • runtime or build-time feature dispatch;
  • differential tests and backend-selection proof;
  • unsupported-target fallback;
  • benchmark evidence governed by Performance and microarchitecture controls.

Callers must not select ISA functions directly or duplicate CPU feature tests. The dispatch owner is the only boundary allowed to bind a specialized backend.

Local examples

Layout example (not executable):

one operation owns reference C plus AVX2/NEON/etc backends under one semantic
contract

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

each caller chooses a different ISA implementation and duplicates semantics

CMOD-095: Compiler Warning Policy Preserves Semantics

Class: ARCHITECTURE. Apply the scope and requirement words below.

Treat a compiler warning according to its cause:

  • fix code when the warning identifies a defect;
  • clarify code when it identifies ambiguous intent;
  • suppress or disable it centrally when it is a systematic false positive for an approved construct.

Do not add meaningless casts, dead assignments, unreachable paths, redundant volatile qualification, or performance regressions solely to silence a warning. Every suppression needs the warning identifier, tool versions, narrow scope, rationale, and removal condition.

Local examples

Contextual example:

GCC/Clang build-profile fragment. Other compilers have separately approved flags; a cast inserted only to hide a warning is not a semantic fix.

target_compile_options(module_obj PRIVATE
    -Wall -Wextra -Wpedantic -Werror -Wconversion -Wshadow
)


CMOD-096: Hardening Primitives Have One Security Owner

Class: ARCHITECTURE. Apply the scope and requirement words below.

Fault-sensitive state encoding, constant-time comparison, secret erasure, redundant security checks, hardened copies, and critical MMIO sequencing must come from a security-owned primitive library when the threat model requires them.

The owner must define:

  • threat and fault model;
  • encoded-value distance or redundancy property;
  • compiler and linker constraints;
  • generated-code inspection or fault-injection tests;
  • expected side-effect count and order when relevant;
  • interaction with optimization and logging.

Duplicating an ordinary C condition does not prove that the compiler emits two independent checks.

Local examples

Layout example (not executable):

security/hardware owner defines hardened primitive/threat model; ordinary
modules call the primitive

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

each module invents its own duplicated checks and MMIO hardening sequences

CMOD-097: Checkers Enforce Controls but Do Not Define Them

Class: ARCHITECTURE. Apply the scope and requirement words below.

The written CSTYLE-*, CMOD-*, and CPERF-* controls define project policy. A formatter, checker, compiler warning, or heuristic analyzer implements a diagnostic approximation. CI must preserve a narrow documented deviation path for false positives without weakening the governing rule.

A deviation records the control ID, diagnostic, affected scope, technical rationale, compensating evidence, owner, and review or expiry date.

Local examples

Contextual example:

A diagnostic is resolved by fixing the code or by a scoped approved deviation. An empty deviation field does not itself grant permission.

typedef struct DiagnosticDisposition
{
        const char *rule_id;
        const char *tool_version;
        const char *diagnostic_id;
        const char *evidence_path;
        const char *approved_deviation_id;
} diagnostic_disposition_t;


CMOD-098: Arena and Pool Lifetimes Are Named Boundaries

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

An arena, pool, region, or scoped allocator must name the event that ends its lifetime. Every returned pointer inherits that boundary and must not escape into an object, callback, queue, cache, or asynchronous operation that can outlive the allocator.

When pool-owned objects acquire non-memory resources, the pool owner must register deterministic cleanup or prohibit those resources in the pool. Cleanup order, idempotence, callback context, failure policy, and access to partially destroyed state must be documented. Bulk memory reclamation does not close file descriptors, unlock mutexes, unregister callbacks, or release device handles.

nginx registers pool cleanup handlers for resources such as file descriptors; Wireshark gives allocations explicit packet, file, and application scopes. Both patterns make allocator lifetime an architecture boundary rather than a hidden property of a raw pointer.nginx-development-guide

Local examples

Contextual example:

The callback completes before arena reset and retains no pointer. Lifetime ends on the explicit reset even when the same address is later reused.

ret = consume_in_scope(consumer_context, &borrowed_view);
if (ret != EXIT_SUCCESS)
        goto function_output;


CMOD-099: One Compatibility Boundary Owns Platform Feature Selection

Class: ARCHITECTURE. Apply the scope and requirement words below.

Platform feature-test macros, compiler probes, system-header ordering quirks, missing declarations, and fallback definitions belong to one compatibility or platform boundary. Ordinary module files must consume project abstractions and must not reproduce operating-system or compiler version tests independently.

Prefer a direct capability probe over a version comparison. A fallback must have the same semantic contract as the native facility or expose the reduced capability explicitly. Git similarly centralizes platform differences and feature macros in a compatibility header before ordinary system includes.

Local examples

Contextual example:

Dedicated compatibility header outside the ordinary core include graph; platform-specific dependencies are not convenience transitive includes.

#if defined(PROJECT_PLATFORM_LINUX)
  #include "platform_linux.h"
#elif defined(PROJECT_PLATFORM_WINDOWS)
  #include "platform_windows.h"
#else
  #error "Select a supported platform profile"
#endif


CMOD-100: Every Supported Configuration Is a Build-and-Test Product

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

The project must inventory supported combinations of language profile, compiler, architecture width, byte order, optimization mode, optional subsystem, threading/preemption mode, allocator, sanitizer, hardening profile, and static/shared packaging. CI may use pairwise or risk-based reduction, but every supported conditional branch must be built and its distinctive behavior tested by an owned matrix entry.

An optional feature needs defined enabled and disabled behavior. Disabled builds must retain type checking through a compatible stub or a compile-time exclusion at the owning boundary; call sites must not accumulate divergent #ifdef control flow. Linux's submission checklist explicitly calls for relevant configuration combinations, 32/64-bit and big/little-endian builds, and debug configurations.linux-submit-checklist SQLite also tests the as-delivered optimized configuration after coverage instrumentation.sqlite-testing

Local examples

Layout example (not executable):

CI covers semantic feature combinations, compilers, build types,
sanitizers/analysis modes relevant to support matrix

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

optional feature is never compiled until customer enables it

CMOD-101: Failure Paths Require Deterministic Fault Injection

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

Each owned boundary that can fail must provide a deterministic test mechanism for relevant failures, including allocation exhaustion, short or interrupted I/O, dependency rejection, storage corruption, unavailable hardware, timeout, and partial progress when applicable.

Injection belongs in the allocator, I/O port, callback adapter, MMIO simulator, or other owner; production logic must not contain ad hoc test branches. Tests must verify returned status, preserved invariants, cleanup, retry limits, observable diagnostics, and absence of partial external mutation.

Linux asks submitters to inject allocation and subsystem failures. SQLite's test harnesses inject allocation, I/O, crash, and compound failures and retain the resulting regression cases; its official forum also describes a custom allocator used for allocation-fault injection.linux-submit-checklist sqlite-testing sqlite-fault-forum

Local examples

Contextual example:

Test-only allocator context. The failpoint index is illustrative; declare/zero fault_state at entry and set this field after validation. Verify ownership and cleanup in addition to the expected error.

fault_state.fail_on_allocation = 2u;
ret                            = invoke_operation(operation_context);
if (ret != -ENOMEM)
{
        ret = -EDOM;
        goto function_output;
}

ret = verify_unchanged(operation_context);


CMOD-102: Verification Evidence Uses Complementary Test Modes

Class: ARCHITECTURE. Apply the scope and requirement words below.

One coverage number is not a verification strategy. Each module profile must select and justify a portfolio from:

  • requirements-to-test traceability;
  • boundary, state-transition, and error-path tests;
  • branch or MC/DC evidence where assurance requires it;
  • sanitizers and dynamic analysis on supported host configurations;
  • fuzzing and malformed-input campaigns at trust boundaries;
  • mutation testing for critical decision and test-suite effectiveness;
  • power-loss, restart, concurrency, and fault-injection tests where applicable;
  • retained regression inputs for every repaired defect.

Coverage instrumentation tests the test suite. Release or as-delivered binaries must also run the applicable behavioral corpus, because instrumentation and optimization can change generated code. SQLite documents this distinction and uses branch/MC/DC, mutation, fuzz, malformed-file, OOM, I/O-error, and delivered configuration testing as complementary evidence.sqlite-testing

Local examples

Contextual example:

Executable commands for the included hosted example. Sanitizers complement, not replace, ordinary Release tests, target analysis or formal verification.

cmake -S examples -B build-sanitize -DSAMPLE_SANITIZE=ON
cmake --build build-sanitize
ctest --test-dir build-sanitize --output-on-failure


CMOD-103: Generated Sources Have Reproducible Provenance

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

Every generated C or header file must identify its generator, authoritative inputs, regeneration command, generator/tool version policy, and whether the output is committed. Developers edit the authoritative input or generator, not the generated output.

CI must regenerate and compare committed output, or generate into the build tree from pinned inputs. The generated translation unit is still compiled, analyzed, and tested under the same applicable rules as handwritten code. A generator exception needs the rule ID, narrow reason, and validation performed on the emitted C. CPython's developer workflow similarly pairs regeneration commands with checks for generated C API artifacts.cpython-c-api

Local examples

Contextual example:

Build manifest fields. Regeneration checks compare real digests; editing generated C without updating its generating input violates this contract.

typedef struct GeneratedSourceRecord
{
        const char *generator_revision;
        const char *input_digest;
        const char *invocation_record;
        const char *output_digest;
} generated_source_record_t;


CMOD-104: Imported Source Preserves Its Upstream Lineage

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

Imported or vendored source must record the upstream project, immutable revision, license, import method, local patch series, update owner, and security-update policy. Keep mechanical formatting or naming rewrites separate from functional patches. Preserving an upstream style may be approved when it materially reduces resynchronization risk; the boundary adapter still follows project interfaces.

U-Boot records where imported code came from and its revision, and may preserve the original style of synchronized subsystems to ease future migration. A local fork without provenance or an update procedure is an unowned dependency. u-boot-process u-boot-style

Local examples

Layout example (not executable):

wrap upstream API or contribute/track a maintained patch with explicit
provenance

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

copy a private chunk of upstream implementation into project and silently
diverge

CMOD-105: Lock Preconditions Are Machine-Visible When Practical

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

A helper that requires, acquires, releases, excludes, or returns with a lock held must state the exact lock in its contract. Use project portability macros for compiler thread-safety annotations where the supported toolchain can check them, and use runtime lock assertions in debug profiles where appropriate.

The annotation supplements the synchronization design; it does not create ordering or ownership. The function name retains the Locked suffix when the caller must already hold the lock. Open vSwitch similarly defines portability macros for lock requirements so Clang can diagnose violations while other compilers receive a compatible declaration.ovs-coding-style

Local examples

Layout example (not executable):

port contract states caller-held locks, ISR/thread context, blocking
allowance, and callback reentrancy

Noncompliant fragment (do not copy):

Layout, diagnostic, or contract example. This block is not executable C.

callback may sleep from ISR and requires a lock that the interface never
documents

CMOD-110: Public Boundary Documentation Is Part of the Header Contract

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

A public header owns the caller-visible contract for every exported function, type, callback, and constant. The header documents ownership, lifetime, units, nullability, extent, threading context, and error semantics that cannot be represented by the C type alone.

The implementation may explain algorithms; callers must not need to read it to learn how to use the API safely.

The C Documentation Standard defines the construction sequence and comment patterns for public headers, internal headers, module implementations, and application entry points.

Local examples

Contextual example:

Public-header excerpt: document that buffer is caller-owned, the output state on failure, synchronization, and whether partial reads are valid. Declaration requires the owning opaque type and standard type headers.

int STORAGE_read(storage_t *storage, uint32_t object_id, unsigned char *buffer,
                 size_t capacity_bytes, size_t *read_bytes);


CMOD-111: Mandatory Callback Invariants Are Established Before Use

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

Related pitfalls:

create or bind validates every mandatory callback and context before publishing a usable module instance. The module contract distinguishes mandatory and optional ports; a successful bind establishes the invariant used by later calls.

Local examples

Contextual example:

Constructor-body excerpt after validating config and private state. Bind only after validating the complete required callback set; the context contract is checked at composition.

if (config->write == (storage_write_cb_t)(NULL))
{
        ret = -EINVAL;
        goto function_output;
}

state->write         = config->write;
state->write_context = config->write_context;


CMOD-112: Optimized Backends Have an Independent Reference Oracle

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

A module that ships architecture-specific, SIMD, assembly, JIT, or otherwise specialized implementations owns one portable semantic reference where practical. Tests compare specialized results to that reference rather than accepting the specialized code as its own oracle.

Local examples

Contextual example:

Test-body fragment. Declare ret, expected and actual at entry; validate both callbacks and their contexts. Repeat over the allowed length, alignment and aliasing matrix. Matching one output does not prove general equivalence.

ret = reference(reference_context, data, size_bytes, &expected);
if (ret != EXIT_SUCCESS)
        goto function_output;
ret = candidate(candidate_context, data, size_bytes, &actual);
if (ret != EXIT_SUCCESS)
        goto function_output;
if (actual != expected)
{
        ret = -EDOM;
        goto function_output;
}


CMOD-113: Dispatch Tests Prove Which Backend Executed

Class: ARCHITECTURE. Apply the scope and requirement words below.

A runtime-dispatch test must verify the selected implementation identity or a test hook that proves the intended backend ran. Output equality alone can accidentally test the portable fallback while claiming AVX, NEON, SVE, RVV, or another backend.

Local examples

Contextual example:

Harness fragment with entry-declared IDs and ret. selected_backend reports the implementation that will execute; unsupported hardware is not silently counted as passing the optimized test.

ret = selected_backend(dispatch_context, &actual_backend_id);
if (ret != EXIT_SUCCESS)
        goto function_output;
if (actual_backend_id != requested_backend_id)
{
        ret = -ENOTSUP;
        goto function_output;
}

ret = differential_test(test_context);


CMOD-114: ISA and Variable-Vector-Length Coverage Is Explicit

Class: ARCHITECTURE. Apply the scope and requirement words below.

Each supported optimized feature level has a named CI/test configuration. Variable- vector-length implementations run at multiple supported lengths. Unsupported hardware may use emulation where the emulator is itself part of the documented evidence chain. FFmpeg checkasm/FATE provides a mature model for this kind of matrix.

Local examples

Contextual example:

Test-plan schema fields, not a completed test result. Each supported combination needs its own execution evidence.

{
  "required_dimensions": [
    "backend_id", "ISA_features", "OS_vector_state_support",
    "vector_length_bytes", "input_length", "alignment", "aliasing"
  ],
  "unsupported_target_result": "not_executed"
}


CMOD-115: Documentation and Cross-Reference Gate

Class: ARCHITECTURE. Apply the scope and requirement words below.

The documentation build shall fail on structural defects in normative Markdown. At minimum, the gate checks:

Tool suppressions are scoped and reviewed. Technical vocabulary goes into the project dictionary rather than disabling spelling/prose analysis for entire documents.

Local examples

Contextual example:

The included validator describes executable build/link checks. Run the chosen matrix before updating the delivery evidence.

python3 examples/tools/validate.py --help


CMOD-116: Compiler and Static-Analysis Deviations Are Machine-Readable

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

Every persistent compiler/static-analysis suppression identifies the owning rule or warning, rationale, scope, owner/module, and where practical an expiry/review trigger. CI rejects undocumented broad suppressions added to core code.

Local examples

Contextual example:

Deviation-record shape. The example does not assert that a deviation is approved.

{
  "rule": "CSTYLE-052",
  "scope": "platform/compiler_adapter.h",
  "required_record_fields": [
    "reason", "target", "compiler", "risk", "evidence", "approver"
  ]
}


CMOD-117: Bounded-Stack Profiles Produce Stack-Usage Evidence

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

Kernel, RTOS, ISR, and safety-critical build profiles collect compiler/linker stack- usage evidence or an equivalent target measurement. CI flags frames/call chains that exceed the profile threshold for review.

Local examples

Contextual example:

Command template: replace source/include paths with a real translation unit and its production flags. Static stack reports do not bound recursion, interrupt nesting or dynamic call depth by themselves.

cc -std=c17 -fstack-usage -I include -c source.c -o source.o


CMOD-118: Trust-Boundary Allocation Failure Is Recoverable

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

A parser, protocol, guest, plugin, file, or RPC boundary owns the resource limit before it requests memory. Externally influenced sizes use a bounded, checked, recoverable allocator path. Fatal allocation policy may exist elsewhere in the process but shall not be reachable merely by requesting a larger untrusted object.

Local examples

Contextual example:

Test-only allocator context. The failpoint index is illustrative; declare/zero fault_state at entry and set this field after validation. Verify ownership and cleanup in addition to the expected error.

fault_state.fail_on_allocation = 2u;
ret                            = invoke_operation(operation_context);
if (ret != -ENOMEM)
{
        ret = -EDOM;
        goto function_output;
}

ret = verify_unchanged(operation_context);


CMOD-119: Runtime ISA Dispatch Is Centralized and Immutable During Use Unless Synchronized

Class: ARCHITECTURE. Apply the scope and requirement words below.

CPU feature detection and backend binding happen in one initialization/dispatch layer. If the function table can change after publication, the owner defines synchronization and lifetime rules; otherwise bind once and treat the table as immutable.

Local examples

Contextual example:

Consumer-owned in-process port. Include <stddef.h> and <stdint.h>. The composition owner validates ISA/OS prerequisites and publishes a complete table; the table and context remain stable during calls.

typedef int (*opt_count_cb_t)(void *context, const unsigned char *data,
                              size_t size_bytes, size_t *count_out);

typedef struct OptDispatch
{
        void          *context;
        opt_count_cb_t count;
        uint32_t       selected_backend_id;
} opt_dispatch_t;


CMOD-120: Cached Specialization Has an Invalidation Owner

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related C standard rules:

Any JIT, translated block, cached execution plan, or precomputed specialization records which state it assumes invariant and which module invalidates it when that state changes. No optimization may cache an assumption whose invalidation owner is unclear.

Local examples

Contextual example:

Externally synchronized owner fragment. Generation changes cover every assumed invariant; wrap/reuse is prohibited while an older artifact may exist. invalidate, bind and execute are validated owner callbacks. Rebinding must not publish a partial artifact.

if (cached_generation != current_generation)
{
        ret = invalidate(cache_context);
        if (ret != EXIT_SUCCESS)
                goto function_output;
        ret = bind(cache_context, current_generation);
        if (ret != EXIT_SUCCESS)
                goto function_output;
}

ret = execute(cache_context);


CMOD-121: Partition Ownership Before Optimizing a Global Lock

Class: ARCHITECTURE. Apply the scope and requirement words below.

Before replacing a contended lock with a more complex atomic protocol, ask whether the state can be partitioned by CPU, thread, shard, connection, tenant, or queue owner. Local ownership usually removes more coherence traffic and proof burden than a cleverer global lock.

Local examples

Contextual example:

Owning-thread fragment. local_count is retained in owner state; ret is local. flush is a validated injected callback; failure must mean no batch committed, otherwise retain explicit partial progress. Flush at deadline and shutdown too; batch size is not the only latency bound.

if (local_count == UINT64_MAX)
{
        ret = -ERANGE;
        goto function_output;
}

local_count++;
if (local_count >= flush_threshold)
{
        ret = flush(owner_context, local_count);
        if (ret != EXIT_SUCCESS)
                goto function_output;
        local_count = 0u;
}


CMOD-122: Performance Evidence Is Stored With the Optimization Owner

Class: ARCHITECTURE. Apply the scope and requirement words below.

A target-specific optimization records benchmark workload, target, compiler, relevant counters, and fallback assumptions in the module's design/test evidence. CI or a repeatable benchmark harness must make later regression checks possible; folklore is not an architecture contract.

Local examples

Contextual example:

Evidence paths must resolve to actual run artifacts. This type is not a completed approval record.

typedef struct OptimizationReview
{
        const char *rule_ids;
        const char *baseline_record;
        const char *candidate_record;
        const char *correctness_log;
        const char *regression_policy;
        const char *rollback_trigger;
        const char *owner;
} optimization_review_t;


CMOD-123: Runtime Topology and Affinity Have One Owner

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

NUMA placement, CPU affinity, IRQ routing, device queue affinity, and memory placement shall be coordinated by one runtime/composition owner when they form one performance contract. Peer modules may expose requirements or telemetry, but they shall not pin threads or relocate shared memory independently in ways that can create contradictory topology policy.

Local examples

Contextual example:

In-process DTO using <stdint.h>. A single runtime/platform owner applies and verifies the plan. The IDs are platform identifiers; equal numeric IDs across the fields do not imply locality.

typedef struct PlacementPlan
{
        uint32_t cpu_id;
        uint32_t memory_node_id;
        uint32_t irq_cpu_id;
        uint32_t device_node_id;
} placement_plan_t;


CMOD-124: Microarchitecture Layout Does Not Leak Into Portable Public ABI

Class: ARCHITECTURE. Apply the scope and requirement words below.

Cache-line padding, NUMA-local replicas, prefetch guards, vector-width-specific storage, and other target-specific layout choices stay behind an opaque or explicitly target-specific boundary. A portable public DTO or ABI shall not acquire incidental padding or alignment solely because one optimized backend benefits from it.

Local examples

Contextual example:

Illustrative private target profile with a 64-byte coherence unit, not a universal constant. Include <stdalign.h>, <stddef.h>, <stdint.h>. Storage must satisfy alignof(owner_slot_t); ordinary malloc is not assumed to provide this extended alignment. No remote unsynchronized reads/writes.

#define OPT_PROFILE_CACHE_LINE_BYTES ((size_t)(64U))

typedef struct OwnerSlot
{
        alignas(OPT_PROFILE_CACHE_LINE_BYTES) uint64_t completed_count;
} owner_slot_t;

_Static_assert(sizeof(owner_slot_t) % OPT_PROFILE_CACHE_LINE_BYTES == 0u,
               "array stride must preserve the measured isolation");


CMOD-125: Device DMA and Cache-Maintenance Policy Has a Hardware Owner

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

A device/platform module owns DMA mapping, cache maintenance, coherent versus non-coherent buffer policy, transaction alignment requirements, and publication ordering. Ordinary modules exchange semantic buffers/descriptors and shall not issue ad hoc cache flush/invalidate operations or depend on undocumented device cache-line geometry.

Local examples

Contextual example:

A platform-owner descriptor, not a wire format. Include <stddef.h>/<stdint.h>. The mapping contract defines actual alignment, coherency, cache maintenance, permitted cache-line rounding and ownership handover. used_bytes never authorizes stores into adjacent allocations.

typedef struct DmaBufferView
{
        unsigned char *data;
        size_t         capacity_bytes;
        size_t         used_bytes;

        uint32_t mapping_id;
} dma_buffer_view_t;


CMOD-126: Performance Dispatch and Topology Decisions Are Observable in Tests

Class: ARCHITECTURE. Apply the scope and requirement words below.

Related pitfalls:

Tests and benchmark harnesses shall be able to identify the selected optimized backend, relevant CPU/NUMA placement, and profile/configuration that produced a performance result. A benchmark that cannot prove which backend or topology executed is not valid evidence for a target-specific optimization.

Local examples

Contextual example:

Harness fragment with entry-declared IDs and ret. selected_backend reports the implementation that will execute; unsupported hardware is not silently counted as passing the optimized test.

ret = selected_backend(dispatch_context, &actual_backend_id);
if (ret != EXIT_SUCCESS)
        goto function_output;
if (actual_backend_id != requested_backend_id)
{
        ret = -ENOTSUP;
        goto function_output;
}

ret = differential_test(test_context);


Appendix A. Complete composition and build example

Combine these files with the standard's foundation and buffersources and catalogue testprogram. The accompanying ZIP contains the same files in their directories. No file is an omitted stub.

The emitter invokes a consumer-owned write port; the adapter calls BUFFER_append. The root binds the provider and unwinds in reverse lifetime order. The host allocator is an intentionally allocating, non-real-time adapter. Its malloc/realloc return pointers use normal C conversion. It has no global mutable error channel.

The compiler adapter contains the only vendor attribute spellings. The build rejects unqualified platforms rather than silently selecting a target. Windows macro branches illustrate export/import spelling but are not a tested Windows build profile. Both maintained peers produce static and shared ELF variants; public exports are explicit and private foundation helpers stay hidden. The map files are export allowlists; the CMake -Map outputs are link reports.


foundation/inc/compiler_api.h

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#if !defined(SAMPLE_COMPILER_API_H)
  #define SAMPLE_COMPILER_API_H

  /* Compiler adapter. Unsupported shared-library targets fail at configure. */
  #if defined(_WIN32)
    #define SAMPLE_EXPORT __declspec(dllexport)
    #define SAMPLE_IMPORT __declspec(dllimport)
  #elif defined(__GNUC__) || defined(__clang__)
    #define SAMPLE_EXPORT __attribute__((visibility("default")))
    #define SAMPLE_IMPORT __attribute__((visibility("default")))
  #else
    #define SAMPLE_EXPORT
    #define SAMPLE_IMPORT
  #endif

  #if defined(BUFFER_SHARED_BUILD)
    #define BUFFER_API SAMPLE_EXPORT
  #elif defined(BUFFER_SHARED_USE)
    #define BUFFER_API SAMPLE_IMPORT
  #else
    #define BUFFER_API
  #endif

  #if defined(EMITTER_SHARED_BUILD)
    #define EMITTER_API SAMPLE_EXPORT
  #elif defined(EMITTER_SHARED_USE)
    #define EMITTER_API SAMPLE_IMPORT
  #else
    #define EMITTER_API
  #endif

#endif


modules/emitter/inc/emitter_port.h

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#if !defined(SAMPLE_EMITTER_PORT_H)
  #define SAMPLE_EMITTER_PORT_H

  #include <stddef.h>
  #include <stdint.h>

/*
 * Synchronous call in the sender's thread. No retained data pointer.
 * NULL data is valid only when size_bytes is zero. context may be NULL when
 * the provider supports it. The binder owns context and code lifetime.
 * Return PROJECT_OK or a project error. External side effects on failure
 * are provider-specific; the emitter does not retry automatically.
 * The callback must not destroy or re-enter its invoking emitter instance.
 */
typedef int (*emitter_write_fn_t)(void *context, const uint8_t *data,
                                  size_t size_bytes);

typedef struct EmitterPort
{
        void              *context;
        emitter_write_fn_t write;
} emitter_port_t;

#endif


modules/emitter/inc/emitter.h

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#if !defined(SAMPLE_EMITTER_H)
  #define SAMPLE_EMITTER_H

  #include <stddef.h>
  #include <stdint.h>

  #include "compiler_api.h"
  #include "emitter_port.h"
  #include "memory_port.h"

typedef struct Emitter emitter_t;

/*
 * All operations require external synchronization; not ISR/signal-safe.
 * out points to initialized NULL storage and is unchanged on failure.
 * Tables are copied. Their contexts/code outlive the emitter.
 * create allocates the control object. max_payload_bytes must be positive.
 */
EMITTER_API int EMITTER_create(emitter_t **out, const memory_port_t *memory,
                               const emitter_port_t *port,
                               size_t                max_payload_bytes);

/*
 * data is readable for size_bytes; NULL is valid only for zero bytes.
 * No internal allocation. The callback can allocate, block, or fail.
 * Synchronous re-entry is rejected with PROJECT_ERR_BUSY, not serialized.
 */
EMITTER_API int EMITTER_send(emitter_t *emitter, const uint8_t *data,
                             size_t size_bytes);

/* Reject destruction during a callback. NULL handle value is a no-op. */
EMITTER_API int EMITTER_destroy(emitter_t **handle);

#endif


modules/emitter/src/emitter.c

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#include "emitter.h"

#if __STDC_VERSION__ < 202311L
  #include <stdbool.h>
#endif
#include <stddef.h>
#include <stdint.h>

#include "emitter_port.h"
#include "memory_port.h"
#include "project_status.h"

struct Emitter
{
        memory_port_t  memory;
        emitter_port_t port;
        size_t         max_payload_bytes;

        bool is_active;
};

int EMITTER_create(emitter_t **out, const memory_port_t *memory,
                   const emitter_port_t *port, size_t max_payload_bytes)
{
        int ret = PROJECT_OK;

        emitter_t *emitter = (emitter_t *)(NULL);

        if ((out == (emitter_t **)(NULL)) ||
            (memory == (const memory_port_t *)(NULL)) ||
            (port == (const emitter_port_t *)(NULL)) ||
            (max_payload_bytes == 0u))
        {
                ret = PROJECT_ERR_INVALID;
                goto function_output;
        }

        if ((*out != (emitter_t *)(NULL)) ||
            (memory->alloc == (memory_alloc_fn_t)(NULL)) ||
            (memory->release == (memory_release_fn_t)(NULL)) ||
            (port->write == (emitter_write_fn_t)(NULL)))
        {
                ret = PROJECT_ERR_INVALID;
                goto function_output;
        }

        emitter = (emitter_t *)memory->alloc(memory->context, sizeof(*emitter));
        if (emitter == (emitter_t *)(NULL))
        {
                ret = PROJECT_ERR_MEMORY;
                goto function_output;
        }

        *emitter = (emitter_t){ .memory            = *memory,
                                .port              = *port,
                                .max_payload_bytes = max_payload_bytes,
                                .is_active         = false };
        *out     = emitter;

function_output:
        return ret;
}

int EMITTER_send(emitter_t *emitter, const uint8_t *data, size_t size_bytes)
{
        int ret = PROJECT_OK;

        if ((emitter == (emitter_t *)(NULL)) ||
            ((data == (const uint8_t *)(NULL)) && (size_bytes != 0u)))
        {
                ret = PROJECT_ERR_INVALID;
                goto function_output;
        }

        if (emitter->is_active)
        {
                ret = PROJECT_ERR_BUSY;
                goto function_output;
        }

        if (size_bytes > emitter->max_payload_bytes)
        {
                ret = PROJECT_ERR_CAPACITY;
                goto function_output;
        }

        emitter->is_active = true;
        ret = emitter->port.write(emitter->port.context, data, size_bytes);
        /* A rejected nested call must not clear the outer call's busy state. */
        emitter->is_active = false;

function_output:
        return ret;
}

int EMITTER_destroy(emitter_t **handle)
{
        int ret = PROJECT_OK;

        memory_port_t memory  = { 0 };
        emitter_t    *emitter = (emitter_t *)(NULL);

        if (handle == (emitter_t **)(NULL))
        {
                ret = PROJECT_ERR_INVALID;
                goto function_output;
        }

        if (*handle == (emitter_t *)(NULL))
                goto function_output;
        emitter = *handle;
        if (emitter->is_active)
        {
                ret = PROJECT_ERR_BUSY;
                goto function_output;
        }

        memory = emitter->memory;
        memory.release(memory.context, emitter);
        *handle = (emitter_t *)(NULL);

function_output:
        return ret;
}


integration/host_memory.h

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#if !defined(SAMPLE_HOST_MEMORY_H)
  #define SAMPLE_HOST_MEMORY_H

  #include "memory_port.h"

/* Hosted libc allocator. No real-time, ISR, or signal-safety guarantee. */
memory_port_t HOST_memory(void);

#endif


integration/host_memory.c

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#include "host_memory.h"

#include <stddef.h>
#include <stdlib.h>

#include "memory_port.h"

static void *host_alloc(void *context, size_t size_bytes)
{
        void *ret = (void *)(NULL);

        (void)context;
        if (size_bytes == 0u)
                goto function_output;
        ret = (void *)malloc(size_bytes);

function_output:
        return ret;
}

/* CSTYLE-057: callback ABI fixes the context/base parameter order. */
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
static void *host_resize(void *context, void *base, size_t size_bytes)
{
        void *ret = (void *)(NULL);

        (void)context;
        if ((base == (void *)(NULL)) || (size_bytes == 0u))
                goto function_output;
        /* CBAN-068: hosted adapter preserves base on allocation failure. */
        // NOLINTNEXTLINE(bugprone-unsafe-functions)
        ret = (void *)realloc(base, size_bytes);

function_output:
        return ret;
}

/* CSTYLE-057: callback ABI fixes the context/base parameter order. */
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
static void host_release(void *context, void *base)
{
        (void)context;
        free(base);
        goto function_output;

function_output:
        return;
}

memory_port_t HOST_memory(void)
{
        memory_port_t ret = { 0 };

        ret.context = (void *)(NULL);
        ret.alloc   = host_alloc;
        ret.resize  = host_resize;
        ret.release = host_release;
        goto function_output;

function_output:
        return ret;
}


integration/buffer_sink.h

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#if !defined(SAMPLE_BUFFER_SINK_H)
  #define SAMPLE_BUFFER_SINK_H

  #include <stddef.h>
  #include <stdint.h>

/*
 * context borrows a live buffer_t.
 * The signature exactly matches emitter_write_fn_t.
 * A failed write leaves the buffer unchanged. Lifetime belongs to composition.
 */
int ADAPTER_writeBuffer(void *context, const uint8_t *data, size_t size_bytes);

#endif


integration/buffer_sink.c

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#include "buffer_sink.h"

#include <stddef.h>
#include <stdint.h>

#include "buffer.h"
#include "project_status.h"

int ADAPTER_writeBuffer(void *context, const uint8_t *data, size_t size_bytes)
{
        int ret = PROJECT_OK;

        buffer_t *buffer = (buffer_t *)(NULL);

        if (context == (void *)(NULL))
        {
                ret = PROJECT_ERR_INVALID;
                goto function_output;
        }

        buffer = (buffer_t *)context;
        ret    = BUFFER_append(buffer, data, size_bytes);

function_output:
        return ret;
}


integration/main.c

/*
 * SPDX-FileCopyrightText: 2026 Rafael V. Volkmer
 * SPDX-License-Identifier: GPL-3.0-only
 */

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#include "buffer.h"
#include "buffer_sink.h"
#include "emitter.h"
#include "emitter_port.h"
#include "host_memory.h"
#include "memory_port.h"
#include "project_status.h"

#define APP_PAYLOAD_LIMIT ((size_t)64u)

static const uint8_t app_input[] = { 1u, 2u, 3u };

static int app_verify(const uint8_t *output, size_t written)
{
        int    ret   = PROJECT_OK;
        size_t index = 0u;
        if ((output == (const uint8_t *)(NULL)) ||
            (written != sizeof(app_input)))
        {
                ret = PROJECT_ERR_IO;
                goto function_output;
        }

        for (index = 0u; index < written; index++)
        {
                if (output[index] != app_input[index])
                {
                        ret = PROJECT_ERR_IO;
                        goto function_output;
                }
        }

function_output:
        return ret;
}

int main(void)
{
        int ret = PROJECT_OK;

        uint8_t        output[sizeof(app_input)] = { 0 };
        emitter_port_t port                      = { 0 };
        memory_port_t  memory                    = { 0 };
        buffer_t      *buffer                    = (buffer_t *)(NULL);
        emitter_t     *emitter                   = (emitter_t *)(NULL);
        size_t         written                   = 0u;
        int            cleanup_ret               = PROJECT_OK;

        memory = HOST_memory();
        ret    = BUFFER_create(&buffer, &memory, APP_PAYLOAD_LIMIT);
        if (ret != PROJECT_OK)
                goto function_output;
        port = (emitter_port_t){ .context = buffer,
                                 .write   = ADAPTER_writeBuffer };
        ret  = EMITTER_create(&emitter, &memory, &port, APP_PAYLOAD_LIMIT);
        if (ret != PROJECT_OK)
                goto function_output;
        ret = EMITTER_send(emitter, app_input, sizeof(app_input));
        if (ret != PROJECT_OK)
                goto function_output;
        ret = BUFFER_copy(buffer, output, sizeof(output), &written);
        if (ret != PROJECT_OK)
                goto function_output;
        ret = app_verify(output, written);

function_output:
{
        /* Consumers stop before their callback targets are released. */
        cleanup_ret = EMITTER_destroy(&emitter);
        if (ret == PROJECT_OK)
                ret = cleanup_ret;
        cleanup_ret = BUFFER_destroy(&buffer);
        if (ret == PROJECT_OK)
                ret = cleanup_ret;
        ret = (ret == PROJECT_OK) ? EXIT_SUCCESS : EXIT_FAILURE;

        return ret;
}
}


CMakeLists.txt

cmake_minimum_required(VERSION 3.21)
project(c_policy_example LANGUAGES C)

set(CMAKE_C_STANDARD 23 CACHE STRING "Explicit C language edition")
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
option(SAMPLE_SANITIZE "Host AddressSanitizer and UndefinedBehaviorSanitizer" OFF)

if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
    message(FATAL_ERROR "This example build is qualified only for GCC/Clang")
endif()
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
    message(FATAL_ERROR "This executed example profile is Linux ELF only")
endif()

add_library(sample_build_options INTERFACE)
target_compile_options(sample_build_options INTERFACE
    -Wall -Wextra -Wpedantic -Werror -Wconversion -Wsign-conversion
    -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wformat=2
    -Wundef -Wvla -Wdeclaration-after-statement -fno-common
)
if(SAMPLE_SANITIZE)
    target_compile_options(sample_build_options INTERFACE
        -fsanitize=address,undefined -fno-omit-frame-pointer
    )
    target_link_options(sample_build_options INTERFACE
        -fsanitize=address,undefined -fno-omit-frame-pointer
    )
endif()

add_library(contracts INTERFACE)
target_include_directories(contracts INTERFACE foundation/inc)

add_library(checked STATIC foundation/src/checked.c)
target_link_libraries(checked PUBLIC contracts PRIVATE sample_build_options)
set_target_properties(checked PROPERTIES
    POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden
)

add_subdirectory(modules/buffer)
add_subdirectory(modules/emitter)

enable_testing()
foreach(variant IN ITEMS static shared)
    add_library(integration_${variant} STATIC
        integration/host_memory.c integration/buffer_sink.c
    )
    target_include_directories(integration_${variant} PUBLIC integration)
    target_link_libraries(integration_${variant}
        PUBLIC buffer_${variant} emitter_${variant} contracts
        PRIVATE sample_build_options
    )
    foreach(program IN ITEMS demo tests)
        if(program STREQUAL "demo")
            set(source integration/main.c)
        else()
            set(source tests/test_main.c)
        endif()
        add_executable(${program}_${variant} ${source})
        target_link_libraries(${program}_${variant}
            PRIVATE integration_${variant} checked sample_build_options
        )
        target_link_options(${program}_${variant} PRIVATE
            "LINKER:-Map,${CMAKE_CURRENT_BINARY_DIR}/${program}_${variant}.map"
        )
        add_test(NAME ${program}_${variant} COMMAND ${program}_${variant})
    endforeach()
endforeach()

add_executable(optimization_tests
    performance/optimization_examples.c performance/test_optimization.c
)
target_include_directories(optimization_tests PRIVATE performance)
target_link_libraries(optimization_tests PRIVATE contracts sample_build_options)
add_test(NAME optimization_tests COMMAND optimization_tests)


modules/buffer/CMakeLists.txt

foreach(variant IN ITEMS static shared)
    if(variant STREQUAL "static")
        add_library(buffer_${variant} STATIC src/buffer.c)
    else()
        add_library(buffer_${variant} SHARED src/buffer.c)
        target_compile_definitions(buffer_${variant}
            PRIVATE BUFFER_SHARED_BUILD INTERFACE BUFFER_SHARED_USE
        )
        target_link_options(buffer_${variant} PRIVATE
            "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/exports.map"
        )
        # Clang ASan resolves instrumentation in the final executable.
        if(NOT SAMPLE_SANITIZE)
            target_link_options(buffer_${variant} PRIVATE
                "LINKER:--no-undefined"
            )
        endif()
        set_property(TARGET buffer_${variant} APPEND PROPERTY LINK_DEPENDS
            "${CMAKE_CURRENT_SOURCE_DIR}/exports.map"
        )
    endif()
    target_include_directories(buffer_${variant} PUBLIC inc)
    target_link_libraries(buffer_${variant}
        PUBLIC contracts PRIVATE checked sample_build_options
    )
    set_target_properties(buffer_${variant} PROPERTIES
        POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden
    )
endforeach()


modules/emitter/CMakeLists.txt

foreach(variant IN ITEMS static shared)
    if(variant STREQUAL "static")
        add_library(emitter_${variant} STATIC src/emitter.c)
    else()
        add_library(emitter_${variant} SHARED src/emitter.c)
        target_compile_definitions(emitter_${variant}
            PRIVATE EMITTER_SHARED_BUILD INTERFACE EMITTER_SHARED_USE
        )
        target_link_options(emitter_${variant} PRIVATE
            "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/exports.map"
        )
        # Clang ASan resolves instrumentation in the final executable.
        if(NOT SAMPLE_SANITIZE)
            target_link_options(emitter_${variant} PRIVATE
                "LINKER:--no-undefined"
            )
        endif()
        set_property(TARGET emitter_${variant} APPEND PROPERTY LINK_DEPENDS
            "${CMAKE_CURRENT_SOURCE_DIR}/exports.map"
        )
    endif()
    target_include_directories(emitter_${variant} PUBLIC inc)
    target_link_libraries(emitter_${variant}
        PUBLIC contracts PRIVATE sample_build_options
    )
    set_target_properties(emitter_${variant} PROPERTIES
        POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden
    )
endforeach()


modules/buffer/exports.map

{
    global:
        BUFFER_create;
        BUFFER_resize;
        BUFFER_append;
        BUFFER_copy;
        BUFFER_destroy;
    local: *;
};

modules/emitter/exports.map

{
    global:
        EMITTER_create;
        EMITTER_send;
        EMITTER_destroy;
    local: *;
};

Links and references

The three guides share one policy and use stable rule identifiers for cross-references.