c - 如何强制链接到旧的 libc `fcntl` 而不是 `fcntl64`?
问题描述
似乎GLIBC 2.28(2018 年 8 月发布)对 fcntl 进行了相当激进的更改。定义更改<fcntl.h>
为不再是外部函数,而是#define
fcntl64。
结果是,如果你在使用这个 glibc 的系统上编译你的代码——如果它完全使用 fcntl() ——生成的二进制文件将不会在 2018 年 8 月之前的系统上执行。这会影响相当多的应用程序。 .fcntl() 的手册页显示它是一小部分子功能的入口点:
https://linux.die.net/man/2/fcntl
如果您能告诉链接器您想要的 GLIBC 函数的特定版本,那就太好了。但我发现的最接近的是另一个帖子的答案中描述的这个技巧:
这有点复杂。 fcntl
是不vffcntl
带 va_list 的可变参数。在这种情况下,您不能转发可变参数函数的调用。:-(
当一个人拥有具有故意低依赖性的稳定代码时,在当前的 Ubuntu 上构建它是令人失望的......然后让可执行文件拒绝在仅一年前(几乎当天)发布的另一个 Ubuntu 上运行。对此有什么追索权?
解决方案
对此有什么追索权?
GLIBC 没有办法#define USE_FCNTL_NOT_FCNTL64
说明很多的事实。不管是对还是错,大多数操作系统+工具链制造商似乎都认为,从较新的系统中针对旧版本系统的二进制文件并不是一个高优先级。
阻力最小的路径是在构建项目的最古老的 OS+工具链周围保留一个虚拟机。每当您认为二进制文件将在旧系统上运行时,使用它来制作二进制文件。
但...
- 如果您认为您的用法属于 fcntl() 调用的子集,不受偏移大小更改的影响(也就是说您不使用字节范围锁)
- 或者愿意审查您的代码以使用向后兼容的结构定义
- 并且不怕伏都教
...然后继续阅读。
名称不同,fcntl 是可变参数,没有采用 va_list 的 vffcntl。在这种情况下,您不能转发可变参数函数的调用。
...然后要应用提到的包装技巧,您必须逐行浏览 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 将它们标记出来。
这影响了相当多的应用程序...... fcntl() 的手册页显示它是一小部分子功能的入口点
......这可能应该是给人们的一个教训:避免通过可变参数滥用创建这种“厨房水槽”功能。
推荐阅读
- abap - 将选定的行内容传递给维护生成器中的自定义按钮
- sony-audio-control-api - Sony Audio Control API - 无法选择 AFD 声场
- hyperledger-fabric - Hyperledger Composer:如何使用卡片调用composer REST服务
- jquery - 从第二次悬停在动态元素上出现的 Bootstrap 4 工具提示
- php - 尝试在空对象引用上调用接口方法 java.lang.string com.facebook.react.bridge.readablemap.get string
- c++ - GLSL 统一 ivec 未设置并且在被询问时具有默认(错误)值
- angular - ngFor 在自定义组件中
- android - Firebase 电话身份验证在 Android 中不起作用
- android - 如何使用 base64 加载带有毕加索的图像?
- java - 如何在 Spring AMQP 中解析 JSON 对象?