[ Home ]
SBCL Internals

The pages on this CLiki-driven site can be edited by anybody at any time. No warranty of any kind can therefore be made; any implied warranties of merchantability or fitness for a particular purpose are expressly disclaimed
[ Home ] [ Recent Changes ] [ About CLiki ] [ Text Formatting ]

The system fails in cold init:
fatal error encountered in SBCL runtime system:
internal error too early in init, can't recover
There's no LDB in this build; exiting.
internal error #23
    SC: 14, Offset: 0   lispobj 0x505a84a7

Commonly this is just because something (a variable or function) is undefined. Wouldn't it be nice to know the name of the undefined thing? With gdb, and with a little patience, this can be done.

(This page was written using values from sbcl-0.7.1.17. Magic numbers like (e.g. 23) and layouts (e.g. for fdefn) may change, so it's advisable to look them up yourself in the current sbcl.h.)

Search for "23" in src/runtime/sbcl.h. The relevant entry is

#define ERRORS { \
    ...
    "Object is not coerceable to type FUNCTION.", /*23*/ \
    ...
    }

Now we run gdb:

$ gdb /usr/stuff/sbcl/src/runtime/sbcl
(gdb) break call_into_c
(gdb) run --core /usr/stuff/sbcl/output/cold-sbcl.core --sysinit /dev/null --userinit /dev/null
When gdb stops at the breakpoint
Program received signal SIGTRAP, Trace/breakpoint trap.
0x7425 in undefined_tramp ()
(gdb) 
we're ready to poke around in the lispobj value reported above until we find the name of the undefined function.

The lispobj value is a tagged pointer: the low 3 bits are a "lowtag" value which are used by Lisp to implement dynamic typing. Grepping for LOWTAG in sbcl.h can tell you what they mean. Gdb doesn't know about them, so in general we have to mask them off before passing the pointer to gdb. So, the lispobj value 0x505a84a7 becomes the native pointer 0x505a84a0, and we ask gdb to look at that.

(gdb) x/8x 0x505a84a0
0x505a84a0: 0x000003ae 0x505a849b 0x2800000b 0x00007424
0x505a84b0: 0x00000596 0x000000a2 0x285b6680 0x2800000b

The first word's low bits are tag bits. The low 3 bits are 0x6, which sbcl.h tells us are OTHER_IMMEDIATE_1_LOWTAG. An other-immediate lowtag mean that the entire low byte is used as a wide tag. Looking in sbcl.h for the WIDETAG value associated with 0xae gives us FDEFN_WIDETAG. Then we can look up the C representation of an fdefn later in sbcl.h:

struct fdefn {
    lispobj header;
    lispobj name;
    lispobj fun;
    char * raw_addr;
};

So, the name slot value is the tagged pointer 0x505a849b. The lowtag bits are 0x3, i.e. LIST_POINTER_LOWTAG, so the pointed-to thing is a cons. The cons layout is

struct cons {
    lispobj car;
    lispobj cdr;
};

Converting the tagged pointer to a native pointer by stripping off the lowtag bits and looking at it with gdb, we see

(gdb) x/2x 0x505a8498
0x505a8498: 0x50009e07 0x505a8493

Since this is a function name, its CAR should be SETF (since that's the only kind of function name which can be a list). If we were feeling extremely skeptical, we could check that, but today we'll just look at the CDR, another cons:

(gdb) x/8x 0x505a8490
0x505a8490: 0x50009edf 0x2800000b

Looking at the CAR of this

(gdb) x/8x 0x50009ed8
0x50009ed8: 0x00000596 0x000000a2 0x063206a0 0x2800000b
0x50009ee8: 0x50009ef7 0x51e36dd1 0x0000002a 0x00000028

we can see that its tag bits are SYMBOL_HEADER_WIDETAG. The layout for symbol is

struct symbol {
    lispobj header;
    lispobj value;
    lispobj hash;
    lispobj plist;
    lispobj name;
    lispobj package;
};

We want the name of the symbol:

(gdb) x/8x 0x50009ef0
0x50009ef0: 0x0000002a 0x00000028 0x544f4c53 0x4c41562d
0x50009f00: 0x00004555 0x00000000 0x00000596 0x000000a2

To figure out a SIMPLE-STRING, you can either guess and experiment, or look at the old CMU CL internals documentation. Basically, the first word after the header is the count (as a Lisp FIXNUM, not a native integer), and the characters start in the second word after the header. So:

(gdb) x/s 0x50009ef8
0x50009ef8:  "SLOT-VALUE"

Voila! (Or, if there was any ambiguity about which package the symbol was in, we'd either (the easy way) look it up in cold-sbcl.map, or (the harder way) chase pointers starting with the SYMBOL-PACKAGE slot.


Note also that gdb can do a little more pretty-printing to stop you from having to count quite as much. To look at the symbol in this example, after having detemined that it's a symbol, you could do

(gdb) print /x * ((struct symbol *)0x50009ed8)


This page is linked from: gdb  

CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively