Bionicは、Googleが開発したAndroidオペレーティングシステム (OS) 用標準Cライブラリ実装である。このライブラリはGNU Cライブラリ (glibc) とは違い、一般的Linuxシステムよりも少ないメモリや低性能なプロセッサを搭載するデバイス向けに設計されている。Bionicは新規のコードと、BSDライセンスに基づきリリースされたFreeBSD、NetBSDおよびOpenBSDから取り込んだコードとを組み合わせたものである。静的リンクが一般的だったAndroid初期の時点においては、コードがglibcが利用するGNU Lesser General Public Licenseではなく、BSDライセンスに基づくことが重要であった。なぜならBionicは独自のアプリケーションバイナリインタフェース (ABI) を持っているため、既存のアプリを壊さないと標準CライブラリをBionicへ置き換えられないからである。
BionicはLinuxカーネルと共に使用するためのCライブラリであり、libc、、そしてlibmを提供する(libpthreadの機能はlibcの一部で、他のシステムのように独立したライブラリではない)。BSD libcとBionicはコードを共有するが、BSDカーネルを必要とする点がBionicと異なる。
当初の目標 Bionicについて公表された当初の目標を以下に示す:
BSDライセンス:Googleはプロプライエタリなユーザー空間とアプリによるエコシステムを作成するために、コピーレフトなライセンスをAndroidから排除することを望んでいた。しかし: AndroidのベースはLinuxカーネルだが、これはコピーレフトなライセンスであるGNU General Public License (GPL) バージョン2に従う。 最も普及しているLinuxカーネル用標準CライブラリはGNU Cライブラリ (glibc) だが、これもコピーレフトなライセンスのGNU Lesser General Public License (LGPL) に従う。LGPLはGPLに比べて明示的に動的リンクを許可してはいるが、ソースコードやリンク可能なオブジェクトファイルを提供しないプロプライエタリソフトウェアとの静的リンクは許可していない。 パーミッシブなBSDライセンスはコピーレフトなライセンスではないが、コピーレフトなライセンスと双方向で互換性がある。glibcのBSDライセンスによるは、コピーレフトであるコア(カーネル)とコピーレフトではないアプリとの間に存在する層の役目を果たすことが可能なため、GoogleはBionicをglibcの代替として選んだ。 小サイズ:BionicのサイズはGNU Cライブラリと比べ小さい。必要なメモリが少ないことは現在でも重要である。 速度:Bionicは比較的低いクロック周波数のCPU向けに設計された。
サポートするアーキテクチャ BionicはLinuxカーネルのみをサポートするが、現時点でサポートするアーキテクチャはarm、arm64、riscv64、x86、およびx86-64である。Androidプラットフォーム自体はMarshmallow以降、NEON搭載のarmv7が必要だが、Android Native Development Kit (NDK) はr16までarmv5(通称armeabi)のサポートを続けていた。armv7のサポートは続いているが、r24からNEONを搭載していないarmv7のサポートは中止された。Androidプラットフォームは歴史的に一部のSH-4をサポートしていたが、SH-4を搭載したデバイスが出荷されたことがないためこのサポートは削除された。NDKではSH-4のサポートは一切なく、MIPSおよびMIPS64のサポートはr17でNDKから削除された。
コンポーネント stdioなどlibcソースの部分のいくつかはBSD(主にOpenBSD)発祥であるが、pthread実装などそれ以外の部分はスクラッチで書かれた。
の実装は時代と共に変化していった。Lollipopより前は単一のネイティブメモリアロケータである、Doug Leaのdlmallocが存在し、LollipopからMarshmallowまではdlmallocとjemallocという2つの実装が存在した。jemallocはdlmallocより高いパフォーマンスを提供するが、代償としてブックキーピング用として余分なメモリが必要となる。ほとんどのデバイスがjemallocを利用したが、低メモリデバイスでは依然としてdlmallocを利用した。NougatからAndroid 10に掛けて、全てのデバイスがjemallocを利用するようになった。低メモリデバイスではjemallocの "svelte" 設定を利用してtcacheを無効にすることで、jemallocの速度をほとんど維持しつつもdlmallocの低メモリオーバーヘッドへうまく適合させられる。Android 11で、ほとんどのデバイス向けメモリアロケータはScudoに置き換えられた。これはjemallocの高パフォーマンスをある程度犠牲にして、セキュリティ強化機能を追加したものである。しかしながら、低メモリデバイスでは未だにjemallocの利用が許可されている。
Nexus 9など64ビットデバイスの中には、64ビットポインタの追加スペース要求と2つのZygote(Zygoteは全てのAndroidアプリプロセスの親となる、Androidシステムサービスのこと)のホスティングにより、実質的に低メモリデバイスとなるものもある。
libmソースは主にFreeBSD発祥であるが、様々なSoCベンダーからの寄付による最適化されたアセンブラが搭載されている。
動的リンカ(およびlibdl)はスクラッチで書かれた。
Bionicにはlibthread_db(により使用される)が含まれていないが、NDKには含まれていた。Androidプラットフォームには静的リンクされたgdbserverが含まれていたため、開発者は古いデバイスでも最新のgdbを利用できた。ただしAndroidはgdbサポートを中止しLLDBに切り替えたため、libthread_dbとAndroidの関係は最早ない。
Androidにおいてlibpthread、libresolv、およびlibrtは独立しておらず、それらは全てlibc内の機能である。libpthreadについては、サードパーティー製コードの最初の命令の実行前から既にアプリはマルチスレッド環境内に存在するため、シングルスレッド状態向けに最適化が試みられることは一切ない。
Androidプラットフォームは標準C++ライブラリとしてlibc++を使用する(Lollipopを含むリリースまではstlportを使用)。NDKでは歴史的にstlportとGNU libstdc++を提供していたが、NDK r18よりそれらは削除された。Androidアプリ内でネイティブコードがC++を使用する場合、全てのC++が同じSTLを使用する必要があることに注意すること。AndroidはオペレーティングシステムがSTLを提供しないので、アプリ一つ一つにSTLをバンドルする必要がある。
POSIXとの相違点 BionicはC11とPOSIXの全てを実装することを目標としているが、(Android 15時点において)libcのPOSIX関数のうち約11個が未だに存在しない 。endpwent/getpwent/setpwentファミリーなどのPOSIX関数は存在こそしているが、passwdデータベースが欠落しているためAndroidには適用できない。libm実装はOreo時点で完了した。
セキュリティ上の理由から、POSIXや標準Cライブラリの実装へ意図的に従わない関数も存在する。例えばprintfでは%n書式指定文字列をサポートしていない。
Bionicには最も利用されるGNU拡張はほとんど実装されており、同様に様々なBSD拡張も実装されている。
NDKとの関係 AndroidプラットフォームのコードはBionicを直接使用するが、サードパーティーの開発者はAndroid Native Development Kit (NDK) を使用する。サードパーティーの開発者の大半は未だに古いOSを対象としているため、Bionicには多くの機能が欠落していると思い込んでしまっている。Gingerbreadはlibcから803個の関数がエクスポートされたが、Oreoでは(1.6倍増の)1278個の関数がエクスポートされた。
歴史的にNDKとAndroidプラットフォームは分離されていたが、NDK r11以降、NDKフォークはその時点で最新のAndroidプラットフォーム相当なものへ置き換えられている。この作業は当初、GCCやClangコンパイラに焦点が当てられていた。
「統一された」ヘッダがオプトインを原則として最初に提供されたNDK r14以前、NDKが持つフォークされたプラットフォームヘッダのコピーはAPIレベルが異なっていた。これはNDKユーザーのほとんどがヘッダファイルのみの修正(例えば定数や構造体の定義の修正など)を利用できないことを意味していた。なぜなら彼らは古いAPIレベルを対象としているのに、プラットフォームの修正はその時点で最新のプラットフォーム向けヘッダにしか入っていないからである。Oreo開発期間中にプラットフォームのヘッダへAPIレベル情報がアノテーションされたことで、全てのAPIレベルで同じヘッダ群が利用可能となり、開発者が対象とするAPIレベルで利用可能な関数のみが表示されるようになった。これらがいわゆる「統一された」ヘッダで、NDK r15以降ではデフォルトとなっている。
NDK r16より前、NDKはlibc++を使うコードにlibandroid_support.aと呼ばれるライブラリをリンクしていた。これは古いOSリリースには存在しないがlibc++には必要な関数を提供するためである。libandroid_support.aがリンクされたコードはプラットフォームで使われるコードと同じではないため、多くのバグ(libc++を使用する任意のコードでprintfファミリの位置引数が壊れるなど)をもたらすこととなった。NDK r16からr25までlibandroid_support.aはまだ存在していたが、NDKのビルド時点で最新のプラットフォームのソースから直接ビルドされるようになった。NDK r26以降よりlibandroid_support.aは存在しなくなったが、これはNDKによりサポートされているOSバージョン全てで、libc++が必要な全てのものが含まれるようになったためである。
ソースの要塞化 Android Jelly Bean MR1 (4.2) 以降より、Bionicはglibcの_FORTIFY_SOURCEと類似した機能をサポートする。これは安全でない文字列やメモリ関数(strcpy()、strcat()、およびmemcpy()など)にバッファオーバーランのチェックを含める機能である。このチェックはバッファサイズをコンパイル時に決められる場合はコンパイル時に、そうでない場合はランタイムで行われる。要塞化はlibcによるランタイムサポートに依存するので、古いAndroidリリースへの移植性には制限がある。プラットフォーム自体は_FORTIFY_SOURCEを有効にしてビルドされている。
歴史的に要塞化の欠点の1つは、それがGCCと密接に結び付いていたことであったため、ClangなどGCC以外のコンパイラのサポートがとても困難であった。このことはAndroidがデフォルトのコンパイラをClangに切り替えた際、Bionicの要塞化実装の有用性が大幅に低下することを意味していた。Android Oreo (8.0) において、Bionicの要塞化はClangを念頭に置いてオーバーホールされ、Clang上の要塞化はGCC上のそれとほぼ互角な体験を提供するようになった。このオーバーホールによりglibcを上回るチェックが追加されたため、未定義な振る舞いを引き起こすとは限らなくとも明らかに誤ったコードを検出するようになった。新しい実装はlibcのサポートを以前より必要としなくなったため、Clang独自の強化はOreo以前のAndroidバージョンを目標としたアプリで利用可能となった。
論争 Bionicの作成のため、GoogleはGPLv2でライセンスされたLinuxカーネルのヘッダファイルを使用した。GoogleはGPL排除のため、ヘッダファイルからGPLで保護された部分を取り除き、それらをGPLで保護されない「事物」にしたと主張したGoogle android and the linux headers - The Register (2011)Android: Sued by Microsoft, not by Linux "Microsoft launches new Android suit, Linus Torvalds' take on Linux kernel headers and Android" - ITworld (March 21, 2011)。Linux創設者であるリーナス・トーバルズはGoogleの行動は受け入れられると主張したが、のレイモンド・ニマー教授Infringement and disclosure risk in development on copyleft platforms on ipinfoblog.com by Raymond Nimmer (2011)などはGoogleによるGPLの解釈に異議を唱えている。
関連項目
musl Toybox
脚注
外部リンク
Developer home page for Android Android Native Development Kit (NDK) のホームページ Bionicのソース(公式リポジトリ) Bionic notes on eLinux.org