mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
parent
9a049789de
commit
ff5a474e74
@ -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'.
|
||||
|
@ -1,3 +0,0 @@
|
||||
INC_DIR += $(REP_DIR)/src/lib/gdbserver_libc_support
|
||||
|
||||
CC_CXX_WARN_STRICT =
|
@ -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
|
@ -1,3 +0,0 @@
|
||||
SHARED_LIB = yes
|
||||
|
||||
CC_CXX_WARN_STRICT =
|
@ -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 =
|
@ -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
|
@ -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 =
|
@ -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
|
@ -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
|
@ -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
|
||||
}
|
@ -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 :
|
@ -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 :
|
@ -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 :
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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(); }
|
@ -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_ */
|
@ -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();
|
||||
}
|
@ -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_ */
|
@ -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_ */
|
@ -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, ®_content) == 0)
|
||||
supply_register(regcache, regno, ®_content);
|
||||
else
|
||||
supply_register(regcache, regno, 0);
|
||||
}
|
||||
} else {
|
||||
if (genode_fetch_register(regno, ®_content) == 0)
|
||||
supply_register(regcache, regno, ®_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, ®_content);
|
||||
genode_store_register(regno, reg_content);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((Genode::size_t)register_size(regcache->tdesc, regno) <=
|
||||
sizeof(reg_content)) {
|
||||
collect_register(regcache, regno, ®_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 "";
|
||||
}
|
@ -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 */
|
@ -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_ */
|
@ -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 ®ion_map_component()
|
||||
{
|
||||
if (!_region_map_component) {
|
||||
Genode::error("_region_map_component is not set");
|
||||
abort();
|
||||
}
|
||||
return *_region_map_component;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _GENODE_CHILD_RESOURCES_H_ */
|
@ -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);
|
||||
});
|
||||
}
|
@ -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 ®ion_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_ */
|
@ -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);
|
||||
}
|
@ -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_ */
|
@ -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_ */
|
@ -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());
|
||||
}
|
||||
};
|
@ -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_ */
|
@ -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
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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), ")");
|
||||
}
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
TARGET = test-gdb_monitor
|
||||
SRC_CC = main.cc
|
||||
LIBS = posix
|
||||
|
||||
CC_OLEVEL = -O0
|
||||
|
||||
CC_CXX_WARN_STRICT =
|
@ -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");
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
TARGET = test-gdb_monitor_target_config
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
||||
|
||||
CC_CXX_WARN_STRICT =
|
@ -17,7 +17,6 @@ fetchurl_lwip
|
||||
fetchurl_lxip
|
||||
fs_query
|
||||
gdb
|
||||
gdb_monitor
|
||||
hello
|
||||
ieee754
|
||||
init_smp
|
||||
|
Loading…
Reference in New Issue
Block a user