Jump to content

Code assembles and links but bash rejects it

- - - - -

  • Please log in to reply
10 replies to this topic

#1
mikfig

mikfig

    Newbie

  • Members
  • Pip
  • 7 posts
I just wrote this code, it assembles fine. I used GAS and its x86_64.
I linked it like this:

ld -o ./maximum /usr/lib64/crt1.o /usr/lib64/crti.o ./maximum.o -lc

There's no errors. But then I run it and I get this:

bash: ./maximum: No such file or directory

Just to make sure, I ran it and gdb and got the same error.

Here's the code:


# PURPOSE:      This program finds the maximum number of a

#               set of data items.

#



.section .data


hello_msg:


        .asciz "Hello from GAS :)\\n"


current_data_item_msg:


        .asciz "Current data item #%d: %d\\n"


greatest_data_item:


        .asciz "Greatest data item: %d\\n"


exit_msg:


        .asciz "See ya!!\\n"


debug_1:


        .asciz "Args #: %d\\n"


debug_2:


        .asciz "Mem for args: %d + 8 bytes\\n"


.section .text


.globl main


main:


leaq (%rsp), %rdx                       # need old stack frame to get args


pushq %rbp                              # Create a new stack frame

movq %rsp, %rbp                         # following C runtime rules

pushq %rsi

pushq %rdi

pushq %rbx


movq (%rdx), %rcx                       # Move argc to counter




                pushq %rdx                      # START DEBUG: Print # args

                pushq %rcx


                pushq %rcx

                pushq $debug_1

                call printf


                addq $16, %rsp


                popq %rcx

                popq %rdx                       # END DEBUG





cmpq $2, %rcx                           # if no arguments, goto no_args

jl no_args


decq %rcx

movq %rdx, %rsi

addq $8, %rsi                           # skip the program name, store start of args in rsi


movq %rcx, %rax                         # add enough memory to the stack frame

movq $8, %rbx

mulq %rbx                               # to hold all the args

movq %rax, %rbx

addq $8, %rbx                           # add extra 8 bytes for terminating data item (0)




                pushq %rdx                      # START DEBUG: Print mem needed for args on stack

                pushq %rbx

                pushq %rcx


                subq $8, %rbx


                pushq %rbx

                pushq $debug_2

                call printf


                addq $16, %rsp

                popq %rcx

                popq %rbx

                popq %rdx                       # END DEBUG




movq %rsp, %rdi                         # Make room on stack for args

subq %rbx, %rsp                         # put starting address of args in %rdi


movq $0, %rax                           # Going to store args source index in %rax


#

# Go through argv[ i ] and put every argv on the stack

#

# Or if there are no argvs, then just use random numbers

#


# rcx: decrementing counter on number of args left

# rdi: start of stack space for storing args

# rsi: start of pointers to arg strings

# rax: current arg number


arg_loop_start:


        cmpq $0, %rcx

        je arg_quit


                pushq %rcx


                movq %rax, %r9

                pushq %r9


                pushq 0(%rsi,%rax,8)

                call atoi

                addq $8, %rsp


                popq %r9


                movq %rax, 0(%rdi,%r9,8)        # put current arg in stack 


                movq %r9, %rax


                popq %rcx


        incq %rax

        decq %rcx


no_args:


        movq %rsp, %rdi

        subq $40, %rsp          # Make room for 5 random data items


        pushq $0                # Seed random number gen

        call time

        addq $8, %rsp


        pushq %rax

        call srand

        addq $8, %rsp


        movq $5, %rcx           # Use 5 random data items

        movq $0, %rax


no_args_loop_start:


        cmpq $0, %rcx

        je arg_quit


        movq %rax, %r9


        pushq %r9


        call rand

        addq $1, %rax           # Make sure its not 0


        popq %r9


        movq %rax, 0(%rdi, %r9, 8)


        movq %r9, %rax


        incq %rax

        decq %rcx


arg_quit:


        movq $0, 0(%rdi, %rax, 8)       # Add the terminating zero



pushq $hello_msg

call printf

addq $8, %rsp



movq $0, %rcx           # Put 0 in the data item index

movq (%rdi), %rbx       # Put the first number in ebx as the largest item


start_data_loop:

        movq 0(%rdi, %rcx, 4), %rdx


        pushq %rcx

        pushq %rdx


        pushq %rdx

        pushq %rcx

        pushq $current_data_item_msg

        call printf


        addq $24, %rsp


        popq %rdx

        popq %rcx


        cmpq $0, %rdx

        je data_loop_quit


        cmpq %rdx, %rbx

        jle not_larger


        movq %rdx, %rbx


