日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

第七章之main函数和启动例程

發(fā)布時(shí)間:2025/3/15 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第七章之main函数和启动例程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

main函數(shù)和啟動(dòng)例程?


為什么匯編程序的入口是_start,而C程序的入口是main函數(shù)呢?本節(jié)就來(lái)解釋這個(gè)問(wèn)題。在講例 18.1 “最簡(jiǎn)單的匯編程序”時(shí),我們的匯編和鏈接步驟是:

$ as hello.s -o hello.o
$ ld hello.o -o hello
以前我們常用gcc main.c -o main命令編譯一個(gè)程序,其實(shí)也可以分三步做,第一步生成匯編代碼,第二步生成目標(biāo)文件,第三步生成可執(zhí)行文件:

$ gcc -S main.c
$ gcc -c main.s
$ gcc main.o
-S選項(xiàng)生成匯編代碼,-c選項(xiàng)生成目標(biāo)文件,此外在第 2 節(jié) “數(shù)組應(yīng)用實(shí)例:統(tǒng)計(jì)隨機(jī)數(shù)”還講過(guò)-E選項(xiàng)只做預(yù)處理而不編譯,如果不加這些選項(xiàng)則gcc執(zhí)行完整的編譯步驟,直到最后鏈接生成可執(zhí)行文件為止。

?

這些選項(xiàng)都可以和-o搭配使用,給輸出的文件重新命名而不使用gcc默認(rèn)的文件名(xxx.c、xxx.s、xxx.o和a.out),例如gcc main.o -o main將main.o鏈接成可執(zhí)行文件main。先前由匯編代碼例 18.1 “最簡(jiǎn)單的匯編程序”生成的目標(biāo)文件hello.o我們是用ld來(lái)鏈接的,可不可以用gcc鏈接呢?試試看。

