スタティックリンクでもPIE(Position Independent Executable)
PIEとは
PIE(Position Independent Executable)はプログラムをロードするアドレスが固定ではない実行ファイルです。OSはロードアドレスをランダム化することで安全性を上げることができます。
gccに-pie
というオプションがかなり昔からありましたが、単にこのオプションをつけるだけでPIEとして実行できるわけではなく、実行直前にダイナミックリンカがアドレスを解決する必要があったので、スタティックリンクした場合にはPIEにはなりませんでした。
しかし、最近gccに-static-pie
というオプションがいつの間にか追加されていることに気がつきました。
hello worldでmain関数のアドレスを調べてみる
AArch64のUbuntu 22.04を使用しています。
テストプログラムはこちら。
#include <stdio.h>
int main()
{
printf("Hello, world! main=%p\n", main);
}
使用したgccのバージョン。
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
まずは普通にコンパイル。最近ではPIEを生成するのがデフォルトになっています。
$ gcc -o hello hello.c
$ file hello
hello: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=e16d464604a4e7037dfa901b45b1c890ee19501c, for GNU/Linux 3.7.0, not stripped
$ ./hello
Hello, world! main=0xaaaae1400754
$ ./hello
Hello, world! main=0xaaaaba1c0754
$ ./hello
Hello, world! main=0xaaaaac870754
$ ./hello
Hello, world! main=0xaaaac9570754
$ ./hello
Hello, world! main=0xaaaae5d30754
main
のアドレスが毎回変わっています。
次にstatic link してみます。
$ gcc -o hello_static -static hello.c
$ file hello_static
hello_static: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=b7dcb5685275ccededf79295b7b1bbf7ec55fa7a, for GNU/Linux 3.7.0, not stripped
$ ./hello_static
Hello, world! main=0x4006d4
$ ./hello_static
Hello, world! main=0x4006d4
$ ./hello_static
Hello, world! main=0x4006d4
$ ./hello_static
Hello, world! main=0x4006d4
$ ./hello_static
Hello, world! main=0x4006d4
main
のアドレスは毎回同じです。
今度は -static-pie
オプションを試してみました。
$ gcc -o hello_static_pie -static-pie hello.c
$ file hello_static_pie
hello_static_pie: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (GNU/Linux), static-pie linked, BuildID[sha1]=13c05ddbd6a58129ee0921a8ed3c2cdea6eddc7f, for GNU/Linux 3.7.0, not stripped
$ ./hello_static_pie
Hello, world! main=0xffffbd531bd4
$ ./hello_static_pie
Hello, world! main=0xffffa45f2bd4
$ ./hello_static_pie
Hello, world! main=0xffffb48ccbd4
$ ./hello_static_pie
Hello, world! main=0xffffba07cbd4
$ ./hello_static_pie
Hello, world! main=0xffffb339cbd4
static linkでありながら、main
のアドレスが毎回変わっています。
適当にpie
がつくシンボルを探してみると
$ nm hello_static_pie |grep pie
000000000002b840 t _dl_relocate_static_pie
_dl_relocate_static_pie
いかにもそれっぽい関数が含まれていました。おそらくCランタイムからこれが呼び出されて、アドレスのリロケーションを行うのでしょう。
以下のページの情報によるとこの関数はglibcに含まれるもののようです。
gccに-static-pie
オプションが追加されたときのパッチはこれのようです。
ELFのヘッダ情報を見てみる
$ readelf -h hello_static
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: EXEC (Executable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x400580
Start of program headers: 64 (bytes into file)
Start of section headers: 644656 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 6
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
$ readelf -h hello_static_pie
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x8ac0
Start of program headers: 64 (bytes into file)
Start of section headers: 689840 (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: 36
Section header string table index: 35
ELFのヘッダのTypeの項目でPIEかどうかを判別できるようです。OSはこれを見てロードアドレスをランダムにするかどうかを決めているのでしょう。
clangの場合
$ clang --version
clang version 16.0.0 (https://github.com/llvm/llvm-project.git 434575c026c81319b393f64047025b54e69e24c2)
Target: aarch64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
$ clang -o hello_static_pie_clang -static-pie hello.c
$ file hello_static_pie_clang
hello_static_pie_clang: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (GNU/Linux), static-pie linked, for GNU/Linux 3.7.0, not stripped
$ ./hello_static_pie_clang
Hello, world! main=0xffff88eeebd4
$ ./hello_static_pie_clang
Hello, world! main=0xffff87d60bd4
$ ./hello_static_pie_clang
Hello, world! main=0xffffa4342bd4
$ ./hello_static_pie_clang
Hello, world! main=0xffffacdcabd4
clangでも -static-pie
オプションでstatic-pieの実行ファイルを作ることができました。
参考
関連
Discussion