ports: remove old GDB monitor

Fixes #5067
This commit is contained in:
Christian Prochaska 2023-12-04 06:38:00 +01:00 committed by Norman Feske
parent 9a049789de
commit ff5a474e74
51 changed files with 0 additions and 5814 deletions

View File

@ -1,455 +0,0 @@
======================================
User-level debugging on Genode via GDB
======================================
Norman Feske
Abstract
########
A convenient solution for debugging individual applications is a feature that
is repeatedly asked for by Genode developers. In the past, the answer to this
question was rather unsatisfying. There existed a multitude of approaches but
none was both simple to use and powerful. With GDB monitor, this has changed.
Using this component, debugging an individual Genode application over a
remote GDB connection has become possible. This way, most debugging facilities
valued and expected from user-level debuggers on commodity operating systems
become available to Genode developers.
Traditional approaches
######################
There are several ways of debugging user-level programs on Genode. Probably the
most prominent approach is spilling debug messages throughout the code of
interest. The colored 'printf' macros 'PDBG', 'PINF', 'PWRN', and 'PERR' become
handy in such situations. By default, those messages are targeting core's LOG
service. Hence, each debug message appears nicely tagged with the originating
process on the kernel debug console. Even though this approach looks like from
the stone age, it remains to be popular because it is so intuitive to use.
For debugging the interaction between different processes, however, the classical
'printf' methodology becomes inefficient. Here is where platform-specific
debugging facilities enter the picture. Most L4 kernels come with built-in
kernel debuggers that allow the inspection of kernel objects such as threads
and address spaces. This way, we get a global view on the system from the
kernel's perspective. For example, the mapping of virtual memory to
physical pages can be revealed, the communication relationships between
threads become visible, and the ready queue of the scheduler can be
observed. To a certain extent, kernel debuggers had been complemented with
useful facilities for debugging user-level programs. For example, the Fiasco
kernel debugger comes with a convenient backtrace function that parses the
call stack of a given thread. Using the addresses printed in the backtrace,
the corresponding code can be matched against the output of the 'objdump'
utility that comes with the GNU binutils. Among the kernel debuggers of
Genode's supported base platforms, the variants of L4/Fiasco and respectively
Fiasco.OC stand out. We often find ourself switching to one of these kernel
platforms when we hit a hard debugging problem for the sole reason that
the kernel debugger is so powerful.
However, with complex applications, the kernel debugger becomes awkward to
use. For example, if an application uses shared libraries, the kernel
has no interpretation of them. Addresses that appear as the backtrace of the
stack must be manually matched against the loading addresses of the individual
shared libraries, the 'objdump' must be used with the offset of the return
address from the shared-library's base address. Saying that this process
is inconvenient would be a blatant understatement. Of course, sophisticated
features like source-level debugging and single-stepping of applications is
completely out of the scope of a kernel debugger.
For problems that call for source-level debugging and single-stepping, however,
we have found Qemu's GDB stub extremely useful. This stub can be used to attach
GDB to a virtual machine emulated by Qemu. By manually loading symbols into
GDB, this approach can be used to perform source-level debugging to a certain
degree. However, there are a number of restrictions attached to this solution.
First, Qemu is not aware of any abstractions of the running operating system.
So if the kernel decides to preempt the current thread and switch to another,
the single-stepping session comes to a surprising end. Also, Qemu is not aware
of the different address spaces. Hence, a breakpoint triggers as soon as the
program counter reaches the breakpoint address regardless of the process. If
multiple applications use the same virtual addresses (which is usually the
case), we get an aliasing problem. This problem can be mitigated by linking
each application to a different virtual-address range. However, this effort is
hardly recommendable as a general solution. Still, Qemu's GDB stub can save
the soul of a developer who has to deal with problems in the category of
low-level C++ exception handling.
For debugging higher-level application code and protocols, using GDB on Linux
is a viable choice if the application scenario can executed on the 'base-linux'
platform. For many problems on Genode, this is apparently the case because most
higher-level code is platform-independent. On the Linux base platform, each
Genode process runs as an individual Linux process. Consequently, GDB can be
attached to such a process using the 'gdb -p' command. To synchronize the point
in time of attaching GDB, the utility function 'wait_for_continue' provided by
the Linux version of Genodes 'env' library can be utilized. In general, this
approach is viable for high-level code. There are even success stories with
debugging the program logic of a Genode device driver on Linux even though no
actual hardware has been present the Linux platform. However, also this
approach has severe limitations (besides being restricted to the 'base-linux'
platform). The most prevalent limitation is the lack of thread debugging. For
debugging normal Linux applications, GDB relies on certain glibc features
(e.g., the way of how threads are managed using the pthread library and the
handling of thread-local storage). Since, Genode programs are executed with no
glibc, GDB lacks this information.
To summarize, there are plentiful ways of debugging programs on Genode. The
fact that Genode supports a range of base platforms opens up a whole range of
possibilities of all base platforms combined. But none of those mechanisms is
ideal for debugging native Genode applications. GDB monitor tries to fill this
gap by enabling GDB to be attached to a Genode process. Once attached, GDB can
be used to debugged the process GDB's full power including source-level
debugging, breakpoints, single-stepping, backtraces, and call-frame inspection.
GDB monitor concept
###################
In the following, the term _target_ refers to the Genode program to debug. The
term _host_ refers to the system where GDB is executed. When using the
normal work flow of Genode's run tool, the host is typically a Linux
system that executes Genode using Qemu.
GDB monitor is a Genode process that sits in-between the _target_ and its
normal parent. As the parent of the target it has full control over all
interactions of the target with the rest of the system. I.e., all session
requests originating from the target including those that normally refer to
core's services are first seen by GDB monitor. GDB monitor, in turn, can decide
whether to forward such a session request to the original parent or to
virtualize the requested service using a local implementation. The latter is
done for all services that GDB monitor needs to inspect the target's address
space and thread state. In particular, GDB monitor provides local
implementations of the 'CPU' and 'RM' (and 'ROM') services. Those local
implementations use real core services as their respective backend and a
actually mere wrappers around the core service functions. However, by having
the target to interact with GDB monitor instead of core directly, GDB monitor
gains full control over all threads and memory objects (dataspace) and the
address space of the target. All session requests that are of no specific
interest to GDB monitor are just passed through to the original parent.
This way, the target can use services provided by other Genode programs
as normally. Furthermore, service announcements of the target are propagated
to the original parent as well. This way, the debugging of Genode services
becomes possible.
Besides providing a virtual execution environment for the target, the GDB
monitor contains the communication protocol code to interact with a remote GNU
debugger. This code is a slightly modified version of the so-called 'gdbserver'
and uses a Genode terminal session to interact with GDB running on the host.
From GDB monitor's point of view, the terminal session is just a bidirectional
line of communication with GDB. The actual communication mechanism depends on
the service that provides the terminal session on Genode. Currently, there are
two services that can be used for this purpose: TCP terminal provides terminal
sessions via TCP connections, and Genode's UART drivers provides one terminal
session per physical UART. Depending on which of those terminal services is
used, the GDB on the host must be attached either to a network port or
to a comport of the target, i.e. Qemu.
Building
########
The source code of GDB monitor builds upon the original 'gdbserver' that
comes as part of the GDB package. This 3rd-party source code is not included
in Genode's source tree. To download the code and integrate it with Genode,
issue the following command
! ./tool/ports/prepare_port gdb
This way, the 3rd-party source code will be downloaded, unpacked, and patched.
To build and use GDB monitor, you will need to enable the 'ports' source-code
repository on your '<build-dir>/etc/build.conf' file (in addition to the
default repositories):
If you intend to use the TCP terminal for connecting GDB, you will further
need to prepare the 'lwip' package and enable the following repositories in your
'build.conf':
:libports: providing the lwIP stack needed by TCP terminal
:gems: hosting the source code of TCP terminal
With those preparations made, GDB monitor can be built from within the build
directory via
! make app/gdb_monitor
The build targets for the TCP terminal and the UART drivers are
'server/tcp_terminal' and 'drivers/uart' respectively.
Integrating GDB monitor into an application scenario
####################################################
To integrate GDB monitor into an existing Genode configuration, the start
node of the target
must be replaced by an instance of GDB monitor. GDB monitor, in turn,
needs to know which binary to debug. So we have provide GDB monitor with
this information using a 'config/target' node.
For example, the original start node of the Nitpicker GUI server as found in
the 'os/run/demo.run' run script looks as follows:
! <start name="nitpicker">
! <resource name="RAM" quantum="1M"/>
! <provides><service name="Gui"/></provides>
! </start>
For debugging the Nitpicker service, it must be replaced with the following
snippet (see the 'debug_nitpicker.run' script at 'ports/run/' for reference):
! <start name="gdb_monitor">
! <resource name="RAM" quantum="4M"/>
! <provides> <service name="Gui"/> </provides>
! <config><target name="nitpicker"/></config>
! </start>
Please note that the RAM quota has been increased to account for the needs
of both GDB monitor and Nitpicker. On startup, GDB monitor will ask its
parent for a 'Terminal' service. So we have to enhance the Genode scenario
with either an UART driver or the TCP terminal.
For using an UART, add the following start entry to the scenario:
! <start name="uart_drv">
! <resource name="RAM" quantum="1M"/>
! <provides> <service name="Terminal"/> </provides>
! <config>
! <policy label_prefix="gdb_monitor" uart="1"/>
! </config>
! </start>
This entry will start the UART driver and defines the policy of which UART to
be used for which client. In the example above, the client with the label
"gdb_monitor" will receive the UART 1. UART 0 is typically used for the kernel
and core's LOG service. So the use of UART 1 is recommended.
For using the TCP terminal, you will need to start the 'tcp_terminal' and a NIC
driver ('nic_drv'). On PC hardware, the NIC driver will further need the PCI
driver ('pci_drv'). For an example of integrating TCP terminal into a Genode
scenario, please refer to the 'tcp_terminal.run' script proved at 'gems/run/'.
GDB monitor is built upon the libc and a few custom libc plugins, each coming
in the form of a separate shared library. Please make sure to integrate the
shared C library (libc.lib.so) along with the dynamic linker (ld.lib.so) in
your boot image. For using the TCP terminal, 'lwip.lib.so' (TCP/IP stack) is
needed as well.
Examples
########
The following examples are using the Fiasco.OC kernel on the x86_32
platform. This is the only platform where all debugging features are fully
supported at the time of this writing. Please refer to the Section
[Current limitations and technical remarks] for more platform-specific
information.
Working with shared libraries
=============================
To get acquainted with GDB monitor, the 'ports' repository comes with two
example run scripts. The 'gdb_monitor_interactive.run' script executes a
simple test program via GDB monitor. The test program can be found at
'ports/src/test/gdb_monitor/'. When looking behind the scenes, the simple
program is not simple at all. It uses shared libraries (the libc)
plugin and executes multiple threads. So it is a nice testbed for exercising
these aspects. The run script can be invoked right from the build directory
via 'make run/gdb_monitor_interactive'. It will execute the scenario on Qemu and
use the UART to communicate with GDB. Qemu is instructed to redirect the second
serial interface to a local socket (using the port 5555):
! -serial chardev:uart
! -chardev socket,id=uart,port=5555,host=localhost,server,nowait,ipv4
The used TCP port is then specified to the GDB as remote target:
! target remote localhost:5555
The 'gdb_monitor_interactive.run' script performs these steps for you and spawns
GDB in a new terminal window. From within your build directory, execute the
run script via:
! make run/gdb_monitor_interactive
On startup, GDB monitor halts the target program and waits for GDB to
connect. Once connected, GDB will greet you with a prompt like this:
! Breakpoint 2, main () at /.../ports/src/test/gdb_monitor/main.cc:67
! 67 {
! (gdb)
At this point, GDB has acquired symbol information from the loaded shared
libraries and stopped the program at the beginning of its 'main()' function.
Now let's set a breakpoint to the 'puts' function, which is called by the test
program, by using the 'breakpoint' command:
! (gdb) b puts
! Breakpoint 3 at 0x106e120: file /.../libc-8.2.0/libc/stdio/puts.c, line 53.
After continuing the execution via 'c' (continue), you will see that the
breakpoint will trigger with a message like this:
! (gdb) c
! Continuing.
! Breakpoint 3, puts (s=0x10039c0 "in func2()\n")
! at /.../libc-8.2.0/libc/stdio/puts.c:53
! 53 {
_The following example applies to an older version of Genode and must_
_be revised for recent versions._
Now, you can inspect the source code of the function via the 'list' command,
inspect the function arguments ('info args' command) or start stepping
into the function using the 'next' command. For a test of printing a large
backtrace including several functions located in different shared libraries,
set another breakpoint at the 'stdout_write' function. This function is
used by the 'libc_log' backend and provided by the dynamic linker. The
backtrace will reveal all the intermediate steps throughout the libc when
'puts' is called.
! (gdb) b stdout_write
! Breakpoint 4 at 0x59d10: file /.../log_console.cc, line 108.
! (gdb) c
! Continuing.
! Breakpoint 4, stdout_write (s=0x1015860 "in func2()\n")
! at /.../genode/base/src/base/console/log_console.cc:108
! 108 {
! (gdb) bt
! #0 stdout_write (s=0x1015860 "in func2()\n")
! at /.../genode/base/src/base/console/log_console.cc:108
! #1 0x010c3701 in (anonymous namespace)::Plugin::write (this=0x10c4378,
! fd=0x10c0fa8, buf=0x6590, count=11)
! at /.../genode/libports/src/lib/libc_log/plugin.cc:93
! #2 0x010937bf in _write (libc_fd=1, buf=0x6590, count=11)
! at /.../genode/libports/src/lib/libc/file_operations.cc:406
! #3 0x0106ec4f in __swrite (cookie=0x10a1048, buf=0x6590 "in func2()\n", n=11)
! at /.../genode/libports/contrib/libc-8.2.0/libc/stdio/stdio.c:71
! #4 0x0106ef5a in _swrite (fp=0x10a1048, buf=0x6590 "in func2()\n", n=11)
! at /.../genode/libports/contrib/libc-8.2.0/libc/stdio/stdio.c:133
! #5 0x01067598 in __sflush (fp=0x10a1048)
! at /.../genode/libports/contrib/libc-8.2.0/libc/stdio/fflush.c:123
! #6 0x010675f8 in __fflush (fp=0x10a1048)
! at /.../genode/libports/contrib/libc-8.2.0/libc/stdio/fflush.c:96
! #7 0x0106a223 in __sfvwrite (fp=0x10a1048, uio=0x1015a44)
! at /.../genode/libports/contrib/libc-8.2.0/libc/stdio/fvwrite.c:194
! #8 0x0106e1ad in puts (s=0x10039c0 "in func2()\n")
! at /.../genode/libports/contrib/libc-8.2.0/libc/stdio/puts.c:68
! #9 0x0100041d in func2 ()
! at /.../genode/ports/src/test/gdb_monitor/main.cc:51
! #10 0x01000444 in func1 ()
! at /.../genode/ports/src/test/gdb_monitor/main.cc:60
! #11 0x01000496 in main ()
! at /.../genode/ports/src/test/gdb_monitor/main.cc:70
To inspect a specific call frame, switch to a particular frame by using
the number printed in the backtrace. For example, to print the local
variables of the call frame 5:
! (gdb) f 5
! #5 0x01067598 in __sflush (fp=0x10a1048)
! at /.../libc-8.2.0/libc/stdio/fflush.c:123
! 123 t = _swrite(fp, (char *)p, n);
! (gdb) info locals
! p = 0x6590 "in func2()\n"
! n = 11
! t = <optimized out>
The test program consists of multiple threads. To see which threads there are,
use the 'info thread' command. To switch another thread, use the 'thread'
command with the thread number as argument. Please make sure to issue the
'info threads' command prior using the 'thread' command for the first time.
Inspecting a Genode service
===========================
As a reference for debugging a native Genode service, the 'debug_nitpicker.run'
script provides a ready-to-use scenario. You can invoke it via
'make run/debug_nitpicker'.
Nitpicker is a statically linked program. Hence, no special precautions are
needed to obtain its symbol information. As a stress test for GDB monitor,
let us monitor the user input events supplied to the Nitpicker GUI server.
First, we need to set 'pagination' to off. Otherwise, we will be repeatedly
prompted by GDB after each page scrolled. We will then define a breakpoint
for the 'User_state::handle_event' function, which is called for each
input event received by Nitpicker:
! (gdb) set pagination off
! (gdb) b User_state::handle_event(Input::Event)
For each call of the function, we want to let GDB print the input event,
which is passed as function argument named 'ev'. We can use the 'commands'
facility to tell GDB what to do each time the breakpoint triggers:
! (gdb) commands
! Type commands for breakpoint(s) 1, one per line.
! End with a line saying just "end".
! >silent
! >print ev
! >c
! >end
Now, let's continue the execution of the program via the 'continue' command.
When moving the mouse over the Nitpicker GUI or when pressing/releasing
keys, you should see a message with the event information.
Current limitations and technical remarks
#########################################
Platform support
================
At the time of this writing the platform support is available on the following
base platforms:
:Fiasco.OC on x86_32: This is the primary platform fully supported by GDB
monitor. To enable user-land debugging support for the Fiasco.OC kernel
a kernel patch ('base-foc/patches/foc_single_step_x86.patch') is
required, which is applied on './tool/ports/prepare_port foc'.
:Fiasco.OC on ARM: GDB Monitor works on this platform but it has not received
the same amount of testing as the x86_32 version. Please use it with caution
and report any bugs you discover. To enable Fiasco.OC to deliver the
correct instruction pointer on the occurrence of an exception, a kernel
patch ('base-foc/patches/fix_exception_ip.patch') is required.
:OKL4 on x86_32: Partially supported. Breaking into a running programs using
Control-C, working with threads, printing backtraces, and inspecting
target memory works. However, breakpoints and single-stepping are not
supported. To use GDB monitor on OKL4, please make sure to have applied
the kernel patches in the 'base-okl4/patched' directory.
All required patches are applied to the respective kernel by default when
issuing './tool/ports/prepare_port <platform>'.
The other base platforms are not yet covered. We will address them according to
the demanded by the Genode developer community.
No simulation of read-only memory
=================================
The current implementation of GDB monitor hands out only RAM dataspaces to the
target. If the target opens a ROM session, the ROM dataspace gets copied into a
RAM dataspace. This is needed to enable GDB monitor to patch the code of the
target. Normally, the code is provided via read-only ROM dataspace. So patching
won't work. The current solution is the creation of a RAM copy.
However, this circumstance may have subtle effects on the target. For example
a program that crashed because it tries to write to its own text segment will
behave differently when executed within GDB monitor.
CPU register state during system calls
======================================
When intercepting the execution of the target while the target performs a
system call, the CPU register state as seen by GDB may be incorrect or
incomplete. The reason is that GDB monitor has to retrieve the CPU state from
the kernel. Some kernels, in particular Fiasco.OC, report that state only when
the thread crosses the kernel/user boundary (at the entry and exit of system
calls or then the thread enters the kernel via an exception). For a thread that
has already entered the kernel at interception time, this condition does not
apply. However, when stepping through target code, triggering breakpoints, or
intercepting a busy thread, the observed register state is current.
No support for watchpoints
==========================
The use of watchpoints is currently not supported. This feature would require
special kernel support, which is not provided by most kernels used as base
platforms of Genode.
Memory consumption
==================
GDB monitor is known to be somehow lax with regard to consuming memory. Please
don't be shy with over-provisioning RAM quota to 'gdb_monitor'.

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/lib/gdbserver_libc_support
CC_CXX_WARN_STRICT =

View File

@ -1,29 +0,0 @@
GDB_CONTRIB_DIR := $(call select_from_ports,gdb)/src/noux-pkg/gdb
INC_DIR += $(REP_DIR)/src/lib/gdbserver_libc_support \
$(REP_DIR)/src/lib/gdbserver_platform \
$(REP_DIR)/src/app/gdb_monitor \
$(REP_DIR)/src/app/gdb_monitor/gdbserver \
$(REP_DIR)/src/app/gdb_monitor/gdbsupport \
$(GDB_CONTRIB_DIR)/include \
$(GDB_CONTRIB_DIR) \
$(GDB_CONTRIB_DIR)/gdb \
$(GDB_CONTRIB_DIR)/gdb/common \
$(GDB_CONTRIB_DIR)/gdb/regformats \
$(GDB_CONTRIB_DIR)/gdbserver \
$(GDB_CONTRIB_DIR)/gnulib/import
SRC_CC += gdbserver_platform_helper.cc
CC_OPT_gdbserver_platform_helper += -fpermissive
CC_OPT += -DGDBSERVER
CC_OPT += -DHAVE_DECL_PTRACE -DHAVE_SYS_PTRACE_H
CC_CXX_OPT_STD = -std=gnu++17
LIBS += stdcxx libc
vpath %.cc $(REP_DIR)/src/lib/gdbserver_platform
vpath %.cc $(GDB_CONTRIB_DIR)/gdb
vpath %.cc $(GDB_CONTRIB_DIR)/gdbserver

View File

@ -1,3 +0,0 @@
SHARED_LIB = yes
CC_CXX_WARN_STRICT =

View File

@ -1,12 +0,0 @@
REQUIRES += nova
SRC_CC = spec/x86_32/low.cc \
native_cpu.cc
SHARED_LIB = yes
vpath %.cc $(REP_DIR)/src/lib/gdbserver_platform-nova
include $(REP_DIR)/lib/mk/spec/x86_32/gdbserver_platform-x86_32.inc
CC_CXX_WARN_STRICT =

View File

@ -1,11 +0,0 @@
INC_DIR += $(REP_DIR)/src/lib/gdbserver_platform/spec/x86_32
SRC_CC += arch/i386.cc \
linux-x86-low.cc \
linux-x86-tdesc.cc \
nat/x86-linux.cc \
nat/x86-linux-dregs.cc
CC_OPT += -fpermissive -Wno-unused-function
include $(REP_DIR)/lib/mk/gdbserver_platform.inc

View File

@ -1,12 +0,0 @@
REQUIRES += nova
SRC_CC += spec/x86_64/low.cc \
native_cpu.cc
SHARED_LIB = yes
vpath %.cc $(REP_DIR)/src/lib/gdbserver_platform-nova
include $(REP_DIR)/lib/mk/spec/x86_64/gdbserver_platform-x86_64.inc
CC_CXX_WARN_STRICT =

View File

@ -1,12 +0,0 @@
INC_DIR += $(REP_DIR)/src/lib/gdbserver_platform/spec/x86_64
SRC_CC += arch/i386.cc \
arch/amd64.cc \
nat/x86-linux.cc \
nat/x86-linux-dregs.cc \
linux-x86-low.cc \
linux-x86-tdesc.cc
CC_OPT += -fpermissive -Wno-unused-function
include $(REP_DIR)/lib/mk/gdbserver_platform.inc

View File

@ -1,268 +0,0 @@
_Unwind_Resume U
_Z11ptid_of_lwpP8lwp_info U
_Z12debug_printfPKcz U
_Z12find_lwp_pid6ptid_t U
_Z13all_processesB5cxx11 U
_Z13register_sizePK11target_desci U
_Z14agent_loaded_pv U
_Z14fetch_registerPKcmRm T
_Z14linux_stop_lwpP8lwp_info U
_Z14lwp_is_stoppedP8lwp_info U
_Z14store_registerPKcRmm T
_Z14tdesc_add_flagP22tdesc_type_with_fieldsiPKc U
_Z15current_processv U
_Z15lwp_stop_reasonP8lwp_info U
_Z15set_tdesc_osabiP11target_descPKc U
_Z15tdesc_add_fieldP22tdesc_type_with_fieldsPKcP10tdesc_type U
_Z16current_lwp_ptidv U
_Z16find_process_pidi U
_Z16init_target_descP11target_descPPKc U
_Z16perror_with_namePKc U
_Z16regcache_releasev U
_Z16switch_to_threadP11thread_info U
_Z16tdesc_create_regP13tdesc_featurePKciiS2_iS2_ U
_Z16tdesc_named_typePK13tdesc_featurePKc U
_Z17iterate_over_lwps6ptid_tN3gdb13function_viewIFiP8lwp_infoEEE U
_Z18tdesc_add_bitfieldP22tdesc_type_with_fieldsPKcii U
_Z18tdesc_create_flagsP13tdesc_featurePKci U
_Z18tdesc_create_unionP13tdesc_featurePKc U
_Z18x86_low_init_dregsP19x86_debug_reg_state U
_Z19get_thread_regcacheP11thread_infoi U
_Z19initialize_low_archv T
_Z19target_write_memorymPKhl U
_Z19tdesc_create_structP13tdesc_featurePKc U
_Z19tdesc_create_vectorP13tdesc_featurePKcP10tdesc_typei U
_Z19x86_debug_reg_statei T
_Z20relocate_instructionPmm U
_Z20tdesc_create_featureP11target_descPKc U
_Z20x86_linux_new_threadP8lwp_info T
_Z21cannot_fetch_registerPKc T
_Z21cannot_store_registerPKcm T
_Z21genode_fetch_registeriPm T
_Z21genode_store_registerim T
_Z21get_raw_reg_func_addrv U
_Z21lwp_arch_private_infoP8lwp_info U
_Z21tdesc_set_struct_sizeP22tdesc_type_with_fieldsi U
_Z21x86_linux_dr_get_addri T
_Z21x86_linux_dr_set_addrim T
_Z22claim_trampoline_spacemPm U
_Z22find_any_thread_of_pidi U
_Z22genode_child_resourcesv U
_Z22i386_get_ipa_tdesc_idxPK11target_desc T
_Z22set_tdesc_architectureP11target_descPKc U
_Z23amd64_get_ipa_tdesc_idxPK11target_desc T
_Z23copy_target_descriptionP11target_descPKS_ U
_Z23genode_stop_all_threadsv U
_Z23initialize_regsets_infoP12regsets_info U
_Z23supply_register_by_nameP8regcachePKcPKv U
_Z23x86_linux_delete_threadP13arch_lwp_info T
_Z23x86_linux_dr_get_statusv T
_Z24collect_register_by_nameP8regcachePKcPv U
_Z24get_current_thread_statev T
_Z24set_current_thread_stateN6Genode12Thread_stateE T
_Z24x86_dr_insert_watchpointP19x86_debug_reg_state17target_hw_bp_typemi U
_Z24x86_dr_remove_watchpointP19x86_debug_reg_state17target_hw_bp_typemi U
_Z24x86_linux_dr_get_controlv T
_Z24x86_linux_dr_set_controlm T
_Z25lwp_set_arch_private_infoP8lwp_infoP13arch_lwp_info U
_Z27allocate_target_descriptionv U
_Z27i386_linux_read_descriptionm T
_Z27lwp_debug_registers_changedP8lwp_info T
_Z27x86_dr_stopped_data_addressP19x86_debug_reg_statePm U
_Z27x86_linux_prepare_to_resumeP8lwp_info T
_Z28amd64_linux_read_descriptionmb T
_Z30i386_create_target_descriptionmbb T
_Z31amd64_create_target_descriptionmbbb T
_Z31lwp_set_debug_registers_changedP8lwp_infoi T
_Z32x86_linux_update_debug_registersP8lwp_info T
_Z34raw_bkpt_type_to_target_hw_bp_type13raw_bkpt_type U
_Z38have_fast_tracepoint_trampoline_bufferPc U
_Z7warningPKcz U
_Z8paddressm U
_ZN10x86_target10low_get_pcEP8regcache T
_ZN10x86_target10low_set_pcEP8regcachem T
_ZN10x86_target12low_new_forkEP12process_infoS1_ T
_ZN10x86_target13get_regs_infoEv T
_ZN10x86_target14low_arch_setupEv T
_ZN10x86_target14low_new_threadEP8lwp_info T
_ZN10x86_target15low_new_processEv T
_ZN10x86_target16low_insert_pointE13raw_bkpt_typemiP14raw_breakpoint T
_ZN10x86_target16low_remove_pointE13raw_bkpt_typemiP14raw_breakpoint T
_ZN10x86_target16update_xmltargetEv T
_ZN10x86_target17get_ipa_tdesc_idxEv T
_ZN10x86_target17low_breakpoint_atEm T
_ZN10x86_target17low_delete_threadEP13arch_lwp_info T
_ZN10x86_target17low_siginfo_fixupEP9__siginfoPhi T
_ZN10x86_target18low_delete_processEP17arch_process_info T
_ZN10x86_target18process_qsupportedEN3gdb10array_viewIKPKcEE T
_ZN10x86_target19low_get_thread_areaEiPm T
_ZN10x86_target20supports_tracepointsEv T
_ZN10x86_target21low_prepare_to_resumeEP8lwp_info T
_ZN10x86_target21supports_z_point_typeEc T
_ZN10x86_target23low_decr_pc_after_breakEv T
_ZN10x86_target23sw_breakpoint_from_kindEiPi T
_ZN10x86_target24low_get_syscall_trapinfoEP8regcachePi T
_ZN10x86_target24low_stopped_data_addressEv T
_ZN10x86_target24low_supports_breakpointsEv T
_ZN10x86_target25low_cannot_fetch_registerEi T
_ZN10x86_target25low_cannot_store_registerEi T
_ZN10x86_target25low_stopped_by_watchpointEv T
_ZN10x86_target25supports_fast_tracepointsEv T
_ZN10x86_target26low_supports_catch_syscallEv T
_ZN10x86_target27low_supports_range_steppingEv T
_ZN10x86_target32get_min_fast_tracepoint_insn_lenEv T
_ZN10x86_target32install_fast_tracepoint_jump_padEmmmmmPmS0_S0_PhS0_S0_S0_Pc T
_ZN10x86_target8emit_opsEv T
_ZN11Gdb_monitor21Cpu_session_component10thread_capEm U
_ZN11Gdb_monitor21Cpu_session_component17_setup_native_cpuEv T
_ZN11Gdb_monitor21Cpu_session_component18parent_cpu_sessionEv U
_ZN11Gdb_monitor21Cpu_session_component19_cleanup_native_cpuEv T
_ZN11Gdb_monitor21Cpu_session_component9thread_epEv U
_ZN20linux_process_target11read_memoryEmPhi U
_ZN20linux_process_target11thread_nameE6ptid_t U
_ZN20linux_process_target11unpause_allEb U
_ZN20linux_process_target12insert_pointE13raw_bkpt_typemiP14raw_breakpoint U
_ZN20linux_process_target12multifs_openEiPKcit U
_ZN20linux_process_target12qxfer_osdataEPKcPhPKhmi U
_ZN20linux_process_target12read_offsetsEPmS0_ U
_ZN20linux_process_target12remove_pointE13raw_bkpt_typemiP14raw_breakpoint U
_ZN20linux_process_target12thread_aliveE6ptid_t U
_ZN20linux_process_target12write_memoryEmPKhi U
_ZN20linux_process_target13qxfer_siginfoEPKcPhPKhmi U
_ZN20linux_process_target14core_of_threadE6ptid_t U
_ZN20linux_process_target14multifs_unlinkEiPKc U
_ZN20linux_process_target14start_non_stopEb U
_ZN20linux_process_target14supports_agentEv U
_ZN20linux_process_target14thread_stoppedEP11thread_info U
_ZN20linux_process_target15create_inferiorEPKcRKSt6vectorIPcSaIS3_EE U
_ZN20linux_process_target15fetch_registersEP8regcachei U
_ZN20linux_process_target15get_tls_addressEP11thread_infommPm U
_ZN20linux_process_target15look_up_symbolsEv U
_ZN20linux_process_target15store_registersEP8regcachei U
_ZN20linux_process_target16low_get_next_pcsEP8regcache U
_ZN20linux_process_target16multifs_readlinkEiPKcPcm U
_ZN20linux_process_target16pid_to_exec_fileEi U
_ZN20linux_process_target16supports_multifsEv U
_ZN20linux_process_target17request_interruptEv U
_ZN20linux_process_target17stabilize_threadsEv U
_ZN20linux_process_target17supports_non_stopEv U
_ZN20linux_process_target18low_fetch_registerEP8regcachei U
_ZN20linux_process_target18supports_read_auxvEv U
_ZN20linux_process_target20post_create_inferiorEv U
_ZN20linux_process_target20qxfer_libraries_svr4EPKcPhPKhmi U
_ZN20linux_process_target20stopped_data_addressEv U
_ZN20linux_process_target20supports_exec_eventsEv U
_ZN20linux_process_target20supports_fork_eventsEv U
_ZN20linux_process_target20thread_pending_childEP11thread_info U
_ZN20linux_process_target21stopped_by_watchpointEv U
_ZN20linux_process_target21supports_qxfer_osdataEv U
_ZN20linux_process_target21supports_read_offsetsEv U
_ZN20linux_process_target21supports_vfork_eventsEv U
_ZN20linux_process_target21thread_pending_parentEP11thread_info U
_ZN20linux_process_target22handle_monitor_commandEPc U
_ZN20linux_process_target22supports_catch_syscallEv U
_ZN20linux_process_target22supports_multi_processEv U
_ZN20linux_process_target22supports_qxfer_siginfoEv U
_ZN20linux_process_target23supports_range_steppingEv U
_ZN20linux_process_target23supports_thread_stoppedEv U
_ZN20linux_process_target24stopped_by_hw_breakpointEv U
_ZN20linux_process_target24stopped_by_sw_breakpointEv U
_ZN20linux_process_target24supports_get_tls_addressEv U
_ZN20linux_process_target25handle_new_gdb_connectionEv U
_ZN20linux_process_target25supports_pid_to_exec_fileEv U
_ZN20linux_process_target26low_supply_ptrace_registerEP8regcacheiPKc U
_ZN20linux_process_target27low_collect_ptrace_registerEP8regcacheiPc U
_ZN20linux_process_target29supports_hardware_single_stepEv U
_ZN20linux_process_target29supports_qxfer_libraries_svr4Ev U
_ZN20linux_process_target30supports_disable_randomizationEv U
_ZN20linux_process_target33supports_stopped_by_hw_breakpointEv U
_ZN20linux_process_target33supports_stopped_by_sw_breakpointEv U
_ZN20linux_process_target4joinEi U
_ZN20linux_process_target4killEP12process_info U
_ZN20linux_process_target4waitE6ptid_tP17target_waitstatus10enum_flagsI16target_wait_flagE U
_ZN20linux_process_target5asyncEb U
_ZN20linux_process_target5mournEP12process_info U
_ZN20linux_process_target6attachEm U
_ZN20linux_process_target6detachEP12process_info U
_ZN20linux_process_target6resumeEP13thread_resumem U
_ZN20linux_process_target7read_pcEP8regcache U
_ZN20linux_process_target8write_pcEP8regcachem U
_ZN20linux_process_target9pause_allEb U
_ZN20linux_process_target9read_auxvEmPhj U
_ZN22process_stratum_target11read_btraceEP18btrace_target_infoP6buffer16btrace_read_type U
_ZN22process_stratum_target12read_loadmapEPKcmPhj U
_ZN22process_stratum_target13enable_btraceEP11thread_infoPK13btrace_config U
_ZN22process_stratum_target13fetch_memtagsEmmRSt6vectorIhN3gdb22default_init_allocatorIhSaIhEEEEi U
_ZN22process_stratum_target13store_memtagsEmmRKSt6vectorIhN3gdb22default_init_allocatorIhSaIhEEEEi U
_ZN22process_stratum_target13thread_handleE6ptid_tPPhPi U
_ZN22process_stratum_target14disable_btraceEP18btrace_target_info U
_ZN22process_stratum_target15get_tib_addressE6ptid_tPm U
_ZN22process_stratum_target15supports_btraceEv U
_ZN22process_stratum_target16read_btrace_confEPK18btrace_target_infoP6buffer U
_ZN22process_stratum_target21supports_read_loadmapEv U
_ZN22process_stratum_target23breakpoint_kind_from_pcEPm U
_ZN22process_stratum_target23supports_memory_taggingEv U
_ZN22process_stratum_target24supports_get_tib_addressEv U
_ZN22process_stratum_target29supports_software_single_stepEv U
_ZN22process_stratum_target34breakpoint_kind_from_current_stateEPm U
_ZN29scoped_restore_current_threadC1Ev U
_ZN29scoped_restore_current_threadD1Ev U
_ZN6Genode13Avl_node_baseC2Ev U
_ZN6Genode14Rpc_entrypoint7_manageEPNS_15Rpc_object_baseE U
_ZN6Genode14Rpc_entrypoint9_dissolveEPNS_15Rpc_object_baseE U
_ZN6Genode17Native_capability4_decEv U
_ZN6Genode17Native_capability4_incEv U
_ZN6Genode17Native_capabilityC1Ev U
_ZN6Genode3Log3logEv U
_ZN6Genode3Log8_acquireENS0_4TypeE U
_ZN6Genode3Log8_releaseEv U
_ZN6Genode4Lock4lockEv U
_ZN6Genode4Lock6unlockEv U
_ZN6Genode4LockC1ENS0_5StateE U
_ZN6Genode5Mutex7acquireEv U
_ZN6Genode5Mutex7releaseEv U
_ZN6Genode5Trace6Logger17_evaluate_controlEv U
_ZN6Genode5printERNS_6OutputEPKc U
_ZN6Genode5printERNS_6OutputEPKv U
_ZN6Genode5printERNS_6OutputEl U
_ZN6Genode6Thread7_loggerEv U
_ZN6Genode8ipc_callENS_17Native_capabilityERNS_11Msgbuf_baseES2_m U
_ZNK6Genode17Native_capability10local_nameEv U
_ZSt9terminatev U
_ZTI20linux_process_target U
_ZTIN11Gdb_monitor20Cpu_thread_componentE U
_ZTVN10__cxxabiv117__class_type_infoE U
_ZTVN10__cxxabiv120__si_class_type_infoE U
_ZTVN10__cxxabiv121__vmi_class_type_infoE U
_ZdlPvRN6Genode11DeallocatorE U
_ZdlPvm U
_ZnwmRN6Genode9AllocatorE U
__cxa_allocate_exception U
__cxa_atexit U
__cxa_begin_catch U
__cxa_end_catch U
__cxa_get_exception_ptr U
__cxa_pure_virtual U
__cxa_throw U
__dynamic_cast U
__error U
__gnu_Unwind_Find_exidx T
__gxx_personality_v0 U
abort U
amd64_emit_ops D 296
current_insn_ptr U
current_thread U
debug_threads U
dl_unwind_find_exidx W
emit_error U
free U
have_ptrace_getfpxregs B 4
i386_emit_ops D 296
ptrace U
sprintf U
strtok_r U
strtoul U
the_linux_target D 8
x86_dr_low D 48
xcalloc U
xstrdup U

View File

@ -1,42 +0,0 @@
#
# \brief GDB command line arguments for setting a breakpoint in the 'main()' function
# \author Christian Prochaska
# \date 2013-09-04
#
proc gdb_initial_breakpoint_cmds { target_binary_name } {
#
# We set a break in the 'binary_ready_hook_for_gdb()' function in the
# dynamic linker and load the symbols of the application by using the
# following gdb command sequence.
#
set gdb_cmds ""
# don't ask for y/n when loading a new symbol file
append gdb_cmds {-ex "set interactive-mode off" }
# avoid pagination prompts in autopilot test
append gdb_cmds {-ex "set pagination off" }
# set a breakpoint in the 'binary_ready_hook_for_gdb' function
append gdb_cmds {-ex "b binary_ready_hook_for_gdb" }
# continue execution until the breakpoint triggers
append gdb_cmds {-ex "c" }
# delete the 'binary_ready_hook_for_gdb' breakpoint
append gdb_cmds {-ex "delete 1" }
# load the symbols of the test application
append gdb_cmds "-ex \"file debug/$target_binary_name\" "
# set search path for "sharedlibrary" to debug
append gdb_cmds {-ex "set solib-search-path debug" }
# load the symbols of loaded shared libraries
append gdb_cmds {-ex "sharedlibrary" }
return $gdb_cmds
}

View File

@ -1,293 +0,0 @@
#
# \brief Test for using the GDB monitor
# \author Christian Prochaska
# \author Norman Feske
# \date 2011-05-24
#
#
# Only the NOVA platform supports most of the tested features at this time.
#
if {![have_include "power_on/qemu"] ||
!([have_spec nova])} {
puts "Run script is only supported for NOVA in Qemu"; exit 0
}
set build_components {
core lib/ld init timer lib/libc lib/libm lib/vfs lib/posix lib/stdcxx
lib/vfs_pipe
drivers/uart
app/gdb_monitor lib/gdbserver_platform test/gdb_monitor
}
lappend build_components "lib/gdbserver_platform-$::env(KERNEL)"
build $build_components
create_boot_directory
install_config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="2M"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="pc_uart_drv">
<resource name="RAM" quantum="2M"/>
<provides>
<service name="Terminal"/>
<service name="Uart"/>
</provides>
<config>
<policy label_prefix="gdb_monitor" uart="1"/>
</config>
</start>
<start name="gdb_monitor" caps="1000">
<resource name="RAM" quantum="9M"/>
<config>
<target name="test-gdb_monitor">
<config>
<vfs> <dir name="dev"> <log/> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
</config>
</target>
<preserve name="RAM" quantum="5M"/>
<vfs>
<dir name="dev">
<log/>
<terminal raw="yes"/>
<inline name="rtc">2021-01-01 00:01</inline>
</dir>
<dir name="pipe"> <pipe/> </dir>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log" pipe="/pipe" rtc="/dev/rtc"/>
</config>
</start>
</config>
}
# evaluated by the run tool
proc binary_name_gdbserver_platform_lib_so { } {
return "gdbserver_platform-$::env(KERNEL).lib.so"
}
build_boot_image [build_artifacts]
#
# Execute test case
#
set local_port 5555
# qemu config
append qemu_args " -display none "
# connect comport 0 to stdio
append qemu_args " -serial stdio "
# connect comport 1 with TCP port $local_port
append qemu_args " -serial chardev:uart "
append qemu_args " -chardev socket,id=uart,port=$local_port,host=localhost,server,nowait,ipv4 "
run_genode_until {.*\[init -> gdb_monitor\] Remote debugging using /dev/terminal.*} 30
set genode_id [output_spawn_id]
puts "GDB monitor is up, starting GDB"
source ${genode_dir}/repos/ports/run/gdb_monitor.inc
# GDB loads symbols from 'debug/ld.lib.so'
if { [have_spec nova] } {
exec ln -sf ld-nova.lib.so debug/ld.lib.so
}
set gdb_target_binary "test-gdb_monitor"
# sequence of GDB commands to execute at startup
set gdb_cmds ""
append gdb_cmds {-ex "target remote localhost:$local_port" }
append gdb_cmds {-ex "set style enabled off" }
append gdb_cmds [gdb_initial_breakpoint_cmds $gdb_target_binary]
# run GDB
eval spawn [gdb] debug/ld.lib.so -n $gdb_cmds
set gdb_id [list $spawn_id $genode_id]
puts ""
puts "----- test: breakpoint in 'main()' -----"
puts ""
run_genode_until {\(gdb\)} 60 $gdb_id
send "b main\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Breakpoint 2, main ()} $output]} {
puts stderr "*** Error: Breakpoint in main() did not trigger"
exit -1
}
send "delete 2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
puts "\n"
puts "----- test: breakpoint in shared library -----"
puts ""
send "b puts\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Breakpoint 3, puts ()} $output]} {
puts "*** Error: Breakpoint in shared library did not trigger"
exit -1
}
puts "\n"
puts "----- test: stack trace when not in syscall -----"
puts ""
send "bt\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {#0 puts} $output] ||
![regexp {in func2()} $output] ||
![regexp {in func1()} $output] ||
![regexp {in main ()} $output]} {
puts stderr "*** Error: Stack trace when not in syscall is not as expected"
exit -1
}
puts "\n"
puts "----- test: modification of a variable value -----"
puts ""
send "print test_var\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {\$1 = 1} $output]} {
puts stderr "*** Error: first 'print test_var' command didn't result in the expected output"
exit -1
}
send "set var test_var=2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "print test_var\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {\$2 = 2} $output]} {
puts stderr "*** Error: second 'print test_var' command didn't result in the expected output"
exit -1
}
puts "\n"
puts "----- test: 'call' command -----"
puts ""
send "call test_var_func()\n"
run_genode_until {\(gdb\)} 60 $gdb_id
if {![regexp {\$3 = 3} $output]} {
puts stderr "*** Error: 'call' command didn't result in the expected output"
exit -1
}
puts "\n"
puts "----- test: thread info -----"
puts ""
send "b test_thread_start\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Breakpoint 4, test_thread_start} $output]} {
puts stderr "*** Error: Breakpoint in test thread did not trigger"
exit -1
}
send "info threads\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {\* 4 Thread 1.4 test_thread_start} $output] ||
![regexp { 3 Thread 1.3} $output] ||
![regexp { 2 Thread 1.2} $output] ||
![regexp { 1 Thread 1.1} $output]} {
puts stderr "*** Error: Thread info is not as expected"
exit -1
}
puts "\n"
puts "----- test: step into function -----"
puts ""
send "step\n"
run_genode_until {\(gdb\)} 30 $gdb_id
send "thread 2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {test_thread_step} $output]} {
puts stderr "*** Error: Step into function didn't result in the expected output"
exit -1
}
puts "\n"
puts "----- test: catching a segmentation fault -----"
puts ""
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Thread 4 received signal SIGSEGV, Segmentation fault.} $output]} {
puts stderr "*** Error: Segmentation fault exception was not caught"
exit -1
}
# does not work well on ARM yet
if {![have_spec arm]} {
puts "\n"
puts "----- test: stack trace when in syscall -----"
puts ""
send "thread 2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "bt\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Genode::Signal_receiver::block_for_signal} $output] ||
![regexp {Genode::Entrypoint::_wait_and_dispatch_one_io_signal} $output] ||
![regexp {Libc::Kernel::run} $output] } {
puts stderr "*** Error: Stack trace when in syscall is not as expected"
exit -1
}
}
puts ""
# vi: set ft=tcl :

