DEV Community

Krinskumar Vaghasia
Krinskumar Vaghasia

Posted on

SPO-lab-4: Assembly in x86_64 & ARM64

Introduction and ObjDumps

We are finally stepping into the realm where we start doing stuff with assembly. For lab 4, we compared assembly's version to c's version of a simple hello world programme.

We used to makefile to make a executable of a c programme. When we used objdump -d hello in both aarch64 and x86. For your reference here is the x86's version of the dump which only includes the main part.

0000000000401126 <main>: 401126: 55 push %rbp 401127: 48 89 e5 mov %rsp,%rbp 40112a: bf 10 20 40 00 mov $0x402010,%edi 40112f: b8 00 00 00 00 mov $0x0,%eax 401134: e8 f7 fe ff ff call 401030 <printf@plt> 401139: b8 00 00 00 00 mov $0x0,%eax 40113e: 5d pop %rbp 40113f: c3 ret 
Enter fullscreen mode Exit fullscreen mode

Now we did the same for the assembly's version and the main part can be seen below. NOTE: this dump is from x86 and from GNU as assembler.

0000000000401000 <_start>: 401000: 48 c7 c2 0e 00 00 00 mov $0xe,%rdx 401007: 48 c7 c6 00 20 40 00 mov $0x402000,%rsi 40100e: 48 c7 c7 01 00 00 00 mov $0x1,%rdi 401015: 48 c7 c0 01 00 00 00 mov $0x1,%rax 40101c: 0f 05 syscall 40101e: 48 c7 c7 00 00 00 00 mov $0x0,%rdi 401025: 48 c7 c0 3c 00 00 00 mov $0x3c,%rax 40102c: 0f 05 syscall 
Enter fullscreen mode Exit fullscreen mode

Assembly code in Aarch64

