基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

时间:2022-08-27 22:03:39
很久了,EBX有什么用, 看了很汇编代码, 几乎很少用到EBX

如果函数中修改了它, 会有影响吗?, 它的性质是否和EAX, ECX,EDX 一样是通用寄存器, 可以直接在函数中修改它, 而不需要保存与恢复.


写一段完美无损(只多两条指令)调用memcpy, 将指令地址保存在了ebx
这样通过VBA申明CopyMem方便调用cdel 的 memcpy



#pragma function(memcpy)
__declspec(naked) void __stdcall CopyMem(void * Dst, const void * Src, size_t Size)
{
__asm
{
pop ebx //保存调用 CopyMem 的 eip (下一条指令) 至 ebx
call dword ptr [memcpy] // call 会保存 eip 以及 esp 减4
mov dword ptr [esp], ebx //恢复栈区的 eip 数据
ret 08h
}
}
#pragma intrinsic(memcpy)

14 个解决方案

#1


单纯的汇编,用 ebx 的不会少吧;高级语言里,通常被作为寄存器变量或优化时用到的中间变量。
按照寄存器上的使用约定,这里用 ebx 是不大好理解,在函数里对其进行修改又没保护性动作,后果是很难预料的。

#2


引用 1 楼 zara 的回复:
单纯的汇编,用 ebx 的不会少吧;高级语言里,通常被作为寄存器变量或优化时用到的中间变量。
按照寄存器上的使用约定,这里用 ebx 是不大好理解,在函数里对其进行修改又没保护性动作,后果是很难预料的。


你理解错了吧
eax, ecx 和 edx 这三个通用寄存器肯定不是需要保存的. 你随便用C建一个无返回的void函数, 再看汇编, 编译器是根本不会保存.
当然 eax默认用于有返回值的函数.

我这里用ebx, 是因为memcpy 没有用到ebx

我发这个贴只是深入了解一下ebx, 虽然字面是 基址寄存器 意思

当然你不能说修改EBX后有难预料的后果, 必须给个例证.

#3


例证,很简单了,写个函数,里面定义上 8 个 int 类型变量,赋值了,输出它们,调用你那个 CopyMem(),然后再输出那些 int 的值。将这个代码,分别以速度优化和不优化生成可执行文件,分别运行这两个程序,看看输出结果。
还是不明白, cl /c /Fas /Od xx.cpp 及  cl /c /Fas /Ox xx.cpp 生成相应的汇编,看看汇编里对局部变量的处理上的不同,以及对 ebx 的涉及。

#4


引用 3 楼 zara 的回复:
例证,很简单了,写个函数,里面定义上 8 个 int 类型变量,赋值了,输出它们,调用你那个 CopyMem(),然后再输出那些 int 的值。将这个代码,分别以速度优化和不优化生成可执行文件,分别运行这两个程序,看看输出结果。
还是不明白, cl /c /Fas /Od xx.cpp 及  cl /c /Fas /Ox xx.cpp 生成相应的汇编,看看汇编里对局部变量的处理上的不同,以及对 ebx 的涉及。


基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

你错了, 不过你这段也对我有所提示
这两天在研究google-code-prettify时, 想修改关键字, 顺便看看 volatile.! 对我来说惊天发现
不管你DEBUG/RELEASE, 还是X优化. 函数的调用会告诉编译器, 4个通用寄存器会重写, 即不要将变量缓存到寄存器,因为函数(这段代码)可能会用到通用寄存器.

测试代码还没写, 大致如下
声明4个变量
4个变量入4个寄存器
printf 4个变量(printf应该也一样)
或 你的函数调用(注意:即使release也能调用用)
再次 printf 4个变量, (查看是不是从寄存器, 还是栈区)
基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?


#5


引用 2 楼 topses 的回复:
我这里用ebx, 是因为memcpy 没有用到ebx


但是调用CopyMem的上下文可能用到,你中间把ebx改了,后果不好说,可能没问题也可能很严重。另外这也与用的编译器有关。

#6


引用 5 楼 DelphiGuy 的回复:
Quote: 引用 2 楼 topses 的回复:

我这里用ebx, 是因为memcpy 没有用到ebx


