diff --git a/lib/libc/glibc/io/fcntl.h b/lib/libc/glibc/io/fcntl.h index 8917a73b420b..5c76999da3ae 100644 --- a/lib/libc/glibc/io/fcntl.h +++ b/lib/libc/glibc/io/fcntl.h @@ -167,6 +167,10 @@ typedef __pid_t pid_t; effective IDs, not real IDs. */ #endif + +/* fcntl was a simple symbol until glibc 2.27 inclusive. glibc 2.28 onwards + * re-defines it to fcntl64 (via #define) if _FILE_OFFSET_BITS == 64. */ +#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2 /* Do the file control operation described by CMD on FD. The remaining arguments are interpreted depending on CMD. @@ -197,6 +201,9 @@ extern int __fcntl_time64 (int __fd, int __request, ...) __THROW; # define fcntl __fcntl_time64 # endif #endif +#else /* glibc 2.27 or lower */ +extern int fcntl (int __fd, int __cmd, ...); +#endif /* Open FILE and return a new file descriptor for it, or -1 on error. OFLAG determines the type of access used. If O_CREAT or O_TMPFILE is set diff --git a/lib/libc/include/generic-glibc/fcntl.h b/lib/libc/include/generic-glibc/fcntl.h index ceeb63138b0f..fb93ee658bbc 100644 --- a/lib/libc/include/generic-glibc/fcntl.h +++ b/lib/libc/include/generic-glibc/fcntl.h @@ -167,6 +167,11 @@ typedef __pid_t pid_t; effective IDs, not real IDs. */ #endif + +/* fcntl was a simple symbol until glibc 2.27 inclusive. + * glibc 2.28 onwards converted it to a macro when compiled with + * USE_LARGEFILE64. */ +#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2 /* Do the file control operation described by CMD on FD. The remaining arguments are interpreted depending on CMD. @@ -197,6 +202,9 @@ extern int __fcntl_time64 (int __fd, int __request, ...) __THROW; # define fcntl __fcntl_time64 # endif #endif +#else /* glibc 2.27 or lower */ +extern int fcntl (int __fd, int __cmd, ...); +#endif /* Open FILE and return a new file descriptor for it, or -1 on error. OFLAG determines the type of access used. If O_CREAT or O_TMPFILE is set diff --git a/lib/libc/include/generic-glibc/resolv.h b/lib/libc/include/generic-glibc/resolv.h index aadb9e79a7cb..406727e8fb82 100644 --- a/lib/libc/include/generic-glibc/resolv.h +++ b/lib/libc/include/generic-glibc/resolv.h @@ -169,6 +169,28 @@ __END_DECLS #define res_init __res_init #define res_isourserver __res_isourserver +/* In glibc 2.33 and earlier res_search, res_nsearch, res_query, res_nquery, + * res_querydomain, res_nquerydomain were #define'd to __res_search, + * __res_nsearch, etc. glibc 2.34 onwards removes the macros and exposes the + * symbols directly. New glibc exposes compat symbols with underscores for + * backwards compatibility. Applications linked to glibc 2.34+ are expected + * to use the non-underscored symbols. + * + * It will be enough to bring the macros back when compiling against the older + * glibc versions. + * + * See glibc commit ea9878ec271c791880fcbbe519d70c42f8113750. + */ +#if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 34) +#define res_search __res_search +#define res_nsearch __res_nsearch +#define res_query __res_query +#define res_nquery __res_nquery +#define res_querydomain __res_querydomain +#define res_nquerydomain __res_nquerydomain +#endif +/* end glibc compat hacks */ + #ifdef _LIBC # define __RESOLV_DEPRECATED # define __RESOLV_DEPRECATED_MSG(msg) diff --git a/test/link.zig b/test/link.zig index aa0ed4817e33..4b7e7f88d623 100644 --- a/test/link.zig +++ b/test/link.zig @@ -20,6 +20,10 @@ pub const cases = [_]Case{ .build_root = "test/link/interdependent_static_c_libs", .import = @import("link/interdependent_static_c_libs/build.zig"), }, + .{ + .build_root = "test/link/glibc_compat", + .import = @import("link/glibc_compat/build.zig"), + }, // WASM Cases .{ diff --git a/test/link/glibc_compat/build.zig b/test/link/glibc_compat/build.zig new file mode 100644 index 000000000000..9859ee538abe --- /dev/null +++ b/test/link/glibc_compat/build.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test"); + b.default_step = test_step; + + const targets: []const []const u8 = &.{ + "aarch64-linux-gnu.2.27", + "aarch64-linux-gnu.2.28", + "aarch64-linux-gnu.2.33", + "aarch64-linux-gnu.2.34", + }; + + for (targets) |target| { + const exe = b.addExecutable(.{ + .name = target, + .root_source_file = .{ .path = "main.c" }, + .target = std.zig.CrossTarget.parse( + .{ .arch_os_abi = target }, + ) catch unreachable, + }); + exe.linkLibC(); + test_step.dependOn(&exe.step); + } +} diff --git a/test/link/glibc_compat/main.c b/test/link/glibc_compat/main.c new file mode 100644 index 000000000000..d9059eb8a628 --- /dev/null +++ b/test/link/glibc_compat/main.c @@ -0,0 +1,37 @@ +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include + +int main() { + /* in glibc 2.28+ and _FILE_OFFSET_BITS=64 fcntl is #define'd to fcntl64 + * Thus headers say `fcntl64` exists, but libc.so.6 (the old one) + * disagrees, resulting in a linking error unless headers are made + * backwards-compatible. + * + * Glibc 2.28+: + * FUNC GLOBAL DEFAULT UND fcntl64@GLIBC_2.28 (3): + * + * Glibc 2.27 or older: + * FUNC GLOBAL DEFAULT UND fcntl@GLIBC_2.2.5 + */ + printf("address to fcntl: %p\n", fcntl); + + /* The following functions became symbols of their own right with glibc + * 2.34+. Before 2.34 resolv.h would #define res_search __res_search; and + * __res_search is a valid symbol since the beginning of time. + * + * On glibc 2.34+ these symbols are linked this way: + * FUNC GLOBAL DEFAULT UND res_search@GLIBC_2.34 (2) + * + * Pre-glibc 2.34: + * FUNC GLOBAL DEFAULT UND __res_search@GLIBC_2.2.5 (4) + */ + printf("address to res_search: %p\n", res_search); + printf("address to res_nsearch: %p\n", res_nsearch); + printf("address to res_query: %p\n", res_query); + printf("address to res_nquery: %p\n", res_nquery); + printf("address to res_querydomain: %p\n", res_querydomain); + printf("address to res_nquerydomain: %p\n", res_nquerydomain); +}