summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortkoecker <tkoecker@gmx.net>2022-06-23 13:00:57 +0200
committerGitHub <noreply@github.com>2022-06-23 07:00:57 -0400
commit860f0fe9adac2fdb384a11e072fc4e7463c6cd7d (patch)
tree6376b0476b235e803285a6c4af1aedc9efbe3214 /src
parent1f79a05edbd5c06240f8a15187b106831076540e (diff)
downloadlibunwind-860f0fe9adac2fdb384a11e072fc4e7463c6cd7d.tar.gz
added coredump support for s390x (#373)
This change adds s390x coredump support, it contains the following: It adds reading the NT_FPREGSET notes, as the floating point registers are needed for unwinding on s390x. The fpregset is added to the thread data structure. The getter for floating point registers was implemented (at least for s390x). A register mapping for the registers in NT_PRSTATUS was added, as the register indices of the enum in libunwind are different to those in prstatus.
Diffstat (limited to 'src')
-rw-r--r--src/coredump/_UCD_access_reg_linux.c24
-rw-r--r--src/coredump/_UCD_create.c9
-rw-r--r--src/coredump/_UCD_get_threadinfo_prstatus.c8
-rw-r--r--src/coredump/_UCD_internal.h12
-rw-r--r--src/coredump/_UPT_access_fpreg.c21
5 files changed, 64 insertions, 10 deletions
diff --git a/src/coredump/_UCD_access_reg_linux.c b/src/coredump/_UCD_access_reg_linux.c
index 99c28a4c..6ec50bac 100644
--- a/src/coredump/_UCD_access_reg_linux.c
+++ b/src/coredump/_UCD_access_reg_linux.c
@@ -54,9 +54,6 @@ _UCD_access_reg (unw_addr_space_t as,
#elif defined(UNW_TARGET_TILEGX)
if (regnum > UNW_TILEGX_CFA)
goto badreg;
-#elif defined(UNW_TARGET_S390X)
- if (regnum > UNW_S390X_R15)
- goto badreg;
#elif defined(UNW_TARGET_IA64) || defined(UNW_TARGET_HPPA) || defined(UNW_TARGET_PPC32) || defined(UNW_TARGET_PPC64)
if (regnum >= ARRAY_SIZE(ui->prstatus->pr_reg))
goto badreg;
@@ -138,6 +135,27 @@ _UCD_access_reg (unw_addr_space_t as,
[UNW_MIPS_R31] = EF_REG31,
[UNW_MIPS_PC] = EF_CP0_EPC,
};
+#elif defined(UNW_TARGET_S390X)
+ static const uint8_t remap_regs[] =
+ {
+ [UNW_S390X_IP] = offsetof(struct user_regs_struct, psw.addr) / sizeof(long),
+ [UNW_S390X_R0] = offsetof(struct user_regs_struct, gprs[0]) / sizeof(long),
+ [UNW_S390X_R1] = offsetof(struct user_regs_struct, gprs[1]) / sizeof(long),
+ [UNW_S390X_R2] = offsetof(struct user_regs_struct, gprs[2]) / sizeof(long),
+ [UNW_S390X_R3] = offsetof(struct user_regs_struct, gprs[3]) / sizeof(long),
+ [UNW_S390X_R4] = offsetof(struct user_regs_struct, gprs[4]) / sizeof(long),
+ [UNW_S390X_R5] = offsetof(struct user_regs_struct, gprs[5]) / sizeof(long),
+ [UNW_S390X_R6] = offsetof(struct user_regs_struct, gprs[6]) / sizeof(long),
+ [UNW_S390X_R7] = offsetof(struct user_regs_struct, gprs[7]) / sizeof(long),
+ [UNW_S390X_R8] = offsetof(struct user_regs_struct, gprs[8]) / sizeof(long),
+ [UNW_S390X_R9] = offsetof(struct user_regs_struct, gprs[9]) / sizeof(long),
+ [UNW_S390X_R10] = offsetof(struct user_regs_struct, gprs[10]) / sizeof(long),
+ [UNW_S390X_R11] = offsetof(struct user_regs_struct, gprs[11]) / sizeof(long),
+ [UNW_S390X_R12] = offsetof(struct user_regs_struct, gprs[12]) / sizeof(long),
+ [UNW_S390X_R13] = offsetof(struct user_regs_struct, gprs[13]) / sizeof(long),
+ [UNW_S390X_R14] = offsetof(struct user_regs_struct, gprs[14]) / sizeof(long),
+ [UNW_S390X_R15] = offsetof(struct user_regs_struct, gprs[15]) / sizeof(long),
+ };
#elif defined(UNW_TARGET_X86)
static const uint8_t remap_regs[] =
{
diff --git a/src/coredump/_UCD_create.c b/src/coredump/_UCD_create.c
index 9b4b7fe3..21fd0b92 100644
--- a/src/coredump/_UCD_create.c
+++ b/src/coredump/_UCD_create.c
@@ -218,7 +218,8 @@ _UCD_create(const char *filename)
goto err;
}
- ui->prstatus = &ui->threads[0];
+ ui->prstatus = &ui->threads[0].prstatus;
+ ui->fpregset = &ui->threads[0].fpregset;
return ui;
@@ -234,8 +235,10 @@ int _UCD_get_num_threads(struct UCD_info *ui)
void _UCD_select_thread(struct UCD_info *ui, int n)
{
- if (n >= 0 && n < ui->n_threads)
- ui->prstatus = &ui->threads[n];
+ if (n >= 0 && n < ui->n_threads) {
+ ui->prstatus = &ui->threads[n].prstatus;
+ ui->fpregset = &ui->threads[n].fpregset;
+ }
}
pid_t _UCD_get_pid(struct UCD_info *ui)
diff --git a/src/coredump/_UCD_get_threadinfo_prstatus.c b/src/coredump/_UCD_get_threadinfo_prstatus.c
index 45552566..cd03ce36 100644
--- a/src/coredump/_UCD_get_threadinfo_prstatus.c
+++ b/src/coredump/_UCD_get_threadinfo_prstatus.c
@@ -62,9 +62,13 @@ _save_thread_notes(uint32_t n_namesz, uint32_t n_descsz, uint32_t n_type, char *
struct UCD_info *ui = (struct UCD_info *)arg;
if (n_type == NT_PRSTATUS)
{
- memcpy(&ui->threads[ui->n_threads], desc, sizeof(struct PRSTATUS_STRUCT));
+ memcpy(&ui->threads[ui->n_threads].prstatus, desc, sizeof(struct PRSTATUS_STRUCT));
++ui->n_threads;
}
+ if (n_type == NT_FPREGSET)
+ {
+ memcpy(&ui->threads[ui->n_threads-1].fpregset, desc, sizeof(elf_fpregset_t));
+ }
return UNW_ESUCCESS;
}
@@ -102,7 +106,7 @@ _UCD_get_threadinfo(struct UCD_info *ui, coredump_phdr_t *phdrs, unsigned phdr_s
_UCD_elf_visit_notes(segment, segment_size, _count_thread_notes, &thread_count);
Debug(2, "found %zu threads\n", thread_count);
- size_t new_size = sizeof(struct PRSTATUS_STRUCT) * (ui->n_threads + thread_count);
+ size_t new_size = sizeof(struct UCD_thread_info) * (ui->n_threads + thread_count);
ui->threads = realloc(ui->threads, new_size);
if (ui->threads == NULL)
{
diff --git a/src/coredump/_UCD_internal.h b/src/coredump/_UCD_internal.h
index f5150205..e980aa29 100644
--- a/src/coredump/_UCD_internal.h
+++ b/src/coredump/_UCD_internal.h
@@ -34,6 +34,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#ifdef HAVE_SYS_PROCFS_H
#include <sys/procfs.h> /* struct elf_prstatus */
#endif
+#ifdef HAVE_ASM_PTRACE_H
+#include <asm/ptrace.h> /* struct user_regs_struct on s390x */
+#endif
#include <errno.h>
#include <string.h>
#include <limits.h>
@@ -82,6 +85,12 @@ typedef struct coredump_phdr coredump_phdr_t;
#define PRSTATUS_STRUCT non_existent
#endif
+struct UCD_thread_info
+ {
+ struct PRSTATUS_STRUCT prstatus;
+ elf_fpregset_t fpregset;
+ };
+
struct UCD_info
{
int big_endian; /* bool */
@@ -91,8 +100,9 @@ struct UCD_info
unsigned phdrs_count;
void *note_phdr; /* allocated or NULL */
struct PRSTATUS_STRUCT *prstatus; /* points inside note_phdr */
+ elf_fpregset_t *fpregset;
int n_threads;
- struct PRSTATUS_STRUCT *threads;
+ struct UCD_thread_info *threads;
struct elf_dyn_info edi;
};
diff --git a/src/coredump/_UPT_access_fpreg.c b/src/coredump/_UPT_access_fpreg.c
index 0b8b86ac..a6d0bcea 100644
--- a/src/coredump/_UPT_access_fpreg.c
+++ b/src/coredump/_UPT_access_fpreg.c
@@ -28,7 +28,26 @@ int
_UCD_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
int write, void *arg)
{
+ struct UCD_info *ui UNUSED = arg;
+
+ if (write)
+ {
+ Debug(0, "write is not supported\n");
+ return -UNW_EINVAL;
+ }
+
+#ifdef __s390x__
+ if (reg < UNW_S390X_F0 || reg > UNW_S390X_F15)
+ {
+ Debug(0, "bad regnum:%d\n", reg);
+ return -UNW_EINVAL;
+ }
+
+ *val = ui->fpregset->fprs[reg - UNW_S390X_F0].d;
+ return 0;
+#else
print_error (__func__);
- print_error (" not implemented\n");
+ print_error (" not implemented for this architecture\n");
return -UNW_EINVAL;
+#endif
}