mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-21 01:12:32 +08:00
552f1157c6
It is better to rename floatformats_ia64_quad to floatformats_ieee_quad to reflect the reality, and then we can clean up the related code. As Tom Tromey said [1]: These files are maintained in gcc and then imported into the binutils-gdb repository, so any changes to them will have to be proposed there first. the related changes have been merged into gcc master now [2], it is time to do it for gdb. [1] https://sourceware.org/pipermail/gdb-patches/2022-March/186569.html [2] https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=b2dff6b2d9d6 Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
321 lines
11 KiB
C
321 lines
11 KiB
C
/* Target-dependent code for the LoongArch architecture, for GDB.
|
|
|
|
Copyright (C) 2022 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 "defs.h"
|
|
#include "arch-utils.h"
|
|
#include "dwarf2/frame.h"
|
|
#include "elf-bfd.h"
|
|
#include "frame-unwind.h"
|
|
#include "loongarch-tdep.h"
|
|
#include "target-descriptions.h"
|
|
#include "trad-frame.h"
|
|
#include "user-regs.h"
|
|
|
|
/* Implement the loongarch_skip_prologue gdbarch method. */
|
|
|
|
static CORE_ADDR
|
|
loongarch_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
|
|
{
|
|
CORE_ADDR func_addr;
|
|
|
|
/* See if we can determine the end of the prologue via the symbol table.
|
|
If so, then return either PC, or the PC after the prologue, whichever
|
|
is greater. */
|
|
if (find_pc_partial_function (pc, nullptr, &func_addr, nullptr))
|
|
{
|
|
CORE_ADDR post_prologue_pc
|
|
= skip_prologue_using_sal (gdbarch, func_addr);
|
|
if (post_prologue_pc != 0)
|
|
return std::max (pc, post_prologue_pc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Adjust the address downward (direction of stack growth) so that it
|
|
is correctly aligned for a new stack frame. */
|
|
|
|
static CORE_ADDR
|
|
loongarch_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
|
|
{
|
|
return align_down (addr, 16);
|
|
}
|
|
|
|
/* Generate, or return the cached frame cache for LoongArch frame unwinder. */
|
|
|
|
static struct trad_frame_cache *
|
|
loongarch_frame_cache (struct frame_info *this_frame, void **this_cache)
|
|
{
|
|
struct gdbarch *gdbarch = get_frame_arch (this_frame);
|
|
struct trad_frame_cache *cache;
|
|
CORE_ADDR pc;
|
|
|
|
if (*this_cache != nullptr)
|
|
return (struct trad_frame_cache *) *this_cache;
|
|
|
|
cache = trad_frame_cache_zalloc (this_frame);
|
|
*this_cache = cache;
|
|
|
|
loongarch_gdbarch_tdep *tdep = (loongarch_gdbarch_tdep *) gdbarch_tdep (gdbarch);
|
|
trad_frame_set_reg_realreg (cache, gdbarch_pc_regnum (gdbarch), tdep->regs.ra);
|
|
|
|
pc = get_frame_address_in_block (this_frame);
|
|
trad_frame_set_id (cache, frame_id_build_unavailable_stack (pc));
|
|
|
|
return cache;
|
|
}
|
|
|
|
/* Implement the this_id callback for LoongArch frame unwinder. */
|
|
|
|
static void
|
|
loongarch_frame_this_id (struct frame_info *this_frame, void **prologue_cache,
|
|
struct frame_id *this_id)
|
|
{
|
|
struct trad_frame_cache *info;
|
|
|
|
info = loongarch_frame_cache (this_frame, prologue_cache);
|
|
trad_frame_get_id (info, this_id);
|
|
}
|
|
|
|
/* Implement the prev_register callback for LoongArch frame unwinder. */
|
|
|
|
static struct value *
|
|
loongarch_frame_prev_register (struct frame_info *this_frame,
|
|
void **prologue_cache, int regnum)
|
|
{
|
|
struct trad_frame_cache *info;
|
|
|
|
info = loongarch_frame_cache (this_frame, prologue_cache);
|
|
return trad_frame_get_register (info, this_frame, regnum);
|
|
}
|
|
|
|
static const struct frame_unwind loongarch_frame_unwind = {
|
|
"loongarch prologue",
|
|
/*.type =*/NORMAL_FRAME,
|
|
/*.stop_reason =*/default_frame_unwind_stop_reason,
|
|
/*.this_id =*/loongarch_frame_this_id,
|
|
/*.prev_register =*/loongarch_frame_prev_register,
|
|
/*.unwind_data =*/nullptr,
|
|
/*.sniffer =*/default_frame_sniffer,
|
|
/*.dealloc_cache =*/nullptr,
|
|
/*.prev_arch =*/nullptr,
|
|
};
|
|
|
|
/* Implement the "dwarf2_reg_to_regnum" gdbarch method. */
|
|
|
|
static int
|
|
loongarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int num)
|
|
{
|
|
loongarch_gdbarch_tdep *tdep = (loongarch_gdbarch_tdep *) gdbarch_tdep (gdbarch);
|
|
auto regs = tdep->regs;
|
|
|
|
if (0 <= num && num < 32)
|
|
return regs.r + num;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static constexpr gdb_byte loongarch_default_breakpoint[] = {0x05, 0x00, 0x2a, 0x00};
|
|
typedef BP_MANIPULATION (loongarch_default_breakpoint) loongarch_breakpoint;
|
|
|
|
/* Extract a set of required target features out of ABFD. If ABFD is nullptr
|
|
then a LOONGARCH_GDBARCH_FEATURES is returned in its default state. */
|
|
|
|
static struct loongarch_gdbarch_features
|
|
loongarch_features_from_bfd (const bfd *abfd)
|
|
{
|
|
struct loongarch_gdbarch_features features;
|
|
|
|
/* Now try to improve on the defaults by looking at the binary we are
|
|
going to execute. We assume the user knows what they are doing and
|
|
that the target will match the binary. Remember, this code path is
|
|
only used at all if the target hasn't given us a description, so this
|
|
is really a last ditched effort to do something sane before giving
|
|
up. */
|
|
if (abfd != nullptr && bfd_get_flavour (abfd) == bfd_target_elf_flavour)
|
|
{
|
|
unsigned char eclass = elf_elfheader (abfd)->e_ident[EI_CLASS];
|
|
|
|
if (eclass == ELFCLASS32)
|
|
features.xlen = 4;
|
|
else if (eclass == ELFCLASS64)
|
|
features.xlen = 8;
|
|
else
|
|
internal_error (__FILE__, __LINE__,
|
|
_("unknown ELF header class %d"), eclass);
|
|
}
|
|
|
|
return features;
|
|
}
|
|
|
|
/* Find a suitable default target description. Use the contents of INFO,
|
|
specifically the bfd object being executed, to guide the selection of a
|
|
suitable default target description. */
|
|
|
|
static const struct target_desc *
|
|
loongarch_find_default_target_description (const struct gdbarch_info info)
|
|
{
|
|
/* Extract desired feature set from INFO. */
|
|
struct loongarch_gdbarch_features features
|
|
= loongarch_features_from_bfd (info.abfd);
|
|
|
|
/* If the XLEN field is still 0 then we got nothing useful from INFO.BFD,
|
|
maybe there was no bfd object. In this case we fall back to a minimal
|
|
useful target with no floating point, the x-register size is selected
|
|
based on the architecture from INFO. */
|
|
if (features.xlen == 0)
|
|
features.xlen = info.bfd_arch_info->bits_per_address == 32 ? 4 : 8;
|
|
|
|
/* Now build a target description based on the feature set. */
|
|
return loongarch_lookup_target_description (features);
|
|
}
|
|
|
|
/* Initialize the current architecture based on INFO */
|
|
|
|
static struct gdbarch *
|
|
loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
|
{
|
|
const struct target_desc *tdesc = info.target_desc;
|
|
|
|
/* Ensure we always have a target description. */
|
|
if (!tdesc_has_registers (tdesc))
|
|
tdesc = loongarch_find_default_target_description (info);
|
|
|
|
const struct tdesc_feature *feature_cpu
|
|
= tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.base");
|
|
if (feature_cpu == nullptr)
|
|
return nullptr;
|
|
|
|
int xlen_bitsize = tdesc_register_bitsize (feature_cpu, "pc");
|
|
struct loongarch_gdbarch_features features;
|
|
features.xlen = (xlen_bitsize / 8);
|
|
|
|
size_t regnum = 0;
|
|
tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
|
|
loongarch_gdbarch_tdep *tdep = new loongarch_gdbarch_tdep;
|
|
tdep->regs.r = regnum;
|
|
|
|
/* Validate the description provides the mandatory base registers
|
|
and allocate their numbers. */
|
|
bool valid_p = true;
|
|
for (int i = 0; i < 32; i++)
|
|
valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (), regnum++,
|
|
loongarch_r_normal_name[i] + 1);
|
|
valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (),
|
|
tdep->regs.pc = regnum++, "pc");
|
|
valid_p &= tdesc_numbered_register (feature_cpu, tdesc_data.get (),
|
|
tdep->regs.badv = regnum++, "badv");
|
|
if (!valid_p)
|
|
return nullptr;
|
|
|
|
/* LoongArch code is always little-endian. */
|
|
info.byte_order_for_code = BFD_ENDIAN_LITTLE;
|
|
|
|
/* Have a look at what the supplied (if any) bfd object requires of the
|
|
target, then check that this matches with what the target is
|
|
providing. */
|
|
struct loongarch_gdbarch_features abi_features
|
|
= loongarch_features_from_bfd (info.abfd);
|
|
|
|
/* If the ABI_FEATURES xlen is 0 then this indicates we got no useful abi
|
|
features from the INFO object. In this case we just treat the
|
|
hardware features as defining the abi. */
|
|
if (abi_features.xlen == 0)
|
|
abi_features = features;
|
|
|
|
/* Find a candidate among the list of pre-declared architectures. */
|
|
for (arches = gdbarch_list_lookup_by_info (arches, &info);
|
|
arches != nullptr;
|
|
arches = gdbarch_list_lookup_by_info (arches->next, &info))
|
|
{
|
|
/* Check that the feature set of the ARCHES matches the feature set
|
|
we are looking for. If it doesn't then we can't reuse this
|
|
gdbarch. */
|
|
loongarch_gdbarch_tdep *candidate_tdep
|
|
= (loongarch_gdbarch_tdep *) gdbarch_tdep (arches->gdbarch);
|
|
|
|
if (candidate_tdep->abi_features != abi_features)
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
if (arches != nullptr)
|
|
return arches->gdbarch;
|
|
|
|
/* None found, so create a new architecture from the information provided. */
|
|
struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep);
|
|
tdep->abi_features = abi_features;
|
|
|
|
/* Target data types. */
|
|
set_gdbarch_short_bit (gdbarch, 16);
|
|
set_gdbarch_int_bit (gdbarch, 32);
|
|
set_gdbarch_long_bit (gdbarch, info.bfd_arch_info->bits_per_address);
|
|
set_gdbarch_long_long_bit (gdbarch, 64);
|
|
set_gdbarch_float_bit (gdbarch, 32);
|
|
set_gdbarch_double_bit (gdbarch, 64);
|
|
set_gdbarch_long_double_bit (gdbarch, 128);
|
|
set_gdbarch_long_double_format (gdbarch, floatformats_ieee_quad);
|
|
set_gdbarch_ptr_bit (gdbarch, info.bfd_arch_info->bits_per_address);
|
|
set_gdbarch_char_signed (gdbarch, 0);
|
|
|
|
info.target_desc = tdesc;
|
|
info.tdesc_data = tdesc_data.get ();
|
|
|
|
/* Information about registers. */
|
|
tdep->regs.ra = tdep->regs.r + 1;
|
|
tdep->regs.sp = tdep->regs.r + 3;
|
|
set_gdbarch_num_regs (gdbarch, regnum);
|
|
set_gdbarch_sp_regnum (gdbarch, tdep->regs.sp);
|
|
set_gdbarch_pc_regnum (gdbarch, tdep->regs.pc);
|
|
|
|
/* Finalise the target description registers. */
|
|
tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data));
|
|
|
|
/* Advance PC across function entry code. */
|
|
set_gdbarch_skip_prologue (gdbarch, loongarch_skip_prologue);
|
|
|
|
/* Stack grows downward. */
|
|
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
|
|
|
|
/* Frame info. */
|
|
set_gdbarch_frame_align (gdbarch, loongarch_frame_align);
|
|
|
|
/* Breakpoint manipulation. */
|
|
set_gdbarch_breakpoint_kind_from_pc (gdbarch, loongarch_breakpoint::kind_from_pc);
|
|
set_gdbarch_sw_breakpoint_from_kind (gdbarch, loongarch_breakpoint::bp_from_kind);
|
|
|
|
/* Frame unwinders. Use DWARF debug info if available, otherwise use our own unwinder. */
|
|
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, loongarch_dwarf2_reg_to_regnum);
|
|
dwarf2_append_unwinders (gdbarch);
|
|
frame_unwind_append_unwinder (gdbarch, &loongarch_frame_unwind);
|
|
|
|
/* Hook in OS ABI-specific overrides, if they have been registered. */
|
|
gdbarch_init_osabi (info, gdbarch);
|
|
|
|
return gdbarch;
|
|
}
|
|
|
|
void _initialize_loongarch_tdep ();
|
|
void
|
|
_initialize_loongarch_tdep ()
|
|
{
|
|
gdbarch_register (bfd_arch_loongarch, loongarch_gdbarch_init, nullptr);
|
|
}
|