
About Quarkslab
Quarkslab builds cutting-edge cybersecurity solutions used by security-driven companies and institutions around the world. Our QShield product suite focuses on software protection and reverse engineering resistance across desktop, mobile, and embedded platforms.
We’re not in the cloud — we build real software, tested on real systems. If you enjoy diving deep into complex technical environments, automating smart test coverage, and owning quality end-to-end, read on.
Location : Paris or Rennes
Description
Go (Golang) powers the modern cloud. From Kubernetes and Docker to Containerd and gVisor, most of today’s cloud-native technologies are built in Go.
Yet, despite being open-source, Go programs are notoriously hard to analyze or extend dynamically. You usually have to recompile them to modify behavior or collect runtime insights.
But what if you could observe and extend Go binaries while they're running, without touching their source code?
This internship focuses on gTrace, an internal research project that brings dynamic binary instrumentation to Go. Think of it as a way to inject observability, debugging, and new behaviors into Go programs at runtime, similar to what eBPF does for the kernel, and Frida at the user-space level.
Currently, gTrace exists as a working prototype developed over several years of internal R&D. Your mission is to help transform it into a robust, open-source-ready tool for the wider community.
You'll contribute to a project at the intersection of systems programming, Go runtime internals, and binary instrumentation.
What you will do
During this 6-month internship, you will:
Work directly on gTrace, an internal dynamic instrumentation framework for Go.
Design and implement key missing features to bring it to production quality.
Improve runtime stability, API usability, and integration with modern Go versions.
Collaborate with experienced engineers and researchers on systems-level tooling.
By the end of the internship, you'll have deep hands-on experience with Go internals, dynamic analysis techniques, and open-source tooling. You'll also have the opportunity to write a blog post (or why not series of blog posts?) which will be published on our blog and shared with our peers. Upon successful completion, you'll have the chance to present the tool at a conference.
Objective
The goal of this task is to develop a Go program that identifies and retrieves all asynchronous preemption safe points for all user-defined ("userland") functions from a compiled but not stripped Go binary.
The Go compiler embeds detailed runtime metadata in every compiled binary, including:
Function metadata (names, entry points, stack sizes, argument and return value counts).
Line tables (mapping program counters to source file lines).
Stack maps and garbage collection data for function arguments and local variables.
PCDATA and FUNCDATA tables that describe, among other things, safe points for garbage collection and asynchronous preemption
Task Description
1. Locate and parse the pcln table
Extract the pclntab structure from the binary and identify relevant function metadata:
_func entries (per-function metadata)
pclntab headers and offsets
Per-PC (program counter) data streams
Reference Material
2. Extract asynchronous preemption safe points
Use PCDATA channel information to determine preemption-safe locations within each function:
The PCDATA table is multi-channel; channel 0 encodes preemption safety status per program counter.
A value of $-1 (_PCDATA_UnsafePointSafe) indicates a safe preemption point.
A value of $-2 (_PCDATA_UnsafePointUnsafe) indicates an unsafe region.
Reference Material
3. Filter out non-application functions
Exclude Go runtime and standard library symbols to focus only on user-defined functions. This filtering can be performed before or after parsing safe points.
4. Generate structured output
Produce a well-defined output format (e.g., JSON) that includes:
Function name
Code address ranges
Corresponding source file and line number (if available)
Program counters of safe preemption points
Required Skills
Strong knowledge of Go (Golang).
Experience using a debugger (e.g. gdb, delve).
Basic understanding of compilers, linkers, and build systems.
Basic understanding of x86 assembly and Linux.
Assignment
Solve this challenge: Extracting Asynchronous Preemption Safe Points from Compiled Go Binaries
Overview
Asynchronous preemption, introduced in Go 1.14, enhances the responsiveness and fairness of Go's scheduler. Prior to this feature, the Go runtime could only preempt (interrupt) a running goroutine at a limited set of safe points, typically during function calls, channel operations, or blocking system calls. As a result, CPU-bound or long-running loops without such calls could monopolize a thread, delaying garbage collection and starving other goroutines.
With asynchronous preemption, the Go runtime can now interrupt goroutines at more places during their execution, not just at function call boundaries. This mechanism relies on close cooperation between the compiler and the runtime:
The compiler emits metadata marking which instruction regions are safe or unsafe for preemption.
The runtime emits signals to running threads, inspects the generated by the compiler metadata associated to the PC (Program Counter), and, if the PC's address value corresponds to a safe preemption point, the runtime suspends the goroutine safely.
Further Reading
Expected Deliverables
A Go-based tool that:
Parses a compiled Go binary.
Extracts and reports asynchronous preemption safe points for each userland function.
Documentation describing:
The process of parsing the pclntab structure.
The logic used to interpret PCDATA entries and identify safe points.
Known limitations and suggestions for future extensions (e.g., cross-platform parsing or visualization).