1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

GDB support w/new toolchain and UART driver (#5559)

* Add full gdb support with uart/Serial integration

* Fix GDB merge errors

* Update to unpatched GDB protocol specification

It appears that Espressif patched the open source xtensa GDB port in
order to build their old GDB executable and their old gdbstub (basically
removing any register in a generic xtensa and only leaving those
present in the chip they synthesized).  Their GDBStub also assumed this
behavior.

Unpatched upstream GNU GDB now expects all the registers in
xtensa-config.c to be sent/read on a 'g' command.  Change the GDB stub
to send "xxxxxxxx"s (legal per the spec) for unimplemented registers.
This makes the 'g' response much longer, but it's results are cached
and in an interactive debugger it isn't noticeable.

* Fix .iram.literal to come before .iram.text for GDB

* Move functions to flash, call using wrappers

All functions which are not interrupt or exception called are now in
flash. A small IRAM wrapper enables flash when processing main GDB ops
by calling Cache_Read_Enable_New() and then jumping to the main flash
code.  This seems to work for catching exceptions, data and code breaks,
and Ctrl-C.

The UART ISR handler and exception handler register-saving bits of
code in ASM are still in IRAM.

GDB IRAM usage is now about 670 bytes.

* Remove LWIP2 builder commit

* Add documentation and gdbstub_init header

Add some simple GDB documentation to the main tree showing a worked
example.

Adds the definition of `void gdbstub_init()` to <GDBStub.h>

* Clean up GDB include and library dir

Replace GDBstub.h with the version in the internal/ directory, and
adjust stub code accordingly.  This way, only one copy of a file called
"GDBstub.h" will exist.

Update the gdbcommands and replace the obsolete ESPRESSIF readme with
@kylefleming's version since we're mainly doing serial, not TCP,
connected debugging.

Bump the library rev. number since this is a pretty big functionality
change.

Minor documentation tweak.

* Undo much of UART refactoring, set fifo IRQ to 16

Remove the refactoring of pin control and other little things not directly
related to GDB processing.  Should greatly reduce the diff size in uart.c.
Should also remove any register value changes (intended or otherwise)
introduced in the original PR from @kylefleming.

Set the FIFO interrupt to 16 chars when in GDB mode, matching the latest
UART configuration for highest speed.

* Add architecture comments, cleanup uart.c code

Comments added to UART.c trying to explain (as best as I understand it)
the changes done to support GDB and how they interact with standard
operation.

Fix the uart_uninit to stop the ISR and then free appropriately.

Fix uart_isr_handle_data (GDB's shim for sending chars to the 8266 app)
to do the exact same thing as the standard UART handler including set
the overflow properly and either discard or overwrite in that case.

Fix serial reception when GDB enabled by enabling the user recv ISR.

Remove commented attributes from gdbstub, leftover from the move to
flash.

General logic cleanup per comments in the PR.

* Also set the UART flags for HW error in GDB

Ensure we also check the UART flags and set the uart status
appropriately when in GDB mode.
This commit is contained in:
Earle F. Philhower, III 2019-01-23 20:51:35 +00:00 committed by Develo
parent 4657666319
commit bff3a6d963
16 changed files with 1411 additions and 691 deletions

View File

@ -25,12 +25,18 @@
value is in register, it doesn't hurt to return a bool, so that the value is in register, it doesn't hurt to return a bool, so that the
same stub can be used for gdb_present. */ same stub can be used for gdb_present. */
bool ICACHE_RAM_ATTR __gdb_no_op() static bool ICACHE_RAM_ATTR __gdb_no_op()
{ {
return false; return false;
} }
extern void gdb_init(void) __attribute__ ((weak, alias("__gdb_no_op"))); void gdb_init(void) __attribute__ ((weak, alias("__gdb_no_op")));
extern void gdb_do_break(void) __attribute__ ((weak, alias("__gdb_no_op"))); void gdb_do_break(void) __attribute__ ((weak, alias("__gdb_no_op")));
extern bool gdb_present(void) __attribute__ ((weak, alias("__gdb_no_op"))); bool gdb_present(void) __attribute__ ((weak, alias("__gdb_no_op")));
bool gdbstub_has_putc1_control(void) __attribute__ ((weak, alias("__gdb_no_op")));
void gdbstub_set_putc1_callback(void (*func)(char)) __attribute__ ((weak, alias("__gdb_no_op")));
bool gdbstub_has_uart_isr_control(void) __attribute__ ((weak, alias("__gdb_no_op")));
void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) __attribute__ ((weak, alias("__gdb_no_op")));
void gdbstub_write_char(char c) __attribute__ ((weak, alias("__gdb_no_op")));
void gdbstub_write(const char* buf, size_t size) __attribute__ ((weak, alias("__gdb_no_op")));

View File

@ -52,6 +52,71 @@ void gdb_do_break(void);
*/ */
bool gdb_present(void); bool gdb_present(void);
// If gdbstub has these set true, then we will disable our own
// usage of them, but use gdbstub's callbacks for them instead
/**
* @brief Check if GDB is installing a putc1 callback.
*
* By default, this function returns false. When GDBStub library is linked,
* this function is overriden and returns true.
*
* @return true if GDB is installing a putc1 callback
*/
bool gdbstub_has_putc1_control(void);
/**
* @brief Register a putc1 callback with GDB.
* @param func function GDB will proxy putc1 data to
*
* By default, this function is a no-op. When GDBStub library is linked,
* this function is overriden and sets GDB stub's secondary putc1 callback to
* func. When GDB stub is linked, but a GDB session is not current attached,
* then GDB stub will pass putc1 chars directly to this function.
*/
void gdbstub_set_putc1_callback(void (*func)(char));
/**
* @brief Check if GDB is installing a uart0 isr callback.
*
* By default, this function returns false. When GDBStub library is linked,
* this function is overriden and returns true.
*
* @return true if GDB is installing a uart0 isr callback
*/
bool gdbstub_has_uart_isr_control(void);
/**
* @brief Register a uart0 isr callback with GDB.
* @param func function GDB will proxy uart0 isr data to
*
* By default, this function is a no-op. When GDBStub library is linked,
* this function is overriden and sets GDB stub's secondary uart0 isr callback
* to func. When GDB stub is linked, but a GDB session is not current attached,
* then GDB stub will pass uart0 isr data back to this function.
*/
void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg);
/**
* @brief Write a character for output to a GDB session on uart0.
* @param c character to write
*
* By default, this function is a no-op. When GDBStub library is linked,
* this function is overriden and writes a char to either the GDB session on
* uart0 or directly to uart0 if not GDB session is attached.
*/
void gdbstub_write_char(char c);
/**
* @brief Write a char buffer for output to a GDB session on uart0.
* @param buf buffer of data to write
* @param size length of buffer
*
* By default, this function is a no-op. When GDBStub library is linked,
* this function is overriden and writes a buffer to either the GDB session on
* uart0 or directly to uart0 if not GDB session is attached.
*/
void gdbstub_write(const char* buf, size_t size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -47,7 +47,29 @@
#include "user_interface.h" #include "user_interface.h"
#include "uart_register.h" #include "uart_register.h"
//const char overrun_str [] PROGMEM STORE_ATTR = "uart input full!\r\n"; /*
Some general architecture for GDB integration with the UART to enable
serial debugging.
UART1 is transmit only and can never be used by GDB.
When gdbstub_has_uart_isr_control() (only true in the case GDB is enabled),
UART0 needs to be under the control of the GDB stub for enable/disable/irq
(but speed, parity, etc. still alllowable). Basically, GDB needs to make
sure that UART0 is never really disabled.
GDB sets up UART0 with a fifo and a 2-character timeout during init. This
is required to ensure that GDBStub can check every character coming in, even
if it is not read by the user app or if the commands don't hit the FIFO
interrupt level. It checks every character that comes in, and if GDB isn't
active just passes them to the user RAM FIFO unless it's a Ctrl-C (0x03).
GDBStub doesn't care about the struct uart_*, and allocating it or freeing
it has no effect (so you can do Serial.end() and free the uart...but as
mentioned above even if you Serial.end, the actual UART0 HW will still be
kept running to enable GDB to do commands.
*/
static int s_uart_debug_nr = UART0; static int s_uart_debug_nr = UART0;
@ -250,6 +272,54 @@ uart_read(uart_t* uart, char* userbuffer, size_t usersize)
return ret; return ret;
} }
// When GDB is running, this is called one byte at a time to stuff the user FIFO
// instead of the uart_isr...uart_rx_copy_fifo_to_buffer_unsafe()
// Since we've already read the bytes from the FIFO, can't use that
// function directly and need to implement it bytewise here
static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data)
{
uart_t* uart = (uart_t*)arg;
if(uart == NULL || !uart->rx_enabled) {
return;
}
// Copy all the rx fifo bytes that fit into the rx buffer
// called by ISR
struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer;
size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size;
if(nextPos == rx_buffer->rpos)
{
uart->rx_overrun = true;
//os_printf_plus(overrun_str);
// a choice has to be made here,
// do we discard newest or oldest data?
#ifdef UART_DISCARD_NEWEST
// discard newest data
// Stop copying if rx buffer is full
return;
#else
// discard oldest data
if (++rx_buffer->rpos == rx_buffer->size)
rx_buffer->rpos = 0;
#endif
}
rx_buffer->buffer[rx_buffer->wpos] = data;
rx_buffer->wpos = nextPos;
// Check the UART flags and note hardware overflow/etc.
uint32_t usis = USIS(uart->uart_nr);
if(usis & (1 << UIOF))
uart->rx_overrun = true;
if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO)))
uart->rx_error = true;
USIC(uart->uart_nr) = usis;
}
size_t size_t
uart_resize_rx_buffer(uart_t* uart, size_t new_size) uart_resize_rx_buffer(uart_t* uart, size_t new_size)
{ {
@ -286,7 +356,7 @@ uart_get_rx_buffer_size(uart_t* uart)
return uart && uart->rx_enabled? uart->rx_buffer->size: 0; return uart && uart->rx_enabled? uart->rx_buffer->size: 0;
} }
// The default ISR handler called when GDB is not enabled
void ICACHE_RAM_ATTR void ICACHE_RAM_ATTR
uart_isr(void * arg) uart_isr(void * arg)
{ {
@ -303,7 +373,7 @@ uart_isr(void * arg)
if(usis & (1 << UIFF)) if(usis & (1 << UIFF))
uart_rx_copy_fifo_to_buffer_unsafe(uart); uart_rx_copy_fifo_to_buffer_unsafe(uart);
if((usis & (1 << UIOF)) && !uart->rx_overrun) if(usis & (1 << UIOF))
{ {
uart->rx_overrun = true; uart->rx_overrun = true;
//os_printf_plus(overrun_str); //os_printf_plus(overrun_str);
@ -321,6 +391,11 @@ uart_start_isr(uart_t* uart)
if(uart == NULL || !uart->rx_enabled) if(uart == NULL || !uart->rx_enabled)
return; return;
if(gdbstub_has_uart_isr_control()) {
gdbstub_set_uart_isr_callback(uart_isr_handle_data, (void *)uart);
return;
}
// UCFFT value is when the RX fifo full interrupt triggers. A value of 1 // UCFFT value is when the RX fifo full interrupt triggers. A value of 1
// triggers the IRS very often. A value of 127 would not leave much time // triggers the IRS very often. A value of 127 would not leave much time
// for ISR to clear fifo before the next byte is dropped. So pick a value // for ISR to clear fifo before the next byte is dropped. So pick a value
@ -351,6 +426,11 @@ uart_stop_isr(uart_t* uart)
if(uart == NULL || !uart->rx_enabled) if(uart == NULL || !uart->rx_enabled)
return; return;
if(gdbstub_has_uart_isr_control()) {
gdbstub_set_uart_isr_callback(NULL, NULL);
return;
}
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
USC1(uart->uart_nr) = 0; USC1(uart->uart_nr) = 0;
USIC(uart->uart_nr) = 0xffff; USIC(uart->uart_nr) = 0xffff;
@ -358,8 +438,6 @@ uart_stop_isr(uart_t* uart)
ETS_UART_INTR_ATTACH(NULL, NULL); ETS_UART_INTR_ATTACH(NULL, NULL);
} }
/* /*
Reference for uart_tx_fifo_available() and uart_tx_fifo_full(): Reference for uart_tx_fifo_available() and uart_tx_fifo_full():
-Espressif Techinical Reference doc, chapter 11.3.7 -Espressif Techinical Reference doc, chapter 11.3.7
@ -393,6 +471,10 @@ uart_write_char(uart_t* uart, char c)
if(uart == NULL || !uart->tx_enabled) if(uart == NULL || !uart->tx_enabled)
return 0; return 0;
if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) {
gdbstub_write_char(c);
return 1;
}
uart_do_write_char(uart->uart_nr, c); uart_do_write_char(uart->uart_nr, c);
return 1; return 1;
} }
@ -403,6 +485,11 @@ uart_write(uart_t* uart, const char* buf, size_t size)
if(uart == NULL || !uart->tx_enabled) if(uart == NULL || !uart->tx_enabled)
return 0; return 0;
if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) {
gdbstub_write(buf, size);
return 0;
}
size_t ret = size; size_t ret = size;
const int uart_nr = uart->uart_nr; const int uart_nr = uart->uart_nr;
while (size--) while (size--)
@ -412,7 +499,6 @@ uart_write(uart_t* uart, const char* buf, size_t size)
} }
size_t size_t
uart_tx_free(uart_t* uart) uart_tx_free(uart_t* uart)
{ {
@ -452,8 +538,10 @@ uart_flush(uart_t* uart)
if(uart->tx_enabled) if(uart->tx_enabled)
tmp |= (1 << UCTXRST); tmp |= (1 << UCTXRST);
if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) {
USC0(uart->uart_nr) |= (tmp); USC0(uart->uart_nr) |= (tmp);
USC0(uart->uart_nr) &= ~(tmp); USC0(uart->uart_nr) &= ~(tmp);
}
} }
void void
@ -490,7 +578,9 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx
{ {
case UART0: case UART0:
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
if(!gdbstub_has_uart_isr_control()) {
ETS_UART_INTR_ATTACH(NULL, NULL); ETS_UART_INTR_ATTACH(NULL, NULL);
}
uart->rx_enabled = (mode != UART_TX_ONLY); uart->rx_enabled = (mode != UART_TX_ONLY);
uart->tx_enabled = (mode != UART_RX_ONLY); uart->tx_enabled = (mode != UART_RX_ONLY);
uart->rx_pin = (uart->rx_enabled)?3:255; uart->rx_pin = (uart->rx_enabled)?3:255;
@ -555,12 +645,21 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx
uart_set_baudrate(uart, baudrate); uart_set_baudrate(uart, baudrate);
USC0(uart->uart_nr) = config; USC0(uart->uart_nr) = config;
if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) {
uart_flush(uart); uart_flush(uart);
USC1(uart->uart_nr) = 0; USC1(uart->uart_nr) = 0;
USIC(uart->uart_nr) = 0xffff; USIC(uart->uart_nr) = 0xffff;
USIE(uart->uart_nr) = 0; USIE(uart->uart_nr) = 0;
if(uart->uart_nr == UART0 && uart->rx_enabled) }
if(uart->uart_nr == UART0) {
if(uart->rx_enabled) {
uart_start_isr(uart); uart_start_isr(uart);
}
if(gdbstub_has_uart_isr_control()) {
ETS_UART_INTR_ENABLE(); // Undo the disable in the switch() above
}
}
return uart; return uart;
} }
@ -573,16 +672,7 @@ uart_uninit(uart_t* uart)
uart_stop_isr(uart); uart_stop_isr(uart);
switch(uart->rx_pin) if(uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) {
{
case 3:
pinMode(3, INPUT);
break;
case 13:
pinMode(13, INPUT);
break;
}
switch(uart->tx_pin) switch(uart->tx_pin)
{ {
case 1: case 1:
@ -595,12 +685,22 @@ uart_uninit(uart_t* uart)
pinMode(15, INPUT); pinMode(15, INPUT);
break; break;
} }
}
if(uart->rx_enabled) if(uart->rx_enabled) {
{
uart_stop_isr(uart);
free(uart->rx_buffer->buffer); free(uart->rx_buffer->buffer);
free(uart->rx_buffer); free(uart->rx_buffer);
if(!gdbstub_has_uart_isr_control()) {
switch(uart->rx_pin)
{
case 3:
pinMode(3, INPUT);
break;
case 13:
pinMode(13, INPUT);
break;
}
}
} }
free(uart); free(uart);
} }
@ -654,7 +754,6 @@ uart_swap(uart_t* uart, int tx_pin)
IOSWAP &= ~(1 << IOSWAPU0); IOSWAP &= ~(1 << IOSWAPU0);
} }
break; break;
case UART1: case UART1:
// Currently no swap possible! See GPIO pins used by UART // Currently no swap possible! See GPIO pins used by UART
@ -798,22 +897,30 @@ void
uart_set_debug(int uart_nr) uart_set_debug(int uart_nr)
{ {
s_uart_debug_nr = uart_nr; s_uart_debug_nr = uart_nr;
void (*func)(char) = NULL;
switch(s_uart_debug_nr) switch(s_uart_debug_nr)
{ {
case UART0: case UART0:
system_set_os_print(1); func = &uart0_write_char;
ets_install_putc1((void *) &uart0_write_char);
break; break;
case UART1: case UART1:
system_set_os_print(1); func = &uart1_write_char;
ets_install_putc1((void *) &uart1_write_char);
break; break;
case UART_NO: case UART_NO:
default: default:
system_set_os_print(0); func = &uart_ignore_char;
ets_install_putc1((void *) &uart_ignore_char);
break; break;
} }
if(gdbstub_has_putc1_control()) {
gdbstub_set_putc1_callback(func);
} else {
if (uart_nr == UART0 || uart_nr == UART1) {
system_set_os_print(1);
} else {
system_set_os_print(0);
}
ets_install_putc1((void *) func);
}
} }
int int

365
doc/gdb.rst Normal file
View File

@ -0,0 +1,365 @@
Using GDB to Debug Applications
===============================
ESP applications can be debugged using GDB, the GNU debugger, which is
included with the standard IDE installation. This note will only discuss
the ESP specific steps, so please refer to the
`main GNU GDB documentation
<//sourceware.org/gdb/download/onlinedocs/gdb/index.html>`__.
Note that as of 2.5.0, the toolchain moved from the ESPRESSIF patched,
closed-source version of GDB to the main GNU version. The debugging
formats are different, so please be sure to use only the latest Arduino
toolchain GDB executable.
CLI and IDE Note
----------------
Because the Arduino IDE doesn't support interactive debugging, the following
sections describe debugging using the command line. Other IDEs which use
GDB in their debug backends should work identically, but you may need to
edit their configuration files or options to enable the remote serial
debugging required and to set the standard options. PRs are happily
accepted for updates to this document with additional IDEs!
Preparing your application for GDB
----------------------------------
Applications need to be changed to enable GDB debugging support. This
change will add 2-3KB of flash and around 700 bytes of IRAM usage, but
should not affect operation of the application.
In your main ``sketch.ino`` file, add the following line to the top of
the application:
.. code:: cpp
#include <GDBStub.h>
And in the ``void setup()`` function ensure the serial port is initialized
and call ``gdbstub_init()``:
.. code:: cpp
Serial.begin(115200);
gdbstub_init();
Rebuild and reupload your application and it should run exactly as before.
Starting a Debug Session
------------------------
Once your application is running, the process to attach a debugger is
quite simple:
. Close the Arduino Serial Monitor
. Locate Application.ino.elf File
. Open a Command Prompt and Start GDB
. Apply the GDB configurations
. Attach the Debugger
. Debug Away!
Close the Arduino Serial Monitor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Because GDB needs full control of the serial port, you will need to close
any Arduino Serial Monitor windows you may have open. Otherwise GDB will
report an error while attempting to debug.
Locate Application.ino.elf File
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order for GDB to debug your application, you need to locate the compiled
ELF format version of it (which includes needed debug symbols). Under Linux
these files are stored in ``/tmp/arduino_build_*`` and the following command
will help locate the right file for your app
.. code:: cpp
find /tmp -name "*.elf" -print
Note the full path of ELF file that corresponds to your sketch name, it will
be needed later once GDB is started.
Open a Command Prompt and Start GDB
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Open a terminal or ``CMD`` prompt and navigate to the proper ESP8266 toolchain
directory.
.. code:: cpp
~/.arduino15/packages/esp8266/hardware/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb
.. code:: cpp
cd TODO WINDOWS
xtensa-lx106-elf-gdb.exe
Please note the proper GDB name is "xtensa-lx106-elf-gdb". If you accidentally
run "gdb" you may start your own operating system's GDB, which will not know how
to talk to the ESP8266.
Apply the GDB Configurations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the ``(gdb)`` prompt, enter the following options to configure GDB for the
ESP8266 memory map and configuration:
.. code:: cpp
set remote hardware-breakpoint-limit 1
set remote hardware-watchpoint-limit 1
set remote interrupt-on-connect on
set remote kill-packet off
set remote symbol-lookup-packet off
set remote verbose-resume-packet off
mem 0x20000000 0x3fefffff ro cache
mem 0x3ff00000 0x3fffffff rw
mem 0x40000000 0x400fffff ro cache
mem 0x40100000 0x4013ffff rw cache
mem 0x40140000 0x5fffffff ro cache
mem 0x60000000 0x60001fff rw
set serial baud 115200
Now tell GDB where your compiled ELF file is located:
.. code:: cpp
file /tmp/arduino_build_257110/sketch_dec26a.ino.elf
Attach the Debugger
~~~~~~~~~~~~~~~~~~~
Once GDB has been configured properly and loaded your debugging symbols, connect
it to the ESP with the command (replace the ttyUSB0 or COM9 with your ESP's serial
port):
.. code:: cpp
target remote /dev/ttyUSB0
or
.. code:: cpp
target remote \\.\COM9
At this point GDB will send a stop the application on the ESP8266 and you can
begin setting a breakpoint (``break loop``) or any other debugging operation.
Example Debugging Session
-------------------------
Create a new sketch and paste the following code into it:
.. code:: cpp
#include <GDBStub.h>
void setup() {
Serial.begin(115200);
gdbstub_init();
Serial.printf("Starting...\n");
}
void loop() {
static uint32_t cnt = 0;
Serial.printf("%d\n", cnt++);
delay(100);
}
Save it and then build and upload to your ESP8266. On the Serial monitor you
should see something like
.. code:: cpp
1
2
3
....
Now close the Serial Monitor.
Open a command prompt and find the ELF file:
.. code:: cpp
earle@server:~$ find /tmp -name "*.elf" -print
/tmp/arduino_build_257110/testgdb.ino.elf
/tmp/arduino_build_531411/listfiles.ino.elf
/tmp/arduino_build_156712/SDWebServer.ino.elf
In this example there are multiple ``elf`` files found, but we only care about
the one we just built, ``testgdb.ino.elf``.
Open up the proper ESP8266-specific GDB
.. code:: cpp
earle@server:~$ ~/.arduino15/packages/esp8266/hardware/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb
GNU gdb (GDB) 8.2.50.20180723-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=xtensa-lx106-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb)
We're now at the GDB prompt, but nothing has been set up for the ESP8266
and no debug information has been loaded. Cut-and-paste the setup options:
.. code:: cpp
(gdb) set remote hardware-breakpoint-limit 1
(gdb) set remote hardware-watchpoint-limit 1
(gdb) set remote interrupt-on-connect on
(gdb) set remote kill-packet off
(gdb) set remote symbol-lookup-packet off
(gdb) set remote verbose-resume-packet off
(gdb) mem 0x20000000 0x3fefffff ro cache
(gdb) mem 0x3ff00000 0x3fffffff rw
(gdb) mem 0x40000000 0x400fffff ro cache
(gdb) mem 0x40100000 0x4013ffff rw cache
(gdb) mem 0x40140000 0x5fffffff ro cache
(gdb) mem 0x60000000 0x60001fff rw
(gdb) set serial baud 115200
(gdb)
And tell GDB where the debugging info ELF file is located:
.. code:: cpp
(gdb) file /tmp/arduino_build_257110/testgdb.ino.elf
Reading symbols from /tmp/arduino_build_257110/testgdb.ino.elf...done.
Now, connect to the running ESP8266:
.. code:: cpp
(gdb) target remote /dev/ttyUSB0
Remote debugging using /dev/ttyUSB0
0x40000f68 in ?? ()
(gdb)
Don't worry that GDB doesn't know what is at our present address, we broke
into the code at a random spot and we could be in an interrupt, in the
ROM, or elsewhere. The important bit is that we're now connected and
two things will now happen: we can debug, and the app's regular serial
output will be displayed on the GDB console..
Continue the running app to see the serial output:
.. code:: cpp
(gdb) cont
Continuing.
74
75
76
77
...
The app is back running and we can stop it at any time using ``Ctrl-C``:
.. code:: cpp
113
^C
Program received signal SIGINT, Interrupt.
0x40000f68 in ?? ()
(gdb)
At this point we can set a breakpoint on the main ``loop()`` and restart
to get into our own code:
.. code:: cpp
(gdb) break loop
Breakpoint 1 at 0x40202e33: file /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino, line 10.
(gdb) cont
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.
bcn_timout,ap_probe_send_start
Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
10 void loop()
(gdb)
Let's examine the local variable:
.. code:: cpp
(gdb) next
loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:13
13 Serial.printf("%d\n", cnt++);
(gdb) print cnt
$1 = 114
(gdb)
And change it:
.. code:: cpp
$2 = 114
(gdb) set cnt = 2000
(gdb) print cnt
$3 = 2000
(gdb)
And restart the app and see our changes take effect:
.. code:: cpp
(gdb) cont
Continuing.
2000
Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
10 void loop() {
(gdb) cont
Continuing.
2001
Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
10 void loop() {
(gdb)
Looks like we left the breakpoint on loop(), let's get rid of it and try again:
.. code:: cpp
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) cont
Continuing.
2002
2003
2004
2005
2006
....
At this point we can exit GDB with ``quit`` or do further debugging.
ESP8266 Hardware Debugging Limitations
--------------------------------------
The ESP8266 only supports a single hardware breakpoint and a single
hardware data watchpoint. This means only one breakpoint in user code
is allowed at any time. Consider using the ``thb`` (temporary hardware
breakpoint) command in GDB while debugging instead of the more common
``break`` command, since ``thb`` will remove the breakpoint once it is
reached automatically and save you some trouble.

View File

@ -12,6 +12,7 @@ Welcome to ESP8266 Arduino Core's documentation!
ESP8266WiFi <esp8266wifi/readme> ESP8266WiFi <esp8266wifi/readme>
OTA Updates <ota_updates/readme> OTA Updates <ota_updates/readme>
PROGMEM <PROGMEM> PROGMEM <PROGMEM>
Using GDB to debug <gdb>
Boards <boards> Boards <boards>
FAQ <faq/readme> FAQ <faq/readme>

View File

@ -1,49 +0,0 @@
Using GDB stub
--------------
- Add ``#include <GDBStub.h>`` to the sketch
- Upload the sketch
- Redirect serial port to TCP port:
::
tcp_serial_redirect.py -p /dev/tty.SLAB_USBtoUART -b 115200 --spy -P 9980 --rts=0 --dtr=0
Change port and baud rate as necessary. This command requires python
and pyserial.
- Observe serial output:
::
nc localhost 9980
- Once crash happens, close nc and start gdb:
::
xtensa-lx106-elf-gdb /path/to/Sketch.cpp.elf -ex "target remote :9980"
Or, using the provided gdbcmds file:
::
xtensa-lx106-elf-gdb /path/to/Sketch.cpp.elf -x gdbcmds
- Use gdb to inspect program state at the point of an exception.
Tips and tricks
---------------
- Upon including GDBStub.h in the sketch, the target is automatically halted when software WDT fires, with
::
*((int*)0) = 0;
at the top of ``__wrap_system_restart_local`` in
core\_esp8266\_postmortem.c.
License
-------
Espressif MIT License. See License file.

View File

@ -1,3 +1,20 @@
# ESP8266 HW limits the number of breakpoints/watchpoints
set remote hardware-breakpoint-limit 1 set remote hardware-breakpoint-limit 1
set remote hardware-watchpoint-limit 1 set remote hardware-watchpoint-limit 1
target remote :9980 # Some GDBstub settings
set remote interrupt-on-connect on
set remote kill-packet off
set remote symbol-lookup-packet off
set remote verbose-resume-packet off
# The memory map, so GDB knows where it can install SW breakpoints
mem 0x20000000 0x3fefffff ro cache
mem 0x3ff00000 0x3fffffff rw
mem 0x40000000 0x400fffff ro cache
mem 0x40100000 0x4013ffff rw cache
mem 0x40140000 0x5fffffff ro cache
mem 0x60000000 0x60001fff rw
# Change the following to your sketch's ELF file
file /path/to/sketch.ino.elf
# Change the following to your serial port and baud
set serial baud 115200
target remote /dev/ttyUSB0

View File

@ -1,6 +1,6 @@
{ {
"name": "GDBStub", "name": "GDBStub",
"version": "0.2", "version": "0.3",
"keywords": "gdb, debug", "keywords": "gdb, debug",
"description": "GDB server stub helps debug crashes when JTAG isn't an option.", "description": "GDB server stub helps debug crashes when JTAG isn't an option.",
"repository": "repository":

View File

@ -1,5 +1,5 @@
name=GDBStub name=GDBStub
version=0.2 version=0.3
author=Jeroen Domburg author=Jeroen Domburg
maintainer=Ivan Grokhotkov <ivan@esp8266.com> maintainer=Ivan Grokhotkov <ivan@esp8266.com>
sentence=GDB server stub by Espressif sentence=GDB server stub by Espressif

View File

@ -1,6 +1,40 @@
#ifndef GDBSTUB_H #ifndef GDBSTUB_H
#define GDBSTUB_H #define GDBSTUB_H
// this header is intentionally left blank #include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#endif //GDBSTUB_H #include "internal/gdbstub-cfg.h"
#ifdef __cplusplus
extern "C" {
#endif
void gdbstub_init();
//Indicates whether gdbstub will attach to these or not
//Useful for other uart libs to avoid conflicts
bool gdbstub_has_putc1_control();
bool gdbstub_has_uart_isr_control();
#if GDBSTUB_REDIRECT_CONSOLE_OUTPUT
void gdbstub_set_putc1_callback(void (*callback)(char));
#endif
void gdbstub_write_char(char c);
void gdbstub_write(const char* buf, size_t size);
#if GDBSTUB_CTRLC_BREAK && !GDBSTUB_FREERTOS
void gdbstub_set_uart_isr_callback(void (*callback)(void*, uint8_t), void* arg);
//Override points for enabling tx and rx pins for uart0
void gdbstub_hook_enable_tx_pin_uart0(uint8_t pin);
void gdbstub_hook_enable_rx_pin_uart0(uint8_t pin);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -19,6 +19,14 @@ stops when you run into an error in your code, try enabling this.
#define GDBSTUB_USE_OWN_STACK 0 #define GDBSTUB_USE_OWN_STACK 0
#endif #endif
/*
Enable this to cause the program to pause and wait for gdb to be connected when an exception is
encountered.
*/
#ifndef GDBSTUB_BREAK_ON_EXCEPTION
#define GDBSTUB_BREAK_ON_EXCEPTION 1
#endif
/* /*
If this is defined, gdbstub will break the program when you press Ctrl-C in gdb. it does this by If this is defined, gdbstub will break the program when you press Ctrl-C in gdb. it does this by
hooking the UART interrupt. Unfortunately, this means receiving stuff over the serial port won't hooking the UART interrupt. Unfortunately, this means receiving stuff over the serial port won't
@ -26,7 +34,7 @@ work for your program anymore. This will fail if your program sets an UART inter
the gdbstub_init call. the gdbstub_init call.
*/ */
#ifndef GDBSTUB_CTRLC_BREAK #ifndef GDBSTUB_CTRLC_BREAK
#define GDBSTUB_CTRLC_BREAK 0 #define GDBSTUB_CTRLC_BREAK 1
#endif #endif
/* /*
@ -35,7 +43,7 @@ will show up in your gdb session, which is useful if you use gdb to do stuff. It
you use a normal terminal, you can't read the printfs anymore. you use a normal terminal, you can't read the printfs anymore.
*/ */
#ifndef GDBSTUB_REDIRECT_CONSOLE_OUTPUT #ifndef GDBSTUB_REDIRECT_CONSOLE_OUTPUT
#define GDBSTUB_REDIRECT_CONSOLE_OUTPUT 0 #define GDBSTUB_REDIRECT_CONSOLE_OUTPUT 1
#endif #endif
/* /*
@ -55,7 +63,25 @@ flash somehow is disabled (eg during SPI operations or flash write/erase operati
are called when the flash is disabled (eg due to a Ctrl-C at the wrong time), the ESP8266 will most are called when the flash is disabled (eg due to a Ctrl-C at the wrong time), the ESP8266 will most
likely crash. likely crash.
*/ */
#ifndef ATTR_GDBINIT
#define ATTR_GDBINIT ICACHE_FLASH_ATTR #define ATTR_GDBINIT ICACHE_FLASH_ATTR
#endif
#ifndef ATTR_GDBFN
#define ATTR_GDBFN ICACHE_RAM_ATTR #define ATTR_GDBFN ICACHE_RAM_ATTR
#endif
#ifndef ATTR_GDBEXTERNFN
#define ATTR_GDBEXTERNFN ICACHE_FLASH_ATTR
#endif
#ifndef ASATTR_GDBINIT
#define ASATTR_GDBINIT .section .irom0.text
#endif
#ifndef ASATTR_GDBFN
#define ASATTR_GDBFN .section .iram.text
#endif
#ifndef ASATTR_GDBEXTERNFN
#define ASATTR_GDBEXTERNFN .section .irom0.text
#endif
#endif #endif

View File

@ -24,10 +24,13 @@
.global gdbstub_exceptionStack .global gdbstub_exceptionStack
#endif #endif
.text ASATTR_GDBFN
.literal_position .literal_position
.text ASATTR_GDBINIT
.literal_position
ASATTR_GDBFN
.align 4 .align 4
/* /*
@ -51,6 +54,7 @@ This is the debugging exception routine; it's called by the debugging vector
We arrive here with all regs intact except for a2. The old contents of A2 are saved We arrive here with all regs intact except for a2. The old contents of A2 are saved
into the DEBUG_EXCSAVE special function register. EPC is the original PC. into the DEBUG_EXCSAVE special function register. EPC is the original PC.
*/ */
.type gdbstub_debug_exception_entry, @function
gdbstub_debug_exception_entry: gdbstub_debug_exception_entry:
/* /*
//Minimum no-op debug exception handler, for debug //Minimum no-op debug exception handler, for debug
@ -64,32 +68,32 @@ gdbstub_debug_exception_entry:
//Save all regs to structure //Save all regs to structure
movi a2, gdbstub_savedRegs movi a2, gdbstub_savedRegs
s32i a0, a2, 0x10 s32i a0, a2, 0x10
s32i a1, a2, 0x58 s32i a1, a2, 0x14
rsr a0, DEBUG_PS rsr a0, DEBUG_PS
s32i a0, a2, 0x04 s32i a0, a2, 0x04
rsr a0, DEBUG_EXCSAVE //was R2 rsr a0, DEBUG_EXCSAVE //was R2
s32i a0, a2, 0x14 s32i a0, a2, 0x18
s32i a3, a2, 0x18 s32i a3, a2, 0x1c
s32i a4, a2, 0x1c s32i a4, a2, 0x20
s32i a5, a2, 0x20 s32i a5, a2, 0x24
s32i a6, a2, 0x24 s32i a6, a2, 0x28
s32i a7, a2, 0x28 s32i a7, a2, 0x2c
s32i a8, a2, 0x2c s32i a8, a2, 0x30
s32i a9, a2, 0x30 s32i a9, a2, 0x34
s32i a10, a2, 0x34 s32i a10, a2, 0x38
s32i a11, a2, 0x38 s32i a11, a2, 0x3c
s32i a12, a2, 0x3c s32i a12, a2, 0x40
s32i a13, a2, 0x40 s32i a13, a2, 0x44
s32i a14, a2, 0x44 s32i a14, a2, 0x48
s32i a15, a2, 0x48 s32i a15, a2, 0x4c
rsr a0, SAR rsr a0, SAR
s32i a0, a2, 0x08 s32i a0, a2, 0x08
rsr a0, LITBASE rsr a0, LITBASE
s32i a0, a2, 0x4C
rsr a0, 176
s32i a0, a2, 0x50 s32i a0, a2, 0x50
rsr a0, 208 rsr a0, 176
s32i a0, a2, 0x54 s32i a0, a2, 0x54
rsr a0, 208
s32i a0, a2, 0x58
rsr a0, DEBUGCAUSE rsr a0, DEBUGCAUSE
s32i a0, a2, 0x5C s32i a0, a2, 0x5C
rsr a4, DEBUG_PC rsr a4, DEBUG_PC
@ -127,33 +131,33 @@ DebugExceptionExit:
movi a2, gdbstub_savedRegs movi a2, gdbstub_savedRegs
l32i a0, a2, 0x00 l32i a0, a2, 0x00
wsr a0, DEBUG_PC wsr a0, DEBUG_PC
// l32i a0, a2, 0x54 // l32i a0, a2, 0x58
// wsr a0, 208 // wsr a0, 208
l32i a0, a2, 0x50 l32i a0, a2, 0x54
//wsr a0, 176 //Some versions of gcc do not understand this... //wsr a0, 176 //Some versions of gcc do not understand this...
.byte 0x00, 176, 0x13 //so we hand-assemble the instruction. .byte 0x00, 176, 0x13 //so we hand-assemble the instruction.
l32i a0, a2, 0x4C l32i a0, a2, 0x50
wsr a0, LITBASE wsr a0, LITBASE
l32i a0, a2, 0x08 l32i a0, a2, 0x08
wsr a0, SAR wsr a0, SAR
l32i a15, a2, 0x48 l32i a15, a2, 0x4c
l32i a14, a2, 0x44 l32i a14, a2, 0x48
l32i a13, a2, 0x40 l32i a13, a2, 0x44
l32i a12, a2, 0x3c l32i a12, a2, 0x40
l32i a11, a2, 0x38 l32i a11, a2, 0x3c
l32i a10, a2, 0x34 l32i a10, a2, 0x38
l32i a9, a2, 0x30 l32i a9, a2, 0x34
l32i a8, a2, 0x2c l32i a8, a2, 0x30
l32i a7, a2, 0x28 l32i a7, a2, 0x2c
l32i a6, a2, 0x24 l32i a6, a2, 0x28
l32i a5, a2, 0x20 l32i a5, a2, 0x24
l32i a4, a2, 0x1c l32i a4, a2, 0x20
l32i a3, a2, 0x18 l32i a3, a2, 0x1c
l32i a0, a2, 0x14 l32i a0, a2, 0x18
wsr a0, DEBUG_EXCSAVE //was R2 wsr a0, DEBUG_EXCSAVE //was R2
l32i a0, a2, 0x04 l32i a0, a2, 0x04
wsr a0, DEBUG_PS wsr a0, DEBUG_PS
l32i a1, a2, 0x58 l32i a1, a2, 0x14
l32i a0, a2, 0x10 l32i a0, a2, 0x10
//Read back vector-saved a2 value, put back address of this routine. //Read back vector-saved a2 value, put back address of this routine.
@ -162,8 +166,10 @@ DebugExceptionExit:
//All done. Return to where we came from. //All done. Return to where we came from.
rfi XCHAL_DEBUGLEVEL rfi XCHAL_DEBUGLEVEL
.size gdbstub_debug_exception_entry, .-gdbstub_debug_exception_entry
#if GDBSTUB_BREAK_ON_EXCEPTION
#if GDBSTUB_FREERTOS #if GDBSTUB_FREERTOS
/* /*
@ -184,32 +190,34 @@ the user exception handler vector:
*/ */
.global gdbstub_handle_user_exception .global gdbstub_handle_user_exception
.global gdbstub_user_exception_entry .global gdbstub_user_exception_entry
.type gdbstub_user_exception_entry, @function
ASATTR_GDBFN
.align 4 .align 4
gdbstub_user_exception_entry: gdbstub_user_exception_entry:
//Save all regs to structure //Save all regs to structure
movi a0, gdbstub_savedRegs movi a0, gdbstub_savedRegs
s32i a1, a0, 0x14 //was a2 s32i a1, a0, 0x18 //was a2
s32i a3, a0, 0x18 s32i a3, a0, 0x1c
s32i a4, a0, 0x1c s32i a4, a0, 0x20
s32i a5, a0, 0x20 s32i a5, a0, 0x24
s32i a6, a0, 0x24 s32i a6, a0, 0x28
s32i a7, a0, 0x28 s32i a7, a0, 0x2c
s32i a8, a0, 0x2c s32i a8, a0, 0x30
s32i a9, a0, 0x30 s32i a9, a0, 0x34
s32i a10, a0, 0x34 s32i a10, a0, 0x38
s32i a11, a0, 0x38 s32i a11, a0, 0x3c
s32i a12, a0, 0x3c s32i a12, a0, 0x40
s32i a13, a0, 0x40 s32i a13, a0, 0x44
s32i a14, a0, 0x44 s32i a14, a0, 0x48
s32i a15, a0, 0x48 s32i a15, a0, 0x4c
rsr a2, SAR rsr a2, SAR
s32i a2, a0, 0x08 s32i a2, a0, 0x08
rsr a2, LITBASE rsr a2, LITBASE
s32i a2, a0, 0x4C
rsr a2, 176
s32i a2, a0, 0x50 s32i a2, a0, 0x50
rsr a2, 208 rsr a2, 176
s32i a2, a0, 0x54 s32i a2, a0, 0x54
rsr a2, 208
s32i a2, a0, 0x58
rsr a2, EXCCAUSE rsr a2, EXCCAUSE
s32i a2, a0, 0x5C s32i a2, a0, 0x5C
@ -243,10 +251,13 @@ is still something we need to implement later, if there's any demand for it, or
FreeRTOS to allow this in the future. (Which will then kill backwards compatibility... hmmm.) FreeRTOS to allow this in the future. (Which will then kill backwards compatibility... hmmm.)
*/ */
j UserExceptionExit j UserExceptionExit
.size gdbstub_user_exception_entry, .-gdbstub_user_exception_entry
.global gdbstub_handle_uart_int .global gdbstub_handle_uart_int
.global gdbstub_uart_entry .global gdbstub_uart_entry
.type gdbstub_uart_entry, @function
ASATTR_GDBFN
.align 4 .align 4
gdbstub_uart_entry: gdbstub_uart_entry:
//On entry, the stack frame is at SP+16. //On entry, the stack frame is at SP+16.
@ -255,29 +266,37 @@ gdbstub_uart_entry:
add a2, a2, a1 add a2, a2, a1
movi a3, gdbstub_handle_uart_int movi a3, gdbstub_handle_uart_int
jx a3 jx a3
.size gdbstub_uart_entry, .-gdbstub_uart_entry
#endif
#endif #endif
.global gdbstub_save_extra_sfrs_for_exception .global gdbstub_save_extra_sfrs_for_exception
.type gdbstub_save_extra_sfrs_for_exception, @function
ASATTR_GDBFN
.align 4 .align 4
//The Xtensa OS HAL does not save all the special function register things. This bit of assembly //The Xtensa OS HAL does not save all the special function register things. This bit of assembly
//fills the gdbstub_savedRegs struct with them. //fills the gdbstub_savedRegs struct with them.
gdbstub_save_extra_sfrs_for_exception: gdbstub_save_extra_sfrs_for_exception:
movi a2, gdbstub_savedRegs movi a2, gdbstub_savedRegs
rsr a3, LITBASE rsr a3, LITBASE
s32i a3, a2, 0x4C
rsr a3, 176
s32i a3, a2, 0x50 s32i a3, a2, 0x50
rsr a3, 208 rsr a3, 176
s32i a3, a2, 0x54 s32i a3, a2, 0x54
rsr a3, 208
s32i a3, a2, 0x58
rsr a3, EXCCAUSE rsr a3, EXCCAUSE
s32i a3, a2, 0x5C s32i a3, a2, 0x5C
ret ret
.size gdbstub_save_extra_sfrs_for_exception, .-gdbstub_save_extra_sfrs_for_exception
.global gdbstub_init_debug_entry .global gdbstub_init_debug_entry
.global _DebugExceptionVector .global _DebugExceptionVector
.type gdbstub_init_debug_entry, @function
ASATTR_GDBINIT
.align 4 .align 4
gdbstub_init_debug_entry: gdbstub_init_debug_entry:
//This puts the following 2 instructions into the debug exception vector: //This puts the following 2 instructions into the debug exception vector:
@ -294,10 +313,13 @@ gdbstub_init_debug_entry:
wsr a2, DEBUG_EXCSAVE wsr a2, DEBUG_EXCSAVE
ret ret
.size gdbstub_init_debug_entry, .-gdbstub_init_debug_entry
//Set up ICOUNT register to step one single instruction //Set up ICOUNT register to step one single instruction
.global gdbstub_icount_ena_single_step .global gdbstub_icount_ena_single_step
.type gdbstub_icount_ena_single_step, @function
ASATTR_GDBFN
.align 4 .align 4
gdbstub_icount_ena_single_step: gdbstub_icount_ena_single_step:
movi a3, XCHAL_DEBUGLEVEL //Only count steps in non-debug mode movi a3, XCHAL_DEBUGLEVEL //Only count steps in non-debug mode
@ -306,6 +328,7 @@ gdbstub_icount_ena_single_step:
wsr a2, ICOUNT wsr a2, ICOUNT
isync isync
ret ret
.size gdbstub_icount_ena_single_step, .-gdbstub_icount_ena_single_step
//These routines all assume only one breakpoint and watchpoint is available, which //These routines all assume only one breakpoint and watchpoint is available, which
@ -313,6 +336,8 @@ gdbstub_icount_ena_single_step:
.global gdbstub_set_hw_breakpoint .global gdbstub_set_hw_breakpoint
.type gdbstub_set_hw_breakpoint, @function
ASATTR_GDBFN
gdbstub_set_hw_breakpoint: gdbstub_set_hw_breakpoint:
//a2 - addr, a3 - len (unused here) //a2 - addr, a3 - len (unused here)
rsr a4, IBREAKENABLE rsr a4, IBREAKENABLE
@ -323,8 +348,11 @@ gdbstub_set_hw_breakpoint:
isync isync
movi a2, 1 movi a2, 1
ret ret
.size gdbstub_set_hw_breakpoint, .-gdbstub_set_hw_breakpoint
.global gdbstub_del_hw_breakpoint .global gdbstub_del_hw_breakpoint
.type gdbstub_del_hw_breakpoint, @function
ASATTR_GDBFN
gdbstub_del_hw_breakpoint: gdbstub_del_hw_breakpoint:
//a2 - addr //a2 - addr
rsr a5, IBREAKENABLE rsr a5, IBREAKENABLE
@ -336,8 +364,11 @@ gdbstub_del_hw_breakpoint:
isync isync
movi a2, 1 movi a2, 1
ret ret
.size gdbstub_del_hw_breakpoint, .-gdbstub_del_hw_breakpoint
.global gdbstub_set_hw_watchpoint .global gdbstub_set_hw_watchpoint
.type gdbstub_set_hw_watchpoint, @function
ASATTR_GDBFN
//a2 - addr, a3 - mask, a4 - type (1=read, 2=write, 3=access) //a2 - addr, a3 - mask, a4 - type (1=read, 2=write, 3=access)
gdbstub_set_hw_watchpoint: gdbstub_set_hw_watchpoint:
//Check if any of the masked address bits are set. If so, that is an error. //Check if any of the masked address bits are set. If so, that is an error.
@ -362,9 +393,12 @@ gdbstub_set_hw_watchpoint:
mov a2, a3 mov a2, a3
isync isync
ret ret
.size gdbstub_set_hw_watchpoint, .-gdbstub_set_hw_watchpoint
.global gdbstub_del_hw_watchpoint .global gdbstub_del_hw_watchpoint
.type gdbstub_del_hw_watchpoint, @function
ASATTR_GDBFN
//a2 - addr //a2 - addr
gdbstub_del_hw_watchpoint: gdbstub_del_hw_watchpoint:
//See if the address matches //See if the address matches
@ -384,11 +418,14 @@ gdbstub_del_hw_watchpoint:
return_w_error: return_w_error:
movi a2, 0 movi a2, 0
ret ret
.size gdbstub_del_hw_watchpoint, .-gdbstub_del_hw_watchpoint
//Breakpoint, with an attempt at a functional function prologue and epilogue... //Breakpoint, with an attempt at a functional function prologue and epilogue...
.global gdbstub_do_break_breakpoint_addr .global gdbstub_do_break_breakpoint_addr
.global gdbstub_do_break .global gdbstub_do_break
.type gdbstub_do_break, @function
ASATTR_GDBFN
.align 4 .align 4
gdbstub_do_break: gdbstub_do_break:
addi a1, a1, -16 addi a1, a1, -16
@ -402,3 +439,4 @@ gdbstub_do_break_breakpoint_addr:
l32i a15, a1, 12 l32i a15, a1, 12
addi a1, a1, 16 addi a1, a1, 16
ret ret
.size gdbstub_do_break, .-gdbstub_do_break

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
#ifndef GDBSTUB_H
#define GDBSTUB_H
#ifdef __cplusplus
extern "C" {
#endif
void gdbstub_init();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -192,7 +192,7 @@ SECTIONS
*(.entry.text) *(.entry.text)
*(.init.literal) *(.init.literal)
*(.init) *(.init)
*(.literal .text .iram.text .iram.text.* .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) *(.literal .text .iram.literal .iram.text .iram.text.* .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
#ifdef VTABLES_IN_IRAM #ifdef VTABLES_IN_IRAM
*(.rodata._ZTV*) /* C++ vtables */ *(.rodata._ZTV*) /* C++ vtables */
#endif #endif