$ gcc hello.o -o hello
hello.o: In function `_start':
(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../lib/crt1.o:(.text+0x0): first defined here
/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../lib/crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status
提示兩個(gè)錯(cuò)誤:一是_start有多個(gè)定義,一個(gè)定義是由我們的匯編代碼提供的,另一個(gè)定義來(lái)自/usr/lib/crt1.o;二是crt1.o的_start函數(shù)要調(diào)用main函數(shù),而我們的匯編代碼中沒(méi)有提供main函數(shù)的定義。從最后一行還可以看出這些錯(cuò)誤提示是由ld給出的。由此可見(jiàn),如果我們用gcc做鏈接,gcc其實(shí)是調(diào)用ld將目標(biāo)文件crt1.o和我們的hello.o鏈接在一起。crt1.o里面已經(jīng)提供了_start入口點(diǎn),我們的匯編程序中再實(shí)現(xiàn)一個(gè)_start就是多重定義了,鏈接器不知道該用哪個(gè),只好報(bào)錯(cuò)。另外,crt1.o提供的_start需要調(diào)用main函數(shù),而我們的匯編程序中沒(méi)有實(shí)現(xiàn)main函數(shù),所以報(bào)錯(cuò)。

如果目標(biāo)文件是由C代碼編譯生成的,用gcc做鏈接就沒(méi)錯(cuò)了整個(gè)程序的入口點(diǎn)是crt1.o中提供的_start,它首先做一些初始化工作(以下稱為啟動(dòng)例程,Startup Routine),然后調(diào)用C代碼中提供的main函數(shù)。所以,以前我們說(shuō)main函數(shù)是程序的入口點(diǎn)其實(shí)不準(zhǔn)確,_start才是真正的入口點(diǎn),而main函數(shù)是被_start調(diào)用的。

我們繼續(xù)研究上一節(jié)的例 19.1 “研究函數(shù)的調(diào)用過(guò)程”。如果分兩步編譯,第二步gcc main.o -o main其實(shí)是調(diào)用ld做鏈接的,相當(dāng)于這樣的命令:

$ ld /usr/lib/crt1.o /usr/lib/crti.o main.o -o main -lc -dynamic-linker /lib/ld-linux.so.2
也就是說(shuō),除了crt1.o之外其實(shí)還有crti.o,這兩個(gè)目標(biāo)文件和我們的main.o鏈接在一起生成可執(zhí)行文件main。-lc表示需要鏈接libc庫(kù),在第 1 節(jié) “數(shù)學(xué)函數(shù)”講過(guò)-lc選項(xiàng)是gcc默認(rèn)的,不用寫(xiě),而對(duì)于ld則不是默認(rèn)選項(xiàng),所以要寫(xiě)上。-dynamic-linker /lib/ld-linux.so.2指定動(dòng)態(tài)鏈接器是/lib/ld-linux.so.2,稍后會(huì)解釋什么是動(dòng)態(tài)鏈接。

那么crt1.o和crti.o里面都有什么呢?我們可以用readelf命令查看。在這里我們只關(guān)心符號(hào)表,如果只看符號(hào)表,可以用readelf命令的-s選項(xiàng),也可以用nm命令。

$ nm /usr/lib/crt1.o?
00000000 R _IO_stdin_used
00000000 D __data_start
???????? U __libc_csu_fini
???????? U __libc_csu_init
???????? U __libc_start_main
00000000 R _fp_hw
00000000 T _start
00000000 W data_start
???????? U main
$ nm /usr/lib/crti.o
???????? U _GLOBAL_OFFSET_TABLE_
???????? w __gmon_start__
00000000 T _fini
00000000 T _init
U main這一行表示main這個(gè)符號(hào)在crt1.o中用到了,但是沒(méi)有定義(U表示Undefined),因此需要?jiǎng)e的目標(biāo)文件提供一個(gè)定義并且和crt1.o鏈接在一起。具體來(lái)說(shuō),在crt1.o中要用到main這個(gè)符號(hào)所代表的地址,例如有一條指令是push $符號(hào)main所代表的地址,但不知道這個(gè)地址是多少,所以在crt1.o中這條指令暫時(shí)寫(xiě)成push $0x0,等到和main.o鏈接成可執(zhí)行文件時(shí)就知道這個(gè)地址是多少了,比如是0x80483c4,那么可執(zhí)行文件main中的這條指令就被鏈接器改成了push $0x80483c4。鏈接器在這里起到符號(hào)解析(Symbol Resolution)的作用,在第 5.2 節(jié) “可執(zhí)行文件”我們看到鏈接器起到重定位的作用,這兩種作用都是通過(guò)修改指令中的地址實(shí)現(xiàn)的,鏈接器也是一種編輯器,vi和emacs編輯的是源文件,而鏈接器編輯的是目標(biāo)文件,所以鏈接器也叫Link Editor。T _start這一行表示_start這個(gè)符號(hào)在crt1.o中提供了定義,這個(gè)符號(hào)的類型是代碼(T表示Text)。我們從上面的輸出結(jié)果中選取幾個(gè)符號(hào)用圖示說(shuō)明它們之間的關(guān)系:

圖 19.3. C程序的鏈接過(guò)程

?


其實(shí)上面我們寫(xiě)的ld命令做了很多簡(jiǎn)化,gcc在鏈接時(shí)還用到了另外幾個(gè)目標(biāo)文件,所以上圖多畫(huà)了一個(gè)框,表示組成可執(zhí)行文件main的除了main.o、crt1.o和crti.o之外還有其它目標(biāo)文件,本書(shū)不做深入討論,用gcc的-v選項(xiàng)可以了解詳細(xì)的編譯過(guò)程:

$ gcc -v main.c -o main
Using built-in specs.
Target: i486-linux-gnu
...
?/usr/lib/gcc/i486-linux-gnu/4.3.2/cc1 -quiet -v main.c -D_FORTIFY_SOURCE=2 -quiet -dumpbase main.c -mtune=generic -auxbase main -version -fstack-protector -o /tmp/ccRGDpua.s
...
?as -V -Qy -o /tmp/ccidnZ1d.o /tmp/ccRGDpua.s
...
?/usr/lib/gcc/i486-linux-gnu/4.3.2/collect2 --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker /lib/ld-linux.so.2 -o main -z relro /usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.3.2/crtbegin.o -L/usr/lib/gcc/i486-linux-gnu/4.3.2 -L/usr/lib/gcc/i486-linux-gnu/4.3.2 -L/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.3.2/../../.. /tmp/ccidnZ1d.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-linux-gnu/4.3.2/crtend.o /usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../lib/crtn.o
鏈接生成的可執(zhí)行文件main中包含了各目標(biāo)文件所定義的符號(hào),通過(guò)反匯編可以看到這些符號(hào)的定義:

$ objdump -d main
main:???? file format elf32-i386


Disassembly of section .init:

08048274 <_init>:
?8048274:?55?????????????????? ?push?? %ebp
?8048275:?89 e5??????????????? ?mov??? %esp,%ebp
?8048277:?53?????????????????? ?push?? %ebx
...
Disassembly of section .text:

080482e0 <_start>:
?80482e0:?31 ed??????????????? ?xor??? %ebp,%ebp
?80482e2:?5e?????????????????? ?pop??? %esi
?80482e3:?89 e1??????????????? ?mov??? %esp,%ecx
...
08048394 <bar>:
?8048394:?55?????????????????? ?push?? %ebp
?8048395:?89 e5??????????????? ?mov??? %esp,%ebp
?8048397:?83 ec 10???????????? ?sub??? $0x10,%esp
...
080483aa <foo>:
?80483aa:?55?????????????????? ?push?? %ebp
?80483ab:?89 e5??????????????? ?mov??? %esp,%ebp
?80483ad:?83 ec 08???????????? ?sub??? $0x8,%esp
...
080483c4 <main>:
?80483c4:?8d 4c 24 04????????? ?lea??? 0x4(%esp),%ecx
?80483c8:?83 e4 f0???????????? ?and??? $0xfffffff0,%esp
?80483cb:?ff 71 fc???????????? ?pushl? -0x4(%ecx)
...
Disassembly of section .fini:

0804849c <_fini>:
?804849c:?55?????????????????? ?push?? %ebp
?804849d:?89 e5??????????????? ?mov??? %esp,%ebp
?804849f:?53?????????????????? ?push?? %ebx
crt1.o中的未定義符號(hào)main在main.o中定義了,所以鏈接在一起就沒(méi)問(wèn)題了。crt1.o還有一個(gè)未定義符號(hào)__libc_start_main在其它幾個(gè)目標(biāo)文件中也沒(méi)有定義,所以在可執(zhí)行文件main中仍然是個(gè)未定義符號(hào)。這個(gè)符號(hào)是在libc中定義的,libc并不像其它目標(biāo)文件一樣鏈接到可執(zhí)行文件main中,而是在運(yùn)行時(shí)做動(dòng)態(tài)鏈接:

操作系統(tǒng)在加載執(zhí)行main這個(gè)程序時(shí),首先查看它有沒(méi)有需要?jiǎng)討B(tài)鏈接的未定義符號(hào)。

如果需要做動(dòng)態(tài)鏈接,就查看這個(gè)程序指定了哪些共享庫(kù)(我們用-lc指定了libc)以及用什么動(dòng)態(tài)鏈接器來(lái)做動(dòng)態(tài)鏈接(我們用-dynamic-linker /lib/ld-linux.so.2指定了動(dòng)態(tài)鏈接器)。

動(dòng)態(tài)鏈接器在共享庫(kù)中查找這些符號(hào)的定義,完成鏈接過(guò)程。

了解了這些原理之后,現(xiàn)在我們來(lái)看_start的反匯編:

...
Disassembly of section .text:

080482e0 <_start>:
?80482e0:?????? 31 ed?????????????????? xor??? %ebp,%ebp
?80482e2:?????? 5e????????????????????? pop??? %esi
?80482e3:?????? 89 e1?????????????????? mov??? %esp,%ecx
?80482e5:?????? 83 e4 f0??????????????? and??? $0xfffffff0,%esp
?80482e8:?????? 50????????????????????? push?? %eax
?80482e9:?????? 54????????????????????? push?? %esp
?80482ea:?????? 52????????????????????? push?? %edx
?80482eb:?????? 68 00 84 04 08????????? push?? $0x8048400
?80482f0:?????? 68 10 84 04 08????????? push?? $0x8048410
?80482f5:?????? 51????????????????????? push?? %ecx
?80482f6:?????? 56????????????????????? push?? %esi
?80482f7:?????? 68 c4 83 04 08????????? push?? $0x80483c4
?80482fc:?????? e8 c3 ff ff ff????????? call?? 80482c4 <__libc_start_main@plt>
...
首先將一系列參數(shù)壓棧,然后調(diào)用libc的庫(kù)函數(shù)__libc_start_main做初始化工作,其中最后一個(gè)壓棧的參數(shù)push $0x80483c4是main函數(shù)的地址,__libc_start_main在完成初始化工作之后會(huì)調(diào)用main函數(shù)。由于__libc_start_main需要?jiǎng)討B(tài)鏈接,所以這個(gè)庫(kù)函數(shù)的指令在可執(zhí)行文件main的反匯編中肯定是找不到的,然而我們找到了這個(gè):

Disassembly of section .plt:
...
080482c4 <__libc_start_main@plt>:
?80482c4:?????? ff 25 04 a0 04 08?????? jmp??? *0x804a004
?80482ca:?????? 68 08 00 00 00????????? push?? $0x8
?80482cf:?????? e9 d0 ff ff ff????????? jmp??? 80482a4 <_init+0x30>
這三條指令位于.plt段而不是.text段,.plt段協(xié)助完成動(dòng)態(tài)鏈接的過(guò)程。我們將在下一章詳細(xì)講解動(dòng)態(tài)鏈接的過(guò)程。

main函數(shù)最標(biāo)準(zhǔn)的原型應(yīng)該是int main(int argc, char *argv[]),也就是說(shuō)啟動(dòng)例程會(huì)傳兩個(gè)參數(shù)給main函數(shù),這兩個(gè)參數(shù)的含義我們學(xué)了指針以后再解釋。我們到目前為止都把main函數(shù)的原型寫(xiě)成int main(void),這也是C標(biāo)準(zhǔn)允許的,如果你認(rèn)真分析了上一節(jié)的習(xí)題,你就應(yīng)該知道,多傳了參數(shù)而不用是沒(méi)有問(wèn)題的,少傳了參數(shù)卻用了則會(huì)出問(wèn)題。

由于main函數(shù)是被啟動(dòng)例程調(diào)用的,所以從main函數(shù)return時(shí)仍返回到啟動(dòng)例程中,main函數(shù)的返回值被啟動(dòng)例程得到,如果將啟動(dòng)例程表示成等價(jià)的C代碼(實(shí)際上啟動(dòng)例程一般是直接用匯編寫(xiě)的),則它調(diào)用main函數(shù)的形式是:

exit(main(argc, argv));
也就是說(shuō),啟動(dòng)例程得到main函數(shù)的返回值后,會(huì)立刻用它做參數(shù)調(diào)用exit函數(shù)。exit也是libc中的函數(shù),它首先做一些清理工作,然后調(diào)用上一章講過(guò)的_exit系統(tǒng)調(diào)用終止進(jìn)程,main函數(shù)的返回值最終被傳給_exit系統(tǒng)調(diào)用,成為進(jìn)程的退出狀態(tài)。我們也可以在main函數(shù)中直接調(diào)用exit函數(shù)終止進(jìn)程而不返回到啟動(dòng)例程,例如:

#include <stdlib.h>

int main(void)
{
?exit(4);
}
這樣和int main(void) { return 4; }的效果是一樣的。在Shell中運(yùn)行這個(gè)程序并查看它的退出狀態(tài):

$ ./a.out?
$ echo $?
4
按照慣例,退出狀態(tài)為0表示程序執(zhí)行成功,退出狀態(tài)非0表示出錯(cuò)。注意,退出狀態(tài)只有8位,而且被Shell解釋成無(wú)符號(hào)數(shù),如果將上面的代碼改為exit(-1);或return -1;,則運(yùn)行結(jié)果為

$ ./a.out?
$ echo $?
255
注意,如果聲明一個(gè)函數(shù)的返回值類型是int,函數(shù)中每個(gè)分支控制流程必須寫(xiě)return語(yǔ)句指定返回值,如果缺了return則返回值不確定(想想這是為什么),編譯器通常是會(huì)報(bào)警告的,但如果某個(gè)分支控制流程調(diào)用了exit或_exit而不寫(xiě)return,編譯器是允許的,因?yàn)樗紱](méi)有機(jī)會(huì)返回了,指不指定返回值也就無(wú)所謂了。使用exit函數(shù)需要包含頭文件stdlib.h,而使用_exit函數(shù)需要包含頭文件unistd.h,

轉(zhuǎn)載于:https://www.cnblogs.com/invisible2/p/6874645.html

總結(jié)

以上是生活随笔為你收集整理的第七章之main函数和启动例程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: www.国产精品| 日本大尺度吃奶做爰久久久绯色 | 实拍澡堂美女洗澡av | 老司机在线永久免费观看 | 日韩一二三区在线观看 | 免费看污片网站 | 在线观看不卡av | 久久久久久久久久av | 少妇高潮久久久久久潘金莲 | 青青草香蕉 | 97麻豆视频 | 黑人巨大猛交丰满少妇 | 青青草伊人 | 亚洲视频国产视频 | 国产精品久久久久久一区二区 | 丝袜诱惑一区二区 | 嫩草视频在线观看视频 | 亚洲综合婷婷久久 | 欧美三级视频 | 99ri在线| 色哟哟视频在线观看 | 国产亚洲精品久久久久久久久动漫 | 免费看黄色一级视频 | ,亚洲人成毛片在线播放 | 四虎永久免费观看 | 日韩性大片 | 激情一区二区 | 夜色在线视频 | 亚洲v日本 | 久久亚洲日本 | 国产三级大片 | 国产正在播放 | 麻豆系列在线观看 | 高跟鞋丝袜猛烈xxxx | 婷婷一区二区三区 | 国产美女明星三级做爰 | 精品一区二区精品 | av一区二区不卡 | 黄视频在线免费看 | 国产成人精品二区三区亚瑟 | 精品久久久久久久久久久久久久久久久久 | 国产全是老熟女太爽了 | 色综合久久久久无码专区 | 国内自拍网站 | 久久久国产高清 | 国产在线精品观看 | 欧美专区第二页 | av动漫在线免费观看 | 久久99一区 | 在线不卡毛片 | 91免费网址 | 特种兵之深入敌后高清全集免费观看 | 亚洲 欧美 激情 另类 校园 | 大尺度摸揉捏胸床戏视频 | 5个黑人躁我一个视频 | 国产精品久久久久久久一区探花 | 国产日韩大片 | 国产精品视频一区二区三区不卡 | 日韩欧美99 | 亚洲视频大全 | 一本一道波多野结衣一区二区 | 免费毛片视频网站 | 亚洲国产一区二区在线观看 | 91日韩在线| 中文字幕乱码在线观看 | 久久久999 | 偷自在线 | 91羞羞网站 | 黄色变态网站 | 国内偷拍一区二区 | 人妻少妇精品一区二区三区 | 强行糟蹋人妻hd中文字幕 | 性色av免费 | 一二三不卡视频 | 午夜在线| 伊人福利在线 | 超碰xxx | 操极品| 亚洲 另类 春色 国产 | 日日撸夜夜撸 | 精品日本一区二区三区在线观看 | av大帝在线观看 | 肉色欧美久久久久久久免费看 | 男女午夜激情视频 | 欧美三级视频在线播放 | 日韩精品xxx| 国语一区二区 | 国产精品视频不卡 | 麻豆视频免费入口 | 男生和女生一起搞鸡 | 国产精品嫩草久久久久 | 亚洲精品一二三 | 波多野结衣视频网址 | 性欧美精品男男 | 日本天堂一区 | 国产婷婷精品 | 一级久久 | 逼特逼视频在线观看 | 致命魔术电影高清在线观看 |