There are three servers that we will use for the course. The first two, aarchie and betty (soon also jughead?), are ARM8 AArch64 systems. The third, xerxes (the Great?), is an x86_64 server. In groups, we examined the result of compiling C versions and assembly language versions of a “Hello World” on each of the two different architectures.
objdump -d hello to examine the instructions of the binary files created from the C source. Both versions of the program–the one compiled on x86_64 and the one compiled on aarch64–were fairly similar. There was a large amount of code, and the <main> sections, with which we were concerned, were about the same. The mnemonics were different, and so were some of the instructions, but the gist of was that there was a location in memory in which the message was, and then a call or branch to <printf@plt>.
For the assembler source, we compiled the files on both architectures, as before, and examined them with
objdump -d hello. Instead of a whole bunch of code, like we had with the compiled C programs, we saw only a very small amount. aarchie’s file has the format elf64-littleaarch64, while xerxes’ had elf64-x86-64. Then there was a disassembly of section .text; both files had their own instructions.
Now, having examined the structure of the assembler code, we were given the task of creating a simple loop that would output the word “Loop” and each number from 1 to 9 (i.e. “Loop: 1,” “Loop: 2,” etc.). Printing the word was simple, and printing the number was simple, but where our group ran into trouble was printing the word and the loop. We had the string in one register and the ASCII number in another, and we were trying to combine the two. Our teacher, however, told us that we could simply write the ASCII number to the location in memory where the string was stored, plus the proper offset. Once set on the right track, we finished the project without trouble.
Looping it up to 30 was also relatively simple. To suppress the tens digit for the first 10 numbers, we moved the iterator to a new register and divided the number by 10. Then we checked the tens digit, compared it to 0, and, if it was 0, jumped over the part of the code that would have printed it.
Writing in assembler is, I feel, more difficult than writing in higher-level programming languages because of the need to manipulate and keep track of the registers, and because we are dealing with specific locations in memory. Something as simple as printing a counting loop, which would be trivial in most high-level programming languages, takes a decent amount of work and requires attention.
The precision and the level of control the programmer has, however, seems like a serious advantage, and I am sure there are techniques for optimization that cannot be done in a high-level language, and that require the use of assembler code.