Lab 4 - 64-bit Assembly Language Lab

 Today, I will discuss 64-bit Assembly Language Lab and modify the code to get the different results.

First, let's unpack the code examples to my home directory by using tar -xvf /public/spo600-assembler-lab-examples.tgz command.

Now I can find the directory in my home directory. Let's start the lab!

1. Review, build, and run the aarh64 assembly language programs. Take a look at the code using objdump -d objectfile and compare it to the source code.



Firstly, I ran the command so that it makes an object and the executable file.


Then I ran objdump -d hello so that it prints the object code.
 

$ objdump -d hello


hello:     file format elf64-littleaarch64



Disassembly of section .text:


00000000004000b0 <_start>:

  4000b0: d2800020 mov x0, #0x1                   // #1

  4000b4: 100800e1 adr x1, 4100d0 <msg>

  4000b8: d28001c2 mov x2, #0xe                   // #14

  4000bc: d2800808 mov x8, #0x40                  // #64

  4000c0: d4000001 svc #0x0

  4000c4: d2800000 mov x0, #0x0                   // #0

  4000c8: d2800ba8 mov x8, #0x5d                  // #93

  4000cc: d4000001 svc #0x0


The above is the result of object code and below is the source code. 

$ cat hello.s

.text

.globl _start

_start:

 

mov     x0, 1           /* file descriptor: 1 is stdout */

adr     x1, msg   /* message location (memory address) */

mov     x2, len   /* message length (bytes) */


mov     x8, 64     /* write is syscall #64 */

svc     0          /* invoke syscall */

 

mov     x0, 0     /* status -> 0 */

mov     x8, 93    /* exit is syscall #93 */

svc     0          /* invoke syscall */

 

.data

msg: .ascii      "Hello, world!\n"

len= . - msg


When comparing the two, they are similar but have some differences, such as the object code printing numbers in hexadecimal format.



2. Fix the code to print a word each time it loops.

The basic code doesn't contain anything in the body of the loop so it does nothing while looping. 
.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:

    /* ... body of the loop ... do something useful here ... */

    add     x19, x19, 1
    cmp     x19, max
    b.ne    loop

    mov     x0, 0           /* status -> 0 */
    mov     x8, 93          /* exit is syscall #93 */
    svc     0               /* invoke syscall */

I added some code in the body of the loop and set the variable msg and the symbol len using .set
.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       /* file descriptor: 1 is stdout */
    adr     x1, msg   	/* message location (memory address) */
    mov     x2, len   	/* message length (bytes) */

    mov     x8, 64     	/* write is syscall #64 */
    svc     0          	/* invoke syscall */ 
add x19, x19, 1 cmp x19, max b.ne loop mov x0, 0 /* status -> 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop\n" .set len , . - msg

Then, make the pure assembler source file be able to be run in an assembly program.
Below is the output I got.



Next, let's modify the code to make the message contains the index value as well.

.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:
    add    x18, x19, '0'   /* convert binary to digit char */
    adr    x17, msg+6      /* get a pointer to # position in msg */
    strb    w18, [x17]     /* write one byte only */

    mov     x0, 1       /* file descriptor: 1 is stdout */
    adr     x1, msg   	/* message location (memory address) */
    mov     x2, len   	/* message length (bytes) */

    mov     x8, 64     	/* write is syscall #64 */
    svc     0          	/* invoke syscall */    
add x19, x19, 1 cmp x19, max b.ne loop mov x0, 0 /* status -> 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop: #\n" .set len , . - msg

This is the output:

3.  Repeat the previous step for x86_64.


To do this, I moved to portugal server and run make command. Below is the output I got.



Then I ran objdump -d hello-gas for x86_64.    

$ objdump -d hello-gas


hello-gas:     file format elf64-x86-64



Disassembly of section .text:


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 




This is hello-gas.s file code.

$ cat hello-gas.s

/* 

   This is a 'hello world' program in x86_64 assembler using the 

   GNU assembler (gas) syntax. Note that this program runs in 64-bit

   mode.


   CTyler, Seneca College, 2014-01-20

   Licensed under GNU GPL v2+

*/


.text

.globl _start


_start:


movq $len,%rdx /* message length */

movq $msg,%rsi /* message location */

movq $1,%rdi /* file descriptor stdout */

movq $1,%rax /* syscall sys_write */

syscall


movq $0,%rdi /* exit status */

movq $60,%rax /* syscall sys_exit */

syscall


.section .data


msg: .ascii      "Hello, world!\n"

len = . - msg


Below is the loop code in x86_64 assembler:
.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     $min,%r15           /* loop index */

loop:
    /* ... body of the loop ... do something useful here ... */

    inc     %r15                /* increment index */
    cmp     $max,%r15           /* see if we're done */
    jne     loop                /* loop if we're not */

    mov     $0,%rdi             /* exit status */
    mov     $60,%rax            /* syscall sys_exit */
    syscall

I modified the code to make it print the message.
.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     $min,%r15           /* loop index */

loop:

    movq	$len,%rdx	/* message length */
    movq 	$msg,%rsi	/* message location */
    movq	$1,%rdi		/* file descriptor stdout */
    movq	$1,%rax		/* syscall sys_write */
    syscall

    inc     %r15                /* increment index */
    cmp     $max,%r15           /* see if we're done */
    jne     loop                /* loop if we're not */

    mov     $0,%rdi             /* exit status */
    mov     $60,%rax            /* syscall sys_exit */
    syscall

.section .data

msg:	.ascii      "Loop\n"
	len = . - msg

This is the output:


Now, let's modify it again to print the message with the index.
.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     $min,%r15           /* loop index */

loop:
    mov     %r15, %r14
    add     $'0', %r14
    movb    %r14b, msg+6

    movq	$len,%rdx	/* message length */
    movq 	$msg,%rsi	/* message location */
    movq	$1,%rdi		/* file descriptor stdout */
    movq	$1,%rax		/* syscall sys_write */
    syscall

    inc     %r15                /* increment index */
    cmp     $max,%r15           /* see if we're done */
    jne     loop                /* loop if we're not */

    mov     $0,%rdi             /* exit status */
    mov     $60,%rax            /* syscall sys_exit */
    syscall

.section .data

msg:	.ascii      "Loop: #\n"
	len = . - msg

This is the output:




4. Extend the AArch64 code to loop from 00-30, printing each value as a 2-digit decimal number.


I used the code I used above.
To make it loop until 30, these are what to consider:
    1. max value should be 31.
    2. Both digits should be printed. Consider the msg location increments 1 for the single digit
    3. The tens' digit can be the value divided by 10.

Below is the code that meets the requirements:
.text
.globl _start

min = 0                          /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 31                         /* loop exits when the index hits this number (loop condition is i<max) */

_start:

    mov     x19, min
    mov     x20, 10  

loop:

    udiv    x21, x19, x20   
    msub    x22, x20, x21, x19

    add     x18, x21, '0'  
    adr     x17, msg+6     
    strb    w18, [x17]     

    add     x18, x22, '0'  
    adr     x17, msg+7     
    strb    w18, [x17] 

    mov     x0, 1       /* file descriptor: 1 is stdout */
    adr     x1, msg   	/* message location (memory address) */
    mov     x2, len   	/* message length (bytes) */

    mov     x8, 64     	/* write is syscall #64 */
    svc     0          	/* invoke syscall */    
add x19, x19, 1 cmp x19, max b.ne loop mov x0, 0 /* status -> 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop: 00\n" .set len , . - msg

And this is the output:



4-1. Repeat the step for x86_64.

I improved the code to make it print from 0 to 30.
Below is the entire code.

.text
.globl    _start

min = 0                         /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 31                        /* loop exits when the index hits this number (loop condition is i<max) */

_start:
    mov     $min,%r15           /* loop index */
    mov     $10, %r13           /* divisor */

loop:
    mov     $0, %rdx
    mov     %r15, %rax
    div     %r13
    mov     %rax, %r14
    add     $'0', %r14
    cmp     $'0', %r14

    movb    %r14b, msg+6
    mov     %rdx, %r14
    add     $'0', %r14
    movb    %r14b, msg+7

    movq	$len,%rdx	/* message length */
    movq 	$msg,%rsi	/* message location */
    movq	$1,%rdi		/* file descriptor stdout */
    movq	$1,%rax		/* syscall sys_write */
    syscall

    inc     %r15                /* increment index */
    cmp     $max,%r15           /* see if we're done */
    jne     loop                /* loop if we're not */

    mov     $0,%rdi             /* exit status */
    mov     $60,%rax            /* syscall sys_exit */
    syscall

.section .data

msg:	.ascii      "Loop: 00\n"
	len = . - msg

After I run the code, I got this output:



5. Change the code as needed to suppress the leading zero (printing 0-30 instead of 00-30).

I modified the code that I used above for AArch64.
Here is the entire code to print the message without the leading zero.

.text
.globl _start

min = 0                          /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 31                         /* loop exits when the index hits this number (loop condition is i<max) */

_start:

    mov     x19, min
    mov     x20, 10  

loop:

    udiv    x21, x19, x20   
    msub    x22, x20, x21, x19

    add     x18, x21, '0' 
    
    cmp     x18, '0'
    b.ne    single_digit
    mov     x18, ' '

single_digit:
    adr     x17, msg+6
    strb    w18, [x17]     
    add     x18, x22, '0'  
    adr     x17, msg+7     
    strb    w18, [x17] 

    mov     x0, 1       /* file descriptor: 1 is stdout */
    adr     x1, msg   	/* message location (memory address) */
    mov     x2, len   	/* message length (bytes) */

    mov     x8, 64     	/* write is syscall #64 */
    svc     0          	/* invoke syscall */    
add x19, x19, 1 cmp x19, max b.ne loop mov x0, 0 /* status -> 0 */ mov x8, 93 /* exit is syscall #93 */ svc 0 /* invoke syscall */ .data msg: .ascii "Loop: 00\n" .set len , . - msg

The output is:




5-1. Repeat the step for x86_64.

I modified the code to make the leading zero a single space for single digit outputs.
This is the final code:

.text
.globl    _start

min = 0                         /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 31                        /* loop exits when the index hits this number (loop condition is i<max) */

_start:
    mov     $min,%r15           /* loop index */
    mov     $10, %r13           /* divisor */

loop:
    mov     $0, %rdx
    mov     %r15, %rax
    div     %r13
    mov     %rax, %r14
    add     $'0', %r14
    cmp     $'0', %r14
    jne     single_digit
    mov     $' ', %r14

single_digit:
    movb    %r14b, msg+6
    mov     %rdx, %r14
    add     $'0', %r14
    movb    %r14b, msg+7

    movq	$len,%rdx	/* message length */
    movq 	$msg,%rsi	/* message location */
    movq	$1,%rdi		/* file descriptor stdout */
    movq	$1,%rax		/* syscall sys_write */
    syscall

    inc     %r15                /* increment index */
    cmp     $max,%r15           /* see if we're done */
    jne     loop                /* loop if we're not */

    mov     $0,%rdi             /* exit status */
    mov     $60,%rax            /* syscall sys_exit */
    syscall

.section .data

msg:	.ascii      "Loop: 00\n"
	len = . - msg

The output is:





Reflection

During this lab, I found it challenging to write and debug assembly code compared to other programming languages. While having reference code eased the process to some extent, in the final stages, I had to carefully modify the code and verify each change to identify where things went wrong.





Comments

Popular posts from this blog

Unveiling the World of Software Development Methodologies: Agile, Scrum, and DevOps Made Simple

Understanding Docker: A Comprehensive Guide

Lab1 - Code Review