Saturday, March 1, 2025

Seventh Posting, Lab 5

 First  we are going to take a look in x86_001 and do the experiments on assembler.

We see that all the necessary files are extracted,
We also see that a lot of "hello.c" related files for us to tes them out so let go ahead and run them and see its in action.

Tried running without make command to build and saw the result as well as make ran each file.

When excuted second time we see that make doing nothing, just like wiki explanation on make.
if we take a look at those files we see that its a bit different structure, I believe that the first one is our good ol, standard library. with <stdio.h>
and of course, stdout will help the printf to flush the buffer.

the second hello2.c has the direct system call wrapper write(), this is not standdard library. it sends data directly to the file descriptor.

the last hello3.c syscall() is used for interfere the Linux's kernel's rawsystem call interface.

pretty cool stuffs to see there are other ways to make text outputs without standard library!

Let me try to disassemble this and see whats going on with using objdump 

We can see that a lot of different parts here like 
_init
_start
main
_fini

in here we are going too take a look at the main section

If we simply explain in main we can see steps of how the strings are excuted, stored, and how its cleaning up after that as well.
    1. Stack setup ( push%rbp, move %rsp, %rbp).
    2. Loads "Hello World!\n" address into rdi (mov $0x402010, %edi)
    3. Sets eax =0 to follow calling conventions.
    4. Calls printf(), which prints the string.
    5. Returns 0 to indicate success
    6. Restores stack and exits.

By this we can know that the main function sets up arguments for printf() and calls it via plt. As well as not only  setting up argument we see that there is a stack frame management. And the program follows specific order and structured call, return sequences with _start setting things up and main excueting.

Now lets do the same operation in Aarch64 and see whats difference. Just as same as above steps and with objdump -d hello command we can see that there differences on Aarch-64.



Checking the code contents, we can see that there is no difference in it.
in above you can see Aarch-64 output of the objdump -d hello, main section.

Both are following similar structure but we can see some convention differences as well as stack frame management differences. Looks like Aarch 64 avoids direct memory accesses using adrp and add for efficient address calculations when 86 was using direct mov instruction.
Function arguments and return values are sotred in different registers ( x0 in Aarch64 and rdi;eax in 86)
As well as bl branches and link, stores return address in x30 and 86's call pushes return address onto stack.


And now lets see how gcc -S  result is any different than above ones that are done with make command and objdump. gcc -S generates assembly code and just like what happened in objdump we see that each environment show different behaviour.


Just like what we have found in objdump we can see the details inside, and i believe it has more readability than objdump.

gcc -S compiles the C source code into assembly but does not assemble or link them. and gives human readable assembly file that represents the C code before any optimization or linking.

objdump disassembles a compiled binary into assembly, shows the actual instructions in excutable, including memory address and real function calls.

seems like gcc -S gives more reduced amount of code since it does not touch the memory address yet.


Now lets go and make some new things, we are now aiming to make some text outputs when we run.
 .text
 .globl _start
 min = 0                          /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 6                         /* 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     /* 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 */
As the lab say lets try this one first, we are looking for it to have output text "Loop" 6 times.
Lets combine with hello.s code and see if we can achieve the goal.

Modified code to output "Loop" 6 times with new line.





implementing main then start might cause issue but i was keeping getting issue with having duplicate _start declarations whever i followed the orginal code's direction. Maybe i do need to investigate mroe about this. but in here, compare to the original code we have implemented few things that original codes do not have.
    -syscall 64 to write "Loop\n"
    -stored msg string in to .data section

Now lets jump into next section and do something more advanced and give index numbers after the strings

For an example our output can be

Loop: 0

Loop: 1

Loop: 2

Loop: 3

Loop: 4

Loop: 5

Lets modify our code as below




Seems like we made it! now we can see the index nums as well as moving on to new line. In here we have added few more stuffs than the previous code that only prints "Loop\n"
We dynamically modifies idx so that it replaces "0\n" with the current loop number.
It also uses strb w20, [x21] to store single byte of the converted number.

now lets try to achieve more greatness by making it more advanced, with two decimals and printing maybe till 32 as the lab instruction says.

So now we modified the code as below.



Now we got them looping until 32 with two decimal digits.

in here we have loop counter that is going till 33 (0-32)
for number conversions we use udiv and msub to extract tens and ones digits seperately And writes two digits instead of one digit.

Now another step forward to make the single digit values prints out as one decimal and double digits prints out as two decimals



as the lab instruction says we should be using conditions here so we implemented cbz (conditional branch) to check if first digit is 0 and writes either one or two ASCII digits dynamically.

As final challenge lets change those numbers as hexadecimals 0-20.



In here we have used 16 for 10th digit, and we have base output with 0x and we handled two different ASCII ranges. and used cmp to do conditionally convert.

After careful investigation on codes i was able to achieve the same on 86_64 as well.
But there are differences though, in x86_64  we have used different registers like rax and rdi.

Seems like Aarch 64 has simpler streamlined than x86_64. and through this we have learned looping, memory management and conditional excution in both Aarch64 and x86_64.





No comments:

Post a Comment

10th Posting - Project: Stage 3

 Hello All! now we are on the final stage of the project which is project 3. If you remember correctly from my Stage 2 posting, i was able t...