但是调用CopyMem的上下文可能用到,你中间把ebx改了,后果不好说,可能没问题也可能很严重。另外这也与用的编译器有关。



是的, 修改ebx 隐藏的风险很严重.
那我的 CopyMem 在 __declspec(naked) 下怎么高效的写呢? 我可不想重新再将参数压栈出栈.

to: zara
刚才跟踪汇编, 是我错了, 但你没讲得很明白, 可能还是我太菜了

#7


搞几个小时,学到不少相关知识:
内存屏障--- asm volatile("" ::: "memory")
http://blog.csdn.net/kissmonx/article/details/9105823
C语言的那些小秘密之volatile
http://blog.csdn.net/bigloomy/article/details/6645810

#8


测试代码. 测试于VS2010.
void general_register_test(int ax, int bx, int cx, int dx) 
{
__asm
{
mov eax, 1
mov ebx, 2
mov ecx, 3
mov edx, 4
mov esi, 5
mov edi, 6
}
}

__declspec(naked) void general_register_naked_test(int ax, int bx, int cx, int dx)
{
__asm
{
mov eax, 1
mov ebx, 2
mov ecx, 3
mov edx, 4
mov esi, 5
mov edi, 6
ret
}
}

int main()
{
int ax=getchar();
int bx=getchar(), cx=getchar(), dx=getchar();
int di=getchar(), si=getchar();
ax++; bx++; cx++; dx++; di++; si++;
printf("初始值: %x,%x,%x,%x,%x,%x\n", ax, bx, cx, dx, di, si);

ax++; bx++; cx++; dx++; di++; si++;
general_register_test(ax, bx, cx, dx);
//general_register_naked_test(ax, bx, cx, dx);
printf("调用后: %x,%x,%x,%x,%x,%x\n", ax, bx, cx, dx, di, si);

return 0;
}



总结:
关于EAX, ECX, EDX.
在call 被调用函数前, 编译器(100%可能)作废寄存器EAX, ECX, EDX.内的数据(变量).
在call 被调用函数后, 编译器(100%可能)覆写寄存器EAX, ECX, EDX内的数据(变量).
关于EBX, EDI, ESI.
调用函数在call 被调用函数后, 编译器(100%可能)直接读取EBX, EDI, ESI.内的数据(变量), 而不会从栈区重取变量.
那么, 被调用函数如果有对EBX, ESI, EDI中的数据进行修改, 那它必须负责保存与恢复.
因此, 我们常常看到
函数汇编开始
push        ebx 
push        esi  
push        edi  
函数汇编结束
pop        ebx 
pop        esi  
pop        edi  

#9


我记得在编译学里面这种上下文相关的寄存器叫非杂合寄存器

放断某语言的参考文档吧,vc下也是这样我试过

13.1.10.1 寄存器协定 

   •  EAX、ECX、EDX 都是杂合(scratch)寄存器,并且会被函数破坏(destroy) 。  
   •  EBX、ESI、EDI、EBP 在跨函数调用时都需要被保护。  
   •  EFLAGS 在跨函数调用时被假定破坏了,除了值必须为向前的方向标志以外。  
   •  FPU 栈在调用一个函数时必须为空。  
   •  FPU 控制字在跨函数调用时必须被保护。  
   •  浮点返回值被返回在 FPU 栈里。即使不使用它们,也需要调用者将它们清除干净。  

#10


引用 6 楼 topses 的回复:
Quote: 引用 5 楼 DelphiGuy 的回复:

Quote: 引用 2 楼 topses 的回复:

我这里用ebx, 是因为memcpy 没有用到ebx


但是调用CopyMem的上下文可能用到,你中间把ebx改了,后果不好说,可能没问题也可能很严重。另外这也与用的编译器有关。



是的, 修改ebx 隐藏的风险很严重.
那我的 CopyMem 在 __declspec(naked) 下怎么高效的写呢? 我可不想重新再将参数压栈出栈.

to: zara
刚才跟踪汇编, 是我错了, 但你没讲得很明白, 可能还是我太菜了


调痈约定相同可以直接jmp直接这样写就可以了
__declspec(naked) void __cdecl CopyMem(void * Dst, const void * Src, size_t Size)
{
    __asm jmp dword ptr [memcpy]       
}
或者

