macOS 上の Qt アプリケーションの動的リンク

本日、macOS および M1/M2/M3/M4 プロセッサ (Apple Silicon) を搭載した Apple デバイス用の RaidenVideoRipper のバージョンをリリースしました。 RaidenVideoRipper は、ビデオ ファイルの一部を新しいファイルに切り取ることができる迅速なビデオ編集アプリケーションです。 gif を作成したり、オーディオ トラックを mp3 にエクスポートしたりすることもできます。

次に、これを実現するために使用したコマンドについて簡単に説明します。ここで何が起こっているかの理論、ユーティリティのドキュメントは、次のリンクで読むことができます。
https://www.unix.com/man-page/osx/1/otool/
https://www.unix.com/man-page/osx/1/install_name_tool/
https://llvm.org/docs/CommandGuide/llvm-nm.html
https://linux.die.net/man/1/file
https://www.unix.com/man-page/osx/8/SPCTL/
https://linux.die.net/man/1/chmod
https://linux.die.net/man/1/ls
https://man7.org/linux/man-pages/man7/xattr.7.html
https://doc.qt.io/qt-6/macos-deployment.html

開始するには、macOS に Qt をインストールし、Qt デスクトップ開発用の環境もインストールします。この後、たとえば Qt Creator でプロジェクトをアセンブルし、アプリケーションをエンド ユーザーに配布するときに外部動的ライブラリとの依存関係が正しく処理されるようにするために必要なことについて説明します。

アプリケーションの YOUR_APP.app/Contents フォルダーに Frameworks ディレクトリを作成し、その中に外部依存関係を配置します。たとえば、RaidenVideoRipper アプリケーションのフレームワークは次のようになります。

Frameworks
├── DullahanFFmpeg.framework
│   ├── dullahan_ffmpeg.a
│   ├── libavcodec.60.dylib
│   ├── libavdevice.60.dylib
│   ├── libavfilter.9.dylib
│   ├── libavformat.60.dylib
│   ├── libavutil.58.dylib
│   ├── libpostproc.57.dylib
│   ├── libswresample.4.dylib
│   └── libswscale.7.dylib
├── QtCore.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtCore -> Versions/Current/QtCore
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtGui.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtGui -> Versions/Current/QtGui
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtMultimedia.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtMultimedia -> Versions/Current/QtMultimedia
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtMultimediaWidgets.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtMultimediaWidgets -> Versions/Current/QtMultimediaWidgets
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtNetwork.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtNetwork -> Versions/Current/QtNetwork
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
└── QtWidgets.framework
    ├── Headers -> Versions/Current/Headers
    ├── QtWidgets -> Versions/Current/QtWidgets
    ├── Resources -> Versions/Current/Resources
    └── Versions

簡単にするために、ネストの 2 番目のレベルのみを出力しました。

次に、アプリケーションの現在の動的依存関係を出力します。

otool -L RaidenVideoRipper 

RaidenVideoRipper.app/Contents/MacOS にある RaidenVideoRipper バイナリの出力:

RaidenVideoRipper:
	@rpath/DullahanFFmpeg.framework/dullahan_ffmpeg.a (compatibility version 0.0.0, current version 0.0.0)
	@rpath/QtMultimediaWidgets.framework/Versions/A/QtMultimediaWidgets (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtWidgets.framework/Versions/A/QtWidgets (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtMultimedia.framework/Versions/A/QtMultimedia (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtGui.framework/Versions/A/QtGui (compatibility version 6.0.0, current version 6.8.1)
	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 2575.20.19)
	/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 367.4.0)
	@rpath/QtNetwork.framework/Versions/A/QtNetwork (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtCore.framework/Versions/A/QtCore (compatibility version 6.0.0, current version 6.8.1)
	/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
	/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers (compatibility version 1.0.0, current version 709.0.0)
	/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1800.101.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)

Qt の RaidenVideoRipper と dulahan_ffmpeg の依存関係に見られるように。 Dullahan FFmpeg は FFmpeg のフォークであり、その機能を動的ライブラリにカプセル化し、C ルーチンを使用して現在の実行の進行状況とキャンセルを取得する機能を備えています。
次に、install_name_tool を使用して、アプリケーションと必要なすべてのライブラリのパスを置き換えます。

このためのコマンドは次のとおりです。

install_name_tool -change old_path new_path target

使用例:

install_name_tool -change /usr/local/lib/libavfilter.9.dylib @rpath/DullahanFFmpeg.framework/libavfilter.9.dylib dullahan_ffmpeg.a

正しいパスをすべて入力すると、アプリケーションが正しく起動するはずです。ライブラリへのすべてのパスが相対パスであることを確認し、バイナリを転送して、再度開きます。
エラーが表示された場合は、otool を使用してパスを確認し、install_name_tool を使用して再度変更します。

依存関係の混同によるエラーもあります。置き換えたライブラリにテーブル内にシンボルがない場合は、次のようにシンボルの有無を確認できます。

nm -gU path

実行すると、ライブラリまたはアプリケーションのシンボル テーブル全体が表示されます。
間違ったアーキテクチャの依存関係をコピーした可能性もあります。次のファイルを使用してこれを確認できます。

file path

ファイル ユーティリティは、ライブラリまたはアプリケーションがどのアーキテクチャに属しているかを示します。

Qt では、YOUR_APP.app ディレクトリの Content フォルダーに Plugins フォルダーも必要です。プラグインを Qt から Contents にコピーします。次に、アプリケーションの機能を確認します。その後、Plugins フォルダーの最適化、このフォルダーからの要素の削除、およびアプリケーションのテストを開始できます。

macOS セキュリティ

すべての依存関係をコピーし、動的リンクのパスを修正した後、開発者の署名でアプリケーションに署名し、さらに公証のためにアプリケーションのバージョンを Apple に送信する必要があります。

開発者ライセンスの 100 ドルを持っていない場合、または何も署名したくない場合は、アプリケーションの起動方法についてユーザーに指示を書きます。

この手順は RaidenVideoRipper でも機能します。

  • ゲートキーパーを無効にする: spctl –master-disable
  • プライバシーとセキュリティで任意のソースからの起動を許可: アプリケーションがどこからでも切り替えられるようにする
  • zip または dmg アプリケーションからダウンロードした後、隔離フラグを削除します: xattr -d com.apple.quarantine app.dmg
  • 検疫フラグ (com.apple.quarantine) が欠落していることを確認します: ls -l@ app.dmg
  • 必要に応じてアプリケーションを起動するための確認を「プライバシーとセキュリティ」に追加します

隔離フラグによるエラーは通常、ユーザーの画面に「アプリケーションが破損しています」というエラーが表示されて再現されます。この場合、メタデータから隔離フラグを削除する必要があります。

Apple Silicon 用の RaidenVideoRipper をビルドするためのリンク:
https://github.com/demensdeum/RaidenVideoRipper/releases/download/1.0.1.0/RaidenVideoRipper-1.0.1.0.dmg

Leave a Comment

Your email address will not be published. Required fields are marked *