Skip to content

core: fix build on musl libc (Alpine) - stray #undef _GNU_SOURCE in lib/url.c#4119

Open
Lt-Flash wants to merge 1 commit into
OpenSIPS:masterfrom
Lt-Flash:fix/musl-url-gnu-source
Open

core: fix build on musl libc (Alpine) - stray #undef _GNU_SOURCE in lib/url.c#4119
Lt-Flash wants to merge 1 commit into
OpenSIPS:masterfrom
Lt-Flash:fix/musl-url-gnu-source

Conversation

@Lt-Flash

Copy link
Copy Markdown

What

Fixes the OpenSIPS core failing to compile on musl libc (Alpine Linux) with a modern GCC. A single stray #undef _GNU_SOURCE in lib/url.c hides the POSIX time prototypes on musl, so the very first core object (lib/url.o) never builds.

Symptom

Building master in a stock Alpine 3.24 container (musl 1.2.x, gcc 15.2.0) dies immediately:

Compiling lib/url.c
lib/../mem/../dprint.h:204:9: error: implicit declaration of function 'ctime_r'; did you mean 'ctime'?
lib/../ut.h:1401:5:  error: implicit declaration of function 'clock_gettime'; did you mean 'lock_get'?
lib/../ut.h:1401:19: error: 'CLOCK_REALTIME' undeclared (first use in this function)
make: *** [Makefile.rules:28: lib/url.o] Error 1

With gcc 14+, -Wimplicit-function-declaration is an error by default, so this is a hard build failure, not a warning. The same tree builds fine on glibc.

Root cause

lib/url.c opens with:

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#undef _GNU_SOURCE      /* <-- the problem */

#include "../mem/mem.h"
#include "../ut.h"       /* pulls in <time.h>: clock_gettime, ctime_r, CLOCK_REALTIME */

The difference between the two libcs is when the feature-test macros are evaluated:

  • musl re-checks the feature-test macros in every system header. Its features.h only turns on the permissive default set (_BSD_SOURCE + _XOPEN_SOURCE 700, which is what exposes clock_gettime/ctime_r) when no source macro is defined:

    #if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) \
     && !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) \
     && !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__)
    #define _BSD_SOURCE 1
    #define _XOPEN_SOURCE 700
    #endif

    When <stdio.h> first includes features.h, _GNU_SOURCE is defined, so that default block is skipped and features.h latches (include guard). The following #undef _GNU_SOURCE then leaves the translation unit with no feature-test macro at all. When ut.h later includes <time.h>, features.h is guard-skipped and never re-derives the defaults, so time.h's guarded prototypes (clock_gettime, ctime_r, CLOCK_REALTIME) are all absent -> implicit declarations -> build error.

  • glibc latches internal __USE_POSIX*/__USE_GNU macros at the first header inclusion. The later #undef _GNU_SOURCE does not clear those, so the prototypes stay visible and the build succeeds. That is why this has gone unnoticed.

A one-line reproducer confirms the mechanism on musl:

#define _GNU_SOURCE
#include <stdio.h>
#undef _GNU_SOURCE
#include <time.h>
int main(void){ struct timespec t; clock_gettime(CLOCK_REALTIME,&t); return 0; }
// gcc: implicit declaration of 'clock_gettime'; 'CLOCK_REALTIME' undeclared
// remove the #undef line -> compiles clean

The fix

Drop the #undef and keep _GNU_SOURCE defined for the whole translation unit (with a comment explaining why). This matches how other core sources already enable it unconditionally (io_wait.h, transformations.c), and is a no-op on glibc.

+/* _GNU_SOURCE must stay defined for the rest of this file. On musl libc
+ * the feature-test macros are re-evaluated by every system header, so
+ * undefining it here would hide the clock_gettime()/ctime_r() prototypes
+ * that ut.h and dprint.h (via <time.h>) rely on, breaking the build. */
 #define _GNU_SOURCE
 #include <stdio.h>
 #include <string.h>
-#undef _GNU_SOURCE
 
 #include "../mem/mem.h"
 #include "../ut.h"

Testing

  • Alpine 3.24 / musl 1.2.x / gcc 15.2.0: make opensips fails at lib/url.o before this change; builds to completion after it. Full core binary produced, modules build and load, runtime verified.
  • Debian / glibc: unchanged (the #undef was already a no-op there).

lib/url.c is the only core file that uses this #define/#undef _GNU_SOURCE bracket, so this one file is the whole fix for the core.


Opening as a draft for maintainer review of the approach (per-file fix here vs. defining _GNU_SOURCE tree-wide in the build).

url.c defined _GNU_SOURCE only around <stdio.h>/<string.h> and then
undefined it again before including ut.h. On musl libc the feature-test
macros are re-evaluated by every system header, so once _GNU_SOURCE is
removed the later <time.h> (pulled in through ut.h and dprint.h) no
longer exposes clock_gettime(), CLOCK_REALTIME or ctime_r(). The core
then fails to compile on Alpine/musl with gcc 15:

    lib/../mem/../dprint.h:204: implicit declaration of 'ctime_r'
    lib/../ut.h:1401: implicit declaration of 'clock_gettime'
    lib/../ut.h:1401: 'CLOCK_REALTIME' undeclared

glibc is unaffected: it latches internal __USE_* macros at the first
header inclusion, which the later #undef does not clear.

Keep _GNU_SOURCE defined for the whole translation unit, matching how
other core files (io_wait.h, transformations.c) enable it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant