Earlier today I wrote some assembly code to compare 2 strings and print out whether or not they were equal to one another. I’ve been learning more assembly and so I thought I would tweak it a bit to improve the way it works and add more “features”. After some studying I’ve managed to get it working as a real function that actually takes arguments via the stack. On top of that, the function now returns a value that designates the index where the comparison failed; this is stored in the EAX register. Even further, by using a buffer in memory I was able to print a string that informs the user of the index where the comparison failed.
String Comparison Part 2
The strings being compared are stored in Str1 and Str2 directly in the .data section of the code. They are passed in as parameters by utilizing the stack. This took much longer than my first attempt, which always used the same strings and did not utilize the stack.
The code
This will print out “Strings are equal!” when executed. If you modify Str1 or Str2 so that they are not equal, you will see “Strings are not equal!” instead; depending on the modifications you make you may have to adjust the EBX register in _start since it contains the string length.
This particular example has a limitation that it will only work as designed for strings up to 10 characters. This is because when printing the index where the strings differ I have to convert the decimal value to ascii. The simple method I’m using to do this is to add the number 48, which only works on numbers between 0 and 9.
I’ll also point out that I use the 64-bit registers frequently in this code because I’m on a 64-bit machine and I have to push the 64-bit memory addresses onto the stack. Therefore this one will look quite a bit different from the last one.
.data
Str1:
.asciz "abcdefghi"
Str2:
.asciz "abcdefghi"
StrEqual:
.asciz "Strings are equal!\n"
StrNotEqual:
.asciz "Strings are not equal!\n"
StrDiffer:
.asciz "Strings differ at index !\n"
.bss
.lcomm StrDifferBuff 100
.text
.globl _start
.type StrCmp, @function
.type PrintEqual, @function
.type PrintNotEqual, @function
_start:
# for stepping through the debugger
nop
# string length is stored in rbx
movq $10, %rbx
# push the arguments to StrCmp onto the stack (in the reverse order that I'll pop them off)
pushq %rbx
pushq $Str2
pushq $Str1
# compare the strings
call StrCmp
# restore the stack pointer
addq $24, %rsp
# if EAX is the string length, the strings are equal
cmp %ebx, %eax
# if the comparison was successful the strings are equal
jz CallPrintEqual
# else they were not equal
# push the arguments to PrintNotEqual onto the stack
pushq %rax
# print the difference
call PrintNotEqual
# restore the stack pointer
addq $8, %rsp
jmp Finish
CallPrintEqual:
call PrintEqual
# finish up (both branches of the above if-else flow will get here)
Finish:
jmp ExitProgram
######################################
# compare two strings of equal length
# @param str1
# @param str2
# @param strlength
# @ret the index at which the two strings differ or $strLength if the strings are equal
######################################
StrCmp:
# save the current base pointer by pushing it onto the stack
pushq %rbp
# move the base pointer to the top of the stack
movq %rsp, %rbp
# retrieve our arguments from the stack
movq 16(%rbp), %rsi
movq 24(%rbp), %rdi
movq 32(%rbp), %rcx
# save the string length into EAX
movl %ecx, %eax
# clear the direction flag if it's set
cld
# continue comparing each character in the strings until they do not match
# or the end of the string is reached
ContinueCmp:
cmpsb
loopz ContinueCmp
# place the index of the character that did not match into EAX
sub %ecx, %eax
# restore the base pointer and return
popq %rbp
ret
ExitProgram:
movl $1, %eax
movl $0, %ebx
int $0x80
######################################
# print that the strings are not equal and the index where the comparison failed
# @param index
######################################
PrintNotEqual:
# save the current base pointer by pushing it onto the stack
pushq %rbp
# move the base pointer to the top of the stack
movq %rsp, %rbp
# retrieve our arguments from the stack
movq 16(%rbp), %rax
# put the string into the buffer
movl $StrDiffer, %esi
movl $StrDifferBuff, %edi
movl $28, %ecx
rep movsb
# increment eax by 48 to get the ascii representation of the integer
addl $48, %eax
# overwrite the last space in the string with the index where
# the strings differ
movb %al, StrDifferBuff+24
# write StrNotEqual
movl $4, %eax
movl $1, %ebx
leal StrNotEqual, %ecx
movl $24, %edx
int $0x80
# write StrDifferBuff
movl $4, %eax
movl $1, %ebx
leal StrDifferBuff, %ecx
movl $28, %edx
int $0x80
# restore the base pointer and return
popq %rbp
ret
######################################
# print that the strings are equal
######################################
PrintEqual:
movl $4, %eax
movl $1, %ebx
leal StrEqual, %ecx
movl $20, %edx
int $0x80
ret