5 Replies - 2278 Views - Last Post: 04 July 2012 - 06:21 PM

#1 litedrive  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 102
  • Joined: 30-September 10

Segmentation fault in string manipulation program (nasm/x86/linux)

Posted 04 July 2012 - 04:14 PM

So, I'm getting a segmentation fault, which appears to be happening as soon as my print_num subroutine executes its `ret` instruction.

I'd like to know what the issue could be here in terms of what I'm doing wrong, and how I could fix it?

Most of my experience is in C++, but I'm just learning assembly.

The goal of the program is to simply take in a lowercase string (from stdin - i.e., the keyboard), convert it to uppercase, and then print it. It's as simple as that.

Codez

bits 32

[section .bss]

        buf: resb 1024                  ;allocate 1024 bytes of memory to buf

[section .data]

        ;*************
        ;* CONSTANTS *
        ;*************

        ;ASCII comparison/conversion

        LowercaseA:     equ 0x61
        LowercaseZ:     equ 0x7A
        SubToUppercase: equ 0x20

        ;IO specifiers/descriptors

        EOF:            equ 0x0

        sys_read:       equ 0x3
        sys_write:      equ 0x4

        stdin:          equ 0x0
        stdout:         equ 0x1
        stderr:         equ 0x2

        ;Kernel Commands/Program Directives

        _exit:          equ 0x1
        exit_success:   equ 0x0
        execute_cmd:    equ 0x80

        ;Memory Usage

        buflen:         	equ 0x400   ;1KB of memory
		max_num_write_len:	equ 0xFFFF	;32-bit max

        ;*****************
        ;* NON-CONSTANTS *
        ;*****************

        iteration_count:    dd 0
        query :             db "Please enter a string of lowercase characters, and I will output them for you in uppercase ^.^: ", 10   
        querylen :          equ $-query

[section .text]

    global _start
	extern _printf
;===========================================
;             Entry Point
;===========================================

_start:
        nop                                         ;keep GDB from complaining
        call    AskUser 
        call    Read
        call    SetupBuf
        call    Scan
        call    Write
        jmp     Exit

;===========================================
;           IO Instructions
;===========================================

Read:
        mov     eax, sys_read                       ;we're going to read in something
        mov     ebx, stdin                          ;where we obtain this is from stdin
        mov     ecx, buf                            ;read data into buf
        mov     edx, buflen                         ;amount of data to read

        int     execute_cmd                         ;invoke kernel to do its bidding
        ret

Write:
        mov     eax, sys_write                      ;we're going to write something
        mov     ebx, stdout                         ;where we output this is going to be in stdout
        mov     ecx, buf                            ;buf goes into ecx; thus, whatever is in ecx gets written out to
        mov     edx, buflen                         ;write the entire buf

        int     execute_cmd                         ;invoke kernel to do its bidding
        ret

AskUser:
        mov     eax, sys_write
        mov     ebx, stdout
        mov     ecx, query
        mov     edx, querylen   

        int     execute_cmd
        ret

;PROC: takes an integer to print; be sure to push that integer before calling 
print_num:
		push 	ecx						;save the loop count in edx
		pop		ecx						;grab data to print, store in ecx
		
		mov		eax, sys_write
		mov		ebx, stdout
		mov		edx, max_num_write_len	;store the num write len in edx
		
		int		execute_cmd				;call kernel
		
		pop 	ecx						;grab the loop count again

		sub		esp, 8					;subtract 8 from esp (the stack pointer register) to ensure that it's put back in its proper place
		
		ret
;ENDPROC
     
;===========================================
;           Program Preperation
;===========================================

SetupBuf:
        mov     ecx, esi                        ;place the number of bytes read into ecx
        mov     ebp, buf                        ;place the address of buf into ebp
		mov		dword [ebp + ecx], 0			;null terminate string
        dec     ebp                             ;decrement buf by 1 to prevent "off by one" error
        
		ret                                         

;===========================================
;           Conversion Routines     
;===========================================

ToUpper:
        sub     dword [ebp + ecx], SubToUppercase   ;grab the address of buf and sub its value to create uppercase character
		ret

