cli_monitor: add a 'gdb' command

This patch adds a 'gdb' command to 'cli_monitor', which makes it possible
to debug an application with GDB.

The command works similarly to the 'start' command, but instead of
starting the subsystem binary directly, an 'init' subsystem gets
started, which then starts 'terminal_crosslink', 'noux', GDB and
'gdb_monitor' (which starts the application binary as its target).

So, for the 'gdb' command to work, these additional components need to
be available, too. 'terminal_crosslink', 'noux', 'gdb_monitor' and the
file 'gdb_command_config' are expected to be ROM modules. The Noux GDB
client needs to get mounted at '/bin' in Noux and the target binaries need
to be available as ROM modules (loaded by 'gdb_monitor') and also mounted
at '/gdb' in Noux (loaded by the GDB client).
Additionally, the source code of the target application can be provided
at '/gdb/src/ in Noux. How the Noux mountings get established can
be configured in the 'gdb_command_config' file. The default configuration
in 'os/src/server/cli_monitor/gdb_command_config' mounts GDB from a tar
archive named 'gdb.tar', the GDB target binaries from a tar archive named
'gdb_target.tar' and the target source code from a tar archive named
'gdb_target-src.tar'.

The patch includes an 'expect' include file (ports/run/noux_gdb.inc)
which provides functions that help to create those tar files:

- 'create_gdb_tar' creates a tar archive for the 'gdb' client
- 'create_binary_tar' creates a tar archive for the target application
- 'create_source_tar' creates a tar archive for the source code of
  the target application
- 'create_binary_and_source_tars' is a convenience wrapper for the previous
  two functions

The patch also includes an example run script
(ports/run/noux_gdb_dynamic.run).

The 'gdb' command supports the following command line options:

- --ram: the initial RAM quota provided to the whole subsystem
         (including the GDB-related components)
- --ram-limit: limit for expanding RAM quota
- --gdb-ram-preserve: the RAM quota that 'gdb_monitor' ahould preserve
                      for itself

Fixes #928.
This commit is contained in:
Christian Prochaska 2013-10-25 22:28:55 +02:00 committed by Norman Feske
parent cc04ffcf42
commit 27aeecf5d1
9 changed files with 964 additions and 74 deletions

View File

@ -0,0 +1,19 @@
/*
* \brief Prefix of the GDB binary
* \author Christian Prochaska
* \date 2013-10-23
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _GDB_PREFIX_H_
#define _GDB_PREFIX_H_
static const char *gdb_prefix = "genode-arm-";
#endif /* _GDB_PREFIX_H_ */

View File

