Skip to content

Instantly share code, notes, and snippets.

@sogaiu
Last active March 6, 2025 12:10
Show Gist options
  • Save sogaiu/03b7bfd7f794920b14d4c7d19756d2f7 to your computer and use it in GitHub Desktop.
Save sogaiu/03b7bfd7f794920b14d4c7d19756d2f7 to your computer and use it in GitHub Desktop.
janet cfun via gdb notes

Calling a janet cfun via GDB

It's possible to call a janet cfun (e.g. the c bits that implement math/random or math/cos, but also see the end of this doc) from gdb and examine the results. What follows is a demonstration using janet_rand and janet_cos, which are the cfuns underlying math/random and math/cos respectively. See src/core/math.c for details.

Flow Summary

  • Preparation
    • Compile janet with debugging info
    • Create sample janet source file
    • Run janet with sample file under gdb
  • Demos of calling cfuns
    • Prepare some arguments for some janet cfuns
    • Call the janet cfuns
    • Examine the results

Detailed Steps

Preparation

  • Get janet source

    $ git clone https://github.com/janet-lang/janet
    $ cd janet
    
  • Compile janet with debugging info -- but first tweak Makefile

    $ sed -i "s/^CFLAGS?=-O2/CFLAGS?=-O0 -g3/" Makefile
    $ make clean && make
    
  • Create a test janet script -- nothing special about pp here, just chose something

    echo "(pp {:a 1})" > test.janet
    
  • Start debug-enabled janet

    $ gdb -quiet -cd . --args build/janet test.janet
    Reading symbols from build/janet...
    
  • Set an appropriate break point -- cfun_io_printf is part of pp's implementation

    (gdb) b cfun_io_printf
    Breakpoint 1 at 0x2dfe9: file src/core/io.c, line 631.
    
  • Start the janet process via gdb

    (gdb) r
    Starting program: /home/user/src/janet.internals/build/janet test.janet
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/usr/lib/libthread_db.so.1".
    
    Breakpoint 1, cfun_io_printf (argc=2, argv=0x55555566c738) at src/core/io.c:631
    631	    return cfun_io_printf_impl(argc, argv, 1, "out", stdout);
    

Calling a cfun that doesn't use argv -- janet_rand

  • Call the janet_rand cfun -- like all janet cfuns, the signature is (int32_t argc, Janet *argv). Below, argc is 0 and argv is 0 (argv is unused within janet_rand)

    (gdb) p janet_rand(0, 0)
    $2 = {u64 = 4588835678547723040, i64 = 4588835678547723040, number = 0.060193915906785245, pointer = 0x3faed1bca879c320}
    
  • Determine the JanetType of the result

    (gdb) p janet_type($2)
    $3 = JANET_NUMBER
    
  • Peek inside the result

    (gdb) p janet_unwrap_number($3)
    $4 = 0.48718118769171492
    

Calling a cfun that does use argv -- janet_cos

  • Allocate some memory to use for argv of length 1

    (gdb) p (Janet *)janet_malloc(sizeof(Janet) * (size_t) 1)
    $5 = (Janet *) 0x55555566cbb0
    
  • Put something in at the beginning of argv -- assigning to argv[0]

    (gdb) $5[0] = janet_wrap_number(0)
    $6 = {u64 = 0, i64 = 0, number = 0, pointer = 0x0}
    
  • Call the janet_cos cfun -- below, argc is 1 and argv is $5

    (gdb) janet_cos(1, $5)
    $7 = {u64 = 4607182418800017408, i64 = 4607182418800017408, number = 1, pointer = 0x3ff0000000000000}
    
  • Determine the JanetType of the result

    (gdb) p janet_type($7)
    $8 = JANET_NUMBER
    
  • Peek inside the result

    (gdb) p janet_unwrap_number($7)
    $9 = 1
    

Remarks

  • Not using a test file and/or trying to do some of the steps above before janet has finished intializing may lead to various things not quite working, e.g.

    • Being forced to cast the result of janet_malloc
    • Not being able to call janet_type successfully

    So I suggest following the steps above as closely as possible on an initial attempt.

  • What is a cfun? Looking at src/include/janet.h and src/core/fiber.h, my guess is that it's another name for JanetCFunction. According to the C functions section from the "The Janet Abstract Machine" page at the official janet documentation website:

    Janet uses C functions to bridge to native code. A C function (JanetCFunction * in C) is a C function pointer that can be called like a normal Janet closure. From the perspective of the bytecode instruction set, there is no difference in invoking a C function and invoking a normal Janet function.

    On a tangential note, that last bit about there being no difference might be seen here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment