• VS debug下为什么多此一举jmp函数地址?


    VS debug下为什么call 函数后,会jmp函数地址?多此一举?

    http://blog.csdn.net/viper/article/details/6332934

    在写跑在main之前的时候,碰到了很奇怪的问题。

    1. int initBreak()  
    2. {  
    3. DebugBreak();  
    4. return 0;  
    5. }  
    6.   
    7. typedef int (*pInit)();  
    8.   
    9. pInit start3 = 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={testC!__native_startup_lock (0041956c)}
    0:000> kPL
    ChildEBP RetAddr 
    0012ff34 0041272b MSVCR100D!_initterm_e(
    <function> ** pfbegin = 0x0041641c, 
    <function> ** 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
    00416624 0041118b 00000000 00000000 00000000
    0:000> dd start4 l4
    00416838 0041118b 00000000 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:projectminevs.net estc estc estc.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即可,代价是链接时间的变长,没有两全其美的方法。


    Keep it simple!
    作者:N3verL4nd
    知识共享,欢迎转载。
  • 相关阅读:
    海康威视web插件安装后还是不能看视频问题
    超详细解释 react,flux,redux 的概念与关系
    vue3和react虚拟DOM的diff算法区别
    leetcode 78. 子集 js 实现
    js 实现扁平数组转为树形结构数组及树形结构数组转为扁平数组
    vite 为什么比 webpack 快?
    实现一个简单版 Vue2 双向数据绑定
    leetcode 415. 字符串相加 js 实现
    transform rotate实现环形进度条
    leetcode 258. 各位相加 js 实现
  • 原文地址:https://www.cnblogs.com/lgh1992314/p/5834867.html
Copyright © 2020-2023  润新知