Bionicは、 GoogleがAndroidオペレーティングシステム向けに開発したC標準ライブラリの実装です。一般的なLinuxシステムよりもメモリとプロセッサ能力の低いデバイス向けに設計されている点で、 GNU Cライブラリ(glibc)とは異なります。Bionicは、 GNU Lesser General Public Licenseを使用するglibcとは異なり、BSDライセンスでリリースされたFreeBSD、NetBSD、OpenBSDのコードと新規コードを組み合わせたものです。この違いは、静的リンクが一般的だったAndroidの初期の頃には重要でした。また、Bionicは独自のアプリケーションバイナリインターフェースを持っているため、既存のすべてのアプリを壊さずに別のlibcに置き換えることはできません。
BionicはLinuxカーネルで使用するためのCライブラリであり、libc、libdl、libmを提供します(libpthread機能はlibcの一部であり、他のシステムのように独立したライブラリではありません)。これは、Bionicがコードを共有するBSD Cライブラリとは異なります。BSD CライブラリはBSDカーネルを必要とするためです。
Bionicの当初の公に表明された目標は以下のとおりでした。[ 1 ] [ 2 ]
Bionic は Linux カーネルのみをサポートしていますが、現在、arm、arm64、riscv64、x86、およびx86-64アーキテクチャをサポートしています。プラットフォーム自体はMarshmallow以降、 Neonを使用した armv7 を必要としていましたが、[ 5 ] Android Native Development Kit (NDK) は NDK r16 まで armv5 (armeabi と呼ばれていました) のサポートを継続していました。NDK は現在も armv7 をサポートしていますが、NDK r24 では Neon 以外のサポートが終了しました。歴史的には、プラットフォームで部分的なSH-4サポートがありましたが、デバイスが出荷されることはなく、その後サポートは削除されました。NDK は SH-4 をサポートしたことはなく、MIPSおよび MIPS64 のサポートは r17 で NDK から削除されました。
libcのソースコードの一部、例えばstdioなどはBSD系OS(主にOpenBSD )由来のものですが、 pthreadの実装など、他の部分はゼロから書き直されました。
動的メモリ割り当ての実装は、時間の経過とともに変化してきました。Lollipop より前は、 Doug Lea 氏のdlmallocという単一のネイティブメモリ割り当てがありました。Lollipop と Marshmallow では、dlmalloc とjemalloc の2 つの実装がありました。jemalloc は dlmalloc よりもはるかに高いパフォーマンスを提供しますが、管理に必要な追加メモリのコストがかかります。ほとんどのデバイスは jemalloc を使用しましたが、メモリの少ないデバイスは依然として dlmalloc を使用していました。NougatからAndroid 10までは、すべてのデバイスが jemalloc を使用しています。メモリの少ないデバイスは、tcache を無効にする jemalloc の「svelte」構成を使用して、dlmalloc の低いメモリオーバーヘッドにほぼ一致させながら、jemalloc の速度のほとんどを維持します。Android 11では、ほとんどのデバイスのメモリ割り当てが切り替えられました。Scudoは、jemalloc の高いパフォーマンスの一部を犠牲にして、セキュリティ強化機能を追加しています。 [ 6 ]ただし、メモリ容量の少ないデバイスでも jemalloc を使用することは可能です。 [ 7 ]
Nexus 9のような一部の64 ビットデバイスは、64 ビット ポインタの追加スペース要件と 2 つの zygote のホスティングのため、実質的にメモリ容量の少ないデバイスとなっています。(Zygote は、すべての Android アプリケーション プロセスの親となる Android システム サービスです。[ 8 ])
libmのソースコードは大部分がFreeBSDのものですが、様々なSoCベンダーによって最適化されたアセンブラコードが提供されています。
ダイナミックリンカー(およびlibdl)はゼロから書き直されました。
Bionicにはlibthread_db(gdbserverで使用されるライブラリ)は含まれていませんが、NDKには含まれていました。Androidプラットフォームには静的にリンクされたgdbserverが含まれていたため、開発者は古いデバイスでも最新のgdbを使用することができました。Androidがgdbのサポートを終了し、lldbに切り替えたため、これはもはや関係ありません。
Androidにはlibpthread、libresolv、librtといった独立したライブラリは存在せず、これらの機能はすべてlibcに統合されています。libpthreadに関しては、サードパーティ製コードの最初の命令が実行される前からアプリはマルチスレッド環境にあるため、シングルスレッドの場合に最適化する試みは行われていません。
Android プラットフォームは C++ 標準ライブラリとして libc++ を使用します (Lollipop 以前のリリースでは stlport が使用されていました)。NDK は従来 stlport と GNU libstdc++ を提供していましたが、NDK r18 以降は削除されました。[ 9 ] Android アプリのネイティブ コードで C++ を使用する場合は、すべての C++ で同じSTL を使用する必要があることに注意してください。STL は Android OS では提供されておらず、各アプリにバンドルする必要があります。
BionicはC11とPOSIXのすべてを実装することを目指していますが、(Android 15の時点で)libcにはPOSIX関数が約11個欠けています[ 10 ] 。また、 passwdデータベースがないためAndroidには適用できないendpwent/getpwent/setpwentファミリーなどのPOSIX関数もあります。Oreoの時点ではlibmは完成しています。
セキュリティ上の理由から、意図的にPOSIXやC規格に準拠しない関数もいくつかあります。例えば、フォーマット文字列をサポートしていないprintf%nなどです。[ 11 ]
最もよく使われるGNU拡張機能の多くはBionicに実装されており、様々なBSD拡張機能も同様である。
プラットフォームコードはBionicを直接使用しますが、サードパーティ開発者はAndroid Native Development Kit(NDK)を使用します。多くのサードパーティ開発者は依然として古いOSリリースをターゲットにしているため、Bionicには多くの機能が欠けているという認識が広まっています。Gingerbreadはlibcから803個の関数をエクスポートしていましたが、Oreoは1278個(1.6倍の増加)をエクスポートしています。[ 10 ]
歴史的に見て、NDKとプラットフォームは分岐していましたが、NDK r11以降では、NDKのフォークは現在のプラットフォームの同等のものに置き換えられています。この作業は当初、GCCとClangコンパイラに焦点を当てていました。
NDK r14 より前、オプトイン方式で「統合」ヘッダーが初めて提供されるまで、NDK は異なる API レベル用にプラットフォーム ヘッダーのフォークコピーを作成していました。そのため、ヘッダーのみの修正 (定数や構造体の定義の修正など) は、古い API レベルをターゲットとしているため、ほとんどの NDK ユーザーには利用できませんでしたが、プラットフォームの修正は最新のプラットフォーム ヘッダーにのみ適用されていました。Oreo の開発期間中に、プラットフォーム ヘッダーに API レベルの情報が注釈として付加されたため、すべての API レベルで同じヘッダーセットを使用でき、開発者がターゲットとする API レベルで利用可能な関数のみが表示されるようになりました。これが、いわゆる「統合」ヘッダーであり、NDK r15 以降はデフォルトとなっています。
NDK r16 より前は、古い OS リリースには含まれていなかった libc++ に必要な関数を提供するために、libandroid_support.a というライブラリが libc++ を使用するコードにリンクされていました。これはプラットフォームで使用されているコードとは異なり、多くのバグ(libc++ を使用するコードで printf ファミリーの位置引数が壊れるなど)を引き起こしていました。NDK r16 から r25 までは、libandroid_support.a は存在していましたが、各 NDK がビルドされた時点での最新のプラットフォームソースから直接ビルドされていました。NDK r26 以降は、NDK でサポートされているすべての OS バージョンに libc++ に必要なすべての機能が含まれているため、libandroid_support.a は削除されました。
Android Jelly Bean MR1 (4.2)以降、Bionic は glibc の と同様の機能をサポートしています_FORTIFY_SOURCE。[ 12 ]これは、安全でない文字列関数やメモリ関数( strcpy()、strcat()、 などmemcpy()) にバッファオーバーランのチェックが含まれる機能です。これらのチェックは、コンパイル時にバッファサイズが判明する場合はコンパイル時に実行され、そうでない場合は実行時に実行されます。fortify は libc のランタイムサポートに依存しているため、古い Android リリースへの移植性は限られています。[ 13 ]プラットフォーム自体は を_FORTIFY_SOURCE有効にして構築されています。
歴史的に、fortify の欠点の 1 つは、GCC と密接に結びついているため、Clang などの他のコンパイラで適切にサポートすることが非常に難しいことでした。そのため、Android がデフォルトのコンパイラとして Clang に切り替えたとき、[ 14 ] Bionic の fortify 実装は大幅に使いにくくなりました。Android Oreo (8.0) では、Clang を念頭に置いて Bionic の fortify が見直され[ 15 ]、Clang 上の fortify は GCC 上の fortify と同等のエクスペリエンスを提供するようになりました。この見直し以降、必ずしも未定義の動作を引き起こすわけではないものの、明らかに間違っているコードを検出するために、glibc のチェックに加えていくつかのチェックが追加されました。この新しい実装は以前のものよりも libc のサポートを必要としないため、Clang 固有の機能強化は Oreo より前のバージョンの Android をターゲットとするアプリケーションで使用できます。
Bionic の作成にあたり、Google は GPLv2 ライセンスの Linux カーネルヘッダー ファイルを使用しました。GPL を排除するために、Google はヘッダー ファイルから著作権で保護される可能性のある著作物をすべて削除し、著作権で保護されない「事実」に減らしたと主張しました。[ 16 ] [ 17 ] Linux の開発者であるLinus Torvalds はGoogle の行動を容認できると考えていましたが、[ 17 ] Google の GPL の解釈は、例えばヒューストン大学ロースクールの法学教授である Raymond Nimmer によって異議を唱えられています。[ 18 ]
「Bionic」という名前は、BSD と Linux の両方の要素から来ています。ソースコードは、BSD C ライブラリの要素と、スレッド、プロセス、シグナル、その他いくつかのものを扱うために使用されるカスタムの Linux 固有の要素が混在しています。
ライセンス: ユーザー空間からGPLを排除したい