[RFC,v4,18/32] objtool: mcount: Move nop_mcount()
diff mbox series

Message ID 7109ceb239a88c2901eeb7f52c29f69cdb413cd3.1591125127.git.mhelsley@vmware.com
State New
Headers show
Series
  • objtool: Make recordmcount a subcommand
Related show

Commit Message

Matt Helsley June 2, 2020, 7:50 p.m. UTC
The nop_mcount() function overwrites mcount calls that should be
ignored with no-ops. This operation varies by architecture and
wordsize so we retain the function pointers used to implement
the fundamental operation while nop_mcount() itself is responsible
for walking the relocations, determining if they should be turned
into no-ops, then calling the arch-specific code. Since none of
these use the recordmcount ELF wrappers anymore we can move it out
of the wrapper.

Signed-off-by: Matt Helsley <mhelsley@vmware.com>
---
 tools/objtool/recordmcount.c | 47 +++++++++++++++++++++++++++++++++
 tools/objtool/recordmcount.h | 50 ------------------------------------
 2 files changed, 47 insertions(+), 50 deletions(-)

Comments

Peter Zijlstra June 12, 2020, 1:26 p.m. UTC | #1
On Tue, Jun 02, 2020 at 12:50:11PM -0700, Matt Helsley wrote:
> +static int nop_mcount(struct section * const rels,
> +		      const char *const txtname)
> +{
> +	struct reloc *reloc;
> +	struct section *txts = find_section_by_index(lf, rels->sh.sh_info);
> +	unsigned mcountsym = 0;
> +	int once = 0;
> +
> +	list_for_each_entry(reloc, &rels->reloc_list, list) {
> +		int ret = -1;
> +
> +		if (!mcountsym)
> +			mcountsym = get_mcountsym(reloc);
> +
> +		if (mcountsym == GELF_R_INFO(reloc->sym->idx, reloc->type) && !is_fake_mcount(reloc)) {

This makes no sense to me; why not have mcountsym be a 'struct symbol
*' and have get_mcountsym() return one of those.

	if (reloc->sym == mcountsym && ... )

is much nicer, no?

> +			if (make_nop) {
> +				ret = make_nop(txts, reloc->offset);
> +				if (ret < 0)
> +					return -1;
> +			}
> +			if (warn_on_notrace_sect && !once) {
> +				printf("Section %s has mcount callers being ignored\n",
> +				       txtname);
> +				once = 1;
> +				/* just warn? */
> +				if (!make_nop)
> +					return 0;
> +			}
> +		}
> +
> +		/*
> +		 * If we successfully removed the mcount, mark the relocation
> +		 * as a nop (don't do anything with it).
> +		 */
> +		if (!ret) {
> +			reloc->type = rel_type_nop;
> +			rels->changed = true;

I have an elf_write_rela(), I'll make sure to Cc you.

> +		}
> +	}
> +	return 0;
> +}
Peter Zijlstra June 12, 2020, 4:05 p.m. UTC | #2
On Fri, Jun 12, 2020 at 03:26:57PM +0200, Peter Zijlstra wrote:
> On Tue, Jun 02, 2020 at 12:50:11PM -0700, Matt Helsley wrote:
> > +static int nop_mcount(struct section * const rels,
> > +		      const char *const txtname)
> > +{
> > +	struct reloc *reloc;
> > +	struct section *txts = find_section_by_index(lf, rels->sh.sh_info);
> > +	unsigned mcountsym = 0;
> > +	int once = 0;
> > +
> > +	list_for_each_entry(reloc, &rels->reloc_list, list) {
> > +		int ret = -1;
> > +
> > +		if (!mcountsym)
> > +			mcountsym = get_mcountsym(reloc);
> > +
> > +		if (mcountsym == GELF_R_INFO(reloc->sym->idx, reloc->type) && !is_fake_mcount(reloc)) {
> 
> This makes no sense to me; why not have mcountsym be a 'struct symbol
> *' and have get_mcountsym() return one of those.
> 
> 	if (reloc->sym == mcountsym && ... )
> 
> is much nicer, no?

On top of that, I suppose we can do something like the below.

Then you can simply write:

	if (reloc->sym->class == SYM_MCOUNT && ..)

---

diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile
index 45452facff3b..94e4b8fcf9c1 100644
--- a/kernel/locking/Makefile
+++ b/kernel/locking/Makefile
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 # Any varying coverage in these files is non-deterministic
 # and is generally not a function of system call inputs.
-KCOV_INSTRUMENT		:= n
+# KCOV_INSTRUMENT		:= n
 
 obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o
 
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 432417a83902..133c0c285be6 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -341,6 +341,24 @@ static int read_sections(struct elf *elf)
 	return 0;
 }
 
+static bool is_mcount_symbol(const char *name)
+{
+	if (name[0] == '.')
+		name++;
+
+	if (name[0] == '_')
+		name++;
+
+	return !strcmp(name, "mcount", 6) ||
+	       !strcmp(name, "_fentry__") ||
+	       !strcmp(name, "_gnu_mcount_nc");
+}
+
+static bool is_kcov_symbol(const char *name)
+{
+	return !strncmp(name, "__sanitizer_cov_", 16);
+}
+
 static int read_symbols(struct elf *elf)
 {
 	struct section *symtab, *symtab_shndx, *sec;
@@ -410,6 +428,12 @@ static int read_symbols(struct elf *elf)
 		} else
 			sym->sec = find_section_by_index(elf, 0);
 
+
+		if (is_mcount_symbol(sym->name))
+			sym->class = SYM_MCOUNT;
+		else if (is_kcov_symbol(sym->name))
+			sym->class = SYM_KCOV;
+
 		sym->offset = sym->sym.st_value;
 		sym->len = sym->sym.st_size;
 
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index 78a2db23b8b6..3c1cccb7b5ff 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -42,6 +42,12 @@ struct section {
 	bool changed, text, rodata, noinstr;
 };
 
+enum symbol_class {
+	SYM_REGULAR = 0,
+	SYM_MCOUNT,
+	SYM_KCOV,
+};
+
 struct symbol {
 	struct list_head list;
 	struct rb_node node;
@@ -55,6 +61,7 @@ struct symbol {
 	unsigned long offset;
 	unsigned int len;
 	struct symbol *pfunc, *cfunc, *alias;
+	enum symbol_class class;
 	bool uaccess_safe;
 };
Matt Helsley June 13, 2020, 7:49 p.m. UTC | #3
On Fri, Jun 12, 2020 at 03:26:56PM +0200, Peter Zijlstra wrote:
> On Tue, Jun 02, 2020 at 12:50:11PM -0700, Matt Helsley wrote:
> > +static int nop_mcount(struct section * const rels,
> > +		      const char *const txtname)
> > +{
> > +	struct reloc *reloc;
> > +	struct section *txts = find_section_by_index(lf, rels->sh.sh_info);
> > +	unsigned mcountsym = 0;
> > +	int once = 0;
> > +
> > +	list_for_each_entry(reloc, &rels->reloc_list, list) {
> > +		int ret = -1;
> > +
> > +		if (!mcountsym)
> > +			mcountsym = get_mcountsym(reloc);
> > +
> > +		if (mcountsym == GELF_R_INFO(reloc->sym->idx, reloc->type) && !is_fake_mcount(reloc)) {
> 
> This makes no sense to me; why not have mcountsym be a 'struct symbol
> *' and have get_mcountsym() return one of those.
> 
> 	if (reloc->sym == mcountsym && ... )
> 
> is much nicer, no?

Indeed! I'll change it from returning an unsigned long to struct symbol * before I
move it out of the wrapper code.

> 
> > +			if (make_nop) {
> > +				ret = make_nop(txts, reloc->offset);
> > +				if (ret < 0)
> > +					return -1;
> > +			}
> > +			if (warn_on_notrace_sect && !once) {
> > +				printf("Section %s has mcount callers being ignored\n",
> > +				       txtname);
> > +				once = 1;
> > +				/* just warn? */
> > +				if (!make_nop)
> > +					return 0;
> > +			}
> > +		}
> > +
> > +		/*
> > +		 * If we successfully removed the mcount, mark the relocation
> > +		 * as a nop (don't do anything with it).
> > +		 */
> > +		if (!ret) {
> > +			reloc->type = rel_type_nop;
> > +			rels->changed = true;
> 
> I have an elf_write_rela(), I'll make sure to Cc you.

Thanks! I might also make use of your patch to rewrite instructions. We
need a way to turn certain prologue instructions into nops. Would it be
more widely useful to move that functionality out of mcount and into
the objtool ELF/per-arch code or do you think it's better inside the
mcount subcommand code?

Cheers,
     -Matt
Matt Helsley June 17, 2020, 5:36 p.m. UTC | #4
On Fri, Jun 12, 2020 at 06:05:34PM +0200, Peter Zijlstra wrote:
> On Fri, Jun 12, 2020 at 03:26:57PM +0200, Peter Zijlstra wrote:
> > On Tue, Jun 02, 2020 at 12:50:11PM -0700, Matt Helsley wrote:
> > > +static int nop_mcount(struct section * const rels,
> > > +		      const char *const txtname)
> > > +{
> > > +	struct reloc *reloc;
> > > +	struct section *txts = find_section_by_index(lf, rels->sh.sh_info);
> > > +	unsigned mcountsym = 0;
> > > +	int once = 0;
> > > +
> > > +	list_for_each_entry(reloc, &rels->reloc_list, list) {
> > > +		int ret = -1;
> > > +
> > > +		if (!mcountsym)
> > > +			mcountsym = get_mcountsym(reloc);
> > > +
> > > +		if (mcountsym == GELF_R_INFO(reloc->sym->idx, reloc->type) && !is_fake_mcount(reloc)) {
> > 
> > This makes no sense to me; why not have mcountsym be a 'struct symbol
> > *' and have get_mcountsym() return one of those.
> > 
> > 	if (reloc->sym == mcountsym && ... )
> > 
> > is much nicer, no?

(this is already incorporated in my unposted revisions but...)

> 
> On top of that, I suppose we can do something like the below.
> 
> Then you can simply write:
> 
> 	if (reloc->sym->class == SYM_MCOUNT && ..)

This looks like a good way to move towards a "single pass" through the ELF data
for mcount.

What order do you want to see this patch go in? Before this series (i.e. perhaps
just a kcov SYM_ class to start)? Early or late in this series? After?

Right now I'm thinking of putting this on the end of my series because
I'm focusing on converting recordmcount in the series and this isn't
strictly necessary but is definitely nicer.

> 
> ---
> 
> diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile
> index 45452facff3b..94e4b8fcf9c1 100644
> --- a/kernel/locking/Makefile
> +++ b/kernel/locking/Makefile
> @@ -1,7 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0
>  # Any varying coverage in these files is non-deterministic
>  # and is generally not a function of system call inputs.
> -KCOV_INSTRUMENT		:= n
> +# KCOV_INSTRUMENT		:= n
>  
>  obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o
>  
> diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
> index 432417a83902..133c0c285be6 100644
> --- a/tools/objtool/elf.c
> +++ b/tools/objtool/elf.c
> @@ -341,6 +341,24 @@ static int read_sections(struct elf *elf)
>  	return 0;
>  }
>  
> +static bool is_mcount_symbol(const char *name)
> +{
> +	if (name[0] == '.')
> +		name++;
> +
> +	if (name[0] == '_')
> +		name++;
> +
> +	return !strcmp(name, "mcount", 6) ||

Looks like you intended this to be a strncmp() but I don't see a reason to
use strncmp(). Am I missing something?

> +	       !strcmp(name, "_fentry__") ||
> +	       !strcmp(name, "_gnu_mcount_nc");
> +}

This mashes all of the arch-specific mcount name checks together. I
don't see a problem with that because I doubt there will be a collision
with other functions. Just to be careful I looked through the Clang and
GCC sources, though I only dug through the history of Clang's output --
GCC's history with respect to mcount symbol names across architectures is
much harder to trace so I only looked at the current sources.

<snip> (the rest looks good)

Cheers,
    -Matt Helsley
Peter Zijlstra June 17, 2020, 6:30 p.m. UTC | #5
On Wed, Jun 17, 2020 at 10:36:20AM -0700, Matt Helsley wrote:
> On Fri, Jun 12, 2020 at 06:05:34PM +0200, Peter Zijlstra wrote:

> > On top of that, I suppose we can do something like the below.
> > 
> > Then you can simply write:
> > 
> > 	if (reloc->sym->class == SYM_MCOUNT && ..)
> 
> This looks like a good way to move towards a "single pass" through the ELF data
> for mcount.
> 
> What order do you want to see this patch go in? Before this series (i.e. perhaps
> just a kcov SYM_ class to start)? Early or late in this series? After?
> 
> Right now I'm thinking of putting this on the end of my series because
> I'm focusing on converting recordmcount in the series and this isn't
> strictly necessary but is definitely nicer.

No particular thoughts about where, so at the end would be fine.


> > ---
> > 
> > diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile
> > index 45452facff3b..94e4b8fcf9c1 100644
> > --- a/kernel/locking/Makefile
> > +++ b/kernel/locking/Makefile
> > @@ -1,7 +1,7 @@
> >  # SPDX-License-Identifier: GPL-2.0
> >  # Any varying coverage in these files is non-deterministic
> >  # and is generally not a function of system call inputs.
> > -KCOV_INSTRUMENT		:= n
> > +# KCOV_INSTRUMENT		:= n
> >  
> >  obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o
> >  
> > diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
> > index 432417a83902..133c0c285be6 100644
> > --- a/tools/objtool/elf.c
> > +++ b/tools/objtool/elf.c
> > @@ -341,6 +341,24 @@ static int read_sections(struct elf *elf)
> >  	return 0;
> >  }
> >  
> > +static bool is_mcount_symbol(const char *name)
> > +{
> > +	if (name[0] == '.')
> > +		name++;
> > +
> > +	if (name[0] == '_')
> > +		name++;
> > +
> > +	return !strcmp(name, "mcount", 6) ||
> 
> Looks like you intended this to be a strncmp() but I don't see a reason to
> use strncmp(). Am I missing something?

typing hard :-)

> > +	       !strcmp(name, "_fentry__") ||
> > +	       !strcmp(name, "_gnu_mcount_nc");
> > +}
> 
> This mashes all of the arch-specific mcount name checks together. I
> don't see a problem with that because I doubt there will be a collision
> with other functions. 

This, I assumed it would just work.

> Just to be careful I looked through the Clang and
> GCC sources, though I only dug through the history of Clang's output --
> GCC's history with respect to mcount symbol names across architectures is
> much harder to trace so I only looked at the current sources.

Fair enough; thanks for the due-dilligence.

Patch
diff mbox series

diff --git a/tools/objtool/recordmcount.c b/tools/objtool/recordmcount.c
index 89762908290e..88998a505859 100644
--- a/tools/objtool/recordmcount.c
+++ b/tools/objtool/recordmcount.c
@@ -398,6 +398,53 @@  static int find_section_sym_index(unsigned const txtndx,
 	return missing_sym;
 }
 
+/*
+ * Read the relocation table again, but this time its called on sections
+ * that are not going to be traced. The mcount calls here will be converted
+ * into nops.
+ */
+static int nop_mcount(struct section * const rels,
+		      const char *const txtname)
+{
+	struct reloc *reloc;
+	struct section *txts = find_section_by_index(lf, rels->sh.sh_info);
+	unsigned mcountsym = 0;
+	int once = 0;
+
+	list_for_each_entry(reloc, &rels->reloc_list, list) {
+		int ret = -1;
+
+		if (!mcountsym)
+			mcountsym = get_mcountsym(reloc);
+
+		if (mcountsym == GELF_R_INFO(reloc->sym->idx, reloc->type) && !is_fake_mcount(reloc)) {
+			if (make_nop) {
+				ret = make_nop(txts, reloc->offset);
+				if (ret < 0)
+					return -1;
+			}
+			if (warn_on_notrace_sect && !once) {
+				printf("Section %s has mcount callers being ignored\n",
+				       txtname);
+				once = 1;
+				/* just warn? */
+				if (!make_nop)
+					return 0;
+			}
+		}
+
+		/*
+		 * If we successfully removed the mcount, mark the relocation
+		 * as a nop (don't do anything with it).
+		 */
+		if (!ret) {
+			reloc->type = rel_type_nop;
+			rels->changed = true;
+		}
+	}
+	return 0;
+}
+
 /* 32 bit and 64 bit are very similar */
 #include "recordmcount.h"
 #define RECORD_MCOUNT_64
diff --git a/tools/objtool/recordmcount.h b/tools/objtool/recordmcount.h
index 6754bde0bacc..e033b600bd61 100644
--- a/tools/objtool/recordmcount.h
+++ b/tools/objtool/recordmcount.h
@@ -20,7 +20,6 @@ 
 #undef append_func
 #undef mcount_adjust
 #undef sift_rel_mcount
-#undef nop_mcount
 #undef has_rel_mcount
 #undef tot_relsize
 #undef do_func
@@ -37,7 +36,6 @@ 
 #ifdef RECORD_MCOUNT_64
 # define append_func		append64
 # define sift_rel_mcount	sift64_rel_mcount
-# define nop_mcount		nop_mcount_64
 # define has_rel_mcount		has64_rel_mcount
 # define tot_relsize		tot64_relsize
 # define do_func		do64
@@ -53,7 +51,6 @@ 
 #else
 # define append_func		append32
 # define sift_rel_mcount	sift32_rel_mcount
-# define nop_mcount		nop_mcount_32
 # define has_rel_mcount		has32_rel_mcount
 # define tot_relsize		tot32_relsize
 # define do_func		do32
@@ -171,53 +168,6 @@  static uint_t *sift_rel_mcount(uint_t *mlocp,
 	return mlocp;
 }
 
-/*
- * Read the relocation table again, but this time its called on sections
- * that are not going to be traced. The mcount calls here will be converted
- * into nops.
- */
-static int nop_mcount(struct section * const rels,
-		      const char *const txtname)
-{
-	struct reloc *reloc;
-	struct section *txts = find_section_by_index(lf, rels->sh.sh_info);
-	unsigned mcountsym = 0;
-	int once = 0;
-
-	list_for_each_entry(reloc, &rels->reloc_list, list) {
-		int ret = -1;
-
-		if (!mcountsym)
-			mcountsym = get_mcountsym(reloc);
-
-		if (mcountsym == GELF_R_INFO(reloc->sym->idx, reloc->type) && !is_fake_mcount(reloc)) {
-			if (make_nop) {
-				ret = make_nop(txts, reloc->offset);
-				if (ret < 0)
-					return -1;
-			}
-			if (warn_on_notrace_sect && !once) {
-				printf("Section %s has mcount callers being ignored\n",
-				       txtname);
-				once = 1;
-				/* just warn? */
-				if (!make_nop)
-					return 0;
-			}
-		}
-
-		/*
-		 * If we successfully removed the mcount, mark the relocation
-		 * as a nop (don't do anything with it).
-		 */
-		if (!ret) {
-			reloc->type = rel_type_nop;
-			rels->changed = true;
-		}
-	}
-	return 0;
-}
-
 static char const *has_rel_mcount(const struct section * const rels)
 {
 	const struct section *txts;