在这个帖子How do i read single character input from keyboard using nasm (assembly) under ubuntu?之后,我正在尝试编译一个回显NASM输入的程序。 我已经制作了以下文件:
my_load2.asm:
%include "testio.inc"
global _start
section .text
_start: mov eax, 0
call canonical_off
call canonical_on
testio.inc:
termios: times 36 db 0
stdin: equ 0
ICANON: equ 1<<1
ECHO: equ 1<<3
canonical_off:
call read_stdin_termios
; clear canonical bit in local mode flags
push rax
mov eax, ICANON
not eax
and [termios+12], eax
pop rax
call write_stdin_termios
ret
echo_off:
call read_stdin_termios
; clear echo bit in local mode flags
push rax
mov eax, ECHO
not eax
and [termios+12], eax
pop rax
call write_stdin_termios
ret
canonical_on:
call read_stdin_termios
; set canonical bit in local mode flags
or dword [termios+12], ICANON
call write_stdin_termios
ret
echo_on:
call read_stdin_termios
; set echo bit in local mode flags
or dword [termios+12], ECHO
call write_stdin_termios
ret
read_stdin_termios:
push rax
push rbx
push rcx
push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5401h
mov edx, termios
int 80h
pop rdx
pop rcx
pop rbx
pop rax
ret
write_stdin_termios:
push rax
push rbx
push rcx
push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5402h
mov edx, termios
int 80h
pop rdx
pop rcx
pop rbx
pop rax
ret
然后我跑:
[root@localhost asm]# nasm -f elf64 my_load2.asm
[root@localhost asm]# ld -m elfx86_64 my_load2.o -o my_load2
当我尝试运行它时,我得到:
[root@localhost asm]# ./my_load2
Segmentation fault
调试器说:
(gdb) run
Starting program: /root/asm/my_load2
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000b1 in canonical_off ()
有人可以解释为什么没有“导入”步骤会崩溃吗? 另外,我在Win7 64位的Virtualbox中运行RHEL。这会导致编译问题吗?
答案 0 :(得分:2)
首先,让我们解决丹尼尔提到的不退出的问题。让我们注释掉两个call
指令,这样程序基本上什么都不做:
%include "testio.inc"
global _start
section .text
_start: mov eax, 0
;call canonical_off
;call canonical_on
当我们运行时:
$ ./my_load2
Segmentation fault (core dumped)
它还在死!丹尼尔是对的 - 你需要退出:
%include "testio.inc"
global _start
section .text
_start: mov eax, 0
;call canonical_off
;call canonical_on
mov eax, 1
mov ebx, 0
int 0x80
这一次:
$ ./my_load2
$
没有段错误。所以让我们取消注释call
s:
%include "testio.inc"
global _start
section .text
_start: mov eax, 0
call canonical_off
call canonical_on
mov eax, 1
mov ebx, 0
int 0x80
再次运行:
$ ./my_load2
Segmentation fault (core dumped)
我们再次遇到段错误。但至少我们可以(相当)确定它来自call
ed例程中的一个。
使用strace
运行可执行文件也非常有用:
$ strace ./my_load2
execve("./my_load2", ["./my_load2"], [/* 57 vars */]) = 0
setsockopt(0, SOL_IP, 0x400080 /* IP_??? */, NULL, 0) = -1 EFAULT (Bad address)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x40008c} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
setsockopt
行归因于ioctl
中发生的read_stdin_termios
请求。 strace
告诉我们返回值为EFAULT
。 setsockopt(2)
手册页告诉我们这种情况发生在:
optval指向的地址不在进程地址空间的有效部分。
实际上,这告诉我们写入termios
结构的内存块是只读的。弗兰克是对的;程序中的所有内容 - 包括termios
空间和所有代码 - 都在只读.text
部分。您可以通过以下方式看到:
$ objdump -h my_load2.o
my_load2.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 000000cd 0000000000000000 0000000000000000 000001c0 2**4
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
即。只有一个部分.text
,而且是READONLY
。
然而,实际导致段错误的那一行是这样的:
and [termios+12], eax
因为它也试图写入(只读)termios
内存。
解决此问题的最快方法是将termios
内存放在.data
部分以及.text
部分中的所有其他内容中:
section .data
termios: times 36 db 0
section .text
stdin: equ 0
ICANON: equ 1<<1
ECHO: equ 1<<3
canonical_off:
call read_stdin_termios
[...]
(stdin
,ICANON
和ECHO
可以位于只读.text
部分,因为它们仅用作常量 - 即我们不会写入那些内存。)
进行了这些更改后:
$ ./my_load2
$
程序正常运行并退出。