not_larger:

        incq %rcx


data_loop_quit:


        pushq %rdx

        pushq $greatest_data_item

        call printf


        addq $16, %rsp


        pushq $exit_msg

        call printf


        sub $8, %rsp



popq %rbx               # Destroy the stack frame and exit

popq %rdi

popq %rsi

movq %rbp, %rsp

popq %rbp


ret


Btw, I'm just starting to learn assembly so I'm guessing this code doesn't look to good :P

In fact, I'm pretty sure I used mulq wrong here:

movq %rcx, %rax # add enough memory to the stack frame
movq $8, %rbx
mulq %rbx
# to hold all the args
movq %rax, %rbx
addq $8, %rbx # add extra 8 bytes for terminating data item (0)

Thanks,
mikfig

#2
mikfig

mikfig

    Newbie

  • Members
  • Pip
  • 7 posts
Ok, I just linked it with gcc. Now it just gets a segmentation fault.

#3
RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Members
  • PipPipPipPipPipPipPipPip
  • 1,252 posts
  • Location:C:\Countries\US

mikfig said:

...

leaq (%rsp), %rdx                       # need old stack frame to get args

...

Are you sure it's the old stack frame?


Also,

mikfig said:

...

mulq %rbx                               # to hold all the args

movq %rax, %rbx

...

What are you trying to get those two instructions to do?

#4
mebob

mebob

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 490 posts
One issue that pops out at me is that you are trying to access argc using a 64 bit register. I'm pretty sure it isn't a 64 bit integer, but don't take my word for it lol, i haven't much experience with 64 bit assembly. Also, i notice that you load the current stack address into RDX and use that to access argc. I'm pretty sure that what you are accessing in that code right now is in fact the address of the return address. I'm pretty sure that you have to add 4 bytes (or 2 bytes for x86) before you use any args, otherwise you will run into those return addresses. But please wait until somebody confirms my findings before you make any drastic changes :D
Latinamne loqueris?

#5
mikfig

mikfig

    Newbie

  • Members
  • Pip
  • 7 posts

RhetoricalRuvim said:

Are you sure it's the old stack frame?


Also,

What are you trying to get those two instructions to do?

I'm trying to make enough space on the stack do store all the integers from the command line. So I'm trying to multiply argc by 8 in order to have room for all the arguments in long long plus a terminating zero.

And I guess I was probably wrong about accessing the arguments from the stack like that. First of all, x86_64 on linux follows the AMD64 ABI. As far as calling conventions go, I found a quick summary on Wikipedia. So argc is in edi, argv is in rsi.

---------- Post added at 09:03 PM ---------- Previous post was at 09:01 PM ----------

mebob said:

One issue that pops out at me is that you are trying to access argc using a 64 bit register. I'm pretty sure it isn't a 64 bit integer, but don't take my word for it lol, i haven't much experience with 64 bit assembly. Also, i notice that you load the current stack address into RDX and use that to access argc. I'm pretty sure that what you are accessing in that code right now is in fact the address of the return address. I'm pretty sure that you have to add 4 bytes (or 2 bytes for x86) before you use any args, otherwise you will run into those return addresses. But please wait until somebody confirms my findings before you make any drastic changes :D

Yes you're right, argc is still just an int even though its x86_64. And in x86_64 it looks like they try to use up all the new registers before putting anything on the stack. So argc is in edi.

#6
mebob

mebob

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 490 posts
Oh OK, I never knew that.

EDIT: After reading that link, I'm pretty sure that is referring to how the Linux kernel itself takes arguments with INT 80. It is still probably regular CDECL when using GCC. So, if I am right, you need to just add 64 bits to the stack pointer before you do any accessing to the arguments (to take into account the return address), and instead of loading ARGC into a 64 bit register, load it into a 32 bit one.
Latinamne loqueris?

#7
mikfig

mikfig

    Newbie

  • Members
  • Pip
  • 7 posts

mebob said:

Oh OK, I never knew that.

EDIT: After reading that link, I'm pretty sure that is referring to how the Linux kernel itself takes arguments with INT 80. It is still probably regular CDECL when using GCC. So, if I am right, you need to just add 64 bits to the stack pointer before you do any accessing to the arguments (to take into account the return address), and instead of loading ARGC into a 64 bit register, load it into a 32 bit one.

No, it's true. I fixed my code and used that argument passing convention and it worked. However, for syscalls on x64 there's this.

