git: 6b71405bfe8d - main - Store core dump notes for all valid register sets for FreeBSD processes.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Thu, 10 Mar 2022 23:41:34 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=6b71405bfe8dc76834603f8f9e48346c3f49eae0

commit 6b71405bfe8dc76834603f8f9e48346c3f49eae0
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-03-10 23:40:19 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-03-10 23:40:19 +0000

    Store core dump notes for all valid register sets for FreeBSD processes.
    
    In particular, use a generic wrapper around struct regset rather than
    requiring per-regset helpers.  This helper replaces the MI
    __elfN(note_prstatus) and __elfN(note_fpregset) helpers.  It also
    removes the need to explicitly dump NT_ARM_ADDR_MASK in the arm64
    __elfN(dump_thread).
    
    Reviewed by:    markj, emaste
    Sponsored by:   University of Cambridge, Google, Inc.
    Differential Revision:  https://reviews.freebsd.org/D34446
---
 sys/arm64/arm64/elf_machdep.c |  20 +-------
 sys/kern/imgact_elf.c         | 110 +++++++++++++++++++++++++++---------------
 2 files changed, 74 insertions(+), 56 deletions(-)

diff --git a/sys/arm64/arm64/elf_machdep.c b/sys/arm64/arm64/elf_machdep.c
index 383a0911b7fe..57c43ed8206d 100644
--- a/sys/arm64/arm64/elf_machdep.c
+++ b/sys/arm64/arm64/elf_machdep.c
@@ -143,25 +143,9 @@ static struct regset regset_arm64_addr_mask = {
 ELF_REGSET(regset_arm64_addr_mask);
 
 void
-elf64_dump_thread(struct thread *td, void *dst, size_t *off)
+elf64_dump_thread(struct thread *td __unused, void *dst __unused,
+    size_t *off __unused)
 {
-	struct arm64_addr_mask addr_mask;
-	size_t len, mask_size;
-
-	len = 0;
-	if (dst != NULL) {
-		mask_size = sizeof(addr_mask);
-		get_arm64_addr_mask(&regset_arm64_addr_mask, td, &addr_mask,
-		    &mask_size);
-
-		len += elf64_populate_note(NT_ARM_ADDR_MASK, &addr_mask, dst,
-		    sizeof(addr_mask), NULL);
-	} else {
-		len += elf64_populate_note(NT_ARM_ADDR_MASK, NULL, NULL,
-		    sizeof(addr_mask), NULL);
-	}
-
-	*off += len;
 }
 
 bool
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index 332bb221e746..0f60835ce5e2 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -102,6 +102,8 @@ static bool __elfN(check_note)(struct image_params *imgp,
     uint32_t *fctl0);
 static vm_prot_t __elfN(trans_prot)(Elf_Word);
 static Elf_Word __elfN(untrans_prot)(vm_prot_t);
+static size_t __elfN(prepare_register_notes)(struct thread *td,
+    struct note_info_list *list, struct thread *target_td);
 
 SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE),
     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
@@ -1519,6 +1521,7 @@ struct phdr_closure {
 
 struct note_info {
 	int		type;		/* Note type. */
+	struct regset	*regset;	/* Register set. */
 	outfunc_t 	outfunc; 	/* Output function. */
 	void		*outarg;	/* Argument for the output function. */
 	size_t		outsize;	/* Output size. */
@@ -1538,9 +1541,7 @@ static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t,
     struct note_info_list *, size_t, int);
 static void __elfN(putnote)(struct thread *td, struct note_info *, struct sbuf *);
 
-static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *);
 static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *);
-static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *);
 static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *);
 static void __elfN(note_thrmisc)(void *, struct sbuf *, size_t *);
 static void __elfN(note_ptlwpinfo)(void *, struct sbuf *, size_t *);
@@ -1833,7 +1834,8 @@ __elfN(prepare_notes)(struct thread *td, struct note_info_list *list,
 	p = td->td_proc;
 	size = 0;
 
-	size += __elfN(register_note)(td, list, NT_PRPSINFO, __elfN(note_prpsinfo), p);
+	size += __elfN(register_note)(td, list, NT_PRPSINFO,
+	    __elfN(note_prpsinfo), p);
 
 	/*
 	 * To have the debugger select the right thread (LWP) as the initial
@@ -1843,10 +1845,7 @@ __elfN(prepare_notes)(struct thread *td, struct note_info_list *list,
 	 */
 	thr = td;
 	while (thr != NULL) {
-		size += __elfN(register_note)(td, list, NT_PRSTATUS,
-		    __elfN(note_prstatus), thr);
-		size += __elfN(register_note)(td, list, NT_FPREGSET,
-		    __elfN(note_fpregset), thr);
+		size += __elfN(prepare_register_notes)(td, list, thr);
 		size += __elfN(register_note)(td, list, NT_THRMISC,
 		    __elfN(note_thrmisc), thr);
 		size += __elfN(register_note)(td, list, NT_PTLWPINFO,
@@ -1968,6 +1967,34 @@ __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs,
 	each_dumpable_segment(td, cb_put_phdr, &phc, flags);
 }
 
+static size_t
+__elfN(register_regset_note)(struct thread *td, struct note_info_list *list,
+    struct regset *regset, struct thread *target_td)
+{
+	const struct sysentvec *sv;
+	struct note_info *ninfo;
+	size_t size, notesize;
+
+	size = 0;
+	if (!regset->get(regset, target_td, NULL, &size) || size == 0)
+		return (0);
+
+	ninfo = malloc(sizeof(*ninfo), M_TEMP, M_ZERO | M_WAITOK);
+	ninfo->type = regset->note;
+	ninfo->regset = regset;
+	ninfo->outarg = target_td;
+	ninfo->outsize = size;
+	TAILQ_INSERT_TAIL(list, ninfo, link);
+
+	sv = td->td_proc->p_sysent;
+	notesize = sizeof(Elf_Note) +		/* note header */
+	    roundup2(strlen(sv->sv_elf_core_abi_vendor) + 1, ELF_NOTE_ROUNDSIZE) +
+						/* note name */
+	    roundup2(size, ELF_NOTE_ROUNDSIZE);	/* note description */
+
+	return (notesize);
+}
+
 size_t
 __elfN(register_note)(struct thread *td, struct note_info_list *list,
     int type, outfunc_t out, void *arg)
@@ -2066,7 +2093,16 @@ __elfN(putnote)(struct thread *td, struct note_info *ninfo, struct sbuf *sb)
 	if (note.n_descsz == 0)
 		return;
 	sbuf_start_section(sb, &old_len);
-	ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize);
+	if (ninfo->regset != NULL) {
+		struct regset *regset = ninfo->regset;
+		void *buf;
+
+		buf = malloc(ninfo->outsize, M_TEMP, M_ZERO | M_WAITOK);
+		(void)regset->get(regset, ninfo->outarg, buf, &ninfo->outsize);
+		sbuf_bcat(sb, buf, ninfo->outsize);
+		free(buf, M_TEMP);
+	} else
+		ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize);
 	sect_len = sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0);
 	if (sect_len < 0)
 		return;
@@ -2237,24 +2273,6 @@ static struct regset __elfN(regset_prstatus) = {
 };
 ELF_REGSET(__elfN(regset_prstatus));
 
-static void
-__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
-{
-	struct thread *td;
-	elf_prstatus_t *status;
-
-	td = arg;
-	if (sb != NULL) {
-		KASSERT(*sizep == sizeof(*status), ("%s: invalid size",
-		    __func__));
-		status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK);
-		__elfN(get_prstatus)(NULL, td, status, sizep);
-		sbuf_bcat(sb, status, sizeof(*status));
-		free(status, M_TEMP);
-	}
-	*sizep = sizeof(*status);
-}
-
 static bool
 __elfN(get_fpregset)(struct regset *rs, struct thread *td, void *buf,
     size_t *sizep)
@@ -2299,21 +2317,37 @@ static struct regset __elfN(regset_fpregset) = {
 };
 ELF_REGSET(__elfN(regset_fpregset));
 
-static void
-__elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep)
+static size_t
+__elfN(prepare_register_notes)(struct thread *td, struct note_info_list *list,
+    struct thread *target_td)
 {
-	struct thread *td;
-	elf_prfpregset_t *fpregset;
+	struct sysentvec *sv = td->td_proc->p_sysent;
+	struct regset **regsetp, **regset_end, *regset;
+	size_t size;
 
-	td = arg;
-	if (sb != NULL) {
-		KASSERT(*sizep == sizeof(*fpregset), ("invalid size"));
-		fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK);
-		__elfN(get_fpregset)(NULL, td, fpregset, sizep);
-		sbuf_bcat(sb, fpregset, sizeof(*fpregset));
-		free(fpregset, M_TEMP);
+	size = 0;
+
+	/* NT_PRSTATUS must be the first register set note. */
+	size += __elfN(register_regset_note)(td, list, &__elfN(regset_prstatus),
+	    target_td);
+
+	regsetp = sv->sv_regset_begin;
+	if (regsetp == NULL) {
+		/* XXX: This shouldn't be true for any FreeBSD ABIs. */
+		size += __elfN(register_regset_note)(td, list,
+		    &__elfN(regset_fpregset), target_td);
+		return (size);
 	}
-	*sizep = sizeof(*fpregset);
+	regset_end = sv->sv_regset_end;
+	MPASS(regset_end != NULL);
+	for (; regsetp < regset_end; regsetp++) {
+		regset = *regsetp;
+		if (regset->note == NT_PRSTATUS)
+			continue;
+		size += __elfN(register_regset_note)(td, list, regset,
+		    target_td);
+	}
+	return (size);
 }
 
 static void