<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux on dere3046</title><link>https://dere3046.github.io/tags/linux/</link><description>Recent content in Linux on dere3046</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 02 Jul 2026 14:00:00 +0800</lastBuildDate><atom:link href="https://dere3046.github.io/tags/linux/index.xml" rel="self" type="application/rss+xml"/><item><title>ksymless on older kernels</title><link>https://dere3046.github.io/posts/ksymless-older-kernels/</link><pubDate>Thu, 02 Jul 2026 14:00:00 +0800</pubDate><guid>https://dere3046.github.io/posts/ksymless-older-kernels/</guid><description>&lt;p&gt;v3 discovered kallsyms data by scanning for &lt;code&gt;token_index&lt;/code&gt; (256 x u16,
&lt;code&gt;ti[0]=0&lt;/code&gt;), then reading &lt;code&gt;kallsyms_offsets&lt;/code&gt; 512 bytes after it. works
on GKI 6.6 and 6.12. fails on 5.10, 5.15, and 6.1.&lt;/p&gt;
&lt;p&gt;the reason is simple. the binary output order of &lt;code&gt;write_src()&lt;/code&gt; in
&lt;code&gt;scripts/kallsyms.c&lt;/code&gt; changed between kernel versions.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;the layout shift happened somewhere between 6.1 and 6.6. android14-6.1
still uses the old order, android15-6.6 uses the new one.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;in the old layout (i call it v1), offsets come first. the entire block
looks like: offsets, then &lt;code&gt;kallsyms_relative_base&lt;/code&gt; (8 bytes, value is
&lt;code&gt;_text&lt;/code&gt;), then &lt;code&gt;kallsyms_num_syms&lt;/code&gt; (4 bytes, equals the symbol count N),
then names, markers, token table, and finally token_index at the end.
offsets are nowhere near token_index.&lt;/p&gt;
&lt;p&gt;in the new layout (v2, 6.6+), &lt;code&gt;token_index&lt;/code&gt; sits right before offsets.
token_index + 512 lands exactly on offsets. the v3 approach works
because of this.&lt;/p&gt;
&lt;p&gt;so for older kernels we need a different way to find offsets. without
token_index as a shortcut, we look for a sorted u32 array that ends
with the pattern: 8 bytes of &lt;code&gt;_text&lt;/code&gt; followed by 4 bytes matching the
array length. kind of like finding the signature of a packed struct.&lt;/p&gt;
&lt;p&gt;the scan works in two phases. first, we check if the current page is
entirely sorted:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; run &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; max_i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;4096&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; off) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; max_i; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; v &lt;span style="color:#f92672"&gt;=&lt;/span&gt; buf[(off &lt;span style="color:#f92672"&gt;+&lt;/span&gt; i &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)v &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; prev) &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)v; run&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (run &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; max_i) &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;only if the sorted run reaches the page boundary do we cross into
safe_read territory. this filters out almost everything instantly.&lt;/p&gt;
&lt;p&gt;then we verify the pattern:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((rb &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#f92672"&gt;~&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0x1FFFFFULL&lt;/span&gt;) &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; kernel_base) &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (ns &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)run) &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;the mask comparison on &lt;code&gt;_text&lt;/code&gt; saves us from false negatives when the
value is not exactly 2MB aligned across different builds.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;code&gt;kallsyms_seqs_of_names&lt;/code&gt; was added in kernel 6.1. 5.10 and 5.15 don&amp;rsquo;t
have it. when it is missing, &lt;code&gt;get_sym_seq()&lt;/code&gt; falls back to using the
symbol index directly, which works because symbols are emitted in order.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;took me a moment to realize why the scan kept failing on real devices.
i had capped the sorted u32 run at the 4096 byte page boundary. one
page is 1024 u32 entries, the threshold was 10000. the fix was cross
page &lt;code&gt;safe_read&lt;/code&gt; for any candidate that survived the first phase.
simple.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kloffs @ ..., sorted=103903
layout v1, 103904 symbols
verify: addr-&amp;gt;name MATCH
verify: name-&amp;gt;addr MATCH
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;looks like it works on all three. 6.12 through v2, 6.6 through v2,
6.1 through v1. 5.10 and 5.15 builds pass CI.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a class="link" href="https://www.coolapk.com/u/1550124" target="_blank" rel="noopener"
 &gt;汐の月&lt;/a&gt; for providing
the device for GKI 6.6 testing, and to
&lt;a class="link" href="https://www.coolapk.com/u/41654149" target="_blank" rel="noopener"
 &gt;阿尔托莉雅·潘德拉贡&lt;/a&gt; for
providing the device for GKI 6.1 testing.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/Dere3046/ksymless_Android" target="_blank" rel="noopener"
 &gt;https://github.com/Dere3046/ksymless_Android&lt;/a&gt;&lt;/p&gt;</description></item><item><title>finding kallsyms with token_index</title><link>https://dere3046.github.io/posts/ksymless-token-index/</link><pubDate>Thu, 02 Jul 2026 10:00:00 +0800</pubDate><guid>https://dere3046.github.io/posts/ksymless-token-index/</guid><description>&lt;p&gt;the first version relied on ADRP instruction scanning to find kallsyms
data. worked on android16-6.12, not on android15-6.6. the ADRP window
size depends on compiler inlining, which varies across kernel builds.
not something you can hardcode.&lt;/p&gt;
&lt;p&gt;the fix is simple. instead of chasing ADRP instructions, search for the
&lt;code&gt;token_index&lt;/code&gt; structure directly in kernel &lt;code&gt;.rodata&lt;/code&gt;. its layout is
determined by &lt;code&gt;write_src()&lt;/code&gt; in &lt;code&gt;scripts/kallsyms.c&lt;/code&gt;, identical in 6.6
and 6.12.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;output_label&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;kallsyms_token_index&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;256&lt;/span&gt;; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.short&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, best_idx[i]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// best_idx[i] = byte offset into token_table
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// token_index[0] = 0, strictly increasing, 256 x u16
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;output_label&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;kallsyms_offsets&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; table_cnt; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; offset &lt;span style="color:#f92672"&gt;=&lt;/span&gt; table[i]&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;addr &lt;span style="color:#f92672"&gt;-&lt;/span&gt; relative_base;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.long&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\t&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%#x&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)offset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;256 consecutive u16 values, strictly increasing, starting at 0. the
chance of random rodata matching that is essentially zero.&lt;/p&gt;
&lt;p&gt;once found, &lt;code&gt;kallsyms_offsets&lt;/code&gt; is exactly 512 bytes after &lt;code&gt;token_index&lt;/code&gt;
(plus &lt;code&gt;.balign 8&lt;/code&gt; padding). the search stops naturally when
&lt;code&gt;copy_from_kernel_nofault&lt;/code&gt; hits unmapped pages. we start scanning at
&lt;code&gt;kernel_base + 1MB&lt;/code&gt;, which is well inside &lt;code&gt;.text&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kernel_base &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt;)&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;sprint_symbol &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#f92672"&gt;~&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0x1FFFFFULL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;no ADRP scanning, no BL chain, no window sizes to guess. simple.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sorted=126031
SCT MATCH, addr-&amp;gt;name MATCH, name-&amp;gt;addr MATCH
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;that is it.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a class="link" href="https://www.coolapk.com/u/1550124" target="_blank" rel="noopener"
 &gt;汐の月&lt;/a&gt; for providing
the device for GKI 6.6 testing.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/Dere3046/ksymless_Android" target="_blank" rel="noopener"
 &gt;https://github.com/Dere3046/ksymless_Android&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>