@ -0,0 +1,546 @@
/*
* \brief Gdb command
* \author Norman Feske
* \author Christian Prochaska
* \date 2013-03-18
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _GDB_COMMAND_H_
#define _GDB_COMMAND_H_
/* Genode includes */
#include <base/elf.h>
#include <os/attached_rom_dataspace.h>
#include <rom_session/connection.h>
#include <util/arg_string.h>
#include <util/xml_node.h>
/* local includes */
#include <child_registry.h>
#include <gdb_prefix.h>
#include <format_util.h>
#include <process_arg_registry.h>
#include <ram.h>
class Gdb_command_child : public Child
{
private:
Genode::Signal_context_capability _kill_gdb_sig_cap;
Terminal::Session &_terminal;
bool _kill_requested;
void _kill_gdb()
{
_kill_requested = true;
Genode::Signal_transmitter(_kill_gdb_sig_cap).submit();
}
public:
Gdb_command_child(Ram &ram,
char const *label,
char const *binary,
Genode::Cap_session &cap_session,
Genode::size_t ram_quota,
Genode::size_t ram_limit,
Genode::Signal_context_capability yield_response_sig_cap,
Genode::Signal_context_capability kill_gdb_sig_cap,
Terminal::Session &terminal)
: Child(ram, label, binary, cap_session, ram_quota, ram_limit,
yield_response_sig_cap),
_kill_gdb_sig_cap(kill_gdb_sig_cap),
_terminal(terminal),
_kill_requested(false)
{ }
bool kill_requested() const { return _kill_requested; }
/*
* Check if GDB-related (Noux) session-requests will be successful.
* If not, terminate the subsystem and tell the user about it.
*/
Genode::Service *resolve_session_request(const char *service_name,
const char *args)
{
if (_kill_requested)
return 0;
Genode::Service *service =
Child::resolve_session_request(service_name, args);
if (!service) {
tprintf(_terminal, "Error: GDB subsystem session request for service '%s' failed\n",
service_name);
PDBG("session request failed: service_name = %s, args = %s", service_name, args);
_kill_gdb();
return 0;
}
/* Find out if the session request comes from Noux */
const char *noux_label_suffix = " -> noux";
/* Arg::string() needs two extra bytes */
size_t label_size = strlen(name()) + strlen(noux_label_suffix) + 2;
char noux_label_buf[label_size];
snprintf(noux_label_buf, sizeof(noux_label_buf),
"%s%s", name(), noux_label_suffix);
char label_buf[label_size];
Genode::Arg_string::find_arg(args, "label").string(label_buf,
sizeof(label_buf),
"");
if (strcmp(label_buf, noux_label_buf, label_size) == 0) {
/* Try to create and close the session */
try {
Genode::Session_capability s = service->session(args, Genode::Affinity());
service->close(s);
} catch (...) {
tprintf(_terminal, "Error: GDB subsystem session request for service '%s' failed\n",
service_name);
PDBG("session request failed: service_name = %s, args = %s", service_name, args);
_kill_gdb();
return 0;
}
}
return service;
}
};
struct Gdb_command : Command
{
typedef Genode::Xml_node Xml_node;
typedef Genode::Signal_context_capability Signal_context_capability;
struct Child_configuration_failed {}; /* exception */
Ram &_ram;
Child_registry &_children;
Genode::Cap_session &_cap;
Xml_node _config;
Process_arg_registry &_process_args;
List<Argument> _arguments;
Signal_context_capability _yield_response_sigh_cap;
Signal_context_capability _kill_gdb_sig_cap;
Gdb_command(Ram &ram, Genode::Cap_session &cap, Child_registry &children,
Xml_node config, Process_arg_registry &process_args,
Signal_context_capability yield_response_sigh_cap,
Signal_context_capability kill_gdb_sig_cap)
:
Command("gdb", "create new subsystem with GDB"),
_ram(ram), _children(children), _cap(cap), _config(config),
_process_args(process_args),
_yield_response_sigh_cap(yield_response_sigh_cap),
_kill_gdb_sig_cap(kill_gdb_sig_cap)
{
/* scan config for possible subsystem arguments */
try {
Xml_node node = _config.sub_node("subsystem");
for (;; node = node.next("subsystem")) {
char name[Parameter::NAME_MAX_LEN];
try { node.attribute("name").value(name, sizeof(name)); }
catch (Xml_node::Nonexistent_attribute) {
PWRN("Missing name in '<subsystem>' configuration");
continue;
}
char const *prefix = "config: ";
size_t const prefix_len = strlen(prefix);
char help[Parameter::SHORT_HELP_MAX_LEN + prefix_len];
strncpy(help, prefix, ~0);
try { node.attribute("help").value(help + prefix_len,
sizeof(help) - prefix_len); }
catch (Xml_node::Nonexistent_attribute) {
PWRN("Missing help in '<subsystem>' configuration");
continue;
}
_arguments.insert(new Argument(name, help));
}
} catch (Xml_node::Nonexistent_sub_node) { /* end of list */ }
add_parameter(new Parameter("--ram", Parameter::NUMBER, "initial RAM quota"));
add_parameter(new Parameter("--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota"));
add_parameter(new Parameter("--gdb-ram-preserve", Parameter::NUMBER,
"RAM quota which GDB monitor should preserve for itself (default: 5M)"));
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
}
/**
* Lookup subsystem in config
*/
Xml_node _subsystem_node(char const *name)
{
Xml_node node = _config.sub_node("subsystem");
for (;; node = node.next("subsystem")) {
if (node.attribute("name").has_value(name))
return node;
}
}
/**
* Generate the config node for the GDB subsystem
*/
Xml_node _gdb_config_node(const char *binary_name,
const char *target_config_addr,
const size_t target_config_size,
Genode::Number_of_bytes gdb_ram_preserve,
Terminal::Session &terminal)
{
/* local exception types */
struct Config_buffer_overflow { };
struct Binary_elf_check_failed { };
try {
Genode::Attached_rom_dataspace gdb_command_config_ds("gdb_command_config");
enum { CONFIG_BUF_SIZE = 4*1024 };
static char config_buf[CONFIG_BUF_SIZE];
int config_bytes_written = 0;
/*
* Copy the first part of the config file template
*/
Xml_node init_config_node(gdb_command_config_ds.local_addr<const char>(),
gdb_command_config_ds.size());
Xml_node noux_node = init_config_node.sub_node("start");
for (;; noux_node = noux_node.next("start"))
if (noux_node.attribute("name").has_value("noux"))
break;
Xml_node noux_config_node = noux_node.sub_node("config");
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
size_t bytes_to_copy = (Genode::addr_t)noux_config_node.content_addr() -
(Genode::addr_t)init_config_node.addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
init_config_node.addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
/*
* Create the GDB arguments for breaking in 'main()'
*/
enum { GDB_MAIN_BREAKPOINT_ARGS_BUF_SIZE = 768 };
static char gdb_main_breakpoint_args_buf[GDB_MAIN_BREAKPOINT_ARGS_BUF_SIZE];
try {
Genode::Attached_rom_dataspace binary_rom_ds(binary_name);
Genode::Elf_binary elf_binary((Genode::addr_t)binary_rom_ds.local_addr<void>());
if (elf_binary.is_dynamically_linked()) {
snprintf(gdb_main_breakpoint_args_buf,
sizeof(gdb_main_breakpoint_args_buf),
"<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/ld.lib.so\" />\n \
<arg value=\"-ex\" /><arg value=\"b call_main\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 1\" />\n \
<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/%s\" />\n \
<arg value=\"-ex\" /><arg value=\"b main\" />\n \
<arg value=\"-ex\" /><arg value=\"set solib-search-path /gdb\" />\n \
<arg value=\"-ex\" /><arg value=\"sharedlibrary\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 2\" />\n",
binary_name);
} else {
snprintf(gdb_main_breakpoint_args_buf,
sizeof(gdb_main_breakpoint_args_buf),
"<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/%s\" />\n \
<arg value=\"-ex\" /><arg value=\"b main\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 1\" />\n",
binary_name);
}
} catch (...) {
throw Binary_elf_check_failed();
}
/*
* Insert the '<start>' node for 'noux'
*/
config_bytes_written += snprintf(&config_buf[config_bytes_written],
sizeof(config_buf) - config_bytes_written, "\n \
<start name=\"/bin/%sgdb\">\n \
<arg value=\"/gdb/%s\"/>\n \
<arg value=\"-ex\" /><arg value=\"set interactive-mode off\" />\n \
<arg value=\"-ex\" /><arg value=\"directory /gdb/src\" />\n \
<arg value=\"-ex\" /><arg value=\"target remote /dev/gdb\" />\n \
%s \
<arg value=\"-ex\" /><arg value=\"set interactive-mode auto\" />\n \
</start>",
gdb_prefix,
binary_name,
gdb_main_breakpoint_args_buf);
/*
* Copy the second part of the config file template
*/
Xml_node gdb_monitor_node = noux_node.next("start");
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
bytes_to_copy = (Genode::addr_t)gdb_monitor_node.content_addr() -
(Genode::addr_t)noux_config_node.content_addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
noux_config_node.content_addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
/*
* Create a zero-terminated string for the GDB target config node
*/
char target_config[target_config_size + 1];
if (target_config_addr)
Genode::strncpy(target_config, target_config_addr, sizeof(target_config));
else
target_config[0] = '\0';
/*
* Insert the '<config>' node for 'gdb_monitor'
*/
config_bytes_written += Genode::snprintf(&config_buf[config_bytes_written],
(sizeof(config_buf) - config_bytes_written), "\n \
<config>\n \
<target name=\"%s\">\n \
%s \
</target>\n \
<preserve name=\"RAM\" quantum=\"%zu\"/>\n \
</config> \
",
binary_name,
target_config,
(size_t)gdb_ram_preserve);
/*
* Copy the third (and final) part of the config file template
*/
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
bytes_to_copy = ((Genode::addr_t)init_config_node.addr() +
init_config_node.size()) -
(Genode::addr_t)gdb_monitor_node.content_addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
gdb_monitor_node.content_addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
return Xml_node(config_buf, config_bytes_written);
} catch (Config_buffer_overflow) {
tprintf(terminal, "Error: the buffer for the generated GDB "
"subsystem configuration is too small.\n");
throw Child_configuration_failed();
} catch (Binary_elf_check_failed) {
tprintf(terminal, "Error: could not determine link type of the "
"GDB target binary.\n");
throw Child_configuration_failed();
}
}
void execute(Command_line &cmd, Terminal::Session &terminal)
{
/* check if the GDB-related ROM modules are available */
try {
Genode::Rom_connection gdb_command_config("gdb_command_config");
Genode::Rom_connection terminal_crosslink("terminal_crosslink");
Genode::Rom_connection noux("noux");
Genode::Rom_connection gdb_monitor("gdb_monitor");
} catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: The 'gdb' command needs the following ROM "
"modules (of which some are currently missing): "
"gdb_command_config, terminal_crosslink, noux, ",
"gdb_monitor\n");
return;
}
Genode::Number_of_bytes ram = 0;
Genode::Number_of_bytes ram_limit = 0;
Genode::Number_of_bytes gdb_ram_preserve = 10*1024*1024;
char name[128];
name[0] = 0;
if (cmd.argument(0, name, sizeof(name)) == false) {
tprintf(terminal, "Error: no configuration name specified\n");
return;
}
char buf[128];
if (cmd.argument(1, buf, sizeof(buf))) {
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
return;
}
/* check if a configuration for the subsystem exists */
try { _subsystem_node(name); }
catch (Xml_node::Nonexistent_sub_node) {
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
return;
}
/* read default RAM quota from config */
try {
Xml_node rsc = _subsystem_node(name).sub_node("resource");
for (;; rsc = rsc.next("resource")) {
if (rsc.attribute("name").has_value("RAM")) {
rsc.attribute("quantum").value(&ram);
if (rsc.has_attribute("limit"))
rsc.attribute("limit").value(&ram_limit);
break;
}
}
} catch (...) { }
cmd.parameter("--ram", ram);
cmd.parameter("--ram-limit", ram_limit);
cmd.parameter("--gdb-ram-preserve", gdb_ram_preserve);
/* account for cli_monitor local metadata */
size_t preserve_ram = 100*1024;
if ((ram + preserve_ram) > Genode::env()->ram_session()->avail()) {
tprintf(terminal, "Error: RAM quota exceeds available quota\n");
return;
}
bool const verbose = cmd.parameter_exists("--verbose");
/*
* Determine binary name
*
* Use subsystem name by default, override with '<binary>' declaration.
*/
char binary_name[128];
strncpy(binary_name, name, sizeof(binary_name));
try {
Xml_node bin = _subsystem_node(name).sub_node("binary");
bin.attribute("name").value(binary_name, sizeof(binary_name));
} catch (...) { }
/* generate unique child name */
char label[Child_registry::CHILD_NAME_MAX_LEN];
_children.unique_child_name(name, label, sizeof(label));
tprintf(terminal, "starting new subsystem '%s'\n", label);
if (verbose) {
tprintf(terminal, " RAM quota: ");
tprint_bytes(terminal, ram);
tprintf(terminal,"\n");
if (ram_limit) {
tprintf(terminal, " RAM limit: ");
tprint_bytes(terminal, ram_limit);
tprintf(terminal,"\n");
}
tprintf(terminal, " binary: %s\n", binary_name);
}
Child *child = 0;
try {
/* create child configuration */
const char *target_config_addr = 0;
size_t target_config_size = 0;
try {
Xml_node target_config_node = _subsystem_node(name).sub_node("config");
target_config_addr = target_config_node.addr();
target_config_size = target_config_node.size();
} catch (...) { }
Xml_node config_node = _gdb_config_node(binary_name,
target_config_addr,
target_config_size,
gdb_ram_preserve,
terminal);
/* create child */
child = new (Genode::env()->heap())
Gdb_command_child(_ram, label, "init", _cap, ram, ram_limit,
_yield_response_sigh_cap, _kill_gdb_sig_cap,
terminal);
/* configure child */
try {
child->configure(config_node.addr(), config_node.size());
if (verbose)
tprintf(terminal, " config: inline\n");
} catch (...) {
if (verbose)
tprintf(terminal, " config: none\n");
}
}
catch (Child_configuration_failed) {
return;
}
catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
binary_name);
return;
}
catch (Child::Quota_exceeded) {
tprintf(terminal, "Error: insufficient memory, need ");
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
tprintf(terminal, ", have ");
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
tprintf(terminal, "\n");
return;
}
catch (Genode::Allocator::Out_of_memory) {
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
return;
}
_process_args.list.insert(&child->argument);
_children.insert(child);
child->start();
}
List<Argument> &arguments() { return _arguments; }
};
#endif /* _GDB_COMMAND_H_ */

