【问题标题】:NASM basic input-output program crashesNASM 基本输入输出程序崩溃
【发布时间】:2013-11-25 08:42:18
【问题描述】:

在这个线程之后,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。这会导致编译问题吗?

【问题讨论】:

  • 在您发布的代码中没有定义符号write_stdin_termios。您可能发布了不完整的代码版本。
  • 你永远不会退出你的程序。
  • 修复了这个问题。我尝试编译完整的代码。
  • 我没有退出的事实并不能解释为什么它会在 canonical_off 上崩溃,但之后不会。
  • 您的termios 结构似乎位于section .text - 只读内存中。把它放在section .data 有帮助吗?

标签: linux assembly nasm rhel


【解决方案1】:

首先,让我们解决丹尼尔提到的不退出问题。让我们注释掉这两个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
$ 

没有段错误。所以让我们取消注释calls:

%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)

我们又遇到了段错误。但至少我们可以(相当)确定这是来自called 例程之一。

使用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 行是由于read_stdin_termios 中发生的ioctl 请求。 strace 告诉我们返回值为EFAULTsetsockopt(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

[...]

stdinICANONECHO 可以在只读的 .text 部分中,因为它们仅用作常量 - 即我们不写入这些内存位。)

进行了这些更改:

$ ./my_load2 
$ 

程序正常运行退出。

【讨论】:

  • 64 位代码一般应避免使用int 0x80。使用mov eax, 60 / syscall 进行_exit(edi) 系统调用。
猜你喜欢
  • 2016-12-03
  • 2023-01-24
  • 2015-07-20
  • 1970-01-01
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
相关资源
最近更新 更多