__declspec(naked) void __stdcall CopyMem(void * Dst, const void * Src, size_t Size)
{
    __asm
    {
    call    dword ptr [memcpy]        
    ret        12
    }
}

#11


__declspec(naked) void __stdcall CopyMem(void * Dst, const void * Src, size_t Size)
{
    __asm
    {
    call    dword ptr [memcpy]        
    ret        12
    }
}

============================================================================
第二个写错了,貌似没别的好办法。

#12


引用 9 楼 u013550545 的回复:
我记得在编译学里面这种上下文相关的寄存器叫非杂合寄存器

放断某语言的参考文档吧,vc下也是这样我试过

13.1.10.1 寄存器协定 

   •  EAX、ECX、EDX 都是杂合(scratch)寄存器,并且会被函数破坏(destroy) 。  
   •  EBX、ESI、EDI、EBP 在跨函数调用时都需要被保护。  
   •  EFLAGS 在跨函数调用时被假定破坏了,除了值必须为向前的方向标志以外。  
   •  FPU 栈在调用一个函数时必须为空。  
   •  FPU 控制字在跨函数调用时必须被保护。  
   •  浮点返回值被返回在 FPU 栈里。即使不使用它们,也需要调用者将它们清除干净。  



非常感谢! 这个跟我的测试不谋而合. 
能说一下这是什么手册? 这样我少走一些弯路.基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

关于dll中调用cdecl函数然后公开(即达到转发器的效果). 最佳代码, 其实很简单.创建一个(静态)模块级变量, 用于保存EIP的执行指令, 然后4句代码.
1, pop 静态变量
2, call cdecl函数
4, push 静态变量
5, ret x
如果cdecl函数的参数就一两个直接push 参数好了, 如果参数在两个以上, 当然用这个, 即方便又快.

#13


引用 12 楼 topses 的回复:
Quote: 引用 9 楼 u013550545 的回复:

我记得在编译学里面这种上下文相关的寄存器叫非杂合寄存器

放断某语言的参考文档吧,vc下也是这样我试过

13.1.10.1 寄存器协定 

   •  EAX、ECX、EDX 都是杂合(scratch)寄存器,并且会被函数破坏(destroy) 。  
   •  EBX、ESI、EDI、EBP 在跨函数调用时都需要被保护。  
   •  EFLAGS 在跨函数调用时被假定破坏了,除了值必须为向前的方向标志以外。  
   •  FPU 栈在调用一个函数时必须为空。  
   •  FPU 控制字在跨函数调用时必须被保护。  
   •  浮点返回值被返回在 FPU 栈里。即使不使用它们,也需要调用者将它们清除干净。  



非常感谢! 这个跟我的测试不谋而合. 
能说一下这是什么手册? 这样我少走一些弯路.基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

关于dll中调用cdecl函数然后公开(即达到转发器的效果). 最佳代码, 其实很简单.创建一个(静态)模块级变量, 用于保存EIP的执行指令, 然后4句代码.
1, pop 静态变量
2, call cdecl函数
4, push 静态变量
5, ret x
如果cdecl函数的参数就一两个直接push 参数好了, 如果参数在两个以上, 当然用这个, 即方便又快.
http://download.csdn.net/detail/u013550545/9387407真抱歉,这几天都没上论坛。资料来自D语言参考手册上卷 第 13 章 D应用程序接口::13.1.10 函数调用协定::13.1.10.1寄存器协定,pdf上传了
附图
基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?
基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

#14


创建时间: 2016-01-13, 最后更新: 2016-01-13, 来源: 原创/翻译 版本: V1.0
说明备注: 全新翻译;
参考链接: https://msdn.microsoft.com/zh-cn/library/k1a8ss06(v=vs.100).aspx

    一般情况下,__asm区块开始时,您不应该假设寄存器将会有指定的值。寄存器值不一定会保留至不同的__asm区块。如果您结束一个在线码区块,并开始另一个,第二个区块寄存器不能依赖第一个区块保留的值。 __asm区块会继承正常控制流的寄存器值结果。

