Nexoniarz is the sole author and SchOS was never published under Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text, every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE, README.md, docs/licensing.md and the site say so. projects/schfs-linux follows the rest of the tree. The GPL gives copyleft: a fork has to ship its source. It also lifts the old "Linux drivers, never" rule, since GPL-2.0-only code combines with GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still has to pick one version, so docs/licensing.md now keeps Apache-2.0 and GPLv3 code out of the kernel to keep Linux code possible there. BSD 4-clause code goes from "avoid" to "no", because the advertising clause is GPL-incompatible. LDLS still ships no .ko. That boundary is now project policy rather than a legal requirement, and ldls.md, devices.md and licensing.md section 6 say so. refmod declares license=GPL, and ldls-check passes (27 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 "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;
|
|
}
|