With this https://github.com/JohnDowson/CraneLisp/blob/jit-primary/src/jit.rs
code generating following function,
function u0:0(f64) -> f64 system_v {
sig0 = (f64) -> f64 system_v
fn0 = u0:0 sig0
block0(v0: f64):
v1 = f64const 0x1.0000000000000p0
v2 = call fn0(v1)
return v2
}
and external symbol being
#[no_mangle]
pub extern "C" fn cl_print(n: f64) -> f64 {
println!("{}", n);
0.0
}
defined in the same binary as JIT engine.
Stepping through with the debugger yields following picture one step before segfault
; No Symbol Info
; Source location: unknown
5555562E10E0: 55 pushq %rbp
5555562E10E1: 48 89 E5 movq %rsp, %rbp
5555562E10E4: 48 BE 00 00 00 00 00 00 > movabsq $0x3ff0000000000000, %rsi ; imm = 0x3FF0000000000000
5555562E10EE: 66 48 0F 6E C6 movq %rsi, %xmm0
5555562E10F3: 48 8B 35 66 DF FF FF movq -0x209a(%rip), %rsi <-- cursor here
5555562E10FA: FF D6 callq *%rsi
5555562E10FC: 48 89 EC movq %rbp, %rsp
5555562E10FF: 5D popq %rbp
5555562E1100: C3 retq
5555562E1101: 00 00 addb %al, (%rax)
5555562E1103: 00 00 addb %al, (%rax)
And then we end up in std::sys::unix::stack_overflow::imp::signal_handler
Tagging @bjorn3 as that seems to be the only person known to google to run into segfaults with JIT
@Ivan Chinenov What is the value of rsi? (You can use p $rsi
in your debugger I think)
@bjorn3 0x0000000000000000
.
Where is cl_print
defined? It seems like it failed to look it up using dlsym
.
bjorn3 said:
Where is
cl_print
defined? It seems like it failed to look it up usingdlsym
.
In the same binary, https://github.com/JohnDowson/CraneLisp/blob/jit-primary/src/main.rs#L35-L39
There must be a way to define a symbol manually, at least it seems that there was a method for that in SimpleJIT which isn't a thing anymore.
Does cranelift_jit::backend::lookup_with_dlsym
get run if you place a breakpoint on it? And does it run with "cl_print"
as argument at least once?
Ivan Chinenov said:
There must be a way to define a symbol manually, at least it seems that there was a method for that in SimpleJIT which isn't a thing anymore.
The JITBuilder
has a method called .symbol()
.
bjorn3 said:
Does
cranelift_jit::backend::lookup_with_dlsym
get run if you place a breakpoint on it? And does it run with"cl_print"
as argument at least once?
It does in fact run with "cl_print", exactly once
Are you on linux? If so does nm -D /path/to/executable | grep cl_print
show cl_print
for you?
Using .symbol()
may be the easiest way to fix the problem.
Yes, I am on linux. nm -D
does not output "cl_print". Could it be that it is removed by compiler even in debug mode despite being extern "C"
and no_mangle
?
Right, executables don't generally export any symbols other than a specific whitelist inside the linker (or linker script).
Weird then that putting cl_print
into a lib.rs
doesn't do anything either. Using .symbol()
does fix the issue though, although it is not very ergonomic at all
Rust normally statically links libraries. The linker treats object files from statically linked libraries pretty much identical to those from the main executable. Only if you were to make a dynamic library would cl_print
be exported.
Ivan Chinenov has marked this topic as resolved.
I see. Thanks for helping out.
I remember hitting this problem before -- it's an oddity of the way that symbol lookup works at the system level, as @bjorn3 says, and not something that we can really do anything about at the library level (since if the symbol is already invisible from dlsym()
, there's not any other way to look it up).
There is a hack used in Lucet to make all symbols visible to dlsym()
-- in Lucet the symbol lookup is actually done by dlopen()
when loading a precompiled .so, but I think that this might solve the issue here too: this .cargo/config file adds -C link-args=-rdynamic
to the build when the runtime binary (which exports the symbols) is built.
Maybe the above would help?
It would be nice to make finalize_definitions
panic for unresolved symbols again like it used to before I implemented hot swap functionality.
bjorn3 said:
It would be nice to make
finalize_definitions
panic for unresolved symbols again like it used to before I implemented hot swap functionality.
Maybe if it were to be a fallible operation, it should return a result?
I'd rather avoid those sorts of hidden errors
With JITBuilder.symbol()
method I can ever do a wildly unsound thing: define a lambda as a symbol by casting it first to fn pointer then to *const u8
Last updated: Dec 23 2024 at 13:07 UTC