Skip to content

Headless mode never calls OpenProgram → analysis tables (funcs, names, instructions…) return 0 rows #1

Description

@ogeidr

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.cpprun_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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions