【问题标题】:crash on fpu instructions and asm code do not workfpu 指令崩溃,asm 代码不起作用
【发布时间】:2012-01-29 11:50:24
【问题描述】:

对不起我的英语不好

我正在努力提高我的 asm 能力,我发现很容易 使用机器代码例程处理它的入口点 来自c代码

我就是这样用的

    char asmRoutineData2[]  =
    {
    0xC8, 0x00, 0x00, 0x00,         // enter 0, 0
    0xB8, 0xff, 0x00 ,0x00 ,0x00,    // mov eax, 65538
    0xC9,                           // leave
    0xc3                            // ret
    };

 int (*asmRoutine)(void) = (int (*)(void)) asmRoutineData;
 int ret = asmRoutine();

对于某些例程来说效果非常好 - 例如上面

其他一些不起作用:

1)我遇到了麻烦,我无法获取堆栈传递的值

这样的程序

    char asmRoutine_body[]  =
    {

    0xC8, 0x00, 0x00, 0x00,      //enter
    0x8B, 0x45, 0x08,          // mov eax, [ebp+8]
    0xC9,               //leave
    0xC3
    };

 int ( *asmRoutine)(int, int, int) = ( int (*)(int, int, int)) asmRoutine_body;
 int ret = asmRoutine(77,66,55);

据我所知应该有效,但它没有

我在 kompiler 生成的 asm 中查找,它似乎是正确的

mov       eax,offset _asmRoutineData
push      55
push      66
push      77
call      eax
add       esp,12  


    _asmRoutineData label byte
db  200     //enter
db  0
db  0
db  0       
db  139     //  mov  eax, dword [ebp+8H]  ; 8B. 45, 08
db  69
db  8
db  201       //leave
db  195        //ret

不知道出了什么问题(返回我预期的 77 以外的其他值(对于 ebp+12 ebp+16 或 66 或 55)

2) 第二个麻烦是这种调用机器码的方式 适用于我的算术指令,但它会使应用程序崩溃 (某种方式的系统异常)在 fpu 或 sse 指令上

为什么?以及我应该怎么做才能使它对我有用(我很想写汇编 例程这样)

冷杉

//编辑

这是 sse 例程,应该得到一个 float4* 向量 a 和 b 制作点积并将结果放入 float4* c (float4 是 4 个浮点数的结构或表)

(奇怪的是它应该只得到两个向量并返回一个浮点数 通过 eax 但我得到了如果可能形成互联网并且没有时间 测试并重写)

    /*
    enter   0, 0                                    ; 0034 _ C8, 0000, 00
    mov     eax, dword [ebp+8H]                     ; 0038 _ 8B. 45, 08
    mov     ebx, dword [ebp+0CH]                    ; 003B _ 8B. 5D, 0C
    mov     ecx, dword [ebp+10H]                    ; 003E _ 8B. 4D, 10
    movups  xmm0, oword [eax]                       ; 0041 _ 0F 10. 00
    movups  xmm1, oword [ebx]                       ; 0044 _ 0F 10. 0B
    mulps   xmm0, xmm1                              ; 0047 _ 0F 59. C1
    movhlps xmm1, xmm0                              ; 004A _ 0F 12. C8
    addps   xmm1, xmm0                              ; 004D _ 0F 58. C8
    movaps  xmm0, xmm1                              ; 0050 _ 0F 28. C1
    shufps  xmm1, xmm1, 1                           ; 0053 _ 0F C6. C9, 01
    addss   xmm0, xmm1                              ; 0057 _ F3: 0F 58. C1
    movss   dword [ecx], xmm0                       ; 005B _ F3: 0F 11. 01
    leave                                           ; 005F _ C9
    ret                                             ; 0060 _ C3
    */

    char asmDot_body[] =
    {
     0xC8, 0x00, 0x00, 0x00,

     0x8B, 0x45, 0x08,
     0x8B, 0x5D, 0x0C,
     0x8B, 0x4D, 0x10,

     0x0F, 0x10, 0x00,
     0x0F, 0x10, 0x0B,

     0x0F, 0x59, 0xC1,
     0x0F, 0x12, 0xC8,
     0x0F, 0x58, 0xC8,
     0x0F, 0x28, 0xC1,
     0x0F, 0xC6, 0xC9, 0x01,
     0xF3, 0x0F, 0x58, 0xC1,
     0xF3, 0x0F, 0x11, 0x01,
     0xC9,
     0xC3
     };


    void (*asmAddSSE)(float4*, float4*, float4*) = (void (*)(float4*, float4*, float4*)) asmDot_body;

    float4 a = {1,2,1,0};
    float4 b = {1,2,3,0};
    float4 c = {0,0,0,0};

    asmAddSSE(&a,&b,&c);