We were given an incomplete. code below that prints the word loop 10 times. Here is what the completed loop looks like.

 .text .globl _start min = 0 /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */ max = 10 /* loop exits when the index hits this number (loop condition is i<max) */ _start: mov x19, min loop: mov x0, 1`` adr x1, msg mov x2, len mov x8, 64 svc 0 add x19, x19, 1 /* increment the loop counter */ cmp x19, max /* see if we've hit the max */ b.ne loop /* if not, then continue the loop */ mov x0, 0 /* set exit status to 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop:\n" len= . - msg 
Enter fullscreen mode Exit fullscreen mode

We were also asked to print numbers at the end of the text representing the time of times the loops was ran, below is the code for that

.text .globl _start min = 0 /* starting value of the loop idx. this is a symbol */ max = 10 /* ending value of the loop idx. this is a symbol */ prefix = 0 _start: mov x19, min /* store min to x19, which will be our idx register */ loop: mov x0, 1 /* file descriptor: 1 is stdout */ adr x1, msg /* message location (memory address) */ mov x2, len /* message length (bytes) */ mov x4, x19 /* copy idx(x19) to x4 */ add x4, x4, 48 /* add 48 to make it ASCII */ strb w4, [x1, 6] /* store 1 byte into the memory loc of msg + 6 */ mov x8, 64 /* write is syscall #64 */ svc 0 /* invoke syscall */ add x19, x19, 1 /* add 1 to idx(x19) */ cmp x19, max /* compare x19 to max */ b.ne loop /* branch if not equal to loop */ mov x0, 0 /* status -> 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop: 0\n" len= . - msg 
Enter fullscreen mode Exit fullscreen mode

Now the hardest part, we were asked to implement the same logic, but run the loop 30 times. This is challenging because we are now supposed keep a track of 2 variables that is one for ones place and one for second place. Below is the code that I came up with

.text .globl _start min = 0 /* starting value of the loop idx. this is a symbol */ max = 3 /* ending value of the loop idx. this is a symbol */ prefix = 0 default = 0 _start: mov x19, min /* store min to x19, which will be our idx register */ mov x20, prefix loop: mov x0, 1 /* file descriptor: 1 is stdout */ adr x1, msg /* message location (memory address) */ mov x2, len /* message length (bytes) */ mov x4, x19 /* copy idx(x19) to x4 */ add x4, x4, 48 /* add 48 to make it ASCII */ cmp x4, 58 b.ne is_not_over mov x19, default add x20, x20, 1 /* add 1 to idx(x19) */ is_not_over: mov x4, x20 /* copy idx(x19) to x4 */ add x4, x4, 48 /* add 48 to make it ASCII */ strb w4, [x1, 6] /* store 1 byte into the memory loc of msg + 6 */ mov x4, x19 /* copy idx(x19) to x4 */ add x4, x4, 48 /* add 48 to make it ASCII */ strb w4, [x1, 7] /* store 1 byte into the memory loc of msg + 7 */ b continue continue: mov x8, 64 /* write is syscall #64 */ svc 0 /* invoke syscall */ add x19, x19, 1 /* add 1 to idx(x19) */ cmp x20, max /* compare x19 to max */ b.ne loop /* branch if not equal to loop */ mov x0, 0 /* status -> 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop: 00\n" len= . - msg 
Enter fullscreen mode Exit fullscreen mode

Lastly, we were supposed to remove the prefix 0 so that the output is more cleaner, this was actually easy, I just added an extra branch

 default = 0 _start: mov x19, min /* store min to x19, which will be our idx register */ mov x20, prefix loop: mov x0, 1 /* file descriptor: 1 is stdout */ adr x1, msg /* message location (memory address) */ mov x2, len /* message length (bytes) */ mov x4, x19 /* copy idx(x19) to x4 */ add x4, x4, 48 /* add 48 to make it ASCII */ cmp x4, 58 b.ne is_not_over mov x19, default add x20, x20, 1 is_not_over: mov x4, x20 /* copy idx(x19) to x4 */ cmp x4, 0 b.eq print_ones add x4, x4, 48 /* add 48 to make it ASCII */ strb w4, [x1, 6] /* store 1 byte into the memory loc of msg + 6 */ print_ones: mov x4, x19 /* copy idx(x19) to x4 */ add x4, x4, 48 /* add 48 to make it ASCII */ strb w4, [x1, 7] /* store 1 byte into the memory loc of msg + 7 */ b continue continue: mov x8, 64 /* write is syscall #64 */ svc 0 /* invoke syscall */ add x19, x19, 1 /* add 1 to idx(x19) */ cmp x20, max /* compare x19 to max */ b.ne loop /* branch if not equal to loop */ mov x0, 0 /* status -> 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop: 0\n" len= . - msg 
Enter fullscreen mode Exit fullscreen mode

Assembly code in x86

For this part I was supposed to do everything I did for arch in x86, So save some time I will plug the final code here.

.globl _start min = 0 /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */ max = 3 /* loop exits when the index hits this number (loop condition is i<max) */ prefix = 0 default = 0 _start: mov $min,%r15 /* loop index */ mov $prefix,%r12 loop: movq $len,%rdx /* message length */ movq $msg,%rsi /* message location */ mov %r15, %r14 add $48, %r14 movq %r15, %r14 add $48, %r14 cmp $58, %r14 /* Compare r14 with ASCII value of '9' (which is 57) */ jne not_overflow inc %r12 movq $default,%r15 not_overflow: movq %r15, %r14 add $48, %r14 add $7, %rsi /* Move to the 5th index of the message */ movb %r14b, (%rsi) sub $7, %rsi movq %r12, %r14 add $48, %r14 cmp $48, %r14 /* Compare r14 with ASCII value of '9' (which is 57) */ je continue add $6, %rsi /* Move to the 5th index of the message */ movb %r14b, (%rsi) sub $6, %rsi continue: movq $1,%rdi /* file descriptor stdout */ movq $1,%rax /* syscall sys_write */ syscall inc %r15 /* increment the loop index */ cmp $max,%r12 /* see if we've hit the max */ jne loop /* if not, then continue the loop */ mov $0,%rdi /* set exit status to 0 */ mov $60,%rax /* exit is syscall #60 */ syscall .section .data msg: .ascii "Loop: 0\n" len = . - msg 
Enter fullscreen mode Exit fullscreen mode

Conclusion

This lab was an engaging exploration of assembly language programming across both AArch64 and x86 architectures. The stark differences in syntax and structure between the two made for a fascinating challenge, allowing me to appreciate the unique characteristics of each assembly language. I found the AArch64 assembly to be more intuitive, which made debugging feel somewhat more manageable compared to x86.

Completing the task of implementing loops with varying output formats sharpened my understanding of low-level programming concepts, particularly in relation to system calls and data handling.

Top comments (1)

Collapse
 
programmerraja profile image
Boopathi

This lab was a great introduction to assembly! It's fascinating to see how simple tasks like printing "Hello, World!" are handled at such a low level. I especially enjoyed the AArch64 assembly - it felt more intuitive than x86 for debugging.