This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Add direct support for Linux kernel __fentry__ patching
- From: Andi Kleen <andi at firstfloor dot org>
- To: gcc-patches at gcc dot gnu dot org
- Cc: rostedt at goodmis dot org
- Date: Mon, 15 Sep 2014 09:49:21 -0700
- Subject: Re: [PATCH] Add direct support for Linux kernel __fentry__ patching
- Authentication-results: sourceware.org; auth=none
- References: <1409628256-22771-1-git-send-email-andi at firstfloor dot org>
Andi Kleen <andi@firstfloor.org> writes:
Ping!
> From: Andi Kleen <ak@linux.intel.com>
>
> The Linux kernel dynamically patches in __fentry__ calls in and
> out at runtime. This allows using function tracing for debugging
> in production kernels without (significant) performance penalty.
>
> For this it needs a table pointing to each __fentry__ call.
>
> The way it is currently implemented is that a special
> perl script scans the object file, generates the table in a special
> section. When the kernel boots up it nops the calls, and
> then later patches in the calls again as needed.
>
> The recordmcount.pl script in the kernel works, but it seems
> cleaner and faster to support the code generation of the patch table
> directly in gcc.
>
> This also allows to nop the calls directly at code generation
> time, which allows to skip a patching step at kernel boot.
> I also expect that a patchable production tracing facility is also useful
> for other applications.
>
> For example it could be used in ftracer
> (https://github.com/andikleen/ftracer)
>
> Having a nop area at the beginning of each function can be also
> also useful for other things. For example it can be used to patch
> functions at runtime to point to different functions, to do
> binary updates without restarting the program (like ksplice or
> similar)
>
> This patch implements two new options for the i386 target:
>
> -mrecord-mcount
> Generate a __mcount_loc section entry for each __fentry__ or mcount
> call. The section is compatible with the kernel convention
> and the data is put into a section loaded at runtime.
>
> -mnop-mcount
> Generate the mcount/__fentry__ call as 5 byte nop that can be
> patched in later. The nop is generated as a single instruction,
> as the Linux kernel run time patching relies on this.
>
> Limitations:
> - I didn't implement -mnop-mcount for -fPIC. This
> would need a good single instruction 6 byte NOP and it seems a
> bit pointless, as the patching would prevent text sharing.
> - I didn't implement noping for targets that pass a variable
> to mcount.
> - The facility could be useful on architectures too. Currently
> the mcount code is target specific, so I made it a i386 option.
>
> Passes bootstrap and testing on x86_64-linux.
>
> Cc: rostedt@goodmis.org
>
> gcc/:
>
> 2014-09-01 Andi Kleen <ak@linux.intel.com>
>
> * config/i386/i386.c (x86_print_call_or_nop): New function.
> (x86_function_profiler): Support -mnop-mcount and
> -mrecord-mcount.
> * config/i386/i386.opt (-mnop-mcount, -mrecord-mcount): Add
> * doc/invoke.texi: Document -mnop-mcount, -mrecord-mcount
> * testsuite/gcc/gcc.target/i386/nop-mcount.c: New file.
> * testsuite/gcc/gcc.target/i386/record-mcount.c: New file.
> ---
> gcc/config/i386/i386.c | 34 +++++++++++++++++++++++----
> gcc/config/i386/i386.opt | 9 +++++++
> gcc/doc/invoke.texi | 17 +++++++++++++-
> gcc/testsuite/gcc.target/i386/nop-mcount.c | 24 +++++++++++++++++++
> gcc/testsuite/gcc.target/i386/record-mcount.c | 24 +++++++++++++++++++
> 5 files changed, 102 insertions(+), 6 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/i386/nop-mcount.c
> create mode 100644 gcc/testsuite/gcc.target/i386/record-mcount.c
>
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index 61b33782..a651aa1 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -3974,6 +3974,13 @@ ix86_option_override_internal (bool main_args_p,
> }
> }
>
> +#ifndef NO_PROFILE_COUNTERS
> + if (flag_nop_mcount)
> + error ("-mnop-mcount is not compatible with this target");
> +#endif
> + if (flag_nop_mcount && flag_pic)
> + error ("-mnop-mcount is not implemented for -fPIC");
> +
> /* Accept -msseregparm only if at least SSE support is enabled. */
> if (TARGET_SSEREGPARM_P (opts->x_target_flags)
> && ! TARGET_SSE_P (opts->x_ix86_isa_flags))
> @@ -39042,6 +39049,17 @@ x86_field_alignment (tree field, int computed)
> return computed;
> }
>
> +/* Print call to TARGET to FILE. */
> +
> +static void
> +x86_print_call_or_nop (FILE *file, const char *target)
> +{
> + if (flag_nop_mcount)
> + fprintf (file, "1:\tnopl 0x00(%%eax,%%eax,1)\n"); /* 5 byte nop. */
> + else
> + fprintf (file, "1:\tcall\t%s\n", target);
> +}
> +
> /* Output assembler code to FILE to increment profiler label # LABELNO
> for profiling a function entry. */
> void
> @@ -39049,7 +39067,6 @@ x86_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
> {
> const char *mcount_name = (flag_fentry ? MCOUNT_NAME_BEFORE_PROLOGUE
> : MCOUNT_NAME);
> -
> if (TARGET_64BIT)
> {
> #ifndef NO_PROFILE_COUNTERS
> @@ -39057,9 +39074,9 @@ x86_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
> #endif
>
> if (!TARGET_PECOFF && flag_pic)
> - fprintf (file, "\tcall\t*%s@GOTPCREL(%%rip)\n", mcount_name);
> + fprintf (file, "1:\tcall\t*%s@GOTPCREL(%%rip)\n", mcount_name);
> else
> - fprintf (file, "\tcall\t%s\n", mcount_name);
> + x86_print_call_or_nop (file, mcount_name);
> }
> else if (flag_pic)
> {
> @@ -39067,7 +39084,7 @@ x86_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
> fprintf (file, "\tleal\t%sP%d@GOTOFF(%%ebx),%%" PROFILE_COUNT_REGISTER "\n",
> LPREFIX, labelno);
> #endif
> - fprintf (file, "\tcall\t*%s@GOT(%%ebx)\n", mcount_name);
> + fprintf (file, "1:\tcall\t*%s@GOT(%%ebx)\n", mcount_name);
> }
> else
> {
> @@ -39075,7 +39092,14 @@ x86_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
> fprintf (file, "\tmovl\t$%sP%d,%%" PROFILE_COUNT_REGISTER "\n",
> LPREFIX, labelno);
> #endif
> - fprintf (file, "\tcall\t%s\n", mcount_name);
> + x86_print_call_or_nop (file, mcount_name);
> + }
> +
> + if (flag_record_mcount)
> + {
> + fprintf (file, "\t.section __mcount_loc, \"r\"\n");
> + fprintf (file, "\t.%s 1b\n", TARGET_64BIT ? "quad" : "long");
> + fprintf (file, "\t.previous\n");
> }
> }
>
> diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
> index 9208b76..acf6b37 100644
> --- a/gcc/config/i386/i386.opt
> +++ b/gcc/config/i386/i386.opt
> @@ -789,6 +789,15 @@ mfentry
> Target Report Var(flag_fentry) Init(-1)
> Emit profiling counter call at function entry before prologue.
>
> +mrecord-mcount
> +Target Report Var(flag_record_mcount) Init(0)
> +Generate __mcount_loc section with all mcount or __fentry__ calls.
> +
> +mnop-mcount
> +Target Report Var(flag_nop_mcount) Init(0)
> +Generate mcount/__fentry__ calls as nops. To activate they need to be
> +patched in.
> +
> m8bit-idiv
> Target Report Mask(USE_8BIT_IDIV) Save
> Expand 32bit/64bit integer divide into 8bit unsigned integer divide with run-time check
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index d15d4a9..43fd6b7 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -691,7 +691,7 @@ Objective-C and Objective-C++ Dialects}.
> -momit-leaf-frame-pointer -mno-red-zone -mno-tls-direct-seg-refs @gol
> -mcmodel=@var{code-model} -mabi=@var{name} -maddress-mode=@var{mode} @gol
> -m32 -m64 -mx32 -m16 -mlarge-data-threshold=@var{num} @gol
> --msse2avx -mfentry -m8bit-idiv @gol
> +-msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol
> -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
> -mstack-protector-guard=@var{guard}}
>
> @@ -15954,6 +15954,21 @@ counter call before the prologue.
> Note: On x86 architectures the attribute @code{ms_hook_prologue}
> isn't possible at the moment for @option{-mfentry} and @option{-pg}.
>
> +@item -mrecord-mcount
> +@itemx -mno-record-mcount
> +@opindex mrecord-mcount
> +If profiling is active (@option{-pg}), generate a __mcount_loc section
> +that contains pointers to each profiling call. This is useful for
> +automatically patching and out calls.
> +
> +@item -mnop-mcount
> +@itemx -mno-nop-mcount
> +@opindex mnop-mcount
> +If profiling is active (@option{-pg}), generate the calls to
> +the profiling functions as nops. This is useful when they
> +should be patched in later dynamically. This is likely only
> +useful together with @option{-mrecord-mcount}.
> +
> @item -m8bit-idiv
> @itemx -mno-8bit-idiv
> @opindex 8bit-idiv
> diff --git a/gcc/testsuite/gcc.target/i386/nop-mcount.c b/gcc/testsuite/gcc.target/i386/nop-mcount.c
> new file mode 100644
> index 0000000..2592231
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/nop-mcount.c
> @@ -0,0 +1,24 @@
> +/* Test -mnop-mcount */
> +/* { dg-do compile } */
> +/* { dg-options "-pg -mfentry -mrecord-mcount -mnop-mcount" } */
> +/* { dg-final { scan-assembler-not "__fentry__" } } */
> +/* Origin: Andi Kleen */
> +extern void foobar(char *);
> +
> +void func(void)
> +{
> + foobar ("Hello world\n");
> +}
> +
> +void func2(void)
> +{
> + int i;
> + for (i = 0; i < 10; i++)
> + foobar ("Hello world");
> +}
> +
> +void func3(a)
> +char *a;
> +{
> + foobar("Hello world");
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/record-mcount.c b/gcc/testsuite/gcc.target/i386/record-mcount.c
> new file mode 100644
> index 0000000..dae413e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/record-mcount.c
> @@ -0,0 +1,24 @@
> +/* Test -mrecord-mcount */
> +/* { dg-do compile } */
> +/* { dg-options "-pg -mrecord-mcount" } */
> +/* { dg-final { scan-assembler "mcount_loc" } } */
> +/* Origin: Andi Kleen */
> +extern void foobar(char *);
> +
> +void func(void)
> +{
> + foobar ("Hello world\n");
> +}
> +
> +void func2(void)
> +{
> + int i;
> + for (i = 0; i < 10; i++)
> + foobar ("Hello world");
> +}
> +
> +void func3(a)
> +char *a;
> +{
> + foobar("Hello world");
> +}
--
ak@linux.intel.com -- Speaking for myself only