Làm thế nào để buộc liên kết đến libc cũ hơn `fcntl` thay vì` fcntl64`?


8

Có vẻ như GLIBC 2.28 (phát hành tháng 8 năm 2018) đã thực hiện một thay đổi khá tích cực đối với fcntl. Định nghĩa đã được thay đổi <fcntl.h>thành không còn là một chức năng bên ngoài, mà là một #definefcntl64 .

Kết quả cuối cùng là nếu bạn biên dịch mã của bạn trên một hệ thống với glibc này - nếu nó sử dụng fcntl () ở tất cả các --Thư nhị phân kết quả sẽ không thực hiện trên một hệ thống từ trước tháng Tám năm 2018. Điều này ảnh hưởng khá nhiều ứng dụng .. . trang hướng dẫn sử dụng cho fcntl () cho thấy đó là điểm vào của một vũ trụ nhỏ của các hàm phụ:

https://linux.die.net/man/2/fcntl

Sẽ thật tuyệt nếu bạn có thể nói cho người liên kết biết phiên bản cụ thể của chức năng GLIBC mà bạn muốn. Nhưng gần nhất tôi tìm thấy là thủ thuật này được mô tả trong một câu trả lời cho một bài viết khác:

Trả lời "Liên kết với phiên bản biểu tượng cũ hơn trong tệp .so"

Điều này phức tạp hơn một chút. fcntllà matrixdic mà không có vffcntlmà có một va_list. Trong những tình huống như vậy, bạn không thể chuyển tiếp một lời gọi của hàm matrixdic . :-(

Khi một người có mã ổn định với sự phụ thuộc thấp một cách có chủ đích, đó là sự buông thả để xây dựng nó trên Ubuntu hiện tại ... sau đó và có thể thực thi từ chối chạy trên một Ubuntu khác được phát hành chỉ một năm trước đó (gần đến ngày). Người ta có những gì đòi hỏi cho điều này?

Câu trả lời:


6

Người ta có những gì đòi hỏi cho điều này?

Thực tế là GLIBC không có cách nào để #define USE_FCNTL_NOT_FCNTL64nói nhiều. Dù đúng hay sai, hầu hết các nhà sản xuất chuỗi công cụ OS + dường như đã quyết định rằng việc nhắm mục tiêu nhị phân cho các phiên bản cũ hơn của hệ thống của họ từ phiên bản mới hơn không phải là ưu tiên cao.

Con đường ít kháng cự nhất là giữ một máy ảo xung quanh chuỗi công cụ OS + lâu đời nhất xây dựng dự án của bạn. Sử dụng điều đó để tạo nhị phân bất cứ khi nào bạn nghĩ rằng nhị phân sẽ được chạy trên một hệ thống cũ.

Nhưng...

  • Nếu bạn tin rằng tập quán của bạn nằm trong tập hợp con của các lệnh gọi fcntl () không bị ảnh hưởng bởi thay đổi kích thước bù (nghĩa là bạn không sử dụng khóa phạm vi byte)
  • HOẶC sẵn sàng kiểm tra mã của bạn cho các trường hợp bù để sử dụng định nghĩa cấu trúc tương thích ngược
  • VÀ không sợ voodoo

... Sau đó tiếp tục đọc.

Tên là khác nhau, và fcntl là matrixdic mà không có vffcntl có một va_list. Trong những tình huống như vậy, bạn không thể chuyển tiếp một lời gọi của hàm matrixdic.

... sau đó để áp dụng thủ thuật gói được đề cập , bạn phải truy cập từng dòng thông qua tài liệu giao diện của fcntl (), giải nén các từ khóa như nó sẽ, và sau đó gọi phiên bản được bao bọc bằng một lệnh gọi kiểu mới.

May mắn thay, đó không phải là một trường hợp khó khăn (fcntl có 0 hoặc 1 đối số với các loại tài liệu). Để thử cứu bất kỳ ai khác một số rắc rối, đây là mã cho điều đó. Hãy chắc chắn chuyển --wrap = fcntl64 cho trình liên kết ( -Wl, - quấn = fcntl64 nếu không gọi trực tiếp 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;
}

Lưu ý rằng tùy thuộc vào phiên bản bạn thực sự xây dựng, bạn có thể phải #ifdef một số phần cờ đó nếu chúng không khả dụng.

Điều này ảnh hưởng đến khá nhiều ứng dụng ... trang hướng dẫn cho fcntl () cho thấy đó là điểm khởi đầu cho một vũ trụ nhỏ của các chức năng phụ

... Và nó có lẽ nên là một bài học cho mọi người: tránh tạo ra các chức năng "bồn rửa nhà bếp" như vậy thông qua lạm dụng dao động.


Đối với những người có thể muốn thảo luận về những gì thúc đẩy bài đăng này ... đây là một chủ đề
Nghị

Và điều này sẽ phá vỡ những gì? Bằng cách bỏ việc sử dụng fcntl64(), bây giờ sẽ có lỗi được giới thiệu trong khi truy cập các tệp lớn hơn 2 GB? Cách duy nhất để biết là làm một bài kiểm tra hồi quy đầy đủ của tất cả các fcntl()sử dụng. giữ một máy ảo xung quanh chuỗi công cụ OS + lâu đời nhất xây dựng dự án của bạn THAT là câu trả lời thực sự và IMO sẽ được đưa lên phía trước.
Andrew Henle


1
"nhắm mục tiêu nhị phân cho các phiên bản cũ hơn của hệ thống của họ từ phiên bản mới hơn không phải là ưu tiên cao." - đó không phải là "ưu tiên không cao", đó là một mục tiêu phi thường .
Sử dụng tiếng Nga

1
@AndrewHenle "Không có gì sẽ xuất hiện trong tương lai sẽ không chạy với thư viện này / trên hệ điều hành này"? Không ai có thể thực hiện lời hứa đó và bạn không thể dựa vào điều đó. " => Điều này hoàn toàn sai - theo nghĩa là nếu người ta có thể sử dụng phiên bản cũ hơn của OS / toolchain cho kết quả, phiên bản mới của chuỗi công cụ Cũng có thể có một công tắc để có được kết quả đó. Cách đây không lâu, nó đã được coi là không thể chấp nhận để phát hành một trình biên dịch không thể xây dựng các nhị phân chạy trên hệ thống được coi là "mới nhất" chỉ một ngày trước đó. Con tàu đó dường như đã ra khơi cho nhiều người ở đây.
HostileFork nói rằng không tin tưởng vào

2

Làm thế nào để buộc liên kết với libc cũ fcntlthay vì fcntl64?

Biên dịch với phiên bản cũ hơn libc. Giai đoạn = Stage.

Vì glibc không tương thích về phía trước , nó chỉ tương thích ngược :

Thư viện GNU C được thiết kế để trở thành thư viện ISO C tương thích ngược , di động và hiệu suất cao. Nó nhằm mục đích tuân theo tất cả các tiêu chuẩn có liên quan bao gồm ISO C11, POSIX.1-2008 và IEEE 754-2008.

Nếu không có bất kỳ đảm bảo nào về khả năng tương thích về phía trước, bạn không biết những gì khác sẽ không hoạt động đúng .


Thêm vào đó, nếu có thể, chỉ cần sử dụng quy trình xây dựng gói thông thường, thông thường các bước để xây dựng gói trong hầu hết các bản phân phối bao gồm phiên bản cài đặt của thư viện được sử dụng trong bản phân phối đó, theo cách đó nếu quá trình xây dựng của bạn chạy thử nghiệm, bạn có thể sớm gặp bất kỳ vấn đề nào với nó
XANi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.