Many high-level programming languages are designed with tons of features and functionalities to make the development process easier for programmers. With all the available IDE’s it’s easy to forget about (or even notice) the processes being performed behind the scenes. Sure we have these powerful processors but what makes them tick?
Up until beginning my software portability and optimization course, I’ve only ever learned and experimented with high level programming languages such as C, C++, and Java. Building off the fundamentals of each language I never put much thought into how a computer interpreted each line of code. To me the compiler was this tool that automagically combined my source code and produced a functioning program.
For a computer to execute source code, it must first be translated to assembly language. This translation is accomplished by either a compiler or an interpreter. Compilers translate the entire source code before execution. The source code is converted by a program known as an assembler. Each assembly language is specific to a particular computer architecture.
For example, if you’ve ever worked with the gcc compiler, upon successful compilation you are given an executable binary file. By default, usually named
a.out. If open this file, you’ll see a bunch of distorted characters. Binary files aren’t meant to be read by humans.
In order to dissemble a binary file, we can use the CL tool
objdump -d. When we dissemble a binary, we can examine that specific machines assembler instruction set.
Assembler language, it looks scary but….no buts. It is scary.
You don’t have the luxury of working with specialized IDE’s and code completion tools. Want a simple “Hell0 W0rld” program. Be prepared to write 50 lines of assembly.
All kidding aside, this week’s lab was designed to give us exposure to how aarch64 and x86_64 architectures are designed by creating simple applications for each system.
The objective of this lab is to implement a simple C loop written in both x86_64 and aarch64 assembler language. We were provided an empty x86_64 loop block (see below) which loops from 0 to 9, using r15 as the index (loop control) counter.
Loop: 0 Loop: 1 Loop: 2 Loop: 3 Loop: 4 Loop: 5 Loop: 6 Loop: 7 Loop: 8 Loop: 9
Empty loop block
x_86_64 & aarch64 Loops
- X86 64 Register and Instruction Quick Start
- Aarch64 Register and Instruction Quick Start
The goal was to display a simple text message for every iteration of the loop with the current index number.
To avoid scrolling back and forth between an explanation and the code, I’ve added comments in the gist to explain my solutions along with the
objdump -d results.
x86_64 9 Iteration Loop Solution
Although aarch64 assembly has a different syntax than its x86_64 counterpart they still have their similarities in logic.
aarch64 9 Iteration Loop Solution
To build on our previous 9 iteration loop we were tasked with expanding the count to 30. Sounds simple enough. Just increase the max value. Wrong.
To loop to a double-digit number, you need to convert the loop index number by dividing it by 10.
To better explain below is the definition provided by Seneca College Professor Chris Tyler:
To print the loop index value, you will need to convert from an integer to digit character. In ASCII/ISO-8859-1/Unicode UTF-8, the digit characters are in the range 48-57 (0x30-0x39). You will also need to assemble the message to be printed for each line – you can do this by writing the digit into the message buffer before outputting it to stdout, which is probably the best approach, or you can perform a sequence of writes for the three parts of the message (‘Loop: ‘, number, ‘\n’). You may want to refer to the `manpage` for
x86_64 30 Iteration Loop Solution
aarch64 30 Iteration Loop Solution
Personally, I found working with assembly language to be challenging but at the same time very rewarding. The syntax can be very confusing and tedious to work with. I enjoy learning about the full functionality of a process. Everything that happens between A->B. Gaining exposure to low-level assembly programming has given me some insight into how complex and intricate working with the hardware can be. I’ve also gained a whole new appreciation for compilers and IDE’s.