← Back to list

How Kprobe Works — Exceptions

In my previous post, I roughly introduced the way of making stub functions using the Kprobe.

CodePasta · 2025-03-28 17:39 · 0 claps · 2.2 min read
#aarch64 #exception #kprobe #linux-kernel
Open on Medium ↗
Wiki topics: 🔓 · Open Source

How Kprobe Works — Exceptions

Photo by Mads Schmidt Rasmussen on Unsplash

Photo by Mads Schmidt Rasmussen on Unsplash

In my previous post, I roughly introduced the way of making stub functions using the Kprobe.

If you read that post, you might come up with simple question, how it works? Well, it might not a big deal for someone while it is for me. Even though I am not a enthusiastic engineer, I was fascinated to the internals of Kprobe.

Exceptions

This familiar diagram depicts four exception layer of Aarch64 platform. Usually linux kernel resides on EL1, so hypervisors like kvm can take underfloor, EL2.

Kprobe uses the debug exception. There are number of specific debug exceptions that are synchronous:

  • Breakpoint Instruction exceptions
  • Breakpoint exceptions
  • Watchpoint exceptions
  • Vector Catch exceptions
  • Software Step exceptions

The breakpoint instruction exception and breakpoint exception are different. Breakpoint instruction exception is raised by brkinstruction. The breakpoint exception is raised by hardware registers, usually external debugger marks breakpoint to DBGBVR register.

Yes, the breakpoint instruction exception is software breakpoint. Take a look at below figure. It illustrates the EL0 debug scenario, but the same mechanism is applied to Kprobe.

Vector Table

Aarch64 Linux Exception Vector Table

Aarch64 Linux Exception Vector Table

On entry.S, there’s an Exception Vector Table named vectors

Each exception item is filled by macro, that calls el\el\ht()\regsize()_\label

 .macro kernel_ventry, el:req, ht:req, regsize:req, label:req
 .align 7
.Lventry_start\@:

/*.....*/

 b el\el\ht\()_\regsize\()_\label
.org .Lventry_start\@ + 128 // Did we overflow the ventry slot?
 .endm

The el\el\ht()\regsize()\label macro will be expanded like below:

 .macro entry_handler el:req, ht:req, regsize:req, label:req
SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label)
 kernel_entry \el, \regsize
 mov x0, sp
 bl el\el\ht\()_\regsize\()_\label\()_handler
 .if \el == 0
 b ret_to_user
 .else
 b ret_to_kernel
 .endif
SYM_CODE_END(el\el\ht\()_\regsize\()_\label)
 .endm

The macro block consists of:

  • kernel_entry : store current context
  • el handler() : call primary exception handler
  • ret_to_kernel (user) : restore saved context

This is very sequence that Arm’s documentations describes.

The first step, “Specific Handler for Exception Type” is decided by the exception itself by hardware. Then the software defined secondary handler will identify the exception source, using ESR_ELx, as the Arm documentation says:

For synchronous exceptions and SError interrupts, exception syndrome information (the cause of the exception) is written to ESR_ELx.

Okay, now we know the path of exception ~ handler(). Let’s get into handler first. Later we’ll visit here again to explore ret_to_kernel.


메타데이터
post_id
855ca1bcc6f5
slug
how-kprobe-works-exceptions-855ca1bcc6f5
url
https://medium.com/@taeels/how-kprobe-works-exceptions-855ca1bcc6f5
canonical_url
https://medium.com/@taeels/how-kprobe-works-exceptions-855ca1bcc6f5
author_url
https://medium.com/@taeels
status
ok
fetched_at
2026-07-08 20:12:56