カーネルトレーシング用の[[eBPF|BPF]]プログラムは、トレース対象のカーネルの構造体や関数の定義を参照するため、コンパイル時にそれらの宣言を含むカーネルヘッダが必要となる。しかし、コンパイルを実行するホストのカーネルと、BPFアプリケーションの配布先のホストのカーネルのバージョンが異なる場合、宣言と実際の定義が矛盾する。強引にBPFプログラムを実行すると、構造体のフィールド変数のオフセットがカーネルバージョン間で異なれば、BPFプログラムは見当違いの値を読み出すことになる。
BCCを含むこれまでのBPFアプリケーションは、同一ホスト上でBPFプログラムをコンパイルしたのちに実行している。((Nakryikoのスライドでは、この性質を'"On the fly" compilation'と呼んでいる。))そのため、BPFアプリケーションの配布先にコンパイルに必要なパッケージ(clang+LLVM、Linuxカーネルヘッダ)をインストールするか、または、BPFアプリケーションの配布物にこれらのコンパイル用パッケージを同梱する必要がある。これが配布物のサイズの肥大化を招く。また、ホスト上でBPFアプリケーションを起動するときに、コンパイルによる一時的なCPU・メモリ負荷が発生する。多数のホストに対して、常駐でトレーシングする場合、これらのオーバヘッドを無視できなくなることがある。
このような移植性の課題を解決するためには、一度コンパイルされたBPFバイトコードを、再コンパイルすることなく様々な配布先ホストに複製して配置するのみで動作させる必要がある。
CO-REは、コンパイル時に決定される構造体フィールド変数のオフセットなどの参照情報を、BPFプログラムの実行時にカーネルから正しい情報を照合・書き換える機構(再配置:relocation)である。CO-REの構成要素として、コンパイラ、BTF(BPF Type Format)、BPFローダー、カーネルの4つがある。コンパイラ(Clang)は、BPFバイトコードを含むELFオブジェクトファイルの再配置セクションに参照情報を記録する。カーネルは、C言語の構造体、関数、グローバル変数などの定義情報を軽量に表現可能なフォーマットBTF(BPF Type Format)を用いて再配置情報を提供する。フロントエンドプログラムのビルド時に組み込まれるBPFローダー(libbpf)は、フロントエンドプログラムの実行時に、オブジェクトファイルの参照情報を取り出し、実行中のカーネルから提供されるBTF情報と照合し、オフセットやその他の再配置可能な情報を更新する。これらの要素技術の組み合わせにより、実行中のカーネルに適合するように調整されたBPFプログラムを得ることができる。
CO-REをサポートしたアプリケーションを動作させるには、BTFをビルトインでサポートしたカーネル、または、パッケージのインストールとカーネルの設定変更と再ビルドが必要となる。BTFがビルトインされたカーネルは、Ubuntuであれば、Ubuntu 20.10以降でサポートされる。執筆時点の最新のLTSバージョンは、そのままではBTFをサポートしないため注意する必要がある。[libbpfのREADME](https://github.com/libbpf/libbpf#bpf-co-re-compile-once--run-everywhere)により詳細な情報が記載されている。
CO-REのより深い技術詳細を知るには、Nakryikoによる次の記事をすすめる。
[BPF CO-RE (Compile Once – Run Everywhere)](https://nakryiko.com/posts/bpf-portability-and-co-re/)
また、実際にオフセットが書き換えられている要素が、nttlabs [@brewaddict](https://twitter.com/brewaddict)さんの次の記事に書かれている。 [純粋なRustへの愛を貫くため、libbpfを捨て、RustだけでeBPFを動かしたい。 | nttlabs](https://medium.com/nttlabs/bpf-co-re-618a765a110c)