首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用NASM指向EFLAGS的C指针

使用NASM指向EFLAGS的C指针
EN

Stack Overflow用户
提问于 2016-09-13 12:50:58
回答 2查看 797关注 0票数 3

对于我们学校的一项任务,我需要编写一个C程序,它使用一个程序集进行16位的加法。除结果外,还应退还EFLAGS。

这是我的C程序:

代码语言:javascript
复制
int add(int a, int b, unsigned short* flags);//function must be used this way
int main(void){
    unsigned short* flags = NULL;
    printf("%d\n",add(30000, 36000, flags);// printing just the result
    return 0;
}

目前,该程序只是打印结果,而不是旗帜,因为我无法得到它们。

下面是我使用NASM的组装程序:

代码语言:javascript
复制
global add
section .text

add:
    push ebp    
    mov ebp,esp
    mov ax,[ebp+8]
    mov bx,[ebp+12]
    add ax,bx
    mov esp,ebp
    pop ebp
    ret

现在这一切都很顺利。但是,我不知道如何获得指针,该指针必须位于[ebp+16],指向标志寄存器。教授说我们必须使用pushfd命令。

我的问题就在于组装代码。我将修改C程序,在得到旗标的解决方案后,给出这些标志。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-13 16:07:59

通常,您只需要使用调试器来查看标志,而不是编写所有代码,将它们放入C变量中进行调试打印。特别是由于体面的调试器解码条件标志为您象征性,而不是或以及显示一个十六进制值。

您不必知道或关心标志中的哪个位是CF,哪个位是ZF。(这些信息也与编写真正的程序无关。我没有记住它,我只知道哪些标志是由jl这样的不同条件测试的。当然,很好的理解是,标志只是数据,您可以复制,保存/恢复,甚至修改(如果你想)

您的函数args和返回值是int,在您正在使用的System 32位x86 ABI中是32位。(链接到x86标记wiki中的ABI文档)。编写一个只查看其输入的低16位,并在输出的高16位中留下高垃圾的函数是一个错误。原型中的int返回值告诉编译器,EAX的所有32位都是返回值的一部分。

正如迈克尔指出的,你似乎在说你的作业需要使用一个16位的加法。这将产生进位,溢出和其他条件与不同的输入,如果你看完整的32位。(顺便说一下,本文很好地解释了进位与溢出的关系。.)

我会这么做的。请注意添加的32位操作数大小。

代码语言:javascript
复制
global add
section .text

add:
    push  ebp    
    mov   ebp,esp      ; stack frames are optional, you can address things relative to ESP

    mov   eax, [ebp+8]    ; first arg: No need to avoid loading the full 32 bits; the next insn doesn't care about the high garbage.
    add   ax,  [ebp+12]   ; low 16 bits of second arg.  Operand-size implied by AX

    cwde                  ; sign-extend AX into EAX

    mov   ecx, [ebp+16]   ; the pointer arg
    pushf                 ; the simple straightforward way
    pop   edx
    mov   [ecx], dx       ; Store the low 16 of what we popped.  Writing  word [ecx]  is optional, because dx implies 16-bit operand-size
                          ; be careful not to do a 32-bit store here, because that would write outside the caller's object.

    ;  mov esp,ebp   ; redundant: ESP is still pointing at the place we pushed EBP, since the push is balanced by an equal-size pop
    pop ebp
    ret

CWDE ( 8086 CBW指令的16 -> 32形式)与CWD ( AX -> DX:AX 8086指令)不相混淆。如果您不使用AX,那么MOVSX / MOVZX是一个很好的方法。

的乐趣方式:不使用默认操作数大小和执行32位推送和pop,我们可以做一个16位弹出直接进入目标内存地址。这将使堆栈不平衡,因此我们可以再次取消对mov esp, ebp的注释,或者使用一个16位的pushf (带有操作数大小的前缀,根据医生的说法使其只按低16标志,而不是32位EFLAGS)。

代码语言:javascript
复制
; What I'd *really* do: maximum efficiency if I had to use the 32-bit ABI with args on the stack, instead of args in registers
global add
section .text

add:
    mov   eax, [esp+4]    ; first arg, first thing above the return address
    add   ax,  [esp+8]    ; second arg
    cwde                  ; sign-extend AX into EAX

    mov   ecx, [esp+12]   ; the pointer

    pushfw                     ; push the low 16 of FLAGS
    pop     word [ecx]         ; pop into memory pointed to by unsigned short* flags

    ret

PUSHFW和POP WORD都将使用操作数大小的前缀进行组装。来自objdump -Mintel的输出,它使用与NASM略有不同的语法:

代码语言:javascript
复制
  4000c0:       66 9c                   pushfw 
  4000c2:       66 8f 01                pop    WORD PTR [ecx]

PUSHFW与o16 PUSHF相同。在NASM中,o16应用操作数大小前缀.

如果您只需要低的8个标志(不包括),您可以使用拉赫夫将标志加载到AH中并将其存储起来。

直接进入目的地的PUSHFing不是我推荐的。通常,将堆栈临时指向某个随机地址是不安全的。带有信号处理程序的程序将异步地使用堆栈下面的空间。这就是为什么在将堆栈空间与sub esp, 32或其他任何东西一起使用之前,您必须保留堆栈空间,即使您不打算通过在堆栈上推更多的东西来覆盖它的函数调用。唯一的例外是当您有一个红区时。

你打电话来:

您正在传递一个空指针,因此当然您的asm函数分段错误。传递本地地址,以便将函数存储到某个位置。

代码语言:javascript
复制
int add(int a, int b, unsigned short* flags);

int main(void) {
    unsigned short flags;
    int result = add(30000, 36000, &flags);
    printf("%d %#hx\n", result, flags);
    return 0;
}
票数 5
EN

Stack Overflow用户

发布于 2016-09-13 14:57:10

这只是一个简单的方法。我没有测试,但你应该有个主意.

只需将ESP设置为指针值,将其增加2(即使是32位arch),PUSHF如下所示:

代码语言:javascript
复制
global add
section .text

add:
    push ebp    
    mov ebp,esp
    mov ax,[ebp+8]
    mov bx,[ebp+12]
    add ax,bx
    ; --- here comes the mod
    mov esp, [ebp+16]      ; this will set ESP to the pointers address "unsigned short* flags"
    lea esp, [esp+2]       ; adjust ESP to address above target
    db 66h                 ; operand size prefix for 16-bit PUSHF (alternatively 'db 0x66', depending on your assembler
    pushf                  ; this will save the lower 16-bits of EFLAGS to WORD PTR [EBP+16] = [ESP+2-2]
    ; --- here ends the mod
    mov esp,ebp
    pop ebp
    ret

这应该有效,因为PUSHF会使ESP减少2,然后将值保存到WORD PTR [ESP]。因此,必须在使用指针地址之前增加它。将ESP设置为适当的值使您能够命名PUSHF的直接目标。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39470571

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档