Hello Worldの厄介な道

この蚘事を曞くためのむンスピレヌションは、x86アヌキテクチャに関する同様の出版物を読んだ埌に埗られたした[1]。


この資料は、プログラムが内郚からどのように配眮されおいるか、メむンに入る前に䜕が起こっおいるのか、なぜこれがすべお行われおいるのかを理解したい人に圹立ちたす。 たた、glibcラむブラリの機胜のいく぀かを䜿甚する方法も瀺したす。 そしお最終的に、元の蚘事[1]のように、移動した経路が芖芚的に衚瀺されたす。 ほずんどの堎合、この蚘事はglibcラむブラリの解析です。


それでは、旅を始めたしょう。 Linux x86-64、およびlldbをデバッグツヌルずしお䜿甚したす。 たた、時々objdumpを䜿甚しおプログラムを逆アセンブルしたす。


゜ヌスコヌドは通垞のHello、world hello.cpp です。


#include <iostream> int main() { std::cout << "Hello, world!" << std::endl; } 

念のため、システムずプログラムに関する情報
 * Clang -- 4.0.1 * lldb -- 4.0.1 * glibc -- 2.25 * `uname -r` -- 4.12.10-1-ARCH 

コヌドをコンパむルしおデバッグを開始したす。


 clang++ -stdlib=libc++ hello1.cpp -g -o hello1.out lldb hello1.out 

ご泚意

プログラムで考慮されるコヌドのほずんどは、遞択されたコンパむラずc ++ラむブラリからほずんど独立しおいたす。 たたたたllvmむンフラストラクチャがgccよりも私に少し近づいおいるので、libc ++ラむブラリを備えたclangコンパむラが怜蚎されたすが、問題のコヌドのほずんどがglibcラむブラリから解析されるため、倧きな違いはありたせん。


bash だけでなくを䜿甚する堎合のプログラムは、 fork関数を呌び出し、 execveを䜿甚しおコマンドラむン匕数を枡しお新しいプロセスを䜜成するこずによっお生成されたす。 たた、実行可胜ファむルの最初の呜什に制埡を移す前に、入力および出力蚘述子STDIN、STDOUT、STDERRが蚭定されたす。その埌、動的リンクの堎合、プログラムに必芁なラむブラリがロヌドおよび初期化され、「. preinit_array 」セクションの関数が呌び出されたす このすべおの埌、最初の関数が呌び出されたす。これは実行可胜ファむル「 .preinit_array 」セクションをカりントしないにあり、埓来はプログラムの開始ず芋なされる_startず呌ばれたす。 静的リンクの堎合、たずえば、「. preinit_array 」セクションの初期化など、リンカヌの䜜業は実行可胜ファむル内にあり、関数自䜓は動的にリンクされたプログラムずは少し異なりたす。 動的にリンクされたプログラムを怜蚎したす。


実行可胜ファむルの゚ントリポむントは、ヘッダヌに瀺されおいたす。


 readelf -h hello1.out | grep Entry 

次に、 objdump -d hello1.outを䜿甚しお、このアドレスにある関数を確認したす。 これは既に説明した_start関数であり、ブレヌクポむントを蚭定しおデバッグを開始したす。


 b _start r 

ABIに぀いお少し

りィキペディアの定矩
ABIアプリケヌションバむナリむンタヌフェむス-互換性のあるABIを搭茉したマシン間で実行可胜コヌドを移怍できるように蚭蚈された、オペレヌティングシステムおよびその他の䜎レベルサヌビスにアプリケヌションにアクセスするための䞀連の契玄 ゜ヌスコヌドレベルで互換性を芏制するAPIずは異なりたす。 ABIは、バむナリむンタヌフェむスを定矩しながら、すべおのコヌドを再コンパむルせずに、コンパむルされたコンポヌネントモゞュヌルをリンカヌが結合できるようにする䞀連のルヌルず考えるこずができたす。


ABIレベルはc / c ++プログラマヌには隠されおおり、このレベルのすべおの䜜業はコンパむラヌず暙準libcラむブラリヌによっお実装されたす。 私の堎合、 clangコンパむラずglibcラむブラリはすべおのABIルヌルに埓いたす。 Linux x86-64のABIルヌルは、 System V AMD64 ABI [2]で指定されおいたす。 Solaris、Linux、FreeBSD、OS Xは、このドキュメントの芏則に埓いたす。 マむクロ゜フトには独自のABIがあり、それらは慎重に隠されおいたす。 このドキュメントの最初の章[2]では、このアヌキテクチャは32ビットプロセッサ[3]のABIルヌルにも準拠しおいるず述べおいたす。 したがっお、これらはglibcなどの䜎レベルラむブラリの開発者が䟝存する2぀の基本的なドキュメントです。


ABIによるず、プログラムの開始時に、以䞋を陀くすべおのレゞスタが定矩されおいたせん。



補助ベクトル補助ベクトルには、珟圚のマシンに関する情報が含たれおいたす。 LD_SHOW_AUXV=1 ./hello1.outを䜿甚しお、それらの倀を衚瀺できたす。 埗られた倀は、[4]でかなり詳しく説明されおいたす。


そしお実際に

x `$rsp` -s8 -fu -c1プログラム匕数の数
p *(char**)($rsp+8)はプログラムの名前です。 スタックの次は、プログラム匕数、れロ区切り文字、環境匕数、および補助ベクトルです。


さらに、フラグレゞスタが蚭定され、SSEずx87が構成されたす§3.4.1[2]。


メむンナヌザヌ関数の匕数の準備がほが敎っおいるこずに気付くかもしれたせんが、残っおいるのは正しいポむンタヌを蚭定するこずだけです。 ただし、ポむンタを蚭定する以倖に、メむン手順を入力する前にただ倚くの䜜業が必芁です。 将来的には、説明内の関数には、゜ヌスの堎所ず、ツヌルチップの圢匏のバむナリ圢匏の関数自䜓が付随したす。䟋 main


_start関数を芋おみたしょう。これは小さく、その䞻なタスクは__libc_start_main関数に制埡を移すこずです。


diを䜿甚しお珟圚の関数を逆アセンブルしたす明確にするために、出力はここず以䞋でフォヌマットされたす。


 _start: xor %ebp, %ebp mov %rdx, %r9 pop %rsi mov %rsp, %rdx and $-0x10, %rsp push %rax push %rsp lea 0x1aa(%rip), %r8 ; __libc_csu_fini lea 0x133(%rip), %rcx ; __libc_csu_init lea 0xec(%rip), %rdi ; main call *0x200796(%rip) ; __libc_start_main hlt 

_start関数は 、リンカヌによっおオブゞェクトファむルScrt1.oずしおプログラムに接続されたす。 同様の機胜を実行するオブゞェクトファむルcrt1gcrt1、Srct1、Mcrt1にはいく぀かの皮類がありたすが、さたざたな堎合に䜿甚されたす。 たずえば、 Scrt1.oは PICコヌドの生成に䜿甚されたす[5]。 オブゞェクトファむルの遞択を確認するには、 -vスむッチを䜿甚しおプログラムをコンパむルしたす。 これらの関数のオフセットはリンク段階でのみ認識されるため 、オブゞェクトオフセット__libc_csu_fini 、 __libc_csu_init、およびmainは瀺されおいないこずに泚意しおください。


ABI芁件に埓っお、 フレヌムを初期フレヌムずしおマヌクするには、 ebpをれロに蚭定する必芁がありたす。これは、 xorebp、ebpが正確に行うこずです。


次は、 __ libc_start_main関数を呌び出すための準備です。 この関数の眲名は次の圢匏です。


 int __libc_start_main(int (*main) (int, char **, char **), int argc, char **argv, __typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *stack_end) 

ABIによるず、関数の匕数は適切な堎所に配眮する必芁がありたす。


匕数関数呌び出しの䜍眮説明
メむンrdiプログラムの䞻な機胜
argcrsiプログラム匕数の数
argvrdx匕数の配列。 匕数が環境倉数であり、その埌が補助ベクトルである埌
初期化rcxmainの前に呌び出されるグロヌバルオブゞェクトコンストラクタヌ。 この関数のタむプは、メむン関数ず同じです。
フィニr8メむンの埌に呌び出されるグロヌバルオブゞェクトデストラクタ
rtld_finir9動的リンカヌのデストラクタ。 動的に割り圓おられたラむブラリを解攟したす
stack_endrsp敎列されたスタックの珟圚の䜍眮

ABIでは、関数が呌び出されたずきに、スタックが16バむト匕数のタむプに応じお32、堎合によっおは64境界に敎列される必芁がありたす。 芁求は、 and $ -0x10、rsp  呜什の実行埌に満たされたす。 このアラむメントの意味は、SIMD呜什SSE、MMXはアラむメントされたデヌタでのみ動䜜し、スカラヌ呜什はアラむメントされたデヌタでより速く読み曞きされるこずです。


16バむトのアラむメントを保持するには、 __libc_start_mainを呌び出す前に、raxレゞスタヌがスタックに配眮され、未定矩の倀が栌玍されたす。 このスタックセルは読み取られたせん。


プログラムはlibc_start_main関数から返されるべきではなく、䞍正な動䜜を瀺すためにhlt呜什が䜿甚されたす。 この呜什の特城は、保護されたプロセッサモヌドでは保護リング0でのみ実行できるこずです。぀たり、オペレヌティングシステムのみがこの呜什を呌び出すこずができたす。 リング3にありたす。぀たり、プログラムに暩限がないコマンドを実行しようずするず、セグメンテヌション゚ラヌが発生したす。
hlt呜什の埌には、 nopl 0x0呜什rax、rax、1もありたす 。これは、次の関数を16バむト境界に揃えるために必芁です。 ABIはこれを必芁ずしたせんが、コンパむラヌは関数の先頭を揃えおパフォヌマンスを改善したす 1、2 。


それでは先に進みたしょう


 b __libc_start_main c 

__libc_start_main関数の゜ヌスコヌドは、静的および動的にリンクされたラむブラリに察しお異なるコヌドが生成されるこずを瀺しおいたす。 gdbたたはlldbを䜿甚しお、 libc.so.6ラむブラリで関数コヌドがどのように芋えるかを確認できたす。
lldb libc.so.6 -b -o 'di -n __libc_start_main'


__glibc_ [un]に぀いお少し

glibcラむブラリコヌドには、__ glibc_likelyおよび__glibc_unlikelyの倚くのオカレンスが含たれおいたす。 倚数の条件付き操䜜がこのマクロに眮き換えられおいたす。 マクロは、最終的に次の組み蟌み関数に倉換されたす。


 # define __glibc_unlikely(cond) __builtin_expect ((cond), 0) # define __glibc_likely(cond) __builtin_expect ((cond), 1) 

__builtin_expectは、コンパむラがメモリ内のコヌドのセクションを正しく配眮するのに圹立぀䞀皮の最適化です。 どのブランチが実行される可胜性が最も高いかをコンパむラヌに通知し、コンパむラヌはこのメモリヌ領域を比范呜什の盎埌に配眮するこずで、呜什のキャッシュ可胜性を向䞊させ、コンパむラヌは関数の最埌に残りのブランチがあればそれを隠したす。


__libc_start_main関数は少し面倒で、䞻なアクションを簡単に説明したす。



__cxa_atexit


__cxa_atexit関数は、最初のラッパヌのラッパヌであるatexitずは異なり、登録枈み関数のパラメヌタヌを取るこずができたすが、ナヌザヌ空間から盎接呌び出すこずはできたせん。 関数はDSO識別子を䜿甚するため、呌び出さないでください。DSO識別子はコンパむラのみが知っおいたす。 __cxa_atexitf、p、dが呌び出されるず、DSO dがアンロヌドされるずきにfp関数が呌び出されるようにする必芁がありたす[8]。


ただし、匕数をパラメヌタヌ関数に枡す

__cxa_atexitの䜿甚䟋


 #include <cstdio> extern "C" int __cxa_atexit (void (*func) (void *), void *arg, void *d); extern void* __dso_handle; void printArg(void *a) { int arg = *static_cast<int*>(a); printf("%d\n",arg); delete (int*)a; } int main() { int *k = new int(17); __cxa_atexit(printArg, k, __dso_handle); } 

このトリックは、爜快感のためにのみ䜿甚するこずをお勧めしたす。 プログラムを終了するずきに、同様の方法を䜿甚しおデストラクタを呌び出す方が安党です。


rtld_finiは、リンカ関数_dl_finiぞのポむンタです 。 はい、リンカヌはglibcラむブラリの䞀郚です。 _dl_fini関数は、ロヌドされたすべおのラむブラリヌの初期化を解陀しおアンロヌドしたす。


__libc_csu_init


前のものず同じ方法で__libc_csu_init関数にアクセスできたす。 __libc_csu_initは、 _initおよび.init_arrayセクションにある関数ポむンタヌを呌び出したす。


_init


_init関数は、すべお.initセクションにありたす。 そのコヌドは2぀の郚分に分かれおいたす 玹介ず゚ピロヌグ 。 抂芁は、プロロヌグず__gmon_start__関数の呌び出しの詊行で構成されたす。


 _init subq $0x8, %rsp leaq 0x105(%rip), %rax ; __gmon_start__ testq %rax, %rax je 0x5555555548a2 ; je to addq instruction callq *%rax addq $0x8, %rsp retq 

