diff options
Diffstat (limited to 'src/debug/gdbrsp/stream.c')
-rw-r--r-- | src/debug/gdbrsp/stream.c | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/src/debug/gdbrsp/stream.c b/src/debug/gdbrsp/stream.c new file mode 100644 index 0000000..979ed9b --- /dev/null +++ b/src/debug/gdbrsp/stream.c @@ -0,0 +1,696 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream.c - gestion des connexions aux serveurs GDB. + * + * Copyright (C) 2009-2017 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "stream.h" + + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <glib/gthread.h> +#include <sys/select.h> + + +#include "gdb-int.h" +#include "stream-int.h" +#include "utils.h" +#include "../../common/dllist.h" +#include "../../gui/panels/log.h" + + + +/* Initialise la classe des flux de communication avec GDB. */ +static void g_gdb_stream_class_init(GGdbStreamClass *); + +/* Initialise une instance de flux de communication avec GDB. */ +static void g_gdb_stream_init(GGdbStream *); + +/* Supprime toutes les références externes. */ +static void g_gdb_stream_dispose(GGdbStream *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_stream_finalize(GGdbStream *); + +/* Envoie un acquittement pour la dernière réception. */ +static bool gdb_stream_ack(GGdbStream *); + +/* Ecoute une connexion à un serveur GDB. */ +static void *gdb_stream_thread(GGdbStream *); + +/* Reste en alerte quant au changement de statut de l'exécution. */ +static void *gdb_stream_status_thread(GGdbStream *); + +/* Réceptionne un paquet d'un serveur GDB. */ +static bool g_gdb_stream_read_packet(GGdbStream *, GGdbPacket *); + + + +/* Indique le type défini pour un flux de communication avec un serveur GDB. */ +G_DEFINE_TYPE(GGdbStream, g_gdb_stream, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des flux de communication avec GDB. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gdb_stream_class_init(GGdbStreamClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_stream_dispose; + object->finalize = (GObjectFinalizeFunc)g_gdb_stream_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = instance à initialiser. * +* * +* Description : Initialise une instance de flux de communication avec GDB. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gdb_stream_init(GGdbStream *stream) +{ + g_mutex_init(&stream->free_mutex); + + g_cond_init(&stream->recv_cond); + g_mutex_init(&stream->recv_mutex); + + g_cond_init(&stream->status_cond); + g_mutex_init(&stream->status_mutex); + + stream->skip_ack = false; + + stream->want_status = false; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gdb_stream_dispose(GGdbStream *stream) +{ + g_object_unref(G_OBJECT(stream->owner)); + + + /* TODO... */ + + + G_OBJECT_CLASS(g_gdb_stream_parent_class)->dispose(G_OBJECT(stream)); + +} + + +/****************************************************************************** +* * +* Paramètres : stream = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_gdb_stream_finalize(GGdbStream *stream) +{ + + /* TODO */ + + + G_OBJECT_CLASS(g_gdb_stream_parent_class)->finalize(G_OBJECT(stream)); + +} + + +/****************************************************************************** +* * +* Paramètres : stream = instance à modifier. * +* * +* Description : Ne participe plus aux acquitements de paquets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_gdb_stream_do_not_ack(GGdbStream *stream) +{ + stream->skip_ack = true; + +} + + + +/****************************************************************************** +* * +* Paramètres : stream = instance à réellement lancer. * +* * +* Description : Lance l'écoute d'un flux de communication avec GDB. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_gdb_stream_listen(GGdbStream *stream) +{ + bool result; /* Bilan final à retourner */ + + result = true; + + if (!g_thread_new("chrysalide_gdb_stream", (GThreadFunc)gdb_stream_thread, stream)) + result = false; + + if (!g_thread_new("chrysalide_gdb_status", (GThreadFunc)gdb_stream_status_thread, stream)) + result = false; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = encadrement associée à l'opération. * +* * +* Description : Envoie un acquittement pour la dernière réception. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool gdb_stream_ack(GGdbStream *stream) +{ + /// + //return true; + + bool result; /* Bilan à retourner */ + GGdbPacket *packet; /* Paquet à envoyer */ + + packet = g_gdb_stream_get_free_packet(stream); + + g_gdb_packet_start_new_command(packet); + g_gdb_packet_append(packet, "+"); + + result = g_gdb_stream_send_packet(stream, packet); + + g_gdb_stream_mark_packet_as_free(stream, packet); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = encadrement associée à l'opération. * +* * +* Description : Ecoute une connexion à un serveur GDB. * +* * +* Retour : ??? * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void *gdb_stream_thread(GGdbStream *stream) +{ + fd_set rfds; /* Liste des flux à surveiller */ + int ret; /* Bilan d'un appel */ + GGdbPacket *packet; /* Nouveau paquet reçu */ + + + const char *data; /* Données reçues à analyser */ + size_t len; /* Quantité de ces données */ + + + + while (1) + { + FD_ZERO(&rfds); + FD_SET(stream->fd, &rfds); + + ret = select(stream->fd + 1, &rfds, NULL, NULL, NULL); + + switch (ret) + { + case -1: + perror("select()"); + break; + + case 0: + break; + + default: + + packet = g_gdb_stream_get_free_packet(stream); + + g_gdb_packet_start_new_command(packet); + + if (g_gdb_stream_read_packet(stream, packet)) + { + /* Acquittement ? */ + if (!stream->skip_ack) + { + if (!gdb_stream_ack(stream)) goto bad_recv; + } + + + /* On conserve le résultat ? */ + + + g_gdb_packet_get_data(packet, &data, &len, NULL); + + + //printf("---------------------------\n"); + //printf(">> want status ? %d\n", stream->want_status); + //printf(">> got '%s'\n", data); + + + if (len >= 1) + { + if (stream->want_status) + stream->want_status = false; + + else if (index("STWX", data[0]) != NULL) + { + g_mutex_lock(&stream->status_mutex); + g_gdb_packet_push(&stream->status_packets, packet); + g_mutex_unlock(&stream->status_mutex); + + g_cond_signal(&stream->status_cond); + + break; + } + + // else message inconnu -> log_message() ! + + } + + + + + g_mutex_lock(&stream->recv_mutex); + g_gdb_packet_push(&stream->recv_packets, packet); + g_mutex_unlock(&stream->recv_mutex); + + g_cond_signal(&stream->recv_cond); + + } + + else + g_gdb_stream_mark_packet_as_free(stream, packet); + + break; + + bad_recv: + + printf("bad things happend...\n"); + + g_gdb_stream_mark_packet_as_free(stream, packet); + + break; + + } + + } + + + printf("Oh noes....\n"); + + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = encadrement associée à l'opération. * +* * +* Description : Reste en alerte quant au changement de statut de l'exécution.* +* * +* Retour : ??? * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void *gdb_stream_status_thread(GGdbStream *stream) +{ + GGdbPacket *packet; /* Nouveau paquet reçu */ + const char *data; /* Données reçues à analyser */ + size_t len; /* Quantité de ces données */ + bool malformed; /* Echec d'interprétation */ + uint8_t byte; /* Valeur quelconque sur 8 bits*/ + bool ret; /* Bilan d'un appel */ + + while (1) + { + /* Réception d'un nouveau paquet de statut */ + + g_mutex_lock(&stream->status_mutex); + + if (dl_list_empty(stream->status_packets)) + g_cond_wait(&stream->status_cond, &stream->status_mutex); + + packet = g_gdb_packet_pop(&stream->status_packets); + + g_mutex_unlock(&stream->status_mutex); + + /* Traitement du paquet reçu */ + + g_gdb_packet_get_data(packet, &data, &len, NULL); + + malformed = false; + + switch (data[0]) + { + case 'S': + + ret = read_fixed_byte(data + 1, len - 1, &byte); + + if (!ret) + { + malformed = true; + goto gsst_processed; + } + + g_gdb_debugger_receive_signal_reply(stream->owner, byte); + break; + + case 'T': + assert(false); // TODO + break; + + case 'W': + + ret = read_fixed_byte(data + 1, len - 1, &byte); + + if (!ret) + { + malformed = true; + goto gsst_processed; + } + + + // TODO : ";process:pid" + + + printf("Program exited (status=%hhu)\n", byte); + + + g_gdb_debugger_receive_exit_reply(stream->owner, byte, -1); + + + // log_message en cas de mauvais format... + + + break; + + + default: + assert(false); + break; + + } + + gsst_processed: + + if (malformed && true/* TODO : config->show_... */) + log_variadic_message(LMT_WARNING, "Malformed GDB status reply: '%s'", data); + + g_gdb_stream_mark_packet_as_free(stream, packet); + + } + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = flux de communication avec GDB à consulter. * +* * +* Description : Fournit un paquet prêt à emploi. * +* * +* Retour : Paquet prêt à emploi. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GGdbPacket *g_gdb_stream_get_free_packet(GGdbStream *stream) +{ + GGdbPacket *result; /* Paquet à retourner */ + + g_mutex_lock(&stream->free_mutex); + + if (dl_list_empty(stream->free_packets)) + result = g_gdb_packet_new(); + + else + result = g_gdb_packet_pop(&stream->free_packets); + + g_mutex_unlock(&stream->free_mutex); + + // ??? + //g_gdb_packet_start_new_command(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = flux de communication avec GDB à mettre à jour. * +* packet = paquet à considérer comme disponible. * +* * +* Description : Place un paquet en attente d'une future utilisation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_gdb_stream_mark_packet_as_free(GGdbStream *stream, GGdbPacket *packet) +{ + //// Utile ? + g_gdb_packet_start_new_command(packet); + + + g_mutex_lock(&stream->free_mutex); + + g_gdb_packet_push(&stream->free_packets, packet); + + g_mutex_unlock(&stream->free_mutex); + +} + + +/****************************************************************************** +* * +* Paramètres : stream = flux ouvert en lecture à utiliser. * +* packet = données à recevoir. * +* * +* Description : Réceptionne un paquet d'un serveur GDB. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_gdb_stream_read_packet(GGdbStream *stream, GGdbPacket *packet) +{ + bool result; /* Bilan à renvoyer */ + char tmp[3]; /* Tampon de réception */ + uint8_t checksum; /* Contrôle d'intégrité */ + + do + { + result = stream->recv_byte(stream, tmp); + if (tmp[0] != '+') break; + } + while (0); + + if (tmp[0] != '$') return false; + + tmp[1] = '\0'; + + while ((result = stream->recv_byte(stream, tmp))) + { + //printf(" .. '%c'\n", tmp[0]); + + if (tmp[0] == '#') break; + else g_gdb_packet_append(packet, tmp); + } + + if (result) + { + result = stream->recv_byte(stream, &tmp[0]); + result &= stream->recv_byte(stream, &tmp[1]); + + tmp[2] = 0; + checksum = strtol(tmp, NULL, 16); + + } + + if (result) + result = g_gdb_packet_verify_checksum(packet, checksum); + + if (result) + result = g_gdb_packet_decode(packet); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = flux ouvert en écriture à mettre à jour. * +* packet = données à transmettre. * +* * +* Description : Envoie un paquet à un serveur GDB. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ +#include <string.h> +bool g_gdb_stream_send_packet(GGdbStream *stream, GGdbPacket *packet) +{ + bool result; /* Bilan à renvoyer */ + const char *data; /* Données à envoyer */ + size_t len; /* Quantité de ces données */ + uint8_t checksum; /* Contrôle d'intégrité */ + char tmp[3]; /* Impression du checksum */ + + g_gdb_packet_get_data(packet, &data, &len, NULL); + +#if 1 + /* Ack ? */ + if (len == 1 && data[0] == '+') + result = stream->send_data(stream, "+", 1); + + else +#endif + { + + result = stream->send_data(stream, "$", 1); + //result = stream->send_data(stream, "+$", 2); + + g_gdb_packet_compute_checksum(packet); + g_gdb_packet_get_data(packet, &data, &len, &checksum); + + + if (len == 1 && data[0] == '?') + stream->want_status = true; + + /* + if (memcmp(data, "vCont;c", strlen("vCont;c")) == 0) + stream->want_status = true; + */ + + + + result &= stream->send_data(stream, data, len); + + result = stream->send_data(stream, "#", 1); + + snprintf(tmp, 3, "%02hhx", checksum); + result &= stream->send_data(stream, tmp, 2); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = flux de communication avec GDB à consulter. * +* * +* Description : Fournit un paquet reçu d'un serveur GDB. * +* * +* Retour : Paquet GDB. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GGdbPacket *g_gdb_stream_recv_packet(GGdbStream *stream) +{ + GGdbPacket *result; /* Paquet à retourner */ + + g_mutex_lock(&stream->recv_mutex); + + if (dl_list_empty(stream->recv_packets)) + g_cond_wait(&stream->recv_cond, &stream->recv_mutex); + + result = g_gdb_packet_pop(&stream->recv_packets); + + g_mutex_unlock(&stream->recv_mutex); + + return result; + +} |