Anyways, I fixed my code for the most part. It runs just fine if you pass no arguments or if you pass less than 8 arguments. If you pass 8 or more, it will run and get to the final retq instruction; but it will exit with a segmentation fault error.
I'm not sure what the cause is, because I debugged it with gdb and the stack is cleaned up. The registers that are supposed to be reset to the value they had before the function was called isn't being completely done right for some reason.
But when it runs with less than 8 arguments that is still the case, just to a lesser extent. I.e. more of the registers supposed to be saved are set to their original value when less than 8 arguments are passed, but not all of them are.
Also, when I break at the last instruction, retq, and reset the values to their originals there is still a segmentation fault. So I guess I'm going to have to look more into this.

Anyways, here's the better code:

# PURPOSE:      This program finds the maximum number of a

#                      set of data items. The data items can be given as

#	          command line arguments or 5 random data items will be generated.


.section .data


hello_msg:


        .asciz "Hello from GAS\n"


current_data_item_msg:


        .asciz "Current data item #%lld: %lld\n"


greatest_data_item:


        .asciz "Greatest data item: %lld\n"


exit_msg:


        .asciz "See ya!!\n"


debug_2:


        .asciz "Mem for args: %lld + 8 bytes\n"


.section .text


.globl main


main:


pushq %rbp                              # Create a new stack frame

movq  %rsp, %rbp                        # following C runtime rules

pushq %rsi

pushq %rdi

pushq %rbx

pushq %r12

pushq %r13

pushq %r14

pushq %r15



decl %edi               # program name not an "argument"

addq $8, %rsi


#

#

#       INPUTS

#

#       int argc        :       ebx

#       char** argv     :       rsi

#       

#


cmpl $1, %edi                           # if no arguments, goto no_args

jl no_args


# previous operation on %edi

# clears higher 32 bits


movq  %rdi, %rbx

imulq $8, %rbx                          # add enough memory to the stack frame

addq  $8, %rbx                          # to hold all the args

	                                  # add extra 8 bytes for terminating zero



                pushq %rdi                      # START DEBUG: Print mem needed for args on stack

                pushq %rbx

                pushq %rsi


                subq $8, %rbx


                movq %rbx, %rsi

                movq $debug_2, %rdi

                movq $0, %rax

                call printf


                popq %rsi

                popq %rbx

                popq %rdi                       # END DEBUG



movq %rdi, %rcx

movq %rsp, %rdi                         # Make room on stack for args

subq %rbx, %rsp                                 # put starting address of args in %rdi


movq $0, %rax                           # Going to store args source index in %rax


#

# Go through argv[ i ] and put every atoll(*argv[i]) on the stack

#

# Or if there are no argvs, then just use random numbers

#


# rcx: decrementing counter on number of args left

# rdi: start of stack space for storing args

# rsi: start of pointers to arg strings

# rax: current arg number


arg_loop_start:


                cmpq $0, %rcx

	 je arg_quit


                pushq %rcx


                movq %rax, %r9


                pushq %r9

                pushq %rdi

                pushq %rsi


                movq (%rsi,%rax,8), %rdi

                call atoll                      # 64-bit version of atoi


                pop %rsi

                pop %rdi

                pop %r9


                movq %rax, (%rdi,%r9,8)         # put current arg in stack 


                movq %r9, %rax


                popq %rcx


        incq %rax

        decq %rcx

        jmp arg_loop_start


no_args:


        movl $0, %edi           # Seed random number gen

        call time


        movl %eax, %edi

        call srand


        movq %rsp, %rdi

        subq $40, %rsp          # Make room for 5 random data items


        movq $5, %rcx           # Use 5 random data items

        movq $0, %rax


# rcx: decrementing counter on number of args left

# rdi: start of stack space for storing args

# rax: current arg number


no_args_loop_start:


        cmpq $0, %rcx

        je arg_quit


        movq %rax, %r9


        pushq %rdi

        pushq %rcx

        pushq %r9


        call rand

        addl $1, %eax                   # Make sure its not 0


        popq %r9

        popq %rcx

        popq %rdi


        movq %rax, (%rdi, %r9, 8)       # rand putting num in %eax should zero out top 32 bits


        movq %r9, %rax


        incq %rax

        decq %rcx

        jmp no_args_loop_start


arg_quit:


        movq $0, (%rdi, %rax, 8)        # Add the terminating zero


pushq %rdi


movq $hello_msg, %rdi

movq $0, %rax

call printf


popq %rdi


movq $0, %rcx           # Put 0 in the data item index

movq (%rdi), %rbx       # Put the first number in rbx as the largest item


