<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Tramasys - linux</title>
    <link rel="self" type="application/atom+xml" href="https://tramasys.mov/tags/linux/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://tramasys.mov"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-11T00:00:00+00:00</updated>
    <id>https://tramasys.mov/tags/linux/atom.xml</id>
    <entry xml:lang="en">
        <title>mirror, mirror on the wall, which cpu am I running on?</title>
        <published>2023-10-29T00:00:00+00:00</published>
        <updated>2026-08-11T00:00:00+00:00</updated>
        
        <author>
          <name>tramasys</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://tramasys.mov/posts/getcpu/"/>
        <id>https://tramasys.mov/posts/getcpu/</id>
        
        <content type="html" xml:base="https://tramasys.mov/posts/getcpu/">&lt;span id=&quot;continue-reading&quot;&gt;&lt;/span&gt;&lt;h2 id=&quot;experimentation-in-python&quot;&gt;experimentation in python&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#experimentation-in-python&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Starting with the obvious version:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;#!/usr/bin/python3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;/span&gt;&lt;span&gt; psutil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nr&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;/span&gt;&lt;span&gt; psutil&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;Process&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;cpu_num&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;nr&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How does &lt;code&gt;psutil&lt;/code&gt; find out which CPU we&#39;re running on?&lt;/p&gt;
