【发布时间】:2021-02-15 17:46:26
【问题描述】:
我试图在 nasm 中创建一个函数,给定一个整数数组和数组的长度,它返回最小的整数。这是基于 CodeWars 问题"Find the smallest integer in the array"。我在 64 位 BlackArch Linux 上执行此操作。我的函数如下所示:
SECTION .text
global find_smallest_int
find_smallest_int:
; [rdi] is the first value in the array.
; We'll store the smallest value so far found
; in rax. The first value in the array is the
; smallest so far found, therefore we store it
; in rax.
mov rax, [rdi]
; rsi is the second argument to int find_smallest_int(int *, int)
; which represents the length of the array.
; Store it in rbx to be explicit.
mov rbx, rsi
loop:
; Check to see if we've reached the end of the array.
; If we have, we jump to the end of the function and
; return the smallest value (which should be whatever
; is in rax at the moment.
cmp rbx, 0
je end
; Subtract one from our counter. This started as
; the number of elements in the array - when it
; gets to 0, we'll have looped through the entire thing.
sub rbx, 1
; If rax is smaller than [rdi], we'll jump down to the
; rest of the loop. Only if rax is bigger than [rdi] will
; we reassign rax to be the new smallest-yet vaue.
cmp rax, [rdi]
jl postassign
assign:
; If we execute this code, it means rax was not less
; than [rdi]. Therefore, we can safely reassign
; rax to [rdi].
mov rax, [rdi]
postassign:
; Set rdi to point to the next value in the array
add rdi, 4
; if we get here, then we aren't finishing looping yet
; because rbx (the counter) hasn't eached 0 yet.
jmp loop
end:
ret
然后我通过以下 C 代码调用此函数:
extern int find_smallest_int(int *array, int size);
int main(void)
{
int nums[4] = {800, 300, 100, 11};
int ret = find_smallest_int(nums, 4);
return ret;
}
最后,我使用以下命令编译并运行整个程序:
#!/bin/bash
# Make an object file from my assembly code with nasm
nasm -f elf64 -o sum.o call_sum.s
# make an object file from my C code
gcc -O0 -m64 -c -o call_sum.o call_sum.c -g
# compile my two object files into an executable
gcc -O0 -m64 -o run sum.o call_sum.o -g
# Run the executable and get the output in the
# form of the exit code.
./run
echo $?
我没有得到最小的整数,而是得到 100 或 11(分别传递给汇编函数的整数数组的倒数第二个和最后一个成员)。我得到的结果似乎是完全随机的。我可以运行程序几次得到 11,然后再运行几次,然后开始得到 100。
如果有人能帮助我理解这种奇怪的行为,我将不胜感激。谢谢!
更新:我实现了 Jester 注释中的更改(使用 32 位寄存器来保存整数)并且它有效,但我真的不明白为什么。
【问题讨论】:
-
int是 4 个字节,但您在整个代码中使用了 8 个字节。使用eax而不是rax。也不要使用rbx,因为这是一个被调用者保存的寄存器,无论如何从rsi复制是没有意义的。和以前一样,你最好使用esi,因为那是另一个int。 -
@Jester 感谢您的及时回复!当你说我在整个代码中使用 8 个字节时,你指的是我使用 64 位寄存器,对吧?我应该仍然能够在 64 位寄存器中存储 4 字节值,不是吗?由于寄存器的高位只是空的(根据我的有限理解)
-
您应该更改为 32 位寄存器。这样就不需要 REX 前缀。当您访问内存时,您必须使用正确的寄存器大小。如果您从外部传递一个 int 但在函数内部读取 8 个字节,那么您刚刚调用了 UB
-
cmp rax, dword [rdi]不应该组合,因为没有这样的cmp版本。确实我的nasm说error: mismatch in operand sizes。 -
如果您仍然想了解原始代码错误的原因,您是否尝试过使用调试器单步执行它,观察寄存器的值? (这是使用汇编语言工作时必不可少的开发技术。)我认为它会带来一些启示。