chromium-widevineが読み込まれるまで

## 概要

普段chromiumを利用してWebサーフィンをしているのですが、DRMにデフォルトで対応してないことに気づき、chromium-widevineなるものをインストールしました。
よくみると、ただlibwidevinecdm.soを配置しているだけでした。
chromiumがどのようにlibwidevinecdm.soを認識して、どのように利用しているのか気になり、追ってみた記事になります。

## 環境

- OS
    - Linux arch 5.2.5-arch1-1-ARCH #1 SMP PREEMPT Wed Jul 31 08:30:34 UTC 2019 x86_64 GNU/Linux
- chromium
    - Version 76.0.3809.87 (Official Build) Arch Linux (64-bit)

## chromium-widevineのPKGBUILD

自分の環境はArchLinuxなのですが、以下のchromium-widevineをインストールすることで、chromiumでもDRMコンテンツを利用することができます。
https://aur.archlinux.org/packages/chromium-widevine/

ソースコードは、

```console
$ git clone https://aur.archlinux.org/chromium-widevine.git
```
でcloneできます。

このレポジトリでは、PKGBUILDというarchlinuxのパッケージを定義するファイルが管理されています。
PKGBUILDから重要なところだけ抜き出してみました。

```sh:PKGBUILD
source=("https://dl.google.com/linux/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${_chrome_ver}-1_amd64.deb")
# ~~~~~
prepare() {
  bsdtar -x --strip-components 4 -f data.tar.xz opt/google/chrome/libwidevinecdm.so
}
# ~~~~~
package() {
  install -Dm644 libwidevinecdm.so -t "$pkgdir/usr/lib/chromium/"
}
```

### source

PKGBUILDでsourceを指定すると、対象のURLからファイルをダウンロードしてくるようです。
このパッケージでは、chromeをdeb形式でダウンロードしています。
よくみるとamd64で固定したバイナリなので、ARM環境などでは動かないような気がします。

### prepare

prepareはbuildの前に実行される関数のようです。（ただし、このパッケージではbuildは行っていません）
この実装では、data.tar.xzからlibwidevinecdm.soだけを取り出しているようです。
data.tar.xzは、

```
$ ar -x google-chrome-stable_${_chrome_ver}-1_amd64.deb
```

を実行してdebパッケージから取り出したファイルになります。
（このあたりの記述が見つからず、どのタイミングで行われているのかいまひとつわかりませんでした。）

### package

最後にchromeパッケージからlibwidevinecdm.soなるものをchromiumのディレクトリに配置していることがわかります。

## libwidevinecdm.soがどのようにchromiumに読み込まれるか？

soファイルなので、lddコマンドでchromiumを調べてみます。

```console
$ ldd /usr/lib/chromium/chromium | grep -c widevine
0
```

とくにリンクされているわけではなさそうでした。
そもそもlibwidevinecdm.soがなくてもchromiumが動作することを考えると、当然の結果と言えそうです。
では、どのように呼び出しているのでしょうか？

### widevine_cdm_component_installer.cc

https://chromium.googlesource.com/chromium/src/+/8602f880e210a87b8fdb5591a7e40449e664e321/chrome/browser/component_updater/widevine_cdm_component_installer.cc#394

をよむと、

```cpp
base::GetNativeLibraryName(kWidevineCdmLibraryName)
```

のような `kWidevineCdmLibraryName` という変数を元にlibraryを探し出そうとしている記述があります。
`kWidevineCdmLibraryName` はどこで定義されているでしょうか？

### widevine_cdm_common.h

widevine_cdm_component_installer.cc の依存を追っていくと

```cpp
#include "third_party/widevine/cdm/widevine_cdm_common.h"
```

と、`widevine_cdm_common.h` なるものがincludeされていました。

https://chromium.googlesource.com/chromium/src/+/315def7cce5076007e500674ec475e785c180566/third_party/widevine/cdm/widevine_cdm_common.h#36

```cpp
const char kWidevineCdmLibraryName[] = "widevinecdm";
```

と `kWidevineCdmLibraryName` の定義も見つかりました。
あとは、 `GetNativeLibraryName` の実装が見つかれば、 `libwidevinecdm.so` というファイル名が完成しそうです。

### native_library_posix.cc

`base::GetNativeLibraryName` は、OSによって実装が異なっているようです。

https://chromium.googlesource.com/chromium/chromium/+/01cb1298f5ec787880bda4748f30d3b6739bcfc7/base/native_library_posix.cc#50

linuxの場合は、 `native_library_posix.cc` というファイルが該当するようです。実装は

```cpp
return ASCIIToUTF16("lib") + name + ASCIIToUTF16(".so");
```

となっていて、 `libwidevinecdm.so` という名前ができることが確認できました。

## まとめ

なぜchromiumにwidevineが同梱していないのだろう？という疑問から調べてみたのですが、chromiumのソースコードに少し触れることができて非常に面白かったです。
widevineみたいに後から呼び出すlibraryって他にもありそうですが、 `GetNativeLibraryName` を利用しているコードはあまりなさそうです。（少なくともchrome配下にはwidevine関連しかありませんでした）

```console
$ git grep -c GetNativeLibraryName 
base/immediate_crash_unittest.cc:2
base/native_library.h:2
base/native_library_fuchsia.cc:2
base/native_library_ios.mm:2
base/native_library_mac.mm:1
base/native_library_posix.cc:2
base/native_library_unittest.cc:2
base/native_library_win.cc:2
base/profiler/stack_sampling_profiler_unittest.cc:1
base/scoped_native_library_unittest.cc:1
chrome/browser/component_updater/widevine_cdm_component_installer.cc:2
chrome/browser/load_library_perf_test.cc:1
chrome/common/chrome_paths.cc:1
chromeos/services/ime/decoder/decoder_engine.cc:1
net/http/http_auth_gssapi_posix_unittest.cc:2
net/socket/udp_socket_posix.cc:2
```

そういう意味でDRM周りは少し特殊なのかもしれません。
今回は実際にlibwidevinecdmの関数をどんなふうに呼び出すかまでは終えなかったので、機会があればやってみようと思います。