如何强制链接到旧的 libc `fcntl` 而不是 `fcntl64`?
How to force linkage to older libc `fcntl` instead of `fcntl64`?
好像GLIBC 2.28 (released August 2018) made a fairly aggressive change to fcntl. The definition was changed in <fcntl.h>
to no longer be an external function, but a #define
to fcntl64.
结果是,如果您在使用此 glibc 的系统上编译代码——如果它完全使用 fcntl() ——生成的二进制文件将不会在2018 年 8 月之前的系统。这会影响各种各样的应用程序...fcntl() 的手册页显示它是一小部分子函数的入口点:
https://linux.die.net/man/2/fcntl
如果您能告诉链接器您需要哪个特定版本的 GLIBC 函数,那就太好了。但我发现的最接近的是这个技巧在另一个 post:
的回答中描述
Answer to "Linking against older symbol version in a .so file"
这有点复杂。 fcntl
是可变的,没有 vffcntl
而需要 va_list。在这种情况下 you cannot forward an invocation of a variadic function。 :-(
当一个人拥有故意降低依赖性的稳定代码时,在当前 Ubuntu 上构建它是一种失望......然后让可执行文件拒绝 运行 在另一个 Ubuntu 仅在一年前(接近当天)发布。对此有什么办法?
What recourse does one have for this?
GLIBC 无法 #define USE_FCNTL_NOT_FCNTL64
这一事实说明了很多。无论是对是错,大多数 OS+ 工具链制造商似乎已经决定,将新系统的旧版本系统的二进制文件作为目标不是高优先级。
阻力最小的方法是让虚拟机围绕最旧的 OS+ 构建项目的工具链。每当您认为二进制文件在旧系统上 运行 时,就可以使用它来制作二进制文件。
但是...
- 如果您认为您的用法是在不受偏移大小更改影响的 fcntl() 调用的子集中(也就是说您不使用字节范围锁)
- 或者愿意审查您的代码以使用向后兼容的结构定义
- 并且不害怕巫术
...然后继续阅读。
The name is different, and fcntl is variadic without a vffcntl that takes a va_list. In such situations you cannot forward an invocation of a variadic function.
...然后要应用 the wrapping trick mentioned,您必须逐行查看 fcntl() 的接口文档,按原样解压可变参数,然后使用新的可变参数调用。
幸运的是,情况并没有那么困难(fcntl 采用 0 或 1 个具有记录类型的参数)。为了尝试为其他人省去一些麻烦,这里是代码。务必将 --wrap=fcntl64 传递给链接器(-Wl,--wrap=fcntl64 如果不直接调用 ld):
asm (".symver fcntl64, fcntl@GLIBC_2.2.5");
extern "C" int __wrap_fcntl64(int fd, int cmd, ...)
{
int result;
va_list va;
va_start(va, cmd);
switch (cmd) {
//
// File descriptor flags
//
case F_GETFD: goto takes_void;
case F_SETFD: goto takes_int;
// File status flags
//
case F_GETFL: goto takes_void;
case F_SETFL: goto takes_int;
// File byte range locking, not held across fork() or clone()
//
case F_SETLK: goto takes_flock_ptr_INCOMPATIBLE;
case F_SETLKW: goto takes_flock_ptr_INCOMPATIBLE;
case F_GETLK: goto takes_flock_ptr_INCOMPATIBLE;
// File byte range locking, held across fork()/clone() -- Not POSIX
//
case F_OFD_SETLK: goto takes_flock_ptr_INCOMPATIBLE;
case F_OFD_SETLKW: goto takes_flock_ptr_INCOMPATIBLE;
case F_OFD_GETLK: goto takes_flock_ptr_INCOMPATIBLE;
// Managing I/O availability signals
//
case F_GETOWN: goto takes_void;
case F_SETOWN: goto takes_int;
case F_GETOWN_EX: goto takes_f_owner_ex_ptr;
case F_SETOWN_EX: goto takes_f_owner_ex_ptr;
case F_GETSIG: goto takes_void;
case F_SETSIG: goto takes_int;
// Notified when process tries to open or truncate file (Linux 2.4+)
//
case F_SETLEASE: goto takes_int;
case F_GETLEASE: goto takes_void;
// File and directory change notification
//
case F_NOTIFY: goto takes_int;
// Changing pipe capacity (Linux 2.6.35+)
//
case F_SETPIPE_SZ: goto takes_int;
case F_GETPIPE_SZ: goto takes_void;
// File sealing (Linux 3.17+)
//
case F_ADD_SEALS: goto takes_int;
case F_GET_SEALS: goto takes_void;
// File read/write hints (Linux 4.13+)
//
case F_GET_RW_HINT: goto takes_uint64_t_ptr;
case F_SET_RW_HINT: goto takes_uint64_t_ptr;
case F_GET_FILE_RW_HINT: goto takes_uint64_t_ptr;
case F_SET_FILE_RW_HINT: goto takes_uint64_t_ptr;
default:
fprintf(stderr, "fcntl64 workaround got unknown F_XXX constant")
}
takes_void:
va_end(va);
return fcntl64(fd, cmd);
takes_int:
result = fcntl64(fd, cmd, va_arg(va, int));
va_end(va);
return result;
takes_flock_ptr_INCOMPATIBLE:
//
// !!! This is the breaking case: the size of the flock
// structure changed to accommodate larger files. If you
// need this, you'll have to define a compatibility struct
// with the older glibc and make your own entry point using it,
// then call fcntl64() with it directly (bear in mind that has
// been remapped to the old fcntl())
//
fprintf(stderr, "fcntl64 hack can't use glibc flock directly");
exit(1);
takes_f_owner_ex_ptr:
result = fcntl64(fd, cmd, va_arg(va, struct f_owner_ex*));
va_end(va);
return result;
takes_uint64_t_ptr:
result = fcntl64(fd, cmd, va_arg(va, uint64_t*));
va_end(va);
return result;
}
请注意,根据您实际构建的版本,如果这些部分不可用,您可能必须 #ifdef 将其中一些标记部分删除。
This affects quite a variety of applications...the manual page for fcntl() shows that it's the entry point for a small universe of sub-functions
...这可能应该给人们上一课:避免通过可变参数滥用创建这样的 "kitchen sink" 函数。
How to force linkage to older libc fcntl
instead of fcntl64
?
针对旧版本 libc
进行编译。期间.
因为glibc不是forward compatible, it is only backwards-compatible:
The GNU C Library is designed to be a backwards compatible, portable, and high performance ISO C library. It aims to follow all relevant standards including ISO C11, POSIX.1-2008, and IEEE 754-2008.
没有任何向前兼容性的保证,你不知道还有什么不能正常工作。
好像GLIBC 2.28 (released August 2018) made a fairly aggressive change to fcntl. The definition was changed in <fcntl.h>
to no longer be an external function, but a #define
to fcntl64.
结果是,如果您在使用此 glibc 的系统上编译代码——如果它完全使用 fcntl() ——生成的二进制文件将不会在2018 年 8 月之前的系统。这会影响各种各样的应用程序...fcntl() 的手册页显示它是一小部分子函数的入口点:
https://linux.die.net/man/2/fcntl
如果您能告诉链接器您需要哪个特定版本的 GLIBC 函数,那就太好了。但我发现的最接近的是这个技巧在另一个 post:
的回答中描述Answer to "Linking against older symbol version in a .so file"
这有点复杂。 fcntl
是可变的,没有 vffcntl
而需要 va_list。在这种情况下 you cannot forward an invocation of a variadic function。 :-(
当一个人拥有故意降低依赖性的稳定代码时,在当前 Ubuntu 上构建它是一种失望......然后让可执行文件拒绝 运行 在另一个 Ubuntu 仅在一年前(接近当天)发布。对此有什么办法?
What recourse does one have for this?
GLIBC 无法 #define USE_FCNTL_NOT_FCNTL64
这一事实说明了很多。无论是对是错,大多数 OS+ 工具链制造商似乎已经决定,将新系统的旧版本系统的二进制文件作为目标不是高优先级。
阻力最小的方法是让虚拟机围绕最旧的 OS+ 构建项目的工具链。每当您认为二进制文件在旧系统上 运行 时,就可以使用它来制作二进制文件。
但是...
- 如果您认为您的用法是在不受偏移大小更改影响的 fcntl() 调用的子集中(也就是说您不使用字节范围锁)
- 或者愿意审查您的代码以使用向后兼容的结构定义
- 并且不害怕巫术
...然后继续阅读。
The name is different, and fcntl is variadic without a vffcntl that takes a va_list. In such situations you cannot forward an invocation of a variadic function.
...然后要应用 the wrapping trick mentioned,您必须逐行查看 fcntl() 的接口文档,按原样解压可变参数,然后使用新的可变参数调用。
幸运的是,情况并没有那么困难(fcntl 采用 0 或 1 个具有记录类型的参数)。为了尝试为其他人省去一些麻烦,这里是代码。务必将 --wrap=fcntl64 传递给链接器(-Wl,--wrap=fcntl64 如果不直接调用 ld):
asm (".symver fcntl64, fcntl@GLIBC_2.2.5");
extern "C" int __wrap_fcntl64(int fd, int cmd, ...)
{
int result;
va_list va;
va_start(va, cmd);
switch (cmd) {
//
// File descriptor flags
//
case F_GETFD: goto takes_void;
case F_SETFD: goto takes_int;
// File status flags
//
case F_GETFL: goto takes_void;
case F_SETFL: goto takes_int;
// File byte range locking, not held across fork() or clone()
//
case F_SETLK: goto takes_flock_ptr_INCOMPATIBLE;
case F_SETLKW: goto takes_flock_ptr_INCOMPATIBLE;
case F_GETLK: goto takes_flock_ptr_INCOMPATIBLE;
// File byte range locking, held across fork()/clone() -- Not POSIX
//
case F_OFD_SETLK: goto takes_flock_ptr_INCOMPATIBLE;
case F_OFD_SETLKW: goto takes_flock_ptr_INCOMPATIBLE;
case F_OFD_GETLK: goto takes_flock_ptr_INCOMPATIBLE;
// Managing I/O availability signals
//
case F_GETOWN: goto takes_void;
case F_SETOWN: goto takes_int;
case F_GETOWN_EX: goto takes_f_owner_ex_ptr;
case F_SETOWN_EX: goto takes_f_owner_ex_ptr;
case F_GETSIG: goto takes_void;
case F_SETSIG: goto takes_int;
// Notified when process tries to open or truncate file (Linux 2.4+)
//
case F_SETLEASE: goto takes_int;
case F_GETLEASE: goto takes_void;
// File and directory change notification
//
case F_NOTIFY: goto takes_int;
// Changing pipe capacity (Linux 2.6.35+)
//
case F_SETPIPE_SZ: goto takes_int;
case F_GETPIPE_SZ: goto takes_void;
// File sealing (Linux 3.17+)
//
case F_ADD_SEALS: goto takes_int;
case F_GET_SEALS: goto takes_void;
// File read/write hints (Linux 4.13+)
//
case F_GET_RW_HINT: goto takes_uint64_t_ptr;
case F_SET_RW_HINT: goto takes_uint64_t_ptr;
case F_GET_FILE_RW_HINT: goto takes_uint64_t_ptr;
case F_SET_FILE_RW_HINT: goto takes_uint64_t_ptr;
default:
fprintf(stderr, "fcntl64 workaround got unknown F_XXX constant")
}
takes_void:
va_end(va);
return fcntl64(fd, cmd);
takes_int:
result = fcntl64(fd, cmd, va_arg(va, int));
va_end(va);
return result;
takes_flock_ptr_INCOMPATIBLE:
//
// !!! This is the breaking case: the size of the flock
// structure changed to accommodate larger files. If you
// need this, you'll have to define a compatibility struct
// with the older glibc and make your own entry point using it,
// then call fcntl64() with it directly (bear in mind that has
// been remapped to the old fcntl())
//
fprintf(stderr, "fcntl64 hack can't use glibc flock directly");
exit(1);
takes_f_owner_ex_ptr:
result = fcntl64(fd, cmd, va_arg(va, struct f_owner_ex*));
va_end(va);
return result;
takes_uint64_t_ptr:
result = fcntl64(fd, cmd, va_arg(va, uint64_t*));
va_end(va);
return result;
}
请注意,根据您实际构建的版本,如果这些部分不可用,您可能必须 #ifdef 将其中一些标记部分删除。
This affects quite a variety of applications...the manual page for fcntl() shows that it's the entry point for a small universe of sub-functions
...这可能应该给人们上一课:避免通过可变参数滥用创建这样的 "kitchen sink" 函数。
How to force linkage to older libc
fcntl
instead offcntl64
?
针对旧版本 libc
进行编译。期间.
因为glibc不是forward compatible, it is only backwards-compatible:
The GNU C Library is designed to be a backwards compatible, portable, and high performance ISO C library. It aims to follow all relevant standards including ISO C11, POSIX.1-2008, and IEEE 754-2008.
没有任何向前兼容性的保证,你不知道还有什么不能正常工作。