NASM汇编代码中的段错误

4

我的汇编器是YASM,我正在64位Linux上编码。

我使用yasm -f elf -m amd64 -g dwarf2 filename.asm 进行汇编,并使用ld进行链接。

我正在尝试实现选择排序。 rdirsi 指向strbuf2 resb 10数组的不同部分。为什么会发生分段错误?第105行和第106行执行完全相同的操作,为什么它崩溃在第106行而不是第105行?

我包含了代码的相关部分,以及它在崩溃时的gdbtui截图。

更新:计数器已修复。

; ====== Sorting begins here ======
; Register uses:
; bpl holds the current minimum value
; r8 holds the memory address of the current minimum value
; rdi points to the boundary of the "outer loop"
; rsi points to the boundary of the "inner loop"
sorting:
    mov rdi, strbuf2  ; outer loop pointer
    mov rsi, strbuf2+1  ; inner loop pointer
    mov rax, 1  ; inner loop counter
    mov rbx, 0  ; outer loop counter

innerloop:
    mov bpl, [rdi] ; assume beginning element of unsorted array is minimum

    ; store the value of first element of unsorted array
    mov dl, [rdi]

    ; compare the current small value with the value in rsi
    mov cl, [rsi]   
    cmp bpl, cl 
    jg  new_small

    inc rsi
    inc rax
    cmp rax, 9
    jle innerloop
    jg  innerloop_done

new_small:
    inc rax
    mov bpl, cl; save the new small value
    mov r8, rsi  ; save its index 
    inc rsi 
    cmp rax, 9
    jle     innerloop

innerloop_done:
    ; When the inner loop is completed...
    ; First, do the swap
    ; to swap r8 (target memory address)  with [rdi] (outer array boundary)
    mov dl, 0 ; initialise
    mov dl, [rdi]
    mov [rdi], bpl
    mov [r8], dl 

    inc rdi  ; move the outer loop pointer forward
    inc rsi  ; move the inner loop pointer forward
    inc rbx  ; increment the outer loop counter (the unsorted array becomes smaller)

    ; set the inner loop counter to the appropriate position
    mov rax, 1 
    add rax, rbx  ; now rax (inner loop counter)
                  ; will always be rbx+1 (outer loop counter + 1) 
    cmp rbx, 9  
    jle innerloop
; ====== Sorting ends here ======

段错误 gdb 输出


