summaryrefslogtreecommitdiff
path: root/src/net/hosted/abi_assert.c
blob: 8815e89a0284cdbc07a66d5621771cb94d7e01ff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
 * The one ABI check that stands between this build and a silent hang.
 *
 * `hosted_osi_funcs_t` (host/esp_hosted_os_abstraction.h) is the function-pointer table ESP-Hosted
 * reaches everything through - memory, sync, threads, GPIO, SDIO. src/net/port.zig defines it in
 * Zig. Zig can assert its own layout; it cannot assert C's. This file asserts C's, so the two are
 * checked against each other at build time.
 *
 * Why this is not paranoia. Four of the table's entries are guarded:
 *
 *     #ifdef H_USE_MEMPOOL          <- host/esp_hosted_os_abstraction.h:64-69
 *         void *(*_h_get_mempool)(...);
 *         ...
 *     #endif
 *
 * `#ifdef`, not `#if`. H_USE_MEMPOOL is defined by
 * host/port/esp/freertos/include/port_esp_hosted_host_config.h:127-131 - to 1 or to 0, but always
 * DEFINED. So the four pointers are present in any translation unit that saw that header, and
 * absent in any that did not, and every entry after them shifts by four pointers.
 *
 * That is reachable, not theoretical: host/esp_hosted.h:14 and
 * host/drivers/transport/transport_util.h:10 both include esp_hosted_os_abstraction.h as their
 * FIRST include, so a TU reaching the struct through either of those - before any port header -
 * gets the short layout. Under IDF's CMake the ordering happens to work out. Under our flags it
 * would be luck.
 *
 * Measured with our exact flags, both ways:
 *
 *                                     sizeof   _h_config_gpio   _h_event_post
 *     without the force-include         268          132              264
 *     with the force-include            284          148              280
 *
 * Sixteen bytes. A TU with the short layout calling _h_config_gpio jumps through a mempool
 * pointer instead - which is a jump to the wrong function, on a board with no debugger, and the
 * symptom would look exactly like the SDIO bus failing to come up.
 *
 * build.zig therefore force-includes port_esp_hosted_host_config.h into every ESP-Hosted
 * translation unit, and compiles this file to assert that it worked. The numbers below are the
 * long (correct) layout.
 */

#include "esp_hosted_os_abstraction.h"
#include <stddef.h>

/* The guard must be visible here, or this file is asserting the wrong layout and proving nothing. */
#ifndef H_USE_MEMPOOL
#error "H_USE_MEMPOOL is not visible: the force-include of port_esp_hosted_host_config.h is missing."
#endif

_Static_assert(
    sizeof(hosted_osi_funcs_t) == 284,
    "hosted_osi_funcs_t is not the 284-byte layout. Either the force-include of "
    "port_esp_hosted_host_config.h was lost (short layout, 268), or ESP-Hosted changed the table. "
    "Compare against the struct in src/net/port.zig before touching this number.");

/* Two offsets, chosen because they sit on either side of the mempool block: the first entry after
 * it, and one near the end. If the block appears or disappears, both move. */
_Static_assert(
    offsetof(hosted_osi_funcs_t, _h_config_gpio) == 148,
    "_h_config_gpio moved. It is the first entry after the #ifdef H_USE_MEMPOOL block, so this is "
    "what the short layout breaks first: 132 instead of 148.");

_Static_assert(
    offsetof(hosted_osi_funcs_t, _h_event_post) == 280,
    "_h_event_post moved. Together with the _h_config_gpio assertion this pins both ends of the "
    "table.");

/* Field count, checked through the size. src/net/port.zig asserts its Zig struct has 71 fields;
 * every entry is a pointer, so 71 * 4 must be the size on this 32-bit target. A size check alone
 * would not catch a field deleted in one place and duplicated in another - the length survives and
 * the two sides silently disagree about which pointer is which. */
_Static_assert(
    sizeof(hosted_osi_funcs_t) == 71 * sizeof(void (*)(void)),
    "hosted_osi_funcs_t is not 71 function pointers. Compare field by field against the struct in "
    "src/net/port.zig - a count mismatch means one side has an entry the other does not, and every "
    "entry after it calls the wrong function.");

const int esp_hosted_abi_assertions_hold = 1;