Scan:
		mov		eax, iteration_count				;store iteration_count in eax
		push 	eax									;whatever value exists in iteration_count as loop count
        call    print_num 	                        ;print the current iteration within the loop

        cmp     dword [ebp + ecx], LowercaseA       ;Test input char against lowercase 'a'
        jb      ToUpper                             ;If below 'a' in ASCII, then is not lowercase - goto ToLower

        cmp     dword [ebp + ecx], LowercaseZ       ;Test input char against lowercase 'z'
        ja      ToUpper                             ;If above 'z' in ASCII, then is not lowercase - goto ToLower

        dec     ecx                                 ;decrement ecx by one, so we can get the next character
        add		dword [iteration_count], 1             ;increment the __value__ in iteration count by 1
        jnz     Scan                                ;if ecx != 0, then continue the process
        ret

;===========================================

;Next:
;       dec     ecx                             	;decrement ecx by one
;       jnz     Scan                            	;if ecx != 0 scan
;       ret

;===========================================

Exit:
        mov     eax, _exit
        mov     ebx, exit_success

        int     execute_cmd




NASM uses Intel syntax, so for those of you who are use to FASM or TASM but not NASM, it should be pretty easy to decipher.

Is This A Good Question/Topic? 0
  • +

Replies To: Segmentation fault in string manipulation program (nasm/x86/linux)

#2 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 858
  • View blog
  • Posts: 2,287
  • Joined: 28-March 11

Re: Segmentation fault in string manipulation program (nasm/x86/linux)

Posted 04 July 2012 - 04:45 PM

Did none of the help you got here work?

Your stack is NOT aligned!
Here:

Scan:
		mov		eax, iteration_count				;store iteration_count in eax
		push 	eax									;whatever value exists in iteration_count as loop count
		call    print_num 	                        ;print the current iteration within the loop
You push 1 thing on the stack (The stack is DWORD aligned on 32bit systems)

print_num:
		push 	ecx						;save the loop count in edx
		pop		ecx						;grab data to print, store in ecx
		
		mov		eax, sys_write
		mov		ebx, stdout
		mov		edx, max_num_write_len	;store the num write len in edx
		
		int		execute_cmd				;call kernel
		
		pop 	ecx						;grab the loop count again
		
		sub		esp, 8					;subtract 8 from esp (the stack pointer register) to ensure that it's put back in its proper place
		
		ret

Lines 2-3
push/pop ecx stack still aligned correctly.

Line 11
pop ecx - what was pushed? Stack is now NOT aligned

Line 13
sub esp, 8 <<< not sure what in the world your doing here but is sure as hell is screwing up the stack!

Either after you push your parameter and call print_num add esp, 4 like this:
		mov		eax, iteration_count				;store iteration_count in eax
		push 	eax									;whatever value exists in iteration_count as loop count
		call    print_num 	                        ;print the current iteration within the loop
		add     esp, 4

or at the end of your print_num proc use
ret 4 and that will adjust the stack.

You MUST/HAVE TO/IT IS IMPORTANT in almost all cases to pair your push/pops unless you manually adjust esp.
Was This Post Helpful? 1
  • +
  • -

#3 litedrive  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 102
  • Joined: 30-September 10

Re: Segmentation fault in string manipulation program (nasm/x86/linux)

Posted 04 July 2012 - 04:55 PM

Yeah, they failed to mention that. Thank you.
Was This Post Helpful? 0
  • +
  • -

#4 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 858
  • View blog
  • Posts: 2,287
  • Joined: 28-March 11

Re: Segmentation fault in string manipulation program (nasm/x86/linux)

Posted 04 July 2012 - 05:02 PM

I got it not to seg fault, it just returns the entered string.

Don't use ebp, you have esi, edi and ebx that are preserved across calls

If you do use esi, edi, ebx, esp, ebp you MUST preserve them.
Was This Post Helpful? 0
  • +
  • -

#5 litedrive  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 102
  • Joined: 30-September 10

Re: Segmentation fault in string manipulation program (nasm/x86/linux)

Posted 04 July 2012 - 05:33 PM

After adding `ret 4` in `print_num`, I found another seg fault.

What's weird is this code which has been generated by the assembler:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000600178 in iteration_count ()
=> 0x0000000000600178 <iteration_count+0>:	00 00	add    BYTE PTR [rax],al


What's even weirder is how its misinterpreting the variable `iteration_count` as a subroutine.

Any idea on how this is happening?
Was This Post Helpful? 0
  • +
  • -

#6 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 858
  • View blog
  • Posts: 2,287
  • Joined: 28-March 11

Re: Segmentation fault in string manipulation program (nasm/x86/linux)

Posted 04 July 2012 - 06:21 PM

This works, modify it to suit your needs, and learn from it.
Spoiler

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1