Refine gdbstub add conn.
This commit is contained in:
parent
9b9d9f5e5f
commit
b9bef6a9af
|
@ -0,0 +1 @@
|
|||
build/
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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); }
|
|
@ -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
|
Loading…
Reference in New Issue