x86 程序集:尝试反转打印数组时出现分段错误(核心已转储)
x86 Assembly: Segmentation Fault (Core dumped) while trying to reverse print array
在我的代码中,我试图反向打印一个数组。
我的两个主要想法是要么使用堆栈和 LIFO 属性 来执行此操作,要么使用循环作为从 10 到 0 零的索引以反向访问元素。由于堆栈方法的对齐问题,我选择了第二个。
我对汇编很陌生,希望能在这方面提供一些帮助,以了解我的错误在哪里。提前致谢!
DEFAULT REL
; external functions for inputs/outputs printf and scanf/printf
extern printf
extern scanf
section .data
prompt db "Entrez un entier : ",0
longIntFormat db "%ld",0
section .bss
entier resb 10 ; array of 10 integers
section.text
push rbp
mov rcx, 0
mov rdx, 0 ; initialise counter
lea rcx, [entier] ; load array into register rcx
; fills the array with user input
_getLoop:
; call printf
lea rdi,[prompt]
mov rax,0
call printf wrt ..plt
; call scanf
lea rdi,[longIntFormat]
lea rsi, [rcx + rdx] ; array + Index
mov rax,0
call scanf wrt ..plt
inc rdx ; inc. Index/counter
cmp rdx, 10
jl _getLoop ; While counter is less than 10 (size of array)
mov rcx, 0 ; set rcx to 0
mov rdx, 10 ; counter set to 10
lea rcx, [entier] ; load array into rcx
; print the array in reverse using the counter as Index
_printLoop:
; call printf
lea rdi, [rcx + rdx] ; rdi = [array + Index]
mov rax,0
call printf wrt ..plt
dec rdx
cmp rdx, 0 ; compare counter with 0
jge _printLoop ; Once 0 is reached the loop has gone through all the array
;restores registers
pop rbp
; returns 0 to C program
mov rax, 0
ret
错误较多,例如:
- 一个函数可以根据calling convention
改变几个寄存器
scanf
和 printf
的格式字符串语法略有不同。如果您打算修改 printf
输出(例如使用 \n
),您必须创建另一个格式字符串。
- 在您的循环中,您忘记将任何格式字符串传递给
printf
,只是一个整数的地址。 (printf 需要格式字符串并按值取整数。)
"%ld"
表示 "long integer"。在我的系统中,这是一个四字(8 个字节)。您严格来说只处理一个字节。
- 在调用函数之前,堆栈必须对齐到 16 的倍数。内核遵循 x86-64 System V ABI 并确保在进程入口(通常入口点称为
_start
).如果您 push/pop,请确保在调用之前不要让堆栈未对齐。
_start
(进程入口点)不是函数;你不能 ret
从它。调用 glibc 的 exit
函数以确保刷新 stdio 缓冲区,或进行原始 _exit
系统调用。
section.text
缺少一个 space。它被解析为像 foo.bar:
这样的标签名称,而不是切换到 .text
部分的指令。所以你的代码最终在 .data
(或者可能 .bss
以某种方式),并且因为这些部分链接到不可执行的内存页面而出现段错误。
看看我更正后的 - 正在运行的 - 程序:
DEFAULT REL
; external functions for inputs/outputs printf and scanf/printf
extern printf, fflush
extern scanf
section .data
prompt db "Entrez un entier : ",0
longIntFormat db " %ld",0
section .bss
entier resq 10 ; array of 10 integers
global _start
section .text
_start:
;and rsp, -16 ; Align stack to 16 (the ABI already guarantees this for _start)
mov rbx, 0 ; initialise counter
; fills the array with user input
_getLoop:
; call printf
lea rdi,[prompt]
mov rax,0
call printf wrt ..plt
; call scanf
lea rdi,[longIntFormat]
lea rsi, [entier + rbx * 8] ; array + Index
mov rax,0
call scanf wrt ..plt
inc rbx ; inc. Index/counter
cmp rbx, 10
jl _getLoop ; While counter is less than 10 (size of array)
mov rbx, 9 ; counter set to 10
; print the array in reverse using the counter as Index
_printLoop:
; call printf
lea rdi,[longIntFormat]
mov rsi, [entier + rbx*8] ; rdi = [array + Index]
mov rax,0
call printf wrt ..plt
dec rbx
cmp rbx, 0 ; compare counter with 0
jge _printLoop
xor edi, edi ; RDI=0: all streams
call fflush wrt ..plt
mov rax,60 ; SYS_EXIT
mov rdi,0 ; Exitcode: RDI=0
syscall ; Call Linux64
在我的代码中,我试图反向打印一个数组。 我的两个主要想法是要么使用堆栈和 LIFO 属性 来执行此操作,要么使用循环作为从 10 到 0 零的索引以反向访问元素。由于堆栈方法的对齐问题,我选择了第二个。
我对汇编很陌生,希望能在这方面提供一些帮助,以了解我的错误在哪里。提前致谢!
DEFAULT REL
; external functions for inputs/outputs printf and scanf/printf
extern printf
extern scanf
section .data
prompt db "Entrez un entier : ",0
longIntFormat db "%ld",0
section .bss
entier resb 10 ; array of 10 integers
section.text
push rbp
mov rcx, 0
mov rdx, 0 ; initialise counter
lea rcx, [entier] ; load array into register rcx
; fills the array with user input
_getLoop:
; call printf
lea rdi,[prompt]
mov rax,0
call printf wrt ..plt
; call scanf
lea rdi,[longIntFormat]
lea rsi, [rcx + rdx] ; array + Index
mov rax,0
call scanf wrt ..plt
inc rdx ; inc. Index/counter
cmp rdx, 10
jl _getLoop ; While counter is less than 10 (size of array)
mov rcx, 0 ; set rcx to 0
mov rdx, 10 ; counter set to 10
lea rcx, [entier] ; load array into rcx
; print the array in reverse using the counter as Index
_printLoop:
; call printf
lea rdi, [rcx + rdx] ; rdi = [array + Index]
mov rax,0
call printf wrt ..plt
dec rdx
cmp rdx, 0 ; compare counter with 0
jge _printLoop ; Once 0 is reached the loop has gone through all the array
;restores registers
pop rbp
; returns 0 to C program
mov rax, 0
ret
错误较多,例如:
- 一个函数可以根据calling convention 改变几个寄存器
scanf
和printf
的格式字符串语法略有不同。如果您打算修改printf
输出(例如使用\n
),您必须创建另一个格式字符串。- 在您的循环中,您忘记将任何格式字符串传递给
printf
,只是一个整数的地址。 (printf 需要格式字符串并按值取整数。) "%ld"
表示 "long integer"。在我的系统中,这是一个四字(8 个字节)。您严格来说只处理一个字节。- 在调用函数之前,堆栈必须对齐到 16 的倍数。内核遵循 x86-64 System V ABI 并确保在进程入口(通常入口点称为
_start
).如果您 push/pop,请确保在调用之前不要让堆栈未对齐。 _start
(进程入口点)不是函数;你不能ret
从它。调用 glibc 的exit
函数以确保刷新 stdio 缓冲区,或进行原始_exit
系统调用。section.text
缺少一个 space。它被解析为像foo.bar:
这样的标签名称,而不是切换到.text
部分的指令。所以你的代码最终在.data
(或者可能.bss
以某种方式),并且因为这些部分链接到不可执行的内存页面而出现段错误。
看看我更正后的 - 正在运行的 - 程序:
DEFAULT REL
; external functions for inputs/outputs printf and scanf/printf
extern printf, fflush
extern scanf
section .data
prompt db "Entrez un entier : ",0
longIntFormat db " %ld",0
section .bss
entier resq 10 ; array of 10 integers
global _start
section .text
_start:
;and rsp, -16 ; Align stack to 16 (the ABI already guarantees this for _start)
mov rbx, 0 ; initialise counter
; fills the array with user input
_getLoop:
; call printf
lea rdi,[prompt]
mov rax,0
call printf wrt ..plt
; call scanf
lea rdi,[longIntFormat]
lea rsi, [entier + rbx * 8] ; array + Index
mov rax,0
call scanf wrt ..plt
inc rbx ; inc. Index/counter
cmp rbx, 10
jl _getLoop ; While counter is less than 10 (size of array)
mov rbx, 9 ; counter set to 10
; print the array in reverse using the counter as Index
_printLoop:
; call printf
lea rdi,[longIntFormat]
mov rsi, [entier + rbx*8] ; rdi = [array + Index]
mov rax,0
call printf wrt ..plt
dec rbx
cmp rbx, 0 ; compare counter with 0
jge _printLoop
xor edi, edi ; RDI=0: all streams
call fflush wrt ..plt
mov rax,60 ; SYS_EXIT
mov rdi,0 ; Exitcode: RDI=0
syscall ; Call Linux64