|
51 | 51 | #endif |
52 | 52 |
|
53 | 53 | #include <fcntl.h> |
| 54 | +#include <inttypes.h> |
54 | 55 | #include <math.h> |
| 56 | +#include <stdarg.h> |
55 | 57 | #include <stdio.h> |
| 58 | +#include <stdlib.h> |
56 | 59 | #include <string.h> |
57 | 60 | #include <unistd.h> |
58 | 61 |
|
@@ -147,4 +150,126 @@ TCN_MUSL_COMPAT char *__strdup(const char *str) { |
147 | 150 | return strdup(str); |
148 | 151 | } |
149 | 152 |
|
| 153 | +/* |
| 154 | + * _FORTIFY_SOURCE entry points. Everything this project compiles passes -U_FORTIFY_SOURCE, so |
| 155 | + * none of our own objects reference these. Two archives we link but do not build can: on a |
| 156 | + * Debian-derived toolchain the packaged libstdc++.a (cp-demangle.o, floating_to_chars.o, |
| 157 | + * debug.o) and libgcc.a (_eprintf.o) are themselves fortified, and cp-demangle.o is pulled in |
| 158 | + * through the verbose terminate handler. The result is an undefined __sprintf_chk that makes |
| 159 | + * `ldd` fail on Alpine even though nothing on the load path calls it. Same shape as the rest |
| 160 | + * of this file: weak so glibc's own definition wins on glibc, defined here so musl has one. |
| 161 | + * |
| 162 | + * Semantics follow glibc: slen is the destination size, or (size_t) -1 when unknown. |
| 163 | + */ |
| 164 | +TCN_MUSL_COMPAT int __sprintf_chk(char *s, int flag, size_t slen, const char *format, ...) { |
| 165 | + va_list ap; |
| 166 | + int written; |
| 167 | + (void) flag; |
| 168 | + va_start(ap, format); |
| 169 | + if (slen == (size_t) -1) { |
| 170 | + written = vsprintf(s, format, ap); |
| 171 | + } else { |
| 172 | + written = vsnprintf(s, slen, format, ap); |
| 173 | + } |
| 174 | + va_end(ap); |
| 175 | + return written; |
| 176 | +} |
| 177 | + |
| 178 | +TCN_MUSL_COMPAT int __fprintf_chk(FILE *stream, int flag, const char *format, ...) { |
| 179 | + va_list ap; |
| 180 | + int written; |
| 181 | + (void) flag; |
| 182 | + va_start(ap, format); |
| 183 | + written = vfprintf(stream, format, ap); |
| 184 | + va_end(ap); |
| 185 | + return written; |
| 186 | +} |
| 187 | + |
| 188 | +/* |
| 189 | + * glibc 2.32+ exports this byte, and libstdc++ 11+ headers read it (ext/atomicity.h, |
| 190 | + * __gnu_cxx::__is_single_threaded) to skip atomic reference counting while a process is still |
| 191 | + * single-threaded. Any C++ compiled against such headers -- BoringSSL's libssl on a current |
| 192 | + * toolchain -- imports it as a plain data symbol, and musl has no such thing. Zero is the |
| 193 | + * conservative value: "not single-threaded", so the atomic path is always taken. On glibc the |
| 194 | + * libc definition wins as usual. The JVM is multi-threaded long before this library loads, so |
| 195 | + * the value could never legitimately be nonzero here anyway. |
| 196 | + */ |
| 197 | +TCN_MUSL_COMPAT char __libc_single_threaded = 0; |
| 198 | + |
| 199 | +/* |
| 200 | + * glibc 2.38 made the integer parsers and the scanf family C23-conformant (binary "0b" |
| 201 | + * prefixes) under a new symbol version, and redirects every call to an __isoc23_* name |
| 202 | + * whenever _GNU_SOURCE is defined, whatever -std says. Anything built on glibc >= 2.38 |
| 203 | + * (Ubuntu 24.04, Debian 13) imports them: APR (sockaddr.o, apr_strings.o), the packaged libstdc++.a |
| 204 | + * (eh_alloc.o, debug.o), and on x86_64 something else again brings in strtoull. musl exports |
| 205 | + * only the plain names. The whole family is covered here so the next builder does not find |
| 206 | + * the next member. |
| 207 | + * |
| 208 | + * The bodies must call the PLAIN symbols. A literal strtol() here is subject to the same |
| 209 | + * redirect, so on musl it would resolve to this very function and recurse. The asm labels |
| 210 | + * bind each reference to the unversioned name, which both libcs export. |
| 211 | + * |
| 212 | + * __restrict, not restrict: the Debian 7 image compiles with GCC 4.9, whose default is gnu90, |
| 213 | + * where `restrict` is not a keyword. |
| 214 | + */ |
| 215 | +extern long tcn_plain_strtol(const char *, char **, int) __asm__("strtol"); |
| 216 | +extern unsigned long tcn_plain_strtoul(const char *, char **, int) __asm__("strtoul"); |
| 217 | +extern long long tcn_plain_strtoll(const char *, char **, int) __asm__("strtoll"); |
| 218 | +extern unsigned long long tcn_plain_strtoull(const char *, char **, int) __asm__("strtoull"); |
| 219 | +extern intmax_t tcn_plain_strtoimax(const char *, char **, int) __asm__("strtoimax"); |
| 220 | +extern uintmax_t tcn_plain_strtoumax(const char *, char **, int) __asm__("strtoumax"); |
| 221 | +extern int tcn_plain_vsscanf(const char *, const char *, va_list) __asm__("vsscanf"); |
| 222 | + |
| 223 | +TCN_MUSL_COMPAT long __isoc23_strtol(const char *__restrict nptr, char **__restrict endptr, int base) { |
| 224 | + return tcn_plain_strtol(nptr, endptr, base); |
| 225 | +} |
| 226 | + |
| 227 | +TCN_MUSL_COMPAT unsigned long __isoc23_strtoul(const char *__restrict nptr, char **__restrict endptr, int base) { |
| 228 | + return tcn_plain_strtoul(nptr, endptr, base); |
| 229 | +} |
| 230 | + |
| 231 | +TCN_MUSL_COMPAT long long __isoc23_strtoll(const char *__restrict nptr, char **__restrict endptr, int base) { |
| 232 | + return tcn_plain_strtoll(nptr, endptr, base); |
| 233 | +} |
| 234 | + |
| 235 | +TCN_MUSL_COMPAT unsigned long long __isoc23_strtoull(const char *__restrict nptr, char **__restrict endptr, int base) { |
| 236 | + return tcn_plain_strtoull(nptr, endptr, base); |
| 237 | +} |
| 238 | + |
| 239 | +TCN_MUSL_COMPAT intmax_t __isoc23_strtoimax(const char *__restrict nptr, char **__restrict endptr, int base) { |
| 240 | + return tcn_plain_strtoimax(nptr, endptr, base); |
| 241 | +} |
| 242 | + |
| 243 | +TCN_MUSL_COMPAT uintmax_t __isoc23_strtoumax(const char *__restrict nptr, char **__restrict endptr, int base) { |
| 244 | + return tcn_plain_strtoumax(nptr, endptr, base); |
| 245 | +} |
| 246 | + |
| 247 | +TCN_MUSL_COMPAT int __isoc23_vsscanf(const char *__restrict str, const char *__restrict format, va_list ap) { |
| 248 | + return tcn_plain_vsscanf(str, format, ap); |
| 249 | +} |
| 250 | + |
| 251 | +TCN_MUSL_COMPAT int __isoc23_sscanf(const char *__restrict str, const char *__restrict format, ...) { |
| 252 | + va_list ap; |
| 253 | + int matched; |
| 254 | + va_start(ap, format); |
| 255 | + matched = tcn_plain_vsscanf(str, format, ap); |
| 256 | + va_end(ap); |
| 257 | + return matched; |
| 258 | +} |
| 259 | + |
| 260 | +/* |
| 261 | + * glibc 2.35 added _dl_find_object, and libgcc_eh.a from gcc 12 on (unwind-dw2-fde-dip.o) |
| 262 | + * calls it to locate a frame's .eh_frame when unwinding. musl has no equivalent. Returning |
| 263 | + * -1 means "no object found": the unwinder then reports no FDE and a C++ exception would |
| 264 | + * terminate instead of propagating. Nothing here throws - BoringSSL compiles its C++ with |
| 265 | + * -fno-exceptions, APR and this module are C - so the only observable effect is that the |
| 266 | + * symbol resolves. Declared with a void * result on purpose: the real struct only exists in |
| 267 | + * glibc >= 2.35 headers and the release image is glibc 2.12. |
| 268 | + */ |
| 269 | +TCN_MUSL_COMPAT int _dl_find_object(void *address, void *result) { |
| 270 | + (void) address; |
| 271 | + (void) result; |
| 272 | + return -1; |
| 273 | +} |
| 274 | + |
150 | 275 | #endif /* __linux__ */ |
0 commit comments