View File

@ -1,134 +0,0 @@
#
# \brief Test for using the GDB monitor
# \author Christian Prochaska
# \author Norman Feske
# \date 2011-05-24
#
if {![have_include "power_on/qemu"] ||
!([have_spec nova])} {
puts "Run script is only supported for NOVA in Qemu"; exit 0
}
set build_components {
core lib/ld init timer
lib/libc lib/libm lib/vfs lib/stdcxx lib/vfs_pipe lib/posix
drivers/uart
app/gdb_monitor lib/gdbserver_platform test/gdb_monitor
}
lappend build_components "lib/gdbserver_platform-$::env(KERNEL)"
build $build_components
create_boot_directory
install_config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="2M"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="pc_uart_drv">
<resource name="RAM" quantum="2M"/>
<provides>
<service name="Terminal"/>
<service name="Uart"/>
</provides>
<config>
<policy label_prefix="gdb_monitor" uart="1"/>
</config>
</start>
<start name="gdb_monitor" caps="1000">
<resource name="RAM" quantum="9M"/>
<config>
<target name="test-gdb_monitor">
<config>
<vfs> <dir name="dev"> <log/> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
</config>
</target>
<preserve name="RAM" quantum="5M"/>
<vfs>
<dir name="dev">
<log/>
<terminal raw="yes"/>
<inline name="rtc">2021-01-01 00:01</inline>
</dir>
<dir name="pipe"> <pipe/> </dir>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log" pipe="/pipe" rtc="/dev/rtc"/>
</config>
</start>
</config>
}
# evaluated by the run tool
proc binary_name_gdbserver_platform_lib_so { } {
return "gdbserver_platform-$::env(KERNEL).lib.so"
}
build_boot_image [build_artifacts]
#
# Execute test case
#
set local_port 5555
# qemu config
append qemu_args " -display none "
# connect comport 0 to stdio
append qemu_args " -serial stdio "
# connect comport 1 with TCP port $local_port
append qemu_args " -serial chardev:uart "
append qemu_args " -chardev socket,id=uart,port=$local_port,host=localhost,server,nowait,ipv4 "
run_genode_until {.*Remote debugging using /dev/terminal.*} 30
puts "GDB monitor is up, starting GDB in a new terminal"
source ${genode_dir}/repos/ports/run/gdb_monitor.inc
# GDB loads symbols from 'debug/ld.lib.so'
if { [have_spec nova] } {
exec ln -sf ld-nova.lib.so debug/ld.lib.so
}
if { [have_spec foc] } {
exec ln -sf ld-foc.lib.so debug/ld.lib.so
}
set gdb_target_binary "test-gdb_monitor"
# sequence of GDB commands to execute at startup
set gdb_cmds ""
append gdb_cmds "-ex \"target remote localhost:$local_port\" "
append gdb_cmds [gdb_initial_breakpoint_cmds $gdb_target_binary]
# ask the user for confirmations again
append gdb_cmds {-ex "set interactive-mode auto" }
puts "command: [gdb] debug/ld.lib.so $gdb_cmds"
exec [terminal] -e "bash -lc \'[gdb] debug/ld.lib.so $gdb_cmds\'" &
interact -i [output_spawn_id]
# vi: set ft=tcl :