_init関数の䞻な目的は、 gprofプロファむラヌを初期化するこずです。 呜什「 leaq 0x105(%rip), %rax 」は、関数__gmon_start__-プロファむラヌを初期化する関数のアドレスを取りたす。 プロファむラヌが存圚しない堎合、raxには倀0が含たれ、遷移jeは機胜したす。 subq $ 0x8、rspおよびaddq $ 0x8、rsp呜什は、スタックを敎列し、元の状態に戻したす。 このアラむメントが必芁なのは、関数を呌び出すずきに、x86-64アヌキテクチャでのサむズが8バむトの戻りアドレスをスタックに配眮するためです。


コヌドの独自のセクションを.initセクションに远加できたす。 hello2.cppの䟋を考えおみたしょう。


 #include <cstdio> extern "C" void my_init() { puts("Hello from init"); } __asm__( ".section .init\n" "call my_init" ); int main(){} 

_initが次のようになっおいるこずを考慮しおください。


 subq $0x8, %rsp movq 0x200835(%rip), %rax testq %rax, %rax je 0x5555555547ba callq *%rax callq 0x555555554990 ; ::my_init() addq $0x8, %rsp retq 

リストからcallq 0x555555554990ように、 callq 0x555555554990呜什callq 0x555555554990゚ントリず関数の゚ピロヌグの間callq 0x555555554990远加され、 my_initを呌び出すだけです。 _init関数は、プログラムの䞀郚の独自の初期化を簡単に远加できるように実装されおいるようです。


興味深い事実 気配りのある読者は、hello2.cppの出力がputs関数を介しお出力されおいるこずに気付いおいるはずです。 coutを介しお出力する堎合、 libstdc ++ラむブラリでコンパむルするず、セグメンテヌション゚ラヌが発生し、 libc ++ラむブラリを䜿甚するず、メッセヌゞが正垞に衚瀺されたす。 これは䜕のために起こっおいるのですか 実際、 libstdc ++では、 cout通垞のグロヌバルオブゞェクトずしお初期化され、グロヌバルオブゞェクトは少し埌に初期化されたす。 libc ++の堎合、 ld-linux-x86-64.so.2ラむブラリから_dl_init関数のラむブラリをロヌドするずきに初期化が行われたす 。 この関数は、 _start関数に制埡を枡す盎前に、 _dl_start_userから呌び出されたす。


各方法には長所ず短所がありたす。 libc ++ラむブラリが接続されおいる堎合、 coutような暙準のc ++出力ツヌルcout䜿甚されcoutいなくおも、コンストラクタはずにかく呌び出されたす。 libstdc ++ラむブラリの堎合、最適化フラグを有効にしおも、 iostreamヘッダヌファむルが接続されおいる回数だけコンストラクタヌが呌び出されたす。 圓然、コンストラクタヌは、耇数回呌び出すこずができ、再初期化がスキップされるずいう事実を考慮したす。 もちろん、これはプログラムの初期化をそれほど遅くしたせんが、それでも䞍快です。 この理由から、倚くの高性胜プロゞェクトは、iostreamヘッダヌファむルの䜿甚を䜿甚せず、掚奚せず、さらには犁止し、その結果、入出力甚の独自のむンタヌフェむスを䜜成したす。


.init_array


次に、ポむンタヌが.init_arrayセクションにある関数が呌び出されたす。
セクションの内容を確認したす。


 objdump hello1.out -s -j .init_array 

私の堎合、 .init_arrayの内容は次の意味を持っおいたす a00f0000 00000000 、これはリトル゚ンディアンのバむト順の 64ビットシステム䞊のアドレス0x0fa0を意味したす。 このアドレスにはframe_dummy関数がありたす。


frame_dummy

興味深いこずに、 frame_dummyはgccラむブラリの䞀郚です。


gccはそれず䜕の関係がありたすか clangコンパむラがありたす

gccプロゞェクトは非垞に倧きく、すでにLinuxオペレヌティングシステムに根を匵っおいるこずを忘れないでください。 gccプロゞェクトには、コンパむラだけでなく、コンパむルに必芁なファむルも含たれおいたす。 したがっお、リンクはcrtbeginS.oやcrtendS.oなどのcrtファむルを䜿甚したす。
したがっお、gccプロゞェクトを完党に削陀するこずはできたせん。少なくずも、補助crtファむルを残す必芁がありたす。 メむンのようにgccコンパむラを䜿甚しないUnixオペレヌティングシステム。


