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"
Related
I don't know why my program doesn't show the result of the sum of elements in the stuck. It just shows the string '-----'
I'm working on Linux.
.data
#################################
tabEntier : .long 3, 4, 2, 10, 16, 5, 6 #long = 4
maVar: .space 4
msgFin: .string "\n-----\n"
taillemsgFin = . - msgFin
################################
.text
.global _start
_start:
movl $3, %eax # eax ?
movl $0, %ebx # ebx ?
movl $tabEntier, %ecx # ecx ?
additionne:
addl (%ecx), %ebx # ebx ?
addl $4, %ecx # ecx ?
decl %eax # eax ?
jnz additionne
stocke:
movl %ebx, maVar
messageSortie:
movl $4, %eax
movl $1, %ebx
movl $msgFin,%ecx
movl $taillemsgFin,%edx
int $0x80
sortie:
movl $0, %ebx
movl $1, %eax
int $0x80
The result of the sum must be shown after '------' message
I don't know why my program don't show the result of the sum
It's because you don't actually have any code for outputting that. You syscall-4 to output the ---- string, then you immediately syscall-1 to exit.
If you want to output an integral value as well, you're going to need some code to do it. While modern CPUs have speculative execution, that doesn't mean speculating on what you really wanted to do :-)
You may also want to check what you're loading into eax. This seems to be the count of elements and there are seven in the list, but you only process the first three. No doubt you would have found that once you'd successfully output the sum but I thought I'd mention it anyway.
In terms of the code required to fix your issue, I include a small utility function for printing of signed longs, along with a test harness so that you can see it in action.
The test harness is a simple bash script which processes a series of test data items, injecting them in turn into the actual test program:
#!/bin/bash
# Selected test points, and some random ones.
testdata="0 1 -1 9 10 -9 -10 99 100 2147483647 -2147483648"
for i in {1..10} ; do
testdata="${testdata} ${RANDOM} $((0 - ${RANDOM}))"
done
# Do each test item.
for item in ${testdata} ; do
# Morph the asm template to create actual asm file,
# then assemble and link.
sed "s/XYZZY/${item}/" prog.in >prog.asm
as --32 -o prog.o prog.asm
ld -melf_i386 -o prog prog.o
# Test that output is as expected.
result="$(./prog)"
if [[ "${item}" == "${result}" ]] ; then
echo "Okay ('${item}')"
else
echo "BAD ('${item}' -> '${result}'"
fi
done
The code for testing this is in a template prog.in so it can be processed by that test harness to produce one with the actual test item. The file contains:
.data
numOut: .string "2147483648" # Largest signed-32 magnitude.
numEnd:
numLast= numEnd - 1
negOut: .string "-" # Negative prefix.
# ======================================================================
# Actual function for output of signed long in EAX.
.text
outLong: push %eax # Save registers.
push %ebx
push %ecx
push %edx
cmpl $0, %eax
je olZero # Zero special case.
jg olPos # Already positive.
push %eax # Negative handled here.
movl $4, %eax # SysWrite "-".
movl $1, %ebx
movl $negOut, %ecx
movl $1, %edx
int $0x80
pop %eax
negl %eax # Then negate.
olPos: movl $numEnd, %ecx # Last character placed.
olStore: movl $0, %edx # eax = edx:eax / 10, remainder -> edx
movl $10, %ebx
divl %ebx
addl $'0', %edx # Turn into ASCII and store.
decl %ecx
movb %dl, (%ecx)
cmpl $0, %eax # Continue until zero.
jnz olStore
jmp olPrint
olZero: movl $numLast, %ecx # Load 0 into buffer.
movb $'0', (%ecx)
olPrint: movl $4, %eax # SysWrite call.
movl $1, %ebx # File descriptor 1.
movl $numLast, %edx # Calculate length.
incl %edx
subl %ecx, %edx
int $0x80 # And print.
pop %edx # Clean up and return.
pop %ecx
pop %ebx
pop %eax
ret
# ======================================================================
# Test harness.
.global _start
_start: movl $XYZZY, %eax # Load up test value.
call outLong
movl $1, %eax # SysExit, success.
movl $0, %ebx
int $0x80
The output of one particular run of the test harness follows:
Okay ('0')
Okay ('1')
Okay ('-1')
Okay ('9')
Okay ('10')
Okay ('-9')
Okay ('-10')
Okay ('99')
Okay ('100')
Okay ('2147483647')
Okay ('-2147483648')
Okay ('3700')
Okay ('-30889')
Okay ('19074')
Okay ('-19825')
Okay ('22601')
Okay ('-19364')
Okay ('9291')
Okay ('-24785')
Okay ('28133')
Okay ('-2892')
Okay ('20544')
Okay ('-10782')
Okay ('20878')
Okay ('-28479')
Okay ('13808')
Okay ('-9415')
Okay ('17693')
Okay ('-6797')
Okay ('16385')
Okay ('-10299')
In terms of what you need to add to your code, it's basically everything except the test harness at the end. Then, you call it with a simple:
mov $42, %eax # Load EAX however you desire.
call outLong # And print it.
I'm writing an assembly program with GAS compiler and AT&T syntax.
I have 2 files: one to read from and another one to write in.
From terminal i launch the program in this way: ./myprogram inputfile.txt outputfile.txt
Now I want to read input data character by character and this is how I'm trying to do it:
.section .data
buff_size: .long 1
.section .bss
.lcomm buff, 18
.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
popl %ebx # Get the first actual argument - file to read
# open the file
movl $5, %eax # open
movl $0, %ecx # read-only mode
int $0x80
# read the file
movl $0, %esi
# these 6 instructions read first character from inputfile.txt
movl %eax, %ebx # file_descriptor,
movl $3, %eax
movl $buff, %edi
leal (%esi,%edi,1), %ecx
movl buff_size, %edx
int $0x80
# these 6 instructions can't read the second character from inputfile.txt and I can't understand why.
movl $1, %esi
movl $3, %eax
movl $buff, %edi
leal (%esi,%edi,1), %ecx
movl buff_size, %edx
int $0x80
# open the file
popl %ebx # Get the second actual argument - file to write
movl $5, %eax # open
movl $2, %ecx # read-only mode
int $0x80
# write to STDOUT
movl %eax, %ebx # file_descriptor
movl $4, %eax
leal buff, %ecx
movl buff_size, %edx
int $0x80
# exit
movl $1, %eax
movl $0, %ebx
int $0x80
This piece of code read the first character from inputfile.txt successfully and it writes that character inside outputfile.txt...
Now I want to read and write the second character but it doesn't work as suggested in the comment I've written through the code.
FYI:
OS: Ubuntu 14 - 64 bit
Compiler: GAS
My inputfile.txt looks so: 4,3,55,15,8,9
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'm trying to print a range of ascii characters with this assembly program.
I'm trying to do it using only the registers, but haven't been having much luck. Everything looks fine to me, but I'm a novice at assembly programming and might have missed something obvious. Any insight will be appreciated. Thanks :)
emphasized text
.text
.global _start
_start:
movl $1, %edx
movl $65, %ebx
start_loop:
addl $1, %ebx
movl $0x04, %eax
int $0x80
cmpl $126, %ebx
jle start_loop
jmp start_loop
exit
movl $0, %ebx
movl $1, %eax
int $0x80
You are invoking the sys_write system call. sys_write() takes three arguments, file descriptor of the output device(it should be 1 for stdout),address of the buffer where you stored the value to be printed, and the size of the data to be printed. So you have to store file descriptor in %ebx, and store address of the buffer in %ecx and size of the data in %edx. To store the file descriptor you can use the following instruction.
movl $1, %ebx // store 1 (stdout) in ebx)
To store the size of the data you can use:
movl $1, %edx // size is 1 byte
Now, you have to store the address of the buffer, you need to put your data in the memory some where and need to store the address of the memory in %ecx. Assume that you want store the data in the stack it self, then you can do like this:
subl $4, %esp // get 4 bytes of memory in the stack
movl $65, (%esp) // store data in the memory where esp points to
movl %esp, %ecx // store address of the data in the ecx
Now you can issue the int 0x80.
movl $04, %eax // store syscall number in eax
int $0x80 // issue the trap interrupt
As a whole you can write the following code:
movl $1, %ebx
subl $0x4, %esp
movl $64, (%esp)
start_loop:
movl (%esp), %eax
addl $1, %eax
movl %eax, (%esp)
movl %esp, %ecx
movl $1, %edx
movl $0x04, %eax
int $0x80
movl (%esp), %eax
cmpl $126, %eax
jle start_loop
addl $0x4, %esp
See Linux System Calls Part2 at http://www.rulingminds.com/syscallspart2 to know more about registers and system calls usage.
"Thank you very much for the informative answer, but is there a way to store and retrieve the value to be printed in a register without pointing to it?" -- this should probably have been edited into the question.
If you insist on using only syscalls (int $0x80) to interface with the system then the answer is no. You have to somehow pass a buffer to write and rullingminds answer applies.
Using the libc putchar(3) it's straight forward. I use %ebx to keep the ascii code as this register is on linux preserved between function calls. Simply assemble using gcc filename.S (remembering to use -m32 if you are on x86_64).
.text
.extern putchar
.global main
main:
# make room for argument to putchar on the stack
sub $4, %esp
# initialize ebx with first value to print
mov $'A', %ebx
1:
# give character to print as argument
mov %ebx, (%esp)
call putchar
# move to next character
inc %ebx
# are we done?
cmp $'~', %ebx
jle 1b
# print newline
movl $10, (%esp)
call putchar
# adjust stack back to normal
add $4, %esp
# return 0 from main
mov $0, %eax
ret
simple upper-casifier loop infinitely
what's wrong with my code?
any advice?
my programming environment is linux, emacs, assembly, at&t syntax
.section .data
.section .bss
.lcomm buffer,1
.section .text
.global _start
_start:
movl %esp,%ebp
subl $8,%esp
#8(%ebp) is 2nd arg == input
#12(%ebp) is 3rd arg == output
#open,read,open,write,close
movl $5,%eax
movl 8(%ebp),%ebx
movl $0,%ecx
movl $0666,%edx
int $0x80
#%eax contains input's fd
#movl to first local var
movl %eax,-4(%ebp)
movl $5,%eax
movl 12(%ebp),%ebx
movl $03101,%ecx
movl $0666,%edx
int $0x80
#eax contains output's fd
#movl to second local var
movl %eax,-8(%ebp)
loop:
#read 1 byte from file 1st byte of data
movl $3,%eax
movl -4(%ebp),%ebx
movl $buffer,%ecx
movl $1,%edx
int $0x80
#buffer contains 1 byte of file
cmpb $0,buffer
je program_exit
pushl buffer
call convert #will return converted to %al
addl $4,%esp
movb %al,buffer
#write 1 byte from buffer to file
movl $1,%edx
movl $buffer,%ecx
movl -8(%ebp),%ebx
movl $4,%eax
int $0x80
jmp loop
program_exit:
movl buffer,%ebx
movl $1,%eax
int $0x80
.type convert,#function
convert:
pushl %ebp
movl %esp,%ebp
movb 8(%ebp),%al #1 byte data in the buffer
cmpb $'a',%al
jl convert_end
cmpb $'z',%al
jg convert_end
addb $32,%al #convert to upper
convert_end:
movl %ebp,%esp
popl %ebp
ret
Note that read(2) signals an end-of-file condition by returning 0. You're trying to find the end of the file by looking for an ascii NUL, which is very rare on Unix-derived systems. (If you want an easy way to make a terabyte-sized file, dd if=/dev/zero of=/tmp/huge bs=1048576 seek=1048576 count=1. The whole thing will be filled with ascii NUL characters. (Or the integer 0, however you want to interpret it.)
You need to modify your code to find the end of file by comparing the return value of the read(2) system call, not by looking at the data you receive into your buffer.