Summary
In headless mode (--binary/--project/--project-name -q …, no --url), every analysis table returns 0 rows and db_info reports program_name=active-program with empty language_id/md5/image_base, even though Ghidra imports and analyzes the program successfully and the host binds it (the server log prints LIBGHIDRA_HEADLESS_READY … program=<name>).
Environment
- Ghidra 12.0.4 (also reproduced on 12.1), JDK 21
- Linux aarch64 (the bug is platform-independent)
- Built following
install-prompt.md
Repro
ghidrasql --binary /bin/ls --project /tmp/p --project-name boot --analyze -q "SELECT COUNT(*) FROM funcs"
# -> 0
ghidrasql --binary /bin/ls --project /tmp/p --project-name boot -q "SELECT * FROM db_info"
# -> program_name = active-program, language_id / md5 empty, image_base 0
Passing --program /ls or --initial-program /ls does not help.
Root cause
src/cli/main.cpp — run_headless_live_query_local() and run_headless_live_server() hardcode:
source_opts.auto_open_program = false;
and never set source_opts.program_path. The client therefore never issues OpenProgram against the host it just launched, so the queries in src/lib/src/source_libghidra.cpp (gated on opened_program_, set only via ensure_session_open_locked() → OpenProgram) see no active program. db_info's program_name falls back to the "active-program" default (source_libghidra.cpp:1112).
The --url connect path (around main.cpp:1014) does set program_path + auto_open_program and works correctly — so headless mode is the outlier.
Suggested fix
In both headless paths, mirror the connect path: set auto_open_program and program_path from --program/--initial-program (selected_program_arg(args)), defaulting program_path to the imported binary's project path so the documented --binary … -q form works out of the box. For example:
std::string selected_program = selected_program_arg(args);
if (selected_program.empty() && !args.binary_paths.empty()) {
std::string base = args.binary_paths.front();
auto slash = base.find_last_of('/');
if (slash != std::string::npos) base = base.substr(slash + 1);
selected_program = "/" + base;
}
source_opts.auto_open_program = !selected_program.empty();
source_opts.program_path = selected_program;
source_opts.project_path = args.project;
source_opts.project_name = args.project_name;
With this change db_info populates correctly and analysis tables return rows.
Summary
In headless mode (
--binary/--project/--project-name -q …, no--url), every analysis table returns 0 rows anddb_inforeportsprogram_name=active-programwith emptylanguage_id/md5/image_base, even though Ghidra imports and analyzes the program successfully and the host binds it (the server log printsLIBGHIDRA_HEADLESS_READY … program=<name>).Environment
install-prompt.mdRepro
Passing
--program /lsor--initial-program /lsdoes not help.Root cause
src/cli/main.cpp—run_headless_live_query_local()andrun_headless_live_server()hardcode:source_opts.auto_open_program = false;and never set
source_opts.program_path. The client therefore never issuesOpenProgramagainst the host it just launched, so the queries insrc/lib/src/source_libghidra.cpp(gated onopened_program_, set only viaensure_session_open_locked()→OpenProgram) see no active program.db_info'sprogram_namefalls back to the"active-program"default (source_libghidra.cpp:1112).The
--urlconnect path (aroundmain.cpp:1014) does setprogram_path+auto_open_programand works correctly — so headless mode is the outlier.Suggested fix
In both headless paths, mirror the connect path: set
auto_open_programandprogram_pathfrom--program/--initial-program(selected_program_arg(args)), defaultingprogram_pathto the imported binary's project path so the documented--binary … -qform works out of the box. For example:With this change
db_infopopulates correctly and analysis tables return rows.