似乎GLIBC 2.28 (released August 2018)对fcntl进行了相当激进的更改。在<fcntl.h>
中将定义更改为不再是外部函数but a #define
to fcntl64。
结果是,如果您使用此glibc在系统上编译代码(如果它完全使用fcntl()),那么从2018年8月开始,生成的二进制文件将不会在系统上执行。这会影响很多应用程序。 .fcntl()的手册页显示,这是一小部分子功能的入口:
https://linux.die.net/man/2/fcntl
如果您可以告诉链接器所需的GLIBC函数的特定版本,那就太好了。但是我发现最接近的是在另一篇文章的答案中描述的这个技巧:
Answer to "Linking against older symbol version in a .so file"
这有点复杂。 fcntl
是可变参数,而没有接受va_list。在这种情况下,you cannot forward an invocation of a variadic function。 :-(
当一个程序具有稳定的代码且具有较低的依赖关系时,就很难在当前的Ubuntu上构建它了……然后让可执行文件拒绝在仅一年前(即一天)发布的另一个Ubuntu上运行。一个人有什么追索权?
最佳答案
一个人有什么追索权?
GLIBC没有办法#define USE_FCNTL_NOT_FCNTL64
的事实说明了很多。不管是对是错,大多数OS +工具链制造商似乎已经决定,从较新版本中针对较旧版本系统的二进制文件作为目标并非是当务之急。
阻力最小的途径是使虚拟机远离用于构建项目的最旧的OS +工具链。只要您认为二进制文件将在旧系统上运行,就可以使用该文件来生成二进制文件。
但...
如果您认为用法位于fcntl()调用的子集中,而该子集不受偏移量大小更改的影响(也就是说,您不使用字节范围锁)
或愿意审查偏移量情况的代码,以使用向后兼容的结构定义
并且不害怕伏都教
...然后继续阅读。
名称不同,并且fcntl是可变参数,而没有带va_list的vffcntl。在这种情况下,您将无法转发可变参数函数的调用。
...然后应用the wrapping trick mentioned,您必须逐行浏览fcntl()的接口文档,解压缩可变参数,然后使用新的可变参数调用来调用包装的版本。
幸运的是,情况并没有那么困难(fcntl使用文档类型的0或1个参数)。为了避免给其他人带来麻烦,请使用以下代码。确保将--wrap = fcntl64传递给链接器(-Wl,如果不直接调用ld则--wrap = fcntl64):
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()的手册页显示,这是一小部分子函数的切入点
...这可能对人们来说是一个教训:避免通过各种各样的滥用来创建这样的“厨房水槽”功能。