web-dev-qa-db-ja.com

`fcntl64`の代わりに古いlibc` fcntl`へのリンクを強制する方法は?

GLIBC 2.28(2018年8月リリース) はfcntlにかなり積極的な変更を加えたようです。定義は<fcntl.h>で変更され、外部関数ではなくなりました ただし#defineからfcntl64

その結果、このglibcを使用するシステムでコードをコンパイルする場合、つまりfcntl()を使用する場合まったく-結果のバイナリは2018年8月より前のシステムで実行します。これは非常にさまざまなアプリケーションに影響します... fcntl()のマニュアルページは、サブ関数の小さなユニバースのエントリポイントであることを示しています。

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

必要なGLIBC関数の特定のバージョンをリンカーに伝えることができれば、すばらしいでしょう。しかし、私が見つけた最も近いものは、別の投稿への回答で説明されているこのトリックでした:

「。soファイル内の古いシンボルバージョンに対するリンク」への回答

これはもう少し複雑です。 fcntlは、va_listを取るvffcntlなしの可変個です。このような状況では 可変関数の呼び出しを転送することはできません 。 :-(

意図的に低い依存関係を持つ安定したコードがある場合、それを現在のUbuntuでビルドするのは失望です...そして、実行可能ファイルを1年前(ほぼ当日)にリリースされた別のUbuntuで実行することを拒否します。これにはどのような手段がありますか?

これにはどのような手段がありますか?

GLIBCが#define USE_FCNTL_NOT_FCNTL64はたくさん言います。正しいか間違っているかにかかわらず、ほとんどのOS +ツールチェーンメーカーは、システムの古いバージョンのバイナリを新しいバージョンからターゲットにすることは、高い優先度ではないと決定しているようです。

最も抵抗の少ない方法は、プロジェクトを構築する最も古いOS +ツールチェーンの周りに仮想マシンを置くことです。バイナリが古いシステムで実行されると思われる場合はいつでも、それを使用してバイナリを作成してください。

だが...

  • 使用がオフセットサイズの変更の影響を受けないfcntl()呼び出しのサブセットにあると確信している場合(つまり、バイト範囲ロックを使用していないことを意味します)
  • または、下位互換性のある構造体定義を使用するために、オフセットケースについてコードを精査します
  • そして、ブードゥー教を怖がっていません

...その後、読み続けます。

名前は異なり、fcntlはva_listをとるvffcntlを持たない可変長です。このような状況では、可変関数の呼び出しを転送できません。

...次に、適用します 前述のラッピングトリック 、fcntl()のインターフェースドキュメントを1行ずつ実行し、バリアントをそのように展開してから、ラップされたバージョンを次のように呼び出す必要があります。新しい可変の呼び出し。

幸い、それほど難しいことではありません(fcntlは、文書化された型で0または1の引数を取ります)。誰か他の人のトラブルを救うために、そのためのコードを次に示します。 ldを直接呼び出さない場合は、必ず-wrap = fcntl64をリンカー(-Wl、-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()のマニュアルページは、それがサブ関数の小さなユニバースのエントリポイントであることを示しています

...そしてそれはおそらく人々への教訓になるはずです:可変的虐待を通じてそのような「キッチンシンク」関数を作成することは避けてください。

fcntl64の代わりに古いlibc fcntlへのリンケージを強制する方法は?

古いバージョンのlibcに対してコンパイルします。限目。

Glibcは 前方互換 ではないため、 後方互換 のみです。

GNU Cライブラリは、後方互換性、ポータブル、および高性能ISO Cライブラリとなるように設計されています。 ISO C11、POSIX.1-2008、およびIEEE 754-2008を含むすべての関連規格に従うこと。

前方互換性の保証がない場合、他に何が正しく機能しないかわかりません

2
Andrew Henle