如果您使用__fastcall调用约定时,编译器在寄存器中传递函数参数,而不是在堆栈上。这可能会导致函数中创建 __asm 块出现问题,因为函数没有办法告诉哪个参数是哪个寄存器中。如果函数已经有在 EAX 接收一个参数,但又立即将其他部分存储在 EAX 中的,那么原始参数将会丢失。此外,任何 __fastcall 声明的函数,必须保留 ECX 寄存器.

若要避免此类寄存器的冲突,请不要使用 __fastcall 调用约定的函数去包含一个 __asm 块。如果使用 /Gr 编译器选项来指定默认的调用约定为 __fastcall ,那么函数声明必须使用 __cdecl 或 __stdcall 才能包含 __asm块(__cdecl 特性通知编译器为该函数使用 C 调用约定)。 如果您不使用 /Gr 编译,避免声明函数时使用 __fastcall 特性。

当使用 __asm 在 C/C++ 函数中编写汇编语言,您不需要保留 EAX、 EBX、 ECX、 EDX,ESI 或 EDI 寄存器。例如,示例 POWER2 (在“使用在线汇编编写函数”中)。power2 函数并不会保留在 EAX 寄存器中的值。但是,使用这些寄存器会影响代码质量,因为寄存器分配器就不能使用它们跨 __asm 块来存储值。此外,在在线汇编代码中使用 EBX、 ESI 或 EDI,你必须在函数 prologue(入口) 与 epilogue(出口),强制编译器来保存和还原这些寄存器。

__asm块中,应该保留对其他寄存器的使用 (例如 DS、 SS、 SP、 BP、 和标志寄存器) 。除非您有一些理由更改它们 (如切换堆栈),您应该保留 ESP 和 EBP 寄存器。请参阅“优化汇编代码”。

某些 SSE 类型需要堆栈 8 字节对齐方式,强制编译器将发出动态堆栈对齐方式的代码。只有在对齐方式之后,才能存取局部变量和函数参数,编译器会维护两个框架指针(ESP,EBP)。如果编译器执行框架指针省略 (FPO:frame pointer omission),它将会使用 EBP 和 ESP。如果编译器没有执行 FPO,它将会使用 EBX 和 EBP。如果函数需要动态堆栈对齐,为确保代码运行正常,在asm 代码中不要修改 EBX,因为它可能用来修改框架指针。要么函数中没有移动 8 个字节对齐的类型,要么避免使用 EBX。

注意
如果在线汇编代码使用指令 STD 或 CLD 修改了方向标志,则必须还原标志的原始值。


特意结个贴!

#1


单纯的汇编,用 ebx 的不会少吧;高级语言里,通常被作为寄存器变量或优化时用到的中间变量。
按照寄存器上的使用约定,这里用 ebx 是不大好理解,在函数里对其进行修改又没保护性动作,后果是很难预料的。

#2


引用 1 楼 zara 的回复:
单纯的汇编,用 ebx 的不会少吧;高级语言里,通常被作为寄存器变量或优化时用到的中间变量。
按照寄存器上的使用约定,这里用 ebx 是不大好理解,在函数里对其进行修改又没保护性动作,后果是很难预料的。


你理解错了吧
eax, ecx 和 edx 这三个通用寄存器肯定不是需要保存的. 你随便用C建一个无返回的void函数, 再看汇编, 编译器是根本不会保存.
当然 eax默认用于有返回值的函数.

我这里用ebx, 是因为memcpy 没有用到ebx

我发这个贴只是深入了解一下ebx, 虽然字面是 基址寄存器 意思

当然你不能说修改EBX后有难预料的后果, 必须给个例证.

#3


例证,很简单了,写个函数,里面定义上 8 个 int 类型变量,赋值了,输出它们,调用你那个 CopyMem(),然后再输出那些 int 的值。将这个代码,分别以速度优化和不优化生成可执行文件,分别运行这两个程序,看看输出结果。
还是不明白, cl /c /Fas /Od xx.cpp 及  cl /c /Fas /Ox xx.cpp 生成相应的汇编,看看汇编里对局部变量的处理上的不同,以及对 ebx 的涉及。

#4


