Reading the Source: Traps, Abstract Machine, and the Runtime Environment

C/C++

Posted by Bruce Lee on 2024-07-13

About Me

Welcome to my blog! This is where I collect my observations and notes on programming and technology. The main subjects range from implementation details to broader ideas about programming.

Main Topics

  • Engineering Projects: Exploring implementation details and how technical systems work.
  • C/C++: Notes on language features and programming techniques.
  • The Programmer’s Perspective: Ideas about developing a career and a way of thinking as a programmer.

For more, visit the categories page.

Contact

If you have questions or would like to discuss something, please get in touch through the About page.

Thank you for reading and for your support. I hope these notes help you on your own technical journey!


The ebreak Instruction in the Emulator

ebreak is a debugging instruction that transfers control to a debugger or monitor according to the execution environment. A programmer or debugger can insert it as a breakpoint. Such debugging facilities are useful in both embedded microcontrollers and larger processors.

The instruction has a fixed encoding. Although several operand fields are zero, the entire encoding is not all zero bits.

In this NEMU implementation, a matching ebreak invokes NEMUTRAP, which calls set_nemu_state.

set_nemu_state

The arguments are the new state, such as NEMU_END; the current instruction address, thispc; and an exit code. The function writes them into the state, halt_pc, and halt_ret fields of nemu_state.

Why Have Abstract Machine?

Abstract Machine provides a small runtime environment with the APIs needed by a program. A general-purpose operating system provides a much broader environment, including facilities such as filesystems, processor-resource management, and performance tools.

AM still relies on a host build environment. It does not automatically provide all the services of a general-purpose OS, such as process isolation or a complete multithreaded scheduler. The point of the abstraction is to expose the runtime facilities a program needs without requiring every platform detail to appear in the program itself.

Compiling for AM Versus GNU/Linux

An early question was: if source code eventually becomes machine instructions in object files, why not compile it normally for GNU/Linux and simply implement those instructions in NEMU?

The missing part is the surrounding runtime. Building an executable involves preprocessing, compilation, assembly, and linking. Implementing the instructions corresponding to the visible application source does not supply the library implementations, startup code, ABI conventions, or operating-system services that the executable expects.

Header inclusion exposes declarations and, in some cases, definitions. References to external functions remain as symbols until the linker connects them to their implementations. Those implementations can depend on facilities the emulator’s target environment does not provide.

We therefore use a toolchain and runtime appropriate to the target. In particular, we should not assume that the small NEMU environment already supports the execution services expected by a normal hosted program.

How Good and Bad Traps Are Reported

The tests in am-kernels contain checks of expected results. Missing instructions can prevent a test from finishing correctly; correctly implemented instructions allow the checks to pass. An incorrect implementation may pass some tests and fail others.

The important question is how a semantically wrong result is detected even when the instruction’s encoding is recognized. The test source includes trap.h, which defines the check helper. Each call supplies a Boolean condition; a failed condition causes the helper to call halt with a failure code.

What Does __attribute__((noinline)) Do?

trap.h includes am.h, klib.h, and klib-macros.h, and defines check using:

1
2
3
4
__attribute__((noinline))
void check(bool cond) {
if (!cond) halt(1);
}

GCC attributes attach implementation-specific properties to declarations. Here, noinline asks the compiler not to inline the function at its call sites, which makes following and tracing those calls easier. It does not disable every other optimization.

Attributes can appear in different positions depending on the declaration. For example: void fatal_error() __attribute__((deprecated));.

klib.h and extern "C"

klib.h declares facilities corresponding to parts of string.h, stdlib.h, and stdio.h. It uses extern "C" when included by C++ code.

C++ supports overloading and generally encodes type information into external symbol names. C and C++ therefore need an explicit linkage agreement when sharing functions. extern "C" requests C language linkage so C++ callers can link to functions implemented with that linkage. It does not make arbitrary C++ source valid C.

halt

trap.h includes am.h, where halt is declared:

1
void halt (int code) __attribute__((__noreturn__));

The __noreturn__ attribute tells the compiler that this function does not return to its caller.

The original investigation followed the platform-specific trm.c implementation and the NEMU platform’s nemu_trap definitions in abstract-machine/am/src/platform/nemu/include/nemu.h. The trap macro has architecture-specific implementations and forwards the halt code to the emulator.

Inline Assembly

For RISC-V, the essential operation places the result code in a0 and executes ebreak, using an instruction sequence such as mv a0, %0; ebreak. This is inline assembly embedded in C.

The parameter’s journey ends in the set_nemu_state handling described above.

Unsupported or Invalid Instructions

The INV macro receives s->pc and calls invalid_inst. After printing the information needed for diagnosis, that function calls set_nemu_state with NEMU_ABORT, the current PC, and -1.

This separates an invalid-instruction abort from a program that deliberately reaches the emulator’s normal trap convention.


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !