自分が作成しているライブラリがあります。次のいずれかを実行すると、すべてのオブジェクトがコンパイルおよびリンクされます:ar rcs lib/libryftts.a $^
gcc -shared $^ -o lib/libryftts.so
私のメイクファイルで。また、それらを/usr/local/lib
に正常にインストールすることもできます。nmでファイルをテストすると、すべての関数が存在します。私の問題は、gcc testing/test.c -lryftts -o test && file ./test
またはgcc testing/test.c lib/libryftts.a -o test && file ./test
を実行すると次のように表示されることです。
test: ELF 64-bit LSB shared object
ではなくtest: ELF 64-bit LSB executable
。何が悪いのですか?
何が悪いのですか?
何もない。
GCCはデフォルトで-pie
バイナリをビルドするように設定されているようです。これらのバイナリは実際にはare共有ライブラリ(タイプET_DYN
)ですが、通常の実行可能ファイルと同じように実行されます。
したがって、バイナリを実行するだけでよく、(動作する場合は)心配する必要はありません。
または、バイナリをgcc -no-pie ...
とリンクすると、タイプET_EXEC
の非PIE
実行可能ファイルが生成され、file
はELF 64-bit LSB executable
と表示されます。
file
5.36はそれをはっきり言っています
file
5.36は、実行可能ファイルがPIEであるかどうかにかかわらず、 に実際に明確に出力します(= /// =) -a-linux-binary-was-compiled-as-position-independent-code/435038#435038
たとえば、PIE実行可能ファイルは次のように表示されます。
main.out:ELF 64ビットLSBパイ実行可能ファイル、x86-64、バージョン1(SYSV)、動的にリンク、ストリップされない
そして非PIEのものとして:
main.out:ELF 64ビットLSB実行可能ファイル、x86-64、バージョン1(SYSV)、静的にリンク、削除されない
この機能は5.33で導入されましたが、単純なchmod +x
チェックだけを行いました。その前に、PIEのshared object
を出力しました。
5.34では、より専門的なDF_1_PIE
ELFメタデータのチェックを開始することを意図していましたが、コミット時の実装のバグ 9109a696f3289ba00eaa222fd432755ec4287e28 により、実際に問題が発生し、GCCが表示されましたshared objects
としてのPIE実行可能ファイル。
バグは5.36のコミット時に修正されました 03084b161cf888b5286dbbcd964c31ccad4f64d9 。
このバグは特に、file
5.34を含むUbuntu 18.10に存在します。
偶然のため、アセンブリコードをld -pie
にリンクすると、それは現れません。
ソースコードの内訳は、この回答の「file
5.36ソースコード分析」セクションに示されています。
Linuxカーネル5.0は、ET_DYN
に基づいてASLRを使用できるかどうかを決定します
file
"混乱"の根本的な原因は、 PIE実行可能ファイル と共有ライブラリの両方が位置に依存せず、ランダムなメモリ位置に配置できることです。
fs/binfmt_elf.c では、カーネルは次の2種類のELFファイルのみを受け入れます。
/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN)
goto out;
次に、ET_DYN
の場合のみ、load_bias
をゼロ以外の値に設定します。 ELFオフセットを決定するのはload_bias
です: LinuxでPIE実行可能ファイルのテキストセクションのアドレスはどのように決定されますか?
/*
* If we are loading ET_EXEC or we have already performed
* the ET_DYN load_addr calculations, proceed normally.
*/
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
elf_flags |= elf_fixed;
} else if (loc->elf_ex.e_type == ET_DYN) {
/*
* This logic is run once for the first LOAD Program
* Header for ET_DYN binaries to calculate the
* randomization (load_bias) for all the LOAD
* Program Headers, and to calculate the entire
* size of the ELF mapping (total_size). (Note that
* load_addr_set is set to true later once the
* initial mapping is performed.)
*
* There are effectively two types of ET_DYN
* binaries: programs (i.e. PIE: ET_DYN with INTERP)
* and loaders (ET_DYN without INTERP, since they
* _are_ the ELF interpreter). The loaders must
* be loaded away from programs since the program
* may otherwise collide with the loader (especially
* for ET_EXEC which does not have a randomized
* position). For example to handle invocations of
* "./ld.so someprog" to test out a new version of
* the loader, the subsequent program that the
* loader loads must avoid the loader itself, so
* they cannot share the same load range. Sufficient
* room for the brk must be allocated with the
* loader as well, since brk must be available with
* the loader.
*
* Therefore, programs are loaded offset from
* ELF_ET_DYN_BASE and loaders are loaded into the
* independently randomized mmap region (0 load_bias
* without MAP_FIXED).
*/
if (elf_interpreter) {
load_bias = ELF_ET_DYN_BASE;
if (current->flags & PF_RANDOMIZE)
load_bias += Arch_mmap_rnd();
elf_flags |= elf_fixed;
} else
load_bias = 0;
私はこれを実験的に確認しています: gccとldの位置に依存しない実行可能ファイルの-fPIEオプションとは何ですか?
file
5.36動作の内訳
ソースからfile
がどのように機能するかを調べた後。結論は次のとおりです。
Elf32_Ehdr.e_type == ET_EXEC
executable
Elf32_Ehdr.e_type == ET_DYN
DT_FLAGS_1
動的セクションエントリが存在する場合DF_1_PIE
がDT_FLAGS_1
に設定されている場合:pie executable
shared object
pie executable
shared object
そしてこれはそれを確認するいくつかの実験です:
Executable generation ELF type DT_FLAGS_1 DF_1_PIE chdmod +x file 5.36
--------------------------- -------- ---------- -------- -------------- --------------
gcc -fpie -pie ET_DYN y y y pie executable
gcc -fno-pie -no-pie ET_EXEC n n y executable
gcc -shared ET_DYN n n y pie executable
gcc -shared ET_DYN n n n shared object
ld ET_EXEC n n y executable
ld -pie --dynamic-linker ET_DYN y y y pie executable
ld -pie --no-dynamic-linker ET_DYN y y y pie executable
Ubuntu 18.10、GCC 8.2.0、Binutils 2.31.1でテスト済み。
各タイプの実験の完全なテスト例は、次の場所で説明されています。
gcc -pie
およびgcc -no-pie
: gccおよびldでの位置に依存しない実行可能ファイルの-fPIEオプションとは何ですか?
Ubuntu 17.10以降、-pie
はデフォルトでオンに設定されていることに注意してください。 32ビット絶対アドレスはx86-64 Linuxでは許可されなくなりましたか?
gcc -shared
(.so
共有ライブラリ): https://github.com/cirosantilli/cpp-cheat/tree/b80ccb4a842db52d719a16d3716b02b684ebbf11/shared_library/basic
ld
実験: Linuxで静的にリンクされた位置に依存しない実行可能ELFを作成する方法ELF type
およびDF_1_PIE
は、それぞれ次のように決定されます。
readelf --file-header main.out | grep Type
readelf --dynamic main.out | grep FLAGS_1
file
5.36ソースコード分析
分析する重要なファイルは magic/Magdir/elf です。
このマジックフォーマットは、固定位置のバイトの値のみに応じてファイルタイプを決定します。
フォーマット自体は次の場所に記載されています。
man 5 magic
したがって、この時点で、次のドキュメントを手元に置いておきます。
ファイルの終わりに向かって、次のように表示されます。
0 string \177ELF ELF
!:strength *2
>4 byte 0 invalid class
>4 byte 1 32-bit
>4 byte 2 64-bit
>5 byte 0 invalid byte order
>5 byte 1 LSB
>>0 use elf-le
>5 byte 2 MSB
>>0 use \^elf-le
\177ELF
は、すべてのELFファイルの先頭にある4つのマジックバイトです。 \177
は0x7F
の8進数です。
次に、標準のElf32_Ehdr
構造体と比較すると、バイト4(5番目のバイト、マジック識別子の後の最初のバイト)がELFクラスを決定していることがわかります。
e_ident[EI_CLASSELFCLASS]
可能な値の一部は次のとおりです。
ELFCLASS32 1
ELFCLASS64 2
file
ソースでは、次のようになります。
1 32-bit
2 64-bit
と32-bit
と64-bit
は、file
がstdoutに出力する文字列です!
したがって、そのファイルでshared object
を検索すると、次のように導かれます。
0 name elf-le
>16 leshort 0 no file type,
!:mime application/octet-stream
>16 leshort 1 relocatable,
!:mime application/x-object
>16 leshort 2 executable,
!:mime application/x-executable
>16 leshort 3 ${x?pie executable:shared object},
したがって、このelf-le
は、コードの前の部分に含まれるある種の識別子です。
バイト16はまさにELFタイプです。
Elf32_Ehdr.e_type
その値の一部は次のとおりです。
ET_EXEC 2
ET_DYN 3
したがって、ET_EXEC
は常にexecutable
として出力されます。
ET_DYN
には、${x
に応じて2つの可能性があります。
pie executable
shared object
${x
の質問:ファイルは実行可能ですか、それともユーザー、グループ、その他のいずれでも実行できませんか?はいの場合はpie executable
、それ以外の場合はshared object
を表示します。
この展開は、 src/softmagic.c
のvarexpand
関数で行われます。
static int
varexpand(struct magic_set *ms, char *buf, size_t len, const char *str)
{
[...]
case 'x':
if (ms->mode & 0111) {
ptr = t;
l = et - t;
} else {
ptr = e;
l = ee - e;
}
break;
ただし、もう1つハックがあります。 src/readelf.c
関数dodynamic
で、動的セクション(DT_FLAGS_1
)のPT_DYNAMIC
フラグエントリが存在する場合、 st->mode
の権限は、DF_1_PIE
フラグの有無によって上書きされます。
case DT_FLAGS_1:
if (xdh_val & DF_1_PIE)
ms->mode |= 0111;
else
ms->mode &= ~0111;
break;
5.34のバグは、最初のコードが次のように記述されていたことです。
if (xdh_val == DF_1_PIE)
つまり、DF_1_NOW
が原因でGCCがデフォルトで行う別のフラグが設定されている場合、実行可能ファイルはshared object
と表示されていました。
DT_FLAGS_1
フラグエントリはELF標準で記述されていないため、Binutils拡張である必要があります。
このフラグはLinuxカーネル5.0またはglibc 2.27では使用されないため、ファイルがPIEであるかどうかを示すだけで情報が得られるようです。