View File

@ -1,131 +0,0 @@
#
# \brief Test for providing a config file to the target
# \author Christian Prochaska
# \date 2012-04-16
#
if {![have_include "power_on/qemu"] ||
!([have_spec nova])} {
puts "Run script is only supported for NOVA in Qemu"; exit 0
}
set build_components {
core lib/ld init timer lib/libc lib/libm lib/vfs lib/stdcxx lib/vfs_pipe
drivers/uart
app/gdb_monitor lib/gdbserver_platform test/gdb_monitor_target_config
}
lappend build_components "lib/gdbserver_platform-$::env(KERNEL)"
build $build_components
create_boot_directory
install_config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="pc_uart_drv">
<resource name="RAM" quantum="1M"/>
<provides>
<service name="Terminal"/>
<service name="Uart"/>
</provides>
<config>
<policy label_prefix="gdb_monitor" uart="1"/>
</config>
</start>
<start name="gdb_monitor" caps="1000">
<resource name="RAM" quantum="9M"/>
<config>
<target name="test-gdb_monitor_target_config">
<config>
<vfs> <dir name="dev"> <log/> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
<test_config_subnode/>
</config>
</target>
<preserve name="RAM" quantum="5M"/>
<vfs>
<dir name="dev">
<log/>
<terminal raw="yes"/>
<inline name="rtc">2021-01-01 00:01</inline>
</dir>
<dir name="pipe"> <pipe/> </dir>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log" pipe="/pipe" rtc="/dev/rtc"/>
</config>
</start>
</config>
}
# evaluated by the run tool
proc binary_name_gdbserver_platform_lib_so { } {
return "gdbserver_platform-$::env(KERNEL).lib.so"
}
build_boot_image [build_artifacts]
#
# Execute test case
#
set local_port 5555
# qemu config
append qemu_args " -display none "
# connect comport 0 to stdio
append qemu_args " -serial stdio "
# connect comport 1 with TCP port $local_port
append qemu_args " -serial chardev:uart "
append qemu_args " -chardev socket,id=uart,port=$local_port,host=localhost,server,nowait,ipv4 "
run_genode_until {.*Remote debugging using /dev/terminal.*} 30
puts "GDB monitor is up, starting GDB in a new terminal"
source ${genode_dir}/repos/ports/run/gdb_monitor.inc
# GDB loads symbols from 'debug/ld.lib.so'
if { [have_spec nova] } {
exec ln -sf ld-nova.lib.so debug/ld.lib.so
}
if { [have_spec foc] } {
exec ln -sf ld-foc.lib.so debug/ld.lib.so
}
set gdb_target_binary "test-gdb_monitor_target_config"
# sequence of GDB commands to execute at startup
set gdb_cmds ""
append gdb_cmds "-ex \"target remote localhost:$local_port\" "
append gdb_cmds [gdb_initial_breakpoint_cmds $gdb_target_binary]
# continue execution
append gdb_cmds {-ex "c" }
exec [terminal] -e "bash -lc \'[gdb] debug/ld.lib.so $gdb_cmds\'" &
interact -i [output_spawn_id]
# vi: set ft=tcl :

View File

@ -1,363 +0,0 @@
/*
* \brief Application child
* \author Christian Prochaska
* \date 2009-10-05
*/
/*
* Copyright (C) 2009-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _APP_CHILD_H_
#define _APP_CHILD_H_
/* Genode includes */
#include <base/child.h>
#include <base/service.h>
#include <init/child_policy.h>
#include <os/session_requester.h>
#include <util/arg_string.h>
/* sandbox library private includes */
#include <sandbox/server.h>
/* GDB monitor includes */
#include "genode_child_resources.h"
#include "cpu_session_component.h"
#include "pd_session_component.h"
#include "rom.h"
#include "child_config.h"
namespace Gdb_monitor {
using namespace Genode;
class App_child;
}
class Gdb_monitor::App_child : public Child_policy,
public Async_service::Wakeup,
public Sandbox::Report_update_trigger,
public Sandbox::Routed_service::Pd_accessor,
public Sandbox::Routed_service::Ram_accessor
{
private:
typedef Registered<Genode::Parent_service> Parent_service;
typedef Registry<Parent_service> Parent_services;
typedef Registry<Sandbox::Routed_service> Child_services;
/**
* gdbserver blocks in 'select()', so a separate entrypoint is used.
*/
struct Local_env : Genode::Env
{
Genode::Env &genode_env;
Genode::Entrypoint local_ep {
genode_env, 4*1024*sizeof(addr_t), "target_ep", Affinity::Location() };
Local_env(Env &genode_env) : genode_env(genode_env) { }
Parent &parent() override { return genode_env.parent(); }
Cpu_session &cpu() override { return genode_env.cpu(); }
Region_map &rm() override { return genode_env.rm(); }
Pd_session &pd() override { return genode_env.pd(); }
Entrypoint &ep() override { return local_ep; }
Cpu_session_capability cpu_session_cap() override { return genode_env.cpu_session_cap(); }
Pd_session_capability pd_session_cap() override { return genode_env.pd_session_cap(); }
Id_space<Parent::Client> &id_space() override { return genode_env.id_space(); }
Pd_session_capability ram_session_cap() { return pd_session_cap(); }
Pd_session &ram() { return pd(); }
Session_capability session(Parent::Service_name const &service_name,
Parent::Client::Id id,
Parent::Session_args const &session_args,
Affinity const &affinity) override
{
return genode_env.session(service_name, id, session_args, affinity);
}
Session_capability try_session(Parent::Service_name const &service_name,
Parent::Client::Id id,
Parent::Session_args const &session_args,
Affinity const &affinity) override
{
return genode_env.session(service_name, id, session_args, affinity);
}
void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override
{
return genode_env.upgrade(id, args);
}
void close(Parent::Client::Id id) override { return genode_env.close(id); }
void exec_static_constructors() override { }
};
Local_env _env;
Allocator &_alloc;
Pd_session_capability _ref_pd_cap { _env.pd_session_cap() };
Pd_session &_ref_pd { _env.pd() };
const char *_unique_name;
Dataspace_capability _elf_ds;
Region_map &_rm;
Ram_quota _ram_quota;
Cap_quota _cap_quota;
Parent_services _parent_services;
Child_services _child_services;
Init::Child_config _child_config;
Init::Child_policy_provide_rom_file _config_policy;
Genode_child_resources _genode_child_resources;
Signal_handler<App_child> _unresolved_page_fault_handler;
Dataspace_pool _managed_ds_map;
Pd_session_component _pd { _env.ep().rpc_ep(),
_env,
_alloc,
_unique_name,
_managed_ds_map };
Pd_service::Single_session_factory _pd_factory { _pd };
Pd_service _pd_service { _pd_factory };
Local_cpu_factory _cpu_factory;
Cpu_service _cpu_service { _cpu_factory };
Local_rom_factory _rom_factory;
Rom_service _rom_service { _rom_factory };
Genode::Session_requester _session_requester { _env.ep().rpc_ep(),
_env.ram(),
_env.rm() };
Sandbox::Server _server { _env, _alloc, _child_services, *this };
Child *_child;
void _handle_unresolved_page_fault()
{
_genode_child_resources.cpu_session_component().handle_unresolved_page_fault();
}
template <typename T>
static Service *_find_service(Registry<T> &services,
Service::Name const &name)
{
Service *service = nullptr;
services.for_each([&] (T &s) {
if (!service && (s.name() == name))
service = &s; });
return service;
}
/**
* Child_service::Wakeup callback
*/
void wakeup_async_service() override
{
_session_requester.trigger_update();
}
/**
* Sandbox::Report_update_trigger callbacks
*/
void trigger_report_update() override { }
void trigger_immediate_report_update() override { }
/**
* Sandbox::Routed_service::Pd_accessor interface
*/
Pd_session &pd() override { return _child->pd(); }
Pd_session_capability pd_cap() const override { return _child->pd_session_cap(); }
/**
* Sandbox::Routed_service::Ram_accessor interface
*/
Pd_session &ram() override { return _child->pd(); }
Pd_session_capability ram_cap() const override { return _child->pd_session_cap(); }
Service &_matching_service(Service::Name const &service_name,
Session_label const &label)
{
Service *service = nullptr;
/* check for config file request */
if ((service = _config_policy.resolve_session_request_with_label(service_name, label)))
return *service;
/* check for "session_requests" ROM request */
if ((service_name == Genode::Rom_session::service_name()) &&
(label.last_element() == Genode::Session_requester::rom_name()))
return _session_requester.service();
if (service_name == "CPU")
return _cpu_service;
if (service_name == "PD")
return _pd_service;
if (service_name == "ROM")
return _rom_service;
service = _find_service(_parent_services, service_name);
if (!service)
service = new (_alloc) Parent_service(_parent_services, _env, service_name);
if (!service)
throw Service_denied();
return *service;
}
public:
/**
* Constructor
*/
App_child(Env &env,
Allocator &alloc,
char const *unique_name,
Ram_quota ram_quota,
Cap_quota cap_quota,
Entrypoint &signal_ep,
Xml_node target_node,
int const new_thread_pipe_write_end,
int const breakpoint_len,
unsigned char const *breakpoint_data)
:
_env(env),
_alloc(alloc),
_unique_name(unique_name),
_rm(_env.rm()),
_ram_quota(ram_quota), _cap_quota(cap_quota),
_child_config(_env.ram(), _rm, target_node),
_config_policy("config", _child_config.dataspace(), &_env.ep().rpc_ep()),
_unresolved_page_fault_handler(signal_ep, *this,
&App_child::_handle_unresolved_page_fault),
_cpu_factory(_env, _env.ep().rpc_ep(), _alloc, _pd.core_pd_cap(),
signal_ep, new_thread_pipe_write_end,
breakpoint_len, breakpoint_data,
&_genode_child_resources),
_rom_factory(_env, _env.ep().rpc_ep(), _alloc)
{
_genode_child_resources.region_map_component(&_pd.region_map());
_pd.region_map().fault_handler(_unresolved_page_fault_handler);
}
~App_child()
{
_child_services.for_each([&] (Sandbox::Routed_service &service) {
destroy(_alloc, &service);
});
destroy(_alloc, _child);
}
Genode_child_resources *genode_child_resources()
{
return &_genode_child_resources;
}
void start()
{
_child = new (_alloc) Child(_rm, _env.ep().rpc_ep(), *this);
}
/****************************
** Child-policy interface **
****************************/
Name name() const override { return _unique_name; }
Pd_session &ref_pd() override { return _ref_pd; }
Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
Id_space<Parent::Server> &server_id_space() override
{
return _session_requester.id_space();
}
void init(Pd_session &session,
Pd_session_capability cap) override
{
session.ref_account(_ref_pd_cap);
_env.ep().rpc_ep().apply(cap, [&] (Pd_session_component *pd) {
if (pd) {
_ref_pd.transfer_quota(pd->core_pd_cap(), _cap_quota);
_ref_pd.transfer_quota(pd->core_pd_cap(), _ram_quota);
}
});
}
Route resolve_session_request(Service::Name const &service_name,
Session_label const &label,
Session::Diag const diag) override
{
return Route { .service = _matching_service(service_name, label),
.label = label,
.diag = diag };
}
void announce_service(Service::Name const &service_name) override
{
if (_find_service(_child_services, service_name)) {
Genode::warning(name(), ": service ", service_name, " is already registered");
return;
}
new (_alloc) Sandbox::Routed_service(_child_services,
"target",
*this, *this,
_session_requester.id_space(),
_child->session_factory(),
service_name,
*this);
char server_config[4096];
try {
Xml_generator xml(server_config, sizeof(server_config),
"config", [&] () {
_child_services.for_each([&] (Service &service) {
xml.node("service", [&] () {
xml.attribute("name", service_name);
xml.node("default-policy", [&] () {
xml.node("child", [&] () {
xml.attribute("name", "target");
});
});
});
});
});
_server.apply_updated_policy();
} catch(Xml_generator::Buffer_exceeded &) {
error("XML buffer for server configuration exceeded");
}
_env.parent().announce(service_name.string());
}
};
#endif /* _APP_CHILD_H_ */

