tools/elfutils: update MIPS support patches

The original patch series partially added by commit
f97da2c61 ("tools/elfutils: add missing MIPS reloc support")
now has a v3 which has half of it's commits accepted.

To prepare for updating to the new release,
use the new series that includes backports.

Manually adjusted patch:
 - 010-backport-mips-support-reloc.patch

Add patch:
 - 011-backport-mips-support-strip.patch
 - 012-backport-mips-support-readelf.patch
 - 013-backport-mips-support-elflint.patch
 - 014-backport-mips-support-stack.patch
 - 015-backport-mips-support-regs.patch

Tested-by: Tony Ambardar <itugrok@yahoo.com>
Signed-off-by: Michael Pratt <mcpratt@pm.me>
Link: https://github.com/openwrt/openwrt/pull/16522
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Michael Pratt 2024-10-21 09:47:25 -04:00 committed by Robert Marko
parent c83941d288
commit 5bb84fb53b
6 changed files with 2220 additions and 2232 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,230 @@
In mips64 little-endian, r_info consists of four byte fields(contains
three reloc types) and a 32-bit symbol index. In order to adapt
GELF_R_SYM and GELF_R_TYPE, need convert raw data to get correct symbol
index and type.
libelf/elf_getdata.c: Some eu-utils use read-mmap method to map file,
so we need to malloc and memcpy raw data to avoid segment fault. After
modification, the correct value are saved in the malloced memory not in
process address space.
libelf/elf_updata.c: Because we converted the relocation info in mips
order when we call elf_getdata.c, so we need to convert the modified data
in original order bits before writing the data to the file.
Signed-off-by: Ying Huang <ying.huang@oss.cipunited.com>
---
libelf/elf_getdata.c | 132 ++++++++++++++++++++++++++++++++++++++++++-
libelf/elf_update.c | 53 +++++++++++++++++
2 files changed, 183 insertions(+), 2 deletions(-)
--- a/libelf/elf_getdata.c
+++ b/libelf/elf_getdata.c
@@ -135,6 +135,119 @@ __libelf_data_type (GElf_Ehdr *ehdr, int
/* Convert the data in the current section. */
static void
+convert_data_for_mips64el (Elf_Scn *scn, int eclass,
+ int data, size_t size, Elf_Type type)
+{
+ /* Do we need to convert the data and/or adjust for alignment? */
+ if (data == MY_ELFDATA || type == ELF_T_BYTE)
+ {
+ /* In order to adapt macro GELF_R_SYM and GELF_R_TYPE on mips64, need to convert
+ relocation info(raw data). Some eu-utils use read-mmap method to map file, so
+ we need to malloc and memcpy raw data to avoid segment fault. After modification,
+ the correct value are saved in the malloced memory not in process address space. */
+ scn->data_base = malloc (size);
+ if (scn->data_base == NULL)
+ {
+ __libelf_seterrno (ELF_E_NOMEM);
+ return;
+ }
+
+ /* The copy will be appropriately aligned for direct access. */
+ memcpy (scn->data_base, scn->rawdata_base, size);
+ }
+ else
+ {
+ xfct_t fp;
+
+ scn->data_base = malloc (size);
+ if (scn->data_base == NULL)
+ {
+ __libelf_seterrno (ELF_E_NOMEM);
+ return;
+ }
+
+ /* Make sure the source is correctly aligned for the conversion
+ function to directly access the data elements. */
+ char *rawdata_source;
+ /* In order to adapt macro GELF_R_SYM and GELF_R_TYPE on mips64, need to convert
+ relocation info(raw data). Some eu-utils use read-mmap method to map file, so
+ we need to malloc and memcpy raw data to avoid segment fault. After modification,
+ the correct value are saved in the malloced memory not in process address space. */
+ rawdata_source = malloc (size);
+ if (rawdata_source == NULL)
+ {
+ __libelf_seterrno (ELF_E_NOMEM);
+ return;
+ }
+
+ /* The copy will be appropriately aligned for direct access. */
+ memcpy (rawdata_source, scn->rawdata_base, size);
+
+ /* Get the conversion function. */
+ fp = __elf_xfctstom[eclass - 1][type];
+
+ fp (scn->data_base, rawdata_source, size, 0);
+
+ if (rawdata_source != scn->rawdata_base)
+ free (rawdata_source);
+ }
+
+ scn->data_list.data.d.d_buf = scn->data_base;
+ scn->data_list.data.d.d_size = size;
+ scn->data_list.data.d.d_type = type;
+ scn->data_list.data.d.d_off = scn->rawdata.d.d_off;
+ scn->data_list.data.d.d_align = scn->rawdata.d.d_align;
+ scn->data_list.data.d.d_version = scn->rawdata.d.d_version;
+
+ scn->data_list.data.s = scn;
+
+ /* In mips64 little-endian, r_info consists of four byte fields(contains
+ three reloc types) and a 32-bit symbol index. In order to adapt
+ GELF_R_SYM and GELF_R_TYPE, need to convert r_info to get correct symbol
+ index and type. */
+ /* references:
+ https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf
+ Page40 && Page41 */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr->sh_type == SHT_REL)
+ {
+ size_t sh_entsize = gelf_fsize (scn->elf, ELF_T_REL, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+ for (int cnt = 0; cnt < nentries; ++cnt)
+ {
+ Elf_Data_Scn *data_scn = (Elf_Data_Scn *) &scn->data_list.data.d;
+ Elf64_Rel *value = &((Elf64_Rel *) data_scn->d.d_buf)[cnt];
+ Elf64_Xword info = value->r_info;
+ value->r_info = (((info & 0xffffffff) << 32)
+ | ((info >> 56) & 0xff)
+ | ((info >> 40) & 0xff00)
+ | ((info >> 24) & 0xff0000)
+ | ((info >> 8) & 0xff000000));
+ ((Elf64_Rel *) data_scn->d.d_buf)[cnt] = *value;
+ }
+ }
+ else if (shdr->sh_type == SHT_RELA)
+ {
+ size_t sh_entsize = gelf_fsize (scn->elf, ELF_T_RELA, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+ for (int cnt = 0; cnt < nentries; cnt++)
+ {
+ Elf_Data_Scn *data_scn = (Elf_Data_Scn *) &scn->data_list.data.d;
+ Elf64_Rela *value = &((Elf64_Rela *) data_scn->d.d_buf)[cnt];
+ Elf64_Xword info = value->r_info;
+ value->r_info = (((info & 0xffffffff) << 32)
+ | ((info >> 56) & 0xff)
+ | ((info >> 40) & 0xff00)
+ | ((info >> 24) & 0xff0000)
+ | ((info >> 8) & 0xff000000));
+ ((Elf64_Rela *) data_scn->d.d_buf)[cnt] = *value;
+ }
+ }
+}
+
+/* Convert the data in the current section. */
+static void
convert_data (Elf_Scn *scn, int eclass,
int data, size_t size, Elf_Type type)
{
@@ -451,8 +564,23 @@ __libelf_set_data_list_rdlock (Elf_Scn *
return;
}
- /* Convert according to the version and the type. */
- convert_data (scn, elf->class,
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (scn->elf, &ehdr_mem);
+ if (shdr != NULL && (shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL) &&
+ scn->elf->class == ELFCLASS64 && ehdr != NULL &&
+ ehdr->e_machine == EM_MIPS && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+ convert_data_for_mips64el (scn, elf->class,
+ (elf->class == ELFCLASS32
+ || (offsetof (struct Elf, state.elf32.ehdr)
+ == offsetof (struct Elf, state.elf64.ehdr))
+ ? elf->state.elf32.ehdr->e_ident[EI_DATA]
+ : elf->state.elf64.ehdr->e_ident[EI_DATA]),
+ scn->rawdata.d.d_size, scn->rawdata.d.d_type);
+ else
+ /* Convert according to the version and the type. */
+ convert_data (scn, elf->class,
(elf->class == ELFCLASS32
|| (offsetof (struct Elf, state.elf32.ehdr)
== offsetof (struct Elf, state.elf64.ehdr))
--- a/libelf/elf_update.c
+++ b/libelf/elf_update.c
@@ -228,7 +228,60 @@ elf_update (Elf *elf, Elf_Cmd cmd)
size = -1;
}
else
+ {
+ /* Because we converted the relocation info in mips order when we call elf_getdata.c,
+ so we need to convert the modified data in original order bits before writing the
+ data to the file. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (scn->elf, &ehdr_mem);
+ if (shdr != NULL && (shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL) &&
+ scn->elf->class == ELFCLASS64 &&
+ ehdr != NULL && ehdr->e_machine == EM_MIPS && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+ {
+ Elf_Data *d = elf_getdata (scn, NULL);
+ if (shdr->sh_type == SHT_REL)
+ {
+ size_t sh_entsize = gelf_fsize (scn->elf, ELF_T_REL, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+ for (int cnt = 0; cnt < nentries; ++cnt)
+ {
+ Elf_Data_Scn *data_scn = (Elf_Data_Scn *) d;
+ Elf64_Rel *value = &((Elf64_Rel *) data_scn->d.d_buf)[cnt];
+ Elf64_Xword info = value->r_info;
+ value->r_info = (info >> 32
+ | ((info << 56) & 0xff00000000000000)
+ | ((info << 40) & 0xff000000000000)
+ | ((info << 24) & 0xff0000000000)
+ | ((info << 8) & 0xff00000000));
+ ((Elf64_Rel *) data_scn->d.d_buf)[cnt] = *value;
+ }
+ }
+ else if (shdr->sh_type == SHT_RELA)
+ {
+ size_t sh_entsize = gelf_fsize (scn->elf, ELF_T_RELA, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+ for (int cnt = 0; cnt < nentries; cnt++)
+ {
+ Elf_Data_Scn *data_scn = (Elf_Data_Scn *) d;
+ Elf64_Rela *value = &((Elf64_Rela *) data_scn->d.d_buf)[cnt];
+ Elf64_Xword info = value->r_info;
+ value->r_info = (info >> 32
+ | ((info << 56) & 0xff00000000000000)
+ | ((info << 40) & 0xff000000000000)
+ | ((info << 24) & 0xff0000000000)
+ | ((info << 8) & 0xff00000000));
+ ((Elf64_Rela *) data_scn->d.d_buf)[cnt] = *value;
+ }
+ }
+ }
+ }
size = write_file (elf, size, change_bo, shnum);
+ }
}
out:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
The errors were:
$ src/elflint --gnu src/nm
section [ 2] '.MIPS.options' contains unknown flag(s) 0x8000000
section [ 7] '.dynsym': symbol 165 (_DYNAMIC_LINKING): non-local section symbol
section [24] '.got' contains invalid processor-specific flag(s) 0x10000000
section [25] '.sdata' contains invalid processor-specific flag(s) 0x10000000
section [29] '.debug_aranges' has wrong type: expected PROGBITS, is MIPS_DWARF
section [30] '.debug_info' has wrong type: expected PROGBITS, is MIPS_DWARF
section [31] '.debug_abbrev' has wrong type: expected PROGBITS, is MIPS_DWARF
section [32] '.debug_line' has wrong type: expected PROGBITS, is MIPS_DWARF
section [33] '.debug_frame' has wrong type: expected PROGBITS, is MIPS_DWARF
section [34] '.debug_str' has wrong type: expected PROGBITS, is MIPS_DWARF
section [35] '.debug_loc' has wrong type: expected PROGBITS, is MIPS_DWARF
section [36] '.debug_ranges' has wrong type: expected PROGBITS, is MIPS_DWARF
section [38] '.symtab': symbol 785 (_gp): st_value out of bounds
section [38] '.symtab': symbol 910 (_fbss): st_value out of bounds
section [38] '.symtab': symbol 1051 (_DYNAMIC_LINKING): non-local section symbol
After fixing:
$ src/elflint --gnu src/nm
No errors
Signed-off-by: Ying Huang <ying.huang@oss.cipunited.com>
---
backends/mips_init.c | 3 +++
backends/mips_symbol.c | 37 +++++++++++++++++++++++++++++++++++++
src/elflint.c | 26 +++++++++++++++++++++-----
3 files changed, 61 insertions(+), 5 deletions(-)
--- a/backends/mips_init.c
+++ b/backends/mips_init.c
@@ -51,9 +51,12 @@ mips_init (Elf *elf __attribute__ ((unus
HOOK (eh, section_type_name);
HOOK (eh, machine_flag_check);
HOOK (eh, machine_flag_name);
+ HOOK (eh, machine_section_flag_check);
HOOK (eh, segment_type_name);
HOOK (eh, dynamic_tag_check);
HOOK (eh, dynamic_tag_name);
HOOK (eh, check_object_attribute);
+ HOOK (eh, check_special_symbol);
+ HOOK (eh, check_reloc_target_type);
return eh;
}
--- a/backends/mips_symbol.c
+++ b/backends/mips_symbol.c
@@ -158,6 +158,43 @@ mips_section_type_name (int type,
return NULL;
}
+bool
+mips_check_reloc_target_type (Ebl *ebl __attribute__ ((unused)), Elf64_Word sh_type)
+{
+ return (sh_type == SHT_MIPS_DWARF);
+}
+
+/* Check whether given symbol's st_value and st_size are OK despite failing
+ normal checks. */
+bool
+mips_check_special_symbol (Elf *elf,
+ const GElf_Sym *sym __attribute__ ((unused)),
+ const char *name __attribute__ ((unused)),
+ const GElf_Shdr *destshdr)
+{
+ size_t shstrndx;
+ if (elf_getshdrstrndx (elf, &shstrndx) != 0)
+ return false;
+ const char *sname = elf_strptr (elf, shstrndx, destshdr->sh_name);
+ if (sname == NULL)
+ return false;
+ return (strcmp (sname, ".got") == 0 || strcmp (sname, ".bss") == 0);
+}
+
+/* Check whether SHF_MASKPROC flags are valid. */
+bool
+mips_machine_section_flag_check (GElf_Xword sh_flags)
+{
+ return ((sh_flags &~ (SHF_MIPS_GPREL |
+ SHF_MIPS_MERGE |
+ SHF_MIPS_ADDR |
+ SHF_MIPS_STRINGS |
+ SHF_MIPS_NOSTRIP |
+ SHF_MIPS_LOCAL |
+ SHF_MIPS_NAMES |
+ SHF_MIPS_NODUPE)) == 0);
+}
+
/* Check whether machine flags are valid. */
bool
mips_machine_flag_check (GElf_Word flags)
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -936,7 +936,9 @@ section [%2d] '%s': symbol %zu (%s): non
}
if (GELF_ST_TYPE (sym->st_info) == STT_SECTION
- && GELF_ST_BIND (sym->st_info) != STB_LOCAL)
+ && GELF_ST_BIND (sym->st_info) != STB_LOCAL
+ && ehdr->e_machine != EM_MIPS
+ && strcmp (name, "_DYNAMIC_LINKING") != 0)
ERROR (_("\
section [%2d] '%s': symbol %zu (%s): non-local section symbol\n"),
idx, section_name (ebl, idx), cnt, name);
@@ -3828,6 +3830,10 @@ cannot get section header for section [%
&& ebl_bss_plt_p (ebl))
good_type = SHT_NOBITS;
+ if (ehdr->e_machine == EM_MIPS
+ && (strstr(special_sections[s].name, ".debug") != NULL))
+ good_type = SHT_MIPS_DWARF;
+
/* In a debuginfo file, any normal section can be SHT_NOBITS.
This is only invalid for DWARF sections and .shstrtab. */
if (shdr->sh_type != good_type
@@ -3988,12 +3994,21 @@ section [%2zu] '%s': size not multiple o
ERROR (_("section [%2zu] '%s'"
" contains invalid processor-specific flag(s)"
" %#" PRIx64 "\n"),
- cnt, section_name (ebl, cnt), sh_flags & SHF_MASKPROC);
+ cnt, section_name (ebl, cnt), sh_flags & SHF_MASKPROC);
sh_flags &= ~(GElf_Xword) SHF_MASKPROC;
}
if (sh_flags & SHF_MASKOS)
- if (gnuld)
- sh_flags &= ~(GElf_Xword) SHF_GNU_RETAIN;
+ {
+ if (gnuld)
+ sh_flags &= ~(GElf_Xword) SHF_GNU_RETAIN;
+ if (!ebl_machine_section_flag_check (ebl,
+ sh_flags & SHF_MASKOS))
+ ERROR (_("section [%2zu] '%s'"
+ " contains invalid os-specific flag(s)"
+ " %#" PRIx64 "\n"),
+ cnt, section_name (ebl, cnt), sh_flags & SHF_MASKOS);
+ sh_flags &= ~(GElf_Xword) SHF_MASKOS;
+ }
if (sh_flags != 0)
ERROR (_("section [%2zu] '%s' contains unknown flag(s)"
" %#" PRIx64 "\n"),
@@ -4059,6 +4074,7 @@ section [%2zu] '%s': merge flag set but
switch (shdr->sh_type)
{
case SHT_PROGBITS:
+ case SHT_MIPS_DWARF:
break;
case SHT_NOBITS:
@@ -4716,7 +4732,7 @@ program header offset in ELF header and
if (shdr != NULL
&& ((is_debuginfo && shdr->sh_type == SHT_NOBITS)
|| (! is_debuginfo
- && (shdr->sh_type == SHT_PROGBITS
+ && (is_debug_section_type(shdr->sh_type)
|| shdr->sh_type == SHT_X86_64_UNWIND)))
&& elf_strptr (ebl->elf, shstrndx, shdr->sh_name) != NULL
&& ! strcmp (".eh_frame_hdr",

View File

@ -0,0 +1,273 @@
From f2acb06970522a9563d82490f2f1b8fc0bb5b720 Mon Sep 17 00:00:00 2001
From: Ying Huang <ying.huang@oss.cipunited.com>
Date: Tue, 5 Mar 2024 17:51:21 +0800
Subject: [PATCH] stack: Fix stack unwind failure on mips
Add abi_cfi, set_initial_registers_tid, unwind on mips.
* backends/Makefile.am (mips_SRCS): Add mips_initreg.c,
mips_cfi.c and mips_unwind.c.
* backends/mips_init.c (mips_init): HOOK abi_cfi, unwind and
set_initial_registers_tid. Set frame_nregs to 71.
* backends/mips_cfi.c: New file.
* backends/mips_initreg.c: Likewise.
* backends/mips_unwind.c: Likewise.
Signed-off-by: Ying Huang <ying.huang@oss.cipunited.com>
---
backends/Makefile.am | 3 +-
backends/mips_cfi.c | 68 +++++++++++++++++++++++++++++++++
backends/mips_init.c | 4 ++
backends/mips_initreg.c | 61 ++++++++++++++++++++++++++++++
backends/mips_unwind.c | 84 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 219 insertions(+), 1 deletion(-)
create mode 100644 backends/mips_cfi.c
create mode 100644 backends/mips_initreg.c
create mode 100644 backends/mips_unwind.c
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -102,7 +102,8 @@ loongarch_SRCS = loongarch_init.c loonga
arc_SRCS = arc_init.c arc_symbol.c
-mips_SRCS = mips_init.c mips_symbol.c mips_attrs.c
+mips_SRCS = mips_init.c mips_symbol.c mips_attrs.c mips_initreg.c \
+ mips_cfi.c mips_unwind.c
libebl_backends_a_SOURCES = $(i386_SRCS) $(sh_SRCS) $(x86_64_SRCS) \
$(ia64_SRCS) $(alpha_SRCS) $(arm_SRCS) \
--- /dev/null
+++ b/backends/mips_cfi.c
@@ -0,0 +1,68 @@
+/* MIPS ABI-specified defaults for DWARF CFI.
+ Copyright (C) 2009 Red Hat, Inc.
+ Copyright (C) 2024 CIP United Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * 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
+
+ or both in parallel, as here.
+
+ elfutils 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+
+#define BACKEND mips_
+#include "libebl_CPU.h"
+
+int
+mips_abi_cfi (Ebl *ebl __attribute__ ((unused)), Dwarf_CIE *abi_info)
+{
+ static const uint8_t abi_cfi[] =
+ {
+ DW_CFA_def_cfa, ULEB128_7 (31), ULEB128_7 (0),
+ /* Callee-saved regs. */
+ DW_CFA_same_value, ULEB128_7 (16), /* s0 */
+ DW_CFA_same_value, ULEB128_7 (17), /* s1 */
+ DW_CFA_same_value, ULEB128_7 (18), /* s2 */
+ DW_CFA_same_value, ULEB128_7 (19), /* s3 */
+ DW_CFA_same_value, ULEB128_7 (20), /* s4 */
+ DW_CFA_same_value, ULEB128_7 (21), /* s5 */
+ DW_CFA_same_value, ULEB128_7 (22), /* s6 */
+ DW_CFA_same_value, ULEB128_7 (23), /* s7 */
+ DW_CFA_same_value, ULEB128_7 (28), /* gp */
+ DW_CFA_same_value, ULEB128_7 (29), /* sp */
+ DW_CFA_same_value, ULEB128_7 (30), /* fp */
+
+ DW_CFA_val_offset, ULEB128_7 (29), ULEB128_7 (0),
+ };
+
+ abi_info->initial_instructions = abi_cfi;
+ abi_info->initial_instructions_end = &abi_cfi[sizeof abi_cfi];
+ abi_info->data_alignment_factor = 8;
+
+ abi_info->return_address_register = 31; /* %ra */
+
+ return 0;
+}
--- a/backends/mips_init.c
+++ b/backends/mips_init.c
@@ -58,5 +58,9 @@ mips_init (Elf *elf __attribute__ ((unus
HOOK (eh, check_object_attribute);
HOOK (eh, check_special_symbol);
HOOK (eh, check_reloc_target_type);
+ HOOK (eh, set_initial_registers_tid);
+ HOOK (eh, abi_cfi);
+ HOOK (eh, unwind);
+ eh->frame_nregs = 71;
return eh;
}
--- /dev/null
+++ b/backends/mips_initreg.c
@@ -0,0 +1,61 @@
+/* Fetch live process registers from TID.
+ Copyright (C) 2024 CIP United Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * 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
+
+ or both in parallel, as here.
+
+ elfutils 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#if (defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)) && defined(__linux__)
+# include <sys/user.h>
+# include <sys/ptrace.h>
+#include <asm/ptrace.h>
+#endif
+
+#define BACKEND mips_
+#include "libebl_CPU.h"
+
+
+bool
+mips_set_initial_registers_tid (pid_t tid __attribute__ ((unused)),
+ ebl_tid_registers_t *setfunc __attribute__ ((unused)),
+ void *arg __attribute__ ((unused)))
+{
+#if (!defined(mips) && !defined(__mips) && !defined(__mips__) && !defined(MIPS) && !defined(__MIPS__)) || !defined(__linux__)
+ return false;
+#else /* __mips__ */
+/* For PTRACE_GETREGS */
+
+ struct pt_regs gregs;
+ if (ptrace (PTRACE_GETREGS, tid, 0, &gregs) != 0)
+ return false;
+ if (! setfunc (-1, 1, (Dwarf_Word *) &gregs.cp0_epc, arg))
+ return false;
+ return setfunc (0, 32, (Dwarf_Word *) &gregs.regs[0], arg);
+#endif /* __mips__ */
+}
--- /dev/null
+++ b/backends/mips_unwind.c
@@ -0,0 +1,84 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 2016 The Qt Company Ltd.
+ Copyright (C) 2024 CIP United Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * 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
+
+ or both in parallel, as here.
+
+ elfutils 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define BACKEND mips_
+#define SP_REG 29
+#define FP_REG 30
+#define LR_REG 31
+#define FP_OFFSET 0
+#define LR_OFFSET 8
+#define SP_OFFSET 16
+
+#include "libebl_CPU.h"
+
+/* There was no CFI. Maybe we happen to have a frame pointer and can unwind from that? */
+
+bool
+EBLHOOK(unwind) (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr pc __attribute__ ((unused)),
+ ebl_tid_registers_t *setfunc, ebl_tid_registers_get_t *getfunc,
+ ebl_pid_memory_read_t *readfunc, void *arg,
+ bool *signal_framep __attribute__ ((unused)))
+{
+ Dwarf_Word fp, lr, sp;
+
+ if (!getfunc(LR_REG, 1, &lr, arg))
+ return false;
+
+ if (lr == 0 || !setfunc(-1, 1, &lr, arg))
+ return false;
+
+ if (!getfunc(FP_REG, 1, &fp, arg))
+ fp = 0;
+
+ if (!getfunc(SP_REG, 1, &sp, arg))
+ sp = 0;
+
+ Dwarf_Word newLr, newFp, newSp;
+
+ if (!readfunc(fp + LR_OFFSET, &newLr, arg))
+ newLr = 0;
+
+ if (!readfunc(fp + FP_OFFSET, &newFp, arg))
+ newFp = 0;
+
+ newSp = fp + SP_OFFSET;
+
+ // These are not fatal if they don't work. They will just prevent unwinding at the next frame.
+ setfunc(LR_REG, 1, &newLr, arg);
+ setfunc(FP_REG, 1, &newFp, arg);
+ setfunc(SP_REG, 1, &newSp, arg);
+
+ // If the fp is invalid, we might still have a valid lr.
+ // But if the fp is valid, then the stack should be moving in the right direction.
+ return fp == 0 || newSp > sp;
+}

View File

@ -0,0 +1,475 @@
From db33cb0cac3253c34881c0377ada51d9803eaae0 Mon Sep 17 00:00:00 2001
From: Ying Huang <ying.huang@oss.cipunited.com>
Date: Tue, 5 Mar 2024 17:51:22 +0800
Subject: [PATCH] backends: Add register_info, return_value_location, core_note
on mips
* backends/Makefile.am (mips_SRCS): Add mips_regs.c,
mips_retval.c and mips_corenote.c.
* backends/mips_init.c (mips_init): HOOK register_info,
return_value_location and core_note.
* backends/mips_corenote.c: New file.
* backends/mips_regs.c: Likewise.
* backends/mips_retval.c: Likewise.
Signed-off-by: Ying Huang <ying.huang@oss.cipunited.com>
---
backends/Makefile.am | 3 +-
backends/mips_corenote.c | 85 +++++++++++++++++
backends/mips_init.c | 3 +
backends/mips_regs.c | 135 +++++++++++++++++++++++++++
backends/mips_retval.c | 196 +++++++++++++++++++++++++++++++++++++++
5 files changed, 421 insertions(+), 1 deletion(-)
create mode 100644 backends/mips_corenote.c
create mode 100644 backends/mips_regs.c
create mode 100644 backends/mips_retval.c
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -103,7 +103,8 @@ loongarch_SRCS = loongarch_init.c loonga
arc_SRCS = arc_init.c arc_symbol.c
mips_SRCS = mips_init.c mips_symbol.c mips_attrs.c mips_initreg.c \
- mips_cfi.c mips_unwind.c
+ mips_cfi.c mips_unwind.c mips_regs.c mips_retval.c \
+ mips_corenote.c
libebl_backends_a_SOURCES = $(i386_SRCS) $(sh_SRCS) $(x86_64_SRCS) \
$(ia64_SRCS) $(alpha_SRCS) $(arm_SRCS) \
--- /dev/null
+++ b/backends/mips_corenote.c
@@ -0,0 +1,85 @@
+/* MIPS specific core note handling.
+ Copyright (C) 2024 CIP United Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * 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
+
+ or both in parallel, as here.
+
+ elfutils 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <elf.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#define BACKEND mips_
+#include "libebl_CPU.h"
+
+#define BITS 64
+#ifndef BITS
+# define BITS 32
+#else
+# define BITS 64
+#endif
+
+#define PRSTATUS_REGS_SIZE (45 * (BITS / 8))
+static const Ebl_Register_Location prstatus_regs[] =
+ {
+ { .offset = 0, .regno = 0, .count = (BITS == 32 ? 40 : 34), .bits = BITS },
+ { .offset = BITS/8 * (BITS == 32 ? 41 : 35), .regno = (BITS == 32 ? 41 : 35), .count = (BITS == 32 ? 4 : 10), .bits = BITS },
+ };
+
+#define PRSTATUS_REGSET_ITEMS \
+ { \
+ .name = "pc", .type = ELF_T_ADDR, .format = 'x', \
+ .offset = offsetof (struct EBLHOOK(prstatus), pr_reg) + ((BITS/8) * (BITS == 32 ? 40 : 34)), \
+ .group = "register", \
+ .pc_register = true \
+ }
+
+#if BITS == 32
+# define ULONG uint32_t
+# define ALIGN_ULONG 4
+# define TYPE_ULONG ELF_T_WORD
+#define TYPE_LONG ELF_T_SWORD
+#else
+#define ULONG uint64_t
+#define ALIGN_ULONG 8
+#define TYPE_ULONG ELF_T_XWORD
+#define TYPE_LONG ELF_T_SXWORD
+#endif
+#define PID_T int32_t
+#define UID_T uint32_t
+#define GID_T uint32_t
+#define ALIGN_PID_T 4
+#define ALIGN_UID_T 4
+#define ALIGN_GID_T 4
+#define TYPE_PID_T ELF_T_SWORD
+#define TYPE_UID_T ELF_T_WORD
+#define TYPE_GID_T ELF_T_WORD
+
+#include "linux-core-note.c"
--- a/backends/mips_init.c
+++ b/backends/mips_init.c
@@ -61,6 +61,9 @@ mips_init (Elf *elf __attribute__ ((unus
HOOK (eh, set_initial_registers_tid);
HOOK (eh, abi_cfi);
HOOK (eh, unwind);
+ HOOK (eh, register_info);
+ HOOK (eh, return_value_location);
+ HOOK (eh, core_note);
eh->frame_nregs = 71;
return eh;
}
--- /dev/null
+++ b/backends/mips_regs.c
@@ -0,0 +1,135 @@
+/* Register names and numbers for mips DWARF.
+ Copyright (C) 2006 Red Hat, Inc.
+ Copyright (C) 2024 CIP United Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * 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
+
+ or both in parallel, as here.
+
+ elfutils 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <dwarf.h>
+#include <string.h>
+
+#define BACKEND mips_
+#include "libebl_CPU.h"
+#include <system.h>
+ssize_t
+mips_register_info (Ebl *ebl __attribute__ ((unused)),
+ int regno, char *name, size_t namelen,
+ const char **prefix, const char **setname,
+ int *bits, int *type)
+{
+ if (name == NULL)
+ return 72;
+
+ if (regno < 0 || regno > 71 || namelen < 4)
+ return -1;
+
+ *prefix = "$";
+ if (regno < 38)
+ {
+ *setname = "integer";
+ *type = DW_ATE_signed;
+ *bits = 32;
+ }
+ else
+ {
+ *setname = "FPU";
+ *type = DW_ATE_float;
+ *bits = 64;
+ }
+
+ if (regno < 32)
+ {
+ if (regno < 10)
+ {
+ name[0] = regno + '0';
+ namelen = 1;
+ }
+ else
+ {
+ name[0] = (regno / 10) + '0';
+ name[1] = (regno % 10) + '0';
+ namelen = 2;
+ }
+ if (regno == 28 || regno == 29 || regno == 31)
+ *type = DW_ATE_address;
+ }
+ else if (regno == 32)
+ {
+ return stpcpy (name, "lo") + 1 - name;
+ }
+ else if (regno == 33)
+ {
+ return stpcpy (name, "hi") + 1 - name;
+ }
+ else if (regno == 34)
+ {
+ return stpcpy (name, "pc") + 1 - name;
+ }
+ else if (regno == 35)
+ {
+ *type = DW_ATE_address;
+ return stpcpy (name, "bad") + 1 - name;
+ }
+ else if (regno == 36)
+ {
+ return stpcpy (name, "sr") + 1 - name;
+ }
+ else if (regno == 37)
+ {
+ *type = DW_ATE_address;
+ return stpcpy (name, "cause") + 1 - name;
+ }
+ else if (regno < 70)
+ {
+ name[0] = 'f';
+ if (regno < 38 + 10)
+ {
+ name[1] = (regno - 38) + '0';
+ namelen = 2;
+ }
+ else
+ {
+ name[1] = (regno - 38) / 10 + '0';
+ name[2] = (regno - 38) % 10 + '0';
+ namelen = 3;
+ }
+ }
+ else if (regno == 70)
+ {
+ return stpcpy (name, "fsr") + 1 - name;
+ }
+ else if (regno == 71)
+ {
+ return stpcpy (name, "fir") + 1 - name;
+ }
+
+ name[namelen++] = '\0';
+ return namelen;
+}
--- /dev/null
+++ b/backends/mips_retval.c
@@ -0,0 +1,196 @@
+/* Function return value location for Linux/mips ABI.
+ Copyright (C) 2005 Red Hat, Inc.
+ Copyright (C) 2024 CIP United Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * 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
+
+ or both in parallel, as here.
+
+ elfutils 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <dwarf.h>
+#include <string.h>
+#include <elf.h>
+#include <stdio.h>
+
+#define BACKEND mips_
+#include "libebl_CPU.h"
+#include "libdwP.h"
+#include <stdio.h>
+
+/* $v0 or pair $v0, $v1 */
+static const Dwarf_Op loc_intreg_o32[] =
+ {
+ { .atom = DW_OP_reg2 }, { .atom = DW_OP_piece, .number = 4 },
+ { .atom = DW_OP_reg3 }, { .atom = DW_OP_piece, .number = 4 },
+ };
+
+static const Dwarf_Op loc_intreg[] =
+ {
+ { .atom = DW_OP_reg2 }, { .atom = DW_OP_piece, .number = 8 },
+ { .atom = DW_OP_reg3 }, { .atom = DW_OP_piece, .number = 8 },
+ };
+#define nloc_intreg 1
+#define nloc_intregpair 4
+
+/* $f0 (float), or pair $f0, $f1 (double).
+ * f2/f3 are used for COMPLEX (= 2 doubles) returns in Fortran */
+static const Dwarf_Op loc_fpreg_o32[] =
+ {
+ { .atom = DW_OP_regx, .number = 32 }, { .atom = DW_OP_piece, .number = 4 },
+ { .atom = DW_OP_regx, .number = 33 }, { .atom = DW_OP_piece, .number = 4 },
+ { .atom = DW_OP_regx, .number = 34 }, { .atom = DW_OP_piece, .number = 4 },
+ { .atom = DW_OP_regx, .number = 35 }, { .atom = DW_OP_piece, .number = 4 },
+ };
+
+/* $f0, or pair $f0, $f2. */
+static const Dwarf_Op loc_fpreg[] =
+ {
+ { .atom = DW_OP_regx, .number = 32 }, { .atom = DW_OP_piece, .number = 8 },
+ { .atom = DW_OP_regx, .number = 34 }, { .atom = DW_OP_piece, .number = 8 },
+ };
+#define nloc_fpreg 1
+#define nloc_fpregpair 4
+#define nloc_fpregquad 8
+
+/* The return value is a structure and is actually stored in stack space
+ passed in a hidden argument by the caller. But, the compiler
+ helpfully returns the address of that space in $v0. */
+static const Dwarf_Op loc_aggregate[] =
+ {
+ { .atom = DW_OP_breg2, .number = 0 }
+ };
+#define nloc_aggregate 1
+
+int
+mips_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
+{
+ unsigned int regsize = (gelf_getclass (functypedie->cu->dbg->elf) == ELFCLASS32 ) ? 4 : 8;
+ if (!regsize)
+ return -2;
+
+ /* Start with the function's type, and get the DW_AT_type attribute,
+ which is the type of the return value. */
+
+ Dwarf_Attribute attr_mem;
+ Dwarf_Attribute *attr = dwarf_attr_integrate (functypedie, DW_AT_type, &attr_mem);
+ if (attr == NULL)
+ /* The function has no return value, like a `void' function in C. */
+ return 0;
+
+ Dwarf_Die die_mem;
+ Dwarf_Die *typedie = dwarf_formref_die (attr, &die_mem);
+ int tag = dwarf_tag (typedie);
+
+ /* Follow typedefs and qualifiers to get to the actual type. */
+ while (tag == DW_TAG_typedef
+ || tag == DW_TAG_const_type || tag == DW_TAG_volatile_type
+ || tag == DW_TAG_restrict_type)
+ {
+ attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
+ typedie = dwarf_formref_die (attr, &die_mem);
+ tag = dwarf_tag (typedie);
+ }
+
+ switch (tag)
+ {
+ case -1:
+ return -1;
+
+ case DW_TAG_subrange_type:
+ if (! dwarf_hasattr_integrate (typedie, DW_AT_byte_size))
+ {
+ attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
+ typedie = dwarf_formref_die (attr, &die_mem);
+ tag = dwarf_tag (typedie);
+ }
+ /* Fall through. */
+ FALLTHROUGH;
+
+ case DW_TAG_base_type:
+ case DW_TAG_enumeration_type:
+ CASE_POINTER:
+ {
+ Dwarf_Word size;
+ if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_byte_size,
+ &attr_mem), &size) != 0)
+ {
+ if (dwarf_is_pointer (tag))
+ size = regsize;
+ else
+ return -1;
+ }
+ if (tag == DW_TAG_base_type)
+ {
+ Dwarf_Word encoding;
+ if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
+ &attr_mem), &encoding) != 0)
+ return -1;
+
+#define ARCH_LOC(loc, regsize) ((regsize) == 4 ? (loc ## _o32) : (loc))
+
+ if (encoding == DW_ATE_float)
+ {
+ *locp = ARCH_LOC(loc_fpreg, regsize);
+ if (size <= regsize)
+ return nloc_fpreg;
+
+ if (size <= 2*regsize)
+ return nloc_fpregpair;
+
+ if (size <= 4*regsize)
+ return nloc_fpregquad;
+
+ goto aggregate;
+ }
+ }
+ *locp = ARCH_LOC(loc_intreg, regsize);
+ if (size <= regsize)
+ return nloc_intreg;
+ if (size <= 2*regsize)
+ return nloc_intregpair;
+
+ /* Else fall through. Shouldn't happen though (at least with gcc) */
+ }
+ FALLTHROUGH;
+
+ case DW_TAG_structure_type:
+ case DW_TAG_class_type:
+ case DW_TAG_union_type:
+ case DW_TAG_array_type:
+ aggregate:
+ *locp = loc_aggregate;
+ return nloc_aggregate;
+ case DW_TAG_unspecified_type:
+ return 0;
+ }
+
+ /* XXX We don't have a good way to return specific errors from ebl calls.
+ This value means we do not understand the type, but it is well-formed
+ DWARF and might be valid. */
+ return -2;
+}