From b9bef6a9af4bc31accb20554b4edfefe91fe1acc Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 22 Sep 2025 05:21:17 +0000 Subject: [PATCH] Refine gdbstub add conn. --- .gitignore | 1 + .vscode/settings.json | 52 +++++++++++- gdbstub/CMakeLists.txt | 58 ++++++++++++++ gdbstub/stub.cpp | 12 +++ gdbstub/utils/conn.c | 177 +++++++++++++++++++++++++++++++++++++++++ gdbstub/utils/conn.h | 25 ++++++ gdbstub/utils/csum.c | 9 +++ gdbstub/utils/csum.h | 9 +++ gdbstub/utils/log.h | 22 +++++ gdbstub/utils/packet.c | 90 +++++++++++++++++++++ gdbstub/utils/packet.h | 32 ++++++++ 11 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 gdbstub/CMakeLists.txt create mode 100644 gdbstub/stub.cpp create mode 100644 gdbstub/utils/conn.c create mode 100644 gdbstub/utils/conn.h create mode 100644 gdbstub/utils/csum.c create mode 100644 gdbstub/utils/csum.h create mode 100644 gdbstub/utils/log.h create mode 100644 gdbstub/utils/packet.c create mode 100644 gdbstub/utils/packet.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index efb228b..51f9f15 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,56 @@ "BUILD": "bazel", "*.inc": "cpp", "stdio.h": "c", - "cstdlib": "c" + "cstdlib": "c", + "conn.h": "c", + "commands.h": "c", + "csum.h": "c", + "log.h": "c", + "translate.h": "c", + "iostream": "c", + "un.h": "c", + "inet.h": "c", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp" } } \ No newline at end of file diff --git a/gdbstub/CMakeLists.txt b/gdbstub/CMakeLists.txt new file mode 100644 index 0000000..773b85d --- /dev/null +++ b/gdbstub/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.9) + +project(gdbstub) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -Wall -g2 -ggdb") +set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") + +add_compile_options(-Wall -Wextra -Wunused-function -Wpedantic) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-Werror=return-local-addr -Wno-deprecated-copy -Werror=maybe-uninitialized) + add_compile_options(-Wno-cast-function-type) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options(-Werror=return-stack-address -Wno-deprecated -Werror=uninitialized) + add_compile_options(-Wno-bad-function-cast -Wno-gnu-designator ) + add_compile_options(-Wno-inconsistent-missing-override) + add_compile_options(-Wno-nested-anon-types -Wno-unused-private-field) + add_compile_options(-Wno-delete-non-abstract-non-virtual-dtor) +endif() + +add_compile_options(-Werror=return-type -Werror=shadow -Werror=strict-aliasing + -Werror=uninitialized) + +add_compile_options(-Wno-reorder -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare) +add_compile_options (-Wno-ignored-qualifiers) + +# enable_language(C ASM) + +message(STATUS "C++ Compiler: ${CMAKE_CXX_COMPILER}") +message(STATUS "C++ Compiler ID: ${CMAKE_CXX_COMPILER_ID}") +message(STATUS "C++ Compiler Version: ${CMAKE_CXX_COMPILER_VERSION}") + +message(STATUS "CMAKE_BUILD_TYPE:" ${CMAKE_BUILD_TYPE}) +message(STATUS "CMAKE_INSTALL_PREFIX:" ${CMAKE_INSTALL_PREFIX}) +set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "" FORCE) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_C_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS ON) + + + + +file(GLOB UTILS_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.c) +set_source_files_properties(${UTILS_SRC} PROPERTIES LANGUAGE CXX) + +add_executable(stub ${UTILS_SRC} stub.cpp) +target_include_directories(stub PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(stub PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/utils) + diff --git a/gdbstub/stub.cpp b/gdbstub/stub.cpp new file mode 100644 index 0000000..280a029 --- /dev/null +++ b/gdbstub/stub.cpp @@ -0,0 +1,12 @@ + +// #include "gdbstub.h" +#include + +#include "conn.h" + +int main(int argc, char *argv[]) { + conn_t conn; + if (!conn_init(&conn, "127.0.0.1", 1234)) std::cout << "conn_init error" << std::endl; + + return 0; +} diff --git a/gdbstub/utils/conn.c b/gdbstub/utils/conn.c new file mode 100644 index 0000000..fbfe4ff --- /dev/null +++ b/gdbstub/utils/conn.c @@ -0,0 +1,177 @@ +#include "conn.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils/csum.h" +#include "utils/log.h" + +static bool socket_poll(int socket_fd, int timeout, int events) +{ + struct pollfd pfd = (struct pollfd){ + .fd = socket_fd, + .events = events, + }; + + return (poll(&pfd, 1, timeout) > 0) && (pfd.revents & events); +} + +static bool socket_readable(int socket_fd, int timeout) +{ + return socket_poll(socket_fd, timeout, POLLIN); +} + +static bool socket_writable(int socket_fd, int timeout) +{ + return socket_poll(socket_fd, timeout, POLLOUT); +} + +bool conn_init(conn_t *conn, char *addr_str, int port) +{ + if (!pktbuf_init(&conn->pktbuf)) + return false; + + struct in_addr addr_ip; + if (inet_aton(addr_str, &addr_ip) != 0) { + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = addr_ip.s_addr; + addr.sin_port = htons(port); + conn->listen_fd = socket(AF_INET, SOCK_STREAM, 0); + if (conn->listen_fd < 0) + return false; + + int optval = 1; + if (setsockopt(conn->listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, + sizeof(optval)) < 0) { + warn("Set sockopt fail.\n"); + goto fail; + } + + if (bind(conn->listen_fd, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + warn("Bind fail.\n"); + goto fail; + } + } else { + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, addr_str, sizeof(addr.sun_path) - 1); + unlink(addr_str); + conn->listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (conn->listen_fd < 0) + return false; + + if (bind(conn->listen_fd, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + warn("Bind fail.\n"); + goto fail; + } + } + + if (listen(conn->listen_fd, 1) < 0) { + warn("Listen fail.\n"); + goto fail; + } + + conn->socket_fd = accept(conn->listen_fd, NULL, NULL); + if (conn->socket_fd < 0) { + warn("Accept fail.\n"); + goto fail; + } + + return true; + +fail: + close(conn->listen_fd); + return false; +} + +void conn_recv_packet(conn_t *conn) +{ + while (!pktbuf_is_complete(&conn->pktbuf) && + socket_readable(conn->socket_fd, -1)) { + ssize_t nread = pktbuf_fill_from_file(&conn->pktbuf, conn->socket_fd); + if (nread == -1) + break; + } + + conn_send_str(conn, STR_ACK); +} + +packet_t *conn_pop_packet(conn_t *conn) +{ + packet_t *pkt = pktbuf_pop_packet(&conn->pktbuf); + + return pkt; +} + +bool conn_try_recv_intr(conn_t *conn) +{ + char ch; + + if (!socket_readable(conn->socket_fd, 0)) + return false; + + ssize_t nread = read(conn->socket_fd, &ch, 1); + if (nread != 1) + return false; + + /* FIXME: The character must be INTR_CHAR, otherwise the library + * may work incorrectly. However, I'm not sure if this implementation + * can always meet our expectation (concurrent is so hard QAQ). */ + assert(ch == INTR_CHAR); + return true; +} + +void conn_send_str(conn_t *conn, char *str) +{ + size_t len = strlen(str); + + while (len > 0 && socket_writable(conn->socket_fd, -1)) { + ssize_t nwrite = write(conn->socket_fd, str, len); + if (nwrite == -1) + break; + len -= nwrite; + } +} + +void conn_send_pktstr(conn_t *conn, char *pktstr) +{ + char packet[MAX_SEND_PACKET_SIZE]; + size_t len = strlen(pktstr); + + /* 2: '$' + '#' + * 2: checksum digits(maximum) + * 1: '\0' */ + assert(len + 2 + CSUM_SIZE + 1 < MAX_SEND_PACKET_SIZE); + + packet[0] = '$'; + memcpy(packet + 1, pktstr, len); + packet[len + 1] = '#'; + + char csum_str[4]; + uint8_t csum = compute_checksum(pktstr, len); + size_t csum_len = snprintf(csum_str, sizeof(csum_str) - 1, "%02x", csum); + assert(csum_len == CSUM_SIZE); + memcpy(packet + len + 2, csum_str, csum_len); + packet[len + 2 + csum_len] = '\0'; + +#ifdef DEBUG + printf("send packet = %s,", packet); + printf(" checksum = %d\n", csum); +#endif + conn_send_str(conn, packet); +} + +void conn_close(conn_t *conn) +{ + close(conn->socket_fd); + close(conn->listen_fd); + pktbuf_destroy(&conn->pktbuf); +} diff --git a/gdbstub/utils/conn.h b/gdbstub/utils/conn.h new file mode 100644 index 0000000..4017d91 --- /dev/null +++ b/gdbstub/utils/conn.h @@ -0,0 +1,25 @@ +#ifndef CONN_H +#define CONN_H + +#include +#include +#include "packet.h" + +#define MAX_SEND_PACKET_SIZE (0x1000) +#define MAX_DATA_PAYLOAD (MAX_SEND_PACKET_SIZE - (2 + CSUM_SIZE + 2)) + +typedef struct { + int listen_fd; + int socket_fd; + + pktbuf_t pktbuf; +} conn_t; + +bool conn_init(conn_t *conn, char *addr_str, int port); +void conn_recv_packet(conn_t *conn); +packet_t *conn_pop_packet(conn_t *conn); +bool conn_try_recv_intr(conn_t *conn); +void conn_send_str(conn_t *conn, char *str); +void conn_send_pktstr(conn_t *conn, char *pktstr); +void conn_close(conn_t *conn); +#endif diff --git a/gdbstub/utils/csum.c b/gdbstub/utils/csum.c new file mode 100644 index 0000000..8c34561 --- /dev/null +++ b/gdbstub/utils/csum.c @@ -0,0 +1,9 @@ +#include "utils/csum.h" + +uint8_t compute_checksum(char *buf, size_t len) +{ + uint8_t csum = 0; + for (size_t i = 0; i < len; ++i) + csum += buf[i]; + return csum; +} diff --git a/gdbstub/utils/csum.h b/gdbstub/utils/csum.h new file mode 100644 index 0000000..8782cdc --- /dev/null +++ b/gdbstub/utils/csum.h @@ -0,0 +1,9 @@ +#ifndef CSUM_H +#define CSUM_H + +#include +#include + +uint8_t compute_checksum(char *buf, size_t len); + +#endif diff --git a/gdbstub/utils/log.h b/gdbstub/utils/log.h new file mode 100644 index 0000000..970a8ea --- /dev/null +++ b/gdbstub/utils/log.h @@ -0,0 +1,22 @@ +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include +#include + +static inline void log_print(const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + vfprintf(stderr, format, vargs); + va_end(vargs); +} + +#define warn(format, ...) \ + log_print("ERRNO: %s\n" format, strerror(errno), ##__VA_ARGS__) +#define info(format, ...) log_print(format, ##__VA_ARGS__) + +#endif diff --git a/gdbstub/utils/packet.c b/gdbstub/utils/packet.c new file mode 100644 index 0000000..2146bec --- /dev/null +++ b/gdbstub/utils/packet.c @@ -0,0 +1,90 @@ +#include "packet.h" + +#include +#include +#include + +static void pktbuf_clear(pktbuf_t *pktbuf) { + pktbuf->end_pos = -1; + pktbuf->size = 0; +} + +#define DEFAULT_CAP (10) +bool pktbuf_init(pktbuf_t *pktbuf) { + pktbuf->cap = DEFAULT_CAP; + pktbuf->data = (uint8_t *)calloc(1, (1 << pktbuf->cap) * sizeof(uint8_t)); + pktbuf_clear(pktbuf); + + return true; +} + +ssize_t pktbuf_fill_from_file(pktbuf_t *pktbuf, int fd) { + assert((1 << pktbuf->cap) >= pktbuf->size); + + /* enlarge the buffer to read from file when it is full */ + if ((1 << pktbuf->cap) == pktbuf->size) { + pktbuf->cap++; + pktbuf->data = (uint8_t *)realloc(pktbuf->data, (1 << pktbuf->cap) * sizeof(uint8_t)); + } + + int left = (1 << pktbuf->cap) - pktbuf->size; + uint8_t *buf = pktbuf->data + pktbuf->size; + ssize_t nread = read(fd, buf, left); + + if (nread > 0) pktbuf->size += nread; + + return nread; +} + +bool pktbuf_is_complete(pktbuf_t *pktbuf) { + int head = -1; + + /* skip to the head of next packet */ + for (int i = 0; i < pktbuf->size; i++) { + if (pktbuf->data[i] == '$') { + head = i; + break; + } + } + + if (head < 0) { + pktbuf_clear(pktbuf); + return false; + } + + if (head > 0) { + /* moving memory for a valid packet */ + memmove(pktbuf->data, pktbuf->data + head, pktbuf->size - head); + pktbuf->size -= head; + } + + /* check the end of the buffer */ + uint8_t *end_pos_ptr = (uint8_t *)memchr(pktbuf->data, '#', pktbuf->size); + if (end_pos_ptr == NULL) return false; + + int end_pos = (end_pos_ptr - pktbuf->data) + CSUM_SIZE; + if (end_pos > pktbuf->size) return false; + + pktbuf->end_pos = end_pos; + + return true; +} + +packet_t *pktbuf_pop_packet(pktbuf_t *pktbuf) { + if (pktbuf->end_pos == -1) { + return NULL; + } + + int old_pkt_size = pktbuf->end_pos + 1; + packet_t *pkt = (packet_t *)calloc(1, sizeof(packet_t) + old_pkt_size + 1); + memcpy(pkt->data, pktbuf->data, old_pkt_size); + pkt->end_pos = pktbuf->end_pos; + pkt->data[old_pkt_size] = 0; + + memmove(pktbuf->data, pktbuf->data + old_pkt_size + 1, pktbuf->size - old_pkt_size); + pktbuf->size -= old_pkt_size; + pktbuf->end_pos = -1; + return pkt; +} + +void pktbuf_destroy(pktbuf_t *pktbuf) { free(pktbuf->data); } diff --git a/gdbstub/utils/packet.h b/gdbstub/utils/packet.h new file mode 100644 index 0000000..e8d4bba --- /dev/null +++ b/gdbstub/utils/packet.h @@ -0,0 +1,32 @@ +#ifndef PACKET_H +#define PACKET_H + +#include +#include +#include + +#define STR_ACK "+" +#define INTR_CHAR '\x03' + +#define CSUM_SIZE (2) + +typedef struct { + int end_pos; + uint8_t data[]; +} packet_t; + +/* A naive packet buffer: maintain a big array to fill the packet */ +typedef struct { + int size; /* the size for all valid characters in data buffer */ + int cap; /* the capacity (1 << cap) of the data buffer */ + int end_pos; /* the end position of the first packet in data buffer */ + uint8_t *data; +} pktbuf_t; + +bool pktbuf_init(pktbuf_t *pktbuf); +ssize_t pktbuf_fill_from_file(pktbuf_t *pktbuf, int fd); +bool pktbuf_is_complete(pktbuf_t *pktbuf); +packet_t *pktbuf_pop_packet(pktbuf_t *pktbuf); +void pktbuf_destroy(pktbuf_t *pktbuf); + +#endif