// SPDX-License-Identifier: GPL-2.0-or-later // // Naming an address. // // docs/architecture/debugging.md milestone D0. It is the least clever thing on that // plan and the one everything else is worth more with: a call chain of six // hex numbers has to be resolved by hand against a disassembly that may not // be the build that was running, and the resolving is done at exactly the // moment when you least want a second task. // // The table is built by tools/ksyms.pl from the *first* of two link passes and // linked into the second. kernel/kernel.ld puts .ksym after .text and .rodata, // so carrying the table cannot move the addresses recorded in it; the Makefile // checks that rather than trusting it, by listing the text symbols of both // passes and refusing to build if they differ. #include "types.h" #include "defs.h" // Weak, and the reason is worth stating: the first link pass has no table -- // it is the input to the program that writes one. An undefined weak symbol // links as zero, so that pass resolves these to nothing and still links. The // same property means a kernel built with the generator removed boots fine and // simply cannot name anything, which is a much better failure than a build // that stops. extern const uint32 ksym_count __attribute__((weak)); extern const uint64 ksym_base __attribute__((weak)); extern const uint64 ksym_textend __attribute__((weak)); extern const uint32 ksym_addr[] __attribute__((weak)); extern const uint32 ksym_nameoff[] __attribute__((weak)); extern const char ksym_names[] __attribute__((weak)); int ksym_ready(void) { return &ksym_count != 0 && ksym_count > 0; } // The greatest symbol at or below addr. // // Returns 0 for anything outside the kernel's text, which includes every // userspace address -- naming a user rip with a kernel symbol would be worse // than printing the number, because it would look like an answer. const char * ksym_lookup(uint64 addr, uint64 *off) { if (!ksym_ready()) return 0; if (addr < ksym_base || addr >= ksym_textend) return 0; uint32 want = (uint32)(addr - ksym_base); // Binary search for the last entry <= want, in the half-open form so the // indices stay unsigned and nothing has to go below zero. ksym_addr[0] is 0 // by construction -- the base *is* the first symbol -- so the invariant // "ksym_addr[lo] <= want" holds on entry and there is no empty case. uint32 lo = 0, hi = ksym_count; while (hi - lo > 1) { uint32 mid = lo + (hi - lo) / 2; if (ksym_addr[mid] <= want) lo = mid; else hi = mid; } if (off) *off = want - ksym_addr[lo]; return &ksym_names[ksym_nameoff[lo]]; } static int put(char *buf, int cap, int n, const char *s) { while (*s && n < cap - 1) buf[n++] = *s++; return n; } static int puthex(char *buf, int cap, int n, uint64 v) { static const char h[] = "0123456789abcdef"; char tmp[17]; int i = 0; if (v == 0) return put(buf, cap, n, "0"); while (v && i < 16) { tmp[i++] = h[v & 0xF]; v >>= 4; } while (i-- > 0 && n < cap - 1) buf[n++] = tmp[i]; return n; } // "sched+0x2a", or the bare address when there is nothing to say. // // Writes into a caller's buffer rather than printing, because both callers // that matter are in panic context: one is building a line for the crash // screen and the other is building the QR payload. Neither can call cprintf. int ksym_fmt(char *buf, int cap, uint64 addr) { uint64 off = 0; const char *name; int n = 0; if (cap <= 1) { if (cap == 1) buf[0] = 0; return 0; } name = ksym_lookup(addr, &off); if (!name) { n = put(buf, cap, n, "0x"); n = puthex(buf, cap, n, addr); buf[n] = 0; return n; } n = put(buf, cap, n, name); if (off) { n = put(buf, cap, n, "+0x"); n = puthex(buf, cap, n, off); } buf[n] = 0; return n; }