Skip to main content

Chapter 2 Machine Code

We start with a very simple imperative programming language, the machine code for the MIPS instruction set architecture. An instruction set architecture is the definition of a machine code language. There may be various different implementations (i.e. different processors) that all implement the same ISA. For example, all modern ARM processors from different vendors implement the AArch64 ISA.

Machine code is very primitive, does not contain a lot of concepts, and is very close to the machine. By learning machine code, we get an understanding of the lowest abstraction in programming, the so-called hardware-software interface, which is essentially how software “talks” to hardware. Understanding what happens at the machine code level is relevant in many situations: For example, many software security problems exploit the fact that some programming languages cannot really abstract the machine code level entirely which can be exploited to “make the program behave” in an unintended way. Another example is performance optimization: If code needs to run as fast as possible, engineers often look at the machine code generated from a compiler to understand possible performance bottlenecks. Sometimes performance-critical code is even written in machine code.

The abstractions that higher programming languages (such as C or Java) provide are of course helpful because they save us work. They relieve the programmer from having to think about low-level details and enable portable code. Portable means that a program can be run on different machines that have possibly different instruction set architectures. One way of enabling portability is using a compiler. A compiler is a program that translates a program written in one language (e.g. C) to a program in another language (such as machine code).