]> www.infradead.org Git - users/jedix/linux-maple.git/commit
dtrace: add support for probes in sections other than .text
authorKris Van Hees <kris.van.hees@oracle.com>
Sat, 18 Jun 2016 14:19:03 +0000 (10:19 -0400)
committerNick Alcock <nick.alcock@oracle.com>
Mon, 4 Jul 2016 16:14:28 +0000 (17:14 +0100)
commitacdb33ef09b04b65d142d6c257f950d248863156
treeda98d30df8f6ad937fd73084c7ae1d7f57bbe2a5
parent59d28d52983c811dac8f4ebf5a86f1442c221186
dtrace: add support for probes in sections other than .text

This commit adds support for SDT probes in sections other than the
regular .text section, both for the kernel and kernel modules.

The core of the problem is that probe locations (for modules and
for the kernel proper) were stored as an offset relatve to the start
of the code section they occur in.  Processing them at load time (or
boot time, for the kernel) resulted in incorrect addresses for any
probes in sections other than .text because the offset was getting
applied to the wrong base address.

I.e. a probe at offset 0x1d0 in section .text.unlikely would be
resolved as
BASE(.text) + 0x1d0
rather than
BASE(.text.unlikely) + 0x1d0

The end result was that (using x86_64 as example) we would be writing
a 5-byte NOP sequence in a location that was really not expecting that,
with very odd and entirely unpredictable results.

Solving this required two distinct mechanisms because modules are
linked in a way where the distict code sections are retained.  Only
at module load time are they laoded consecutively into a single block
of memory that starts at module_core; worse yet, probes can be
duplicated by the compiler into multiple such sections.  This means
that a simple offset will not do; the only thing we can rely on is the
function name (since symbol names are guaranteed unique, and the
compiler synthesizes new ones not valid in C when it clones and
specializes functions), and its start address.

The question is how to identify the functions.  We can't use the symbol
table index, because the symbol table is rewritten by strip(1) as part
of RPM generation: and we can't directly refer to the names given to
cloned functions because they are not valid in C: even if valid, they
might well be local to the translation unit in any case.  So we run the
.o file through a new tool, scripts/kmodsdt, which identifies all
functions containing DTrace probes by hunting for relocations to the
__dtrace_probe_* probe calls and, if the containing functions are local,
introduces an alias to each named __dta_<function>_<symindex> (the
function name and index are necessary because these functions are local,
so there could be several of them with the same name in different
translation units: the symindex makes the aliases unique).  The function
name is rewritten to ensure it is valid in C by translating . into _.

The address in the sdtinfo then references each function symbol (or
alias) and simply adds the offset from that function symbol to the probe
to it in perfectly normal C code, and the linker then deals with all the
problems involving keeping the addresses valid across stripping, module
loading, etc.

When the module is loaded, the kernel resolves all relocations.  This
means that the sdtinfo data that is compiled into the module will have
the exact address of all probe locations by the time we need it.

The kernel is a different story altogether...

The kernel performs a final link into an executable object, merging
all non-init code sections into a single .text.  This mens that for
the kernel, we need to calculate the absolute address of each probe
location based on the knowledge that section merging occurs.

We make use of the multi-step linking process that the kernel uses to
ensure stability of the kallsyms data.  Three steps take place in the
sdtinfo generation process (though they will appear as a single step,
because they are a single pipeline):

  - a list of code sections is extracted from vmlinux.o, and for each
    section we take note of the function with the lowest offset in
    the section

  - temporary kernel image .tmp_vmlinux1 is used to determine the
    absolute load address of each merged section by searching for the
    first function of each respective section, and subtracting its
    offset from its address.

  - using the list of absolute base addresses for each section, the
    list of probes (associated with the function they occur in, and
    the section the function belongs in) gets its absolute addresses
    for the probe locations by adding the probe offset (relative to
    its encapsulating section) to the section base address.

(Note: if two distinct sections in the kernel happen to have fuctions
       at the lowest offset with identical names, this process cannot
       distinguish between those two sections.  That is a limitation
       of the heuristic but is extremely unlikely.  If it were to
       occur, a fix would require to use a more complex comparison to
       determine whether a function in the final kernel image is the
       start of one section or another.)

Orabug: 23344927
Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Acked-by: Nick Alcock <nick.alcock@oracle.com>
kernel/dtrace/dtrace_sdt_core.c
scripts/.gitignore
scripts/Makefile
scripts/dtrace_sdt.sh
scripts/kmodsdt.c [new file with mode: 0644]