|  | /* Copyright (C) 2009-2025 Free Software Foundation, Inc. | 
|  |  | 
|  | 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 3 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, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include JIT_READER_H  /* Please see jit-reader.exp for an explanation.  */ | 
|  | #include "jit-reader-host.h" | 
|  |  | 
|  | GDB_DECLARE_GPL_COMPATIBLE_READER; | 
|  |  | 
|  | enum register_mapping | 
|  | { | 
|  | AMD64_RA = 16, | 
|  | AMD64_RBP = 6, | 
|  | AMD64_RSP = 7, | 
|  | }; | 
|  |  | 
|  | struct reader_state | 
|  | { | 
|  | struct { | 
|  | uintptr_t begin; | 
|  | uintptr_t end; | 
|  | } func_stack_mangle; | 
|  | }; | 
|  |  | 
|  | static enum gdb_status | 
|  | read_debug_info (struct gdb_reader_funcs *self, | 
|  | struct gdb_symbol_callbacks *cbs, | 
|  | void *memory, long memory_sz) | 
|  | { | 
|  | struct jithost_abi *symfile = memory; | 
|  | struct gdb_object *object = cbs->object_open (cbs); | 
|  | struct gdb_symtab *symtab = cbs->symtab_open (cbs, object, ""); | 
|  |  | 
|  | struct reader_state *state = (struct reader_state *) self->priv_data; | 
|  |  | 
|  | /* Record the stack mangle function's range, for the unwinder.  */ | 
|  | state->func_stack_mangle.begin | 
|  | = (uintptr_t) symfile->function_stack_mangle.begin; | 
|  | state->func_stack_mangle.end | 
|  | = (uintptr_t) symfile->function_stack_mangle.end; | 
|  |  | 
|  | cbs->block_open (cbs, symtab, NULL, | 
|  | (GDB_CORE_ADDR) symfile->function_stack_mangle.begin, | 
|  | (GDB_CORE_ADDR) symfile->function_stack_mangle.end, | 
|  | "jit_function_stack_mangle"); | 
|  |  | 
|  | /* Add some line table information.  This ensures that GDB can handle | 
|  | accepting this information, and can scan the table.  However, this | 
|  | information is constructed such that none of the tests actually hit any | 
|  | of these line entries.  */ | 
|  | struct gdb_line_mapping mangle_lines[] = | 
|  | { | 
|  | { 1, (GDB_CORE_ADDR) symfile->function_stack_mangle.begin + 0 }, | 
|  | { 0, (GDB_CORE_ADDR) symfile->function_stack_mangle.begin + 1 }, | 
|  | }; | 
|  | int mangle_nlines = sizeof (mangle_lines) / sizeof (mangle_lines[0]); | 
|  | cbs->line_mapping_add (cbs, symtab, mangle_nlines, mangle_lines); | 
|  |  | 
|  | cbs->block_open (cbs, symtab, NULL, | 
|  | (GDB_CORE_ADDR) symfile->function_add.begin, | 
|  | (GDB_CORE_ADDR) symfile->function_add.end, | 
|  | "jit_function_add"); | 
|  |  | 
|  | cbs->symtab_close (cbs, symtab); | 
|  | cbs->object_close (cbs, object); | 
|  | return GDB_SUCCESS; | 
|  | } | 
|  |  | 
|  | static void | 
|  | free_reg_value (struct gdb_reg_value *value) | 
|  | { | 
|  | free (value); | 
|  | } | 
|  |  | 
|  | static void | 
|  | write_register (struct gdb_unwind_callbacks *callbacks, int dw_reg, | 
|  | uintptr_t value) | 
|  | { | 
|  | const int size = sizeof (uintptr_t); | 
|  | struct gdb_reg_value *reg_val = | 
|  | malloc (sizeof (struct gdb_reg_value) + size - 1); | 
|  | reg_val->defined = 1; | 
|  | reg_val->free = free_reg_value; | 
|  |  | 
|  | memcpy (reg_val->value, &value, size); | 
|  | callbacks->reg_set (callbacks, dw_reg, reg_val); | 
|  | } | 
|  |  | 
|  | static int | 
|  | read_register (struct gdb_unwind_callbacks *callbacks, int dw_reg, | 
|  | uintptr_t *value) | 
|  | { | 
|  | const int size = sizeof (uintptr_t); | 
|  | struct gdb_reg_value *reg_val = callbacks->reg_get (callbacks, dw_reg); | 
|  | if (reg_val->size != size || !reg_val->defined) | 
|  | { | 
|  | reg_val->free (reg_val); | 
|  | return 0; | 
|  | } | 
|  | memcpy (value, reg_val->value, size); | 
|  | reg_val->free (reg_val); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Read the stack pointer into *VALUE.  IP is the address the inferior | 
|  | is currently stopped at.  Takes care of demangling the stack | 
|  | pointer if necessary.  */ | 
|  |  | 
|  | static int | 
|  | read_sp (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs, | 
|  | uintptr_t ip, uintptr_t *value) | 
|  | { | 
|  | struct reader_state *state = (struct reader_state *) self->priv_data; | 
|  | uintptr_t sp; | 
|  |  | 
|  | if (!read_register (cbs, AMD64_RSP, &sp)) | 
|  | return GDB_FAIL; | 
|  |  | 
|  | /* If stopped at the instruction after the "xor $-1, %rsp", demangle | 
|  | the stack pointer back.  */ | 
|  | if (ip == state->func_stack_mangle.begin + 5) | 
|  | sp ^= (uintptr_t) -1; | 
|  |  | 
|  | *value = sp; | 
|  | return GDB_SUCCESS; | 
|  | } | 
|  |  | 
|  | static enum gdb_status | 
|  | unwind_frame (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs) | 
|  | { | 
|  | const int word_size = sizeof (uintptr_t); | 
|  | uintptr_t prev_sp, this_sp; | 
|  | uintptr_t prev_ip, this_ip; | 
|  | uintptr_t prev_bp, this_bp; | 
|  | struct reader_state *state = (struct reader_state *) self->priv_data; | 
|  |  | 
|  | if (!read_register (cbs, AMD64_RA, &this_ip)) | 
|  | return GDB_FAIL; | 
|  |  | 
|  | if (this_ip >= state->func_stack_mangle.end | 
|  | || this_ip < state->func_stack_mangle.begin) | 
|  | return GDB_FAIL; | 
|  |  | 
|  | /* Unwind RBP in order to make the unwinder that tries to unwind | 
|  | from the just-unwound frame happy.  */ | 
|  | if (!read_register (cbs, AMD64_RBP, &this_bp)) | 
|  | return GDB_FAIL; | 
|  | /* RBP is unmodified.  */ | 
|  | prev_bp = this_bp; | 
|  |  | 
|  | /* Fetch the demangled stack pointer.  */ | 
|  | if (!read_sp (self, cbs, this_ip, &this_sp)) | 
|  | return GDB_FAIL; | 
|  |  | 
|  | /* The return address is saved on the stack.  */ | 
|  | if (cbs->target_read (this_sp, &prev_ip, word_size) == GDB_FAIL) | 
|  | return GDB_FAIL; | 
|  | prev_sp = this_sp + word_size; | 
|  |  | 
|  | write_register (cbs, AMD64_RA, prev_ip); | 
|  | write_register (cbs, AMD64_RSP, prev_sp); | 
|  | write_register (cbs, AMD64_RBP, prev_bp); | 
|  | return GDB_SUCCESS; | 
|  | } | 
|  |  | 
|  | static struct gdb_frame_id | 
|  | get_frame_id (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs) | 
|  | { | 
|  | struct reader_state *state = (struct reader_state *) self->priv_data; | 
|  | struct gdb_frame_id frame_id; | 
|  | uintptr_t ip; | 
|  | uintptr_t sp; | 
|  |  | 
|  | read_register (cbs, AMD64_RA, &ip); | 
|  | read_sp (self, cbs, ip, &sp); | 
|  |  | 
|  | frame_id.code_address = (GDB_CORE_ADDR) state->func_stack_mangle.begin; | 
|  | frame_id.stack_address = (GDB_CORE_ADDR) sp; | 
|  |  | 
|  | return frame_id; | 
|  | } | 
|  |  | 
|  | static void | 
|  | destroy_reader (struct gdb_reader_funcs *self) | 
|  | { | 
|  | free (self->priv_data); | 
|  | free (self); | 
|  | } | 
|  |  | 
|  | struct gdb_reader_funcs * | 
|  | gdb_init_reader (void) | 
|  | { | 
|  | struct reader_state *state = calloc (1, sizeof (struct reader_state)); | 
|  | struct gdb_reader_funcs *reader_funcs = | 
|  | malloc (sizeof (struct gdb_reader_funcs)); | 
|  |  | 
|  | reader_funcs->reader_version = GDB_READER_INTERFACE_VERSION; | 
|  | reader_funcs->priv_data = state; | 
|  | reader_funcs->read = read_debug_info; | 
|  | reader_funcs->unwind = unwind_frame; | 
|  | reader_funcs->get_frame_id = get_frame_id; | 
|  | reader_funcs->destroy = destroy_reader; | 
|  |  | 
|  | return reader_funcs; | 
|  | } |