Assembly NASM组件二进制搜索中的Seg故障

Assembly NASM组件二进制搜索中的Seg故障,assembly,nasm,binary-search,Assembly,Nasm,Binary Search,嗨,我正在努力找出为什么我的二进制搜索实现是seg错误(我是NASM汇编的新手) 对不起,我知道这不是一个多MVP,但我想不出一个适当的方式,使一个在组装 %define n [ebp+8] %define list [ebp+12] %define low [ebp+16] %define high [ebp+20] ; Parameters loading from the stack binary_search: push ebp mov ebp, esp mov ebx,

嗨,我正在努力找出为什么我的二进制搜索实现是seg错误(我是NASM汇编的新手)

对不起,我知道这不是一个多MVP,但我想不出一个适当的方式,使一个在组装

%define n [ebp+8]
%define list [ebp+12]
%define low [ebp+16]
%define high [ebp+20] ; Parameters loading from the stack
binary_search:
  push ebp
  mov ebp, esp

  mov ebx, n
  mov edi, list
  mov ecx, low
  mov edx, high

  cmp ecx, edx ; if (low > high)
  jg .FIRST

  mov eax, edx ; Next few lines for mid = low + (high - low)/2
  sub eax, ecx
  sar eax, 1 ; Will this give an appropriate index? (i.e is it floor division?)
  add eax, ecx
  lea esi, [edi+eax*4] ;Getting list[mid]
  cmp ebx, [esi]; if (n == list[mid])
  je .END
  jl .SECOND
  jg .THIRD

  jmp .END

.FIRST:
  mov eax, -1 ; return -1
  jmp .END

.SECOND:
  mov edx, eax ; return middle - 1
  dec edx
  jmp .CONTINUE

.THIRD:
  mov ecx, eax ; low = mid - 1
  dec ecx
  jmp .CONTINUE

.CONTINUE:
  push edx
  push ecx
  push edi
  push esi
  push ebx
  call binary_search ; recursive call, causing the segfault.
  pop ebx
  pop esi
  pop edi
  pop ecx
  pop edx
  jmp .END

.END:
  mov esp, ebp
  pop ebp
  ret
在注释了不同的部分之后,我确定这肯定与我对二进制搜索的递归调用有关,这是导致seg故障的原因。(在内部找到。继续) 我在二进制搜索体中搞砸了什么,它不符合多个递归调用

二进制搜索算法:

binary_search(n, list, low, high)

    if (low > high)
        return -1

    mid = low + (high - low) / 2

    if (n == list[mid])
       return middle;
    if (n < list[mid])
       high = mid - 1
    else
        low = mid + 1

    return binary_search(n, list, low, high)



二进制搜索(n,列表,低,高)
如果(低>高)
返回-1
中=低+(高-低)/2
如果(n==列表[mid])
返回中间;
如果(n
我知道这很难,谢谢:)


编辑:其32位模式

此部分定义函数的协议:

%define n [ebp+8]
%define list [ebp+12]
%define low [ebp+16]
%define high [ebp+20] ; Parameters loading from the stack
binary_search:
  push ebp
  mov ebp, esp
dword
[ebp]
将保存旧的
ebp
值,dword
[ebp+4]
将保存旧的
eip
,然后您的参数将随之返回。它们是n、list、low和high,按此顺序排列。当堆栈向下增长时,您需要按顺序推送到堆栈,以便首先推送最高的寻址参数(恰好是“高”),然后推送第二高(低),依此类推(列表,n,
eip
ebp

下一部分确定用于保存从这些堆栈帧参数初始化的变量的寄存器:

  mov ebx, n
  mov edi, list
  mov ecx, low
  mov edx, high
(在递归调用之前,
ecx
edx
会被修改。但它们仍然充当该代码中的“低”和“高”变量。)

现在检查递归调用站点。基本上,您希望将正在使用的所有寄存器再次传递给函数<代码>ebx
=n,
edi
=list,
ecx
=low,
edx
=high。这就是你要做的:

.CONTINUE:
  push edx
  push ecx
  push edi
  push esi
  push ebx
  call binary_search ; recursive call, causing the segfault.
  pop ebx
  pop esi
  pop edi
  pop ecx
  pop edx
如果将推送变量与函数协议预期的堆栈帧参数匹配,则会得到:

  push edx    ; (unused)
  push ecx    ; high
  push edi    ; low
  push esi    ; list
  push ebx    ; n
  call binary_search
esi
表示的变量仅在函数内部使用。它不需要传递给后续调用。更重要的是,它扰乱了函数的协议
edx
ecx
edi
被推到一个堆栈槽上,每个堆栈槽都比它们应该的要高,以便将这些变量传递给递归调用。解决方案是在此处删除
推送esi
弹出esi


您的代码还有一些潜在问题:

  • 正如我在评论中提到的,您应该使用
    inc-ecx
    而不是
    dec-ecx

  • 您的程序调用此函数的调用约定是什么?您似乎使用了很多寄存器,只保留了
    ebp
    esp

  • 如果调用约定允许无限制地更改堆栈帧参数内容,则可以使用“尾部调用”替换用于递归的文本
    调用
    ,将参数更改为当前传递给
    调用
    的参数。并重新使用整个堆栈帧。但在这一点上,它看起来非常类似于一个循环。也许您确实想要一个实际的(字面上的)递归实现。但是,尾部调用优化或迭代方法需要O(1)个堆栈空间,而不是O(log2n)


根据您的评论,算术右移具有向负无穷大舍入的效果,所以是的,是地板。你可以试着用一些例子来证实。@liamod:你部分是对的。但您将ebx作为n传递,esi作为list传递,edi作为high传递,ecx作为low传递。您也在推送和弹出edx,但不使用该值作为参数。我认为edi不适合这样做。不过,我必须查看更多详细信息才能找到修复方法。您应该删除
push-esi
pop-esi
。我认为这对于正确的递归调用是必要的,尽管可能不足以修复所有角落情况下的函数。我认为在
中。第三个
中,您可能应该使用
inc ecx
而不是
dec
。谢谢@ecm,主要问题是推/弹出esi,您可以更详细地解释为什么会出现这种情况吗?我会接受这个答案。谢谢@ecm提供的详细答案,是的,我更改了dec->inc,只是一个输入错误,我们使用的是cdecl,我在函数调用之前按下寄存器时没有保留所有寄存器,还是只是传递了它们?我知道这种方法的无效性,但它必须是递归的。再次感谢@liamod:如果函数不修改堆栈上的参数,它实际上是传递和保留这5个寄存器(或者4个,如果您删除
esi
ops)。但是,这只发生在递归调用周围。它不会发生在最外层的函数调用周围(此处未显示代码)。看来,。为此,您应添加以下内容:
mov-ebp,esp
之后的
push
每个寄存器,以及
mov-esp,ebp
之前的
pop