引用 3 楼 zara 的回复:
例证,很简单了,写个函数,里面定义上 8 个 int 类型变量,赋值了,输出它们,调用你那个 CopyMem(),然后再输出那些 int 的值。将这个代码,分别以速度优化和不优化生成可执行文件,分别运行这两个程序,看看输出结果。
还是不明白, cl /c /Fas /Od xx.cpp 及  cl /c /Fas /Ox xx.cpp 生成相应的汇编,看看汇编里对局部变量的处理上的不同,以及对 ebx 的涉及。


基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

你错了, 不过你这段也对我有所提示
这两天在研究google-code-prettify时, 想修改关键字, 顺便看看 volatile.! 对我来说惊天发现
不管你DEBUG/RELEASE, 还是X优化. 函数的调用会告诉编译器, 4个通用寄存器会重写, 即不要将变量缓存到寄存器,因为函数(这段代码)可能会用到通用寄存器.

测试代码还没写, 大致如下
声明4个变量
4个变量入4个寄存器
printf 4个变量(printf应该也一样)
或 你的函数调用(注意:即使release也能调用用)
再次 printf 4个变量, (查看是不是从寄存器, 还是栈区)
基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?


#5


引用 2 楼 topses 的回复:
我这里用ebx, 是因为memcpy 没有用到ebx


但是调用CopyMem的上下文可能用到,你中间把ebx改了,后果不好说,可能没问题也可能很严重。另外这也与用的编译器有关。

#6


引用 5 楼 DelphiGuy 的回复:
Quote: 引用 2 楼 topses 的回复:

我这里用ebx, 是因为memcpy 没有用到ebx


但是调用CopyMem的上下文可能用到,你中间把ebx改了,后果不好说,可能没问题也可能很严重。另外这也与用的编译器有关。



是的, 修改ebx 隐藏的风险很严重.
那我的 CopyMem 在 __declspec(naked) 下怎么高效的写呢? 我可不想重新再将参数压栈出栈.

to: zara
刚才跟踪汇编, 是我错了, 但你没讲得很明白, 可能还是我太菜了

#7


搞几个小时,学到不少相关知识:
内存屏障--- asm volatile("" ::: "memory")
http://blog.csdn.net/kissmonx/article/details/9105823
C语言的那些小秘密之volatile
http://blog.csdn.net/bigloomy/article/details/6645810

#8


测试代码. 测试于VS2010.
void general_register_test(int ax, int bx, int cx, int dx) 
{
__asm
{
mov eax, 1
mov ebx, 2
mov ecx, 3
mov edx, 4
mov esi, 5
mov edi, 6
}
}

__declspec(naked) void general_register_naked_test(int ax, int bx, int cx, int dx)
{
__asm
{
mov eax, 1
mov ebx, 2
mov ecx, 3
mov edx, 4
mov esi, 5
mov edi, 6
ret
}
}

int main()
{
int ax=getchar();
int bx=getchar(), cx=getchar(), dx=getchar();
int di=getchar(), si=getchar();
ax++; bx++; cx++; dx++; di++; si++;
printf("初始值: %x,%x,%x,%x,%x,%x\n", ax, bx, cx, dx, di, si);

ax++; bx++; cx++; dx++; di++; si++;
general_register_test(ax, bx, cx, dx);
//general_register_naked_test(ax, bx, cx, dx);
printf("调用后: %x,%x,%x,%x,%x,%x\n", ax, bx, cx, dx, di, si);

return 0;
}



总结:
关于EAX, ECX, EDX.
在call 被调用函数前, 编译器(100%可能)作废寄存器EAX, ECX, EDX.内的数据(变量).
在call 被调用函数后, 编译器(100%可能)覆写寄存器EAX, ECX, EDX内的数据(变量).
关于EBX, EDI, ESI.
调用函数在call 被调用函数后, 编译器(100%可能)直接读取EBX, EDI, ESI.内的数据(变量), 而不会从栈区重取变量.
那么, 被调用函数如果有对EBX, ESI, EDI中的数据进行修改, 那它必须负责保存与恢复.
因此, 我们常常看到
函数汇编开始
push        ebx 
push        esi  
push        edi  
函数汇编结束
pop        ebx 
pop        esi  
pop        edi  