//编辑 L8R

找到了!它非常酷而且很棒 (传递参数以及 fpu 甚至 sse) 我很高兴

tnx necrolis 表示它正在你的系统上运行,

我开始尝试用编译器开关 tu 设置对齐和 也禁用一些,它是 -pr (使用 fastcall ) 已启用,我应该将其关闭

(有两个 compile.bat - 一个用于正常编译和 第二个用于 olso 生成程序集,并且在 第二个所以我写的asm代码还可以 - 但我的正常 compile.bat 生成的 fastcall 调用 ant 它会出错!)

【问题讨论】:

  • 您没有使用汇编程序。您正在编写机器代码。获取汇编程序。
  • 如何在调试器中逐条指令执行代码?这会告诉你你的代码有什么问题。
  • 没有调试器:/我也知道我正在编写机器代码 - 没有内联汇编,也不想与独立汇编作斗争(它要复杂得多)一些其他的如何调试和修理它?我不知道怎么了
  • @user982377 你刚才是不是说汇编比写机器代码更复杂

标签: c assembly x86 calling-convention


【解决方案1】:

您的第一个问题是您假设代码是可执行的,如果幸运的话,DEP 已关闭,您可以从堆栈中执行代码,但通常(99.99% 的时间)您需要 分配可执行内存来执行此操作。其次,像你这样写出纯机器代码是可怕的,并且容易出现错误,如果你觉得你不能使用你的编译器提供的内联汇编器,请改用 AsmJIT 之类的东西(或任何其他内存中的汇编器)。

但是,您的代码可以正常工作(使用__cdecl 调用时),一旦这些问题得到解决,它仍然不安全。 (我运行它并得到 77 的预期结果, 将它放入可执行内存)。在修复虚拟和绝对调用/跳远的过程中,您可能会遇到问题,这将使这更加变得更加复杂。

您在 FPU 和 SSE 指令上的崩溃很可能是对齐问题,但如果没有系统代码、程序集或您使用的 CPU,就无法判断,在这种情况下,最好使用调试器,例如作为 ollydbg(免费)并逐步执行代码。


半修正码:

static char asmRoutine_body[]  =
{

0xC8, 0x00, 0x00, 0x00,      //enter
0x8B, 0x45, 0x08,          // mov eax, [ebp+8]
0xC9,               //leave
0xC3
};

void* p = (void*)VirtualAlloc(NULL,sizeof(asmRoutine_body),MEM_COMMIT,PAGE_EXECUTE_READWRITE);
memcpy(p,asmRoutine_body,sizeof(asmRoutine_body));
int ( *asmRoutine)(int, int, int) = ( int (*)(int, int, int))p;
int ret = asmRoutine(77,66,55);
VirtualFree(p,sizeof(asmRoutine_body),MEM_RELEASE);
printf("%d\n",ret);

输出:77

【讨论】:

  • 你能解释一下“但是你的代码工作正常,(..)我运行它并得到了预期的结果” - 这是否意味着 asmRoutine(77,66,55) 的示例适用于你? MUCH TNX 用于对齐建议 - 我在这里使用旧的 pentium 4 / Win XP(和旧的 bo​​rland 55 - 适用于我的纯 winapi 游戏)
  • @user982377:是的,它工作正常(我得到 77),我实际上会添加使用的代码。你应该认真考虑使用更新的编译器......
  • 也许吧,但我只使用 c 和纯 winapi,它在我注意到的这些领域并没有过时(编译速度快,我喜欢它,有很好的编辑器等等)——至于 asm,tnx for信息,我也会尝试编辑我的帖子并包含一些对我来说崩溃的 sse 例程,您能否也检查一下它是否可以在您的系统上运行?
  • 至于对齐,我不确定我应该在这里尝试什么,但.. 参数推送到堆栈上?
  • @user982377:您的 float4 参数必须按照 x86 SSE 标准的要求与 16 字节边界对齐,否则您需要使用未对齐的加载/存储
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-09
  • 2013-08-13
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多