I've been recently working towards learning a bit of assembly and I'm currently stumped on an exercise which requires me to find the maximum number of a list of long values.
The code is as follows:
.section .data
data_items: .long 200, 201, 101, 10, 0
min_val: .long 0x8000000000000000 # MIN_VALUE in long
.section .text
.global _start
_start:
movl $0, %edi # init counter to 0
movl min_val, %ebx
start_loop:
cmpl $0, %eax
je loop_exit # go to end if 0 encountered
incl %edi
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax
jle start_loop # if new value < max value in ebx, read next element
movl %eax, %ebx
jmp start_loop
loop_exit:
movl $1, %eax
int $0x80
Two problems with this code:
When trying to assemble the code, I get the message: Warning: value 0x8000000000000000 truncated to 0x0
If I rewrite my code in an alternative logic (one which doesn't require min_value variable), any value greater than 255 in the list of data_items is truncated or returned as value % 256 even though the range of .long should be much larger?
Can anyone help me understand what I'm doing wrong?
EDIT: After changes, the code looks like below. Note how maximum in this case turns out to be 145 instead of 401.
.section .data
data_items: .long 401, 201, 101, 10, 0
max_val: .long 0x80000000
.section .text
.global _start
_start:
movl $0, %edi # init counter to 0
movl max_val, %ebx
start_loop:
movl data_items(,%edi,4), %eax
cmpl $0, %eax
je loop_exit # go to end if 0 encountered
incl %edi
cmpl %ebx, %eax
jle start_loop # if new value < max value in ebx, read next element
movl %eax, %ebx
jmp start_loop
loop_exit:
movl $1, %eax
int $0x80
First of all, 0x8000000000000000 doesn't even fit in a long, it's a long long. A long -1 is 0xffffffff.
As for the other point, I can't comment on code you haven't posted.
Related
So I have written a program based on NASM that receives user input (two numbers to be exact), and then it realizes the addition and difference between the two numbers, and then it prints it back. The program works fine in NASM but I am having trouble with GAS. The subroutine that counts the length of a string using the repne scasb instruction is giving me a headache because of a segmentation fault.
I have checked the code for segmentation faults, and I have located the fault in the repne scasb line.
What I am basically doing is translating a NASM code I made into its respective GAS code. However, as I said before, it's giving me a segmentation fault. After I get the first number from the user, to be more specific.
.section .data
msg: .ascii "Insert a number: "
msgLen = .-msg
msg2: .ascii "Insert another number: "
msg2Len = .-msg2
errorMsg: .ascii "Error: invalid character!\n"
errorMsgLen = .-errorMsg
displaySuma: .ascii "The result of the addition is: "
displaySumaLen = .-displaySuma
displayResta: .ascii "The result of the difference is: "
displayRestaLen = .-displayResta
enterChar: .ascii "\n"
terminator: .byte 0
.section .bss
.lcomm num1, 8
.lcomm num2, 8
.lcomm buffer, 10
.lcomm buffer2, 10
.section .text
.global _start
_start:
call _clear #clear registers. Probably an useless routine
call _msg1 #Display msg1
call _num1 #Read num1
movl num1, %edi
call _lenString #ECX now has num1 length
lea (num1), %esi
call _stringToInt #EAX now has num1 in integer
movl %eax, %r15d #R15D now has the integer
call _msg2 #Display msg2
call _num2 #Read num2
xor %edi, %edi #Clear EDI
movl num2, %edi #Moving num2 to EDI register to call _lenstring
xor %ecx, %ecx #Clear ECX
call _lenString #ECX has num2 length
xor %esi, %esi #clear ESI
lea (num2), %esi
call _stringToInt #EAX now has integer value of num2
mov %eax,%r14d ###R14D has num2 now
#Addition
#r8d = num1 + num2
mov %r15d, %r8d
add %r14d, %r8d #R8D has num1 + num2
#Difference
#If num1 > num2 =======> r9d = num1 - num2
#If num1 < num2 =======> r9d = num2 - num1
cmp %r14d, %r15d
jg .greater
mov %r14d, %r9d
sub %r15d, %r9d #R9D has num1 - num2
jmp .next
.greater:
mov %r15d, %r9d
sub %r14d, %r9d #R9D has num2 - num1
jmp .next
.next:
mov %r8d, %eax #Sum is now at EAX to convert it to ascii characters
lea (buffer), %esi
call _intToString
#EAX ascii of the sum
mov %eax, %r10d #Using R10D to store the new string
mov %r9d, %eax #Difference result is now at EAX
lea (buffer2), %esi
call _intToString
#EAX has the pointer to the difference result.
mov %eax, %r11d #Storing string in R10D
xor %edi, %edi
xor %r15d, %r15d
xor %r14d, %r14d
mov %r10d, %edi
call _lenString #ECX length of sum string
mov %ecx, %r15d #R15D now has that value
call _suma #This prints the sum result
xor %edi, %edi #Clear EDI
mov %r11d, %edi
call _lenString #ECX has length of dif. string
mov %ecx, %r14d #R14D has that value
call _resta #Print dif. result
movl $1, %eax #End of the program
movl $0, %ebx
int $0x80
_stringToInt:
xor %ebx, %ebx
.next_digit:
movzxb (%esi), %eax
cmp $0x30, %eax #These 4 lines check for invalid characters
jb _errorMsg
cmp $0x39, %eax
ja _errorMsg
inc %esi
sub $0x30, %eax ###Sub 48 (converts to integer)
imul $10, %ebx
add %eax, %ebx #ebx = ebx*10 + eax
loop .next_digit #loop [ECX] times
mov %ebx, %eax
ret
_intToString:
add $10, %esi
mov (terminator),%esi
mov $10, %ebx
.next_digit1:
xor %edx, %edx
div %ebx
add $0x30, %edx ##
dec %esi
mov %dl, (%esi)
test %eax, %eax
jnz .next_digit1
mov %esi, %eax
ret
#######################################################################################################
_msg1:
movl $4, %eax #msg1 routine
movl $1, %ebx
movl $msg, %ecx
movl $msgLen, %edx
int $0x80
ret
_num1:
movl $3, %eax #Reads first number
movl $0, %ebx
movl $num1, %ecx
movl $8, %edx
int $0x80
ret
_msg2:
movl $4, %eax #msg2 display
movl $1, %ebx
movl $msg2, %ecx
movl $msg2Len, %edx
int $0x80
ret
_num2:
movl $3, %eax #Reads the next number
movl $0, %ebx
movl $num2, %ecx
movl $8, %edx
int $0x80
ret
_salir:
movl $1, %eax #Exit
movl $0, %ebx
int $0x80
_errorMsg:
movl $4, %eax #Error msg
movl $1, %ebx
movl $errorMsg, %ecx
movl $errorMsgLen, %edx
int $0x80
jmp _salir
_lenString:
xor %ecx, %ecx
not %ecx
xor %al, %al
mov $0xA, %al
cld
repne scasb #Segmentation fault is caused by this line
not %ecx
dec %ecx
ret
_suma:
movl $4, %eax
movl $1, %ebx
movl $displaySuma, %ecx
movl $displayRestaLen, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
mov %r10d, %ecx
mov %r15d, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
movl $enterChar, %ecx
movl $1, %edx
int $0x80
ret
_resta:
movl $4, %eax
movl $1, %ebx
movl $displayResta, %ecx
movl $displayRestaLen, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
mov %r11d, %ecx
mov %r14d, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
movl $enterChar, %ecx
movl $1, %edx
int $0x80
ret
_clear:
xor %eax, %eax
xor %ebx, %ebx
xor %ecx, %ecx
xor %edx, %edx
xor %esi, %esi
xor %edi, %edi
xor %r8d, %r8d
xor %r9d, %r9d
xor %r10d, %r10d
xor %r11d, %r11d
xor %r14d, %r14d
xor %r15d, %r15d
ret
I am using this makefile to create the .o and .exe files (Given by my professor):
#*************************************************
# Executable name : hola
# Version : 2.0
# Created date : February 12, 2019
# Authors :
# Eduardo A. Canessa M
# Description : simple makefile for GAS
# Important Notice: To be used for GAS only
#*************************************************
#change the name "ejemplo" for the name of your source file
name=addSubInteger
#program to use as the assembler (you could use NASM or YASM for this makefile)
ASM=as
#flags for the assember
#ASM_F= #*** place here flags if needed ***
#program to use as linker
LINKER=ld
#link executable
$(name): $(name).o
$(LINKER) -o $(name) $(name).o
#assemble source code
$(name).o: $(name).s
$(ASM) $(ASM_F) -o $(name).o $(name).s
There is a segmentation fault error after the program reads the first user input.
This is the NASM code of my program (Hope you don't mind the spanish comments in it, but it's essentially the same program as the one written on GAS).
I know I have made some next level spaghetti code, but this is the solution I came to.
movl num1, %edi
call _lenString #ECX now has num1 length
lea (num1), %esi
call _stringToInt #EAX now has num1 in integer
The first instruction does not load the address in %edi. You can use lea like you did for the call to _stringToInt that follows next. Or if you care about a shorter encoding then write mov $num1, %edi.
lea (num1), %edi
call _lenString #ECX now has num1 length
The same problem exists for the second number:
movl num2, %edi SAME PROBLEM
xor %ecx, %ecx
call _lenString
The _intToString subroutine has 2 problems!
You destroy the address in %esi by writing a random value in it.
You (try to) write in memory beyond the buffer that was reserved via .lcomm buffer, 10. This will destroy the first byte in buffer2.
Since converting a 32-bit integer can produce (at most) 10 characters, you will need to enlarge your buffer to 11 bytes so you can safely store the byte-sized terminator.
.lcomm buffer, 11
.lcomm buffer2, 11
Then use this code:
_intToString:
mov $10, %ebx
add %ebx, %esi #Instead of 'ADD $10, %ESI' now that EBX==10
mov (terminator), %dl
mov %dl, (%esi)
.next_digit1:
xor %edx, %edx
div %ebx
add $0x30, %edx ##
dec %esi
mov %dl, (%esi)
test %eax, %eax
jnz .next_digit1
mov %esi, %eax
ret
The original NASM source uses
STRING_TERMINATOR equ 10
An equ does not consume memory at run-time. Your terminator: .byte 0 does use run-time memory! A good translation for the equ would be
.set terminator,0
Now you can write
_intToString:
mov $10, %ebx
add %ebx, %esi #Instead of 'ADD $10, %ESI' now that EBX==10
movb $terminator, (%esi)
This code reads data from an inputfile character by character and it writes to another file.
It should stop to read and write once it finds a character with value equal to 3.
I said should because the program doesn't stop once it finds a value equal to 3, instead it continues to read until the end of file.
Inputfile is like: 2 4 5 3 1 8
My code is:
.section .data
varInputHandle: .long 100
varOutputHandle: .long 100
varExitCode: .long 1
cont: .long 1
.section .bss
.lcomm varBuffer, 1
.section .text # declaring our .text segment
.globl _start # telling where program execution should start
_start:
popl %eax # Get the number of arguments
popl %ebx # Get the program name
# open input file first
popl %ebx # Get the first actual argument - file to read
movl $5, %eax # open
movl $0, %ecx # read-only mode
int $0x80
movl %eax, varInputHandle #store input file handle to memory
#open output file, make it writable, create if not exists
popl %ebx # Get the second actual argument - file to write
movl $5, %eax # open
movl $0101, %ecx # create flag + write only access (if google is telling me truth)
movl $0666, %edx #permissions for out file as rw-rw-rw-
int $0x80
movl %eax, varOutputHandle #store output file handle to memory
contToZero:
movl $0, cont
processingLoop:
incb cont
#read single char to varBuffer
movl $3, %eax
movl varInputHandle, %ebx
movl $varBuffer, %ecx
movl $1, %edx
int $0x80
#if no char was read (EOF?), jmp finishProcessing
cmpl $0, %eax
jz finishProcessing # looks like total success, finish cleanly
cmpl $3, varBuffer // this instruction is never true, don't know why
je exitToOs
#write it
movl $4, %eax
movl varOutputHandle, %ebx # file_descriptor
movl $varBuffer, %ecx
movl $1, %edx
int $0x80
# done, go for the next char
goForTheNextChar:
jmp processingLoop
finishProcessing:
movl $0, varExitCode #everything went OK, set exit code to 0
exitToOs:
movl varOutputHandle, %ebx # file_descriptor
movl varInputHandle, %ebx
movl $1, %eax
movl varExitCode, %ebx
int $0x80
closeFile:
cmpl $-1, %ebx
movl $6, %eax #sys_close
int $0x80
cmpl $3, varBuffer seems to never be true therefore I can't jump to exitToOs.
In your code you have:
cmpl $3, varBuffer
This can never be true because you do not have binary data in your file.
When you read character by character, you are reading ASCII values. In order to properly make this comparison you must do one of two things:
Convert the character read from ASCII to decimal
Compare the read value to an ASCII value
Since you are relying on $0 to identify when you have read zero bytes, I would suggest that you take the (easier) approach of checking for the ASCII value that you would like to find. In your case this would be:
cmpb $'3', varBuffer # Compare character to 0x33 / 51 / "3"
I am trying to add two 128 bits numbers using ATT assembly syntax in linux ubuntu 64b and I am debugging it in gdb so I know that after every loop the result of adding two parts is correct but how to store all 4 results together?? I was considering adding every result to the stack but I can't add the register content to the stack, right? Am I even doing it correctly? I am a real beginner in assembler but I need it for uni :/
EXIT_SUCCESS = 0
SYSEXIT = 1
number1:
.long 0x10304008, 0x701100FF, 0x45100020, 0x08570030
number2:
.long 0xF040500C, 0x00220026, 0x321000CB, 0x04520031
.global _start
_start:
movl $4, %edx
clc
pushf
_loop:
dec %edx
movl number1(,%edx,4), %eax
movl number2(,%edx,4), %ebx
popf
adcl %ebx, %eax
cmp $0, %edx
jne _loop
popf
jnc _end
push $1
_end:
mov $SYSEXIT, %eax
mov $EXIT_SUCCESS, %ebx
int $0x80
Hi Guys I got some annoying problem ,so I try to write a code just to reverse small string sequential
I Already got this :
.section .data
string:
.ascii "AAAAAABBBBBB"
length:
.quad . -string #Dot = 'here'
.section .text
.globl _start #Make entry point visible to linker
_start:
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80 #Call Operating System
movl length,%edi #counter
shrl $1,%edi #half of string
movl $0,%ecx #start from index one
movl length,%edx #start from end
reverse:
movl string(,%ecx,1),%eax
movl string(,%edx,1),%ebx
movl %eax,string(,%edx,1)
movl %ebx,string(,%ecx,1)
inc %ecx
dec %edx
dec %edi
loop reverse #looping
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80 #Call Operating System
movl $0, %ebx #Make program return syscall exit status
movl $1, %eax #1=exit
int $0x80 #Call System Again
And it's not working correctly , cuz in gbd i get wrong values in registers after making
movl string(,%ecx,1),%eax
or the next steps I think there should be in %eax value for A letter but its doesn't any ideas ?
Working at 64arch but emulating in as --32 so its problem with my addressing i guess
You should be processing bytes not longs, so use movb with 8 bit registers (al and bl, for example). Also, the LOOP instruction uses ECX automatically, you probably meant JNZ there to repeat until EDI reaches zero.
I made simple arguments adder with assembly but it isn't working
it always return 0
.section .data
.section .text
.global _start
_start:
call adder
movl %eax,%ebx
movl $1,%eax
int $0x80
adder:
pushl %ebp
movl %esp,%ebp
movl $0,%eax #eax = return
movl $1,%ebx #ebx = index
movl 8(%ebp),%ecx #number of args
loop:
cmpl %ebx,%ecx
jg exit
addl 8(%ebp,%ebx,4),%eax
incl %ebx
jmp loop
exit:
movl %ebp,%esp
popl %ebp
ret
There are a few problems with this code, but you're on the right track.
1. Loop condition
You are using this to quit the loop when ebx >= ecx:
cmpl %ebx,%ecx
jg exit
The syntax is rather confusing, but this actually means "exit if ecx is greater than ebx". Changing it to jng exit fixes this problem.
2. Arguments on stack
You are referring to arguments with 8(%ebp,%ebx,4), but the arguments actually start at 12(%ebp). You are right in that you should start with index 1, because the argument with index 0 is merely the name of the program.
3. Arguments are always strings
The arguments on the stack are only pointers to strings. movl 12(%ebp),%eax will not put a number from the command line in eax. It will only put a memory address in eax, which points to a series of characters that make up the first argument.
To get the number represented by the string "123" you need to parse it with a function such as atoi. atoi will then return 123 in eax.
Here's what the code looks like once these things are fixed. I put a comment next to each changed line.
.section .data
.section .text
.global _start
_start:
call adder
movl %eax,%ebx
movl $1,%eax
int $0x80
adder:
pushl %ebp
movl %esp,%ebp
movl $0,%eax #eax = return
movl $1,%ebx #ebx = index
movl 8(%ebp),%ecx #number of args
loop:
cmpl %ebx,%ecx
jng exit # quit if ecx is not greater than ebx
pushl %eax # save registers on stack
pushl %ecx # because they will be destroyed soon
pushl 12(%ebp,%ebx,4) # push next argument pointer on stack
call atoi # invoke atoi, this destroys registers eax,ecx,edx
add $4,%esp # restore stack pointer
mov %eax,%edx # atoi returns the value in eax, save that to edx
popl %ecx # restore ecx from stack
popl %eax # restore eax from stack
addl %edx,%eax # add parsed number to accumulator
incl %ebx
jmp loop
exit:
movl %ebp,%esp
popl %ebp
ret
And the program seems to work now:
$ gcc -nostartfiles test.S -m32 && ./a.out 1 2 3 4 5 ; echo $?
15
The program returns the result in its exit value, which means it can not count higher than 255 :)
It would probably be better to let the program print the result to stdout, using printf.