I've written some assembly code on making a counter to count the length of a string.
The string is -123.
I'm having just the one issue:
My negative check (cmp %r15, %r14 / je Negative_counter) is being bypassed even if I have a negative integer
.data
S: .string "123"
Result: .quad
.text
.globl main
main:
mov $S,%rdx #Storage of string
mov $S,%rbx
mov Result, %rax #Location of result storage
mov $10, %r8
mov $1, %r11 #-1 counter creation with 2s complement
not %r11 #negation of 1
add $1, %r11 #2's complement complete
mov $1, %r12 #-1 counter creation with 2s complement
not %r12 #negation of 1
add $1, %r12 #2's complement complete, -1 established
#R[rbx] is used here.
Loop1: #loop string from end to beginning
cmp $0, (%rbx) #compare base addresss value with null
je Counter_Made #if null, branch to end loop.
add $1, %r11 #increment %r11 by 1 for each digit thats not null (creates counter for 2nd loop)
add $1, %rbx #Next string digit
jmp Loop1 #reinitiate loop
#Counter of string made -149, would given counter value of 3
#R[rdx] and r14 is used here.
Counter_Made:
cmp $0,%r11 #check if %r11 is zero
je Output #End program, output null result
mov $S, %r14 #move into register 14
sub $7, %r14 #Shift to least significant bit
mov $13, %r15
and $15, %r15
cmp %r15, %r14 #Determine negativity/positivity of integer, if <0 value is negative
je Negative_counter
jmp Positive_loop
Positive_loop:
cmp %r12,%r11 #End of loop check
je Output #Store result if loop end condition satisfied
mov (%rdx), %r10 #grab first byte in address string
sub $30,(%rdx) #Conversion from 8bitASCII to 2Bit Binary
and $15, %r10 #initialize size to match
Positive_inner_loop:
mov %r11, %r9
cmp $0, %r9 #Compare loop length with 0 to see if it needs multiplication
je InnerLoopDone #Jump to inner loop done once length = 0
imul %r8, %r10 #Place holder multiplication
InnerLoopDone:
add %r10,%rax
sub $1, %r11 #Decrease Length to grab next ten multiplication place holder position
mov 1(%rdx), %rdx #next digit position
jmp Positive_loop
Negative_counter:
add $1,%rdx
jmp Negative_loop
Negative_loop:
cmp %r12,%r11
je Negative_Complement
jmp Negative_loop
Negative_Complement:
not %rdx #Convert to 2's complement with negation and then + 1
add %r14,%rdx
jmp Output
Output:
ret
I think you're talking about this block of code. I've re-commented it with less useless comments. e.g. move into register 14 doesn't tell you anything you can't tell from the mov $S, %r14 instruction itself. Comments should explain what's going on in the algorithm. Assume that the person reading the comments has a copy of the instruction reference manual available, so only comment on the mechanical details if you're doing something non-obvious. (Like using a flag that's still set from a few instructions ago).
mov $S, %r14 # r14 = pointer to the start of the string
sub $7, %r14 # r14 = pointer to 7 bytes before the beginning of the string
mov $13, %r15
and $15, %r15 # r15 = 13 & 0xF = 13
cmp %r15, %r14 #
je Negative_counter # jump if (S-7) == 13
# jmp Positive_loop # this is totally redundant, you don't need a jmp to jump over the blank line before the next block of code.
Positive_loop:
Clearly S-7 (i.e. &S[-7] in C syntax) is never going to equal 13, because addresses of things in the .data or .rodata section will never be that close to 0 on Linux.
You could have easily seen this with a debugger, by setting a breakpoint or single-stepping until you got to the cmp/je and looking at the contents of those regs.
See the bottom of the x86 tag wiki for a quick explanation of putting gdb into layout reg mode where it shows the register values as you single-step.
There are probably a lot of other things wrong with your code, too, but it's long and I didn't read it all.
Related
I am trying to link 2 files. There are 3 symbols (Assembly procedures) which I am trying to link. It worked perfectly with static linking, but when trying to do it dynamically - I receive an error.
/usr/bin/ld: warning: type and size of dynamic symbol `parse_intro' are not defined
/usr/bin/ld: warning: type and size of dynamic symbol `time_to_print' are not defined
/usr/bin/ld: warning: type and size of dynamic symbol `optimizing' are not defined
First, I am making a file into a shared library, from which I want to export 3 symbols. Code of this file:
.data
premsg:
.ascii "0x" #to make the number have distinct x16 look when printed, this will be used as a prefix before a number
msg: #placeholder msg label, reserved memory will be used to store parsed number
.space 16 #16 bytes for 32 ascii numbers
nextline:
.ascii "\n" #switching to the next line after 16 ascii numbers
var1:
.quad 0x00000000000ef12b #number which will be parsed
.global parse_intro
.global optimizing
.global time_to_print
.text
parse_intro:
#binary mask for cutting smallest 4 bits (single number) from the 8 byte number
mov $0b0000000000000000000000000000000000000000000000000000000000001111, %r9
mov $0x1, %rbx #multiplier that will be used to shift from first 4 bits to proper position in the ascii number
mov $0x0, %r12 #register that will hold first half of the reversed number
mov $0x0, %r14 #register that will hold second half of the reversed number
jmp parse_start
parse_start: #main body of the parsing number to ASCII procedure
mov %r8, %rax #making a copy of a number before shifting it
shr $4, %r8 #shifting original number to 4 numbers right to go to the next number on the next step
and %r9, %rax #taking last 4 bits from a number
cmp $0xa, %rax #checking if a number is smaller than 0xa
jl zero_to_9 #jumping to do +30 procedure if it's not x16 numeric symbol
add $0x57, %rax #adding 57 because to change a number to ASCII number from a to f you need to add x16 57
mul %rbx
mul %rbx #we multiplying it twice because we go from 8 bytes to 16 bytes
add %rax, %r12 #adding summed with 30 and increased twice by rbx coefficient piece of the initial number to the previous pieces of a number to put it together in ASCII
shl $4, %rbx #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
jmp check_for_overflow #going to overflow check after adding 8 bytes to %r12
zero_to_9: #function to change from bit number to ascii number
add $0x30, %rax #adding 30 because to change a number to ASCII number from 0 to 9 you need to add x16 30
mul %rbx
mul %rbx #we multiplying it twice because we go from 8 bytes to 16 bytes
add %rax, %r12 #adding summed with 30 and increased twice by rbx coefficient piece of the initial number to the previous pieces of a number to put it together in ASCII
shl $4, %rbx #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
jmp check_for_overflow #going to overflow check after adding 8 bytes to %r12
check_for_overflow: #function to store half of the value inside a second register, because it goes from 8 bytes to 16 bytes when parsed to ASCII
mov $0x1000000000000000, %rax #value for overflow check
cmp %rax, %r12 #checking if r12 is overflowed
jle looping #doing a loop to parse_start with retq if loop ends in case if %r12 is not going to get overflow
cmp $0x1, %rcx #checking if count is 1 and then
je finishing #Finishing to avoid moving %r12 to %r14 another time
mov %r12, %r14 #moving %r12 value to store in %r14
xor %r12, %r12 #resetting %r12
mov $0x1, %rbx #resetting rbx to start from the first position
jmp looping #doing a loop to parse_start with retq if loop ends
looping:
loop parse_start
retq #exiting back to start if loops end, otherwise back to parse_start and decrementing %rcx
optimizing:
#binary mask for cutting lower 4 bytes out in optimization routine
mov $0b1111111111111111111111111111111100000000000000000000000000000000, %rbx
mov $16, %rcx #the counter
movq %r8, %rax #moving the initial number value to accumulator register %rax
andq %rbx, %rax #leaving only first 32 not as 0 to check if biggest part of the number is full of 0 or not
cmp $0, %rax #checking if number is full of 0
jne finishing #first (from the left) 32 bits aren't full of 0 so we can't ignore them
subq $8, %rcx #second half of a number is full of 0, so we can only count from 8 instead of 16
retq #returning to _start
finishing:
retq #little function to jump return
time_to_print:
mov $0b1111111100000000000000000000000000000000000000000000000000000000, %r9
cmp $0, %r14 #checking if we skipped 4 bytes because the original had zeroes in the biggest part of the number
je skipped_bytes #jumping to function that will swap r14 and r12, so that lower part is kept in r14 and r12 is full of zeroes (0x30 in ASCII)
mov $56, %rcx #making a counter which would stop the jump loop which would also work as a shift left value
call byte_fun #calling the reversing procedure
mov %r14, %r12 #moving second half of the number to r12
mov %rax, %r14 #storing the first half of the number from the accumulator having the value after completing byte_fun to %r14
mov $56, %rcx #we are putting 56 and not 64 because the last step will be made after the loop to avoid additional actions
call byte_fun #reversing the second half of a number
mov %rax, %r12 #moving stored second half of the number to the different register
lea msg(%rip), %rax #storing the address of the msg inside %rax
mov %r14, (%rax) #Putting on the first half of the reserved memory by msg, linked to %rax through previous instruction, the first half of the number needed to display formatted to ASCII saying mov value (rax) makes you move the value to the address stored in the register
add $8, %rax #adding 8 to address the second half of the reserved memory in msg
mov %r12, (%rax) #putting the second half of the reversed number to an address of the last 8 bytes resrved by msg
mov $1, %rax #putting 1 to %rax for printing syscall
mov $1, %rdi #puttin 1 to %rdi for printing syscall
lea msg(%rip), %rsi #taking a position independent link to the msg label with numbers related to ascii data and putting it to a printing register %rsi
mov $16, %rdx #setting length of 16 bits (2 per 1 number) and 1 bit for newline \n char
syscall #syscalling the print with the proper number
retq #returning to the _start body
byte_fun: #start of the reverse function to save the entry point
pop %rbp #putting entry point to the %rbp register to successfully return after a few jumping back and forth
jmp byte_reverse #jumping to the main body of the reverse function
byte_reverse: #function which separates the highest 2 bits from the reversed number and then shifts the reversed number by 8 bits (2 numbers) to the left and then switches 8 bits to their proper position and pushes them to a stack.
mov %r12, %rax #putting a copy of the currently shifted reversed number to an accumulator register %rax
shl $8, %r12 #shifting reversed number to the left for the next loop cycle
and %r9, %rax #applying binary mask which will only leave 8 bytes (or 2 numbers)
shr %cl, %rax #shifting current 8 bits of a reversed number right, prior to %rcx count, to reverse their position
push %rax #pushing a shifted piece of a number to a stack
sub $8, %rcx #substracting 8 from count to represent a shifting of the next 8 bits
jne byte_reverse #if sub from %rcx not resulted in zero - we are looping
push %r12 #pushing last 2 numbers of the reversed number, shifted to the left, as the final piece of the reversed value, which will be accessed first from stack to start the reverse
mov $8, %rcx #switching count register to 8 for a future loop inside byte_back
xor %rax, %rax #cleaning up the accumulator to 0
jmp byte_back #jumping to a code which put 8 elements inside stack all together and puts them back to stack as a single entity
byte_back: #function which sums elements in the stack to get the reversed version of the number
pop %rbx #poppint highest stack element to a register
add %rbx, %rax #adding the highest element to an accumulator
loop byte_back #decrementing the %rcx counter and starting at byte back again
push %rbp #we exited the loop and now putting the address of the print procedure to the top of the stack to get back to a procedure after calling the byte_fun
retq #returning to print procedure
skipped_bytes: #function to avoid issues with printing when only 4 bytes of the original number were evaluated in parsing
#I put this print of "0x" to avoid _start code because I want to use this program as a library
mov $1, %rax #putting 1 to %rax for print syscall
mov $1, %rdi #putting 1 to %rdi for print syscall
lea premsg(%rip), %rsi #putting a link to "0x" ascii value to %rsi to print it
mov $2, %rdx #setting 2 bytes to display 2 characters
syscall #syscall for printing "0x"
mov %r12, %r14 #moving the first half of the reversed number to a register that will be pushed to a stack first, so it would be last when we get it back
mov $0x3030303030303030, %r12 #changing a second half or reversed number to ascii zeroes
jmp time_to_print #we are ready to start the printing procedure
I make this program into .so file by using a command:
gcc printing.s -shared -o libprint.so
Then, I use these 3 procedures from this .so (parse_intro, time_to_print, and optimizing)in the main file, which has this code
.data
linked_space: #space reserved for linked list nodes
.space 0x3000
list_head: #default list_head value
.quad 0x0
.quad 0x0
opening_bracket: #part of printing function construct
.ascii "["
straight_line: #part of printing function construct
.ascii "|"
closing_part: #part of printing function construct
.ascii "] -> "
last_part: #part of printing function construct, representing empty "first" node
.ascii "[empty|node]\n"
cut_error_text:
.ascii "Error: can't cut a core node\n"
.global _start
.text
add_head: #function which adds new element as head and makes a link to a previous head element
mov list_head(%rip), %rax #moving contents of a label (link to a head node) to a register
add $16, %rax #Moving the link to a point where a new node will start (1 node is 16 bytes)
mov %rbx, (%rax) #Putting a value that we want to hold in a new node inside the value address of a new head node
add $8, %rax #Moving address inside register by 8 to put a link to a previous head
mov list_head(%rip), %rdx #Moving old head address to an %rdx to put it then inside the new head
mov %rdx, (%rax) #Putting link of the old head inside a node of a new head
add $16, list_head(%rip) #Changing label which points to the head element to the new node we created
retq
cut_head: #Function which cuts the head by shifting the label 16 bytes back, with exception check to avoid touching core node
mov list_head(%rip), %rax #Putting link of the current head element which we will cutaway
add $8, %rax #Getting a link to a previous element to check if it's 0x0, which means it's a core node
cmp $0, (%rax) #Comparing it to 0
je cut_error #Jumping to error version of the cut if it's equal
sub $24, %rax #Moving to the beginning of the previous element, 16 bytes + 8 after previous add 8
mov %rax, list_head(%rip) #Moving new link to a head label
retq
cut_error: #Function which prints error text and avoid cutting the core element
mov $1, %rax #Printing error text
mov $1, %rdi
lea cut_error_text(%rip), %rsi
mov $29, %rdx
syscall
retq
pre_print: #Intro for print to put first head element, to avoid issues with loop shenanigans
push list_head(%rip)
jmp print_node #Moving to main print function
print_node: #Function which takes functions from printing code and prints linked list visually
pop %rbx #Putting stored link to node value into %rbx
push %rbx #Pushing back to a stack a link to avoid mutations of a link after working with %rbx
add $8, %rbx #Moving link to the address part of the node with the address to a previous list
mov (%rbx), %rax #Putting actual address to the previous node to check if it's 0x0, which means, it's a first empty node
cmp $0, %rax #Comparing link inside %rax to 0
je return_printing #If it's zero - moving to the final part of the print, where the first empty node is printed with \n char
mov $1, %rax #Printing opening bracket
mov $1, %rdi
lea opening_bracket(%rip), %rsi
mov $1, %rdx
syscall
pop %rbx #Getting link to the value field of the current node
mov (%rbx), %r8 #Moving it to %r8, which is a register that will contain a numeric value for future printing
push %rbx #Saving link stored inside %rbx because %rbx will be used in printing functions
call optimizing #Printing routine consists of 3 functions which need to be called from printing part of the program
call parse_intro
call time_to_print
mov $1, %rax #Printing straight line to separate value from link
mov $1, %rdi
lea straight_line(%rip), %rsi
mov $1, %rdx
syscall
pop %rbx #Putting link back from the stack again in rbx
push %rbx #Storing the link before mutating it again
add $8, %rbx #Changing link to the address which has a link to the previous element
mov (%rbx), %r8 #Moving link to %r8 to print it
call optimizing #Launching a print routine
call parse_intro
call time_to_print
mov $1, %rax #Printing closing part of the node "construct"
mov $1, %rdi
lea closing_part(%rip), %rsi
mov $5, %rdx
syscall
pop %rbx #Popping link of the printed node to shift it 16 bytes back to move to the previous node
sub $16, %rbx #Moving to the previous node
push %rbx #Putting a link to the previous node inside stack to use it later
jmp print_node #Looping back to print_node
return_printing: #Finalizing function which will print the first empty node and \n char
mov $1, %rax
mov $1, %rdi
lea last_part(%rip), %rsi
mov $13, %rdx
syscall
pop %rbx #Since we didn't pop the stored value in loop body, we need to get rid of it from stack to jump back to _start
retq
list_initialization:
lea linked_space(%rip), %rax #initializing the first node, it already has 0x0 as value and it's needed to put on the space for the linked list
mov %rax, list_head(%rip) #Putting link to the list_head, which currently contains the first node value/link 0x0, on the linked_space
retq
_start:
call list_initialization
#call pre_print #Function which prints the linked list
#mov $1, %rbx #%rbx will hold value which will be put inside new linked list node
#call add_head #Function which adds new node as head element
#call pre_print
#mov $2, %rbx
#call add_head
#call pre_print
#mov $3, %rbx
#call add_head
#call pre_print
#call cut_head #Function which cuts head element and moves link to a previous element
#call pre_print
#call cut_head
#call pre_print
#call cut_head
#call pre_print
#call cut_head
#call pre_print
mov $60, %rax #Exiting from a program
xor %rdi, %rdi
syscall
And compile it into the binary file, which should be able to run, by command:
gcc -L /home/*path_to_folder_with_so_file* -g -nostdlib -o output linkedlist.s -lprint
I also tried to compile the same file but commented out 6 lines where the program tries to access files from a dynamically shared library. Here is the "readelf" contents of this file.
https://pastebin.com/WUQz09K2
And readelf of .so file:
https://pastebin.com/1F74euqP
What am I potentially missing, why the original file can't find imported symbols from .so?
Since I can't put a comment as a solution, so, I will put an answer myself, which was given by fuz.
The important thing to do here was to pay attention to the error message (I know, how obvious). Every dynamically linked symbol in GAS assembly, even without C libraries, requires to have a type and size assigned inside the .so file, because GCC can't get this information about exported symbols on its own, and the programmer needs to explicitly give it. For example, for a function foo
foo:
...
ret
You need to give a function a type by putting
.type foo, #function
somewhere in the code, I did it on the next line after the
.global foo
line where I make the same function global, allowing it to be exported.
And, to give a size to the dynamic symbol, you need to put the
.size foo, .-foo
right after the last instruction (like, right after the "jmp" or "ret"). "Dot" is the current address and the foo is the address of the first instruction inside the "foo" function. So, by subtracting an address of "foo" out of the current address you are getting the size of a "foo".
As a learning exercise, I've been handwriting assembly. I can't seem to figure out how to load the value of an address into a register.
Semantically, I want to do the following:
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
lea (%ecx, %ecx), %edi # Pull the value at address into %edi
cmp $97, %edi # Compare to 'a'
je done
I've written a higher-level implementation in C:
char buffer[1];
int main()
{
read(0, buffer, 1);
char a = buffer[0];
return (a == 'a') ? 1 : 0;
}
But compiling with gcc -S produces assembly that doesn't port well into my implementation above.
I think lea is the right instruction I should be using to load the value at the given address stored in %ecx into %edi, but upon inspection in gdb, %edi contains a garbage value after this instruction is executed. Is this approach correct?
Instead of the lea instruction, what you need is:
movzbl (%ecx), %edi
That is, zero extending into the edi register the byte at the memory address contained in ecx.
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
movzbl (%ecx), %edi # Pull the value at address ecx into edi
cmp $97, %edi # Compare to 'a'
je done
Some advice
You don't really need the movz instruction: you don't need a separate load operation, since you can compare the byte in memory pointed by ecx directly with cmp:
cmpb $97, (%ecx)
You may want to specify the character to be compared against (i.e., 'a') as $'a' instead of $97 in order to improve readability:
cmpb $'a', (%ecx)
Avoiding conditional branches is usually a good idea. Immediately after performing the system call, you could use the following code that uses cmov for determining the return value, which is stored in eax, instead of performing a conditional jump (i.e., the je instruction):
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
cmovz %edx, %eax # conditionally move edx(=1) into eax
ret # eax is either 0 or 1 at this point
edx was set to 1 prior to the system call. Therefore, this approach above relies on the fact that edx is preserved across the system call (i.e., the int 0x80 instruction).
Even better, you could use sete on al after the comparison instead of the cmov:
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
sete %al # conditionally set al
ret # eax is either 0 or 1 at this point
The register al, which was set to zero by means of xor %eax, %eax, will be set to 1 if the ZF flag was set by the cmp (i.e., if the byte pointed by ecx is 'a'). With this approach you don't need to care about thinking whether the syscall preserves edx or not, since the outcome doesn't depend on edx.
This is my code so far.
.data
S: .string "-149"
Length: .byte -1
Result: .quad
.text
.globl main
main:
mov S,%rdx #Storage of string, counter, position and result in memory
mov Length, %rcx
mov Result, %rax
mov $10, %r10
mov $30, %r13
mov $-1, %r9
Loop1: #loop string from beginning to end
cmp $0,0(%rdx) #compare base addresss value with null
je Counter_Made #if null, branch to end loop.
add %r14, Length #increment length by for each digit thats not null (creates counter for 2nd loop)
add $1, %rdx #increment base by 1 to move onto next digit in string
jmp Loop1 #reinitiate loop
Counter_Made:
cmp %r15,Length #check if length is zero
je Output #End program, output null result
cmp %r15,Length(%rdx) #Determine negativity/positivity of integer
jl Negative_counter
jmp Positive_loop
Positive_loop:
cmp %r9,Length #End of loop check
je Output #Store result if loop end condition satisfied
mov %r10, Length(%rdx) #Store byte of integer in supplementary register
sub %r13, %r10 #Conversion from 8bitASCII to 2Bit Binary
imul %r11, %r10 #Place holder multiplication
add %r10, %rax #Store cumulative addition in memory
sub %r14, Length #Length decrement
jmp Positive_loop #reloop
Negative_counter:
sub %r14,Length
jmp Negative_loop
Negative_loop:
cmp %r9,Length
je Negative_Complement
mov %r10, Length(%rdx) #Store byte of integer in supplementary register
sub %r13, %r10 #Conversion from 8bitASCII to 2Bit Binary
imul %r10, %r10 #Place holder multiplication
add 0(%rdx), %rax #Store cumulative addition in memory
sub %r14, Length
jmp Negative_loop
Negative_Complement:
not %rdx #Convert to 2's complement with negation and then + 1
add %r14,%rdx
jmp Output
Output:
mov %rdx, Result
ret
#size mismatch for imul
#Specific place in memory to put output or no?
The code is supposed to convert a character string that represents any signed integer to its 2’s complement value.
I'm receiving a segmentation fault in one of my loops and I've tried multiple different methods here but to no avail - could anyone explain how I should about fixing this segfault? I'm stumped.
Here is the GDB errors
Program received signal SIGSEGV, Segmentation fault.
Loop1 () at data.s:18
18 cmp $0,0(%rdx) #compare base addresss value with null
This is the second error.
Counter_Made () at data.s:28
28 cmp $0,Length(%rdx) #Determine negativity/positivity of integer, if <0 value is negative
I'm suspecting its the Length(%rdx) method that I'm trying to interpret the loop with. Would it be better to sub $1,%rdx
You want
mov $S,%edx
to load the address of the string into %rdx. This works because the program image is always loaded into the lower 4 GB of the address space. Alternatively, you can use a %rip relative lea to load the address even if the process image is loaded outside of the first 4 GB:
lea S(%rip),%rdx
but that instruction has a somewhat longer encoding (two extra bytes).
The instruction
mov S,%rdx
loads the first eight bytes of the memory S points to into %rdx, which is not what you want.
I'm a beginner in Linux assembler and I have some questions. I'd like to read some characters from keyboard, convert it to value (I understand that this convertion should be from ASCII to decimal, right?), do some math (add, sub, multiply, whatever) and display the result in Terminal. How should I do that? I wrote some code but it probably doesn't make sense:
SYSEXIT = 1
EXIT_SUCC = 0
SYSWRITE = 4
SYSCALL = 0x80
SYSREAD = 3
.data
value: .space 5, 0
value_len: .long .-value
result: .long
result_len: .long .-result
.text
.global _start
_start:
movl $SYSREAD, %eax
movl $EXIT_SUCC, %ebx
movl $value, %ecx
movl value_len, %edx
int $SYSCALL
movl $0, %edx
movl value_len, %ecx
for:
movb value(, %edx, 1), %al
subb $48, %al
movb %al, result(, %edx, 1)
inc %edx
loop for
add $10, result
movl $0, %edx
movl result_len, %ecx
for1:
movb result(, %edx, 1), %al
add $48, %al
movb %al, result(, %edx, 1)
inc %edx
loop for1
movl $SYSWRITE, %eax
movl $SYSEXIT, %ebx
movl $result, %ecx
movl result_len, %edx
int $SYSCALL
movl $SYSEXIT, %eax
movl $EXIT_SUCC, %ebx
int $SYSCALL
I don't know if I should reserve memory by spaces? Or reading characters in loop?
How to convert it, to be able to make some math operation and then convert it to be able to display it?
I know that to get the value of ASCII char I should subtract 48, but what next?
I had an idea to multiply each bits by 2^k where k is 0,1,2...n it's good idea? If so, how to implement something like this?
As you can see I had a lot of questions, but I only need to someone show me how to do, what I am asking about. I saw some similar problems, but nothing like this in Linux.
Thank you in advance for the all information.
All the best.
At first reading and writing the console in Linux is by using the file functions with special console handles, that have always the same values: STDIN=0, STDOUT=1 and STDERR=2.
At second you will need some decent documentation about Linux system calls. Notice that the C-centric one (like "man") are not suitable, because C language does not use the system calls directly, but has wrappers that often change the arguments and the result values.
On the following site you can download an assembly-centric SDK for Linux, that contains the needed documentation and many examples and include files.
If you only need the help files, you can browse them online: Here
If the problem is the conversion from ASCII string to number and then back to string, here are two simple procedures that can do the job, if the requirements are not so big. The StrToNum is not so advanced, it simply convert decimal unsigned number:
; Arguments:
; esi - pointer to the string
; Return:
; CF=0
; eax - converted number
; edx - offset to the byte where convertion ended.
;
; CF=1 - the string contains invalid number.
;
StrToNum:
push ebx esi edi
xor ebx,ebx ; ebx will store our number
xor eax,eax
mov al,[esi]
cmp al,'0'
jb .error
cmp al,'9'
jbe .digit
jmp .error
.digit:
sub al,'0'
add ebx,eax
inc esi
mov al,[esi]
cmp al,'0'
jb .finish
cmp al,'9'
ja .finish
mov edx,ebx ; multiply ebx by 10
shl ebx,3
add ebx,edx
add ebx,edx
jmp .digit
.finish:
mov eax, ebx
mov edx, esi
clc
pop edi esi ebx
ret
.error:
stc
pop edi esi ebx
ret
NumToStr is pretty flexible. It converts number to a string in any radix and with sign:
;**********************************************************************************
; NumToStr converts the number in eax to the string in any radix approx. [2..26]
; Arguments:
; edi - pointer to the string buffer
; ecx - radix
; eax - number to convert.
; There is no parameter check, so be careful.
; returns: edi points to the end of a converted number
;**********************************************************************************
NumToStr:
test eax,eax
jns NumToStrU
neg eax
mov byte [edi],"-"
inc edi
NumToStrU:
cmp eax,ecx
jb .lessA
xor edx,edx
div ecx
push edx
call NumToStrU
pop eax
.lessA:
cmp al, 10
sbb al, 69h
das
stosb
ret
I am using 64-bit linux and programming in assembler using gas. The issue I am having is I let the user enter lets say "1 + 12" using the system call read, and saving it as follows.
My read function:
.type _read, #function
_read:
pushq %rbp # Save old base pointer
movq %rsp,%rbp
movq $200,%rdx # MAX characters to retrieve
movq $equation,%rsi # Buffer for equation string
movq $0,%rdi # STDIN
movq $0,%rax # SYS_READ
syscall
movq %rbp,%rsp # Restore base pointer
popq %rbp
ret # Return from function
equation is declared as:
.section .bss
.lcomm equation, 200
So I parse through each byte of equation trying to save the numbers, but if they enter "12" than I would first get 1 and than 2, I need to somehow save 12 on the stack and be able to just popq %rax and have "12" in there. I am not sure how to go about this? Any input would be greatly appreciated.
You'll have to write some sort of parser. Here's an example (I'm using 16-bit assembly in Intel syntax, but you get the gist of it):
; Parses the zero-terminated string 'equation', converts any numbers
; found in that string from strings to integers and pushes them on
; the stack.
parse_equation:
pop di ; pop the return address
lea si,[equation]
cld
xor cl,cl ; # of chars in the currently parsed number
skip_non_number:
lodsb
test al,al
jz end_of_equation
cmp al,'0'
jb skip_non_number
cmp al,'9'
ja skip_non_number
sub al,'0' ; convert '0'..'9' -> 0..9
movzx bx,al ; zero-extend to word and store in bx
parse_number:
inc cl
lodsb
test al,al
jz end_of_equation
cmp al,'0'
jb end_of_number
cmp al,'9'
ja end_of_number
sub al,'0'
mov ch,al
mov ax,10
mul bx
movzx bx,ch
add bx,ax ; bx = bx*10 + (word)al
jmp parse_number
end_of_number:
push bx ; store the parsed number on the stack
xor cl,cl
jmp skip_non_number ; start over again
end_of_equation:
test cl,cl
jz nothing_to_push
push bx ; the string ended with a number; push it
nothing_to_push:
jmp di ; return
My code ignores anything that isn't a number (like arithmetic operators), and doesn't handle signed numbers. I'll leave it to you to figure out how to handle such things.