user547907
user547907

Reputation:

NASM basic input-output program crashes

Following this thread, How do i read single character input from keyboard using nasm (assembly) under ubuntu? ,I'm trying to compile a program that echoes the input in NASM. I've made following files:

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

Then I run:

[root@localhost asm]# nasm -f elf64 my_load2.asm 
[root@localhost asm]# ld -m elfx86_64 my_load2.o -o my_load2

When I try to run it i get:

[root@localhost asm]# ./my_load2
Segmentation fault

Debugger says:

(gdb) run
Starting program: /root/asm/my_load2

Program received signal SIGSEGV, Segmentation fault.
0x00000000004000b1 in canonical_off ()

Can someone explain why is it crashing without on "import" step? Also, I am running RHEL in Virtualbox under Win7 64 bit. Can this cause problems with compilation?

Upvotes: 0

Views: 716

Answers (1)

Richard Fearn
Richard Fearn

Reputation: 25491

Firstly, let's address the issue of not exiting, as mentioned by Daniel. Let's comment out the two call instructions, so the program essentially does nothing:

%include "testio.inc"
global _start
section .text
_start: mov eax, 0
;call canonical_off
;call canonical_on

When we run this:

$ ./my_load2 
Segmentation fault (core dumped)

It still dies! Daniel is right - you need to exit:

%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

This time:

$ ./my_load2
$ 

No segfault. So let's uncomment the 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

And run it again:

$ ./my_load2
Segmentation fault (core dumped)

We get a segfault again. But at least we can be (fairly) sure that's coming from inside one of the called routines.

Running the executable with strace is also quite informative:

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

The setsockopt line is due to the ioctl request that happens in read_stdin_termios. strace tells us the return value was EFAULT. The setsockopt(2) man page tells us this happens when:

The address pointed to by optval is not in a valid part of the process address space.

Actually this is telling us that the block of memory into which the termios structure is written is read-only. Frank is correct; everything in the program - including the termios space, and all the code - is in the read-only .text section. You can see this with:

$ 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

i.e. there's only one section, .text, and it's READONLY.

The line that actually causes the segfault, however, is this one:

and [termios+12], eax

because it also tries to write to the (read-only) termios memory.

The quickest way to fix this is to put the termios memory in the .data section, and everything else in the .text section:

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, and ECHO can be in the read-only .text section, because they're just used as constants - i.e. we don't write to those bits of memory.)

Having made these changes:

$ ./my_load2 
$ 

The program runs and exits normally.

Upvotes: 3

Related Questions