frame_dummyは次のようになりたす。


 pushq %rbp movq %rsp, %rbp popq %rbp jmp 0x555555554cc0 ; register_tm_clones nopw (%rax,%rax) 

frame_dummyのタスクは、匕数を蚭定し、 register_tm_clones関数を実行するこずです。 この局は、匕数を公開するためにのみ必芁です。 この堎合、匕数は蚭定されおいたせんが、゜ヌスコヌドからわかるように、アヌキテクチャによっおは垞にそうずは限りたせん。 興味深いこずに、最初の2぀の指瀺はプロロヌグであり、3番目の指瀺ぱピロヌグです。 jmp呜什は、関数呌び出しのテヌル最適化です。 そしお、い぀ものように、アラむメントの最埌に。


register_tm_clones関数は、 トランザクション メモリをアクティブにするために必芁です。


グロヌバルオブゞェクトの初期化

グロヌバルオブゞェクトがある堎合、ここで初期化されたす。
グロヌバルオブゞェクトがある堎合、関数アドレス_GLOBAL__sub_I_< > .init_arrayセクションに远加_GLOBAL__sub_I_< > 。


グロヌバル倉数を初期化する䟋を考えおみたしょう。
global1.cpp 


 int k = printf("Hello from .init_array"); 

倉数は次のように初期化されたす。


 push %rbp mov %rsp, %rbp lea 0xf59(%rip), %rdi ; + 4 mov $0x0, %al call 0x555555554e80 ; symbol stub for: printf mov %eax, 0x202130(%rip) ; k pop %rbp ret 

最初の2぀の指瀺はプロロヌグです。 次に、 %rdi行にポむンタヌを眮き、 %alをれロに蚭定しお、 printf関数を呌び出す準備をしたす。 ABI [2]によるず、可倉数の匕数を持぀関数には、ベクトルレゞスタヌに含たれる可倉匕数の数を意味する%alに栌玍された隠しパラメヌタヌが含たれおいたす。 ほずんどの堎合、いく぀かの関数を最適化する必芁がありたすが、 printfはベクトルレゞスタからスタックにデヌタを移動するためにこの情報を䜿甚したす。
printfを呌び出した埌、関数の結果が倉数kのメモリ領域に配眮され、゚ピロヌグが呌び出されたす。


global2.cpp 
デフォルトではないコンストラクタずデストラクタを持぀特定のクラスGlobalがあるずしたしょう


 Global g; 

次に、初期化は次のようになりたす。


 push %rbp mov %rsp, %rbp sub $0x10, %rsp lea 0x202175(%rip), %rdi ; g call 0x5555555550e0 ; Global::Global() lea 0x1c5(%rip), %rdi ; Global::~Global() lea 0x202162(%rip), %rsi ; g lea 0x202147(%rip), %rdx ; __dso_handle call 0x555555554f10 ; symbol stub for: __cxa_atexit mov %eax, -0x4(%rbp) add $0x10, %rsp pop %rbp ret 

ここでは、グロヌバルコンストラクタヌを呌び出した埌、デストラクタが__cxa_atexitを䜿甚しお登録される方法を確認したす。 Itanium ABI [8]に埓っお実装されおいたす。


関数呌び出しの初期化

glibcから、初期化は次のように呌び出されたす (*__init_array_start [i]) (argc, argv, envp);


mainに䌌たパラメヌタヌが初期化関数に枡されるため、䜿甚できるこずに泚意しおください。 gccおよびclangコンパむラにはconstructor属性があり、オブゞェクトの初期化ステップの前に関数が呌び出されたす。


これらの匕数をそれに枡すこずができたす。 次のグロヌバル関数を䜿甚しお、プログラムの出力を確認したす。


 void __attribute__((constructor)) hello(int argc, char **argv, char **env) { printf("#args = %d\n", argc); printf("filename = %s\n", argv[0]); } 

これは、より実甚的な目的に䜿甚できたすhello3.cpp


 #include <cstdio> class C { public: C(int i) { printf("Program has %d argument(s)\n", i); } }; int constructorArg; const C c(constructorArg); void __attribute__((constructor (65535))) hello(int argc, char ** argv, char **env){ constructorArg = argc; } int main(){} 

constructor属性パラメヌタヌは、呌び出しの優先順䜍を瀺したす。


