Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
128 lines
3.8 KiB
C
128 lines
3.8 KiB
C
// 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 "core/types.h"
|
|
#include "core/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;
|
|
}
|