start_data_loop:

        movq (%rdi, %rcx, 8), %rdx


        pushq %rbx

        pushq %rcx

        pushq %rdi

        pushq %rdx


        # 3rd argument already in rdx

        movq %rcx, %rsi

        movq $current_data_item_msg, %rdi

        movq $0, %rax

        call printf


        popq %rdx

        popq %rdi

        popq %rcx

        popq %rbx


        cmpq $0, %rdx

        je data_loop_quit


        cmpq %rdx, %rbx

        jge not_larger


        movq %rdx, %rbx


not_larger:

        incq %rcx

        jmp start_data_loop


data_loop_quit:


        incq %rcx                   # need to pop off terminating zero too

        imulq $8, %rcx

        addq %rcx, %rsp         # clean up used stack space


        movq %rbx, %rsi

        movq $greatest_data_item, %rdi

        movq $0, %rax

        call printf


        movq $exit_msg, %rdi

        movq $0, %rax

        call printf


popq %r15               # Destroy the stack frame and exit

popq %r14

popq %r13

popq %r12

popq %rbx               # AMD64 ABI, have to preserve %rbp, %rbx, %r12-%r15

popq %rdi               # Intel386 ABI, have to preserve %rbp, %rbx, %rdi, %rsi

popq %rsi

movq %rbp, %rsp

popq %rbp


ret


#8
mebob

mebob

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 490 posts
EDIT: oops, I posted something stupid, sorry

---------- Post added at 08:18 AM ---------- Previous post was at 08:12 AM ----------

Maybe you should try printing out the original return address at the beginning, and then print it out as well right before the RET? If you get the segmentation fault right at the RET it is probably because it is trying to return to an invalid address.
Latinamne loqueris?

#9
mikfig

mikfig

    Newbie

  • Members
  • Pip
  • 7 posts
Ok I fixed my code :). Runs perfectly now :). It's because when I put my data on the stack I was going towards increasing memory addresses.
I was thinking that movq %rax, (%rdi, %r9, 8 ) was subtracting %r9*8 from %rdi for some reason, wanting to go from the first "allocated address" to the very top of the stack instead of from the top of the stack to the bottom.
So, silly mistake, and that was what was causing this whole segmentation fault. Overwriting the return address and shooting everything to hell.

Anyways I fixed all my issues and now it works perfectly. I noted the stack before the first instruction in main() is executed and before the least ret instruction.
I did a diff on 800 bytes on the stack and I kept it squeaky clean :). And all my stack allocations are 16-byte aligned :). So as far as I can see I guess I obeyed the ABI, but I have to finish reading through that.

So here's the fixed code :):

# PURPOSE:	This program finds the maximum number of a

#		set of data items.

#


.section .data


hello_msg:


	.asciz "Hello from GAS\n"


current_data_item_msg:


	.asciz "Current data item #%lld: %lld\n"


greatest_data_item:


	.asciz "Greatest data item: %lld\n"


exit_msg:


	.asciz "See ya!!\n"


debug_2:

	

	.asciz "Mem for args: %lld bytes\n"


.section .text


.globl main


main:


pushq %rbp				# Create a new stack frame

movq  %rsp, %rbp			# following C runtime rules

pushq %rsi

pushq %rdi

pushq %rbx

pushq %r9

pushq %r12

pushq %r13

pushq %r14

pushq %r15



addq $8, %rsi				# program name not an "argument"


#

#

#	INPUTS

#

#	int argc	: 	edi

#	char** argv 	: 	rsi

#	

#

#	VARIABLES

#

#	stack space for arguments gone through atoll():		rsi

#	


cmpl $2, %edi				# if no arguments, goto no_args

jl no_args


# previous operation on %edi

# clears higher 32 bits


movq  %rdi, %rbx			# add enough memory to the stack frame

imulq $8, %rbx				# to hold all the args and the terminating zero, i.e. argc


movq %rbx, %rax				# make sure stack reservation is 16-byte aligned

cqto				

movq $16, %rcx	

idivq %rcx				# %rbx = argc * 8

addq %rdx, %rbx				# %rbx = %rbx + (%rbx % 16) 


decq %rdi


		

		pushq %rdi			# START DEBUG: Print mem needed for args on stack

		pushq %rbx

		pushq %rsi

		pushq %rax

	

		movq %rbx, %rsi

		movq $debug_2, %rdi

		movq $0, %rax

		call printf


		popq %rax

		popq %rsi

		popq %rbx

		popq %rdi			# END DEBUG



movq %rdi, %rcx				# Make room on stack for args	

subq %rbx, %rsp				# put address of first arg in %rdi

movq %rsp, %rdi				