おそらく既に掚枬したように、プログラムは正しい数の匕数を出力したす。最も興味深いこずに、 cオブゞェクトは定数です。 このアプロヌチの䞻な欠点は、暙準のサポヌトの欠劂であり、結果ずしお、クロスプラットフォヌムの欠劂です。 たた、このようなコヌドは、䜿甚されるlibcラむブラリに倧きく䟝存しおいたす。


int x = 1 + 2 * 3;ずいう圢匏のグロヌバル倉数を远加したいず思いint x = 1 + 2 * 3; たったく初期化されず、それらの倀は最初にコンパむラによっおメモリに曞き蟌たれたす。 int s = sum(4, 5)などの単玔な関数によっお初期化される倉数も初期化する堎合は、C ++ 11暙準の識別子constexprをsum関数に远加したす。


キャンセルポむントを䜜成


キャンセルポむントは、 setjmpを呌び出しおグロヌバル倉数を蚭定するこずにより䜜成されたす。
メむンストリヌムがキャンセルされたずきに、 元に戻すこずができるように、元に戻すバッファを蚭定するには、 setjmpコンテキストを保存する必芁がありたす。


メむンスレッドをキャンセルする䟋

ファむルcancel.cpp


 #include <pthread.h> pthread_t g_thr = pthread_self(); void * thread_start(void *) { pthread_cancel(g_thr); return 0; } int main() { pthread_t thr; pthread_create(&thr, NULL, thread_start, NULL); pthread_detach(thr); while (1) { pthread_testcancel(); } } 

cancel.cpp , , , exit . , , , , .


, , setjmp :


 br set -n __libc_start_main -R 162 

: , — .


setjmp __GI__setjmp . , . [7]. , , PLT .


main


.


 std::cout << "Hello, world!" << std::endl; 

, :


 operator<<(std::cout, "Hello, World!").operator<<(std::endl); 

たたは


 operator<<(std::cout, "Hello, World!"); std::cout.operator<<(std::endl); 

C++ << . , , , , . , .


endl libc++, libstdc++ : ostream& endl(ostream&);


ostream , << , visitor .


. IFUNC-, __strlen_avx2 _strlen_sse2 . strlen .


stdout _IO_file_doallocate malloc , 1 . , setvbuf .


stdout , . flush , stdout .


, flush , fwrite , __libc_write , syscall ( , ):


 ssize_t __libc_write (int fd, const void *buf, size_t nbytes) { return ({ unsigned long int resultvar = ({ unsigned long int resultvar; long int __arg3 = (long int) (nbytes); long int __arg2 = (long int) (buf); long int __arg1 = (long int) (fd); register long int _a3 asm ("rdx") = __arg3; register long int _a2 asm ("rsi") = __arg2; register long int _a1 asm ("rdi") = __arg1; asm volatile ( "syscall\n\t" : "=a" (resultvar) : "0" (1) , "r" (_a1), "r" (_a2), "r" (_a3) : "memory", "cc", "r11", "cx"); (long int) resultvar; }); resultvar; }); } 

statement expressions , gcc:


 int l = ({int b = 4; int c = 8; c += b}); 

, c += b l == 12 .


__libc_write ( __GI___libc_write , _setjmp ) syscall , syscall , C . rax . =a , rax , "0" (1) , rax 1 ( sys_write ).


, , sys_write , .


, ABI [2], . : %rdi, %rsi, %rdx, %r10, %r8, %r9.


x86 x86-64 !


, , - , , . PIC ( 1 , 2 ).


exit


exit :


  1. __call_tls_dtors — thread local storage , .
  2. , atexit
    • _dl_fini — , _start r9 , .
    • ( ).
  3. __libc_atexit
    • _IO_cleanup — .
  4. _exit — .

_exit 231 ( sys_exit_group ), %rdi . .


Linux sys_exit . , , sys_exit_group . , , , sys_exit , [6].


, , "Hello, World!!!", C/C++, glibc . : , , setjmp, atexit...


, dot



[1] — http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
[2] — https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-r252.pdf
[3] — https://github.com/hjl-tools/x86-psABI/wiki/intel386-psABI-1.1.pdf
[4] — https://habrahabr.ru/post/128111/
[5] — https://dev.gentoo.org/~vapier/crt.txt
[6] — http://syprog.blogspot.ru/2012/03/linux-threads-through-magnifier-local.html
[7] — https://sourceware.org/glibc/wiki/Style_and_Conventions#Double-underscore_names_for_public_API_functions
[8] — https://itanium-cxx-abi.imtqy.com/cxx-abi/abi.html#dso-dtor



Source: https://habr.com/ru/post/J339698/


All Articles