View File

@ -1,110 +0,0 @@
/*
* \brief List which appends new elements at the end
* \author Christian Prochaska
* \date 2011-09-09
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _APPEND_LIST_H_
#define _APPEND_LIST_H_
/*
* \param LT list element type
*/
template <typename LT>
class Append_list
{
private:
LT *_first;
LT *_last;
public:
class Element
{
protected:
friend class Append_list;
LT *_next;
public:
Element(): _next(0) { }
/**
* Return next element in list
*/
LT *next() const { return _next; }
};
public:
/**
* Constructor
*
* Start with an empty list.
*/
Append_list(): _first(0), _last(0) { }
/**
* Return first list element
*/
LT *first() const { return _first; }
/**
* Append element to list
*/
void append(LT *le)
{
if (_last) {
_last->Element::_next = le;
_last = le;
} else {
_first = _last = le;
}
}
/**
* Remove element from list
*/
void remove(LT *le)
{
if (!_first) return;
/* if specified element is the first of the list */
if (le == _first) {
if (_first == _last)
_first = _last = 0;
else
_first = le->Element::_next;
}
else {
/* search specified element in the list */
LT *e = _first;
while (e->_next && (e->_next != le))
e = e->_next;
/* element is not member of the list */
if (!e->_next) return;
/* e->_next is the element to remove, skip it in list */
e->Element::_next = e->Element::_next->Element::_next;
if (le == _last)
_last = e;
}
le->Element::_next = 0;
}
};
#endif /* _APPEND_LIST_H_ */

View File

@ -1,140 +0,0 @@
/*
* \brief Utility for handling child configuration
* \author Norman Feske
* \date 2008-03-22
*/
/*
* Copyright (C) 2008-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CHILD_CONFIG_H_
#define _CHILD_CONFIG_H_
#warning header is deprecated, used os/dynamic_rom_session.h instead
#include <util/xml_node.h>
#include <base/attached_dataspace.h>
#include <base/ram_allocator.h>
namespace Init { class Child_config; }
class Init::Child_config
{
private:
Genode::Ram_allocator &_ram;
typedef Genode::String<64> Rom_name;
Rom_name const _rom_name;
Genode::Ram_dataspace_capability const _ram_ds;
Rom_name _rom_name_from_start_node(Genode::Xml_node start)
{
if (!start.has_sub_node("configfile"))
return Rom_name();
return start.sub_node("configfile").attribute_value("name", Rom_name());
}
/**
* Buffer '<config>' sub node in a dedicated RAM dataspace
*
* \throw Out_of_ram
* \throw Out_of_caps
* \throw Region_map::Region_conflict
*/
Genode::Ram_dataspace_capability
_ram_ds_from_start_node(Genode::Xml_node start,
Genode::Ram_allocator &ram, Genode::Region_map &rm)
{
/*
* If the start node contains a 'config' entry, we copy this entry
* into a fresh dataspace to be provided to our child.
*/
Genode::Xml_node const config = start.has_sub_node("config")
? start.sub_node("config")
: Genode::Xml_node("<config/>");
Genode::Ram_dataspace_capability ram_ds;
try {
/*
* Allocate RAM dataspace that is big enough to hold the
* configuration and the null termination.
*/
ram_ds = ram.alloc(config.size() + 1);
/*
* Make dataspace locally accessible, copy configuration into the
* dataspace, and append a string-terminating zero.
*/
Genode::Attached_dataspace attached(rm, ram_ds);
config.with_raw_node([&] (char const *start, size_t length) {
Genode::memcpy(attached.local_addr<char>(), start, length); });
attached.local_addr<char>()[config.size()] = 0;
return ram_ds;
}
catch (Genode::Region_map::Region_conflict) { ram.free(ram_ds); throw; }
}
public:
/**
* Constructor
*
* The provided RAM session is used to obtain a dataspace for
* holding the copy of the child's configuration data unless the
* configuration is supplied via a config ROM module.
*
* \throw Out_of_ram failed to allocate the backing
* store for holding config data
* \throw Out_of_caps
*
* \throw Region_map::Region_conflict failed to temporarily attach the
* config dataspace to the local
* address space
*
* If the start node contains a 'filename' entry, we only keep the
* information about the ROM module name.
*/
Child_config(Genode::Ram_allocator &ram, Genode::Region_map &local_rm,
Genode::Xml_node start)
:
_ram(ram),
_rom_name(_rom_name_from_start_node(start)),
_ram_ds(_rom_name.valid() ? Genode::Ram_dataspace_capability()
: _ram_ds_from_start_node(start, ram, local_rm))
{ }
/**
* Destructor
*/
~Child_config() { if (_ram_ds.valid()) _ram.free(_ram_ds); }
/**
* Return file name if configuration comes from a file
*
* If the configuration is provided inline, the method returns 0.
*/
char const *filename() const {
return _rom_name.valid() ? _rom_name.string() : nullptr; }
/**
* Request dataspace holding the start node's configuration data
*
* This method returns a valid dataspace only when using an
* inline configuration (if 'filename()' returns 0).
*/
Genode::Dataspace_capability dataspace() {
return Genode::Dataspace_capability(_ram_ds); }
};
#endif /* _CHILD_CONFIG_H_ */

View File

@ -1,354 +0,0 @@
/*
* \brief Implementation of the CPU session interface
* \author Christian Prochaska
* \date 2011-04-28
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/env.h>
#include <base/log.h>
#include <base/sleep.h>
#include <cpu_session_component.h>
#include <util/list.h>
/* GDB monitor includes */
#include "cpu_thread_component.h"
/* libc includes */
#include <sys/signal.h>
/* genode-low.cc */
extern void genode_remove_thread(unsigned long lwpid);
using namespace Genode;
using namespace Gdb_monitor;
Cpu_session &Cpu_session_component::parent_cpu_session()
{
return _parent_cpu_session;
}
Rpc_entrypoint &Cpu_session_component::thread_ep()
{
return _ep;
}
Entrypoint &Cpu_session_component::signal_ep()
{
return _signal_ep;
}
Thread_capability Cpu_session_component::thread_cap(unsigned long lwpid)
{
Cpu_thread_component *cpu_thread = _thread_list.first();
while (cpu_thread) {
if (cpu_thread->lwpid() == lwpid)
return cpu_thread->thread_cap();
cpu_thread = cpu_thread->next();
}
return Thread_capability();
}
Cpu_thread_component *Cpu_session_component::lookup_cpu_thread(unsigned long lwpid)
{
Cpu_thread_component *cpu_thread = _thread_list.first();
while (cpu_thread) {
if (cpu_thread->lwpid() == lwpid)
return cpu_thread;
cpu_thread = cpu_thread->next();
}
return nullptr;
}
Cpu_thread_component *Cpu_session_component::lookup_cpu_thread(Thread_capability thread_cap)
{
Cpu_thread_component *cpu_thread = _thread_list.first();
while (cpu_thread) {
if (cpu_thread->thread_cap().local_name() == thread_cap.local_name())
return cpu_thread;
cpu_thread = cpu_thread->next();
}
return 0;
}
unsigned long Cpu_session_component::lwpid(Thread_capability thread_cap)
{
return lookup_cpu_thread(thread_cap)->lwpid();
}
int Cpu_session_component::signal_pipe_read_fd(Thread_capability thread_cap)
{
return lookup_cpu_thread(thread_cap)->signal_pipe_read_fd();
}
int Cpu_session_component::send_signal(Thread_capability thread_cap,
int signo)
{
Cpu_thread_component *cpu_thread = lookup_cpu_thread(thread_cap);
cpu_thread->pause();
switch (signo) {
case SIGSTOP:
Signal_transmitter(cpu_thread->sigstop_signal_context_cap()).submit();
return 1;
case SIGINT:
Signal_transmitter(cpu_thread->sigint_signal_context_cap()).submit();
return 1;
default:
error("unexpected signal ", signo);
return 0;
}
}
/*
* This function delivers a SIGSEGV to the first thread with an unresolved
* page fault that it finds. Multiple page-faulted threads are currently
* not supported.
*/
void Cpu_session_component::handle_unresolved_page_fault()
{
/*
* It can happen that the thread state of the thread which caused the
* page fault is not accessible yet. In that case, we'll retry until
* it is accessible.
*/
while (1) {
Thread_capability thread_cap = first();
while (thread_cap.valid()) {
try {
Cpu_thread_component *cpu_thread = lookup_cpu_thread(thread_cap);
Thread_state thread_state = cpu_thread->state();
if (thread_state.unresolved_page_fault) {
/*
* On base-foc it is necessary to pause the thread before
* IP and SP are available in the thread state.
*/
cpu_thread->pause();
cpu_thread->deliver_signal(SIGSEGV);
return;
}
} catch (Cpu_thread::State_access_failed) { }
thread_cap = next(thread_cap);
}
}
}
void Cpu_session_component::stop_new_threads(bool stop)
{
_stop_new_threads = stop;
}
bool Cpu_session_component::stop_new_threads()
{
return _stop_new_threads;
}
Mutex &Cpu_session_component::stop_new_threads_mutex()
{
return _stop_new_threads_mutex;
}
int Cpu_session_component::handle_initial_breakpoint(unsigned long lwpid)
{
Cpu_thread_component *cpu_thread = _thread_list.first();
while (cpu_thread) {
if (cpu_thread->lwpid() == lwpid)
return cpu_thread->handle_initial_breakpoint();
cpu_thread = cpu_thread->next();
}
return 0;
}
void Cpu_session_component::pause_all_threads()
{
Mutex::Guard stop_new_threads_mutex_guard(stop_new_threads_mutex());
stop_new_threads(true);
for (Cpu_thread_component *cpu_thread = _thread_list.first();
cpu_thread;
cpu_thread = cpu_thread->next()) {
cpu_thread->pause();
}
}
void Cpu_session_component::resume_all_threads()
{
Mutex::Guard stop_new_threads_guard(stop_new_threads_mutex());
stop_new_threads(false);
for (Cpu_thread_component *cpu_thread = _thread_list.first();
cpu_thread;
cpu_thread = cpu_thread->next()) {
cpu_thread->single_step(false);
cpu_thread->resume();
}
}
Thread_capability Cpu_session_component::first()
{
Cpu_thread_component *cpu_thread = _thread_list.first();
if (cpu_thread)
return cpu_thread->thread_cap();
else
return Thread_capability();
}
Thread_capability Cpu_session_component::next(Thread_capability thread_cap)
{
Cpu_thread_component *next_cpu_thread = lookup_cpu_thread(thread_cap)->next();
if (next_cpu_thread)
return next_cpu_thread->thread_cap();
else
return Thread_capability();
}
Thread_capability Cpu_session_component::create_thread(Capability<Pd_session> pd,
Cpu_session::Name const &name,
Affinity::Location affinity,
Weight weight,
addr_t utcb)
{
Cpu_thread_component *cpu_thread =
new (_md_alloc) Cpu_thread_component(*this, _core_pd, name,
affinity, weight, utcb,
_new_thread_pipe_write_end,
_breakpoint_len,
_breakpoint_data);
_thread_list.append(cpu_thread);
return cpu_thread->cap();
}
void Cpu_session_component::kill_thread(Thread_capability thread_cap)
{
Cpu_thread_component *cpu_thread = lookup_cpu_thread(thread_cap);
if (cpu_thread) {
if (cpu_thread->lwpid())
genode_remove_thread(cpu_thread->lwpid());
_thread_list.remove(cpu_thread);
destroy(_md_alloc, cpu_thread);
} else
error(__PRETTY_FUNCTION__, ": "
"could not find thread info for the given thread capability");
_parent_cpu_session.kill_thread(thread_cap);
}
void Cpu_session_component::exception_sigh(Signal_context_capability handler)
{
_parent_cpu_session.exception_sigh(handler);
}
Affinity::Space Cpu_session_component::affinity_space() const
{
return _parent_cpu_session.affinity_space();
}
Dataspace_capability Cpu_session_component::trace_control()
{
return _parent_cpu_session.trace_control();
}
Capability<Cpu_session::Native_cpu> Cpu_session_component::native_cpu()
{
return _native_cpu_cap;
}
Cpu_session_component::Cpu_session_component(Env &env,
Rpc_entrypoint &ep,
Allocator &md_alloc,
Pd_session_capability core_pd,
Entrypoint &signal_ep,
const char *args,
Affinity const &affinity,
int const new_thread_pipe_write_end,
int const breakpoint_len,
unsigned char const *breakpoint_data)
: _env(env),
_ep(ep),
_md_alloc(md_alloc),
_core_pd(core_pd),
_parent_cpu_session(env.session<Cpu_session>(_id_space_element.id(), args, affinity), *this),
_signal_ep(signal_ep),
_new_thread_pipe_write_end(new_thread_pipe_write_end),
_breakpoint_len(breakpoint_len),
_breakpoint_data(breakpoint_data),
_native_cpu_cap(_setup_native_cpu())
{
_ep.manage(this);
}
Cpu_session_component::~Cpu_session_component()
{
for (Cpu_thread_component *cpu_thread = _thread_list.first();
cpu_thread; cpu_thread = _thread_list.first()) {
_thread_list.remove(cpu_thread);
destroy(_md_alloc, cpu_thread);
}
_cleanup_native_cpu();
_ep.dissolve(this);
}
int Cpu_session_component::ref_account(Cpu_session_capability) { return -1; }
int Cpu_session_component::transfer_quota(Cpu_session_capability, size_t) { return -1; }
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -1,233 +0,0 @@
/*
* \brief Core-specific instance of the CPU session/thread interfaces
* \author Christian Helmuth
* \date 2006-07-17
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CPU_SESSION_COMPONENT_H_
#define _CPU_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/allocator.h>
#include <base/env.h>
#include <base/rpc_server.h>
#include <base/service.h>
#include <base/thread.h>
#include <util/retry.h>
#include <cpu_session/client.h>
#include <parent/parent.h>
#include <pd_session/capability.h>
/* GDB monitor includes */
#include "append_list.h"
#include "genode_child_resources.h"
namespace Gdb_monitor
{
using namespace Genode;
class Cpu_session_component;
class Cpu_thread_component;
class Local_cpu_factory;
typedef Local_service<Cpu_session_component> Cpu_service;
}
class Gdb_monitor::Cpu_session_component : public Rpc_object<Cpu_session>
{
private:
Env &_env;
Parent::Client _parent_client;
Id_space<Parent::Client>::Element const _id_space_element
{ _parent_client, _env.id_space() };
Rpc_entrypoint &_ep;
Allocator &_md_alloc;
Pd_session_capability _core_pd;
struct Expanding_parent_cpu_session : Cpu_session_client
{
Cpu_session_component &_component;
Expanding_parent_cpu_session(Cpu_session_capability cap,
Cpu_session_component &component)
:
Cpu_session_client(cap), _component(component) { }
Thread_capability create_thread(Capability<Pd_session> pd,
Cpu_session::Name const &name,
Affinity::Location affinity,
Weight weight,
addr_t utcb) override
{
enum { UPGRADE_ATTEMPTS = ~0U };
return Genode::retry<Out_of_ram>(
[&] () {
return Genode::retry<Out_of_caps>(
[&] () { return Cpu_session_client::create_thread(pd, name, affinity, weight, utcb); },
[&] () { _component._env.upgrade(_component._id_space_element.id(), "cap_quota=3"); },
UPGRADE_ATTEMPTS);
},
[&] () { _component._env.upgrade(_component._id_space_element.id(), "ram_quota=8K"); },
UPGRADE_ATTEMPTS);
}
};
Expanding_parent_cpu_session _parent_cpu_session;
Entrypoint &_signal_ep;
int const _new_thread_pipe_write_end;
int const _breakpoint_len;
unsigned char const *_breakpoint_data;
Append_list<Cpu_thread_component> _thread_list;
bool _stop_new_threads = true;
Mutex _stop_new_threads_mutex;
Capability<Cpu_session::Native_cpu> _native_cpu_cap;
Capability<Cpu_session::Native_cpu> _setup_native_cpu();
void _cleanup_native_cpu();
public:
/**
* Constructor
*/
Cpu_session_component(Env &env,
Rpc_entrypoint &ep,
Allocator &md_alloc,
Pd_session_capability core_pd,
Entrypoint &signal_ep,
const char *args,
Affinity const &affinity,
int const new_thread_pipe_write_end,
int const breakpoint_len,
unsigned char const *breakpoint_data);
/**
* Destructor
*/
~Cpu_session_component();
Cpu_session &parent_cpu_session();
Rpc_entrypoint &thread_ep();
Entrypoint &signal_ep();
Thread_capability thread_cap(unsigned long lwpid);
unsigned long lwpid(Thread_capability thread_cap);
Cpu_thread_component *lookup_cpu_thread(unsigned long lwpid);
Cpu_thread_component *lookup_cpu_thread(Thread_capability thread_cap);
int signal_pipe_read_fd(Thread_capability thread_cap);
int send_signal(Thread_capability thread_cap, int signo);
void handle_unresolved_page_fault();
void stop_new_threads(bool stop);
bool stop_new_threads();
Mutex &stop_new_threads_mutex();
int handle_initial_breakpoint(unsigned long lwpid);
void pause_all_threads();
void resume_all_threads();
Thread_capability first();
Thread_capability next(Thread_capability);
/***************************
** CPU session interface **
***************************/
Thread_capability create_thread(Capability<Pd_session>,
Name const &,
Affinity::Location,
Weight,
addr_t) override;
void kill_thread(Thread_capability) override;
void exception_sigh(Signal_context_capability handler) override;
Affinity::Space affinity_space() const override;
Dataspace_capability trace_control() override;
int ref_account(Cpu_session_capability c) override;
int transfer_quota(Cpu_session_capability c, size_t q) override;
Quota quota() override;
Capability<Native_cpu> native_cpu() override;
};
class Gdb_monitor::Local_cpu_factory : public Cpu_service::Factory
{
private:
Env &_env;
Rpc_entrypoint &_ep;
Allocator &_md_alloc;
Pd_session_capability _core_pd;
Entrypoint &_signal_ep;
int const _new_thread_pipe_write_end;
int const _breakpoint_len;
unsigned char const *_breakpoint_data;
Genode_child_resources *_genode_child_resources;
public:
Local_cpu_factory(Env &env,
Rpc_entrypoint &ep,
Allocator &md_alloc,
Pd_session_capability core_pd,
Entrypoint &signal_ep,
int new_thread_pipe_write_end,
int const breakpoint_len,
unsigned char const *breakpoint_data,
Genode_child_resources *genode_child_resources)
: _env(env), _ep(ep),
_md_alloc(md_alloc),
_core_pd(core_pd),
_signal_ep(signal_ep),
_new_thread_pipe_write_end(new_thread_pipe_write_end),
_breakpoint_len(breakpoint_len),
_breakpoint_data(breakpoint_data),
_genode_child_resources(genode_child_resources)
{ }
/***********************
** Factory interface **
***********************/
Cpu_session_component &create(Args const &args, Affinity affinity) override
{
Cpu_session_component *cpu_session_component =
new (_md_alloc)
Cpu_session_component(_env,
_ep,
_md_alloc,
_core_pd,
_signal_ep,
args.string(),
affinity,
_new_thread_pipe_write_end,
_breakpoint_len,
_breakpoint_data);
_genode_child_resources->cpu_session_component(cpu_session_component);
return *cpu_session_component;
}
void upgrade(Cpu_session_component &, Args const &) override { }
void destroy(Cpu_session_component &session) override
{
Genode::destroy(_md_alloc, &session);
}
};
#endif /* _CPU_SESSION_COMPONENT_H_ */

View File

@ -1,314 +0,0 @@
/*
* \brief Cpu_thread_component class for GDB monitor
* \author Christian Prochaska
* \date 2016-05-12
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* GDB monitor includes */
#include "cpu_thread_component.h"
#include "genode-low.h"
/* libc includes */
#include <signal.h>
#include <unistd.h>
static unsigned long new_lwpid = GENODE_MAIN_LWPID;
using namespace Gdb_monitor;
bool Cpu_thread_component::_set_breakpoint_at_first_instruction(addr_t ip)
{
_breakpoint_ip = ip;
if (genode_read_memory(_breakpoint_ip, _original_instructions,
_breakpoint_len) != 0) {
warning(__PRETTY_FUNCTION__, ": could not read memory at thread start address");
return false;
}
if (genode_write_memory(_breakpoint_ip, _breakpoint_data,
_breakpoint_len) != 0) {
warning(__PRETTY_FUNCTION__, ": could not set breakpoint at thread start address");
return false;
}
return true;
}
void Cpu_thread_component::_remove_breakpoint_at_first_instruction()
{
if (genode_write_memory(_breakpoint_ip, _original_instructions,
_breakpoint_len) != 0)
warning(__PRETTY_FUNCTION__, ": could not remove breakpoint at thread start address");
}
void Cpu_thread_component::_handle_exception()
{
deliver_signal(SIGTRAP);
}
void Cpu_thread_component::_handle_sigstop()
{
deliver_signal(SIGSTOP);
}
void Cpu_thread_component::_handle_sigint()
{
deliver_signal(SIGINT);
}
Cpu_thread_component::Cpu_thread_component(Cpu_session_component &cpu_session_component,
Capability<Pd_session> pd,
Cpu_session::Name const &name,
Affinity::Location affinity,
Cpu_session::Weight weight,
addr_t utcb,
int const new_thread_pipe_write_end,
int const breakpoint_len,
unsigned char const *breakpoint_data)
:
_cpu_session_component(cpu_session_component),
_parent_cpu_thread(
_cpu_session_component.parent_cpu_session().create_thread(pd,
name,
affinity,
weight,
utcb)),
_new_thread_pipe_write_end(new_thread_pipe_write_end),
_breakpoint_len(breakpoint_len),
_breakpoint_data(breakpoint_data),
_exception_handler(_cpu_session_component.signal_ep(), *this,
&Cpu_thread_component::_handle_exception),
_sigstop_handler(_cpu_session_component.signal_ep(), *this,
&Cpu_thread_component::_handle_sigstop),
_sigint_handler(_cpu_session_component.signal_ep(), *this,
&Cpu_thread_component::_handle_sigint)
{
_cpu_session_component.thread_ep().manage(this);
if (pipe(_pipefd) != 0)
error("could not create pipe");
}
Cpu_thread_component::~Cpu_thread_component()
{
close(_pipefd[0]);
close(_pipefd[1]);
_cpu_session_component.thread_ep().dissolve(this);
}
int Cpu_thread_component::send_signal(int signo)
{
pause();
switch (signo) {
case SIGSTOP:
Signal_transmitter(sigstop_signal_context_cap()).submit();
return 1;
case SIGINT:
Signal_transmitter(sigint_signal_context_cap()).submit();
return 1;
default:
error("unexpected signal ", signo);
return 0;
}
}
int Cpu_thread_component::deliver_signal(int signo)
{
if ((signo == SIGTRAP) && _initial_sigtrap_pending) {
_initial_sigtrap_pending = false;
if (_verbose)
log("received initial SIGTRAP for lwpid ", _lwpid);
if (_lwpid == GENODE_MAIN_LWPID) {
_remove_breakpoint_at_first_instruction();
_initial_breakpoint_handled = true;
}
/*
* The mutex guard prevents an interruption by
* 'genode_stop_all_threads()', which could cause
* the new thread to be resumed when it should be
* stopped.
*/
Mutex::Guard stop_new_threads_mutex_guard(
_cpu_session_component.stop_new_threads_mutex());
if (!_cpu_session_component.stop_new_threads())
resume();
/*
* gdbserver expects SIGSTOP as first signal of a new thread,
* but we cannot write SIGSTOP here, because waitpid() would
* detect that the thread is in an exception state and wait
* for the SIGTRAP. So SIGINFO ist used for this purpose.
*/
signo = SIGINFO;
}
switch (signo) {
case SIGSTOP:
if (_verbose)
log("delivering SIGSTOP to thread ", _lwpid);
break;
case SIGTRAP:
if (_verbose)
log("delivering SIGTRAP to thread ", _lwpid);
break;
case SIGSEGV:
if (_verbose)
log("delivering SIGSEGV to thread ", _lwpid);
break;
case SIGINT:
if (_verbose)
log("delivering SIGINT to thread ", _lwpid);
break;
case SIGINFO:
if (_verbose)
if (_lwpid != GENODE_MAIN_LWPID)
log("delivering initial SIGSTOP to thread ", _lwpid);
break;
default:
error("unexpected signal ", signo);
}
if (!((signo == SIGINFO) && (_lwpid == GENODE_MAIN_LWPID)))
write(_pipefd[1], &signo, sizeof(signo));
/*
* gdbserver might be blocking in 'waitpid()' without having
* the new thread's pipe fd in its 'select' fd set yet. Writing
* into the 'new thread pipe' here will unblock 'select' in this
* case.
*/
if (signo == SIGINFO)
write(_new_thread_pipe_write_end, &_lwpid, sizeof(_lwpid));
return 0;
}
Dataspace_capability Cpu_thread_component::utcb()
{
return _parent_cpu_thread.utcb();
}
void Cpu_thread_component::start(addr_t ip, addr_t sp)
{
_lwpid = new_lwpid++;
_initial_ip = ip;
/* register the exception handler */
exception_sigh(exception_signal_context_cap());
/* set breakpoint at first instruction */
if (lwpid() == GENODE_MAIN_LWPID)
_set_breakpoint_at_first_instruction(ip);
else
genode_set_initial_breakpoint_at(ip);
_parent_cpu_thread.start(ip, sp);
}
void Cpu_thread_component::pause()
{
unsigned loop_cnt = 0;
/* required semantic for gdb is that thread is paused with valid state */
for (;;) {
_parent_cpu_thread.pause();
try {
/* check if the thread state is valid */
_parent_cpu_thread.state();
/* the thread is paused */
return;
} catch (State_access_failed) {
loop_cnt ++;
if (loop_cnt % 100 == 0)
Genode::warning("pausing thread failed ", loop_cnt,
". times, continue looping");
}
}
}
void Cpu_thread_component::resume()
{
_parent_cpu_thread.resume();
}
void Cpu_thread_component::single_step(bool enable)
{
_parent_cpu_thread.single_step(enable);
}
void Cpu_thread_component::state(Thread_state const &state)
{
_parent_cpu_thread.state(state);
}
Thread_state Cpu_thread_component::state()
{
return _parent_cpu_thread.state();
}
void Cpu_thread_component::exception_sigh(Signal_context_capability handler)
{
_parent_cpu_thread.exception_sigh(handler);
}
void Cpu_thread_component::affinity(Affinity::Location location)
{
_parent_cpu_thread.affinity(location);
}
unsigned Cpu_thread_component::trace_control_index()
{
return _parent_cpu_thread.trace_control_index();
}
Dataspace_capability Cpu_thread_component::trace_buffer()
{
return _parent_cpu_thread.trace_buffer();
}
Dataspace_capability Cpu_thread_component::trace_policy()
{
return _parent_cpu_thread.trace_policy();
}

View File

@ -1,143 +0,0 @@
/*
* \brief Cpu_thread_component class for GDB monitor
* \author Christian Prochaska
* \date 2016-05-12
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CPU_THREAD_COMPONENT_H_
#define _CPU_THREAD_COMPONENT_H_
/* Genode includes */
#include <base/thread.h>
#include <cpu_session/cpu_session.h>
#include <cpu_thread/client.h>
#include "config.h"
#include "append_list.h"
#include "cpu_session_component.h"
extern "C" int delete_gdb_breakpoint_at(long long where);
namespace Gdb_monitor { class Cpu_thread_component; }
class Gdb_monitor::Cpu_thread_component : public Rpc_object<Cpu_thread>,
public Append_list<Cpu_thread_component>::Element
{
private:
static constexpr bool _verbose = false;
Cpu_session_component &_cpu_session_component;
Cpu_thread_client _parent_cpu_thread;
int const _new_thread_pipe_write_end;
int const _breakpoint_len;
unsigned char const *_breakpoint_data;
unsigned long _lwpid { 0 };
addr_t _initial_ip { 0 };
/*
* SIGTRAP, SIGSTOP and SIGINT must get delivered to the gdbserver code
* in the same order that they were generated. Since these signals are
* generated by different threads, the signal ep is used as
* synchronization point.
*/
Signal_handler<Cpu_thread_component> _exception_handler;
Signal_handler<Cpu_thread_component> _sigstop_handler;
Signal_handler<Cpu_thread_component> _sigint_handler;
int _pipefd[2];
bool _initial_sigtrap_pending = true;
bool _initial_breakpoint_handled = false;
/* data for breakpoint at first instruction */
enum { MAX_BREAKPOINT_LEN = 8 }; /* value from mem-break.c */
unsigned char _original_instructions[MAX_BREAKPOINT_LEN];
addr_t _breakpoint_ip;
bool _set_breakpoint_at_first_instruction(addr_t ip);
void _remove_breakpoint_at_first_instruction();
void _handle_exception();
void _handle_sigstop();
void _handle_sigint();
public:
Cpu_thread_component(Cpu_session_component &cpu_session_component,
Capability<Pd_session> pd,
Cpu_session::Name const &name,
Affinity::Location affinity,
Cpu_session::Weight weight,
addr_t utcb,
int const new_thread_pipe_write_end,
int const breakpoint_len,
unsigned char const *breakpoint_data);
~Cpu_thread_component();
Signal_context_capability exception_signal_context_cap()
{
return _exception_handler;
}
Signal_context_capability sigstop_signal_context_cap()
{
return _sigstop_handler;
}
Signal_context_capability sigint_signal_context_cap()
{
return _sigint_handler;
}
Thread_capability thread_cap() { return cap(); }
unsigned long lwpid() { return _lwpid; }
Thread_capability parent_thread_cap()
{
return _parent_cpu_thread.rpc_cap();
}
int signal_pipe_read_fd() { return _pipefd[0]; }
int handle_initial_breakpoint()
{
if (!_initial_breakpoint_handled) {
_initial_breakpoint_handled = true;
return 1;
}
return 0;
}
int send_signal(int signo);
int deliver_signal(int signo);
/**************************
** CPU thread interface **
*************************/
Dataspace_capability utcb() override;
void start(addr_t, addr_t) override;
void pause() override;
void resume() override;
void single_step(bool) override;
Thread_state state() override;
void state(Thread_state const &) override;
void exception_sigh(Signal_context_capability) override;
void affinity(Affinity::Location) override;
unsigned trace_control_index() override;
Dataspace_capability trace_buffer() override;
Dataspace_capability trace_policy() override;
};
#endif /* _CPU_THREAD_COMPONENT_H_ */

View File

@ -1,47 +0,0 @@
/*
* \brief Dataspace object for object pool
* \author Christian Prochaska
* \date 2011-09-12
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _DATASPACE_OBJECT_H_
#define _DATASPACE_OBJECT_H_
#include <base/object_pool.h>
#include <dataspace/capability.h>
namespace Gdb_monitor {
using namespace Genode;
class Region_map_component;
class Dataspace_object;
typedef Object_pool<Dataspace_object> Dataspace_pool;
class Dataspace_object : public Dataspace_pool::Entry
{
private:
Region_map_component *_region_map_component;
public:
Dataspace_object(Dataspace_capability ds_cap, Region_map_component *region_map_component)
: Object_pool<Dataspace_object>::Entry(ds_cap),
_region_map_component(region_map_component) { }
Region_map_component *region_map_component() { return _region_map_component; }
};
}
#endif /* _DATASPACE_OBJECT_H_ */

View File

@ -1,831 +0,0 @@
/*
* \brief Genode backend for GDBServer (C++)
* \author Christian Prochaska
* \author Norman Feske
* \date 2011-05-06
*/
/*
* Copyright (C) 2011-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/env.h>
#include <base/attached_rom_dataspace.h>
/* GDB monitor includes */
#include "app_child.h"
#include "cpu_thread_component.h"
#include "genode_child_resources.h"
/* libc includes */
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "genode-low.h"
#include "server.h"
#include "linux-low.h"
static bool verbose = false;
Genode::Env *genode_env;
/*
* 'waitpid()' is implemented using 'select()'. When a new thread is created,
* 'select()' needs to unblock, so there is a dedicated pipe for that. The
* lwpid of the new thread needs to be read from the pipe in 'waitpid()', so
* that the next 'select()' call can block again. The lwpid needs to be stored
* in a variable until it is inquired later.
*/
static int _new_thread_pipe[2];
static unsigned long _new_thread_lwpid;
/*
* When 'waitpid()' reports a SIGTRAP, this variable stores the lwpid of the
* corresponding thread. This information is used in the initial breakpoint
* handler to let the correct thread handle the event.
*/
static unsigned long _sigtrap_lwpid;
using namespace Genode;
using namespace Gdb_monitor;
class Memory_model
{
private:
Mutex _mutex { };
Region_map_component &_address_space;
Region_map &_rm;
/**
* Representation of a currently mapped region
*/
struct Mapped_region
{
Region_map_component::Region *_region;
unsigned char *_local_base;
Mapped_region() : _region(0), _local_base(0) { }
bool valid() { return _region != 0; }
bool loaded(Region_map_component::Region const * region)
{
return _region == region;
}
void flush(Region_map &rm)
{
if (!valid()) return;
rm.detach(_local_base);
_local_base = 0;
_region = 0;
}
void load(Region_map_component::Region *region, Region_map &rm)
{
if (region == _region)
return;
if (!region || valid())
flush(rm);
if (!region)
return;
try {
_region = region;
_local_base = rm.attach(_region->ds_cap(),
0, _region->offset());
}
catch (Region_map::Region_conflict) {
flush(rm);
error(__func__, ": RM attach failed (region conflict)");
}
catch (Region_map::Invalid_dataspace) {
flush(rm);
error(__func__, ": RM attach failed (invalid dataspace)");
}
}
unsigned char *local_base() { return _local_base; }
};
enum { NUM_MAPPED_REGIONS = 1 };
Mapped_region _mapped_region[NUM_MAPPED_REGIONS];
unsigned _evict_idx = 0;
/**
* Return local address of mapped region
*
* The function returns 0 if the mapping fails
*/
unsigned char *_update_curr_region(Region_map_component::Region *region)
{
for (unsigned i = 0; i < NUM_MAPPED_REGIONS; i++) {
if (_mapped_region[i].loaded(region))
return _mapped_region[i].local_base();
}
/* flush one currently mapped region */
_evict_idx++;
if (_evict_idx == NUM_MAPPED_REGIONS)
_evict_idx = 0;
_mapped_region[_evict_idx].load(region, _rm);
return _mapped_region[_evict_idx].local_base();
}
public:
Memory_model(Region_map_component &address_space,
Region_map &rm)
:
_address_space(address_space),
_rm(rm)
{ }
unsigned char read(void *addr)
{
Mutex::Guard guard(_mutex);
addr_t offset_in_region = 0;
Region_map_component::Region *region =
_address_space.find_region(addr, &offset_in_region);
unsigned char *local_base = _update_curr_region(region);
if (!local_base) {
warning(__func__, ": no memory at address ", addr);
throw No_memory_at_address();
}
unsigned char value =
local_base[offset_in_region];
if (verbose)
log(__func__, ": read addr=", addr, ", value=", Hex(value));
return value;
}
void write(void *addr, unsigned char value)
{
if (verbose)
log(__func__, ": write addr=", addr, ", value=", Hex(value));
Mutex::Guard guard(_mutex);
addr_t offset_in_region = 0;
Region_map_component::Region *region =
_address_space.find_region(addr, &offset_in_region);
unsigned char *local_base = _update_curr_region(region);
if (!local_base) {
warning(__func__, ": no memory at address=", addr);
warning("(attempted to write ", Hex(value), ")");
throw No_memory_at_address();
}
local_base[offset_in_region] = value;
}
};
static Genode_child_resources *_genode_child_resources = 0;
static Memory_model *_memory_model = 0;
Genode_child_resources &genode_child_resources()
{
if (!_genode_child_resources) {
Genode::error("_genode_child_resources is not set");
abort();
}
return *_genode_child_resources;
}
/**
* Return singleton instance of memory model
*/
Memory_model &memory_model()
{
if (!_memory_model) {
Genode::error("_memory_model is not set");
abort();
}
return *_memory_model;
}
static void genode_stop_thread(unsigned long lwpid)
{
Cpu_session_component &csc = genode_child_resources().cpu_session_component();
Cpu_thread_component *cpu_thread = csc.lookup_cpu_thread(lwpid);
if (!cpu_thread) {
error(__PRETTY_FUNCTION__, ": "
"could not find CPU thread object for lwpid ", lwpid);
return;
}
cpu_thread->pause();
}
pid_t my_waitpid(pid_t pid, int *status, int flags)
{
extern int remote_desc;
fd_set readset;
Cpu_session_component &csc = genode_child_resources().cpu_session_component();
while(1) {
FD_ZERO (&readset);
if (remote_desc != -1)
FD_SET (remote_desc, &readset);
if (pid == -1) {
FD_SET(_new_thread_pipe[0], &readset);
Thread_capability thread_cap = csc.first();
while (thread_cap.valid()) {
FD_SET(csc.signal_pipe_read_fd(thread_cap), &readset);
thread_cap = csc.next(thread_cap);
}
} else {
FD_SET(csc.signal_pipe_read_fd(csc.thread_cap(pid)), &readset);
}
struct timeval wnohang_timeout = {0, 0};
struct timeval *timeout = (flags & WNOHANG) ? &wnohang_timeout : NULL;
/* TODO: determine the highest fd in the set for optimization */
int res = select(FD_SETSIZE, &readset, 0, 0, timeout);
if (res > 0) {
if ((remote_desc != -1) && FD_ISSET(remote_desc, &readset)) {
/* received input from GDB */
int cc;
char c = 0;
cc = read (remote_desc, &c, 1);
if (cc == 1 && c == '\003' && current_thread != NULL) {
/* this causes a SIGINT to be delivered to one of the threads */
the_target->request_interrupt();
continue;
} else {
if (verbose)
log("input_interrupt, "
"count=", cc, " c=", c, " ('", Char(c), "%c')");
}
} else if (FD_ISSET(_new_thread_pipe[0], &readset)) {
/*
* Linux 'ptrace(2)' manual text related to the main thread:
*
* "If the PTRACE_O_TRACEEXEC option is not in effect, all
* successful calls to execve(2) by the traced process will
* cause it to be sent a SIGTRAP signal, giving the parent a
* chance to gain control before the new program begins
* execution."
*
* Linux 'ptrace' manual text related to other threads:
*
* "PTRACE_O_CLONE
* ...
* A waitpid(2) by the tracer will return a status value such
* that
*
* status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8))
*
* The PID of the new process can be retrieved with
* PTRACE_GETEVENTMSG."
*/
*status = W_STOPCODE(SIGTRAP);
read(_new_thread_pipe[0], &_new_thread_lwpid,
sizeof(_new_thread_lwpid));
if (_new_thread_lwpid != GENODE_MAIN_LWPID) {
*status |= (PTRACE_EVENT_CLONE << 16);
genode_stop_thread(GENODE_MAIN_LWPID);
}
return GENODE_MAIN_LWPID;
} else {
/* received a signal */
Thread_capability thread_cap = csc.first();
while (thread_cap.valid()) {
if (FD_ISSET(csc.signal_pipe_read_fd(thread_cap), &readset))
break;
thread_cap = csc.next(thread_cap);
}
if (!thread_cap.valid())
continue;
int signal;
read(csc.signal_pipe_read_fd(thread_cap), &signal, sizeof(signal));
unsigned long lwpid = csc.lwpid(thread_cap);
if (verbose)
log("thread ", lwpid, " received signal ", signal);
if (signal == SIGTRAP) {
_sigtrap_lwpid = lwpid;
} else if (signal == SIGSTOP) {
/*
* Check if a SIGTRAP is pending
*
* This can happen if a single-stepped thread gets paused while gdbserver
* handles a signal of a different thread and the exception signal after
* the single step has not arrived yet. In this case, the SIGTRAP must be
* delivered first, otherwise gdbserver would single-step the thread again.
*/
Cpu_thread_component *cpu_thread = csc.lookup_cpu_thread(lwpid);
Thread_state thread_state = cpu_thread->state();
if (thread_state.exception) {
/* resend the SIGSTOP signal */
csc.send_signal(cpu_thread->cap(), SIGSTOP);
continue;
}
} else if (signal == SIGINFO) {
if (verbose)
log("received SIGINFO for new lwpid ", lwpid);
/*
* First signal of a new thread. On Genode originally a
* SIGTRAP, but gdbserver expects SIGSTOP.
*/
signal = SIGSTOP;
}
*status = W_STOPCODE(signal);
return lwpid;
}
} else {
return res;
}
}
}
extern "C" long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data)
{
const char *request_str = 0;
switch (request) {
case PTRACE_TRACEME: request_str = "PTRACE_TRACEME"; break;
case PTRACE_PEEKTEXT: request_str = "PTRACE_PEEKTEXT"; break;
case PTRACE_PEEKUSER: request_str = "PTRACE_PEEKUSER"; break;
case PTRACE_POKETEXT: request_str = "PTRACE_POKETEXT"; break;
case PTRACE_POKEUSER: request_str = "PTRACE_POKEUSER"; break;
case PTRACE_CONT: request_str = "PTRACE_CONT"; break;
case PTRACE_KILL: request_str = "PTRACE_KILL"; break;
case PTRACE_SINGLESTEP: request_str = "PTRACE_SINGLESTEP"; break;
case PTRACE_GETREGS: request_str = "PTRACE_GETREGS"; break;
case PTRACE_SETREGS: request_str = "PTRACE_SETREGS"; break;
case PTRACE_ATTACH: request_str = "PTRACE_ATTACH"; break;
case PTRACE_DETACH: request_str = "PTRACE_DETACH"; break;
case PTRACE_GETSIGINFO: request_str = "PTRACE_GETSIGINFO"; break;
case PTRACE_GETEVENTMSG:
/*
* Only PTRACE_EVENT_CLONE is currently supported.
*/
*(unsigned long*)data = _new_thread_lwpid;
return 0;
case PTRACE_GETREGSET: request_str = "PTRACE_GETREGSET"; break;
}
warning("ptrace(", request_str, " (", Hex(request), ")) called - not implemented!");
errno = EINVAL;
return -1;
}
extern "C" int vfork()
{
/* create the thread announcement pipe */
if (pipe(_new_thread_pipe) != 0) {
error("could not create the 'new thread' pipe");
return -1;
}
/* extract target filename from config file */
typedef String<32> Filename;
Filename filename { };
Genode::Attached_rom_dataspace config { *genode_env, "config" };
try {
Xml_node const target = config.xml().sub_node("target");
if (!target.has_attribute("name")) {
error("missing 'name' attribute of '<target>' sub node");
return -1;
}
filename = target.attribute_value("name", Filename());
} catch (Xml_node::Nonexistent_sub_node) {
error("missing '<target>' sub node");
return -1;
}
/* extract target node from config file */
Xml_node target_node = config.xml().sub_node("target");
/*
* preserve the configured amount of memory for gdb_monitor and give the
* remainder to the child
*/
Number_of_bytes preserved_ram_quota = 0;
try {
Xml_node preserve_node = config.xml().sub_node("preserve");
if (preserve_node.attribute("name").has_value("RAM"))
preserve_node.attribute("quantum").value(preserved_ram_quota);
else
throw Xml_node::Exception();
} catch (...) {
error("could not find a valid <preserve> config node");
return -1;
}
Number_of_bytes ram_quota = genode_env->pd().avail_ram().value - preserved_ram_quota;
Cap_quota const avail_cap_quota = genode_env->pd().avail_caps();
Genode::size_t const preserved_caps = 100;
if (avail_cap_quota.value < preserved_caps) {
error("not enough available caps for preservation of ", preserved_caps);
return -1;
}
Cap_quota const cap_quota { avail_cap_quota.value - preserved_caps };
/* start the application */
static Heap alloc(genode_env->ram(), genode_env->rm());
enum { SIGNAL_EP_STACK_SIZE = 2*1024*sizeof(addr_t) };
static Entrypoint signal_ep { *genode_env, SIGNAL_EP_STACK_SIZE,
"sig_handler", Affinity::Location() };
int breakpoint_len = 0;
unsigned char const *breakpoint_data =
the_target->sw_breakpoint_from_kind(0, &breakpoint_len);
App_child *child = new (alloc) App_child(*genode_env,
alloc,
filename.string(),
Ram_quota{ram_quota},
cap_quota,
signal_ep,
target_node,
_new_thread_pipe[1],
breakpoint_len,
breakpoint_data);
_genode_child_resources = child->genode_child_resources();
static Memory_model memory_model(genode_child_resources().region_map_component(), genode_env->rm());
_memory_model = &memory_model;
try { child->start(); }
catch (Out_of_caps) { error("out of caps during child startup"); return -1; }
catch (Out_of_ram) { error("out of RAM during child startup"); return -1; }
catch (Service_denied) { error("service denied during child startup"); return -1; }
catch (...) { error("could not start child process"); return -1; }
return GENODE_MAIN_LWPID;
}
extern "C" int kill(pid_t pid, int sig)
{
Cpu_session_component &csc = genode_child_resources().cpu_session_component();
if (pid <= 0) pid = GENODE_MAIN_LWPID;
Thread_capability thread_cap = csc.thread_cap(pid);
if (!thread_cap.valid()) {
error(__PRETTY_FUNCTION__, ": "
"could not find thread capability for pid ", pid);
return -1;
}
return csc.send_signal(thread_cap, sig);
}
extern "C" int initial_breakpoint_handler(CORE_ADDR addr)
{
Cpu_session_component &csc = genode_child_resources().cpu_session_component();
return csc.handle_initial_breakpoint(_sigtrap_lwpid);
}
void genode_set_initial_breakpoint_at(unsigned long addr)
{
set_breakpoint_at(addr, initial_breakpoint_handler);
}
void genode_remove_thread(unsigned long lwpid)
{
struct thread_info *thread_info =
find_thread_ptid(ptid_t(GENODE_MAIN_LWPID, lwpid, 0));
lwp_info *lwp = get_thread_lwp(thread_info);
the_linux_target->detach_one_lwp(lwp);
}
void genode_stop_all_threads()
{
Cpu_session_component &csc = genode_child_resources().cpu_session_component();
csc.pause_all_threads();
}
void genode_resume_all_threads()
{
Cpu_session_component &csc = genode_child_resources().cpu_session_component();
csc.resume_all_threads();
}
static int genode_detach(int)
{
genode_resume_all_threads();
return 0;
}
int linux_process_target::detach(process_info *process)
{
return genode_detach(process->pid);
}
int linux_process_target::kill(process_info *process)
{
/* TODO */
if (verbose) warning(__func__, " not implemented, just detaching instead...");
return genode_detach(process->pid);
}
void genode_continue_thread(unsigned long lwpid, int single_step)
{
Cpu_session_component &csc = genode_child_resources().cpu_session_component();
Cpu_thread_component *cpu_thread = csc.lookup_cpu_thread(lwpid);
if (!cpu_thread) {
error(__func__, ": " "could not find CPU thread object for lwpid ", lwpid);
return;
}
cpu_thread->single_step(single_step);
cpu_thread->resume();
}
void linux_process_target::fetch_registers(regcache *regcache, int regno)
{
const struct regs_info *regs_info = get_regs_info();
unsigned long reg_content = 0;
if (regno == -1) {
for (regno = 0; regno < regs_info->usrregs->num_regs; regno++) {
if (genode_fetch_register(regno, &reg_content) == 0)
supply_register(regcache, regno, &reg_content);
else
supply_register(regcache, regno, 0);
}
} else {
if (genode_fetch_register(regno, &reg_content) == 0)
supply_register(regcache, regno, &reg_content);
else
supply_register(regcache, regno, 0);
}
}
void linux_process_target::store_registers(regcache *regcache, int regno)
{
if (verbose) log(__func__, ": regno=", regno);
const struct regs_info *regs_info = get_regs_info();
unsigned long reg_content = 0;
if (regno == -1) {
for (regno = 0; regno < regs_info->usrregs->num_regs; regno++) {
if ((Genode::size_t)register_size(regcache->tdesc, regno) <=
sizeof(reg_content)) {
collect_register(regcache, regno, &reg_content);
genode_store_register(regno, reg_content);
}
}
} else {
if ((Genode::size_t)register_size(regcache->tdesc, regno) <=
sizeof(reg_content)) {
collect_register(regcache, regno, &reg_content);
genode_store_register(regno, reg_content);
}
}
}
unsigned char genode_read_memory_byte(void *addr)
{
return memory_model().read(addr);
}
int genode_read_memory(CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
if (verbose)
log(__func__, "(", Hex(memaddr), ", ", myaddr, ", ", len, ")");
if (myaddr)
try {
for (int i = 0; i < len; i++)
myaddr[i] = genode_read_memory_byte((void*)((unsigned long)memaddr + i));
} catch (No_memory_at_address) {
return EFAULT;
}
return 0;
}
int linux_process_target::read_memory(CORE_ADDR memaddr,
unsigned char *myaddr, int len)
{
return genode_read_memory(memaddr, myaddr, len);
}
void genode_write_memory_byte(void *addr, unsigned char value)
{
memory_model().write(addr, value);
}
int genode_write_memory(CORE_ADDR memaddr, const unsigned char *myaddr, int len)
{
if (verbose)
log(__func__, "(", Hex(memaddr), ", ", myaddr, ", ", len, ")");
if (myaddr && (len > 0)) {
if (debug_threads) {
/* Dump up to four bytes. */
unsigned int val = * (unsigned int *) myaddr;
if (len == 1)
val = val & 0xff;
else if (len == 2)
val = val & 0xffff;
else if (len == 3)
val = val & 0xffffff;
fprintf(stderr, "Writing %0*x to 0x%08lx", 2 * ((len < 4) ? len : 4),
val, (long)memaddr);
}
for (int i = 0; i < len; i++)
try {
genode_write_memory_byte((void*)((unsigned long)memaddr + i), myaddr[i]);
} catch (No_memory_at_address) {
return EFAULT;
}
}
return 0;
}
int linux_process_target::write_memory(CORE_ADDR memaddr,
const unsigned char *myaddr, int len)
{
return genode_write_memory(memaddr, myaddr, len);
}
LONGEST linux_common_xfer_osdata(char const*, gdb_byte*, ULONGEST, ULONGEST)
{
Genode::error(__func__, " called, not implemented");
return -1;
}
int linux_common_core_of_thread(ptid_t)
{
return 0;
}
bool linux_process_target::supports_qxfer_libraries_svr4()
{
return false;
}
int linux_process_target::qxfer_libraries_svr4(const char *,
unsigned char *,
unsigned const char *,
CORE_ADDR, int)
{
Genode::error(__func__, " called, not implemented");
return -1;
}
char *linux_proc_pid_to_exec_file(int)
{
return nullptr;
}
ssize_t linux_mntns_readlink(pid_t, const char *, char *, size_t)
{
Genode::error(__func__, " called, not implemented");
return -1;
}
int linux_mntns_unlink (pid_t, const char *)
{
Genode::error(__func__, " called, not implemented");
return -1;
}
int linux_mntns_open_cloexec(pid_t, const char *, int, mode_t)
{
Genode::error(__func__, " called, not implemented");
return -1;
}
const char *linux_proc_tid_get_name(ptid_t)
{
return "";
}

View File

@ -1,47 +0,0 @@
/*
* \brief Genode backend for GDBServer
* \author Christian Prochaska
* \date 2011-05-06
*/
/*
* Copyright (C) 2011-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/*
* Based on gdbserver/linux-low.h
*/
#ifndef GENODE_LOW_H
#define GENODE_LOW_H
#include <sys/types.h>
#include "server.h"
/* exception type */
struct No_memory_at_address { };
/* interface for linux-low.c */
void genode_stop_all_threads();
void genode_continue_thread(unsigned long lwpid, int single_step);
int genode_kill(int pid);
int genode_detach(int pid);
int genode_read_memory(CORE_ADDR memaddr, unsigned char *myaddr, int len);
int genode_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len);
/* interface for genode-low.cc and low.cc */
int genode_fetch_register(int regno, unsigned long *reg_content);
void genode_store_register(int regno, unsigned long reg_content);
unsigned char genode_read_memory_byte(void *addr);
/* interface for cpu_thread_component.cc */
void genode_set_initial_breakpoint_at(unsigned long addr);
#endif /* GENODE_LOW_H */

View File

@ -1,39 +0,0 @@
/*
* \brief Genode-specific configuration
* \author Christian Prochaska
* \date 2011-05-06
*/
/*
* Copyright (C) 2011-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _GDBSERVER_CONFIG_H_
#define _GDBSERVER_CONFIG_H_
#define HAVE_ARPA_INET_H 1
#define HAVE_ERRNO_H 1
#define HAVE_FCNTL_H 1
#define HAVE_NETDB_H 1
#define HAVE_NETINET_IN_H 1
#define HAVE_NETINET_TCP_H 1
#define HAVE_SIGNAL_H 1
#define HAVE_SOCKLEN_T 1
#define HAVE_STRING_H 1
#define HAVE_SYS_SOCKET_H 1
#define HAVE_UNISTD_H 1
#define HAVE_LINUX_REGSETS 1
#define HAVE_LINUX_USRREGS 1
#ifndef __GENODE__
#define __GENODE__
#endif
/* first process id */
#define GENODE_MAIN_LWPID 1
#endif /* _GDBSERVER_CONFIG_H_ */

View File

@ -1,64 +0,0 @@
/*
* \brief Genode child resources provided to GDB monitor
* \author Christian Prochaska
* \date 2011-03-10
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _GENODE_CHILD_RESOURCES_H_
#define _GENODE_CHILD_RESOURCES_H_
#include "region_map_component.h"
extern "C" void abort();
namespace Gdb_monitor {
class Cpu_session_component;
class Genode_child_resources;
}
class Gdb_monitor::Genode_child_resources
{
private:
Cpu_session_component *_cpu_session_component = 0;
Region_map_component *_region_map_component = 0;
public:
void cpu_session_component(Cpu_session_component *cpu_session_component)
{
_cpu_session_component = cpu_session_component;
}
void region_map_component(Region_map_component *region_map_component)
{
_region_map_component = region_map_component;
}
Cpu_session_component &cpu_session_component()
{
if (!_cpu_session_component) {
Genode::error("_cpu_session_component is not set");
abort();
}
return *_cpu_session_component;
}
Region_map_component &region_map_component()
{
if (!_region_map_component) {
Genode::error("_region_map_component is not set");
abort();
}
return *_region_map_component;
}
};
#endif /* _GENODE_CHILD_RESOURCES_H_ */

View File

@ -1,46 +0,0 @@
/*
* \brief GDB Monitor
* \author Christian Prochaska
* \date 2010-09-16
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <libc/component.h>
/*
* Suppress messages of libc dummy functions
*/
extern "C" int _sigaction() { return -1; }
extern "C" int getpid() { return -1; }
extern "C" int sigprocmask() { return -1; }
extern "C" int _sigprocmask() { return -1; }
extern "C" int sigsuspend() { return -1; }
/*
* version.c
*/
extern "C" const char version[] = "8.1.1";
extern "C" const char host_name[] = "";
extern int gdbserver_main(int argc, char *argv[]);
extern Genode::Env *genode_env;
void Libc::Component::construct(Libc::Env &env)
{
genode_env = &env;
int argc = 3;
char *argv[] = { "gdbserver", "/dev/terminal", "target", 0 };
Libc::with_libc([&] () {
gdbserver_main(argc, argv);
});
}

View File

@ -1,158 +0,0 @@
/*
* \brief Core-specific instance of the PD session interface
* \author Norman Feske
* \date 2016-04-20
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _PD_SESSION_COMPONENT_H_
#define _PD_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/rpc_server.h>
#include <pd_session/connection.h>
/* GDB monitor includes */
#include "region_map_component.h"
namespace Gdb_monitor {
using namespace Genode;
class Pd_session_component;
typedef Local_service<Pd_session_component> Pd_service;
}
class Gdb_monitor::Pd_session_component : public Rpc_object<Pd_session>
{
private:
Rpc_entrypoint &_ep;
Allocator &_alloc;
Pd_connection _pd;
Region_map_component _address_space;
Region_map_component _stack_area;
Region_map_component _linker_area;
public:
/**
* Constructor
*/
Pd_session_component(Rpc_entrypoint &ep,
Env &env,
Allocator &alloc,
char const *binary_name,
Dataspace_pool &managed_ds_map)
:
_ep(ep),
_alloc(alloc),
_pd(env, binary_name),
_address_space(_ep, _alloc, managed_ds_map, _pd.rpc_cap(), _pd.address_space()),
_stack_area (_ep, _alloc, managed_ds_map, _pd.rpc_cap(), _pd.stack_area()),
_linker_area (_ep, _alloc, managed_ds_map, _pd.rpc_cap(), _pd.linker_area())
{
_ep.manage(this);
}
~Pd_session_component()
{
_ep.dissolve(this);
}
/**
* Accessor used to let the GDB monitor access the PD's address
* space
*/
Region_map_component &region_map() { return _address_space; }
Pd_session_capability core_pd_cap() { return _pd.cap(); }
/**************************
** Pd_session interface **
**************************/
void assign_parent(Capability<Parent> parent) override {
_pd.assign_parent(parent); }
bool assign_pci(addr_t addr, uint16_t bdf) override {
return _pd.assign_pci(addr, bdf); }
void map(addr_t virt, addr_t size) override {
return _pd.map(virt, size); }
Signal_source_capability alloc_signal_source() override {
return _pd.alloc_signal_source(); }
void free_signal_source(Signal_source_capability cap) override {
_pd.free_signal_source(cap); }
Capability<Signal_context> alloc_context(Signal_source_capability source,
unsigned long imprint) override {
return _pd.alloc_context(source, imprint); }
void free_context(Capability<Signal_context> cap) override {
_pd.free_context(cap); }
void submit(Capability<Signal_context> context, unsigned cnt) override {
_pd.submit(context, cnt); }
Native_capability alloc_rpc_cap(Native_capability ep) override {
return _pd.alloc_rpc_cap(ep); }
void free_rpc_cap(Native_capability cap) override {
_pd.free_rpc_cap(cap); }
Capability<Region_map> address_space() override {
return _address_space.Rpc_object<Region_map>::cap(); }
Capability<Region_map> stack_area() override {
return _stack_area.Rpc_object<Region_map>::cap(); }
Capability<Region_map> linker_area() override {
return _linker_area.Rpc_object<Region_map>::cap(); }
void ref_account(Capability<Pd_session> pd) override {
_pd.ref_account(pd); }
void transfer_quota(Capability<Pd_session> pd, Cap_quota amount) override {
warning("Pd_session::transfer_quota not implemented"); }
Cap_quota cap_quota() const override { return _pd.cap_quota(); }
Cap_quota used_caps() const override { return _pd.used_caps(); }
Alloc_result try_alloc(size_t amount, Cache cache) override {
return _pd.try_alloc(amount, cache); }
void free(Ram_dataspace_capability ds) override { _pd.free(ds); }
size_t dataspace_size(Ram_dataspace_capability ds) const override {
return _pd.dataspace_size(ds); }
void transfer_quota(Pd_session_capability pd, Ram_quota amount) override {
_pd.transfer_quota(pd, amount); }
Ram_quota ram_quota() const override { return _pd.ram_quota(); }
Ram_quota used_ram() const override { return _pd.used_ram(); }
Capability<Native_pd> native_pd() override {
return _pd.native_pd(); }
Capability<System_control> system_control_cap(Affinity::Location const location) override {
return _pd.system_control_cap(location); }
addr_t dma_addr(Ram_dataspace_capability ds) override {
return _pd.dma_addr(ds); }
Attach_dma_result attach_dma(Dataspace_capability ds, addr_t at) override {
return _pd.attach_dma(ds, at); }
};
#endif /* _PD_SESSION_COMPONENT_H_ */

