Refine gdbstub add conn.

This commit is contained in:
Colin 2025-09-22 05:21:17 +00:00
parent 9b9d9f5e5f
commit b9bef6a9af
11 changed files with 486 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

52
.vscode/settings.json vendored
View File

@ -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"
}
}

58
gdbstub/CMakeLists.txt Normal file
View File

@ -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)

12
gdbstub/stub.cpp Normal file
View File

@ -0,0 +1,12 @@
// #include "gdbstub.h"
#include <iostream>
#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;
}

177
gdbstub/utils/conn.c Normal file
View File

@ -0,0 +1,177 @@
#include "conn.h"
#include <arpa/inet.h>
#include <assert.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#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);
}

25
gdbstub/utils/conn.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef CONN_H
#define CONN_H
#include <pthread.h>
#include <stdbool.h>
#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

9
gdbstub/utils/csum.c Normal file
View File

@ -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;
}

9
gdbstub/utils/csum.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef CSUM_H
#define CSUM_H
#include <stddef.h>
#include <stdint.h>
uint8_t compute_checksum(char *buf, size_t len);
#endif

22
gdbstub/utils/log.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef LOG_H
#define LOG_H
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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

90
gdbstub/utils/packet.c Normal file
View File

@ -0,0 +1,90 @@
#include "packet.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
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); }

32
gdbstub/utils/packet.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef PACKET_H
#define PACKET_H
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#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