Как принудительно установить ссылку на более старую библиотеку `fcntl` вместо` fcntl64`?

Кажется, что GLIBC 2.28 (выпущена в августе 2018 г.) внесла довольно агрессивные изменения в fcntl. Определение было изменено в <fcntl.h>, чтобы больше не быть внешней функцией, но #define к fcntl64.

В результате, если вы скомпилируете свой код в системе с этим glibc - если он использует fcntl () вообще - полученный двоичный файл не будет выполняться в системе до августа 2018 г. Это влияет на довольно много приложений ... страница руководства для fcntl () показывает, что это точка входа для небольшого набора подфункций:

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

Было бы неплохо, если бы вы могли сообщить компоновщику, какую конкретную версию функции GLIBC вы хотите. Но ближе всего я нашел этот трюк, описанный в ответе на другой пост:

Ответ на «Связывание со старой версией символа в файле .so»

Это немного сложнее. fcntl является вариативным без vffcntl, который принимает va_list. В таких ситуациях вы не можете перенаправить вызов вариативной функции . :-(

Когда у кого-то есть стабильный код с целенаправленно низкими зависимостями, неудобно создавать его на текущем Ubuntu ... тогда и исполняемый файл отказывается запускаться на другом Ubuntu, выпущенном всего за год до этого (почти в день). Что для этого есть?


person HostileFork says dont trust SE    schedule 20.10.2019    source источник


Ответы (2)


Что для этого есть?

Тот факт, что у GLIBC не было возможности #define USE_FCNTL_NOT_FCNTL64, говорит о многом. Правильно это или нет, но большинство производителей наборов инструментов OS +, похоже, решили, что нацеливание на бинарные файлы для старых версий своих систем из более новых не является приоритетом.

Путь наименьшего сопротивления состоит в том, чтобы поддерживать виртуальную машину вокруг самой старой инструментальной цепочки OS +, которая создает ваш проект. Используйте это для создания двоичных файлов всякий раз, когда вы думаете, что двоичный файл будет запущен в старой системе.

Но...

  • Если вы считаете, что ваше использование относится к подмножеству вызовов fcntl (), на которые не влияет изменение размера смещения (то есть вы не используете блокировки диапазона байтов)
  • ИЛИ готовы проверить ваш код на предмет смещения, чтобы использовать определение структуры с обратной совместимостью
  • И не боятся вуду

... тогда продолжайте читать.

Имя другое, и fcntl является вариативным без vffcntl, который принимает va_list. В таких ситуациях вы не можете пересылать вызов вариативной функции.

... затем, чтобы применить упомянутый трюк с упаковкой, вам нужно построчно пройти через интерфейс 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 () показывает, что это точка входа для небольшого набора подфункций.

... и это, вероятно, должно стать уроком для людей: избегайте создания таких функций "кухонной мойки" путем разнообразного злоупотребления.

person HostileFork says dont trust SE    schedule 20.10.2019
comment
Для тех, кто может захотеть обсудить, что послужило мотивом для этого сообщения ... это дискуссионная ветка о роли двоичной переносимости в современном мире - person HostileFork says dont trust SE; 20.10.2019
comment
И что бы это сломалось? Отказавшись от использования fcntl64(), появятся ли ошибки при доступе к файлам размером более 2 ГБ? Единственный способ узнать это - провести полный регрессионный тест всех fcntl() использований. держите виртуальную машину рядом с самой старой ОС + инструментальная цепочка, которая строит ваш проект ЭТО - настоящий ответ, и IMO должна быть впереди. - person Andrew Henle; 20.10.2019
comment
нацеливание двоичных файлов для более старых версий их систем на более новые не является приоритетом. - это не высокий приоритет, это явная не цель. - person Employed Russian; 20.10.2019
comment
@HostileFork Ожидание того, что что-то построенное в более новой версии ОС, библиотеки или даже версии ЦП будет работать на чем-то более старом, в корне ошибочно. Ничто не может гарантировать прямую совместимость. В будущем не появится ничего, что не работало бы с этой библиотекой / в этой ОС? Никто не может дать такое обещание, и на это нельзя полагаться. - person Andrew Henle; 21.10.2019
comment
@AndrewHenle В будущем не появится ничего, что не работало бы с этой библиотекой / в этой ОС? Никто не может дать такое обещание, и вы не можете на это полагаться. = ›Это явно неверно - в том смысле, что если можно использовать старую версию ОС / инструментальной цепочки для результата, новые версии инструментальной цепочки может иметь переключатель для получения этого результата. Не так давно считалось неприемлемым выпуск компилятора, который не может создавать двоичные файлы, которые можно было бы запускать в системе, которая считалась последней еще накануне. Я вижу, что этот корабль, кажется, проплыл здесь для многих. - person HostileFork says dont trust SE; 21.10.2019
comment
(продолжение) [Установка] [установка] старой ОС - гораздо менее сложный процесс, чем регрессионное тестирование, которое вы не смогли выполнить. Для одной простой функции регрессионное тестирование, необходимое для того, чтобы убедиться, что вы ничего не сломали, - это больше, чем просто сделать все правильно, построив более старую ОС. - person Andrew Henle; 21.10.2019
comment
Вместо того, чтобы объявить семейство стаи несовместимым, не могли бы вы выделить в стеке структуру стаи старого стиля, преобразовать ее, передать на нее указатель, а затем преобразовать / скопировать обратно в переданный указатель на выходе? - person Gavin S. Yancey; 22.06.2020

Как принудительно установить ссылку на более старую libc fcntl вместо fcntl64?

Скомпилировать с более старой версией libc. Период.

Поскольку glibc не прямой совместимости, это только обратно совместимый:

Библиотека GNU C разработана как обратно совместимая, переносимая и высокопроизводительная библиотека ISO C. Он нацелен на соблюдение всех соответствующих стандартов, включая ISO C11, POSIX.1-2008 и IEEE 754-2008.

Без каких-либо гарантий прямой совместимости вы не знаете, что еще не будет работать должным образом.

person Andrew Henle    schedule 20.10.2019
comment
Чтобы добавить к этому, если возможно, просто используйте обычный процесс сборки пакета, обычно шаги по сборке пакета в большинстве дистрибутивов включают установку версии библиотек, используемых в этом дистрибутиве, таким образом, если ваш процесс сборки запускает тесты, вы можете обнаружить любые проблемы с ним на раннем этапе - person XANi; 20.10.2019