@Alex:糟糕,这一行在我的当前代码中不存在...看起来我粘贴了早期版本的代码。我已经更新了清单。感谢你指出这个问题。顺便说一下,现在可能是提出一个事实的好时机,即mov r9b,[rdi]会导致gdb中r9b的void值。我正在努力弄清楚为什么:( - InvalidBrainException
1
如何尽可能简单地使用C语言编写上述代码(不使用复杂表达式),使其在C中运行,然后将变量更改为寄存器,</>=更改为cmp/jb/jge等呢? - Alexey Frunze
@Alex:感谢您提出的建议。我已经在gdb中进行了大量调试,感觉逻辑上已经很接近了。但是,我的问题似乎源于我对这种该死的低级语言理解的不足,而且我不得不同时学习和重新学习。实际上,我先写了一份C程序,结果就变成了这样(哈哈)。也许如果我使用了C的“goto”而不是循环,会有所帮助吗?(呃!) - InvalidBrainException
另一个观察:mov [r8],r10将整个r10寄存器(8字节)写入保存在r8中的地址,您应该只写入一个字节。 - DCoder
@DCoder 我试图通过使用 r8b 而非 r8 来解决这个问题,但是在执行语句 mov [r8], r10 后,r8b 值为空。我在 gdb 中检查寄存器时发现了这一点。 - InvalidBrainException
显示剩余2条评论
2个回答

4
; store the value of first element in [small]
mov rdx, [rsi]
mov r9, rdx

; compare the current small value with the value in rsi
mov rcx, [rsi]  
cmp r9, rcx

你正在比较[rsi][rsi],它们将会相等。
此外,r8仅在new_small块内初始化 - 如果你从未进入该块,则写入[r8]不是一个好主意。

感谢指出我正在比较[rsi]和[rsi]。你能解释一下我是怎么从[r8]中读取数据的吗?据我所知,我只是对[r8]进行<em>写入</em>操作而不是读取它。而且我不知道寄存器在使用之前必须初始化。我以为我们可以直接使用它们? - InvalidBrainException
在按照预期将 mov rdx, [rsi] 替换为 mov rdx, [rdi] 后,程序不再出现段错误。但现在它只是无限循环... - InvalidBrainException
1
@Terribad:你是对的,你的代码正在向[r8]写入。我的错误。但是想想你在做什么——“我不知道r8保存了什么值(因为我从未初始化它),但让我们将其视为指针并写入该内存地址!”我会仔细查看为什么会无限循环并回复你。 - DCoder
我已经更新了源代码,使其不再无限循环,但还是感谢您的尝试。我正在继续修复它。在执行mov r9b,[rdi]这一行之后,我尝试使用p $r9b在gdb中打印r9b,但gdb显示它是“void”。 - InvalidBrainException
我认为我已经理解,在汇编语言中,“初始化”寄存器意味着“在使用之前知道它包含有效的、有意义的数据”。在所有高级编程之后,我的大脑一直被编程成只是认为初始化=将其设置为零,以便您不会得到垃圾数据。 - InvalidBrainException

3
我认为你可能陷入了具体实现的细节中,忘记了代码应该做什么。我建议你先用C实现代码,然后逐步将其改为类似ASM的形式,直到你可以完全用ASM编写它。
请注意从在C中实现的小巧、干净且易于理解的sortC1()到有些混乱但完全等效的类似ASM实现的sortAsm()之间的进展。使用您最喜欢的文件比较工具查看实现之间的更改。
以下是需要翻译的内容:
#include <stdio.h>
#include <string.h>

char strOriginal[11] = "8163045297";
char str[11];

void sortC1(void)
{
  int outIdx, inIdx, minIdx;
  char min, tmp;

  for (outIdx = 0; outIdx < 10; outIdx++)
  {
    minIdx = outIdx;
    min = str[minIdx];

    for (inIdx = outIdx; inIdx < 10; inIdx++)
    {
      if (min > str[inIdx])
      {
        minIdx = inIdx;
        min = str[minIdx];
      }
    }

    tmp = str[outIdx];
    str[outIdx] = min;
    str[minIdx] = tmp;
  }
}

void sortC2(void)
{
  char *outPtr, *inPtr, *minPtr;
  int outCnt, inCnt;
  char min, tmp;

  for (outPtr = str, outCnt = 0;
       outCnt < 10;
       outPtr++, outCnt++)
  {
    minPtr = outPtr;
    min = *minPtr;

    for (inPtr = outPtr, inCnt = 10 - outCnt;
         inCnt > 0;
         inPtr++, inCnt--)
    {
      if (min > *inPtr)
      {
        minPtr = inPtr;
        min = *minPtr;
      }
    }

    tmp = *outPtr;
    *outPtr = min;
    *minPtr = tmp;
  }
}

void sortC3(void)
{
  char *outPtr, *inPtr, *minPtr;
  int outCnt, inCnt;
  char min, tmp;

  outPtr = str;
  outCnt = 0;

  while (outCnt < 10)
  {
    minPtr = outPtr;
    min = *minPtr;

    inPtr = outPtr;
    inCnt = 10 - outCnt;

    while (inCnt > 0)
    {
      if (min > *inPtr)
      {
        minPtr = inPtr;
        min = *minPtr;
      }

      inPtr++;
      inCnt--;
    }

    tmp = *outPtr;
    *outPtr = min;
    *minPtr = tmp;

    outPtr++;
    outCnt++;
  }
}

void sortC4(void)
{
  char *outPtr, *inPtr, *minPtr;
  int outCnt, inCnt;
  char min, tmp;

  outPtr = str;
  outCnt = 0;

outerloop:

  minPtr = outPtr;
  min = *minPtr;

  inPtr = outPtr;
  inCnt = 10 - outCnt;

innerloop:

  if (min > *inPtr)
  {
    minPtr = inPtr;
    min = *minPtr;
  }

  inPtr++;
  inCnt--;

  if (inCnt > 0)
    goto innerloop;

  tmp = *outPtr;
  *outPtr = min;
  *minPtr = tmp;

  outPtr++;
  outCnt++;
  if (outCnt < 10)
    goto outerloop;
}

void sortAsm(void)
{
  char* rdi; // points to the boundary of the "outer loop"
  char* rsi; // points to the boundary of the "inner loop"
  char* r8; // holds the current minimum value
  char r9b; // holds the current minimum value
  char r10b; // temporary storage for character exchange
  long long rbx; // outer loop counter
  long long rax; // inner loop counter

  rdi = str; // initialize outer loop pointer
  rbx = 0; // initialize outer loop counter

outerloop:

  r8 = rdi; // assume current element of partially sorted array is minimum,
  r9b = *r8; // save its index and value

  rsi = rdi; // initialize inner loop pointer
  rax = 10; // initialize inner loop counter
  rax -= rbx;

innerloop:

  // compare the current small value with the value in [rsi]
  if (r9b > *rsi)
  {
    r8 = rsi; // save the new small value's index
    r9b = *r8; // save the new small value
  }

  rsi++; // move the inner loop pointer forward
  rax--; // decrement the inner loop counter
  if (rax > 0)
    goto innerloop;

  // When the inner loop is completed...
  // First, do the swap
  // to swap [r8] (target memory address) with [rdi] (outer array boundary)
  r10b = *rdi;
  *rdi = r9b;
  *r8 = r10b;

  rdi++; // move the outer loop pointer forward
  rbx++; // increment the outer loop counter
  if (rbx < 10)
    goto outerloop;
}

int main(void)
{
  strcpy(str, strOriginal);
  printf("before sorting: %s\n", str);
  sortC1();
  printf("after sorting : %s\n\n", str);

  strcpy(str, strOriginal);
  printf("before sorting: %s\n", str);
  sortC2();
  printf("after sorting : %s\n\n", str);

  strcpy(str, strOriginal);
  printf("before sorting: %s\n", str);
  sortC3();
  printf("after sorting : %s\n\n", str);

  strcpy(str, strOriginal);
  printf("before sorting: %s\n", str);
  sortC4();
  printf("after sorting : %s\n\n", str);

  strcpy(str, strOriginal);
  printf("before sorting: %s\n", str);
  sortAsm();
  printf("after sorting : %s\n\n", str);

  return 0;
}

输出结果:
before sorting: 8163045297
after sorting : 0123456789

before sorting: 8163045297
after sorting : 0123456789

before sorting: 8163045297
after sorting : 0123456789

before sorting: 8163045297
after sorting : 0123456789

before sorting: 8163045297
after sorting : 0123456789

感谢您提供了这个优秀的例子,展示了如何在C中模拟汇编代码。通过您的讲解,汇编似乎变得更加易于理解了。下一个程序计划要比当前的更加困难,我一定会采用这种方法。目前,我觉得离完成这个程序已经非常接近了。根据gdb的调试信息,搜索功能已经正常工作,但细节问题仍需要解决。例如,多亏了DCoder的帮助,我意识到每次交换时我写入了8个字节而不只是一个字节。 - InvalidBrainException
2
我可以提供另一种选择。看一下rax。一旦它达到最大值,你就不需要重新初始化它。考虑算法。 - Alexey Frunze
啊,现在真是/捂脸,简直不敢相信我居然错过了那个。再次感谢您。好吧,我承认我在某个时候失去了对算法的关注! :) 我已经更新了代码清单。 - InvalidBrainException
2
现在,rsi 不也有同样的问题吗? - Alexey Frunze
在你的第一个提示之后,我领会了其中的意思并开始寻找类似的问题。我注意到当处理最后一个元素时,rsi 总是会超出一个字节的边界,所以我采取了一些小技巧:“如果 rsi > 数组末尾,则将其减少。” 很高兴地说,现在我有了一个功能完备的程序,这在很大程度上要归功于你和 DCoder。非常感谢你指引我走向正确的方向! :) - InvalidBrainException

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接