From aae179177c8e657e40f911ee925b9194e1abf0be Mon Sep 17 00:00:00 2001 From: cpq Date: Sat, 10 Oct 2026 12:25:44 +0100 Subject: [PATCH] Minimise build size --- mongoose.c | 43 +++++++++---------- mongoose.h | 12 ++++-- src/health.h | 12 ++++-- src/net_builtin.c | 2 - src/printf.c | 4 +- src/tls_chacha20.c | 3 -- src/util.c | 34 ++++++++------- test/unit_test.c | 8 ++++ .../stm32/nucleo-h723zg/minimal/Makefile | 6 ++- tutorials/stm32/nucleo-h723zg/minimal/hal.c | 6 +++ tutorials/stm32/nucleo-h723zg/minimal/link.ld | 7 ++- tutorials/stm32/nucleo-h723zg/minimal/main.c | 20 +++++---- 12 files changed, 92 insertions(+), 65 deletions(-) diff --git a/mongoose.c b/mongoose.c index 61afa2f7..032d57c6 100644 --- a/mongoose.c +++ b/mongoose.c @@ -8842,8 +8842,6 @@ static void onstatechange(struct mg_tcpip_if *ifp) { mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { srand((unsigned int) mg_millis()); - } else if (ifp->state == MG_TCPIP_STATE_DOWN) { - MG_ERROR(("Link down")); } mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state); } @@ -13525,8 +13523,10 @@ char *mg_mprintf(const char *fmt, ...) { } void mg_pfn_stdout(char c, void *param) { +#if MG_ENABLE_POSIX_FS putchar(c); - (void) param; +#endif + (void) param, (void) c; } static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) { @@ -19901,9 +19901,6 @@ static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx, // ******* END: poly1305-donna/poly1305-donna.h ******** // ******* BEGIN: chacha-portable.c ******** -#include -#include - // this is a fresh implementation of chacha20, based on the description in // rfc8349 it's such a nice compact algorithm that it is easy to do. In // relationship to other c implementation this implementation: @@ -26539,6 +26536,7 @@ struct mg_str mg_url_pass(const char *url) { + // Not using memset for zeroing memory, cause it can be dropped by compiler // See https://github.com/cesanta/mongoose/pull/1265 void mg_bzero(volatile unsigned char *buf, size_t len) { @@ -26681,22 +26679,21 @@ uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) { return (uint16_t) ~c; } -static int isbyte(int n) { - return n >= 0 && n <= 255; -} - -static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { - int n, a, b, c, d, slash = 32, len = 0; - if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || - sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && - isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && - slash < 33) { - len = n; - *net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | - (uint32_t) d; - *mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0; +// Parse "IP" or "IP/BITS" into net and mask. The whole spec must be valid +static bool parse_net(struct mg_str spec, uint32_t *net, uint32_t *mask) { + struct mg_str caps[3], ip = spec, bits = mg_str("32"); + struct mg_addr addr; + uint8_t n = 0; + if (mg_match(spec, mg_str("*/*"), caps)) ip = caps[0], bits = caps[1]; + // ip.len == 0 check: mg_aton() parses an empty string as 0.0.0.0 + if (ip.len == 0 || !mg_aton(ip, &addr) || addr.is_ip6 || + !mg_str_to_num(bits, 10, &n, sizeof(n)) || n > 32) { + return false; } - return len; + memcpy(net, addr.addr.ip, sizeof(*net)); + *net = mg_ntohl(*net); + *mask = n ? (uint32_t) (0xffffffffU << (32 - n)) : (uint32_t) 0; + return true; } int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) { @@ -26710,7 +26707,9 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) { while (mg_span(acl, &entry, &acl, ',')) { uint32_t net, mask; if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1; - if (parse_net(&entry.buf[1], &net, &mask) == 0) return -2; + if (!parse_net(mg_str_n(entry.buf + 1, entry.len - 1), &net, &mask)) { + return -2; + } if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0]; } } diff --git a/mongoose.h b/mongoose.h index 8a784269..9546aa4c 100644 --- a/mongoose.h +++ b/mongoose.h @@ -4931,6 +4931,14 @@ void mg_ota_poll(struct mg_mgr *); #define MG_HEALTH_RAM __attribute__((section(".mg_health"))) #endif +#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5)) +#define MG_HEALTH_DATA_SIZE 250 +#endif + +#ifndef MG_HEALTH_DATA_SIZE +#define MG_HEALTH_DATA_SIZE 10 +#endif + // Places the record in RAM that survives a reset. Define in mongoose_config.h // to match the linker script, e.g. __attribute__((section(".mg_health"))) #ifndef MG_HEALTH_RAM @@ -4951,10 +4959,6 @@ enum mg_health_reason { MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack }; -#ifndef MG_HEALTH_DATA_SIZE -#define MG_HEALTH_DATA_SIZE 250 -#endif - struct mg_health { char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data uint32_t reset_reason; // enum mg_health_reason that started this boot diff --git a/src/health.h b/src/health.h index d438b0b2..34711ec9 100644 --- a/src/health.h +++ b/src/health.h @@ -13,6 +13,14 @@ #define MG_HEALTH_RAM __attribute__((section(".mg_health"))) #endif +#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5)) +#define MG_HEALTH_DATA_SIZE 250 +#endif + +#ifndef MG_HEALTH_DATA_SIZE +#define MG_HEALTH_DATA_SIZE 10 +#endif + // Places the record in RAM that survives a reset. Define in mongoose_config.h // to match the linker script, e.g. __attribute__((section(".mg_health"))) #ifndef MG_HEALTH_RAM @@ -33,10 +41,6 @@ enum mg_health_reason { MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack }; -#ifndef MG_HEALTH_DATA_SIZE -#define MG_HEALTH_DATA_SIZE 250 -#endif - struct mg_health { char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data uint32_t reset_reason; // enum mg_health_reason that started this boot diff --git a/src/net_builtin.c b/src/net_builtin.c index 511a6c76..f017fe00 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -422,8 +422,6 @@ static void onstatechange(struct mg_tcpip_if *ifp) { mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { srand((unsigned int) mg_millis()); - } else if (ifp->state == MG_TCPIP_STATE_DOWN) { - MG_ERROR(("Link down")); } mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state); } diff --git a/src/printf.c b/src/printf.c index 409bcb6b..2ee9b4f4 100644 --- a/src/printf.c +++ b/src/printf.c @@ -71,8 +71,10 @@ char *mg_mprintf(const char *fmt, ...) { } void mg_pfn_stdout(char c, void *param) { +#if MG_ENABLE_POSIX_FS putchar(c); - (void) param; +#endif + (void) param, (void) c; } static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) { diff --git a/src/tls_chacha20.c b/src/tls_chacha20.c index 08206872..291edc73 100644 --- a/src/tls_chacha20.c +++ b/src/tls_chacha20.c @@ -60,9 +60,6 @@ static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx, // ******* END: poly1305-donna/poly1305-donna.h ******** // ******* BEGIN: chacha-portable.c ******** -#include -#include - // this is a fresh implementation of chacha20, based on the description in // rfc8349 it's such a nice compact algorithm that it is easy to do. In // relationship to other c implementation this implementation: diff --git a/src/util.c b/src/util.c index 24619fd2..eb54bf7e 100644 --- a/src/util.c +++ b/src/util.c @@ -1,5 +1,6 @@ #include "util.h" #include "log.h" +#include "net.h" // Not using memset for zeroing memory, cause it can be dropped by compiler // See https://github.com/cesanta/mongoose/pull/1265 @@ -143,22 +144,21 @@ uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) { return (uint16_t) ~c; } -static int isbyte(int n) { - return n >= 0 && n <= 255; -} - -static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { - int n, a, b, c, d, slash = 32, len = 0; - if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || - sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && - isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && - slash < 33) { - len = n; - *net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | - (uint32_t) d; - *mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0; +// Parse "IP" or "IP/BITS" into net and mask. The whole spec must be valid +static bool parse_net(struct mg_str spec, uint32_t *net, uint32_t *mask) { + struct mg_str caps[3], ip = spec, bits = mg_str("32"); + struct mg_addr addr; + uint8_t n = 0; + if (mg_match(spec, mg_str("*/*"), caps)) ip = caps[0], bits = caps[1]; + // ip.len == 0 check: mg_aton() parses an empty string as 0.0.0.0 + if (ip.len == 0 || !mg_aton(ip, &addr) || addr.is_ip6 || + !mg_str_to_num(bits, 10, &n, sizeof(n)) || n > 32) { + return false; } - return len; + memcpy(net, addr.addr.ip, sizeof(*net)); + *net = mg_ntohl(*net); + *mask = n ? (uint32_t) (0xffffffffU << (32 - n)) : (uint32_t) 0; + return true; } int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) { @@ -172,7 +172,9 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) { while (mg_span(acl, &entry, &acl, ',')) { uint32_t net, mask; if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1; - if (parse_net(&entry.buf[1], &net, &mask) == 0) return -2; + if (!parse_net(mg_str_n(entry.buf + 1, entry.len - 1), &net, &mask)) { + return -2; + } if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0]; } } diff --git a/test/unit_test.c b/test/unit_test.c index bfba64c9..3f0660c5 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -3663,6 +3663,14 @@ static void test_check_ip_acl(void) { ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.0.0.0/8"), &ip) == 1); ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.2.3.4"), &ip) == 1); ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.0.0.0/16"), &ip) == 0); + ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4xyz"), &ip) == -2); // junk + ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4/32abc"), &ip) == -2); // junk + ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4/"), &ip) == -2); + ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4/33"), &ip) == -2); + ASSERT(mg_check_ip_acl(mg_str("+1.2.3.256"), &ip) == -2); + ASSERT(mg_check_ip_acl(mg_str("+"), &ip) == -2); + // Not NUL-terminated: must not read past the end of the ACL + ASSERT(mg_check_ip_acl(mg_str_n("+1.2.3.4xyz", 8), &ip) == 1); ip.is_ip6 = true; ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0"), &ip) == -1); // not yet supported diff --git a/tutorials/stm32/nucleo-h723zg/minimal/Makefile b/tutorials/stm32/nucleo-h723zg/minimal/Makefile index 0b65a6bc..e76b7518 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/Makefile +++ b/tutorials/stm32/nucleo-h723zg/minimal/Makefile @@ -8,7 +8,7 @@ CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion CFLAGS += -g3 -Os -ffunction-sections -fdata-sections CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA) -LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map +LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections SOURCES = main.c hal.c SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h723xx.s @@ -28,6 +28,10 @@ firmware.signed.bin: firmware.bin test -f private.pem || node $(MGDIR)/resources/sign.js keygen node $(MGDIR)/resources/sign.js sign firmware.bin +# Show all symbols sorted by size +show_symbols_sorted_by_size: firmware.bin + arm-none-eabi-readelf -sW firmware.elf | sed 1,3d | sed 's,^.*: ,,' | tr -s ' ' | cut -d ' ' -f 2,7 | sort -nk1 | grep -v '^0 ' + flash: firmware.bin STM32_Programmer_CLI -c port=swd -w firmware.elf -hardRst diff --git a/tutorials/stm32/nucleo-h723zg/minimal/hal.c b/tutorials/stm32/nucleo-h723zg/minimal/hal.c index e519bff3..22175a26 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/hal.c +++ b/tutorials/stm32/nucleo-h723zg/minimal/hal.c @@ -3,6 +3,12 @@ #include "hal.h" +// Added to minimise the bin size: avoid pulling printf and friends +void __assert_func(const char *s, int line, const char *fn, const char *expr) { + (void) s, (void) line, (void) fn, (void) expr; + for (;;) (void) 0; // or trigger the fault handler +} + bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) { uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry if (period == 0) return false; // Avoid division by zero diff --git a/tutorials/stm32/nucleo-h723zg/minimal/link.ld b/tutorials/stm32/nucleo-h723zg/minimal/link.ld index 617b312a..f685f592 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/link.ld +++ b/tutorials/stm32/nucleo-h723zg/minimal/link.ld @@ -21,11 +21,10 @@ SECTIONS { _sidata = LOADADDR(.data); .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > ram_d1 + _end = ALIGN(_ebss, 8); /* Heap starts after .bss */ - .eth_ram : { *(.eth_ram .eth_ram*) } > ram_d1 AT > flash - - . = ALIGN(8); - _end = .; + /* Place .eth_ram into the ETH DMA accessible region */ + .eth_ram (NOLOAD) : { *(.eth_ram .eth_ram*) } > ram_d2 /* Retained health record. Startup code must neither copy nor clear it. */ .mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > ram_d3 diff --git a/tutorials/stm32/nucleo-h723zg/minimal/main.c b/tutorials/stm32/nucleo-h723zg/minimal/main.c index 83813d13..34d597cd 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/main.c +++ b/tutorials/stm32/nucleo-h723zg/minimal/main.c @@ -45,12 +45,15 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); - } else if (mg_match(hm->uri, mg_str("/api/kill"), NULL)) { - SCB->SHCSR &= ~SCB_SHCSR_USGFAULTENA_Msk; - __asm volatile("udf #0"); + } else if (mg_match(hm->uri, mg_str("/api/crash"), NULL)) { + __asm volatile("udf #0"); // Trigger HardFault } else { - mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", - hal_get_tick()); + mg_http_reply(c, 200, "", + "Hi from Mongoose!\n" + "tick: %llu\n" + "ram used: %zu\n" + "ram free: %zu\n", + hal_get_tick(), hal_ram_used(), hal_ram_free()); } } } @@ -67,13 +70,14 @@ int main(void) { hal_gpio_output(LED2); hal_gpio_output(LED3); - MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); - MG_INFO(("Crash record: %M", mg_print_crash_record)); - struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); + MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); + MG_INFO(("Crash record: %M", mg_print_crash_record)); + for (;;) { mg_mgr_poll(&mgr, 0); blink_task();