&lt;p&gt;I first stepped through it with &lt;code&gt;pdb&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;python3 -m pdb ./allocs/getcpu.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;n&lt;/code&gt; goes to the next line, &lt;code&gt;s&lt;/code&gt; steps into a call, and &lt;code&gt;ll&lt;/code&gt; lists the current
function. With psutil 7.2.2 the relevant path is:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;psutil.Process.__init__&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; psutil.Process.cpu_num&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; self._proc.cpu_num()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; psutil._pslinux.Process.cpu_num&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; self._parse_stat_file()[&amp;#39;cpu_num&amp;#39;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The public &lt;a rel=&quot;external&quot; href=&quot;https://github.com/giampaolo/psutil/blob/release-7.2.2/psutil/__init__.py#L903-L912&quot;&gt;&lt;code&gt;Process.cpu_num&lt;/code&gt;&lt;/a&gt; method is only the
platform-independent wrapper. On Linux it lands in
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/giampaolo/psutil/blob/release-7.2.2/psutil/_pslinux.py#L1839-L1842&quot;&gt;&lt;code&gt;psutil/_pslinux.py&lt;/code&gt;&lt;/a&gt;, where &lt;code&gt;cpu_num&lt;/code&gt; still calls
&lt;code&gt;_parse_stat_file&lt;/code&gt; and converts the result to an integer.&lt;/p&gt;
&lt;p&gt;The interesting part of &lt;a rel=&quot;external&quot; href=&quot;https://github.com/giampaolo/psutil/blob/release-7.2.2/psutil/_pslinux.py#L1672-L1708&quot;&gt;&lt;code&gt;_parse_stat_file&lt;/code&gt;&lt;/a&gt; is small:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;data&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; bcat&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_procfs_path&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span&gt;pid&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;/stat&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rpar&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;/span&gt;&lt;span&gt; data&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;rfind&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fields&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;/span&gt;&lt;span&gt; data&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;/span&gt;&lt;span&gt;rpar&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:].&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ret&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;cpu_num&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;]&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;/span&gt;&lt;span&gt; fields&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;36&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file is &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/stat&lt;/code&gt;. The process name inside it is surrounded by
parentheses and may itself contain spaces or parentheses, so splitting the
whole line on spaces would be wrong. psutil finds the last &lt;code&gt;)&lt;/code&gt;, removes the
&lt;code&gt;pid&lt;/code&gt; and &lt;code&gt;(comm)&lt;/code&gt; prefix, and only then splits the remaining fields.&lt;/p&gt;
&lt;p&gt;The procfs interface calls this value field 39, &lt;code&gt;processor&lt;/code&gt;: the CPU number
the task last executed on. After removing the first two fields, procfs field
39 becomes Python index 36. So that slightly magic-looking number is at least
accounted for.&lt;/p&gt;
&lt;p&gt;Tracing the script makes the route visible:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; strace -e openat,read,getcpu,rseq python3 getcpu.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rseq(..., 33, 0, 0x53053053) = 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;openat(AT_FDCWD, &amp;quot;/proc/208862/stat&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;openat(AT_FDCWD, &amp;quot;/proc/208862/stat&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two reads because constructing &lt;code&gt;Process()&lt;/code&gt; also records the process
start time to protect against PID reuse. &lt;code&gt;cpu_num()&lt;/code&gt; then reads the same stat
file for the CPU field. The &lt;code&gt;rseq&lt;/code&gt; call belongs to glibc/Python startup.
psutil does not use it for this answer.&lt;/p&gt;
&lt;p&gt;There is a semantic wrinkle here. &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/stat&lt;/code&gt; describes the task with
that PID. For a multithreaded process, &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/stat&lt;/code&gt; is the thread-group
leader&#39;s stat file. It does not tell us which CPU every thread in the process
is using. Individual threads have their own
&lt;code&gt;/proc/&amp;lt;pid&amp;gt;/task/&amp;lt;tid&amp;gt;/stat&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;It also says &quot;last executed on&quot;, which is useful when inspecting another
process or a sleeping task, but is not quite the same question as &quot;which CPU
is this calling thread executing on at this instruction?&quot;&lt;/p&gt;
&lt;h2 id=&quot;experimentation-in-rust&quot;&gt;experimentation in rust&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#experimentation-in-rust&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;For the calling thread, Linux exposes &lt;code&gt;sched_getcpu()&lt;/code&gt;. The Rust &lt;code&gt;libc&lt;/code&gt; crate
contains the FFI binding:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    let&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = unsafe&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;/span&gt;&lt;span&gt; libc&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;sched_getcpu&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;        panic!&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;sched_getcpu failed: &lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{}&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;/span&gt;&lt;span&gt;io&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;last_os_error&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;    println!&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;cpu&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a GNU/Linux target this is a call to glibc, not an implementation inside
the Rust crate. The resulting binary has a call through the dynamic symbol:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;call&lt;/span&gt;&lt;span&gt; sched_getcpu@PLT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting a breakpoint at &lt;code&gt;sched_getcpu&lt;/code&gt; with GDB lands in
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/04e750e75b73957cf1c791535a3f4319534a52fc/sysdeps/unix/sysv/linux/sched_getcpu.c#L21-L43&quot;&gt;&lt;code&gt;sysdeps/unix/sysv/linux/sched_getcpu.c&lt;/code&gt;&lt;/a&gt;. My old notes
had this as &lt;code&gt;THREAD_GETMEM_VOLATILE(THREAD_SELF, rseq_area.cpu_id)&lt;/code&gt;. Current
glibc spells the same operation with &lt;code&gt;RSEQ_GETMEM_ONCE&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;sched_getcpu&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  int&lt;/span&gt;&lt;span&gt; cpu_id &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; RSEQ_GETMEM_ONCE&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span&gt;cpu_id&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; __glibc_likely&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span&gt;cpu_id &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;/span&gt;&lt;span&gt; cpu_id &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; vsyscall_sched_getcpu&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the normal path is now almost disappointingly short: read one integer from
the current thread&#39;s rseq area and return it.&lt;/p&gt;
&lt;h3 id=&quot;new-world-rseq&quot;&gt;new world: rseq&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#new-world-rseq&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rseq&lt;/code&gt; means restartable sequences. Its main purpose is to let userspace do
small per-CPU operations that the kernel can abort and restart if a thread is
preempted, migrated, or interrupted by a signal. A useful side effect is that
the registered userspace area contains &lt;code&gt;cpu_id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;glibc allocates that area in thread-local storage. On x86-64,
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/04e750e75b73957cf1c791535a3f4319534a52fc/sysdeps/x86_64/64/nptl/rseq-access.h#L20-L40&quot;&gt;&lt;code&gt;RSEQ_GETMEM_ONCE&lt;/code&gt;&lt;/a&gt; is a volatile single-copy read relative
to the &lt;code&gt;fs&lt;/code&gt; thread pointer and the exported &lt;code&gt;__rseq_offset&lt;/code&gt;. In the installed
glibc 2.44 binary the important part looks like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;mov  rax&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;QWORD&lt;/span&gt;&lt;span&gt; PTR [&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rip&lt;/span&gt;&lt;span&gt; + __rseq_offset]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;mov  rax&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;QWORD&lt;/span&gt;&lt;span&gt; PTR [&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rax&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;mov  eax&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;DWORD&lt;/span&gt;&lt;span&gt; PTR &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fs&lt;/span&gt;&lt;span&gt;:[&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rax&lt;/span&gt;&lt;span&gt; + &lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0x4&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;test eax&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;eax&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;js&lt;/span&gt;&lt;span&gt;   fallback&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nonnegative values are CPU IDs. A negative value means rseq registration was
disabled, failed, or has not completed, so glibc uses the fallback.&lt;/p&gt;
&lt;p&gt;Who registered this memory with the kernel? glibc did, before &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the initial thread of a statically linked program the current chain is:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_start&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __libc_start_main_impl&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; ARCH_SETUP_TLS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __libc_setup_tls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; call_tls_init_tp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __tls_init_tp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; rseq_register_current_thread&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; rseq syscall&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a dynamically linked program, &lt;code&gt;ld-linux&lt;/code&gt; performs the corresponding TLS
setup and calls &lt;code&gt;__tls_init_tp&lt;/code&gt; before transferring control to the program.
New pthreads register their own area from &lt;code&gt;start_thread&lt;/code&gt; in
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/04e750e75b73957cf1c791535a3f4319534a52fc/nptl/pthread_create.c#L376-L389&quot;&gt;&lt;code&gt;nptl/pthread_create.c&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The allocation is not something to hard-code. The kernel advertises the
feature size and alignment through the ELF auxiliary vector. On this system:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; LD_SHOW_AUXV&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; /bin/true&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; RSEQ&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;AT_RSEQ_FEATURE_SIZE: 33&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;AT_RSEQ_ALIGN:        64&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That explains the &lt;code&gt;rseq(..., 33, ...)&lt;/code&gt; seen by &lt;code&gt;strace&lt;/code&gt;. glibc obtains these
values, allocates suitably aligned extra TLS, initializes &lt;code&gt;cpu_id&lt;/code&gt;, and calls
&lt;code&gt;rseq_register_current_thread&lt;/code&gt; with the kernel&#39;s &lt;code&gt;rseq&lt;/code&gt; syscall.&lt;/p&gt;
&lt;p&gt;On the kernel side, migration marks the registered IDs as changed. In the
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/f5bbbfec59b4e2fb7520a91de3df8a6174325d6a/include/linux/rseq_entry.h#L479-L542&quot;&gt;current source&lt;/a&gt; this runs through
&lt;code&gt;rseq_sched_set_ids_changed&lt;/code&gt; and &lt;code&gt;rseq_sched_switch_event&lt;/code&gt;. Before returning
to userspace,
&lt;code&gt;rseq_set_ids_get_csaddr&lt;/code&gt; writes the new &lt;code&gt;cpu_id_start&lt;/code&gt;, &lt;code&gt;cpu_id&lt;/code&gt;, NUMA node,
and memory-map concurrency ID into the registered area. This is slightly more
precise than saying &quot;the scheduler writes the field during migration&quot;: the
update is arranged so it is correct when the task resumes in userspace.&lt;/p&gt;
&lt;p&gt;The field is extremely cheap for glibc to read because the kernel already did
the work during scheduling. No syscall is required for each &lt;code&gt;sched_getcpu()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is possible to turn the glibc registration off:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; strace -e rseq,getcpu ./getcpu&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rseq(...) = 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;18&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; GLIBC_TUNABLES&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;glibc.pthread.&lt;/span&gt;&lt;span&gt;rseq&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    strace -e rseq,getcpu ./getcpu&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the second run neither syscall appears. &lt;code&gt;rseq&lt;/code&gt; is disabled, yet there is
still no &lt;code&gt;getcpu&lt;/code&gt; syscall. That drops us into the next layer: the vDSO.&lt;/p&gt;
&lt;h3 id=&quot;fallback-one-the-vdso&quot;&gt;fallback one: the vDSO&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#fallback-one-the-vdso&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vsyscall_sched_getcpu&lt;/code&gt; is still the current glibc helper name, although this
is the vDSO path rather than the old fixed-address vsyscall page. It uses
&lt;code&gt;INLINE_VSYSCALL(getcpu, ...)&lt;/code&gt;. During startup,
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/04e750e75b73957cf1c791535a3f4319534a52fc/sysdeps/unix/sysv/linux/dl-vdso-setup.h#L25-L43&quot;&gt;&lt;code&gt;setup_vdso_pointers&lt;/code&gt;&lt;/a&gt; looks up &lt;code&gt;__vdso_getcpu&lt;/code&gt; in the ELF
image supplied by the kernel and stores its address in glibc&#39;s read-only
loader state.&lt;/p&gt;
&lt;p&gt;The vDSO is kernel code mapped into every process, but it executes in user
mode. That is why &lt;code&gt;strace&lt;/code&gt; sees no &lt;code&gt;getcpu&lt;/code&gt; call above.&lt;/p&gt;
&lt;p&gt;Hard-coding a mapping such as &lt;code&gt;0x00007ffff7fc6000&lt;/code&gt; works once and then ASLR
moves it. A less fragile GDB route is:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) set disassembly-flavor intel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) info auxv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) info proc mappings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) dump binary memory /tmp/vdso.bin START END&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;AT_SYSINFO_EHDR&lt;/code&gt; in &lt;code&gt;info auxv&lt;/code&gt; is the vDSO ELF header. &lt;code&gt;readelf -Ws&lt;/code&gt; and
&lt;code&gt;objdump -d -M intel&lt;/code&gt; can then find &lt;code&gt;__vdso_getcpu&lt;/code&gt; in the dumped image.&lt;/p&gt;
&lt;p&gt;The current x86 &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/entry/vdso/common/vgetcpu.c&quot;&gt;&lt;code&gt;__vdso_getcpu&lt;/code&gt;&lt;/a&gt; calls
&lt;code&gt;vdso_read_cpunode&lt;/code&gt;. That helper has two implementations packed into one
instruction-alternative site:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;baseline:  LSL with the __CPUNODE_SEG selector&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;patched:   RDPID when X86_FEATURE_RDPID is present&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The baseline starts by loading selector &lt;code&gt;0x7b&lt;/code&gt;. &lt;code&gt;LSL&lt;/code&gt; returns the limit from a
per-CPU GDT descriptor whose bits contain the CPU and NUMA node. Linux uses the
low 12 bits for the CPU and the bits above them for the node.&lt;/p&gt;
&lt;p&gt;On the machine I am checking now, CPUID leaf 7, subleaf 0, ECX bit 22 is set,
and &lt;code&gt;/proc/cpuinfo&lt;/code&gt; contains &lt;code&gt;rdpid&lt;/code&gt;. The kernel therefore patched the vDSO to
the newer instruction. With rseq disabled, the live mapping disassembles as:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;mov   eax&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0x7b&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdpid rax&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;test  rdi&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;je&lt;/span&gt;&lt;span&gt;    no_cpu_pointer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;mov   edx&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;eax&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;and   edx&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0xfff&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;mov   DWORD&lt;/span&gt;&lt;span&gt; PTR [&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdi&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;edx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The apparently unused &lt;code&gt;mov eax, 0x7b&lt;/code&gt; remains because the alternatives
mechanism patches the &lt;code&gt;LSL&lt;/code&gt; instruction in place. On hardware without
&lt;code&gt;RDPID&lt;/code&gt;, that selector is consumed by &lt;code&gt;LSL&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RDTSCP&lt;/code&gt; can also expose &lt;code&gt;IA32_TSC_AUX&lt;/code&gt; in &lt;code&gt;ecx&lt;/code&gt;, and older notes often list
it as another assembly-level way to obtain per-CPU data. It also reads the
timestamp and has stronger ordering properties than needed here. Current x86
vDSO source specifically chooses between &lt;code&gt;RDPID&lt;/code&gt; and &lt;code&gt;LSL&lt;/code&gt;. It does not use
&lt;code&gt;RDTSCP&lt;/code&gt; for this function.&lt;/p&gt;
&lt;h3 id=&quot;fallback-two-the-actual-syscall&quot;&gt;fallback two: the actual syscall&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#fallback-two-the-actual-syscall&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If the rseq value is negative and the vDSO symbol is unavailable, or the vDSO
returns &lt;code&gt;ENOSYS&lt;/code&gt;, glibc finally executes the real &lt;code&gt;getcpu&lt;/code&gt; syscall. On x86-64
its number is 309, or &lt;code&gt;0x135&lt;/code&gt;, which is visible in the last branch of the
glibc disassembly.&lt;/p&gt;
&lt;p&gt;The kernel implementation is almost the answer in plain C:
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/kernel/sys.c&quot;&gt;&lt;code&gt;SYSCALL_DEFINE3(getcpu)&lt;/code&gt;&lt;/a&gt; takes
&lt;code&gt;raw_smp_processor_id()&lt;/code&gt;, optionally converts it to a NUMA node, and copies
the values to userspace.&lt;/p&gt;
&lt;p&gt;So the Rust path is:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;libc::sched_getcpu()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; glibc sched_getcpu()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; rseq cpu_id TLS read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __vdso_getcpu() if rseq is unavailable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; getcpu syscall if the vDSO is unavailable&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the Python path was separate all along:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;psutil.Process().cpu_num()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; /proc/&amp;lt;pid&amp;gt;/stat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; field 39: processor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;in-the-wild-mongodb-and-tcmalloc&quot;&gt;in the wild: MongoDB and TCMalloc&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#in-the-wild-mongodb-and-tcmalloc&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A place where the answer matters on a very hot path is Google&#39;s TCMalloc.
MongoDB 8.0 and newer ship a version with per-CPU caches, so an ordinary small
allocation may end up taking an object from a cache belonging to the current
logical CPU. That avoids putting every allocating thread through the same
lock.&lt;/p&gt;
&lt;p&gt;At upstream revision &lt;code&gt;622a1d1&lt;/code&gt;, the most direct helper in
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/tcmalloc/blob/622a1d1ed3c76fc298e49e3045b66486cffa056d/tcmalloc/internal/percpu.h#L93-L245&quot;&gt;&lt;code&gt;percpu.h&lt;/code&gt;&lt;/a&gt; is almost identical to the glibc fast path:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;inline int&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; GetRealCpuUnsafe&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; return&lt;/span&gt;&lt;span&gt; __rseq_abi&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span&gt;cpu_id&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;__rseq_abi&lt;/code&gt; is thread-local storage registered with the kernel. The
nonnegative values are real CPU IDs. &lt;code&gt;-1&lt;/code&gt; means uninitialized and &lt;code&gt;-2&lt;/code&gt; means
unsupported. &lt;code&gt;GetRealCpu()&lt;/code&gt; first tries this load and falls back to
&lt;code&gt;sched_getcpu()&lt;/code&gt; if no rseq value is available. TCMalloc uses that general
helper in places such as its &lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/tcmalloc/blob/622a1d1ed3c76fc298e49e3045b66486cffa056d/tcmalloc/deallocation_profiler.cc#L482-L514&quot;&gt;deallocation profiler&lt;/a&gt;, where
it records the CPU, L3 cache, and NUMA node associated with a sample.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Unsafe&lt;/code&gt; suffix is the important part. The load itself is fine, but its
answer can become stale as soon as the thread migrates. This would not be
enough for the allocator fast path:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cpu = current_cpu()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;object = slabs[cpu].pop()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A migration between those two lines could make the thread modify the old
CPU&#39;s cache while running on the new CPU. TCMalloc therefore does more than
ask for a number. &lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/tcmalloc/blob/622a1d1ed3c76fc298e49e3045b66486cffa056d/tcmalloc/internal/percpu.cc#L71-L90&quot;&gt;&lt;code&gt;InitThreadPerCpu&lt;/code&gt;&lt;/a&gt; registers its own
TLS area with the raw &lt;code&gt;rseq&lt;/code&gt; syscall, and the x86-64 push and pop paths run as
actual &lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/tcmalloc/blob/622a1d1ed3c76fc298e49e3045b66486cffa056d/tcmalloc/internal/percpu_tcmalloc.h#L630-L930&quot;&gt;restartable sequences&lt;/a&gt;. The last store updates
the slab header and commits the operation. If the kernel preempts or migrates
the thread before that store, execution goes through the abort trampoline and
starts the operation again.&lt;/p&gt;
&lt;p&gt;The current implementation also avoids calculating &lt;code&gt;slabs[cpu]&lt;/code&gt; on every
allocation. It caches the address of the current CPU&#39;s slab in the
thread-local &lt;code&gt;tcmalloc_slabs&lt;/code&gt;. The layout is slightly evil in a nice way:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;TLS address:  __rseq_abi - 4       __rseq_abi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              +--------------------+--------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8-byte load:  | pointer bits 0..31 | pointer bits 32..63|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              +--------------------+--------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rseq field:                        | cpu_id_start       |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two TLS objects overlap by four bytes on little-endian systems. Bit 63
marks the cached pointer as valid. When the scheduler moves the thread, the
kernel writes the new, small CPU number into &lt;code&gt;cpu_id_start&lt;/code&gt;, overwriting that
part of the pointer and clearing the validity bit as a side effect.&lt;/p&gt;
&lt;p&gt;The x86 fast path can therefore load the cached slab pointer, test and clear
bit 63 with &lt;code&gt;BTR&lt;/code&gt;, and continue if the bit was set. If it was clear, it takes
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/tcmalloc/blob/622a1d1ed3c76fc298e49e3045b66486cffa056d/tcmalloc/internal/percpu_tcmalloc.h#L1171-L1225&quot;&gt;&lt;code&gt;CacheCpuSlabSlow&lt;/code&gt;&lt;/a&gt;, reads the current rseq CPU ID,
computes the new slab address, and caches it again with another small rseq
operation. A scheduler-maintained CPU field has become an automatic cache
invalidation mechanism.&lt;/p&gt;
&lt;p&gt;There is one current integration wrinkle. TCMalloc registers its own rseq
area, while modern glibc registers a different one before &lt;code&gt;main&lt;/code&gt;, and Linux
only permits one registration per thread. MongoDB&#39;s
&lt;a rel=&quot;external&quot; href=&quot;https://www.mongodb.com/docs/manual/administration/tcmalloc-performance/&quot;&gt;deployment documentation&lt;/a&gt; consequently tells operators to
start &lt;code&gt;mongod&lt;/code&gt; with &lt;code&gt;GLIBC_TUNABLES=glibc.pthread.rseq=0&lt;/code&gt;. That leaves the rseq
slot free for TCMalloc. Otherwise the allocator falls back to its legacy
per-thread caches.&lt;/p&gt;
&lt;p&gt;So in this real path, finding the CPU is not an informational query. It is
part of deciding which memory may be touched, and rseq supplies the missing
guarantee that a plain CPU-number snapshot cannot provide.&lt;/p&gt;
&lt;h2 id=&quot;what-does-currently-mean&quot;&gt;what does &quot;currently&quot; mean?&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#what-does-currently-mean&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;None of these interfaces freezes the scheduler. A thread may migrate after
the CPU number is observed and before the caller uses it. Even the rseq load
can be followed by preemption before &lt;code&gt;sched_getcpu()&lt;/code&gt; returns. The API only
gives a snapshot.&lt;/p&gt;
&lt;p&gt;If the CPU must remain stable across some work, reading its number is not
enough. Pinning the thread to one logical CPU with &lt;code&gt;sched_setaffinity&lt;/code&gt; makes
that property explicit. TCMalloc&#39;s push and pop paths show the other option:
an rseq critical section lets a short per-CPU operation detect migration and
restart instead of trying to prevent it.&lt;/p&gt;
&lt;p&gt;For observing another task, psutil&#39;s procfs answer is convenient. For cheaply
asking about the calling thread, &lt;code&gt;sched_getcpu()&lt;/code&gt; is the direct interface.&lt;/p&gt;
&lt;h2 id=&quot;source-trail&quot;&gt;source trail&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#source-trail&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/giampaolo/psutil/blob/release-7.2.2/psutil/__init__.py#L903-L912&quot;&gt;psutil 7.2.2: &lt;code&gt;Process.cpu_num&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/giampaolo/psutil/blob/release-7.2.2/psutil/_pslinux.py#L1672-L1708&quot;&gt;psutil 7.2.2: Linux &lt;code&gt;_parse_stat_file&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html&quot;&gt;Linux &lt;code&gt;proc_pid_stat(5)&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/04e750e75b73957cf1c791535a3f4319534a52fc/sysdeps/unix/sysv/linux/sched_getcpu.c#L21-L43&quot;&gt;glibc &lt;code&gt;sched_getcpu.c&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://sourceware.org/glibc/manual/latest/html_node/Restartable-Sequences.html&quot;&gt;glibc restartable-sequences manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/include/uapi/linux/rseq.h#L94-L145&quot;&gt;Linux rseq userspace ABI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/f5bbbfec59b4e2fb7520a91de3df8a6174325d6a/include/linux/rseq_entry.h#L479-L542&quot;&gt;Linux rseq scheduler/update path&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/include/asm/segment.h#L232-L267&quot;&gt;Linux x86 &lt;code&gt;vdso_read_cpunode&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/tcmalloc/blob/622a1d1ed3c76fc298e49e3045b66486cffa056d/tcmalloc/internal/percpu.h#L93-L245&quot;&gt;TCMalloc&#39;s per-CPU helpers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/tcmalloc/blob/622a1d1ed3c76fc298e49e3045b66486cffa056d/docs/rseq.md&quot;&gt;TCMalloc&#39;s rseq design notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://www.mongodb.com/docs/manual/administration/tcmalloc-performance/&quot;&gt;MongoDB&#39;s TCMalloc deployment notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://martin.uy/blog/global-descriptor-table-gdt-in-linux-x86-64/&quot;&gt;Martin Uy&#39;s GDT notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://lwn.net/Articles/883104/&quot;&gt;LWN: a restartable sequences update&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://maskray.me/blog/2021-02-14-all-about-thread-local-storage&quot;&gt;MaskRay: all about thread-local storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>vDSO - why tho?</title>
        <published>2023-06-04T00:00:00+00:00</published>
        <updated>2026-08-11T00:00:00+00:00</updated>
        
        <author>
          <name>tramasys</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://tramasys.mov/posts/vdso/"/>
        <id>https://tramasys.mov/posts/vdso/</id>
        
        <content type="html" xml:base="https://tramasys.mov/posts/vdso/">&lt;span id=&quot;continue-reading&quot;&gt;&lt;/span&gt;&lt;h2 id=&quot;what-is-the-vdso&quot;&gt;what is the vDSO?&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#what-is-the-vdso&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The vDSO is the virtual dynamic shared object. Linux maps this small shared
library into every new userspace process, and the C library uses functions
from it when that is faster than entering the kernel with a normal system
call.&lt;/p&gt;
&lt;p&gt;The name is used for two closely related things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;an ELF shared object built as part of the kernel&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;[vdso]&lt;/code&gt; memory mapping containing that object in a process.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The code in that mapping still executes in userspace, at ring 3. It does not
gain kernel privileges just because the kernel supplied it. Its advantage is
that it can combine ordinary userspace instructions with data the kernel has
mapped read-only next to it.&lt;/p&gt;
&lt;p&gt;That second mapping is normally called &lt;code&gt;[vvar]&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;7ffff7fc4000-7ffff7fc6000 r--p 00000000 00:00 0  [vvar]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;7ffff7fc6000-7ffff7fc8000 r-xp 00000000 00:00 0  [vdso]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;[vdso]&lt;/code&gt; contains executable code. &lt;code&gt;[vvar]&lt;/code&gt; contains kernel-maintained data
used by that code. Recent x86 kernels may also map a &lt;code&gt;[vvar_vclock]&lt;/code&gt; region
for paravirtual clock pages. The exact addresses, sizes, and available
symbols depend on the architecture, kernel version, and configuration.&lt;/p&gt;
&lt;p&gt;Both mappings are normally randomized on each &lt;code&gt;exec&lt;/code&gt;. A hard-coded vDSO
address would therefore be useless. The kernel instead gives the new process
the address of the ELF header in its auxiliary vector as
&lt;code&gt;AT_SYSINFO_EHDR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The older x86 &lt;code&gt;[vsyscall]&lt;/code&gt; page is a different mechanism. It lived at a fixed
address and had a much more rigid ABI. Some current glibc identifiers still
contain &lt;code&gt;VSYSCALL&lt;/code&gt;, but in the paths below they mean a call into the vDSO, not
the old fixed page.&lt;/p&gt;
&lt;h2 id=&quot;finding-the-image&quot;&gt;finding the image&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#finding-the-image&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I started by checking the process mappings:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; rg &lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;\[(vdso|vvar|vvar_vclock)\]&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt; /proc/self/maps&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;7ffff7fc4000-7ffff7fc6000 r--p 00000000 00:00 0  [vvar]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;7ffff7fc6000-7ffff7fc8000 r-xp 00000000 00:00 0  [vdso]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a small trap in that command: &lt;code&gt;/proc/self&lt;/code&gt; refers to the process
which opens it, so a short-lived tool may actually show its own mapping. For
examining a particular program, GDB is less ambiguous:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) set disassembly-flavor intel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) start&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) info proc mappings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) info auxv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;info auxv&lt;/code&gt; includes the entry used to find the image:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;AT_SYSINFO_EHDR      System-supplied DSO&amp;#39;s ELF header  0x7ffff7fc6000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also visible before &lt;code&gt;main&lt;/code&gt; through the dynamic loader:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; LD_SHOW_AUXV&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; /bin/true&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; rg&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; SYSINFO_EHDR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;AT_SYSINFO_EHDR:      0x7ffce917e000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The address can be obtained programmatically with
&lt;code&gt;getauxval(AT_SYSINFO_EHDR)&lt;/code&gt;. This gives the ELF base, not the address of a
particular function.&lt;/p&gt;
&lt;p&gt;Since the mapping is a real ELF image, it can be dumped and inspected like
one. After taking the start and end addresses from &lt;code&gt;info proc mappings&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) dump binary memory /tmp/vdso.bin 0x7ffff7fc6000 0x7ffff7fc8000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) shell file /tmp/vdso.bin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) shell readelf -Ws /tmp/vdso.bin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(gdb) shell objdump -d -M intel /tmp/vdso.bin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first command produces something along these lines:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;/tmp/vdso.bin: ELF 64-bit LSB shared object, x86-64, dynamically linked, stripped&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My original dump, from Linux 6.4.4, contained the following interesting
dynamic symbols:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;clock_gettime@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_clock_gettime@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gettimeofday@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_gettimeofday@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;time@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_time@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;clock_getres@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_clock_getres@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;getcpu@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_getcpu@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_sgx_enter_enclave@@LINUX_2.6&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The exact table is not permanent. The current x86-64 linker script also
exports &lt;code&gt;getrandom&lt;/code&gt; and &lt;code&gt;__vdso_getrandom&lt;/code&gt;, for example. New functions can be
added because userspace performs an ordinary versioned ELF symbol lookup at
runtime. Programs must not assume that every kernel or every architecture
exports the same names.&lt;/p&gt;
&lt;h2 id=&quot;who-put-it-there&quot;&gt;who put it there?&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#who-put-it-there&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The mappings arrive during &lt;code&gt;exec&lt;/code&gt;, before the program gets control.&lt;/p&gt;
&lt;p&gt;For an x86-64 ELF executable the current route through the kernel is roughly:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;load_elf_binary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; ARCH_SETUP_ADDITIONAL_PAGES&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; arch_setup_additional_pages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; map_vdso(&amp;amp;vdso_image_64, 0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; vdso_install_vvar_mapping&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; create_elf_tables&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; ARCH_DLINFO&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; AT_SYSINFO_EHDR = current-&amp;gt;mm-&amp;gt;context.vdso&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/entry/vdso/vma.c&quot;&gt;&lt;code&gt;map_vdso&lt;/code&gt;&lt;/a&gt; first finds an unused randomized area. It
installs the image as a special readable and executable mapping, then
installs the vvar pages next to it. The resulting vDSO base is saved in the
process&#39;s &lt;code&gt;mm&lt;/code&gt; context.&lt;/p&gt;
&lt;p&gt;With the error handling removed, the relevant part of the current x86 kernel
code is small:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;text_start &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span&gt; addr &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;/span&gt;&lt;span&gt; image&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;sym_vvar_start&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;_install_special_mapping&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;mm&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; text_start&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; image&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    VM_READ &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;|&lt;/span&gt;&lt;span&gt; VM_EXEC &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;|&lt;/span&gt;&lt;span&gt; VM_MAYREAD &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;|&lt;/span&gt;&lt;span&gt; VM_MAYWRITE &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;|&lt;/span&gt;&lt;span&gt; VM_MAYEXEC&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span&gt;vdso_mapping&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;vdso_install_vvar_mapping&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;mm&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; addr&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;mm&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;context.vdso &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;void *&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span&gt;text_start&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;text_start&lt;/code&gt; is the beginning of the ELF image. &lt;code&gt;addr&lt;/code&gt; is positioned so the
vvar pages described by the image land at their expected relative offset.
The last assignment is the value which will later become
&lt;code&gt;AT_SYSINFO_EHDR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The mapping has &lt;code&gt;VM_MAYWRITE&lt;/code&gt; so a debugger can create private copy-on-write
pages when it inserts a breakpoint. That does not make the normal mapping
writable. Its effective permissions are still read and execute.&lt;/p&gt;
&lt;p&gt;Later, &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/fs/binfmt_elf.c&quot;&gt;&lt;code&gt;create_elf_tables&lt;/code&gt;&lt;/a&gt; constructs the initial stack,
including the auxiliary vector. On x86, &lt;code&gt;ARCH_DLINFO&lt;/code&gt; adds
&lt;code&gt;AT_SYSINFO_EHDR&lt;/code&gt; with the base saved by &lt;code&gt;map_vdso&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The dynamic loader then takes over. glibc&#39;s
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/glibc-2.44/sysdeps/unix/sysv/linux/dl-parse_auxv.h&quot;&gt;&lt;code&gt;_dl_parse_auxv&lt;/code&gt;&lt;/a&gt; stores &lt;code&gt;AT_SYSINFO_EHDR&lt;/code&gt; in
&lt;code&gt;dl_sysinfo_dso&lt;/code&gt;. &lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/glibc-2.44/elf/setup-vdso.h&quot;&gt;&lt;code&gt;setup-vdso.h&lt;/code&gt;&lt;/a&gt; treats the already
mapped image as an abridged &lt;code&gt;link_map&lt;/code&gt;, reads its dynamic section and hash
tables, and adds it to the loader namespace. This is why tools such as &lt;code&gt;ldd&lt;/code&gt;
can display it as if it were another shared library:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;linux-vdso.so.1 (0x00007ffd5c3f8000)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, &lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/glibc-2.44/sysdeps/unix/sysv/linux/dl-vdso.h&quot;&gt;&lt;code&gt;dl-vdso.h&lt;/code&gt;&lt;/a&gt; performs versioned weak symbol lookups.
On x86 the expected version is &lt;code&gt;LINUX_2.6&lt;/code&gt;. The resolved addresses are stored
in glibc&#39;s read-only loader state as pointers such as
&lt;code&gt;dl_vdso_clock_gettime&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;No vDSO file was opened from the filesystem in this process. The kernel had
already mapped the object and told the loader where it was.&lt;/p&gt;
&lt;h2 id=&quot;where-did-the-elf-come-from&quot;&gt;where did the ELF come from?&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#where-did-the-elf-come-from&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The x86 vDSO is built with the kernel, but it is not linked into the ordinary
kernel text and executed there.&lt;/p&gt;
&lt;p&gt;The vDSO sources are compiled as position-independent userspace code and
linked with their own linker script. The exported symbol set and symbol
version are defined in &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/entry/vdso/vdso.lds.S&quot;&gt;&lt;code&gt;vdso.lds.S&lt;/code&gt;&lt;/a&gt;. The result is stripped
and checked because the runtime image cannot contain relocations that would
need a normal dynamic linker.&lt;/p&gt;
&lt;p&gt;The x86 build then runs &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/entry/vdso/vdso2c.c&quot;&gt;&lt;code&gt;vdso2c&lt;/code&gt;&lt;/a&gt;. That tool reads the finished
ELF image and emits the data and metadata used to construct a &lt;code&gt;vdso_image&lt;/code&gt; in
the kernel. The 64-bit image eventually becomes &lt;code&gt;vdso_image_64&lt;/code&gt;, which is the
object passed to &lt;code&gt;map_vdso&lt;/code&gt; above.&lt;/p&gt;
&lt;p&gt;So the lifecycle looks like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;kernel source tree&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; compile and link a small position-independent ELF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; turn that ELF into an image embedded in the kernel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; exec maps the image into a process&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; AT_SYSINFO_EHDR publishes its address&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; libc resolves versioned functions from it&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This also explains why the vDSO matches the running kernel rather than the
distribution&#39;s libc package.&lt;/p&gt;
&lt;h2 id=&quot;code-in-vdso-data-in-vvar&quot;&gt;code in vdso, data in vvar&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#code-in-vdso-data-in-vvar&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;clock_gettime&lt;/code&gt; needs data that changes as the kernel maintains time, but
letting userspace write that data would obviously be a problem. Linux splits
the mechanism in two:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[vdso]&lt;/code&gt; is executable code supplied by the kernel.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[vvar]&lt;/code&gt; is kernel-owned data mapped read-only to userspace.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The kernel writes the data through its own mapping. The process can read it
but cannot change the shared kernel state.&lt;/p&gt;
&lt;p&gt;My old notes described this as one &lt;code&gt;struct vdso_data&lt;/code&gt;. That is no longer the
current layout. In Linux 7.1, &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/include/vdso/datapage.h&quot;&gt;&lt;code&gt;include/vdso/datapage.h&lt;/code&gt;&lt;/a&gt;
defines separate pieces including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;struct vdso_clock&lt;/code&gt;, with the sequence counter, clock mode, cycle base,
mask, multiplier, shift, and base times.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;struct vdso_time_data&lt;/code&gt;, containing the clock data, auxiliary clocks,
timezone values, and timer resolution.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;struct vdso_rng_data&lt;/code&gt;, used by the newer vDSO &lt;code&gt;getrandom&lt;/code&gt; path.&lt;/li&gt;
&lt;li&gt;pages for time namespaces and architecture-specific data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/lib/vdso/datastore.c&quot;&gt;&lt;code&gt;lib/vdso/datastore.c&lt;/code&gt;&lt;/a&gt; maps these pages and services
their faults. The generic order currently includes time data, time-namespace
data, and RNG data, followed by architecture-specific pages where required.&lt;/p&gt;
&lt;p&gt;That matters if new vDSO data is added. &lt;code&gt;DECLARE_VVAR&lt;/code&gt; and &lt;code&gt;DEFINE_VVAR&lt;/code&gt; from
older architecture code are not a generic extension interface. Shared time
data belongs in the generic data structures. Architecture-specific data
belongs in the corresponding &lt;code&gt;asm/vdso&lt;/code&gt; structures and pages. Every vDSO
variant which consumes the layout has to stay in agreement, and the exposed
layout has to remain compatible with older userspace code.&lt;/p&gt;
&lt;h2 id=&quot;following-clock-gettime-from-c&quot;&gt;following clock_gettime from c&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#following-clock-gettime-from-c&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Starting with a normal C program:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;stdio.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;time.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    struct&lt;/span&gt;&lt;span&gt; timespec ts&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;clock_gettime&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;CLOCK_REALTIME&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; == -&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;        perror&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;clock_gettime&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;time: %lld.%09ld&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, (&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;long long&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span&gt; ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span&gt;tv_sec&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span&gt;tv_nsec&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On x86-64, &lt;code&gt;clock_gettime&lt;/code&gt; is also system call 228. However, tracing this
program usually does not show that system call:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; cc -O2 clock.c -o clock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; strace -e clock_gettime ./clock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;time: 1786430552.887265796&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+++ exited with 0 +++&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The public function came from glibc. In glibc 2.44 its Linux implementation
is &lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/glibc-2.44/sysdeps/unix/sysv/linux/clock_gettime.c&quot;&gt;&lt;code&gt;__clock_gettime64&lt;/code&gt;&lt;/a&gt;. Depending on the architecture
and its &lt;code&gt;time_t&lt;/code&gt; size, the compiled path uses either the time64 or native-time
vDSO pointer. Reduced to the relevant decisions, it does this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span&gt;vdso_clock_gettime &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;!= NULL&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    r &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; INTERNAL_VSYSCALL_CALL&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;vdso_clock_gettime&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; clock_id&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; tp&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span&gt;r &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;/span&gt;&lt;span&gt; an_error&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; INTERNAL_SYSCALL_CALL&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;clock_gettime&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; clock_id&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; tp&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;INTERNAL_VSYSCALL_CALL&lt;/code&gt; is an indirect userspace function call through the
address resolved by the dynamic loader. The historic macro name does not
mean it performs a system call.&lt;/p&gt;
&lt;p&gt;With an optimized build, GDB therefore reaches an instruction resembling:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;call rax&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value in &lt;code&gt;rax&lt;/code&gt; is inside &lt;code&gt;[vdso]&lt;/code&gt; and resolves to
&lt;code&gt;__vdso_clock_gettime@@LINUX_2.6&lt;/code&gt;. Since execution never crossed into the
kernel, &lt;code&gt;strace&lt;/code&gt; had nothing to report.&lt;/p&gt;
&lt;h2 id=&quot;resolving-it-directly&quot;&gt;resolving it directly&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#resolving-it-directly&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It is possible to reproduce part of glibc&#39;s lookup by hand. This is useful
for investigating the object, although ordinary programs should continue to
call &lt;code&gt;clock_gettime&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;define&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; _GNU_SOURCE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;dlfcn.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;elf.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;stdio.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;string.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;sys/auxv.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;time.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;typedef int&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;/span&gt;&lt;span&gt;vdso_clock_gettime_fn&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)(&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;clockid_t&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; struct&lt;/span&gt;&lt;span&gt; timespec &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    void *&lt;/span&gt;&lt;span&gt;handle &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; dlopen&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;linux-vdso.so.1&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span&gt; RTLD_LAZY &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;|&lt;/span&gt;&lt;span&gt; RTLD_LOCAL&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span&gt;handle &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;== NULL&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;        fprintf&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;stderr&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;dlopen: %s&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; dlerror&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;())&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    dlerror&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    void *&lt;/span&gt;&lt;span&gt;symbol &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; dlvsym&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        handle&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;__vdso_clock_gettime&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;LINUX_2.6&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    const char *&lt;/span&gt;&lt;span&gt;error &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; dlerror&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span&gt;error &lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;!= NULL&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;        fprintf&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;stderr&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;dlvsym: %s&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    vdso_clock_gettime_fn fn&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    _Static_assert&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;sizeof&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; == sizeof&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;symbol&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;), &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;incompatible pointer sizes&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    memcpy&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;symbol&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; sizeof&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    struct&lt;/span&gt;&lt;span&gt; timespec ts&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;CLOCK_REALTIME&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; !=&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;AT_SYSINFO_EHDR:     %#lx&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; getauxval&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;AT_SYSINFO_EHDR&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;__vdso_clock_gettime: %p&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span&gt; symbol&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;time:                 %lld.%09ld&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;           (&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;long long&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span&gt; ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span&gt;tv_sec&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span&gt;tv_nsec&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compiling and running it shows that the symbol address is a small offset from
the auxiliary-vector address:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;shellsession&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; cc vdso-direct.c -ldl -o vdso-direct&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;$&lt;/span&gt;&lt;span&gt; ./vdso-direct&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;AT_SYSINFO_EHDR:      0x7ffcdecd6000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_clock_gettime: 0x7ffcdecd6ff0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;time:                 1786430552.887265796&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux&#39;s own &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/tools/testing/selftests/vDSO/vdso_test_correctness.c&quot;&gt;vDSO correctness selftest&lt;/a&gt; uses the same
general &lt;code&gt;dlopen&lt;/code&gt; and versioned lookup technique.&lt;/p&gt;
&lt;h2 id=&quot;inside-vdso-clock-gettime&quot;&gt;inside __vdso_clock_gettime&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#inside-vdso-clock-gettime&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;On x86, &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/entry/vdso/vclock_gettime.c&quot;&gt;&lt;code&gt;__vdso_clock_gettime&lt;/code&gt;&lt;/a&gt; is a small wrapper around
the generic vDSO time implementation. For &lt;code&gt;CLOCK_REALTIME&lt;/code&gt;, the current call
path is approximately:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;__vdso_clock_gettime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __cvdso_clock_gettime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __cvdso_clock_gettime_data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __cvdso_clock_gettime_common&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; do_hres&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; vdso_get_timestamp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __arch_get_hw_counter&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part is that &lt;code&gt;[vvar]&lt;/code&gt; does not contain a timestamp which the
kernel updates on every nanosecond. It contains a recent base time and the
values needed to convert a hardware counter into elapsed nanoseconds.&lt;/p&gt;
&lt;p&gt;The kernel source compiled into the vDSO makes that read loop visible. With
the capability check and declarations left out, current &lt;code&gt;do_hres&lt;/code&gt; contains:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;do&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;vdso_read_begin_timens&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;vc&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;seq&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; do_hres_timens&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;vd&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; vc&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; clk&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;vdso_get_timestamp&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;vd&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; vc&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; clk&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;sec&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;ns&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; while&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;vdso_read_retry&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;vc&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; seq&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;vdso_set_timespec&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; sec&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; ns&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;return true;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an unusual-looking piece of kernel source: after being built into the
vDSO image, these instructions execute in the calling process. A &lt;code&gt;false&lt;/code&gt;
result tells the outer clock path that this case cannot be answered safely
here, so it should use the syscall fallback. A successful read produces
&lt;code&gt;sec&lt;/code&gt; and &lt;code&gt;ns&lt;/code&gt; without leaving userspace.&lt;/p&gt;
&lt;p&gt;For a TSC clocksource, the simplified calculation is:&lt;/p&gt;
&lt;p&gt;$$
\text{now} = \text{base} +
\frac{(\text{cycles} - \text{cycle_last}) \times \text{mult}}{2^{\text{shift}}}
$$&lt;/p&gt;
&lt;p&gt;The actual &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/lib/vdso/gettimeofday.c&quot;&gt;&lt;code&gt;vdso_calc_ns&lt;/code&gt;&lt;/a&gt; code also deals with masking,
overflow, and the possibility of a slightly backward TSC observation. The
base seconds and nanoseconds, &lt;code&gt;cycle_last&lt;/code&gt;, &lt;code&gt;mult&lt;/code&gt;, and &lt;code&gt;shift&lt;/code&gt; come from
&lt;code&gt;[vvar]&lt;/code&gt;. The current cycle count comes directly from an x86 instruction.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/include/asm/vdso/gettimeofday.h&quot;&gt;&lt;code&gt;__arch_get_hw_counter&lt;/code&gt;&lt;/a&gt; selects that instruction from
the clock mode. For &lt;code&gt;VDSO_CLOCKMODE_TSC&lt;/code&gt; it calls &lt;code&gt;rdtsc_ordered()&lt;/code&gt;. The
kernel&#39;s alternatives mechanism patches this site for the CPU&#39;s available
ordering instruction. In Intel syntax the possible sequences are equivalent
to:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdtsc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;lfence&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdtsc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;rdtscp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two halves of the TSC are then combined into one 64-bit cycle value. A
paravirtual guest may instead read a pvclock or Hyper-V clock page from the
additional vvar mapping.&lt;/p&gt;
&lt;h2 id=&quot;reading-while-the-kernel-writes&quot;&gt;reading while the kernel writes&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#reading-while-the-kernel-writes&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There is still a race to handle. The kernel could update the multiplier or
base time while the process is halfway through reading them.&lt;/p&gt;
&lt;p&gt;The vDSO data therefore includes a sequence counter. The high-resolution
path does roughly this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;read sequence&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;if sequence is odd, wait and retry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;read base and conversion values&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;read hardware cycles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;read sequence again&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;if it changed, retry everything&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;calculate the timestamp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An odd sequence value means a writer is in progress. Equal even values before
and after the read mean the snapshot was consistent. The current helpers live
in &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/include/vdso/helpers.h&quot;&gt;&lt;code&gt;include/vdso/helpers.h&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The writer is on the kernel side. &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/kernel/time/vsyscall.c&quot;&gt;&lt;code&gt;update_vsyscall&lt;/code&gt;&lt;/a&gt;
opens the sequence, updates the clock mode, cycle base, multiplier, shift,
base times, and related values, then closes the sequence. Userspace gets a
coherent snapshot without a lock and without entering the kernel.&lt;/p&gt;
&lt;p&gt;Time namespaces add one more step. A process in a time namespace sees
namespace offsets in its vvar page while the underlying host clock data comes
from the host page. The helper combines the two before returning the result.&lt;/p&gt;
&lt;p&gt;Coarse clocks take a shorter route: they return the kernel&#39;s cached base time
without reading a hardware cycle counter. They are cheaper but intentionally
have lower resolution.&lt;/p&gt;
&lt;h2 id=&quot;when-it-still-becomes-a-syscall&quot;&gt;when it still becomes a syscall&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#when-it-still-becomes-a-syscall&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The vDSO is a fast path, not a promise that every &lt;code&gt;clock_gettime&lt;/code&gt; call avoids
the kernel. It falls back when, for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the requested clock ID has no vDSO implementation.&lt;/li&gt;
&lt;li&gt;the active clocksource cannot be read safely from userspace.&lt;/li&gt;
&lt;li&gt;the kernel does not export the expected symbol.&lt;/li&gt;
&lt;li&gt;an architecture-specific time-width path is unavailable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The generic vDSO code uses its inline syscall fallback for unsupported cases.
glibc also has a syscall path when symbol resolution failed.&lt;/p&gt;
&lt;p&gt;To compare the two paths deliberately, the libc wrapper can be bypassed:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;sys/syscall.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;time.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5E81AC;font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;unistd.h&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;struct&lt;/span&gt;&lt;span&gt; timespec ts&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;syscall&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;SYS_clock_gettime&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span&gt; CLOCK_REALTIME&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;ts&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;strace -e clock_gettime&lt;/code&gt; sees the call. On current Linux the real kernel
handler is still defined in &lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/kernel/time/posix-timers.c&quot;&gt;&lt;code&gt;kernel/time/posix-timers.c&lt;/code&gt;&lt;/a&gt;
with &lt;code&gt;SYSCALL_DEFINE2(clock_gettime, ...)&lt;/code&gt;. That path selects the clock&#39;s
kernel implementation and copies the resulting &lt;code&gt;timespec&lt;/code&gt; back to userspace.&lt;/p&gt;
&lt;h2 id=&quot;rust-reaches-the-same-code&quot;&gt;rust reaches the same code&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#rust-reaches-the-same-code&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The Rust standard library example is just as small as before:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;use&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SystemTime&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;    println!&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;:?&lt;/span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SystemTime&lt;/span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;now&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a normal GNU/Linux target, Rust does not parse the vDSO itself. Its current
Unix time backend eventually asks for &lt;code&gt;CLOCK_REALTIME&lt;/code&gt;, and
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/rust-lang/rust/blob/main/library/std/src/sys/pal/unix/time.rs&quot;&gt;&lt;code&gt;Timespec::now&lt;/code&gt;&lt;/a&gt; calls &lt;code&gt;libc::clock_gettime&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The path is therefore:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SystemTime::now&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; Rust&amp;#39;s Unix time backend&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; Timespec::now(CLOCK_REALTIME)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; libc::clock_gettime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; glibc&amp;#39;s resolved vDSO function&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __vdso_clock_gettime&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is extra handling on 32-bit GNU/Linux for the time64 transition, but
the decision still ends in a libc clock function. Other targets, especially
non-glibc or statically linked ones, can use a different libc implementation
while presenting the same Rust API.&lt;/p&gt;
&lt;p&gt;This explains why &lt;code&gt;strace&lt;/code&gt; normally shows no &lt;code&gt;clock_gettime&lt;/code&gt; system call for
the Rust program either.&lt;/p&gt;
&lt;h2 id=&quot;python-reaches-the-same-code&quot;&gt;python reaches the same code&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#python-reaches-the-same-code&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Python adds more layers but ends up at the same interface:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;import&lt;/span&gt;&lt;span&gt; time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;time_ns&lt;/span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;())&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The names in my original notes had become stale. In current CPython the route
is:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;time.time / time.time_ns&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; time_time / time_time_ns&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; PyTime_Time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; py_get_system_clock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; clock_gettime(CLOCK_REALTIME)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; glibc&amp;#39;s vDSO path&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The module entry points are in
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/python/cpython/blob/main/Modules/timemodule.c&quot;&gt;&lt;code&gt;Modules/timemodule.c&lt;/code&gt;&lt;/a&gt;. The platform work happens in
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/python/cpython/blob/main/Python/pytime.c&quot;&gt;&lt;code&gt;Python/pytime.c&lt;/code&gt;&lt;/a&gt;, where &lt;code&gt;py_get_system_clock&lt;/code&gt; uses
&lt;code&gt;clock_gettime(CLOCK_REALTIME)&lt;/code&gt; on a Unix system which provides it, and keeps
&lt;code&gt;gettimeofday&lt;/code&gt; as a fallback.&lt;/p&gt;
&lt;p&gt;So CPython does not contain a separate vDSO implementation. It calls the
normal C library API and inherits glibc&#39;s resolved fast path. &lt;code&gt;time.time()&lt;/code&gt;
converts the result to a Python float, while &lt;code&gt;time.time_ns()&lt;/code&gt; keeps integer
nanoseconds. The operating-system clock lookup below them is shared.&lt;/p&gt;
&lt;h2 id=&quot;putting-the-pieces-together&quot;&gt;putting the pieces together&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#putting-the-pieces-together&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;For the common &lt;code&gt;CLOCK_REALTIME&lt;/code&gt; case, the complete route is:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;C clock_gettime / Rust SystemTime::now / Python time.time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; libc clock_gettime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; function pointer resolved from the vDSO ELF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; __vdso_clock_gettime executes in userspace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; read a consistent base and conversion data from [vvar]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; read the hardware or paravirtual cycle counter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; calculate the current timestamp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; return without a system call&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The vDSO works because the kernel split the operation into two parts. The
kernel maintains the privileged and slowly changing clock state, while a
small kernel-supplied userspace function combines that state with a counter
the process is allowed to read. The sequence counter makes concurrent updates
detectable, and the syscall path remains available whenever the shortcut
cannot answer safely.&lt;/p&gt;
&lt;p&gt;That is more machinery than the original &lt;code&gt;clock_gettime&lt;/code&gt; call suggests, but
it moves one of the most frequently requested pieces of kernel information
out of the syscall path without giving the process control over it.&lt;/p&gt;
&lt;h2 id=&quot;source-trail&quot;&gt;source trail&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#source-trail&quot; hidden=&quot;&quot;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://man7.org/linux/man-pages/man7/vdso.7.html&quot;&gt;&lt;code&gt;vdso(7)&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://lwn.net/Articles/615809/&quot;&gt;The vDSO: clock_gettime() and beyond&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/Documentation/ABI/stable/vdso&quot;&gt;Linux 7.1 stable vDSO ABI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/fs/binfmt_elf.c&quot;&gt;Linux 7.1 ELF loader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/arch/x86/entry/vdso/vma.c&quot;&gt;Linux 7.1 x86 vDSO mapping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/torvalds/linux/blob/v7.1/lib/vdso/gettimeofday.c&quot;&gt;Linux 7.1 generic vDSO time implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/glibc-2.44/elf/setup-vdso.h&quot;&gt;glibc 2.44 vDSO setup and lookup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/bminor/glibc/blob/glibc-2.44/sysdeps/unix/sysv/linux/clock_gettime.c&quot;&gt;glibc 2.44 &lt;code&gt;clock_gettime&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/rust-lang/rust/blob/main/library/std/src/sys/pal/unix/time.rs&quot;&gt;Rust&#39;s current Unix time backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/python/cpython/blob/main/Modules/timemodule.c&quot;&gt;CPython&#39;s current time module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
</feed>
