类中未调用的成员方法会被编译进EXE吗?
。编译器的解决方案
应该会先将类编译为 obj 文件,然后将这些obj进行逐个拼接来生成最终的EXE
拼接过程会将类中所有成员方法全部COPY到EXE进行封装吗
还是在最终的拼接中只提取需要的成员方法 根据其偏移地址来填补调用函数的地址或其他
//////////////////////////////////////////////////////////////////////////
// 类方法声明
//////////////////////////////////////////////////////////////////////////
class Test
{
public:
Test();
virtual ~Test();
public:
int m_a;
int m_b;
public:
int Add();
void Set(int a,int b);
};
//////////////////////////////////////////////////////////////////////////
// 类方法实现
//////////////////////////////////////////////////////////////////////////
int Test::Add()
{
return m_a + m_b;
}
void Test::Set(int a,int b)
{
m_a = a;
m_b = b;
}
//////////////////////////////////////////////////////////////////////////
// main函数调用
//////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
Test tt;
tt.Set(10,20);
printf("%d %d",tt.m_a,tt.m_b);
return 0;
}
//////////////////////////////////////////////////////////////////////////
// main函数结束
//////////////////////////////////////////////////////////////////////////
在Debug编译模式中可见:
@ILT+0(??1Test@@UAE@XZ):
00401005 jmp Test::~Test (004011c0)
@ILT+5(?Add@Test@@QAEHXZ):
0040100A jmp Test::Add (00401200) // 在Debug模式下确实是有Add这个未使用的方法的
@ILT+10(??_GTest@@UAEPAXI@Z):
0040100F jmp Test::`scalar deleting destructor' (00401150)
@ILT+15(??_GTest@@UAEPAXI@Z):
00401014 jmp Test::`scalar deleting destructor' (00401150)
@ILT+20(?Set@Test@@QAEXHH@Z):
00401019 jmp Test::Set (00401240)
@ILT+25(_main):
0040101E jmp main (00401050)
@ILT+30(??0Test@@QAE@XZ):
00401023 jmp Test::Test (00401110)
注意哦 先调用一下该方法 编译后EXE中一定会有该代码 想要测试的话 需要先Clear之后再进行搜索哦 就会发现VC已经不搭理他了
那 Release 版会是什么情况呢? 如果说Debug版是为了我们调试才全盘封装的,Release版是否会将这些无用代码阉割呢 ?
那有如何来得出最终结论呢?得出这个结论又有什么作用呢?
这里提供两种思路:
一种是在未调用方法中 输入一个特殊字符串,我们根据生成的文件中是否有该字符串来判定;
另一种是先调用下该函数(如Add),调用后用OD等工具动态跟踪出该函数编译后的源码,将其特征码提取
004010C0/$8B41 08 MOV EAX,DWORD PTR DS: ;m_b
004010C3|.8B51 04 MOV EDX,DWORD PTR DS: ;m_a
004010C6|.03C2 ADD EAX,EDX ;m_a + m_b
004010C8\.C3 RETN ;return eax
特征码为:8B 41 08 8B 51 04 03 C2 C3
然后用工具进行内存搜索 若有便可得出真的编译进去了 ~ //////////////////////////////////////////////////////////////////////////
// main 函数样本
//////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
Test tt;
tt.Set(10,20);
tt.Add();
printf("%d %d",tt.m_a,tt.m_b);
return 0;
}
//////////////////////////////////////////////////////////////////////////
// main 函数结束
//////////////////////////////////////////////////////////////////////////
00401000/$6A FF PUSH -1
00401002|.68 A8684000 PUSH test.004068A8 ;SE handler installation
00401007|.64:A1 0000000>MOV EAX,DWORD PTR FS: ;开始处理异常
0040100D|.50 PUSH EAX
0040100E|.64:8925 00000>MOV DWORD PTR FS:,ESP
00401015|.83EC 0C SUB ESP,0C ;把ESP向上移三格
00401018|.8D4C24 00 LEA ECX,DWORD PTR SS:
0040101C|.E8 5F000000 CALL test.00401080 ;构造
00401021|.6A 14 PUSH 14
00401023|.6A 0A PUSH 0A
00401025|.8D4C24 08 LEA ECX,DWORD PTR SS: ;以为压两个参数 这里 ESP+8 为this指针
00401029|.C74424 1C 000>MOV DWORD PTR SS:,0 ;给 0 做什么
00401031|.E8 9A000000 CALL test.004010D0 ;tt.Set()
00401036|.8D4C24 00 LEA ECX,DWORD PTR SS: ;这里ECX位置是虚表
0040103A|.E8 81000000 CALL test.004010C0 ;tt.Add
0040103F|.8B4424 08 MOV EAX,DWORD PTR SS:
00401043|.8B4C24 04 MOV ECX,DWORD PTR SS:
00401047|.50 PUSH EAX
00401048|.51 PUSH ECX
00401049|.68 40804000 PUSH test.00408040 ;ASCII "%d %d"
0040104E|.E8 9D000000 CALL test.004010F0
00401053|.83C4 0C ADD ESP,0C ;自动平衡堆栈
00401056|.8D4C24 00 LEA ECX,DWORD PTR SS:
0040105A|.C74424 14 FFF>MOV DWORD PTR SS:,-1 ;ESP+14 给 -1 做什么
00401062|.E8 49000000 CALL test.004010B0
00401067|.8B4C24 0C MOV ECX,DWORD PTR SS: ;??
0040106B|.33C0 XOR EAX,EAX
0040106D|.64:890D 00000>MOV DWORD PTR FS:,ECX ;??
00401074|.83C4 18 ADD ESP,18
00401077\.C3 RETN
--------------------------------------------------
构造的时候 往虚表中传入了一个地址
构造函数:
00401080/$8BC1 MOV EAX,ECX
00401082|.C700 C0704000 MOV DWORD PTR DS:,test.004070C0// 其中0X004070C0 中存放 0X0401090
00401088\.C3 RETN
构造和析构都给一个奇怪的函数指针:
00401090 .56 PUSH ESI
00401091 .8BF1 MOV ESI,ECX
00401093 .E8 18000000 CALL test.004010B0
00401098 .F64424 08 01TEST BYTE PTR SS:,1
0040109D .74 09 JE SHORT test.004010A8
0040109F .56 PUSH ESI
004010A0 .E8 51040000 CALL test.004014F6
004010A5 .83C4 04 ADD ESP,4
004010A8 >8BC6 MOV EAX,ESI
004010AA .5E POP ESI
004010AB .C2 0400 RETN 4
004010AE 90 NOP
004010AF 90 NOP
004010B0/$C701 C0704000 MOV DWORD PTR DS:,test.004070C0
004010B6\.C3 RETN
析构函数:
004010B0/$C701 C0704000 MOV DWORD PTR DS:,test.004070C0
004010B6\.C3 RETN
这里一些代码还是没看明白 尤其是构造和析构时 给该对象虚表中一个特殊的函数指针
还有就是 main入口和结束时对异常链的处理 不太懂为什么是这些代码
/////////////////////////////////////////////////////////
//看一下入口的这点东西
/////////////////////////////////////////////////////////
00401000/$6A FF PUSH -1
00401002|.68 A8684000 PUSH test.004068A8 ;SE handler installation
00401007|.64:A1 0000000>MOV EAX,DWORD PTR FS: ;开始处理异常
0040100D|.50 PUSH EAX
0040100E|.64:8925 00000>MOV DWORD PTR FS:,ESP
00401015|.83EC 0C SUB ESP,0C ;把ESP向上移三格
00401018|.8D4C24 00 LEA ECX,DWORD PTR SS: ;ESP 已经到 -18 了
0040101C|.E8 5F000000 CALL test.00401080 ;构造
到构造时 ESP 已经退到 -18了
$-18 > 0000D9D2
$-14 >/0012FFC0
$-10 >|00403C44RETURN to test.00403C44 from kernel32.SetUnhandledExceptionFilter
$-C >|0012FFB0Pointer to next SEH record
$-8 >|004068A8SE handler
$-4 >|FFFFFFFF
$ ==> >|004015B5RETURN to test.<ModuleEntryPoint>+0B4 from test.00401000
--------------------------------------
Set() 函数
--------------------------------------
00401021|.6A 14 PUSH 14
00401023|.6A 0A PUSH 0A
00401025|.8D4C24 08 LEA ECX,DWORD PTR SS: ;因为压两个参数 这里 ESP+8 为this指针
00401029|.C74424 1C 000>MOV DWORD PTR SS:,0 ;给 0 做什么 // 这里的是是压入 main 的第一个参数
00401031|.E8 9A000000 CALL test.004010D0 ;tt.Set()
004010D0/$8B4424 04 MOV EAX,DWORD PTR SS:
004010D4|.8B5424 08 MOV EDX,DWORD PTR SS:
004010D8|.8941 04 MOV DWORD PTR DS:,EAX
004010DB|.8951 08 MOV DWORD PTR DS:,EDX
004010DE\.C2 0800 RETN 8
$-C > 00401036RETURN to test.00401036 from test.004010D0
$-8 > 0000000A
$-4 > 00000014
$ ==> > 004070C0test.004070C0
$+4 > 0000000A; m_a
$+8 > 00000014; m_b
看一下析构之后的代码
00401060|.8B4C24 10 MOV ECX,DWORD PTR SS: ;NEXT SEH record
00401064|.33C0 XOR EAX,EAX
00401066|.64:890D 00000>MOV DWORD PTR FS:,ECX ;出函数时还原异常链
0040106D|.83C4 1C ADD ESP,1C ;??
00401070\.C3 RETN 哈哈 原来这个东西就是函数内 所有变量的总空间大小 ~~
SUB ESP,0C
Debug 版要测定局部变量空间 需-44H
11: int main(int argc, char* argv[])
12: {
00401050 push ebp
00401051 mov ebp,esp
00401053 push 0FFh
00401055 push offset __ehhandler$_main (00413419)
0040105A mov eax,fs:
00401060 push eax
00401061 mov dword ptr fs:,esp
00401068 sub esp,50h // here 真是的 大小应该为 50-44=0c
0040106B push ebx
0040106C push esi
0040106D push edi
0040106E lea edi,
00401071 mov ecx,14h
00401076 mov eax,0CCCCCCCCh
0040107B rep stos dword ptr
// ps : 在Debug模式下 为调用的函数也是不会被编译的 :loveliness:
页:
[1]