執筆者 : 箕浦 真
.note.ABI-tag?
今回はカーネルからは離れて番外編。前回記事を書くために、いくつかの実行ファイルでreadelfを実行した。気づいたのは、実行ファイルにある.note.ABI-tagセクション。
c8$ cat /etc/centos-release CentOS Linux release 8.2.2004 (Core) c8$ readelf -n /bin/cat Displaying notes found in: .note.gnu.property Owner Data size Description GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0 Properties: x86 feature: IBT, SHSTK Displaying notes found in: .note.ABI-tag Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 3.2.0 Displaying notes found in: .note.gnu.build-id Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 007217a03b8c26e9ea643d55aa19c19b120143b4 Displaying notes found in: .gnu.build.attributes Owner Data size Description GA$<version>3a1 0x00000010 OPEN Applies to region from 0x14a8 to 0x278f (以下略)
ub$ head -2 /etc/os-release NAME="Ubuntu" VERSION="18.04.5 LTS (Bionic Beaver)" ub$ readelf -n /bin/cat Displaying notes found in: .note.ABI-tag Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 3.2.0 Displaying notes found in: .note.gnu.build-id Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 747e524bc20d33ce25ed4aea108e3025e5c3b78f
Linux, ABI: 3.2.0という文字列からはわかりにくいが、CentOS 7環境などで見てみると、おや? という感じになる。
c7$ cat /etc/centos-release CentOS Linux release 7.8.2003 (Core) c7$ readelf -n /bin/cat Displaying notes found at file offset 0x00000254 with length 0x00000020: Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 2.6.32 Displaying notes found at file offset 0x00000274 with length 0x00000024: Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: b97cc8358edb123e8a9e21390f30924fe66a5e55
2.6.32って、Linuxのバージョンなんでは。ちなみに、CentOS 6では2.6.12、ubuntu 18.04と20.04では3.2.0になる。でも、2.6.12、2.6.32、3.2.0の間で何かABI面で大きな変更があった記憶はない。いくつかシステムコールの追加はあったと思うけど。システムコールの呼び出しに、機械語のint命令を使うか、syscall命令を使うか、sysenter命令を使うか、というような話はもっと昔だったと思うしなぁ。
という訳で、この.note.ABI-tagについて調べてみた。
ABI?
ABIはApplication Binary Interfaceの略だ。システムコールの呼び出し方、仮想メモリ空間の使い方、レジスタの使い方などが規定されていて、ELFヘッダにも出てくる。
c8$ readelf -h /bin/ls ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x5e00 Start of program headers: 64 (bytes into file) Start of section headers: 141384 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 10 Size of section headers: 64 (bytes) Number of section headers: 31 Section header string table index: 30
Linuxなのに、OS/ABI項は「UNIX - System V」。前回スルーしてしまったが、実はELFファイルではどのOSでもどういうわけか「UNIX - System V」だ。
n9$ uname; uname -r NetBSD 9.0 n9$ readelf -h /bin/ls ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x1510 Start of program headers: 64 (bytes into file) Start of section headers: 33504 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 8 Size of section headers: 64 (bytes) Number of section headers: 33 Section header string table index: 32 n9$ readelf -n /bin/ls Displaying notes found in: .note.netbsd.ident Owner Data size Description NetBSD 0x00000004 IDENT 900000000 (9.0.0) Displaying notes found in: .note.netbsd.pax Owner Data size Description NetBSD 0x00000004 PaX <>
s10$ uname ; uname -r SunOS 5.10 s10$ greadelf -h /bin/ls ELF Header: Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, big endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Sparc Version: 0x1 Entry point address: 0x10fc0 Start of program headers: 52 (bytes into file) Start of section headers: 26460 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 6 Size of section headers: 40 (bytes) Number of section headers: 23 Section header string table index: 21 s10$ /usr/sfw/bin/greadelf -n /bin/ls
ELF導入初期、私はNetBSDの開発に参加していたが、実行ファイルがNetBSDネイティブなのか、Linux向けなのか、FreeBSD向けなのか (NetBSDはLinuxやFreeBSDのコマンドを実行するバイナリエミュレーションの機能を持っている) 判別するのは、割とめんどくさいゾ、という議論があったのを記憶している。Linux向けかどうかを判定するのには、この.note.ABI-tagを利用しているようだ。ちなみに、ここでは.note.ABI-tagというセクション名は見ないで、中身で判断している。
だれか確認しているの?
Linuxもカーネルが確認しているのだろうか。
CentOS 6→7→8 (ということは、RHEL 6→7→8) と、このバージョンが上がっているということは、なんらかのチェックの目的で使われている可能性が高いと考えた。簡単なのは、「3.2.0」を謳うCentOS 8のバイナリを、2.6.32ベースのカーネルを使うCentOS 6環境に持っていくことだろう。とはいえ、あまり複雑なプログラムはlibcがうまくリンクできないかもしれない。Hello worldくらいなら動くだろうか。
c8$ cat test.c #include <stdio.h> int main(void) { printf("Hi, how are you?\n"); return 0; } c8$ make test cc test.c -o test c8$ readelf -n test Displaying notes found in: .note.ABI-tag Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 3.2.0 Displaying notes found in: .note.gnu.build-id Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 99d3ae3b669059d6c9b9b916522d4a742772dc93 Displaying notes found in: .gnu.build.attributes Owner Data size Description (以下略) c8$ scp test c6: test 100% 12KB 7.3MB/s 00:00
c6$ cat /etc/centos-release CentOS release 6.9 (Final) c6$ file test test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 3.2.0, not stripped c6$ ./test Hi, how are you? c6$ uname -r 2.6.32-696.1.1.el6.x86_64
お、fileコマンドでABI tagのようなものが出ている。しかし、何事もなく実行されてしまった! ムーン、誰もチェックしていないのか、それとも3.2.0とか2.6.32とかは、いかにもLinuxのバージョンに見えて、そうではないのか。謎は深まった。
「OS: Linux」の部分は見ているのだろうか。試しにNetBSDでtest.cをコンパイルして、これをCentOSで実行してみよう。NetBSDとCentOSとでは、共有ライブラリやダイナミックリンカのパスが違うので、実行するところまではいけないと思われる。そこで、スタティックリンクにしてみよう。
n9$ cc -static test.c -o test-netbsd n9$ scp test-netbsd c8:
c8$ ./test-netbsd Segmentation fault (core dumped)
実行されてしまった! 誤動作は当然だ。ちなみにこの逆にCentOSで作った (8ではスタティックリンクできないので、7で作って) hello world実行ファイルをNetBSDで実行すると、バイナリエミュレーションが働いて正常に実行される。
誰がつけているの?
このよくわからないABI tag、誰がつけているのかがわかれば、なんのためにつけられているのかわかるかもしれない。まず、どの段階でつくのだろうか。
c8$ rm -f test.o test c8$ make test.o cc -c -o test.o test.c c8$ make test cc test.o -o test c8$ readelf -n test.o c8$ readelf -n test Displaying notes found in: .note.ABI-tag Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 3.2.0 Displaying notes found in: .note.gnu.build-id Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 99d3ae3b669059d6c9b9b916522d4a742772dc93 Displaying notes found in: .gnu.build.attributes (以下略)
test.oにはないが、実行ファイルであるtestにはある。これはリンクフェーズでつくことを示すのだろう。
リンカはbinutilsの中に含まれているので、binutilsのソースコードを取り寄せてみよう。
c8$ dnf download --source binutils (中略) No package binutils-2.30-73.el8.src available. Exiting due to strict setting. Error: No package binutils-2.30-73.el8.src available.
がーん。仕方ないのでIIJのミラーから直接取り寄せよう。
c8$ wget ftp://ftp.iij.ad.jp/pub/linux/centos-vault/8.2.2004/BaseOS/Source/SPackages/binutils-2.30-73.el8.src.rpm (中略) c8$ rpm -ivh binutils-2.30-73.el8.src.rpm Updating / installing... (中略) c8$ rpmbuild -bp --nodeps ~/rpmbuild/SPECS/binutils.spec Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.Iqbqn1 (中略) + exit 0 c8$ cd rpmbuild/BUILD/binutils-2.30 c8$ grep -r ABI-tag . ./ld/emulparams/shlelf_linux.sh: .note.ABI-tag ${RELOCATING-0} : { *(.note.ABI-tag) }" ./ld/testsuite/ld-gc/abi-note.d:.* .note.ABI-tag[ ]+NOTE.* ./ld/testsuite/ld-gc/abi-note.s: .section ".note.ABI-tag", "a" ./gold/layout.cc: // .note.ABI-tag (as of version 1.6), so that's the one we go with ./include/elf/common.h:/* Values used in GNU .note.ABI-tag notes (NT_GNU_ABI_TAG). */ ./include/elf/common.h:/* Values for FreeBSD .note.ABI-tag notes. Note name is "FreeBSD". */
うーん、なんかそれらしいのはないな。ただ、include/elf/common.hをみると、.note.ABI-tagにはNT_GNU_ABI_TAGというシンボルがついているらしいこと、それに
#define GNU_ABI_TAG_LINUX 0 #define GNU_ABI_TAG_HURD 1 #define GNU_ABI_TAG_SOLARIS 2 #define GNU_ABI_TAG_FREEBSD 3 #define GNU_ABI_TAG_NETBSD 4 #define GNU_ABI_TAG_SYLLABLE 5 #define GNU_ABI_TAG_NACL 6
というバリエーションがあることがわかる。ただ先に見た通り、SolarisでもNetBSDでも.note.ABI-tagはつかない。GNUとあるので、もしかすると、Debian GNU/NetBSDのようなもので使われるのかもしれない。
コンパイラがリンカに何か渡しているのだろうか。ccというコマンドは、入力ファイルや出力ファイルに合わせて、Cコンパイラ (cc1)、アセンブラ、リンカなどを適宜呼び出すだけのプログラムだ。各子プロセスを呼び出す際の引数は、-vオプションをつけると出てくる。
c8$ cc -v -o test test.c Using built-in specs. (中略) Thread model: posix gcc version 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC) COLLECT_GCC_OPTIONS='-v' '-o' 'test' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-redhat-linux/8/cc1 -quiet -v test.c -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -version -o /tmp/ccHDAWoV.s (中略) COLLECT_GCC_OPTIONS='-v' '-o' 'test' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-redhat-linux/8/collect2 -plugin /usr/libexec/gcc/x86_64-redhat-linux/8/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-redhat-linux/8/lto-wrapper -plugin-opt=-fresolution=/tmp/ccRkU91B.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o test /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/8 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../.. /tmp/ccdW3ZHL.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/8/crtend.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o Collect_GCC_OPTIONS='-v' '-o' 'test' '-mtune=generic' '-march=x86-64'
collect2というのは、リンカのラッパプログラムで、C++のコンストラクタやデストラクタなどをどうにかする、というものだ。collect2がリンカを呼び出す際の様子までは出てこないと見える。しかし、collect2に渡された引数の中に、それらしいものは見当たらない。
これは、collect2のソースも見ないといかんかな、と考えてる途中でふと気づいた。test.cをコンパイルしたファイルの他に、/usr/lib64/crt1.o、/usr/crti.o、/usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o、/usr/lib/gcc/x86_64-redhat-linux/8/crtend.o、/usr/lib64/crtn.oをリンクしている (crtは、C RunTime)。これらは、main()が呼ばれる前後の処理をするモジュールだ。ここに.note.ABI-tagが含まれているのではないのか。
c8$ readelf -n /usr/lib64/crt1.o Displaying notes found in: .note.gnu.property Owner Data size Description GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0 Properties: x86 feature: IBT, SHSTK Displaying notes found in: .note.ABI-tag Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 3.2.0 Displaying notes found in: .gnu.build.attributes.hot (以下略)
ビンゴ! ここにある.note.ABI-tagが実行ファイルにリンクされたに違いない。CentOS 7環境では、
c7$ readelf -n /usr/lib64/crt1.o Displaying notes found at file offset 0x00000040 with length 0x00000020: Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 2.6.32
Linux 2.6.32だ。
crt1.oは、
c8$ rpm -qf /usr/lib64/crt1.o glibc-devel-2.28-101.el8.x86_64
glibcに含まれるようだ。早速ソースを取り寄せてみよう。
c8$ wget ftp://ftp.iij.ad.jp/pub/linux/centos-vault/8.2.2004/BaseOS/Source/SPackages/glibc-2.28-101.el8.src.rpm (中略) c8$ rpm -ivh glibc-2.28-101.el8.src.rpm (中略) c8$ rpmbuild -bp ~/rpmbuild/SPECS/glibc.spec (中略) c8$ rpmbuild -bp --nodeps ~/rpmbuild/SPECS/glibc.spec c8$ cd ~/rpmbuild/BUILD/glibc-2.28/ c8$ grep -r ABI-tag . ./ChangeLog.old/ChangeLog.12: (open_verify): Check .note.ABI-tag of the library if present. ./ChangeLog.old/ChangeLog.12: the same directory have different .note.ABI-tag. Record osversion in ./ChangeLog.old/ChangeLog.12: (build-shlib, build-module-helper): Make sure .note.ABI-tag comes ./ChangeLog.old/ChangeLog.17: * elf/dl-load.c (open_verify): Find .note.ABI-tag notes even ./Makerules: -e 's/^.*\.gnu\.hash[ ]*:.*$$/ .note.ABI-tag : { *(.note.ABI-tag) } &/' \ ./Makerules: -e 's/^.*\.hash[ ]*:.*$$/ .note.ABI-tag : { *(.note.ABI-tag) } &/' \ ./csu/abi-note.S: .section ".note.ABI-tag", "a" ./elf/dl-load.c: /* Check .note.ABI-tag if present. */ ./sysdeps/x86/dl-prop.h: /* Nb: Note sections like .note.ABI-tag and .note.gnu.build-id are
csu/abi-note.Sにあるのが、.note.ABI-tagの定義に違いない。csuは、C Start Upを意味する。どちらもコメントだが、残るelf/dl-load.cとsysdeps/x86/dl-prop.hがチェックする部分だろうか。
dl-prop.hは、ざっと調べてみると、.note.gnu.propertyというのを調べているようで、関係なさそう。
elf/dl-load.cは、うーん、これ読んでみると、共有ライブラリなど (*.so) を読むところのようだ。そして、Makefileを見ていると、rtld (RunTime LoaDer、ダイナミックリンカ)、つまり、/lib64/ld-linux-x86-64.so.2の一部のように思える。確かに、
c8$ readelf -n /lib64/libc.so.6 Displaying notes found in: .note.gnu.build-id Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 5134b8e7ad70b3200eb2aba1a53a7016599f65ab Displaying notes found in: .note.ABI-tag Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 3.2.0 Displaying notes found in: .note.gnu.property (以下略)
と、libcには.note.ABI-tagがついている。カーネルのサービスは、libcを経由して利用するので、これはこれで合理的な動作だ。ちなみに、比較した結果、バージョンが足りないと、
osversion = (abi_note[5] & 0xff) * 65536 + (abi_note[6] & 0xff) * 256 + (abi_note[7] & 0xff); if (abi_note[4] != __ABI_TAG_OS || (GLRO(dl_osversion) && GLRO(dl_osversion) < osversion)) { close_and_out: __close_nocancel (fd); __set_errno (ENOENT); fd = -1; }
ENOENT (No such file or directory) を返すようになっている。
試しに、CentOS 8のlibc (/lib64/libc.so.6) をCentOS 6環境に持っていってみよう。CentOS 8のlibcを/tmp/root/lib64/libc.so.6に配置し、ダイナミックリンカはCentOS 6の/lib64/ld-linux-x86-64.so.2を/tmp/root/lib64/ld-linux-x86-64.so.2にコピー、CentOS 6でコンパイルしてあったhello worldを/tmp/root/testに置いた上で/tmp/rootにchrootする。
c6$ sudo chroot /tmp/root /test /test: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory
ちゃんとNo such file or directoryが出た。
なお、dl-load.cの中でも、NetBSDのバイナリエミュレーションと同様、.note.ABI-tagというセクション名は見ないで、中身を調べている。
どう比較しているのか
ABI-tagを比較して足りていなければENOENTになることは分かったが、その比較対象はなんだろう。前節で引用したコードに出てくるdl_osversionというのが比較対象だ。これは、sysdeps/unix/sysv/linux/dl-osinfo.hで定義されているマクロDL_SYSDEP_OSCHECKの中で、_dl_discover_osversion()の結果を設定している。_dl_discover_osversion()は、sysdeps/unix/sysv/linux/dl-sysdep.cで定義されており、uname()を呼んだり、/proc/sys/kernel/osreleaseを読んだりして実行環境のLinuxバージョンを取得している。
興味深いのは、その実行環境を取得するマクロDL_SYSDEP_OSCHECKの中で、dl_osversion (の元になるローカル変数version) と、定数__LINUX_KERNEL_VERSIONとを比較しているところだ。
int version = _dl_discover_osversion (); \ if (__glibc_likely (version >= 0)) \ { \ if (__builtin_expect (GLRO(dl_osversion) == 0, 1) \ || GLRO(dl_osversion) > version) \ GLRO(dl_osversion) = version; \ \ /* Now we can test with the required version. */ \ if (__LINUX_KERNEL_VERSION > 0 && version < __LINUX_KERNEL_VERSION) \ /* Not sufficent. */ \ FATAL ("FATAL: kernel too old\n"); \ } \
__LINUX_KERNEL_VERSIONは、というと、sysdeps/unix/sysv/linux/configureでminimum_kernelというのから設定されていて、これはconfigureスクリプトのオプション--enable-kernelから設定されているようだ。CentOS 8では、glibcのspecファイルを見ると3.2に設定されている (規定値も3.2.0) ので、結局のところ比較対象であるdl_osversionを取得する段階でuname()の結果 (など) が3.2.0未満だとFATALとなることがわかる。なお、minimum_kernelは、先ほど見たabi-note.Sで.note.ABI-tagのバージョン項の__ABI_TAG_VERSIONを得るにも使われている。
DL_SYSDEP_OSCHECKを呼んでいるのは2箇所あり、1つはダイナミックリンカの中、もう一つはスタティックリンクの時のCスタートアップの中である。
試しに、libcだけではなく、ダイナミックリンカ (/lib64/ld-linux-x86-64.so.2) をCentOS 8環境から持っていってみよう。/tmp/root/lib64/ld-linux-x86-64.so.2に配置し、chrootでhello worldを実行してみる。
c6$ sudo chroot /tmp/root /test FATAL: kernel too old
これはダイナミックリンカが出しているはずだ。CentOS 8ではstatic linkできないので、ubuntu 20.04 (ABI-tagはやはりLinux 3.2.0になる) でスタティックリンクしたhello worldを、CentOS 6に持ってきて実行すると、
/tmp/test-static FATAL: kernel too old Aborted (core dumped)
こちらはhello worldにリンクされているCスタートアップが出していると考えられる。
ただ、これらが何と比べてtoo oldか、というと、.note.ABI-tagとは直接関係なく、glibcのconfigureスクリプトに渡される--enable-kernel=X.X.Xが起源である。.note.ABI-tagも同じconfigureオプションが起源であるという意味で、間接的な関係となる。
今回のまとめ
- .note.ABI-tagは、OS (というか、カーネルの種類) とバージョンを持っている。名前は慣習に過ぎないようで、NOTEセクションにある特定の形式、という点が重要。
- OS項は、NetBSDのLinuxバイナリエミュレーションが見ているが、Linux (カーネル) は見ていないので、CPUが合ったELFであればLinux用だろうがNetBSD用だろうが (おそらくFreeBSDやSolaris用でも) 実行しようとする。
- libcのバージョン項は、ダイナミックリンク時にダイナミックリンカが見て、実行環境と比較する。カーネルを呼び出すのはlibcなので、これは合理的な動きである。
- なおダイナミックリンカは全ての共有ライブラリのリンク時に.note.ABI-tagを見るが、libc以外の大抵のライブラリには.note.ABI-tagはついていない。
- 実行ファイルのバージョン項を見ている人はいないので、新しい実行ファイルを古いOSで実行しようとしても実行できてしまう。
- ただし、スタティックリンクの実行ファイルの場合、.note.ABI-tagにあるのと同じバージョンが実行ファイルの中 (Cスタートアップ内) に埋め込まれていて、main()呼び出し前に実行環境と比較する。スタティックリンクの場合はカーネルを呼び出すlibcも埋め込まれているので、これも合理的な動作であり、埋め込まれたバージョンを人間が知るために.note.ABI-tagは有用である。
- ダイナミックリンクの実行ファイルの.note.ABI-tagは何のためにあるのか分からない (知ってる人は教えてください)。
- ダイナミックリンカも最初に実行環境と埋め込まれたバージョンを比較する。ダイナミックリンカには.note.ABI-tagはないので、この埋め込まれたバージョンを人間が知る方法は逆アセンブルなどによるしかなさそう。またダイナミックリンカも、libcをリンクする前にファイルとしてのlibcを読んでくるので、カーネルを呼び出しているのは間違いない。したがって合理的な動作である。
- CentOS 6→7→8で、ABIバージョンが2.6.12→2.6.32→3.2.0と上がっている理由を調べる前に力尽きた。
いうまでもないが、一般論として、新しい版向けのプログラムは、当然古い版では動作しない。今回hello worldが動作したのは偶然である。