From 27aeecf5d11a4baad65eb6fc5454c431d5323b99 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Fri, 25 Oct 2013 22:28:55 +0200 Subject: [PATCH] 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. --- os/src/app/cli_monitor/arm/gdb_prefix.h | 19 + os/src/app/cli_monitor/gdb_command.h | 546 ++++++++++++++++++++++ os/src/app/cli_monitor/gdb_command_config | 70 +++ os/src/app/cli_monitor/main.cc | 26 ++ os/src/app/cli_monitor/target.mk | 8 + os/src/app/cli_monitor/x86/gdb_prefix.h | 19 + ports/run/noux_gdb.inc | 69 +++ ports/run/noux_gdb.run | 116 ++--- ports/run/noux_gdb_dynamic.run | 165 +++++++ 9 files changed, 964 insertions(+), 74 deletions(-) create mode 100644 os/src/app/cli_monitor/arm/gdb_prefix.h create mode 100644 os/src/app/cli_monitor/gdb_command.h create mode 100644 os/src/app/cli_monitor/gdb_command_config create mode 100644 os/src/app/cli_monitor/x86/gdb_prefix.h create mode 100644 ports/run/noux_gdb.inc create mode 100644 ports/run/noux_gdb_dynamic.run diff --git a/os/src/app/cli_monitor/arm/gdb_prefix.h b/os/src/app/cli_monitor/arm/gdb_prefix.h new file mode 100644 index 000000000..796bdb451 --- /dev/null +++ b/os/src/app/cli_monitor/arm/gdb_prefix.h @@ -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_ */ diff --git a/os/src/app/cli_monitor/gdb_command.h b/os/src/app/cli_monitor/gdb_command.h new file mode 100644 index 000000000..699a6320f --- /dev/null +++ b/os/src/app/cli_monitor/gdb_command.h @@ -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 +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include +#include +#include + +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 _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 '' 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 '' 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(), + 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()); + + if (elf_binary.is_dynamically_linked()) { + + snprintf(gdb_main_breakpoint_args_buf, + sizeof(gdb_main_breakpoint_args_buf), + "\n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n", + binary_name); + } else { + + snprintf(gdb_main_breakpoint_args_buf, + sizeof(gdb_main_breakpoint_args_buf), + "\n \ + \n \ + \n \ + \n", + binary_name); + } + } catch (...) { + throw Binary_elf_check_failed(); + } + + /* + * Insert the '' node for 'noux' + */ + + config_bytes_written += snprintf(&config_buf[config_bytes_written], + sizeof(config_buf) - config_bytes_written, "\n \ + \n \ + \n \ + \n \ + \n \ + \n \ + %s \ + \n \ + ", + 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 '' node for 'gdb_monitor' + */ + + config_bytes_written += Genode::snprintf(&config_buf[config_bytes_written], + (sizeof(config_buf) - config_bytes_written), "\n \ + \n \ + \n \ + %s \ + \n \ + \n \ + \ + ", + 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 '' 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 &arguments() { return _arguments; } +}; + +#endif /* _GDB_COMMAND_H_ */ diff --git a/os/src/app/cli_monitor/gdb_command_config b/os/src/app/cli_monitor/gdb_command_config new file mode 100644 index 000000000..5c912d556 --- /dev/null +++ b/os/src/app/cli_monitor/gdb_command_config @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/os/src/app/cli_monitor/main.cc b/os/src/app/cli_monitor/main.cc index 3e9843ea9..11012a2ea 100644 --- a/os/src/app/cli_monitor/main.cc +++ b/os/src/app/cli_monitor/main.cc @@ -29,6 +29,7 @@ #include #include #include +#include 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(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; diff --git a/os/src/app/cli_monitor/target.mk b/os/src/app/cli_monitor/target.mk index e6fa14057..1f3fe5304 100644 --- a/os/src/app/cli_monitor/target.mk +++ b/os/src/app/cli_monitor/target.mk @@ -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 diff --git a/os/src/app/cli_monitor/x86/gdb_prefix.h b/os/src/app/cli_monitor/x86/gdb_prefix.h new file mode 100644 index 000000000..decbd52d7 --- /dev/null +++ b/os/src/app/cli_monitor/x86/gdb_prefix.h @@ -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_ */ diff --git a/ports/run/noux_gdb.inc b/ports/run/noux_gdb.inc new file mode 100644 index 000000000..211c55c4f --- /dev/null +++ b/ports/run/noux_gdb.inc @@ -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} +} + diff --git a/ports/run/noux_gdb.run b/ports/run/noux_gdb.run index 329d5083a..f4c6d1df0 100644 --- a/ports/run/noux_gdb.run +++ b/ports/run/noux_gdb.run @@ -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 { - - + } +append config " + " +append config { - - - - - - - - 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 - - - - - - - @@ -197,17 +148,34 @@ append config { - - - - + } +append config " + + " +append config { } append config " - " + + + " append config { - - + + + + + + + } +append config " + " +append config { + + + + + + @@ -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 } diff --git a/ports/run/noux_gdb_dynamic.run b/ports/run/noux_gdb_dynamic.run new file mode 100644 index 000000000..5f10020e9 --- /dev/null +++ b/ports/run/noux_gdb_dynamic.run @@ -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 { + + + + + + + + + + + + + + + + + + + + + + + + + } + +# use kernel debugger as UART on Fiasco.OC +append_if [have_spec foc] config { + } + +append config { + + + + + + } + +# on Fiasco.OC the kdb_uart_drv is always UART 0 +append_if [have_spec foc] config { + } + +# on all other kernels, direct terminal_mux to UART 1 (Qemu stdio, see below) +append_if [expr ![have_spec foc]] config { + } + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + } +append config " + " +append 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