Mik*_*e S 5 x86 assembly flags alignment
我已经读过如果可以修改eflags第18位(AC - 对齐检查),你知道CPU是486或更新.在386上,该位抵抗修改.
我从这个站点解除了以下汇编代码并添加了详尽的注释(保留了奇怪的语法):
asm
    mov  bx,sx            ; Save the stack pointer to bx (is sx a typo or a real register?).
    and  sp,$fffc         ; Truncate the stack pointer to a 4-byte boundary.
    pushfl                ; Push the eflags register to the stack.
    pop  eax              ; Pop it into eax.
    mov  ecx,eax          ; Save the original eflags value into ecx.
    xor  eax,$40000       ; Flip bit 18 in eax.
    push eax              ; Push eax to the stack.
    popfl                 ; Pop modified value into eflags.
    pushfl                ; Push eflags back onto the stack.
    pop  eax              ; Pop it into eax.
    xor  eax,ecx          ; Get changed bits from the original value.
    setz al               ; Set al register to 1 if no bits changed (0 otherwise).
    and  sp,$fffc         ; Truncate the stack pointer to a 4-byte boundary.
    push ecx              ; Push ecx (original eflags) to stack.
    popfl                 ; Pop it into eflags to restore the original value.
    mov  sp,bx            ; Restore the original stack pointer.
end ['eax','ebx','ecx'];
如果al寄存器在结尾处设置为1(假设从一开始它不是更旧),则CPU是386,否则它是486或更新.我理解这一部分.
我不明白的是,为什么在进行标志修改测试之前必须将堆栈指针截断为4字节边界?我认为这意味着设置位18,因为它毕竟是对齐位...但是xor和0x40000将翻转该位而不管其值如何.换句话说,无论初始值如何,修改测试应该具有相同的结果,对吗?
如果答案是否定的,那么我最好[未受过教育]猜测"为什么","也许下面的推/弹指令可能会强制对齐?这会对齐一个先前未对齐的堆栈指针并导致对齐位从0翻转到1在这种情况下,成功的修改似乎不成功,反之亦然." (编辑:这肯定是不正确的,因为对齐位是强制执行而不是跟踪对齐.另外,我怀疑pop/push会强制对齐以前未对齐的堆栈.)
即使是这种情况,在测试之后再次对齐堆栈指针的目的是什么(在恢复原始eflags和堆栈指针之前)?它不应该在之前的4字节边界上吗?如果没有,那怎么能从推/弹4字节值改变?
简而言之,一些说明对我来说似乎是多余的,我觉得我必须遗漏一些重要的东西.谁能在这里解释一下吗?
(旁边的问题:第一行将"sx"中的值复制到bx中.我从未在任何地方看到过对sx寄存器的引用.它确实存在,还是一个错字?'x'键相当远从'p'键开始,至少在美国键盘上.)
编辑:既然已经回答了这个问题,我决定从代码中的两个对齐行中删除不正确的注释.我最初假设对齐堆栈会设置对齐位,我将其写入我的注释(问题的其余部分继续使用这个不正确的逻辑).相反,对齐检查位实际上是关于强制对齐(而不是跟踪它),因为flolo关于sigbus的答案表明.我决定修改评论,以避免让有类似问题的人感到困惑.
我的猜测很简单:代码不想使用 sigbus。如果未设置检查对齐方式,而您设置了它,则实际上启用了对齐方式检查(设置时有效)。当堆栈指针未与 4 字节边界对齐时,猜猜会发生什么?您获得了未对齐的内存访问,这导致了 sigbus。如果您不想让无效的内存访问发生(因为您只是想更改位以进行测试),则必须注意测试时的所有访问都假设最坏的情况(即:您已启用它,并且您的堆栈在未对齐之前,因为它不需要,因为到目前为止检查已禁用)。