From ffe68e41fba151c799ac2c43de1472f1c4e07c2f Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 26 Mar 2005 12:57:07 +0000 Subject: [PATCH] Generic and i386 specific Haiku native and target debugger support. The target debugger won't work though, since quite a bit of code would need to be moved from the native support files to those for the target support. But that's not that important at the moment, since we're mainly want a native debugger. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12027 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/bin/gdb/gdb/haiku-nat.c | 1614 +++++++++++++++++++++++++++++ src/bin/gdb/gdb/haiku-nat.h | 42 + src/bin/gdb/gdb/haiku-tdep.c | 22 + src/bin/gdb/gdb/i386-haiku-nat.c | 85 ++ src/bin/gdb/gdb/i386-haiku-tdep.c | 77 ++ 5 files changed, 1840 insertions(+) create mode 100644 src/bin/gdb/gdb/haiku-nat.c create mode 100644 src/bin/gdb/gdb/haiku-nat.h create mode 100644 src/bin/gdb/gdb/haiku-tdep.c create mode 100644 src/bin/gdb/gdb/i386-haiku-nat.c create mode 100644 src/bin/gdb/gdb/i386-haiku-tdep.c diff --git a/src/bin/gdb/gdb/haiku-nat.c b/src/bin/gdb/gdb/haiku-nat.c new file mode 100644 index 0000000000..bcdd98998a --- /dev/null +++ b/src/bin/gdb/gdb/haiku-nat.c @@ -0,0 +1,1614 @@ +/* Haiku native-dependent code common to multiple platforms. + + Copyright 2005 Ingo Weinhold . + + This file is part of GDB. + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include + +#include +#include +#include + +#include "defs.h" // include this first -- otherwise "command.h" will choke +#include "command.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "haiku-nat.h" +#include "inferior.h" +#include "observer.h" +#include "regcache.h" +#include "solib-haiku.h" +#include "target.h" + +//#define TRACE_HAIKU_NAT +#ifdef TRACE_HAIKU_NAT + #define TRACE(x) printf x +#else + #define TRACE(x) while (false) {} +#endif + +static struct target_ops *sHaikuTarget = NULL; + +typedef struct debug_event { + struct debug_event *next; + debug_debugger_message message; + debug_debugger_message_data data; +} debug_event; + +typedef struct debug_event_list { + debug_event *head; + debug_event *tail; +} debug_event_list; + +typedef enum { + SIGNAL_ARRIVED, // the signal has actually arrived and is going to be + // handled (in any way) + SIGNAL_WILL_ARRIVE, // the signal has not yet been sent, but will be sent + // (e.g. an exception occurred and will cause the + // signal when not being ignored) + SIGNAL_FAKED, // the signal didn't arrive and won't arrive, we faked + // it (often in case of SIGTRAP) +} pending_signal_status; + +typedef struct thread_debug_info { + struct thread_debug_info *next; + thread_id thread; + bool stopped; + debug_event *last_event; // last debug message from + // the thread; valid only, + // if stopped + int reprocess_event; // > 0, the event + // shall be processed a + // that many times, which + // will probably trigger + // another target event + int signal; // only valid, if stopped + pending_signal_status signal_status; +} thread_debug_info; + +typedef struct extended_image_info { + haiku_image_info info; + struct extended_image_info *next; +} extended_image_info; + +typedef struct team_debug_info { + team_id team; + port_id debugger_port; + port_id nub_port; + port_id reply_port; + thread_debug_info *threads; + extended_image_info *images; + debug_event_list events; +} team_debug_info; + +static team_debug_info sTeamDebugInfo; + + +typedef struct signal_map_entry { + enum target_signal target; + int haiku; +} signal_map_entry; + +static const signal_map_entry sSignalMap[] = { + { TARGET_SIGNAL_0, 0 }, + { TARGET_SIGNAL_HUP, SIGHUP }, + { TARGET_SIGNAL_INT, SIGINT }, + { TARGET_SIGNAL_QUIT, SIGQUIT }, + { TARGET_SIGNAL_ILL, SIGILL }, + { TARGET_SIGNAL_CHLD, SIGCHLD }, + { TARGET_SIGNAL_ABRT, SIGABRT }, + { TARGET_SIGNAL_PIPE, SIGPIPE }, + { TARGET_SIGNAL_FPE, SIGFPE }, + { TARGET_SIGNAL_KILL, SIGKILL }, + { TARGET_SIGNAL_STOP, SIGSTOP }, + { TARGET_SIGNAL_SEGV, SIGSEGV }, + { TARGET_SIGNAL_CONT, SIGCONT }, + { TARGET_SIGNAL_TSTP, SIGTSTP }, + { TARGET_SIGNAL_ALRM, SIGALRM }, + { TARGET_SIGNAL_TERM, SIGTERM }, + { TARGET_SIGNAL_TTIN, SIGTTIN }, + { TARGET_SIGNAL_TTOU, SIGTTOU }, + { TARGET_SIGNAL_USR1, SIGUSR1 }, + { TARGET_SIGNAL_USR2, SIGUSR2 }, + { TARGET_SIGNAL_WINCH, SIGWINCH }, + { TARGET_SIGNAL_TRAP, SIGTRAP }, + { TARGET_SIGNAL_0, SIGKILLTHR }, // not debuggable anyway + { -1, -1 } +}; + + +// #pragma mark - + + +static char *haiku_pid_to_str (ptid_t ptid); + + +static int +target_to_haiku_signal(enum target_signal targetSignal) +{ + int i; + + if (targetSignal < 0) + return -1; + + for (i = 0; sSignalMap[i].target != -1 || sSignalMap[i].haiku != -1; i++) { + if (targetSignal == sSignalMap[i].target) + return sSignalMap[i].haiku; + } + + return -1; +} + + +static enum target_signal +haiku_to_target_signal(int haikuSignal) +{ + int i; + + if (haikuSignal < 0) + return TARGET_SIGNAL_0; + + for (i = 0; sSignalMap[i].target != -1 || sSignalMap[i].haiku != -1; i++) { + if (haikuSignal == sSignalMap[i].haiku) + return sSignalMap[i].target; + } + + return TARGET_SIGNAL_UNKNOWN; +} + + +static thread_debug_info * +haiku_find_thread(team_debug_info *teamDebugInfo, thread_id threadID) +{ + thread_debug_info *info; + for (info = teamDebugInfo->threads; info; info = info->next) { + if (info->thread == threadID) + return info; + } + + return NULL; +} + + +static thread_debug_info * +haiku_add_thread(team_debug_info *teamDebugInfo, thread_id threadID) +{ + // find the thread first + thread_debug_info *threadDebugInfo + = haiku_find_thread(teamDebugInfo, threadID); + if (threadDebugInfo) + return threadDebugInfo; + + // allocate a new thread debug info + threadDebugInfo = XMALLOC(thread_debug_info); + if (!threadDebugInfo) + error("haiku_thread_added(): Out of memory!\n"); + + // init and add it + threadDebugInfo->thread = threadID; + threadDebugInfo->next = teamDebugInfo->threads; + threadDebugInfo->stopped = false; + threadDebugInfo->last_event = NULL; + teamDebugInfo->threads = threadDebugInfo; + + return threadDebugInfo; +} + + +static void +haiku_remove_thread(team_debug_info *teamDebugInfo, thread_id threadID) +{ + thread_debug_info **info; + for (info = &teamDebugInfo->threads; *info; info = &(*info)->next) { + if ((*info)->thread == threadID) { + thread_debug_info *foundInfo = *info; + *info = foundInfo->next; + if (foundInfo->last_event) + xfree(foundInfo->last_event); + xfree(foundInfo); + return; + } + } +} + + +static void +haiku_init_thread_list(team_debug_info *teamDebugInfo) +{ + thread_info threadInfo; + int32 cookie = 0; + + while (get_next_thread_info(teamDebugInfo->team, &cookie, &threadInfo) + == B_OK) { + haiku_add_thread(teamDebugInfo, threadInfo.thread); + } +} + + +static void +haiku_cleanup_thread_list(team_debug_info *teamDebugInfo) +{ + while (teamDebugInfo->threads) { + thread_debug_info *thread = teamDebugInfo->threads; + teamDebugInfo->threads = thread->next; + xfree(thread); + } +} + + +// #pragma mark - + +static extended_image_info ** +haiku_find_image_insert_location(team_debug_info *teamDebugInfo, + image_id imageID) +{ + extended_image_info **image; + + for (image = &teamDebugInfo->images; *image; image = &(*image)->next) { + if ((*image)->info.id >= imageID) + return image; + } + + return image; +} + + +static extended_image_info * +haiku_find_image(team_debug_info *teamDebugInfo, image_id imageID) +{ + extended_image_info *image = *haiku_find_image_insert_location( + teamDebugInfo, imageID); + + return (image && image->info.id == imageID ? image : NULL); +} + + +static extended_image_info * +haiku_add_image(team_debug_info *teamDebugInfo, image_info *imageInfo) +{ + extended_image_info **imageP = haiku_find_image_insert_location( + teamDebugInfo, imageInfo->id); + extended_image_info *image = *imageP; + + // already known? + if (image && image->info.id == imageInfo->id) + return image; + + image = XMALLOC(extended_image_info); + if (!image) + error("haiku_add_image(): Out of memory!"); + + image->info.id = imageInfo->id; + strncpy(image->info.name, imageInfo->name, sizeof(image->info.name)); + image->info.name[sizeof(image->info.name) - 1] = '\0'; + // TODO: This should be the shared objects soname, not the path. We + // probably need to extend the debugger API. + strncpy(image->info.path, imageInfo->name, sizeof(image->info.path)); + image->info.path[sizeof(image->info.path) - 1] = '\0'; + image->info.text_address = (CORE_ADDR)imageInfo->text; + image->info.text_size = imageInfo->text_size; + image->info.data_address = (CORE_ADDR)imageInfo->data; + image->info.data_size = imageInfo->data_size; + image->info.is_app_image = (imageInfo->type == B_APP_IMAGE); + + image->next = *imageP; + *imageP = image; + + return image; +} + + +static void +haiku_remove_image(team_debug_info *teamDebugInfo, image_id imageID) +{ + extended_image_info **imageP = haiku_find_image_insert_location( + teamDebugInfo, imageID); + extended_image_info *image = *imageP; + + if (image && image->info.id == imageID) { + *imageP = image->next; + xfree(image); + } +} + + +static void +haiku_init_image_list(team_debug_info *teamDebugInfo) +{ + int32 cookie = 0; + image_info info; + while (get_next_image_info(teamDebugInfo->team, &cookie, &info) == B_OK) + haiku_add_image(teamDebugInfo, &info); +} + + +static void +haiku_cleanup_image_list(team_debug_info *teamDebugInfo) +{ + while (teamDebugInfo->images) { + extended_image_info *image = teamDebugInfo->images; + teamDebugInfo->images = image->next; + xfree(image); + } +} + + +/* + * Service function. Call with -1 the first time. + */ +struct haiku_image_info * +haiku_get_next_image_info(int lastID) +{ + extended_image_info *image = *haiku_find_image_insert_location( + &sTeamDebugInfo, (lastID >= 0 ? lastID : 0)); + + if (image && image->info.id == lastID) + image = image->next; + + return (image ? &image->info : NULL); +} + + +// #pragma mark - + +static void +haiku_cleanup_team_debug_info() +{ + delete_port(sTeamDebugInfo.debugger_port); + delete_port(sTeamDebugInfo.reply_port); + sTeamDebugInfo.debugger_port = -1; + sTeamDebugInfo.reply_port = -1; + sTeamDebugInfo.nub_port = -1; + sTeamDebugInfo.team = -1; + + haiku_cleanup_thread_list(&sTeamDebugInfo); + haiku_cleanup_image_list(&sTeamDebugInfo); +} + + +static status_t +haiku_send_debugger_message(team_debug_info *teamDebugInfo, int32 messageCode, + const void *message, int32 messageSize, void *reply, int32 replySize) +{ + // send message + while (true) { + status_t result = write_port(teamDebugInfo->nub_port, messageCode, + message, messageSize); + if (result == B_OK) + break; + if (result != B_INTERRUPTED) + return result; + } + + if (!reply) + return B_OK; + + // read reply + while (true) { + int32 code; + ssize_t bytesRead = read_port(teamDebugInfo->reply_port, &code, + reply, replySize); + if (bytesRead > 0) + return B_OK; + if (bytesRead != B_INTERRUPTED) + return bytesRead; + } +} + + +static void +haiku_init_child_debugging (thread_id threadID, bool debugThread) +{ + thread_info threadInfo; + status_t result; + + // get a thread info + result = get_thread_info(threadID, &threadInfo); + if (result != B_OK) + error("Thread with ID %ld not found: %s", threadID, strerror(result)); + + // init our team debug structure + sTeamDebugInfo.team = threadInfo.team; + sTeamDebugInfo.debugger_port = -1; + sTeamDebugInfo.nub_port = -1; + sTeamDebugInfo.reply_port = -1; + sTeamDebugInfo.threads = NULL; + sTeamDebugInfo.events.head = NULL; + sTeamDebugInfo.events.tail = NULL; + + // create the debugger port + sTeamDebugInfo.debugger_port = create_port(10, "gdb debug"); + if (sTeamDebugInfo.debugger_port < 0) { + error("Failed to create debugger port: %s", + strerror(sTeamDebugInfo.debugger_port)); + } + + // create a reply port + sTeamDebugInfo.reply_port = create_port(10, "gdb debug reply"); + if (sTeamDebugInfo.reply_port < 0) { + error("Failed to create reply port: %s", + strerror(sTeamDebugInfo.reply_port)); + } + + // install ourselves as the team debugger + sTeamDebugInfo.nub_port = install_team_debugger(sTeamDebugInfo.team, + sTeamDebugInfo.debugger_port); + if (sTeamDebugInfo.nub_port < 0) { + error("Failed to install ourselves as debugger for team %ld: %s", + sTeamDebugInfo.team, strerror(sTeamDebugInfo.nub_port)); + } + + // start debugging the thread + if (debugThread) { + result = debug_thread(threadID); + if (result != B_OK) { + error("Failed to start debugging thread %ld: %s", threadID, + strerror(result)); + } + } + + // set the team debug flags + { + debug_nub_set_team_flags message; + message.flags = B_TEAM_DEBUG_SIGNALS /*| B_TEAM_DEBUG_PRE_SYSCALL + | B_TEAM_DEBUG_POST_SYSCALL*/ | B_TEAM_DEBUG_TEAM_CREATION + | B_TEAM_DEBUG_THREADS | B_TEAM_DEBUG_IMAGES; + // TODO: We probably don't need all events + + haiku_send_debugger_message(&sTeamDebugInfo, + B_DEBUG_MESSAGE_SET_TEAM_FLAGS, &message, sizeof(message), NULL, 0); + } + + haiku_init_thread_list(&sTeamDebugInfo); + haiku_init_image_list(&sTeamDebugInfo); + + // the fun can start: push the target and init the rest + push_target(sHaikuTarget); + + init_thread_list (); + + disable_breakpoints_in_shlibs (1); + +// child_clear_solibs (); + // TODO: Implement? Do we need this? + + clear_proceed_status (); + init_wait_for_inferior (); + + target_terminal_init (); + target_terminal_inferior (); +} + + +static void +haiku_continue_thread(team_debug_info *teamDebugInfo, thread_id threadID, + uint32 handleEvent, bool step) +{ + debug_nub_continue_thread message; + status_t err; + + message.thread = threadID; + message.handle_event = handleEvent; + message.single_step = step; + + err = haiku_send_debugger_message(teamDebugInfo, + B_DEBUG_MESSAGE_CONTINUE_THREAD, &message, sizeof(message), NULL, 0); + if (err != B_OK) { + printf_unfiltered ("Failed to resume thread %ld: %s\n", threadID, + strerror(err)); + } +} + + +static status_t +haiku_get_cpu_state(team_debug_info *teamDebugInfo, + debug_nub_get_cpu_state_reply *reply) +{ + status_t err; + thread_id threadID = ptid_get_tid(inferior_ptid); + debug_nub_get_cpu_state message; + + message.reply_port = teamDebugInfo->reply_port; + message.thread = threadID; + + err = haiku_send_debugger_message(teamDebugInfo, + B_DEBUG_MESSAGE_GET_CPU_STATE, &message, sizeof(message), reply, + sizeof(*reply)); + if (err == B_OK) + err = reply->error; + if (err != B_OK) { + printf_unfiltered ("Failed to get status of thread %ld: %s\n", + threadID, strerror(err)); + } + + return err; +} + + +// #pragma mark - + +static debug_event * +haiku_enqueue_debug_event(debug_event_list *list, int32 message, + debug_debugger_message_data *data, int32 size) +{ + debug_event *event = XMALLOC(debug_event); + + if (!event) + error("haiku_enqueue_debug_event(): Out of memory!\n"); + + // init the event + event->next = NULL; + event->message = message; + memcpy(&event->data, data, + (size >= 0 ? size : sizeof(debug_debugger_message_data))); + + // add it to the queue + if (list->tail) { + list->tail->next = event; + list->tail = event; + } else { + list->head = list->tail = event; + } + + return event; +} + + +static debug_event * +haiku_dequeue_next_debug_event(debug_event_list *list) +{ + debug_event *event = list->head; + + if (event) { + // remove it from the queue + list->head = event->next; + if (list->tail == event) + list->tail = NULL; + + event->next = NULL; // just because we're paranoid + } + + return event; +} + + +static debug_event * +haiku_remove_debug_event(debug_event_list *list, debug_event *eventToRemove) +{ + debug_event **event; + + if (eventToRemove) { + for (event = &list->head; *event; event = &(*event)->next) { + if (*event == eventToRemove) { + *event = (*event)->next; + if (eventToRemove == list->tail) + list->tail = NULL; + return eventToRemove; + } + } + } + + error("haiku_remove_debug_event(): event %p not found in list %p\n", + eventToRemove, list); + + return NULL; +} + + +static void +haiku_clear_debug_event_list(debug_event_list *list) +{ + debug_event *event; + + while ((event = haiku_dequeue_next_debug_event(list))) + xfree(event); +} + + +static debug_event * +haiku_find_next_debug_event(debug_event_list *list, + bool (*predicate)(void *closure, debug_event *event), void *closure) +{ + debug_event *event; + + for (event = list->head; event; event = event->next) { + if ((*predicate)(closure, event)) + return event; + } + + return NULL; +} + + +static void +haiku_read_pending_debug_events(team_debug_info *teamDebugInfo, + bool block, bool (*block_predicate)(void *closure, debug_event *event), + void *closure) +{ + while (true) { + // read the next message from the debugger port + debug_debugger_message_data message; + int32 code; + ssize_t bytesRead; + debug_event *event; + +// TRACE(("haiku_read_pending_debug_events(): reading from debugger port " +// "(%sblocking)...\n", (block ? "" : "non-"))); + + do { + bytesRead = read_port_etc(teamDebugInfo->debugger_port, &code, + &message, sizeof(message), (block ? 0 : B_RELATIVE_TIMEOUT), 0); + } while (bytesRead == B_INTERRUPTED); + + if (bytesRead < 0) { + if (bytesRead == B_WOULD_BLOCK && !block) + break; + + error("Failed to read from debugger port: %s\n", + strerror(bytesRead)); + } + +// TRACE(("haiku_read_pending_debug_events(): got event: %lu, " +// "thread: %ld, team: %ld, nub port: %ld\n", code, +// message.origin.thread, message.origin.team, +// message.origin.nub_port)); + + // got a message: queue it + event = haiku_enqueue_debug_event(&teamDebugInfo->events, code, + &message, bytesRead); + + block = !(*block_predicate)(closure, event); + } +} + + +typedef struct thread_event_closure { + team_debug_info *context; + thread_id thread; + debug_event *event; +} thread_event_closure; + + +static bool +haiku_thread_event_predicate(void *_closure, debug_event *event) +{ + thread_event_closure *closure = (thread_event_closure*)_closure; + + if (event->message == B_DEBUGGER_MESSAGE_TEAM_DELETED) { + if (closure->context->team < 0 + || event->data.origin.team == closure->context->team) { + closure->event = event; + } + } + + if (!closure->event) { + if (event->data.origin.team == closure->context->team + && (closure->thread < 0 + || closure->thread == event->data.origin.thread)) { + closure->event = event; + } + } + + return (closure->event != NULL); +} + + +// #pragma mark - + +static void +haiku_child_open (char *arg, int from_tty) +{ + error ("Use the \"run\" command to start a child process."); +} + +static void +haiku_child_close (int x) +{ + printf_unfiltered ("gdb: child_close, inferior_ptid=%d\n", + PIDGET (inferior_ptid)); +} + + +static void +haiku_child_attach (char *args, int from_tty) +{ + extern int stop_after_trap; + thread_id threadID; + thread_info threadInfo; + status_t result; + + TRACE(("haiku_child_attach(`%s', %d)\n", args, from_tty)); + + if (!args) + error_no_arg ("thread-id to attach"); + + // get the thread ID + threadID = strtoul (args, 0, 0); + if (threadID <= 0) + error("The given thread-id %ld is invalid.", threadID); + + haiku_init_child_debugging(threadID, true); + + while (1) { + stop_after_trap = 1; + wait_for_inferior (); +// TODO: Catch deadly events, so that we won't block here. +// if (debugThread && stop_signal != TARGET_SIGNAL_TRAP) +// resume (0, stop_signal); +// else + break; + } + stop_after_trap = 0; +} + + +static void +haiku_child_detach (char *args, int from_tty) +{ + int detached = 1; + status_t result; + + TRACE(("haiku_child_detach(`%s', %d)\n", args, from_tty)); + + result = remove_team_debugger(sTeamDebugInfo.team); + if (result != B_OK) { + error("Failed to detach process %ld: %s\n", sTeamDebugInfo.team, + strerror(result)); + } + + delete_command (NULL, 0); + + if (from_tty) { + char *exec_file = get_exec_file (0); + if (exec_file == 0) + exec_file = ""; + printf_unfiltered ("Detaching from program: %s, Pid %ld\n", exec_file, + sTeamDebugInfo.team); + gdb_flush (gdb_stdout); + } + + haiku_cleanup_team_debug_info(); + + inferior_ptid = null_ptid; + unpush_target (sHaikuTarget); +} + + +static void +haiku_resume_thread(team_debug_info *teamDebugInfo, thread_debug_info *thread, + int step, enum target_signal sig) +{ + thread_id threadID = (thread ? thread->thread : -1); + int pendingSignal = -1; + int signalToSend = target_to_haiku_signal(sig); + uint32 handleEvent = B_THREAD_DEBUG_HANDLE_EVENT; + + if (!thread || !thread->stopped) { + TRACE(("haiku_resume_thread(): thread %ld not stopped\n", + threadID)); + return; + } + + if (thread->reprocess_event > 0) { + TRACE(("haiku_resume_thread(): thread %ld is expected to " + "reprocess its current event\n", threadID)); + return; + } + + // get the pending signal + if (thread->last_event) + pendingSignal = thread->signal; + + // Decide what to do with the event and the pending and passed signal. + // We simply adjust signalToSend, pendingSignal and handleEvent. + // If signalToSend is set afterwards, we need to send it. pendingSignal we + // need to ignore. handleEvent specifies whether to handle/ignore the event. + if (signalToSend > 0) { + if (pendingSignal > 0) { + if (signalToSend == pendingSignal) { + // signal to send is the pending signal + // we don't need to send it + signalToSend = -1; + if (thread->signal_status != SIGNAL_WILL_ARRIVE) { + // the signal has not yet been sent, so we need to ignore + // it only in this case, but not in the other ones + pendingSignal = -1; + } + } else { + // signal to send is not the pending signal + // If the signal has been sent or will be sent, we need to + // ignore the event. + if (thread->signal_status != SIGNAL_FAKED) + handleEvent = B_THREAD_DEBUG_IGNORE_EVENT; + // At any rate we don't need to ignore the signal. + pendingSignal = -1; + // send the signal + } + } else { + // there's a signal to send, but no pending signal + // handle the event + // ignore the signal + // send the signal + } + } else { + if (pendingSignal > 0) { + // there's a pending signal, but no signal to send + // Ignore the event, if the signal has been sent or will be sent. + if (thread->signal_status != SIGNAL_FAKED) + handleEvent = B_THREAD_DEBUG_IGNORE_EVENT; + } else { + // there're neither signal to send nor pending signal + // handle the event + } + } + + // ignore the pending signal, if necessary + if (pendingSignal > 0) { + debug_nub_set_signal_masks message; + status_t err; + + message.thread = threadID; + message.ignore_mask = 0; + message.ignore_once_mask = B_DEBUG_SIGNAL_TO_MASK(pendingSignal); + message.ignore_op = B_DEBUG_SIGNAL_MASK_OR; + message.ignore_once_op = B_DEBUG_SIGNAL_MASK_OR; + + err = haiku_send_debugger_message(teamDebugInfo, + B_DEBUG_MESSAGE_SET_SIGNAL_MASKS, &message, sizeof(message), + NULL, 0); + if (err != B_OK) { + printf_unfiltered ("Set signal ignore masks for thread %ld: %s\n", + threadID, strerror(err)); + } + } + + // send the signal + if (signalToSend > 0) { + if (send_signal(threadID, signalToSend) < 0) { + printf_unfiltered ("Failed to send signal %d to thread %ld: %s\n", + signalToSend, threadID, strerror(errno)); + } + } + + // resume thread + haiku_continue_thread(teamDebugInfo, threadID, handleEvent, step); + thread->stopped = false; +} + + +static void +haiku_child_resume (ptid_t ptid, int step, enum target_signal sig) +{ + team_id teamID = ptid_get_pid(ptid); + thread_id threadID = ptid_get_tid(ptid); + thread_debug_info *thread; + + TRACE(("haiku_child_resume(`%s', step: %d, signal: %d)\n", + haiku_pid_to_str(ptid), step, sig)); + + if (teamID < 0) { + // resume all stopped threads (at least all haiku_wait_child() has + // reported to be stopped) + for (thread = sTeamDebugInfo.threads; thread; thread = thread->next) { + if (thread->stopped) + haiku_resume_thread(&sTeamDebugInfo, thread, step, sig); + } + } else { + // resume the thread in question + thread = haiku_find_thread(&sTeamDebugInfo, threadID); + if (thread) { + haiku_resume_thread(&sTeamDebugInfo, thread, step, sig); + } else { + printf_unfiltered ("haiku_child_resume(): unknown thread %ld\n", + threadID); + } + } + +} + + +static ptid_t +haiku_child_wait_internal (ptid_t ptid, struct target_waitstatus *ourstatus, + bool *ignore) +{ + team_id teamID = ptid_get_pid(ptid); + team_id threadID = ptid_get_tid(ptid); + debug_event *event = NULL; + ptid_t retval = pid_to_ptid(-1); + struct thread_debug_info *thread; + int pendingSignal = -1; + pending_signal_status pendingSignalStatus = SIGNAL_FAKED; + int reprocessEvent = -1; + + if (teamID < 0 || threadID == 0) + threadID = -1; + + // if we're waiting for any thread, search the thread list for already + // stopped threads (needed for reprocessing events) + if (threadID < 0) { + for (thread = sTeamDebugInfo.threads; thread; thread = thread->next) { + if (thread->stopped) { + threadID = thread->thread; + break; + } + } + } + + // check, if the thread exists and is already stopped + if (threadID >= 0 + && (thread = haiku_find_thread(&sTeamDebugInfo, threadID)) != NULL + && thread->stopped) { + // remove the event that stopped the thread from the thread (we will + // add it again, if it shall not be ignored) + event = thread->last_event; + thread->last_event = NULL; + thread->stopped = false; + reprocessEvent = thread->reprocess_event; + + TRACE(("haiku_child_wait_internal(): thread %ld already stopped. " + "re-digesting the last event (%p).\n", threadID, event)); + } else { + // prepare closure for finding interesting events + thread_event_closure threadEventClosure; + threadEventClosure.context = &sTeamDebugInfo; + threadEventClosure.thread = threadID; + threadEventClosure.event = NULL; + + // find the first interesting queued event + threadEventClosure.event = haiku_find_next_debug_event( + &sTeamDebugInfo.events, haiku_thread_event_predicate, + &threadEventClosure); + + // read all events pending on the port + haiku_read_pending_debug_events(&sTeamDebugInfo, + (threadEventClosure.event == NULL), haiku_thread_event_predicate, + &threadEventClosure); + + // get the event of interest + event = haiku_remove_debug_event(&sTeamDebugInfo.events, + threadEventClosure.event); + + if (!event) { + TRACE(("haiku_child_wait_internal(): got no event!\n")); + return retval; + } + + TRACE(("haiku_child_wait_internal(): got event: %u, thread: %ld, " + "team: %ld, nub port: %ld\n", event->message, + event->data.origin.thread, event->data.origin.team, + event->data.origin.nub_port)); + } + + if (event->data.origin.team != sTeamDebugInfo.team) { + // Spurious debug message. Doesn't concern our team. Ignore. + xfree(event); + return retval; + } + + retval = ptid_build(event->data.origin.team, 0, event->data.origin.thread); + + *ignore = false; + + switch (event->message) { + case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: + // TODO: Print the debugger message. + case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: + case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: + case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: + case B_DEBUGGER_MESSAGE_SINGLE_STEP: + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + pendingSignal = SIGTRAP; + pendingSignalStatus = SIGNAL_FAKED; + break; + + case B_DEBUGGER_MESSAGE_PRE_SYSCALL: + ourstatus->kind = TARGET_WAITKIND_SYSCALL_ENTRY; + ourstatus->value.syscall_id = event->data.pre_syscall.syscall; + break; + + case B_DEBUGGER_MESSAGE_POST_SYSCALL: + ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN; + ourstatus->value.syscall_id = event->data.post_syscall.syscall; + break; + + case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: + pendingSignal = event->data.signal_received.signal; + pendingSignalStatus = SIGNAL_ARRIVED; + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = haiku_to_target_signal(pendingSignal); + break; + + case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: + // TODO: Print the exception message. + pendingSignal = event->data.exception_occurred.signal; + pendingSignalStatus = SIGNAL_WILL_ARRIVE; + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = haiku_to_target_signal(pendingSignal); + break; + + case B_DEBUGGER_MESSAGE_TEAM_CREATED: + // ignore + *ignore = true; + break; + + case B_DEBUGGER_MESSAGE_TEAM_DELETED: + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = 0; + // TODO: Extend the debugger interface? + break; + + case B_DEBUGGER_MESSAGE_THREAD_CREATED: + // internal bookkeeping only + haiku_add_thread(&sTeamDebugInfo, + event->data.thread_created.new_thread); + *ignore = true; + break; + + case B_DEBUGGER_MESSAGE_THREAD_DELETED: + // internal bookkeeping + haiku_remove_thread(&sTeamDebugInfo, + event->data.thread_deleted.origin.thread); + *ignore = true; + + // TODO: What if this is the current thread? + break; + + case B_DEBUGGER_MESSAGE_IMAGE_CREATED: + if (reprocessEvent < 0) { + // first time we see the event: update our image list +// haiku_add_image(&sTeamDebugInfo, +// &event->data.image_created.info); +haiku_cleanup_image_list(&sTeamDebugInfo); +haiku_init_image_list(&sTeamDebugInfo); +// TODO: We don't get events when images have been removed in preparation of +// an exec*() yet. + } + + ourstatus->kind = TARGET_WAITKIND_LOADED; + ourstatus->value.integer = 0; + if (event->data.image_created.info.type == B_APP_IMAGE) { + // An app image has been loaded, i.e. the application is now + // fully loaded. We need to send a TARGET_WAITKIND_LOADED + // first, so that GDB's shared object list is updated correctly. + // But we also need to send an TARGET_WAITKIND_EXECD event. + // We use the reprocessEvent mechanism here. + + if (reprocessEvent == 2) { + // the second time the event is processed: send the `exec' + // event + if (inferior_ignoring_startup_exec_events > 0) { + // we shall not send an exec event, so we skip that + // and send the `trap' immediately +TRACE(("haiku_child_wait_internal(): ignoring exec (%d)\n", +inferior_ignoring_startup_exec_events)); + inferior_ignoring_startup_exec_events--; + reprocessEvent = 1; + } + } + + if (reprocessEvent < 0) { +TRACE(("haiku_child_wait_internal(): B_APP_IMAGE created, reprocess < 0\n")); + // the first time the event is processed: send the + // `loaded' event + reprocessEvent = 2; + } else if (reprocessEvent == 2) { +TRACE(("haiku_child_wait_internal(): B_APP_IMAGE created, reprocess -> exec\n")); + // the second time the event is processed: send the `exec' + // event + ourstatus->kind = TARGET_WAITKIND_EXECD; + ourstatus->value.execd_pathname + = event->data.image_created.info.name; + + reprocessEvent = 1; + } else if (reprocessEvent <= 1) { + // the third time the event is processed: send the `trap' + // event + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + pendingSignal = SIGTRAP; + pendingSignalStatus = SIGNAL_FAKED; + + reprocessEvent = 0; + } + } + + // re_enable_breakpoints_in_shlibs (); + // TODO: Needed? + break; + + case B_DEBUGGER_MESSAGE_IMAGE_DELETED: + haiku_remove_image(&sTeamDebugInfo, + event->data.image_created.info.id); + + // send TARGET_WAITKIND_LOADED here too, it causes the shared + // object list to be updated + ourstatus->kind = TARGET_WAITKIND_LOADED; + ourstatus->value.integer = 0; + break; + + default: + // unknown message, ignore + *ignore = true; + break; + } + + // make the event the thread's last event or delete it + if (!*ignore && event->data.origin.thread >= 0) { + struct thread_debug_info *thread = haiku_find_thread(&sTeamDebugInfo, + event->data.origin.thread); + if (thread->last_event) + xfree(thread->last_event); + thread->last_event = event; + thread->stopped = true; + thread->signal = pendingSignal; + thread->signal_status = pendingSignalStatus; + thread->reprocess_event + = (reprocessEvent >= 0 ? reprocessEvent : 0); + } else { + xfree(event); + + // continue the thread + if (event->data.origin.thread >= 0) { + haiku_continue_thread(&sTeamDebugInfo, event->data.origin.thread, + B_THREAD_DEBUG_HANDLE_EVENT, false); + } + +// *ignore = true; +// TODO: This should indeed not be needed. It definitely eats the +// `team deleted' events. + } + + return retval; +} + + +static ptid_t +haiku_child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) +{ + ptid_t retval; + bool ignore = true; + + TRACE(("haiku_child_wait(`%s', %p)\n", + haiku_pid_to_str(ptid), ourstatus)); + + do { + retval = haiku_child_wait_internal(ptid, ourstatus, &ignore); + } while (ignore); + + TRACE(("haiku_child_wait() done: `%s'\n", haiku_pid_to_str(retval))); + + return retval; +} + + +static void +haiku_child_fetch_inferior_registers (int reg) +{ + debug_nub_get_cpu_state_reply reply; + + TRACE(("haiku_child_fetch_inferior_registers(%d)\n", reg)); + + // get the CPU state + haiku_get_cpu_state(&sTeamDebugInfo, &reply); + + // supply the registers (architecture specific) + haiku_supply_registers(reg, &reply.cpu_state); +} + + +static void +haiku_child_store_inferior_registers (int reg) +{ + status_t err; + thread_id threadID = ptid_get_tid(inferior_ptid); + debug_nub_get_cpu_state_reply reply; + debug_nub_set_cpu_state message; + + TRACE(("haiku_child_store_inferior_registers(%d)\n", reg)); + + // get the current CPU state + haiku_get_cpu_state(&sTeamDebugInfo, &reply); + + // collect the registers (architecture specific) + haiku_collect_registers(reg, &reply.cpu_state); + + // set the new CPU state + message.thread = threadID; + memcpy(&message.cpu_state, &reply.cpu_state, sizeof(debug_cpu_state)); + err = haiku_send_debugger_message(&sTeamDebugInfo, + B_DEBUG_MESSAGE_SET_CPU_STATE, &message, sizeof(message), NULL, 0); + if (err != B_OK) { + printf_unfiltered ("Failed to set status of thread %ld: %s\n", + threadID, strerror(err)); + } +} + +static void +haiku_child_prepare_to_store (void) +{ + // Since we always fetching the current state in + // haiku_child_store_inferior_registers(), this should be a no-op. +} + +static int +haiku_child_deprecated_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, struct mem_attrib *attrib, struct target_ops *target) +{ + TRACE(("haiku_child_deprecated_xfer_memory(0x%8lx, %p, %d, %d, %p, %p)\n", + (uint32)memaddr, myaddr, len, write, attrib, target)); + + if (len <= 0) + return 0; + + if (write) { + // write + debug_nub_write_memory message; + debug_nub_write_memory_reply reply; + status_t err; + + if (len > B_MAX_READ_WRITE_MEMORY_SIZE) + len = B_MAX_READ_WRITE_MEMORY_SIZE; + + message.reply_port = sTeamDebugInfo.reply_port; + message.address = (void*)memaddr; + message.size = len; + memcpy(message.data, myaddr, len); + + err = haiku_send_debugger_message(&sTeamDebugInfo, + B_DEBUG_MESSAGE_WRITE_MEMORY, &message, sizeof(message), &reply, + sizeof(reply)); + if (err != B_OK || reply.error != B_OK) +{ +TRACE(("haiku_child_deprecated_xfer_memory() failed: %lx\n", +(err != B_OK ? err : reply.error))); + return 0; +} + +TRACE(("haiku_child_deprecated_xfer_memory(): -> %ld\n", reply.size)); + return reply.size; + } else { + // read + debug_nub_read_memory message; + debug_nub_read_memory_reply reply; + status_t err; + + message.reply_port = sTeamDebugInfo.reply_port; + message.address = (void*)memaddr; + message.size = len; + + err = haiku_send_debugger_message(&sTeamDebugInfo, + B_DEBUG_MESSAGE_READ_MEMORY, &message, sizeof(message), &reply, + sizeof(reply)); + if (err != B_OK || reply.error != B_OK) + return 0; + + memcpy(myaddr, reply.data, reply.size); + return reply.size; + } + + return -1; +} + +LONGEST +haiku_child_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, void *readbuf, const void *writebuf, ULONGEST offset, + LONGEST len) +{ + if (!readbuf && !writebuf) + return -1; + + switch (object) { + case TARGET_OBJECT_MEMORY: + { + int write = !readbuf; + return haiku_child_deprecated_xfer_memory (offset, + (write ? (void*)writebuf : readbuf), len, write, NULL, ops); + } + } + + return -1; +} + +static void +haiku_child_files_info (struct target_ops *ignore) +{ + printf_unfiltered ("\tUsing the running image of %s %s.\n", + attach_flag ? "attached" : "child", target_pid_to_str (inferior_ptid)); +} + +static void +haiku_child_kill_inferior (void) +{ + status_t err; + thread_id teamID = ptid_get_pid(inferior_ptid); + + TRACE(("haiku_child_kill_inferior()\n")); + + err = kill_team(teamID); + if (err != B_OK) { + printf_unfiltered ("Failed to kill team %ld: %s\n", teamID, + strerror(err)); + return; + } + + target_mourn_inferior(); +} + +static void +haiku_child_stop_inferior (void) +{ + status_t err; + thread_id threadID = ptid_get_tid(inferior_ptid); + + TRACE(("haiku_child_stop_inferior()\n")); + + err = debug_thread(threadID); + if (err != B_OK) { + printf_unfiltered ("Failed to stop thread %ld: %s\n", threadID, + strerror(err)); + return; + } +} + +static void +haiku_init_debug_create_inferior(int pid) +{ + extern int stop_after_trap; + + // fix inferior_ptid -- fork_inferior() sets the process ID only + inferior_ptid = ptid_build (pid, 0, pid); + // team ID == team main thread ID under Haiku + + haiku_init_child_debugging(pid, false); + + // eat the initial `debugged' event caused by wait_for_inferior() +// TODO: Maybe we should just dequeue the event and continue the thread instead +// of using gdb's mechanism, since I don't know what undesired side-effects +// they may have. + clear_proceed_status (); + init_wait_for_inferior (); + + if (STARTUP_WITH_SHELL) + inferior_ignoring_startup_exec_events = 2; + else + inferior_ignoring_startup_exec_events = 1; + inferior_ignoring_leading_exec_events = 0; + + while (true) { + thread_debug_info *thread; + + stop_soon = STOP_QUIETLY; + wait_for_inferior(); + + if (stop_signal == TARGET_SIGNAL_TRAP) { + thread = haiku_find_thread(&sTeamDebugInfo, pid); + + if (thread && thread->stopped + && thread->last_event->message + == B_DEBUGGER_MESSAGE_IMAGE_CREATED + && thread->last_event->data.image_created.info.type + == B_APP_IMAGE + && thread->reprocess_event == 0 + && inferior_ignoring_startup_exec_events <= 0) { + // This is the trap for the last (second, if started via shell) + // `load app image' event. Be done. + break; + } + } + + resume(0, stop_signal); + } + stop_soon = NO_STOP_QUIETLY; + +// startup_inferior (START_INFERIOR_TRAPS_EXPECTED); + +//while (1) { +// stop_after_trap = 1; +// wait_for_inferior (); +// if (debugThread && stop_signal != TARGET_SIGNAL_TRAP) +// resume (0, stop_signal); +// else +// break; +//} +//stop_after_trap = 0; + + + +// while (1) { +// thread_debug_info *thread; +// +// stop_after_trap = 1; +// wait_for_inferior (); +//// TODO: Catch deadly events, so that we won't block here. +// +// thread = haiku_find_thread(&sTeamDebugInfo, pid); +//TRACE(("haiku_init_debug_create_inferior(): wait_for_inferior() returned: " +//"thread: %p (%ld)\n", thread, (thread ? thread->thread : -1))); +// if (thread && thread->stopped +// && thread->last_event->message +// == B_DEBUGGER_MESSAGE_IMAGE_CREATED +// && thread->last_event->data.image_created.info.type +// == B_APP_IMAGE) { +//TRACE(("haiku_init_debug_create_inferior(): Got an `app image created' " +//"message\n")); +// break; +// } +// +// resume (0, stop_signal); +// } +} + +static void +haiku_child_create_inferior (char *exec_file, char *allargs, char **env, + int from_tty) +{ + TRACE(("haiku_child_create_inferior(`%s', `%s', %p, %d)\n", exec_file, + allargs, env, from_tty)); + + fork_inferior (exec_file, allargs, env, wait_for_debugger, + haiku_init_debug_create_inferior, NULL, NULL); + + +observer_notify_inferior_created (¤t_target, from_tty); +proceed ((CORE_ADDR) - 1, TARGET_SIGNAL_0, 0); + + + // TODO: Anything more to do here? +} + +static void +haiku_child_mourn_inferior (void) +{ + TRACE(("haiku_child_mourn_inferior()\n")); + + haiku_cleanup_team_debug_info(); + unpush_target (sHaikuTarget); + generic_mourn_inferior (); +} + +static int +haiku_child_can_run (void) +{ + return 1; +} + +static int +haiku_child_thread_alive (ptid_t ptid) +{ + thread_info info; + + TRACE(("haiku_child_thread_alive(`%s')\n", haiku_pid_to_str(ptid))); + + return (get_thread_info(ptid_get_tid(ptid), &info) == B_OK); +} + +static char * +haiku_pid_to_str (ptid_t ptid) +{ + static char buffer[B_OS_NAME_LENGTH + 64 + 64]; + team_info teamInfo; + thread_info threadInfo; + status_t error; + + // get the team info for the target team + error = get_team_info(ptid_get_pid(ptid), &teamInfo); + if (error != B_OK) { + sprintf(buffer, "invalid team ID %d", ptid_get_pid(ptid)); + return buffer; + } + + // get the thread info for the target thread + error = get_thread_info(ptid_get_tid(ptid), &threadInfo); + if (error != B_OK) { + sprintf(buffer, "team %.*s (%ld) invalid thread ID %ld", + (int)sizeof(teamInfo.args), teamInfo.args, teamInfo.team, + ptid_get_tid(ptid)); + return buffer; + } + + sprintf(buffer, "team %.*s (%ld) thread %s (%ld)", + (int)sizeof(teamInfo.args), teamInfo.args, teamInfo.team, + threadInfo.name, threadInfo.thread); + + return buffer; +} + +char * +haiku_child_pid_to_exec_file (int pid) +{ + static char buffer[B_PATH_NAME_LENGTH]; + + // The only way to get the path to the application's executable seems to + // be to get an image_info of its image, which also contains a path. + // Several images may belong to the team (libraries, add-ons), but only + // the one in question should be typed B_APP_IMAGE. + image_info info; + int32 cookie = 0; + + while (get_next_image_info(0, &cookie, &info) == B_OK) { + if (info.type == B_APP_IMAGE) { + strncpy(buffer, info.name, B_PATH_NAME_LENGTH - 1); + buffer[B_PATH_NAME_LENGTH - 1] = 0; + return buffer; + } + } + + return NULL; +} + + +void +_initialize_haiku_nat (void) +{ + // child operations + struct target_ops *t = XZALLOC (struct target_ops); + t->to_shortname = "child"; + t->to_longname = "Haiku child process"; + t->to_doc = "Haiku child process (started by the \"run\" command)."; + t->to_open = haiku_child_open; + t->to_close = haiku_child_close; + t->to_attach = haiku_child_attach; + t->to_detach = haiku_child_detach; + t->to_resume = haiku_child_resume; + t->to_wait = haiku_child_wait; + t->to_fetch_registers = haiku_child_fetch_inferior_registers; + t->to_store_registers = haiku_child_store_inferior_registers; + t->to_prepare_to_store = haiku_child_prepare_to_store; + t->deprecated_xfer_memory = haiku_child_deprecated_xfer_memory; + t->to_xfer_partial = haiku_child_xfer_partial; + t->to_files_info = haiku_child_files_info; + + t->to_insert_breakpoint = memory_insert_breakpoint; + t->to_remove_breakpoint = memory_remove_breakpoint; + // TODO: We can do better. If we wanted to. + + // TODO: The following terminal calls are not documented or not yet + // understood by me. + t->to_terminal_init = terminal_init_inferior; + t->to_terminal_inferior = terminal_inferior; + t->to_terminal_ours_for_output = terminal_ours_for_output; + t->to_terminal_ours = terminal_ours; + t->to_terminal_save_ours = terminal_save_ours; + t->to_terminal_info = child_terminal_info; + + t->to_kill = haiku_child_kill_inferior; + t->to_stop = haiku_child_stop_inferior; + t->to_create_inferior = haiku_child_create_inferior; + t->to_mourn_inferior = haiku_child_mourn_inferior; + t->to_can_run = haiku_child_can_run; + t->to_thread_alive = haiku_child_thread_alive; +// How about to_find_new_threads? Perhaps not necessary, as we could be informed +// about thread creation/deletion. + t->to_pid_to_str = haiku_pid_to_str; + t->to_pid_to_exec_file = haiku_child_pid_to_exec_file; + + t->to_stratum = process_stratum; + t->to_has_all_memory = 1; + t->to_has_memory = 1; + t->to_has_stack = 1; + t->to_has_registers = 1; + t->to_has_execution = 1; + t->to_magic = OPS_MAGIC; + + sHaikuTarget = t; + + add_target (t); +} diff --git a/src/bin/gdb/gdb/haiku-nat.h b/src/bin/gdb/gdb/haiku-nat.h new file mode 100644 index 0000000000..9f68f59535 --- /dev/null +++ b/src/bin/gdb/gdb/haiku-nat.h @@ -0,0 +1,42 @@ +/* Haiku native-dependent definitions. + + Copyright 2005 Ingo Weinhold . + + This file is part of GDB. + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef HAIKU_NAT_H +#define HAIKU_NAT_H + +#include + +/* Required by haiku-nat.c, implemented in -haiku-nat.c. */ + +void haiku_supply_registers(int reg, const debug_cpu_state *cpuState); +void haiku_collect_registers(int reg, debug_cpu_state *cpuState); + + +struct haiku_image_info; + +/* Function used by solib-haiku.c to iterate through the list of images of + the inferior. + TODO: This must go, since it works only in a native debugger. We can + probably tunnel these data through the xfer_memory() function. +*/ +struct haiku_image_info *haiku_get_next_image_info(int lastID); + +#endif /* HAIKU_NAT_H */ diff --git a/src/bin/gdb/gdb/haiku-tdep.c b/src/bin/gdb/gdb/haiku-tdep.c new file mode 100644 index 0000000000..d95255dc79 --- /dev/null +++ b/src/bin/gdb/gdb/haiku-tdep.c @@ -0,0 +1,22 @@ +/* Haiku target-dependent common to multiple platforms. + + Copyright 2005 Ingo Weinhold . + + This file is part of GDB. + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" diff --git a/src/bin/gdb/gdb/i386-haiku-nat.c b/src/bin/gdb/gdb/i386-haiku-nat.c new file mode 100644 index 0000000000..af72bd4b27 --- /dev/null +++ b/src/bin/gdb/gdb/i386-haiku-nat.c @@ -0,0 +1,85 @@ +/* Native-dependent code for Haiku i386. + + Copyright 2005 Ingo Weinhold . + + This file is part of GDB. + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "haiku-nat.h" +#include "i386-tdep.h" +#include "i387-tdep.h" +#include "inferior.h" +#include "regcache.h" +#include "target.h" + +/* Offset in `struct debug_cpu_state' where MEMBER is stored. */ +#define REG_OFFSET(member) offsetof (struct debug_cpu_state, member) + +/* At kHaikuI386RegOffset[REGNUM] you'll find the offset in `struct + debug_cpu_state' where the GDB register REGNUM is stored. */ +static int kHaikuI386RegOffset[] = { + REG_OFFSET (eax), + REG_OFFSET (ecx), + REG_OFFSET (edx), + REG_OFFSET (ebx), + REG_OFFSET (user_esp), + REG_OFFSET (ebp), + REG_OFFSET (esi), + REG_OFFSET (edi), + REG_OFFSET (eip), + REG_OFFSET (eflags), + REG_OFFSET (cs), + REG_OFFSET (user_ss), + REG_OFFSET (ds), + REG_OFFSET (es), + REG_OFFSET (fs), + REG_OFFSET (gs) +}; + + +void +haiku_supply_registers(int reg, const debug_cpu_state *cpuState) +{ + if (reg == -1) { + int i; + for (i = 0; i < NUM_REGS; i++) + haiku_supply_registers(i, cpuState); + } else if (reg < I386_ST0_REGNUM) { + int offset = kHaikuI386RegOffset[reg]; + regcache_raw_supply (current_regcache, reg, (char*)cpuState + offset); + } else { + i387_supply_fxsave (current_regcache, -1, cpuState->extended_regs); + } +} + + +void +haiku_collect_registers(int reg, debug_cpu_state *cpuState) +{ + if (reg == -1) { + int i; + for (i = 0; i < NUM_REGS; i++) + haiku_collect_registers(i, cpuState); + } else if (reg < I386_ST0_REGNUM) { + int offset = kHaikuI386RegOffset[reg]; + regcache_raw_collect (current_regcache, reg, (char*)cpuState + offset); + } else { + i387_collect_fsave (current_regcache, -1, cpuState->extended_regs); + } +} + diff --git a/src/bin/gdb/gdb/i386-haiku-tdep.c b/src/bin/gdb/gdb/i386-haiku-tdep.c new file mode 100644 index 0000000000..eda144d58b --- /dev/null +++ b/src/bin/gdb/gdb/i386-haiku-tdep.c @@ -0,0 +1,77 @@ +/* Target-dependent code for Haiku i386. + + Copyright 2005 Ingo Weinhold . + + This file is part of GDB. + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "gdbarch.h" +#include "i386-tdep.h" +#include "osabi.h" + +//#define TRACE_I386_HAIKU_NAT +#ifdef TRACE_I386_HAIKU_NAT + #define TRACE(x) printf x +#else + #define TRACE(x) while (false) {} +#endif + +static void +i386_haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + // Haiku uses ELF. + i386_elf_init_abi (info, gdbarch); + + // the offset of the PC in the jmp_buf structure (cf. setjmp(), longjmp()) + tdep->jb_pc_offset = 20; + +// TODO: Signal support. +// tdep->sigtramp_p = i386_haiku_sigtramp_p; +// tdep->sigcontext_addr = i386_haiku_sigcontext_addr; +// tdep->sc_reg_offset = i386_haiku_sc_reg_offset; +// tdep->sc_num_regs = ARRAY_SIZE (i386_haiku_sc_reg_offset); + +// We don't need this at the moment. The Haiku runtime loader also relocates +// R_386_JMP_SLOT entries. No lazy resolving is done. +// set_gdbarch_skip_solib_resolver (gdbarch, haiku_skip_solib_resolver); +} + + +static enum gdb_osabi +i386_haiku_osabi_sniffer (bfd * abfd) +{ + char *targetName = bfd_get_target (abfd); + + if (strcmp (targetName, "elf32-i386") == 0) + return GDB_OSABI_HAIKU; + + return GDB_OSABI_UNKNOWN; +} + + +void +_initialize_i386_haiku_tdep (void) +{ + gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour, + i386_haiku_osabi_sniffer); + + gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_HAIKU, + i386_haiku_init_abi); +}