View File

@ -1,136 +0,0 @@
/*
* \brief Implementation of the region map interface
* \author Christian Prochaska
* \date 2011-05-06
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/env.h>
#include <dataspace/client.h>
#include <util/retry.h>
/* local includes */
#include "region_map_component.h"
/**************************************
** Region map component **
**************************************/
using namespace Gdb_monitor;
Region_map_component::Region *Region_map_component::find_region(void *local_addr, addr_t *offset_in_region)
{
Mutex::Guard guard(_region_map_mutex);
Region *first = _region_map.first();
Region *region = first ? first->find_by_addr(local_addr) : 0;
if (!region)
return 0;
*offset_in_region = ((addr_t)local_addr - (addr_t)region->start());
_managed_ds_map.apply(region->ds_cap(), [&] (Dataspace_object *managed_ds_obj) {
if (managed_ds_obj)
region =
managed_ds_obj->region_map_component()->find_region((void*)*offset_in_region,
offset_in_region);
});
return region;
}
Region_map::Local_addr
Region_map_component::attach(Dataspace_capability ds_cap, size_t size,
off_t offset, bool use_local_addr,
Region_map::Local_addr local_addr,
bool executable, bool const writeable)
{
size_t ds_size = Dataspace_client(ds_cap).size();
if (offset < 0 || (size_t)offset >= ds_size) {
warning("offset outside of dataspace");
throw Region_conflict();
}
if (size == 0)
size = ds_size - offset;
else if (size > ds_size - offset) {
warning("size bigger than remainder of dataspace");
throw Region_conflict();
}
void *addr = _parent_region_map.attach(ds_cap, size, offset,
use_local_addr, local_addr,
executable, writeable);
Mutex::Guard guard(_region_map_mutex);
_region_map.insert(new (_alloc) Region(addr, (void*)((addr_t)addr + size - 1), ds_cap, offset));
return addr;
}
void Region_map_component::detach(Region_map::Local_addr local_addr)
{
_parent_region_map.detach(local_addr);
Mutex::Guard guard(_region_map_mutex);
Region *region = _region_map.first()->find_by_addr(local_addr);
if (!region) {
warning("address not in region map");
return;
}
_region_map.remove(region);
destroy(_alloc, region);
}
void Region_map_component::fault_handler(Signal_context_capability handler)
{
_parent_region_map.fault_handler(handler);
}
Region_map::State Region_map_component::state()
{
return _parent_region_map.state();
}
Dataspace_capability Region_map_component::dataspace()
{
Dataspace_capability ds_cap = _parent_region_map.dataspace();
_managed_ds_map.insert(new (_alloc) Dataspace_object(ds_cap, this));
return ds_cap;
}
Region_map_component::Region_map_component(Rpc_entrypoint &ep,
Allocator &alloc,
Dataspace_pool &managed_ds_map,
Pd_session_capability pd,
Capability<Region_map> parent_region_map)
:
_ep(ep),
_alloc(alloc),
_pd(pd),
_parent_region_map(parent_region_map),
_managed_ds_map(managed_ds_map)
{
_ep.manage(this);
}
Region_map_component::~Region_map_component()
{
_ep.dissolve(this);
}

View File

@ -1,116 +0,0 @@
/*
* \brief Region map interface
* \author Christian Helmuth
* \author Norman Feske
* \date 2006-07-17
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _REGION_MAP_COMPONENT_H_
#define _REGION_MAP_COMPONENT_H_
/* Genode includes */
#include <base/rpc_server.h>
#include <base/allocator.h>
#include <pd_session/capability.h>
#include <region_map/client.h>
/* GDB monitor includes */
#include "dataspace_object.h"
namespace Gdb_monitor {
using namespace Genode;
class Region_map_component : public Rpc_object<Region_map>
{
private:
Rpc_entrypoint &_ep;
Allocator &_alloc;
Pd_session_capability _pd;
Region_map_client _parent_region_map;
public:
class Region : public Genode::Avl_node<Region>
{
private:
void *_start;
void *_end;
off_t _offset;
Dataspace_capability _ds_cap;
public:
Region(void *start, void *end, Dataspace_capability ds_cap, Genode::off_t offset)
: _start(start), _end(end), _offset(offset), _ds_cap(ds_cap) {}
bool higher(Region *e) { return e->_start > _start; }
Region *find_by_addr(void *addr)
{
if ((addr >= _start) && (addr <= _end))
return this;
Region *region = child(addr > _start);
return region ? region->find_by_addr(addr) : 0;
}
void *start() { return _start; }
off_t offset() { return _offset; }
Dataspace_capability ds_cap() { return _ds_cap; }
};
private:
Avl_tree<Region> _region_map;
Mutex _region_map_mutex;
Dataspace_pool &_managed_ds_map;
public:
/**
* Constructor
*/
Region_map_component(Rpc_entrypoint &ep,
Allocator &alloc,
Dataspace_pool &managed_ds_map,
Pd_session_capability pd,
Capability<Region_map> parent_region_map);
~Region_map_component();
/**
* Find region for given address
*
* \param local_addr lookup address
* \param offset_in_region translated address in looked up region
*/
Region *find_region(void *local_addr, addr_t *offset_in_region);
/**************************************
** Region manager session interface **
**************************************/
Local_addr attach (Dataspace_capability, size_t,
off_t, bool, Local_addr, bool,
bool) override;
void detach (Local_addr) override;
void fault_handler (Signal_context_capability) override;
State state () override;
Dataspace_capability dataspace () override;
};
}
#endif /* _REGION_MAP_COMPONENT_H_ */

View File

@ -1,140 +0,0 @@
/*
* \brief ROM service
* \author Christian Helmuth
* \date 2011-09-16
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _ROM_H_
#define _ROM_H_
/* Genode includes */
#include <base/service.h>
#include <dataspace/client.h>
#include <root/component.h>
#include <rom_session/connection.h>
#include <util/arg_string.h>
namespace Gdb_monitor
{
using namespace Genode;
class Rom_session_component;
class Local_rom_factory;
typedef Local_service<Rom_session_component> Rom_service;
}
/**
* ROM session backed by RAM dataspace copy of original ROM
*/
class Gdb_monitor::Rom_session_component : public Rpc_object<Rom_session>
{
private:
Env &_env;
Rpc_entrypoint &_ep;
Capability<Ram_dataspace> _clone_cap;
/**
* Clone a ROM dataspace in RAM
*/
Capability<Ram_dataspace> _clone_rom(Capability<Rom_dataspace> rom_cap)
{
using namespace Genode;
size_t rom_size = Dataspace_client(rom_cap).size();
Capability<Ram_dataspace> clone_cap = _env.ram().alloc(rom_size);
if (!clone_cap.valid()) {
error(__func__, ": memory allocation for cloned dataspace failed");
return Capability<Ram_dataspace>();
}
void *rom = _env.rm().attach(rom_cap);
void *clone = _env.rm().attach(clone_cap);
memcpy(clone, rom, rom_size);
_env.rm().detach(rom);
_env.rm().detach(clone);
return clone_cap;
}
public:
Rom_session_component(Env &env,
Rpc_entrypoint &ep,
char const *filename)
: _env(env),
_ep(ep),
_clone_cap(_clone_rom(Rom_connection(env, filename).dataspace()))
{ _ep.manage(this); }
~Rom_session_component()
{
_env.ram().free(_clone_cap);
_ep.dissolve(this);
}
/***************************
** ROM session interface **
***************************/
Rom_dataspace_capability dataspace() override
{
return static_cap_cast<Rom_dataspace>(
static_cap_cast<Dataspace>(_clone_cap));
}
void sigh(Signal_context_capability) override { }
};
class Gdb_monitor::Local_rom_factory : public Rom_service::Factory
{
private:
Env &_env;
Rpc_entrypoint &_ep;
Allocator &_alloc;
public:
Local_rom_factory(Env &env, Rpc_entrypoint &ep, Allocator &alloc)
: _env(env), _ep(ep), _alloc(alloc) { }
/***********************
** Factory interface **
***********************/
Rom_session_component &create(Args const &args, Affinity) override
{
Session_label const label = label_from_args(args.string());
return *new (_alloc)
Rom_session_component(_env,
_ep,
label.last_element().string());
}
void upgrade(Rom_session_component &, Args const &) override { }
void destroy(Rom_session_component &session) override
{
Genode::destroy(_alloc, &session);
}
};
#endif /* _ROM_H_ */

View File

@ -1,36 +0,0 @@
/*
* \brief Signal handler thread implementation
* \author Christian Prochaska
* \date 2011-08-15
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include "signal_handler_thread.h"
using namespace Genode;
using namespace Gdb_monitor;
Signal_handler_thread::Signal_handler_thread(Env &env,
Signal_receiver &receiver)
:
Thread(env, "sig_handler", SIGNAL_HANDLER_THREAD_STACK_SIZE),
_signal_receiver(receiver) { }
void Signal_handler_thread::entry()
{
while(1) {
Signal s = _signal_receiver.wait_for_signal();
Signal_dispatcher_base *signal_dispatcher =
dynamic_cast<Signal_dispatcher_base*>(s.context());
signal_dispatcher->dispatch(s.num());
}
};

View File

@ -1,40 +0,0 @@
/*
* \brief Signal handler thread
* \author Christian Prochaska
* \date 2011-08-15
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SIGNAL_HANDLER_THREAD_H_
#define _SIGNAL_HANDLER_THREAD_H_
#include <base/signal.h>
#include <base/thread.h>
namespace Gdb_monitor {
using namespace Genode;
enum { SIGNAL_HANDLER_THREAD_STACK_SIZE = 2*1024*sizeof(addr_t) };
class Signal_handler_thread : public Thread
{
private:
Signal_receiver &_signal_receiver;
public:
Signal_handler_thread(Env &env, Signal_receiver &receiver);
void entry();
};
}
#endif /* _SIGNAL_HANDLER_THREAD_H_ */

View File