movq $0, %rax				# Going to store args source index in %rax


#

# Go through argv[ i ] and put every argv on the stack

#

# Or if there are no argvs, then just use random numbers

#


# rcx: decrementing counter on number of args left

# rdi: start of stack space for storing args

# rsi: start of pointers to arg strings

# rax: current arg number


arg_loop_start:


	cmpq $0, %rcx

	je arg_quit


		movq  %rax, %r9


		pushq %rcx

		pushq %r9

		pushq %rdi

		pushq %rsi

		pushq %rbx

		pushq $0     # have to push a dummy value to keep stack 16-byte aligned

	

		movq (%rsi,%rax,8), %rdi

		call atoll			# 64-bit version of atoi


		popq %rbx

		popq %rbx

		popq %rsi

		popq %rdi

		popq %r9

		popq %rcx


		movq %rax, (%rdi,%r9,8)		# put current arg in stack 

		

		movq %r9, %rax


	incq %rax

	decq %rcx

	jmp arg_loop_start


no_args:


	movl $0, %edi		# Seed random number gen

	call time


	movl %eax, %edi

	call srand


	call rand		# Generate a random number of data items

	cqto			# from 1 to 50

	movq $50, %rcx

	idivq     %rcx

	addq $1,  %rdx


	movq %rdx, %r9


	addq $1, %rdx		# Need room for terminating zero


	imulq $8, %rdx		# Make sure the stack allocation is 16-byte aligned

	movq %rdx, %rbx

	movq %rdx, %rax

	cqto

	movq $16, %rcx

	idivq %rcx

	addq %rdx, %rbx


	subq %rbx, %rsp		# Make room for random data items + terminating 0

	movq %rsp, %rdi


	movq %r9, %rcx		# %rcx: counter, %rax: index

	movq $0, %rax


no_args_loop_start:


	cmpq $0, %rcx

	je arg_quit


	movq %rax, %r9


	pushq %rdi

	pushq %rcx

	pushq %r9

	pushq %rbx


	call rand

	addl $1, %eax			# Make sure its not 0


	popq %rbx

	popq %r9

	popq %rcx

	popq %rdi


	movq %rax, (%rdi, %r9, 8)	# rand putting num in %eax should zero out top 32 bits


	movq %r9, %rax


	incq %rax

	decq %rcx

	jmp no_args_loop_start


arg_quit:


	movq $0, (%rdi, %rax, 8)	# Add the terminating zero


pushq %rdi

pushq %rbx


movq $hello_msg, %rdi

movq $0, %rax

call printf


popq %rbx

popq %rdi


movq %rbx, %rsi

movq $0, %rcx		# Put 0 in the data item index

movq (%rdi), %rbx 	# Put the first number in rbx as the largest item


start_data_loop:

	movq (%rdi, %rcx, 8), %rdx


	pushq %rbx

	pushq %rcx

	pushq %rdi

	pushq %rdx

	pushq %rsi

	pushq $0	# 16-byte alignment, pushing junk


	# 3rd argument already in rdx

	movq %rcx, %rsi

	movq $current_data_item_msg, %rdi

	movq $0, %rax

	call printf


	popq %rsi

	popq %rsi

	popq %rdx

	popq %rdi

	popq %rcx

	popq %rbx


	cmpq $0, %rdx

	je data_loop_quit


	cmpq %rdx, %rbx

	jge not_larger


	movq %rdx, %rbx


not_larger:

	incq %rcx

	jmp start_data_loop


data_loop_quit:

	

	addq %rsi, %rsp		# clean up used stack space


	movq %rbx, %rsi

	movq $greatest_data_item, %rdi

	movq $0, %rax

	call printf


	movq $exit_msg, %rdi

	movq $0, %rax

	call printf


popq %r15		# Destroy the stack frame and exit

popq %r14

popq %r13

popq %r12

popq %r9		# AMD64 ABI, have to preserve %rbp, %rbx, %r12-%r15

popq %rbx

popq %rdi		# Intel386 ABI, have to preserve %rbp, %rbx, %rdi, %rsi

popq %rsi

movq %rbp, %rsp

popq %rbp


ret



#10
mebob

mebob

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 490 posts
Cool, nice to see you got it working :)
Latinamne loqueris?

#11
mikfig

mikfig

    Newbie

  • Members
  • Pip
  • 7 posts

mebob said:

Cool, nice to see you got it working :)

Thanks :). It was what I learned from it that was important :). The AMD64 ABI calling convention, a little more about manipulating the stack, proper use of mulq, idivq, cqto, etc.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users