VS debug下為什麼多此一舉jmp函式位址?

2021-09-01 20:19:38 字數 4197 閱讀 2136

vs debug下為什麼call 函式後,會jmp函式位址?多此一舉?

在寫跑在main之前

的時候,碰到了很奇怪的問題。

[cpp]view plain

copy

intinitbreak()

typedef

int(*pinit)();

pinitstart3=initbreak;

initbreak是函式名,start3

是指標,它們的值竟然不一樣。

開始學習c語言的時候,就知道函式名代表函式位址,可以被賦值給函式指標,方便後面的呼叫。為什麼這裡的值會不一樣了?

還是使用跑在main之前 (2)

**例子,繼續用windbg分析。

0:000> g

fri apr 15 17:03:10.492 2011 (utc + 8:00): breakpoint 0 hit

eax=00000000 ebx=7ffdf000 ecx=0041956c edx=00130000 esi=00000000 edi=00000000

eip=0f9186a6 esp=0012ff30 ebp=0012ff34 iopl=0 nv up ei pl zr na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

msvcr100d!_initterm_e+0x6:

0f9186a6 c745fc00000000 mov dword ptr [ebp-4],0 ss:0023:0012ff30=

0:000> kpl

childebp retaddr

0012ff34 0041272b msvcr100d!_initterm_e(

** pfbegin = 0x0041641c,

** pfend = 0x00416a40)+0x6

0012ff80 0041263f testc!__tmaincrtstartup(void)+0xdb

0012ff88 77773c45 testc!maincrtstartup(void)+0xf

0012ff94 77ce37f5 kernel32!basethreadinitthunk+0xe

0012ffd4 77ce37c8 ntdll!__rtluserthreadstart+0x70

0012ffec 00000000 ntdll!_rtluserthreadstart+0x1b

0:000> dd start l4

00416728 00411186 00411023 0041118b 00000000

0:000> dd start2 l4

00416208 00411186 00411023 0041118b 00000000

0:000> dd start3 l4

004166240041118b00000000 00000000 00000000

0:000> dd start4 l4

004168380041118b00000000 00000000 00000000

0:000> ln 41118b

(0041118b)testc!ilt+390(_initbreak) | (00411190) testc!ilt+395(__controlfp_s)

exact matches:

0:000> ln start3

(00416624) testc!start3 | (00416728) testc!start

exact matches:

testc!start3 = 0x0041118b

0:000> ln start4

(00416838) testc!start4 | (0041693c) testc!pinit

exact matches:

testc!start4 = 0x0041118b

0:000> ln initbreak

d:\project\mine\vs.net\testc\testc\testc.c(47)

(004120f0) testc!initbreak | (00412150) testc!main

exact matches:

testc!initbreak (void)

能夠看到,start3和start4均為0x0041118b,而函式initbreak則是004120f0,它們的值並不一樣,這是為什麼呢?

再前進一點點就有答案了,

0:000> u 41118b -8

testc!ilt+380(__rtc_initialize)+0x2:

00411183 250000e985 and eax,85e90000h

00411188 0e push cs

00411189 0000 add byte ptr [eax],al

testc!ilt+390(_initbreak):

0041118b e9600f0000 jmp testc!initbreak (004120f0)

testc!ilt+395(__controlfp_s):

00411190 e987300000 jmp testc!controlfp_s (0041421c)

testc!ilt+400(__stackoverflow):

00411195 e9d6040000 jmp testc!_stackoverflow (00411670)

testc!ilt+405(_getsystemtimeasfiletime:

0041119a e90d310000 jmp testc!getsystemtimeasfiletime (004142ac)

testc!ilt+410(_f1):

0041119f e96c0d0000 jmp testc!f1 (00411f10)

真相如此簡單,就是一條5個位元組的jmp指令。

另乙個問題隨之而來,編譯器為何如此做呢,不是降低效率,多此一舉嘛。google了一下,答案在此:

什麼是incremental link table呢?

假如乙個程式有連續兩個foo和bar (所謂連續,就是他們編譯連線之後函式體連續存放), foo入口位置在0x0400,長度為0x200個位元組,那麼bar入口就應該在0x0600 = 0x0400+0x0200。程式設計師在開發的時候總是頻繁的修改code然後build,假如程式設計師在foo裡面增加了一些內容,現在foo函式體占0x300個位元組了,bar的入口也就只好往後移0x100變成了0x0700,這樣就有乙個問題,如果foo在程式中被呼叫了n次,那麼linker不得不修改這n個函式呼叫點,雖然linker不嫌累,但是link時間長了,程式設計師會覺得不爽。所以msvc在debug版的build,不會讓各個函式體之間這麼緊湊,每個函式體後都有padding(全是彙編**int 3,作用是引發中斷,這樣因為古怪原因執行到不該執行的padding部分,會發生異常),有了這些padding,就可以一定程度上緩解上面提到的問題,不過當函式增加內容太多超過padding,還是有問題,怎麼辦呢?msvc在debug build中用上了incremental link table, ilt其實就是一串jmp語句,每個jmp語句對應乙個函式,jmp的目的地就是函式的入口點,和沒有ilt的區別是,現在對函式的呼叫不是直接call到函式入口點了,而是call到ilt中對應的位置,而這個位置上什麼也不做,直接jmp到函式中去。這樣的好處是,當乙個函式入口位址改變時,只要修改ilt中對應值就搞定了,用不著修改每乙個呼叫位置,用乙個冗餘的itl把時間複雜度從o(n)將為o(1),值得,當然debug版的二進位制檔案會稍大稍慢,release版不會用上ilt。

所以,想得到正確的結果,disable incremental linking即可,代價是鏈結時間的變長,沒有兩全其美的方法。

Linux下為什麼有platform匯流排

首先你要知道匯流排是幹什麼的?這裡就不貼上各種百科中的定義了。說一下我的理解吧 1 匯流排用於裝置間傳輸資料,為資料傳輸提供載體和協議。2 匯流排用於給新裝置提供介面。傳輸資料很好理解,那麼怎麼提供介面呢,這就要提到匯流排的控制器,匯流排控制器具有發現裝置插拔的能力。比如usb匯流排上你插入乙個裝置...

工作效率總是低下,為什麼呢

研發進度問題 大部分時候,我們無法輕鬆自如地控制一切,沒辦法完成那些始終縈繞在腦海的事情,是因為 如果你的某個任務能夠明確上述四點,一定可以高效地完成。我最近完成的兩件事就是如此。當我們找到了做某件事的意義 為什麼要做 就會從內在產生動力。當我們明確了預期結果,就能夠準確衡量什麼算是完成,就能定義出...

Linux下為什麼目錄的大小總是4096

檢視os系統塊的大小 root sbin tune2fs l dev sda1 檢視os系統頁的大小 oracle skate test getconf pagesize 4096 修改塊的大小 建立檔案系統時,可以指定塊的大小。如果將來在你的檔案系統中是一些比較大的檔案的話,使用較大的塊大小將得到...