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.