#9


我记得在编译学里面这种上下文相关的寄存器叫非杂合寄存器

放断某语言的参考文档吧,vc下也是这样我试过

13.1.10.1 寄存器协定 

   •  EAX、ECX、EDX 都是杂合(scratch)寄存器,并且会被函数破坏(destroy) 。  
   •  EBX、ESI、EDI、EBP 在跨函数调用时都需要被保护。  
   •  EFLAGS 在跨函数调用时被假定破坏了,除了值必须为向前的方向标志以外。  
   •  FPU 栈在调用一个函数时必须为空。  
   •  FPU 控制字在跨函数调用时必须被保护。  
   •  浮点返回值被返回在 FPU 栈里。即使不使用它们,也需要调用者将它们清除干净。  

#10


引用 6 楼 topses 的回复:
Quote: 引用 5 楼 DelphiGuy 的回复:

Quote: 引用 2 楼 topses 的回复:

我这里用ebx, 是因为memcpy 没有用到ebx


但是调用CopyMem的上下文可能用到,你中间把ebx改了,后果不好说,可能没问题也可能很严重。另外这也与用的编译器有关。



是的, 修改ebx 隐藏的风险很严重.
那我的 CopyMem 在 __declspec(naked) 下怎么高效的写呢? 我可不想重新再将参数压栈出栈.

to: zara
刚才跟踪汇编, 是我错了, 但你没讲得很明白, 可能还是我太菜了


调痈约定相同可以直接jmp直接这样写就可以了
__declspec(naked) void __cdecl CopyMem(void * Dst, const void * Src, size_t Size)
{
    __asm jmp dword ptr [memcpy]       
}
或者

__declspec(naked) void __stdcall CopyMem(void * Dst, const void * Src, size_t Size)
{
    __asm
    {
    call    dword ptr [memcpy]        
    ret        12
    }
}

#11


__declspec(naked) void __stdcall CopyMem(void * Dst, const void * Src, size_t Size)
{
    __asm
    {
    call    dword ptr [memcpy]        
    ret        12
    }
}

============================================================================
第二个写错了,貌似没别的好办法。

#12


引用 9 楼 u013550545 的回复:
我记得在编译学里面这种上下文相关的寄存器叫非杂合寄存器

放断某语言的参考文档吧,vc下也是这样我试过

13.1.10.1 寄存器协定 

   •  EAX、ECX、EDX 都是杂合(scratch)寄存器,并且会被函数破坏(destroy) 。  
   •  EBX、ESI、EDI、EBP 在跨函数调用时都需要被保护。  
   •  EFLAGS 在跨函数调用时被假定破坏了,除了值必须为向前的方向标志以外。  
   •  FPU 栈在调用一个函数时必须为空。  
   •  FPU 控制字在跨函数调用时必须被保护。  
   •  浮点返回值被返回在 FPU 栈里。即使不使用它们,也需要调用者将它们清除干净。  



非常感谢! 这个跟我的测试不谋而合. 
能说一下这是什么手册? 这样我少走一些弯路.基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

关于dll中调用cdecl函数然后公开(即达到转发器的效果). 最佳代码, 其实很简单.创建一个(静态)模块级变量, 用于保存EIP的执行指令, 然后4句代码.
1, pop 静态变量
2, call cdecl函数
4, push 静态变量
5, ret x
如果cdecl函数的参数就一两个直接push 参数好了, 如果参数在两个以上, 当然用这个, 即方便又快.

#13


引用 12 楼 topses 的回复:
Quote: 引用 9 楼 u013550545 的回复:

我记得在编译学里面这种上下文相关的寄存器叫非杂合寄存器

放断某语言的参考文档吧,vc下也是这样我试过

13.1.10.1 寄存器协定 

   •  EAX、ECX、EDX 都是杂合(scratch)寄存器,并且会被函数破坏(destroy) 。  
   •  EBX、ESI、EDI、EBP 在跨函数调用时都需要被保护。  
   •  EFLAGS 在跨函数调用时被假定破坏了,除了值必须为向前的方向标志以外。  
   •  FPU 栈在调用一个函数时必须为空。  
   •  FPU 控制字在跨函数调用时必须被保护。  
   •  浮点返回值被返回在 FPU 栈里。即使不使用它们,也需要调用者将它们清除干净。  



