How to Read Inline Assembly - Examples

BusyBox

To get started with inline assembly, let's look at an example in the open source community - Busy Box.

Busybox is software with smaller versions of the your regular Unix tools, used to run on embedded operating systems with fewer resources (like those in phones and game consoles).

Files that contain Inline Assembly

There are only two files in this program that I've found use inline assembly code:
  1. busybox/include/libbb.h
    • Busybox main internal header file
    • Has only 1 line of assembly
    • Assembly code is used to stop the compiler from changing the order of instructions
  2. busybox/procps/powertop.c
    • A mini 'powertop' utility: Analyzes power consumption on Intel-based laptops.
    • Has 14 lines of assembly
    • Assembly code is used to retrieve the cpu id

libbb.h - A quick Look

The piece of assembly in the file is below:



This line is used in programs where changing the order of operations in the code would result in incorrect behaviour. For example, if you have a multithreaded program that reads and writes to shared variables, we do not want those operations out of order. Otherwise, we may read a value that we think has already been updated, and increment it incorrectly.

Read more here: Memory Reording Caught in the Act

powertop.c - An in-depth look

Take a look at the code below. I’ve commented out some parts irrelevant to our conversation about inline assembly, but if you would like to view the complete code you can find it here: powertop.c

The abridged code:



Where is the Inline Assembly?

The piece of assembly can be found in an #ifdef __i386__ block. This indicates that the code block should only execute if the architecture for the current computer is defined as i386.

What does the code block inside do?

It defines a function with the following signature:



This function returns nothing, but fills four pointer variables (eax, ebx, ecx, edx) with addresses that point to values containing information about the cpu. These values are used as conditions to report on your computer’s C-states in the next #ifdef.

C-states

These are states like:
  • Low power mode
  • Hibernate
  • Sleep
Here’s a list of all the different C-states: C-States

Printing the C-States

Another #ifdef __i386__ block defines a function with the following signature:



Which accepts no parameters and eventually calls the function cpuid() that we defined above and prints your computer’s supported C-States and the current BIOS C-States.

If the architecture is not i386(but is still a version of X86 architecture), the following function is defined:



Which can accept any number of any type parameters and does nothing. This is so that when the main program calls print_intel_cstates(), it can find the function - and not yield an error - yet do nothing.

Take a look at an overall view of this code. Some interesting things to note:
  • The assembly uses the following general registers: eax, ebx, ecx, edx. This confirms that this assembly is written for X86 and not ARM architectures because ARM has general register with very different names. Namely, ARM general purposes register are: R0-R12, SP, LR
  • You can see that the main program will call print_intel_cstates() and do whichever one is defined:


From looking at the code, it seems that ARM architecture is not taken into consideration. This makes sense because the powertop utility has been written specifically to work on X86 machines.

The value of this Assembly

Comparing this code to what the Linux Kernal does side by side (Find the full Linux Kernal code here Linux Kernal processor.h)

Kernal



BusyBox



Analysis

It seems that:
  1. The BusyBox assembly is a variation on the Kernal version for storing this information with some extra inputs and commands specified. Perhaps the BusyBox assembly’s inputs are coming from the Kernal Assembly’s outputs. (As you can see, their values match).
  2. The BusyBox assembly seems to be the only way to change up the way information about the cpu is retrieved. Using only c code, you would be able to use eax, ebx, ecx, edx pointers by calling:

  3. However, if the BusyBox and Kernal code indeed do different things, you would not be able to manipulate the registers in the same way with only C code.
  4. The complexity doesn’t seem to have increased too much, mainly because the authors have thoroughly commented their assembly
  5. The portability is not decreased because the assembly code is only run if an i386 architecture is detected. However, the program as a whole is not portable to ARM architecture as it uses registers specific to X86.

Analyse something yourself!

  1. Find an open source project online
  2. Clone the code. For BusyBox specific cloning: https://busybox.net/source.html
  3. Run the following command to find some places where inline assembly is used.

Note: an assembly block can be written in the syntax of __asm__ () or simply asm (), however, looking for asm will list files with #include statements referencing the word asm and it will be difficult to look through and find actually assembly code blocks.

Comments

Popular Posts