@ -1,135 +0,0 @@
TARGET = gdb_monitor
GDB_CONTRIB_DIR := $(call select_from_ports,gdb)/src/noux-pkg/gdb
INC_DIR += $(GDB_CONTRIB_DIR)/include \
$(GDB_CONTRIB_DIR) \
$(GDB_CONTRIB_DIR)/gdb \
$(GDB_CONTRIB_DIR)/gdb/regformats \
$(GDB_CONTRIB_DIR)/gdbserver \
$(GDB_CONTRIB_DIR)/gnulib/import \
$(REP_DIR)/src/lib/gdbserver_libc_support \
$(PRG_DIR)/gdbserver \
$(PRG_DIR)/gdbsupport \
$(PRG_DIR)
LIBS = base stdcxx libc \
gdbserver_platform gdbserver_libc_support
# libiberty
SRC_C = argv.c \
concat.c \
crc32.c \
xstrdup.c
# gnulib
SRC_C += rawmemchr.c \
strchrnul.c
# gdbserver
SRC_CC += gdbserver/ax.cc \
gdbserver/debug.cc \
gdbserver/dll.cc \
gdbserver/fork-child.cc \
gdbserver/hostio.cc \
gdbserver/i387-fp.cc \
gdbserver/inferiors.cc \
gdbserver/linux-low.cc \
gdbserver/mem-break.cc \
gdbserver/notif.cc \
gdbserver/regcache.cc \
gdbserver/remote-utils.cc \
gdbserver/server.cc \
gdbserver/symbol.cc \
gdbserver/target.cc \
gdbserver/tdesc.cc \
gdbserver/tracepoint.cc \
gdbserver/utils.cc \
gdbserver/x86-low.cc
# gdbsupport
SRC_CC += gdbsupport/agent.cc \
gdbsupport/buffer.cc \
gdbsupport/cleanups.cc \
gdbsupport/common-debug.cc \
gdbsupport/common-exceptions.cc \
gdbsupport/common-inferior.cc \
gdbsupport/common-utils.cc \
gdbsupport/environ.cc \
gdbsupport/errors.cc \
gdbsupport/event-loop.cc \
gdbsupport/event-pipe.cc \
gdbsupport/fileio.cc \
gdbsupport/filestuff.cc \
gdbsupport/format.cc \
gdbsupport/gdb_tilde_expand.cc \
gdbsupport/gdb_vecs.cc \
gdbsupport/job-control.cc \
gdbsupport/netstuff.cc \
gdbsupport/pathstuff.cc \
gdbsupport/print-utils.cc \
gdbsupport/ptid.cc \
gdbsupport/rsp-low.cc \
gdbsupport/safe-strerror.cc \
gdbsupport/search.cc \
gdbsupport/signals.cc \
gdbsupport/tdesc.cc \
gdbsupport/xml-utils.cc
# gdb
SRC_CC += nat/fork-inferior.cc \
nat/linux-ptrace.cc \
nat/x86-dregs.cc \
target/waitstatus.cc \
alloc.cc
# genode
SRC_CC += genode-low.cc \
cpu_session_component.cc \
cpu_thread_component.cc \
region_map_component.cc \
signal_handler_thread.cc \
main.cc
CC_OPT += -DGDBSERVER -DPKGVERSION="\"10.2\"" -DREPORT_BUGS_TO="\"\""
CC_OPT += -DHAVE_SYS_WAIT_H -DHAVE_SYS_PTRACE_H -DHAVE_DECL_PTRACE -DHAVE_TERMIOS
CC_OPT += -fpermissive -Wno-unused-function
vpath %.c $(GDB_CONTRIB_DIR)/gnulib/import
vpath %.c $(GDB_CONTRIB_DIR)/libiberty
vpath %.cc $(GDB_CONTRIB_DIR)/gdb
vpath %.cc $(GDB_CONTRIB_DIR)
vpath %.cc $(PRG_DIR)/gdbserver
#
# Files from sandbox library
#
# Because the 'server.h' file exists both in gdb and in init and both gdb's
# 'server.c' and init's 'server.cc' are compiled to a 'server.o' file, the
# parent directory of the init source is used as reference.
#
SANDBOX_SRC_DIR = $(call select_from_repositories,src/lib/sandbox)
SANDBOX_PARENT_DIR = $(abspath $(addsuffix /..,$(SANDBOX_SRC_DIR)))
SRC_CC += sandbox/server.cc
# needed to compile gdbserver/genode-low.cc
INC_DIR += $(SANDBOX_PARENT_DIR)
vpath sandbox/%.cc $(SANDBOX_PARENT_DIR)
#
# Import headers needed from sandbox library, but exclude server.h because it
# collides with the GDB server's server.h
#
SANDBOX_HEADERS := $(notdir $(wildcard $(addsuffix /*.h,$(SANDBOX_SRC_DIR))))
SANDBOX_HEADERS := $(filter-out server.h,$(SANDBOX_HEADERS))
genode-low.o sandbox/server.o: $(SANDBOX_HEADERS)
%.h: $(SANDBOX_SRC_DIR)/%.h
ln -sf $< $@
CC_CXX_WARN_STRICT =
CC_CXX_OPT_STD = -std=gnu++17

View File

@ -1,32 +0,0 @@
/*
* \brief Dummy declarations of Linux-specific libc types and macros
* needed to build gdbserver
* \author Christian Prochaska
* \date 2011-09-01
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef GDBSERVER_LIBC_DUMMIES_H
#define GDBSERVER_LIBC_DUMMIES_H
/* Missing in libc's elf.h */
#define AT_HWCAP 16 /* Machine dependent hints about
processor capabilities. */
/* Missing in libc's sys/signal.h */
struct siginfo { };
/* sys/user.h */
struct user {
unsigned long int u_debugreg [8];
};
#endif /* GDBSERVER_LIBC_DUMMIES_H */

View File

@ -1,25 +0,0 @@
/*
* \brief Dummy declarations of Linux-specific libc types and macros
* needed to build gdbserver
* \author Christian Prochaska
* \date 2011-09-01
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef SYS_PROCFS_H
#define SYS_PROCFS_H
typedef enum {
PS_OK,
PS_ERR,
} ps_err_e;
struct ps_prochandle { };
#endif /* SYS_PROCFS_H */

View File

@ -1,45 +0,0 @@
/*
* \brief Dummy declarations of Linux-specific libc types and functions
* needed to build gdbserver
* \author Christian Prochaska
* \date 2011-09-01
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef SYS_PTRACE_H
#define SYS_PTRACE_H
#include <sys/types.h>
#include <gdbserver_libc_support.h>
enum __ptrace_request {
PTRACE_TRACEME = 0,
PTRACE_PEEKTEXT = 1,
PTRACE_PEEKUSER = 3,
PTRACE_POKETEXT = 4,
PTRACE_POKEUSER = 6,
PTRACE_CONT = 7,
PTRACE_KILL = 8,
PTRACE_SINGLESTEP = 9,
PTRACE_GETREGS = 12,
PTRACE_SETREGS = 13,
PTRACE_ATTACH = 16,
PTRACE_DETACH = 17,
PTRACE_EVENT_CLONE = 3,
PTRACE_GETEVENTMSG = 0x4201,
PTRACE_GETREGSET = 0x4204,
};
extern "C" long ptrace (enum __ptrace_request, pid_t, void*, void*);
#endif /* SYS_PTRACE_H */

View File

@ -1,19 +0,0 @@
/*
* \brief Dummy header needed to build gdbserver
* \author Christian Prochaska
* \date 2011-09-01
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef SYS_VFS_H
#define SYS_VFS_H
#include <sys/mount.h>
#endif /* SYS_VFS_H */

View File

@ -1,37 +0,0 @@
/*
* \brief Linux-specific types for gdbserver
* \author Christian Prochaska
* \date 2016-03-16
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef SYS_WAIT_H
#define SYS_WAIT_H
#define WNOHANG 1
#define __WCLONE 0x80000000
#define WIFEXITED(status) (status == 0)
#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
#define WIFSIGNALED(status) (!WIFEXITED(status) && !WIFSTOPPED(status))
#define WEXITSTATUS(status) ((status >> 8) & 0xff)
#define WSTOPSIG(status) ((status >> 8) & 0xff)
#define WTERMSIG(status) (status & 0x7f)
#define W_STOPCODE(sig) ((sig) << 8 | 0x7f)
__BEGIN_DECLS
pid_t waitpid(pid_t pid, int *status, int flags);
__END_DECLS
#endif /* SYS_WAIT_H */

View File

@ -1,84 +0,0 @@
/*
* \brief NOVA-specific 'Native_cpu' setup
* \author Christian Prochaska
* \date 2016-05-13
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <nova_native_cpu/client.h>
/* GDB monitor includes */
#include "cpu_session_component.h"
#include "cpu_thread_component.h"
namespace Gdb_monitor {
class Native_cpu_component;
}
using namespace Genode;
class Gdb_monitor::Native_cpu_component : public Rpc_object<Cpu_session::Native_cpu,
Native_cpu_component>
{
private:
Cpu_session_component &_cpu_session_component;
Nova_native_cpu_client _nova_native_cpu;
public:
Native_cpu_component(Cpu_session_component &cpu_session_component)
: _cpu_session_component(cpu_session_component),
_nova_native_cpu(_cpu_session_component.parent_cpu_session().native_cpu())
{
_cpu_session_component.thread_ep().manage(this);
}
~Native_cpu_component()
{
_cpu_session_component.thread_ep().dissolve(this);
}
void thread_type(Thread_capability thread_cap,
Cpu_session::Native_cpu::Thread_type thread_type,
Cpu_session::Native_cpu::Exception_base exception_base) override
{
auto lambda = [&] (Cpu_thread_component *cpu_thread) {
_nova_native_cpu.thread_type(cpu_thread->parent_thread_cap(),
thread_type, exception_base);
};
_cpu_session_component.thread_ep().apply(thread_cap, lambda);
}
};
Capability<Cpu_session::Native_cpu>
Gdb_monitor::Cpu_session_component::_setup_native_cpu()
{
Native_cpu_component *native_cpu_component =
new (_md_alloc) Native_cpu_component(*this);
return native_cpu_component->cap();
}
void Gdb_monitor::Cpu_session_component::_cleanup_native_cpu()
{
Native_cpu_component *native_cpu_component = nullptr;
_ep.apply(_native_cpu_cap, [&] (Native_cpu_component *c) { native_cpu_component = c; });
if (!native_cpu_component) return;
destroy(_md_alloc, native_cpu_component);
}

View File

@ -1,88 +0,0 @@
/*
* \brief NOVA (x86_32) specific helper functions for GDB server
* \author Alexander Boettcher
* \date 2012-08-09
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include "i386.h"
#include "cpu_session_component.h"
#include "gdbserver_platform_helper.h"
#include "genode_child_resources.h"
using namespace Genode;
int genode_fetch_register(int regno, unsigned long *value)
{
Thread_state ts;
try { ts = get_current_thread_state(); }
catch (...) {
error(__PRETTY_FUNCTION__, ": could not get current thread state");
return -1;
}
switch((enum reg_index)regno)
{
case EAX: fetch_register("EAX", ts.eax, *value); return 0;
case ECX: fetch_register("ECX", ts.ecx, *value); return 0;
case EDX: fetch_register("EDX", ts.edx, *value); return 0;
case EBX: fetch_register("EBX", ts.ebx, *value); return 0;
case UESP: fetch_register("ESP", ts.sp, *value); return 0;
case EBP: fetch_register("EBP", ts.ebp, *value); return 0;
case ESI: fetch_register("ESI", ts.esi, *value); return 0;
case EDI: fetch_register("EDI", ts.edi, *value); return 0;
case EIP: fetch_register("EIP", ts.ip, *value); return 0;
case EFL: fetch_register("EFL", ts.eflags, *value); return 0;
case CS: cannot_fetch_register("CS"); return -1;
case SS: cannot_fetch_register("SS"); return -1;
case DS: cannot_fetch_register("DS"); return -1;
case ES: cannot_fetch_register("ES"); return -1;
case FS: cannot_fetch_register("FS"); return -1;
case GS: cannot_fetch_register("GS"); return -1;
default: error("unhandled register ", regno); return -1;
}
return -1;
}
void genode_store_register(int regno, unsigned long value)
{
Thread_state ts;
try { ts = get_current_thread_state(); }
catch (...) {
error(__PRETTY_FUNCTION__, ": could not get current thread state");
return;
}
switch ((enum reg_index)regno)
{
case EAX: if (!store_register("EAX", ts.eax, value)) return; break;
case ECX: if (!store_register("ECX", ts.ecx, value)) return; break;
case EDX: if (!store_register("EDX", ts.edx, value)) return; break;
case EBX: if (!store_register("EBX", ts.ebx, value)) return; break;
case UESP: if (!store_register("ESP", ts.sp, value)) return; break;
case EBP: if (!store_register("EBP", ts.ebp, value)) return; break;
case ESI: if (!store_register("ESI", ts.esi, value)) return; break;
case EDI: if (!store_register("EDI", ts.edi, value)) return; break;
case EIP: if (!store_register("EIP", ts.ip, value)) return; break;
case EFL: if (!store_register("EFL", ts.eflags, value)) return; break;
case CS: cannot_store_register("CS", value); return;
case SS: cannot_store_register("SS", value); return;
case DS: cannot_store_register("DS", value); return;
case ES: cannot_store_register("ES", value); return;
case FS: cannot_store_register("FS", value); return;
case GS: cannot_store_register("GS", value); return;
default: error("unhandled register ", regno); return;
}
set_current_thread_state(ts);
}

View File

@ -1,171 +0,0 @@
/*
* \brief NOVA (x86_64) specific helper functions for GDB server
* \author Alexander Boettcher
* \author Christian Prochaska
* \date 2014-01-30
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include "amd64.h"
#include "cpu_session_component.h"
#include "gdbserver_platform_helper.h"
#include "genode_child_resources.h"
using namespace Genode;
int genode_fetch_register(int regno, unsigned long *value)
{
Thread_state ts;
try { ts = get_current_thread_state(); }
catch (...) {
error(__PRETTY_FUNCTION__, ": could not get current thread state");
return -1;
}
switch((enum reg_index)regno)
{
case RAX: fetch_register("RAX", ts.rax, *value); return 0;
case RBX: fetch_register("RBX", ts.rbx, *value); return 0;
case RCX: fetch_register("RCX", ts.rcx, *value); return 0;
case RDX: fetch_register("RDX", ts.rdx, *value); return 0;
case RSI: fetch_register("RSI", ts.rsi, *value); return 0;
case RDI: fetch_register("RDI", ts.rdi, *value); return 0;
case RBP: fetch_register("RBP", ts.rbp, *value); return 0;
case RSP: fetch_register("RSP", ts.sp, *value); return 0;
case R8: fetch_register("R8 ", ts.r8, *value); return 0;
case R9: fetch_register("R9 ", ts.r9, *value); return 0;
case R10: fetch_register("R10", ts.r10, *value); return 0;
case R11: fetch_register("R11", ts.r11, *value); return 0;
case R12: fetch_register("R12", ts.r12, *value); return 0;
case R13: fetch_register("R13", ts.r13, *value); return 0;
case R14: fetch_register("R14", ts.r14, *value); return 0;
case R15: fetch_register("R15", ts.r15, *value); return 0;
case RIP: fetch_register("RIP", ts.ip, *value); return 0;
case EFLAGS: fetch_register("RFL", ts.eflags, *value); return 0;
case CS: cannot_fetch_register("CS"); return -1;
case SS: cannot_fetch_register("SS"); return -1;
case DS: cannot_fetch_register("DS"); return -1;
case ES: cannot_fetch_register("ES"); return -1;
case FS: cannot_fetch_register("FS"); return -1;
case GS: cannot_fetch_register("GS"); return -1;
case ST0: cannot_fetch_register("ST0"); return -1;
case ST1: cannot_fetch_register("ST1"); return -1;
case ST2: cannot_fetch_register("ST2"); return -1;
case ST3: cannot_fetch_register("ST3"); return -1;
case ST4: cannot_fetch_register("ST4"); return -1;
case ST5: cannot_fetch_register("ST5"); return -1;
case ST6: cannot_fetch_register("ST6"); return -1;
case ST7: cannot_fetch_register("ST7"); return -1;
case FCTRL: cannot_fetch_register("FCTRL"); return -1;
case FSTAT: cannot_fetch_register("FSTAT"); return -1;
case FTAG: cannot_fetch_register("FTAG"); return -1;
case FISEG: cannot_fetch_register("FISEG"); return -1;
case FIOFF: cannot_fetch_register("FIOFF"); return -1;
case FOSEG: cannot_fetch_register("FOSEG"); return -1;
case FOOFF: cannot_fetch_register("FOOFF"); return -1;
case FOP: cannot_fetch_register("FOP"); return -1;
case XMM0: cannot_fetch_register("XMM0"); return -1;
case XMM1: cannot_fetch_register("XMM1"); return -1;
case XMM2: cannot_fetch_register("XMM2"); return -1;
case XMM3: cannot_fetch_register("XMM3"); return -1;
case XMM4: cannot_fetch_register("XMM4"); return -1;
case XMM5: cannot_fetch_register("XMM5"); return -1;
case XMM6: cannot_fetch_register("XMM6"); return -1;
case XMM7: cannot_fetch_register("XMM7"); return -1;
case XMM8: cannot_fetch_register("XMM8"); return -1;
case XMM9: cannot_fetch_register("XMM9"); return -1;
case XMM10: cannot_fetch_register("XMM10"); return -1;
case XMM11: cannot_fetch_register("XMM11"); return -1;
case XMM12: cannot_fetch_register("XMM12"); return -1;
case XMM13: cannot_fetch_register("XMM13"); return -1;
case XMM14: cannot_fetch_register("XMM14"); return -1;
case XMM15: cannot_fetch_register("XMM15"); return -1;
case MXCSR: cannot_fetch_register("MXCSR"); return -1;
default: error("unhandled register ", regno); return -1;
}
return -1;
}
void genode_store_register(int regno, unsigned long value)
{
Thread_state ts;
try { ts = get_current_thread_state(); }
catch (...) {
error(__PRETTY_FUNCTION__, ": could not get current thread state");
return;
}
switch ((enum reg_index)regno)
{
case RAX: if (!store_register("RAX", ts.rax, value)) return; break;
case RBX: if (!store_register("RBX", ts.rbx, value)) return; break;
case RCX: if (!store_register("RCX", ts.rcx, value)) return; break;
case RDX: if (!store_register("RDX", ts.rdx, value)) return; break;
case RSI: if (!store_register("RSI", ts.rsi, value)) return; break;
case RDI: if (!store_register("RDI", ts.rdi, value)) return; break;
case RBP: if (!store_register("RBP", ts.rbp, value)) return; break;
case RSP: if (!store_register("RSP", ts.sp, value)) return; break;
case R8: if (!store_register("R8 ", ts.r8, value)) return; break;
case R9: if (!store_register("R9 ", ts.r9, value)) return; break;
case R10: if (!store_register("R10", ts.r10, value)) return; break;
case R11: if (!store_register("R11", ts.r11, value)) return; break;
case R12: if (!store_register("R12", ts.r12, value)) return; break;
case R13: if (!store_register("R13", ts.r13, value)) return; break;
case R14: if (!store_register("R14", ts.r14, value)) return; break;
case R15: if (!store_register("R15", ts.r15, value)) return; break;
case RIP: if (!store_register("RIP", ts.ip, value)) return; break;
case EFLAGS: if (!store_register("RFL", ts.eflags, value)) return; break;
case CS: cannot_store_register("CS", value); return;
case SS: cannot_store_register("SS", value); return;
case DS: cannot_store_register("DS", value); return;
case ES: cannot_store_register("ES", value); return;
case FS: cannot_store_register("FS", value); return;
case GS: cannot_store_register("GS", value); return;
case ST0: cannot_store_register("ST0", value); return;
case ST1: cannot_store_register("ST1", value); return;
case ST2: cannot_store_register("ST2", value); return;
case ST3: cannot_store_register("ST3", value); return;
case ST4: cannot_store_register("ST4", value); return;
case ST5: cannot_store_register("ST5", value); return;
case ST6: cannot_store_register("ST6", value); return;
case ST7: cannot_store_register("ST7", value); return;
case FCTRL: cannot_store_register("FCTRL", value); return;
case FSTAT: cannot_store_register("FSTAT", value); return;
case FTAG: cannot_store_register("FTAG", value); return;
case FISEG: cannot_store_register("FISEG", value); return;
case FIOFF: cannot_store_register("FIOFF", value); return;
case FOSEG: cannot_store_register("FOSEG", value); return;
case FOOFF: cannot_store_register("FOOFF", value); return;
case FOP: cannot_store_register("FOP", value); return;
case XMM0: cannot_store_register("XMM0", value); return;
case XMM1: cannot_store_register("XMM1", value); return;
case XMM2: cannot_store_register("XMM2", value); return;
case XMM3: cannot_store_register("XMM3", value); return;
case XMM4: cannot_store_register("XMM4", value); return;
case XMM5: cannot_store_register("XMM5", value); return;
case XMM6: cannot_store_register("XMM6", value); return;
case XMM7: cannot_store_register("XMM7", value); return;
case XMM8: cannot_store_register("XMM8", value); return;
case XMM9: cannot_store_register("XMM9", value); return;
case XMM10: cannot_store_register("XMM10", value); return;
case XMM11: cannot_store_register("XMM11", value); return;
case XMM12: cannot_store_register("XMM12", value); return;
case XMM13: cannot_store_register("XMM13", value); return;
case XMM14: cannot_store_register("XMM14", value); return;
case XMM15: cannot_store_register("XMM15", value); return;
case MXCSR: cannot_store_register("MXCSR", value); return;
default: error("unhandled register ", regno); return;
}
set_current_thread_state(ts);
}

View File

@ -1,94 +0,0 @@
/*
* \brief Genode backend for GDBServer - helper functions
* \author Christian Prochaska
* \date 2011-07-07
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <cpu_thread/client.h>
#include "cpu_session_component.h"
#include "genode_child_resources.h"
#include "server.h"
#include "linux-low.h"
#include "genode-low.h"
using namespace Genode;
using namespace Gdb_monitor;
extern Genode_child_resources *genode_child_resources();
static constexpr bool verbose = false;
Thread_state get_current_thread_state()
{
Cpu_session_component &csc = genode_child_resources()->cpu_session_component();
ptid_t ptid = current_thread->id;
Cpu_thread_client cpu_thread(csc.thread_cap(ptid.lwp()));
return cpu_thread.state();
}
void set_current_thread_state(Thread_state thread_state)
{
Cpu_session_component &csc = genode_child_resources()->cpu_session_component();
ptid_t ptid = current_thread->id;
Cpu_thread_client cpu_thread(csc.thread_cap(ptid.lwp()));
cpu_thread.state(thread_state);
}
void fetch_register(const char *reg_name,
addr_t thread_state_reg,
unsigned long &value)
{
value = thread_state_reg;
if (verbose)
log(__func__, ": ", reg_name, " = ", Hex(value));
}
void cannot_fetch_register(const char *reg_name)
{
if (verbose)
log("cannot fetch register ", reg_name);
}
bool store_register(const char *reg_name,
addr_t &thread_state_reg,
unsigned long value)
{
if (verbose)
log(__func__, ": ", reg_name, " = ", Hex(value));
if (thread_state_reg == value)
return false;
thread_state_reg = value;
return true;
}
void cannot_store_register(const char *reg_name, unsigned long value)
{
if (verbose)
log("cannot set contents of register ", reg_name, " (", Hex(value), ")");
}

View File

@ -1,36 +0,0 @@
/*
* \brief Genode backend for GDBServer - helper functions
* \author Christian Prochaska
* \date 2011-07-07
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef GDBSERVER_PLATFORM_HELPER_H
#define GDBSERVER_PLATFORM_HELPER_H
/**
* \throw Cpu_session::State_access_failed
*/
Genode::Thread_state get_current_thread_state();
void set_current_thread_state(Genode::Thread_state thread_state);
void fetch_register(const char *reg_name,
Genode::addr_t thread_state_reg,
unsigned long &value);
void cannot_fetch_register(const char *reg_name);
bool store_register(const char *reg_name,
Genode::addr_t &thread_state_reg,
unsigned long value);
void cannot_store_register(const char *reg_name, unsigned long value);
#endif /* GDBSERVER_PLATFORM_HELPER_H */

View File

@ -1,47 +0,0 @@
/*
* \brief Genode backend for GDB server (ARM-specific code)
* \author Christian Prochaska
* \date 2011-08-01
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef REG_ARM_H
#define REG_ARM_H
/* indices from 'regs_arm' array in reg-arm.c */
enum reg_index {
R0 = 0,
R1 = 1,
R2 = 2,
R3 = 3,
R4 = 4,
R5 = 5,
R6 = 6,
R7 = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
SP = 13,
LR = 14,
PC = 15,
F0 = 16,
F1 = 17,
F2 = 18,
F3 = 19,
F4 = 20,
F5 = 21,
F6 = 22,
F7 = 23,
FPS = 24,
CPSR = 25,
};
#endif /* REG_ARM_H */

View File

@ -1,37 +0,0 @@
/*
* \brief Genode backend for GDB server (x86_32-specific code)
* \author Christian Prochaska
* \date 2011-07-07
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef I386_LINUX_H
#define I386_LINUX_H
/* indices from 'regs_i386' array in i386.c */
enum reg_index {
EAX = 0,
ECX = 1,
EDX = 2,
EBX = 3,
UESP = 4,
EBP = 5,
ESI = 6,
EDI = 7,
EIP = 8,
EFL = 9,
CS = 10,
SS = 11,
DS = 12,
ES = 13,
FS = 14,
GS = 15,
};
#endif /* I386_LINUX_H */

View File

@ -1,78 +0,0 @@
/*
* \brief Genode backend for GDB server (x86_64-specific code)
* \author Christian Prochaska
* \date 2014-01-30
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef AMD64_H
#define AMD64_H
/* indices from 'regs_amd64' array in amd64.c */
enum reg_index {
RAX = 0,
RBX = 1,
RCX = 2,
RDX = 3,
RSI = 4,
RDI = 5,
RBP = 6,
RSP = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15,
RIP = 16,
EFLAGS = 17,
CS = 18,
SS = 19,
DS = 20,
ES = 21,
FS = 22,
GS = 23,
ST0 = 24,
ST1 = 25,
ST2 = 26,
ST3 = 27,
ST4 = 28,
ST5 = 29,
ST6 = 30,
ST7 = 31,
FCTRL = 32,
FSTAT = 33,
FTAG = 34,
FISEG = 35,
FIOFF = 36,
FOSEG = 37,
FOOFF = 38,
FOP = 39,
XMM0 = 40,
XMM1 = 41,
XMM2 = 42,
XMM3 = 43,
XMM4 = 44,
XMM5 = 45,
XMM6 = 46,
XMM7 = 47,
XMM8 = 48,
XMM9 = 49,
XMM10 = 50,
XMM11 = 51,
XMM12 = 52,
XMM13 = 53,
XMM14 = 54,
XMM15 = 55,
MXCSR = 56,
};
#endif /* AMD64_H */

View File

@ -1,83 +0,0 @@
/*
* \brief GDB Monitor test
* \author Christian Prochaska
* \date 2011-05-24
*/
/*
* Copyright (C) 2011-2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <pthread.h>
#include <stdio.h>
/* a variable to be modified with GDB */
int test_var = 1;
void test_thread_step()
{
/* nothing */
}
void test_thread_sigsegv()
{
*(int *)0 = 42;
}
void *test_thread_start(void*)
{
test_thread_step();
test_thread_sigsegv();
return nullptr;
}
/*
* This function returns the current value of 'test_var' + 1 and can be called from
* GDB using the 'call' or 'print' commands
*/
int test_var_func()
{
return test_var + 1;
}
/* this function returns a value to make itself appear in the stack trace when building with -O2 */
int func2()
{
/* set the first breakpoint here to test the 'backtrace' command for a
* thread which is not in a syscall */
puts("in func2()\n");
/* call 'test_var_func()', so the compiler does not throw the function away */
printf("test_var_func() returned %d\n", test_var_func());
return 0;
}
/* this function returns a value to make itself appear in the stack trace when building with -O2 */
int func1()
{
func2();
return 0;
}
int main()
{
func1();
pthread_t test_thread;
pthread_create(&test_thread, nullptr, test_thread_start, nullptr);
pthread_join(test_thread, nullptr);
return 0;
}

View File

@ -1,7 +0,0 @@
TARGET = test-gdb_monitor
SRC_CC = main.cc
LIBS = posix
CC_OLEVEL = -O0
CC_CXX_WARN_STRICT =

View File

@ -1,28 +0,0 @@
/*
* \brief GDB Monitor target config test
* \author Christian Prochaska
* \date 2012-04-16
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/log.h>
void Component::construct(Genode::Env & env)
{
Genode::Attached_rom_dataspace config(env, "config");
try {
config.xml().sub_node("test_config_subnode");
Genode::log("Test succeeded");
} catch (Genode::Xml_node::Nonexistent_sub_node) {
Genode::error("missing '<test_config_subnode>' sub node");
}
}

View File

@ -1,5 +0,0 @@
TARGET = test-gdb_monitor_target_config
SRC_CC = main.cc
LIBS = base
CC_CXX_WARN_STRICT =

View File

@ -17,7 +17,6 @@ fetchurl_lwip
fetchurl_lxip
fs_query
gdb
gdb_monitor
hello
ieee754
init_smp