11# Compat Shims
22
3- Rule matching needs a resolvable callee. The preprocessor keys every
4- expression rule on the function, method, constructor, constant, or global
5- that the pattern's ` return ` expression resolves to, and the only macros it
6- can record are those that
3+ Rule matching needs a resolvable callee. The preprocessor keys every expression
4+ rule on the function, method, constructor, constant, or global that the
5+ pattern's ` return ` expression resolves to, and the only macros it can record are
6+ those that
77[ expand to an integer literal] ( ./writing-rules.md#enum-values-constants-and-macros ) ,
8- which match by macro name. Any other macro is invisible to the rule system:
9- by the time clang has built the AST, the macro is gone and only its expansion
8+ which match by macro name. Any other macro is invisible to the rule system: by
9+ the time clang has built the AST, the macro is gone and only its expansion
1010remains.
1111
12- That is a problem for a small set of libc APIs that are specified as macros
13- over platform internals:
12+ That is a problem for a small set of libc APIs that are specified as macros over
13+ platform internals:
1414
15- * ` errno ` is an object-like macro; glibc expands it to
16- ` (*__errno_location()) ` , macOS to ` (*__error()) ` .
17- * ` assert ` expands to a conditional that stringifies the condition and calls
18- a platform-specific failure handler with file and line arguments.
19- * ` FD_SET ` , ` FD_CLR ` , ` FD_ISSET ` , and ` FD_ZERO ` expand to bit manipulation
20- on the ` fd_set ` representation, through helpers that differ per platform.
21- * ` ntohl ` , ` ntohs ` , ` htonl ` , and ` htons ` expand to byte-swap builtins or to
15+ - ` errno ` is an object-like macro; glibc expands it to ` (*__errno_location()) ` ,
16+ macOS to ` (*__error()) ` .
17+ - ` assert ` expands to a conditional that stringifies the condition and calls a
18+ platform-specific failure handler with file and line arguments.
19+ - ` FD_SET ` , ` FD_CLR ` , ` FD_ISSET ` , and ` FD_ZERO ` expand to bit manipulation on
20+ the ` fd_set ` representation, through helpers that differ per platform.
21+ - ` ntohl ` , ` ntohs ` , ` htonl ` , and ` htons ` expand to byte-swap builtins or to
2222 nothing at all, depending on endianness.
2323
2424There is no stable, platform-independent callee here to key a rule on. The
25- * compat headers * in ` cpp2rust/compat/ ` fix this by rewriting each such macro
25+ _ compat headers _ in ` cpp2rust/compat/ ` fix this by rewriting each such macro
2626into a call to a synthetic, well-known function before matching happens.
2727
2828## How the shims work
2929
3030` cpp2rust/compat ` is injected as a system include directory ahead of the
3131platform headers in every clang invocation the project makes: both when
3232` cpp2rust ` parses the input program and when ` cpp-rule-preprocessor ` compiles
33- rule sources. The shared flag list lives in
34- ` cpp2rust/compat/platform_flags.h ` (` getPlatformClangBeginFlags ` ), and the
35- directory path is baked in at build time via the ` COMPAT_INCLUDE_DIR `
36- definition.
33+ rule sources. The shared flag list lives in ` cpp2rust/compat/platform_flags.h `
34+ (` getPlatformClangBeginFlags ` ), and the directory path is baked in at build time
35+ via the ` COMPAT_INCLUDE_DIR ` definition.
3736
3837A shim header sits at the same relative path as the real header it shadows
3938(` errno.h ` , ` sys/select.h ` , ` arpa/inet.h ` , ...), so an ordinary
4039` #include <errno.h> ` finds the shim first. The header then:
4140
42- 1 . pulls in the real platform header with ` #include_next ` (a GNU extension;
43- the shared flags pass ` -Wno-gnu-include-next ` for it),
41+ 1 . pulls in the real platform header with ` #include_next ` (a GNU extension; the
42+ shared flags pass ` -Wno-gnu-include-next ` for it),
44432 . ` #undef ` s the macro,
45443 . declares a ` cpp2rust_* ` shim function,
46454 . redefines the macro to call the shim.
@@ -57,12 +56,12 @@ int *cpp2rust_errno(void);
5756#define errno (* cpp2rust_errno())
5857```
5958
60- The redefinition keeps `errno` an lvalue by dereferencing the returned
61- pointer, so both reads and assignments like `errno = 0` still parse; what
62- the matcher sees in either case is a call to `int *cpp2rust_errno()`.
59+ The redefinition keeps `errno` an lvalue by dereferencing the returned pointer,
60+ so both reads and assignments like `errno = 0` still parse; what the matcher
61+ sees in either case is a call to `int *cpp2rust_errno()`.
6362
64- Because the input program and the rule sources are compiled with the same
65- shim headers, both sides canonicalize to the same signature, and an ordinary
63+ Because the input program and the rule sources are compiled with the same shim
64+ headers, both sides canonicalize to the same signature, and an ordinary
6665[expression rule](./writing-rules.md) matches it:
6766
6867```c
@@ -72,10 +71,10 @@ shim headers, both sides canonicalize to the same signature, and an ordinary
7271int *f1(void) { return cpp2rust_errno(); }
7372```
7473
75- The shim functions are declared but never defined on the C side. They only
76- exist so that the callee resolves; translation replaces the call with the
77- rule body, so no C implementation is ever linked. Whatever the shim is
78- supposed to * do * is supplied by the Rust targets:
74+ The shim functions are declared but never defined on the C side. They only exist
75+ so that the callee resolves; translation replaces the call with the rule body,
76+ so no C implementation is ever linked. Whatever the shim is supposed to _ do _ is
77+ supplied by the Rust targets:
7978
8079``` rust
8180// rules/errno/tgt_unsafe.rs
@@ -91,11 +90,11 @@ fn f1() -> Ptr<i32> {
9190}
9291```
9392
94- In the unsafe model ` libcc2rs::cpp2rust_errno_unsafe ` wraps the real
95- platform errno location (` __errno_location ` on Linux, ` __error ` on macOS).
96- The refcount model instead * virtualizes * errno as a thread-local
97- ` Value<i32> ` inside ` libcc2rs ` ; this is the same cell that other refcount
98- rules write when they translate a failing libc call into
93+ In the unsafe model ` libcc2rs::cpp2rust_errno_unsafe ` wraps the real platform
94+ errno location (` __errno_location ` on Linux, ` __error ` on macOS). The refcount
95+ model instead _ virtualizes _ errno as a thread-local ` Value<i32> ` inside
96+ ` libcc2rs ` ; this is the same cell that other refcount rules write when they
97+ translate a failing libc call into
9998` libcc2rs::cpp2rust_errno().write(__e as i32) ` .
10099
101100A rule pattern may spell either the macro or the shim directly; the two are
@@ -109,17 +108,17 @@ void f2(int fd, fd_set *set) { return FD_SET(fd, set); }
109108
110109## The current shims
111110
112- | Header | Macros | Shim functions | Rules |
113- |---| ---| ---| ---|
114- | `assert.h` | `assert` | `cpp2rust_assert_fail(bool)` | `rules/assert` maps it to `assert!(a0)` |
115- | `errno.h` | `errno` | `cpp2rust_errno()` | `rules/errno`, see above |
116- | `arpa/inet.h` | `ntohl`, `ntohs`, `htonl`, `htons` | `cpp2rust_ntohl(x)`, ... | `rules/arpa_inet` maps them to `u32::from_be`, `u16::to_be`, ... |
111+ | Header | Macros | Shim functions | Rules |
112+ | -------------- | ----------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------- |
113+ | `assert.h` | `assert` | `cpp2rust_assert_fail(bool)` | `rules/assert` maps it to `assert!(a0)` |
114+ | `errno.h` | `errno` | `cpp2rust_errno()` | `rules/errno`, see above |
115+ | `arpa/inet.h` | `ntohl`, `ntohs`, `htonl`, `htons` | `cpp2rust_ntohl(x)`, ... | `rules/arpa_inet` maps them to `u32::from_be`, `u16::to_be`, ... |
117116| `sys/select.h` | `FD_SET`, `FD_CLR`, `FD_ISSET`, `FD_ZERO` | `cpp2rust_fd_set(fd, set)`, ... | `rules/select` maps them to `libc::FD_SET(...)` (unsafe) or `CFdSet` methods (refcount) |
118117
119- Note how the shim also normalizes the *shape* of the API. C's `assert` is a
120- macro precisely so it can stringify its condition and capture file and line;
121- the shim reduces it to a plain `void(bool)` function, and the Rust side
122- regains the diagnostics by mapping it to the `assert!` macro.
118+ Note how the shim also normalizes the _shape_ of the API. C's `assert` is a
119+ macro precisely so it can stringify its condition and capture file and line; the
120+ shim reduces it to a plain `void(bool)` function, and the Rust side regains the
121+ diagnostics by mapping it to the `assert!` macro.
123122
124123## Adding a new shim
125124
@@ -132,15 +131,15 @@ To make another macro-based API matchable:
132131 signature, and redefine the macro to call it.
1331323. Write rules for the shim in a `rules/` module as for any other function,
134133 including the corresponding header in `src.c`/`src.cpp`.
135- 4. If a model needs runtime support (as refcount errno does), implement it
136- in `libcc2rs` and call it from the rule target.
134+ 4. If a model needs runtime support (as refcount errno does), implement it in
135+ `libcc2rs` and call it from the rule target.
137136
138- Keep the shim's signature platform-independent; the whole point is that
139- both sides of every rule see one canonical declaration on every platform.
137+ Keep the shim's signature platform-independent; the whole point is that both
138+ sides of every rule see one canonical declaration on every platform.
140139
141140## Related normalization
142141
143- The same shared flag list also passes `-D_FORTIFY_SOURCE=0`, which keeps
144- glibc from substituting fortified variants (`__printf_chk` and friends) for
145- standard calls. Like the shims, this ensures that calls in the input program
146- resolve to the standard declarations the rules are written against.
142+ The same shared flag list also passes `-D_FORTIFY_SOURCE=0`, which keeps glibc
143+ from substituting fortified variants (`__printf_chk` and friends) for standard
144+ calls. Like the shims, this ensures that calls in the input program resolve to
145+ the standard declarations the rules are written against.
0 commit comments