非常感谢! 这个跟我的测试不谋而合. 
能说一下这是什么手册? 这样我少走一些弯路.基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

关于dll中调用cdecl函数然后公开(即达到转发器的效果). 最佳代码, 其实很简单.创建一个(静态)模块级变量, 用于保存EIP的执行指令, 然后4句代码.
1, pop 静态变量
2, call cdecl函数
4, push 静态变量
5, ret x
如果cdecl函数的参数就一两个直接push 参数好了, 如果参数在两个以上, 当然用这个, 即方便又快.
http://download.csdn.net/detail/u013550545/9387407真抱歉,这几天都没上论坛。资料来自D语言参考手册上卷 第 13 章 D应用程序接口::13.1.10 函数调用协定::13.1.10.1寄存器协定,pdf上传了
附图
基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?
基础问题: EBX有什么用, 子函数中修改了它, 有影响吗?

#14


创建时间: 2016-01-13, 最后更新: 2016-01-13, 来源: 原创/翻译 版本: V1.0
说明备注: 全新翻译;
参考链接: https://msdn.microsoft.com/zh-cn/library/k1a8ss06(v=vs.100).aspx

    一般情况下,__asm区块开始时,您不应该假设寄存器将会有指定的值。寄存器值不一定会保留至不同的__asm区块。如果您结束一个在线码区块,并开始另一个,第二个区块寄存器不能依赖第一个区块保留的值。 __asm区块会继承正常控制流的寄存器值结果。

如果您使用__fastcall调用约定时,编译器在寄存器中传递函数参数,而不是在堆栈上。这可能会导致函数中创建 __asm 块出现问题,因为函数没有办法告诉哪个参数是哪个寄存器中。如果函数已经有在 EAX 接收一个参数,但又立即将其他部分存储在 EAX 中的,那么原始参数将会丢失。此外,任何 __fastcall 声明的函数,必须保留 ECX 寄存器.

若要避免此类寄存器的冲突,请不要使用 __fastcall 调用约定的函数去包含一个 __asm 块。如果使用 /Gr 编译器选项来指定默认的调用约定为 __fastcall ,那么函数声明必须使用 __cdecl 或 __stdcall 才能包含 __asm块(__cdecl 特性通知编译器为该函数使用 C 调用约定)。 如果您不使用 /Gr 编译,避免声明函数时使用 __fastcall 特性。

当使用 __asm 在 C/C++ 函数中编写汇编语言,您不需要保留 EAX、 EBX、 ECX、 EDX,ESI 或 EDI 寄存器。例如,示例 POWER2 (在“使用在线汇编编写函数”中)。power2 函数并不会保留在 EAX 寄存器中的值。但是,使用这些寄存器会影响代码质量,因为寄存器分配器就不能使用它们跨 __asm 块来存储值。此外,在在线汇编代码中使用 EBX、 ESI 或 EDI,你必须在函数 prologue(入口) 与 epilogue(出口),强制编译器来保存和还原这些寄存器。

__asm块中,应该保留对其他寄存器的使用 (例如 DS、 SS、 SP、 BP、 和标志寄存器) 。除非您有一些理由更改它们 (如切换堆栈),您应该保留 ESP 和 EBP 寄存器。请参阅“优化汇编代码”。

某些 SSE 类型需要堆栈 8 字节对齐方式,强制编译器将发出动态堆栈对齐方式的代码。只有在对齐方式之后,才能存取局部变量和函数参数,编译器会维护两个框架指针(ESP,EBP)。如果编译器执行框架指针省略 (FPO:frame pointer omission),它将会使用 EBP 和 ESP。如果编译器没有执行 FPO,它将会使用 EBX 和 EBP。如果函数需要动态堆栈对齐,为确保代码运行正常,在asm 代码中不要修改 EBX,因为它可能用来修改框架指针。要么函数中没有移动 8 个字节对齐的类型,要么避免使用 EBX。

注意
如果在线汇编代码使用指令 STD 或 CLD 修改了方向标志,则必须还原标志的原始值。


特意结个贴!