View File

@ -0,0 +1,70 @@
<!-- configuration template for the 'gdb' command -->
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
<service name="Timer"/>
<service name="Terminal"/>
</parent-provides>
<!-- additional start nodes can be inserted here -->
<start name="terminal_gdb">
<binary name="terminal_crosslink"/>
<resource name="RAM" quantum="1M"/>
<provides> <service name="Terminal"/> </provides>
<route>
<any-service><parent/><any-child/></any-service>
</route>
</start>
<start name="noux">
<resource name="RAM" quantum="24M"/>
<route>
<service name="Terminal">
<if-arg key="label" value=""/><parent/>
</service>
<service name="Terminal">
<if-arg key="label" value="noux(terminal_fs)"/><child name="terminal_gdb"/>
</service>
<any-service> <any-child/> <parent/> </any-service>
</route>
<config>
<!-- the GDB <start> node gets generated above this comment -->
<fstab>
<dir name="dev"><terminal name="gdb"/></dir>
<!-- the GDB binary is expected in /bin -->
<tar name="gdb.tar" at="/"/>
<!-- the GDB target binaries are expected in /gdb -->
<!-- the GDB target source is expected in /gdb/src -->
<dir name="gdb">
<tar name="gdb_target.tar"/>
<dir name="src"><tar name="gdb_target-src.tar"/></dir>
</dir>
</fstab>
</config>
</start>
<start name="gdb_monitor">
<!-- the <config> node gets generated above this comment -->
<resource name="RAM" quantum="1G"/>
<route>
<service name="Terminal"><child name="terminal_gdb"/></service>
<any-service> <any-child/> <parent/> </any-service>
</route>
</start>
</config>

View File

@ -29,6 +29,7 @@
#include <help_command.h>
#include <yield_command.h>
#include <ram_command.h>
#include <gdb_command.h>
using Genode::Xml_node;
@ -101,6 +102,10 @@ int main(int argc, char **argv)
static Signal_context yield_broadcast_sig_ctx;
static Signal_context resource_avail_sig_ctx;
static Signal_context kill_gdb_sig_ctx;
static Signal_context_capability kill_gdb_sig_cap =
sig_rec.manage(&kill_gdb_sig_ctx);
static Ram ram(ram_preservation_from_config(),
sig_rec.manage(&yield_broadcast_sig_ctx),
sig_rec.manage(&resource_avail_sig_ctx));
@ -109,6 +114,11 @@ int main(int argc, char **argv)
commands.insert(new Help_command);
Kill_command kill_command(children, process_args);
commands.insert(&kill_command);
commands.insert(new Gdb_command(ram, cap, children,
Genode::config()->xml_node(),
process_args,
yield_response_sig_cap,
kill_gdb_sig_cap));
commands.insert(new Start_command(ram, cap, children,
Genode::config()->xml_node(),
process_args,
@ -164,6 +174,22 @@ int main(int argc, char **argv)
child->yield(amount, true);
}
if (signal.context() == &kill_gdb_sig_ctx) {
for (Child *child = children.first(); child; child = child->next()) {
Gdb_command_child *gdb_command_child =
dynamic_cast<Gdb_command_child*>(child);
if (gdb_command_child && gdb_command_child->kill_requested()) {
tprintf(terminal, "Destroying GDB subsystem after an error occured.\n");
process_args.list.remove(&gdb_command_child->argument);
children.remove(gdb_command_child);
Genode::destroy(Genode::env()->heap(), gdb_command_child);
line_editor.reset();
break;
}
}
continue;
}
if (!line_editor.is_complete())
continue;

View File

@ -2,3 +2,11 @@ TARGET = cli_monitor
SRC_CC = main.cc
LIBS = base cli_monitor config
INC_DIR += $(PRG_DIR)
ifeq ($(findstring arm, $(SPECS)), arm)
INC_DIR += $(PRG_DIR)/arm
else
ifeq ($(findstring x86, $(SPECS)), x86)
INC_DIR += $(PRG_DIR)/x86
endif
endif

View File

@ -0,0 +1,19 @@
/*
* \brief Prefix of the GDB binary
* \author Christian Prochaska
* \date 2013-10-23
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _GDB_PREFIX_H_
#define _GDB_PREFIX_H_
static const char *gdb_prefix = "genode-x86-";
#endif /* _GDB_PREFIX_H_ */

69
ports/run/noux_gdb.inc Normal file
View File

@ -0,0 +1,69 @@
#
# Utility functions for run scripts using Noux GDB
#
#
# Return the name of the Noux GDB package for the configured platform
#
proc noux_gdb_pkg_name { } {
if {[have_spec arm]} {
return "gdb_arm"
} elseif {[have_spec x86]} {
return "gdb_x86"
}
}
#
# Create a tar archive for GDB (stripped)
#
proc create_gdb_tar { } {
exec sh -c "find bin/[noux_gdb_pkg_name]/ -type f | (xargs [cross_dev_prefix]strip || true) 2>/dev/null"
exec tar cfhv bin/gdb.tar -C bin/[noux_gdb_pkg_name] .
}
#
# Create a tar archive for a Noux application and its shared libraries (unstripped)
#
proc create_binary_tar { application_name application_binaries } {
foreach application_binary ${application_binaries} {
exec tar ufv bin/${application_name}.tar -h -C bin ${application_binary}
}
}
#
# Create a tar archive for the source code of a Noux application and its shared
# libraries.
#
# Currently, directories need to have their own tar records
#
proc create_source_tar { application_name application_binaries } {
exec mkdir -p bin/${application_name}-src
foreach application_binary $application_binaries {
set source_files [ exec [cross_dev_prefix]objdump -dl bin/${application_binary} | grep "^/.*:.*" | sed -e "s/:.*//" | uniq ]
foreach source_file ${source_files} {
# resolve '..' to avoid problems with 'tar' with parts like '/a/b/../'
# where '/a' exists, but '/a/b' does not
set source_file [file normalize ${source_file}]
if [file exists ${source_file}] {
set dirname [ exec dirname ${source_file}]
exec mkdir -p bin/${application_name}-src${dirname}
exec ln -sf ${source_file} bin/${application_name}-src${source_file}
}
}
}
exec tar chf bin/${application_name}-src.tar -C bin/${application_name}-src .
}
#
# Create tar archives for binaries and source code of a Noux application
#
proc create_binary_and_source_tars { application_name application_binaries } {
create_binary_tar ${application_name} ${application_binaries}
create_source_tar ${application_name} ${application_binaries}
}

View File

@ -3,13 +3,13 @@ if {![have_spec foc] || ![have_spec 32bit]} {
exit 0
}
source ${genode_dir}/ports/run/noux_gdb.inc
if {[have_spec arm]} {
set gdb "gdb_arm"
set tool_prefix "genode-arm-"
}
if {[have_spec x86]} {
set gdb "gdb_x86"
set tool_prefix "genode-x86-"
}
@ -17,57 +17,34 @@ if {[have_spec x86]} {
# Uncomment the following line when working on the GDB source code. Otherwise,
# the package may get recompiled, yet it does not get reinstalled into 'bin/'.
#
#exec rm -rf noux-pkg/$gdb/ bin/$gdb/
#exec rm -rf noux-pkg/[noux_gdb_pkg_name]/ bin/[noux_gdb_pkg_name]/
set build_components {
core init drivers/timer noux lib/libc_noux
drivers/framebuffer drivers/pci drivers/input drivers/usb
server/terminal server/terminal_crosslink
server/ram_fs app/gdb_monitor
app/gdb_monitor
test/gdb_monitor
}
lappend build_components noux-pkg/$gdb
lappend build_components noux-pkg/[noux_gdb_pkg_name]
# the application to be debugged with GDB
lappend build_components test/gdb_monitor
set gdb_target_binary_name test-gdb_monitor
build $build_components
# tar archive for GDB
exec sh -c "find bin/$gdb/ -type f | (xargs [cross_dev_prefix]strip || true) 2>/dev/null"
exec tar cfhv bin/gdb.tar -C bin/$gdb .
# names of the binaries needed for the GDB monitor test
set test_binaries {
set gdb_target_binaries {
test-gdb_monitor
ld.lib.so
libc.lib.so
libc_log.lib.so
}
lappend gdb_target_binaries ${gdb_target_binary_name}
# tar archive for the unstripped binaries of the GDB monitor test
foreach test_binary $test_binaries {
exec tar ufv bin/test-gdb_monitor.tar -h -C bin $test_binary
}
# tar archive for the source code of the GDB monitor test
# currently, directories need to have their own tar records
exec mkdir -p bin/test-gdb_monitor-src
foreach test_binary $test_binaries {
set source_files [ exec [cross_dev_prefix]objdump -dl bin/$test_binary | grep "^/.*:.*" | sed -e "s/:.*//" | uniq ]
foreach source_file $source_files {
# resolve '..' to avoid problems with 'tar' with parts like '/a/b/../'
# where '/a' exists, but '/a/b' does not
set source_file [file normalize $source_file]
if [file exists $source_file] {
set dirname [ exec dirname $source_file]
exec mkdir -p bin/test-gdb_monitor-src$dirname
exec ln -sf $source_file bin/test-gdb_monitor-src$source_file
}
}
}
exec tar chf bin/test-gdb_monitor-src.tar -C bin/test-gdb_monitor-src .
create_gdb_tar
create_binary_and_source_tars ${gdb_target_binary_name} ${gdb_target_binaries}
create_boot_directory
@ -149,39 +126,13 @@ append config {
<service name="Terminal"><child name="terminal_gdb"/></service>
<any-service><parent/><any-child/></any-service>
</route>
<config>
<target name="test-gdb_monitor"/>
<config> }
append config "
<target name=\"${gdb_target_binary_name}\"/> "
append config {
<preserve name="RAM" quantum="5M"/>
</config>
</start>
<start name="ram_fs">
<resource name="RAM" quantum="10M"/>
<provides><service name="File_system"/></provides>
<config>
<content>
<dir name="gdb">
<inline name="commands">
set interactive-mode off
directory /gdb/src
target remote /dev/gdb
symbol-file /gdb/ld.lib.so
b call_main
c
delete 1
symbol-file /gdb/test-gdb_monitor
b main
set solib-search-path /gdb
sharedlibrary
c
delete 2
set interactive-mode auto
</inline>
</dir>
</content>
<!-- constrain sessions according to their labels -->
<policy label="noux -> gdb" root="/gdb" />
</config>
</start>
<start name="noux">
<resource name="RAM" quantum="1G"/>
<route>
@ -197,17 +148,34 @@ append config {
<fstab>
<tar name="gdb.tar" at="/"/>
<dir name="dev"><terminal name="gdb"/></dir>
<dir name="gdb">
<tar name="test-gdb_monitor.tar"/>
<fs label="gdb"/>
<dir name="src"> <tar name="test-gdb_monitor-src.tar"/> </dir>
<dir name="gdb"> }
append config "
<tar name=\"${gdb_target_binary_name}.tar\"/>
<dir name=\"src\"> <tar name=\"${gdb_target_binary_name}-src.tar\"/> </dir> "
append config {
</dir>
</fstab> }
append config "
<start name=\"/bin/${tool_prefix}gdb\"> "
<start name=\"/bin/${tool_prefix}gdb\">
<arg value=\"/bin/${tool_prefix}gdb\"/>
<arg value=\"/gdb/${gdb_target_binary_name}\"/> "
append config {
<arg value="/gdb/test-gdb_monitor"/>
<arg value="-x" /><arg value="/gdb/commands" />
<arg value="-ex" /><arg value="set interactive-mode off" />
<arg value="-ex" /><arg value="directory /gdb/src" />
<arg value="-ex" /><arg value="target remote /dev/gdb" />
<arg value="-ex" /><arg value="symbol-file /gdb/ld.lib.so" />
<arg value="-ex" /><arg value="b call_main" />
<arg value="-ex" /><arg value="c" />
<arg value="-ex" /><arg value="delete 1" /> }
append config "
<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/${gdb_target_binary_name}\" /> "
append config {
<arg value="-ex" /><arg value="b main" />
<arg value="-ex" /><arg value="set solib-search-path /gdb" />
<arg value="-ex" /><arg value="sharedlibrary" />
<arg value="-ex" /><arg value="c" />
<arg value="-ex" /><arg value="delete 2" />
<arg value="-ex" /><arg value="set interactive-mode auto" />
</start>
</config>
</start>
@ -226,7 +194,7 @@ set boot_modules {
core init timer ld.lib.so noux terminal terminal_crosslink
libc.lib.so libm.lib.so libc_noux.lib.so ncurses.lib.so expat.lib.so
libc_lock_pipe.lib.so libc_log.lib.so libc_terminal.lib.so
ram_fs gdb_monitor test-gdb_monitor
gdb_monitor test-gdb_monitor
gdb.tar test-gdb_monitor.tar test-gdb_monitor-src.tar
}

View File

@ -0,0 +1,165 @@
if {![have_spec foc] || ![have_spec 32bit]} {
puts "\nThe Noux GDB scenario is supported on 32-bit Fiasco.OC only\n"
exit 0
}
source ${genode_dir}/ports/run/noux_gdb.inc
#
# Uncomment the following line when working on the GDB source code. Otherwise,
# the package may get recompiled, yet it does not get reinstalled into 'bin/'.
#
#exec rm -rf noux-pkg/[noux_gdb_pkg_name]/ bin/[noux_gdb_pkg_name]/
set build_components {
core init drivers/timer noux lib/libc_noux
drivers/uart
server/terminal_mux server/terminal_crosslink
server/terminal_log
app/cli_monitor
app/gdb_monitor
}
lappend build_components noux-pkg/[noux_gdb_pkg_name]
lappend build_components test/gdb_monitor
set gdb_target_binary_name test-gdb_monitor
build $build_components
# names of the binaries needed for the GDB monitor test
set gdb_target_binaries {
ld.lib.so
libc.lib.so
libc_log.lib.so
}
lappend gdb_target_binaries ${gdb_target_binary_name}
create_gdb_tar
create_binary_and_source_tars "gdb_target" ${gdb_target_binaries}
create_boot_directory
append config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="LOG"/>
<service name="CAP"/>
<service name="RAM"/>
<service name="RM"/>
<service name="CPU"/>
<service name="PD"/>
<service name="IRQ"/>
<service name="IO_PORT"/>
<service name="IO_MEM"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <any-child/> <parent/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
<route> <any-service> <parent/> </any-service> </route>
</start>
<start name="uart_drv">}
# use kernel debugger as UART on Fiasco.OC
append_if [have_spec foc] config {
<binary name="kdb_uart_drv"/>}
append config {
<resource name="RAM" quantum="1M"/>
<provides>
<service name="Uart"/>
<service name="Terminal"/>
</provides>
<config> }
# on Fiasco.OC the kdb_uart_drv is always UART 0
append_if [have_spec foc] config {
<policy label="terminal_mux" uart="0" detect_size="yes"/> }
# on all other kernels, direct terminal_mux to UART 1 (Qemu stdio, see below)
append_if [expr ![have_spec foc]] config {
<policy label="terminal_mux" uart="1" detect_size="yes"/> }
append config {
</config>
<route> <any-service> <parent/> <any-child/> </any-service> </route>
</start>
<start name="terminal_mux">
<resource name="RAM" quantum="3M"/>
<provides><service name="Terminal"/></provides>
<route>
<service name="Terminal"><child name="uart_drv"/></service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<keyboard layout="de"/>
</config>
</start>
<start name="terminal_log">
<resource name="RAM" quantum="2M"/>
<provides>
<service name="LOG"/>
</provides>
<route>
<any-service><child name="terminal_mux"/> <any-child/> <parent/> </any-service>
</route>
</start>
<start name="cli_monitor">
<resource name="RAM" quantum="3G"/>
<config>
<subsystem name="gdb_test" help="GDB test application"> }
append config "
<binary name=\"${gdb_target_binary_name}\"/> "
append config {
<resource name="RAM" quantum="50M"/>
</subsystem>
</config>
<route>
<service name="LOG"><child name="terminal_log"/></service>
<service name="Terminal"><child name="terminal_mux"/></service>
<any-service><parent/><any-child/></any-service>
</route>
</start>
</config>
}
install_config $config
#
# Boot modules
#
exec cp ${genode_dir}/os/src/app/cli_monitor/gdb_command_config bin
# generic modules
set boot_modules {
core init timer ld.lib.so noux terminal_mux terminal_crosslink
libc.lib.so libm.lib.so libc_noux.lib.so ncurses.lib.so expat.lib.so
libc_lock_pipe.lib.so libc_log.lib.so libc_terminal.lib.so
cli_monitor gdb_monitor terminal_log gdb.tar
gdb_command_config
gdb_target.tar
gdb_target-src.tar
}
lappend boot_modules ${gdb_target_binary_name}
# platform-specific modules
lappend_if [expr ![have_spec foc]] boot_modules uart_drv
lappend_if [have_spec foc] boot_modules kdb_uart_drv
set fiasco_serial_esc_arg ""
build_boot_image $boot_modules
append qemu_args " -nographic "
run_genode_until forever
exec rm bin/gdb.tar