3 # A Poor(but Free)'s Man dtrace
5 # Copyright (C) 2014, 2015 Free Software Foundation, Inc.
7 # Contributed by Oracle, Inc.
9 # This file is part of GDB.
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful, but
17 # WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 # General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see
23 # <http://www.gnu.org/licenses/>.
25 # DISCLAIMER DISCLAIMER DISCLAIMER
26 # This script is a test tool. As such it is in no way intended to
27 # replace the "real" dtrace command for any practical purpose, apart
28 # from testing the DTrace USDT probes support in GDB.
32 # pdtrace is a limited dtrace program, implementing a subset of its
35 # - The generation of an ELF file containing an embedded dtrace
36 # program. Equivalent to dtrace -G.
38 # - The generation of a header file with definitions for static
39 # probes. Equivalent to dtrace -h.
41 # This allows to generate DTrace static probes without having to use
42 # the user-level DTrace components. The generated objects are 100%
43 # compatible with DTrace and can be traced by the dtrace kernel module
44 # like if they were generated by dtrace.
46 # Some of the known limitations of this implementation are:
47 # - The input d-script must describe one provider, and only one.
48 # - The "probe " directives in the d-file must not include argument
49 # names, just the types. Thus something like `char *' is valid, but
50 # `char *name' is not.
51 # - The command line options must precede other arguments, since the
52 # script uses the (more) portable getopts.
53 # - Each probe header in the d-script must be contained in
55 # - strip -K removes the debugging information from the input object
57 # - The supported target platforms are i[3456]86 and x86_64.
59 # Please keep this code as portable as possible. Restrict yourself to
62 # This script uses the following external programs, defined in
63 # variables. Some of them are substituted by autoconf.
66 NM
=@NM_TRANSFORM_NAME@
70 READELF
=@READELF_TRANSFORM_NAME@
77 AS
=@GAS_TRANSFORM_NAME@
78 STRIP
=@STRIP_TRANSFORM_NAME@
81 # Sizes for several DOF structures, in bytes.
83 # See linux/dtrace/dof.h for the definition of the referred
86 dof_hdrsize
=64 # sizeof(dtrace_dof_hdr)
87 dof_secsize
=32 # sizeof(dtrace_dof_sect)
88 dof_probesize
=48 # sizeof(dtrace_dof_probe)
89 dof_providersize
=44 # sizeof(dtrace_dof_provider)
91 # Types for the several DOF sections.
93 # See linux/dtrace/dof_defines.h for a complete list of section types
94 # along with their values.
96 dof_sect_type_strtab
=8
97 dof_sect_type_provider
=15
98 dof_sect_type_probes
=16
99 dof_sect_type_prargs
=17
100 dof_sect_type_proffs
=18
101 dof_sect_type_prenoffs
=26
105 # Write a message to the standard error output and exit with an error
113 echo "error: $1" 1>&2; exit 1
116 # Write a usage message to the standard output and exit with an error
121 printf "Usage: pdtrace [-32|-64] [-GhV] [-o output] [-s script] [ args ... ]\n\n"
123 printf "\t-32 generate 32-bit ELF files\n"
124 printf "\t-64 generate 64-bit ELF files\n\n"
126 printf "\t-G generate an ELF file containing embedded dtrace program\n"
127 printf "\t-h generate a header file with definitions for static probes\n"
128 printf "\t-o set output file\n"
129 printf "\t-s handle probes according to the specified D script\n"
130 printf "\t-V report the DTrace API version implemented by the tool\n"
134 # Write a version message to the standard output and exit with a
139 echo "pdtrace: Sun D 1.6.3"
143 # Add a new record to a list and return it.
147 # $2 is the new record
153 { rec
=$
(printf %s
\\n
"$rec"; echo x
); rec
=${rec%x}; }
157 # Collect the providers and probes information from the input object
160 # This function sets the values of the following global variables.
161 # The values are structured in records, each record in a line. The
162 # fields of each record are separated in some cases by white
163 # characters and in other cases by colon (:) characters.
165 # The type codes in the line format descriptors are:
166 # S: string, D: decimal number
169 # Regular probes and is-enabled probes.
170 # TYPE(S) PROVIDER(S) NAME(S) OFFSET(D) BASE(D) BASE_SYM(S)
172 # Base probes, i.e. probes sharing provider, name and container.
173 # PROVIDER(S) NAME(S) BASE(D) BASE_SYM(S)
177 # All the offsets are expressed in bytes.
182 # probes, base_probes, providers
191 # Probe points are function calls to undefined functions featuring
192 # distinct names for both normal probes and is-enabled probes.
193 PROBE_REGEX
="(__dtrace_([a-zA-Z_]+)___([a-zA-Z_]+))"
194 EPROBE_REGEX
="(__dtraceenabled_([a-zA-Z_]+)___([a-zA-Z_]+))"
196 while read type symbol provider name
; do
197 test -z "$type" && f_panic
"No probe points found in $objfile"
199 provider
=$
(printf %s
$provider |
$TR -s _
)
200 name
=$
(printf %s
$name |
$TR -s _
)
202 # Search the object file for relocations defined for the
203 # probe symbols. Then calculate the base address of the
204 # probe (along with the symbol associated with that base
205 # address) and the offset of the probe point.
206 for offset
in $
($READELF -W -r $objfile |
$EGREP $symbol |
$CUT -d' ' -f1)
208 # Figure out the base address for the probe. This is
209 # done finding the function name in the text section of
210 # the object file located above the probed point. But
211 # note that the relocation is for the address operand of
212 # the call instruction, so we have to subtract 1 to find
213 # the real probed point.
214 offset
=$
((0x
$offset - 1))
216 # The addresses of is-enabled probes must point to the
217 # first NOP instruction in their patched instructions
218 # sequences, so modify them (see f_patch_objfile for the
219 # instruction sequences).
220 if test "$type" = "e"; then
221 if test "$objbits" -eq "32"; then
222 offset
=$
((offset
+ 2))
224 offset
=$
((offset
+ 3))
228 # Determine the base address of the probe and its
229 # corresponding function name.
230 funcs
=$
($NM -td $objfile |
$EGREP "^[0-9]+ T " \
231 |
$CUT -d' ' -f1,3 |
$SORT -n -r |
$TR ' ' :)
232 for fun
in $funcs; do
233 func_off
=$
(printf %s
$fun |
$CUT -d: -f1)
234 func_sym
=$
(printf %s
$fun |
$CUT -d: -f2)
235 # Note that `expr' is used to remove leading zeros
236 # to avoid FUNC_OFF to be interpreted as an octal
237 # number in arithmetic contexts.
238 test "$func_off" -le "$offset" && \
239 { base
=$
($EXPR $func_off + 0); break; }
242 f_panic
"could not find base address for probe at $objfile($o)"
244 # Emit the record for the probe.
245 probes
=$
(f_add_record
"$probes" \
246 "$type $provider $name $(($offset - $base)) $base $func_sym")
249 $($NM $objfile | $EGREP " U $PROBE_REGEX" \
250 | $SED -E -e "s/.*$PROBE_REGEX.*/p \1 \2 \3/";
251 $NM $objfile | $EGREP " U $EPROBE_REGEX" \
252 | $SED -E -e "s/.*$EPROBE_REGEX.*/e \1 \2 \3/")
255 # Build the list of providers and of base probes from the probes.
256 while read type provider name offset base base_sym
; do
257 providers
=$
(f_add_record
"$providers" "$provider")
258 base_probes
=$
(f_add_record
"$base_probes" "$provider $name $base $base_sym")
262 providers
=$
(printf %s
\\n
"$providers" |
$SORT |
$UNIQ)
263 base_probes
=$
(printf %s
\\n
"$base_probes" |
$SORT |
$UNIQ)
266 # Collect the argument counts and type strings for all the probes
267 # described in the `probes' global variable. This is done by
268 # inspecting the d-script file provided by the user.
270 # This function sets the values of the following global variables.
271 # The values are structured in records, each record in a line. The
272 # fields of each record are separated in some cases by white
273 # characters and in other cases by colon (:) characters.
275 # The type codes in the line format descriptors are:
276 # S: string, D: decimal number
280 # PROVIDER(S):NAME(S):NARGS(D):ARG1(S):ARG2(S):...:ARGn(S)
287 # $1 is the d-script file from which to extract the arguments
290 f_collect_probes_args
()
293 while read type provider name offset base base_sym
; do
294 # Process normal probes only. Is-enabled probes are not
295 # described in the d-script file and they don't receive any
297 test "$type" = "p" ||
continue
299 # Names are mangled in d-script files to make it possible to
300 # have underscore characters as part of the provider name and
302 m_provider
=$
(printf %s
$provider |
$SED -e 's/_/__/g')
303 m_name
=$
(printf %s
$name |
$SED -e 's/_/__/g')
305 # Ignore this probe if the d-script file does not describe its
307 $EGREP -q "provider +$m_provider" $dscript ||
continue
309 # Look for the line containing the description of the probe.
310 # If we can't find it then ignore this probe.
311 line
=$
($EGREP "^ *probe +$m_name *\(.*\);" $dscript)
312 test -n "$line" ||
continue
314 # Ok, extract the argument types from the probe prototype.
315 # This is fragile as hell as it requires the prototype to be
317 args
=""; nargs
=0; line
=$
(printf %s
"$line" |
$SED -e 's/.*(\(.*\)).*/\1/')
325 # Emit the record for the probe arguments.
326 probes_args
=$
(f_add_record
"$probes_args" "$provider:$name:$nargs$args")
332 # Functions to manipulate the global BCOUNT.
338 BCOUNT
=$
((BCOUNT
+ $1))
343 test $
((BCOUNT
% $1)) -eq 0 || BCOUNT
=$
((BCOUNT
+ ($1 - (BCOUNT
% $1))))
346 # Generate a line of assembly code and add it to the asmprogram global
350 # $1 string to generate in a line.
356 line
=$
(printf "\t$1")
357 asmprogram
=$
(f_add_record
"$asmprogram" "$line")
360 # Helper function to generate the assembly code of a DOF section
363 # This function is used by `f_gen_dof_program'.
366 # $1 is the name of the described section.
367 # $2 is the type of the described section.
368 # $3 is the alignment of the described section.
369 # $4 is the number of entities stored in the described section.
370 # $5 is the offset in the DOF program of the described section.
371 # $6 is the size of the described section, in bytes.
373 f_gen_dof_sect_header
()
376 f_gen_asm
"/* dtrace_dof_sect for the $1 section. */"
377 f_gen_asm
".balign 8"
378 f_gen_asm
".4byte $2\t/* uint32_t dofs_type */"
379 f_gen_asm
".4byte $3\t/* uint32_t dofs_align */"
380 # The DOF_SECF_LOAD flag is 1 => loadable section.
381 f_gen_asm
".4byte 1\t/* uint32_t dofs_flags */"
382 f_gen_asm
".4byte $4\t/* uint32_t dofs_entsize */"
383 f_gen_asm
".8byte $5\t/* uint64_t dofs_offset */"
384 f_gen_asm
".8byte $6\t/* uint64_t dofs_size */"
387 # Generate a DOF program and assembly it in the output file.
389 # The DOF program generated by this function has the following
393 # STRTAB OFFTAB EOFFTAB [PROBES PROVIDER]...
394 # STRTAB_SECT OFFTAB_SECT EOFFTAB_SECT ARGTAB_SECT [PROBES_SECT PROVIDER_SECT]...
397 # probes, base_probes, providers, probes_args, BCOUNT
401 ###### Variables used to cache information needed later.
403 # Number of section headers in the generated DOF program.
405 # Offset of section headers in the generated DOF program, in bytes.
408 # Sizes of the STRTAB, OFFTAB and EOFFTAB sections, in bytes.
413 # Offsets of the STRTAB, OFFTAB EOFFTAB and PROBES sections in the
414 # generated DOF program. In bytes.
421 # Indexes of the section headers of the STRTAB, OFFTAB, EOFFTAB and
422 # PROBES sections in the sections array.
429 # First offsets and eoffsets of the base-probes.
430 # Lines: PROVIDER(S) NAME(S) BASE(D) (DOF_OFFSET(D)|DOF_EOFFSET(D))
434 # Offsets in the STRTAB section for the first type of base probes.
435 # Record per line: PROVIDER(S) NAME(S) BASE(D) OFFSET(D)
439 # Offsets of the provider names in the provider's STRTAB section.
440 # Lines: PROVIDER(S) OFFSET(D)
443 # Offsets of the base-probe names in the provider's STRTAB section.
444 # Lines: PROVIDER(S) NAME(S) BASE(D) OFFSET(D)
447 # Offsets of the provider sections in the DOF program.
448 # Lines: PROVIDER(S) OFFSET(D)
451 ###### Generation phase.
453 # The header of the DOF program contains a `struct
454 # dtrace_dof_hdr'. Record its size, but it is written at the end
456 f_incr_bcount
$dof_hdrsize; f_align_bcount
8
458 # The STRTAB section immediately follows the header. It contains
459 # the following set of packed null-terminated strings:
461 # [PROVIDER [BASE_PROBE_NAME [BASE_PROBE_ARG_TYPE...]]...]...
462 strtab_offset
=$BCOUNT
463 strtab_sect_index
=$dof_secnum
464 dof_secnum
=$
((dof_secnum
+ 1))
466 f_gen_asm
"/* The STRTAB section. */"
467 f_gen_asm
".balign 8"
468 # Add the provider names.
470 while read provider
; do
471 strtab_size
=$
(($strtab_size + ${#prov} + 1))
472 # Note the funny mangling...
473 f_gen_asm
".asciz \"$(printf %s $provider | $TR _ -)\""
474 providers_dof_names
=$
(f_add_record
"$providers_dof_names" \
476 off
=$
(($off + ${#provider} + 1))
478 # Add the base-probe names.
479 while read p_provider name base base_sym
; do
480 test "$p_provider" = "$provider" ||
continue
481 # And yes, more funny mangling...
482 f_gen_asm
".asciz \"$(printf %s $name | $TR _ -)\""
483 probes_dof_names
=$
(f_add_record
"$probes_dof_names" \
484 "$p_provider $name $base $off")
485 off
=$
(($off + ${#name} + 1))
487 a_provider
=$
(printf %s
"$args" |
$CUT -d: -f1)
488 a_name
=$
(printf %s
"$args" |
$CUT -d: -f2)
489 test "$a_provider" = "$p_provider" \
490 && test "$a_name" = "$name" \
493 probes_dof_types
=$
(f_add_record
"$probes_dof_types" \
494 "$a_provider $name $base $off")
495 nargs
=$
(printf %s
"$args" |
$CUT -d: -f3)
496 for n
in $
($SEQ $nargs); do
497 arg
=$
(printf %s
"$args" |
$CUT -d: -f$
(($n + 3)))
498 f_gen_asm
".asciz \"${arg}\""
499 off
=$
(($off + ${#arg} + 1))
511 f_incr_bcount
$strtab_size; f_align_bcount
8
513 # The OFFTAB section contains a set of 32bit words, one per
514 # defined regular probe.
515 offtab_offset
=$BCOUNT
516 offtab_sect_index
=$dof_secnum
517 dof_secnum
=$
((dof_secnum
+ 1))
519 f_gen_asm
"/* The OFFTAB section. */"
520 f_gen_asm
".balign 8"
522 while read type provider name offset base base_sym
; do
523 test "$type" = "p" ||
continue
524 f_gen_asm
".4byte $offset\t/* probe ${provider}:${name} */"
525 probes_dof_offsets
=$
(f_add_record
"$probes_dof_offsets" \
526 "$provider $name $base $off")
532 f_incr_bcount
$offtab_size; f_align_bcount
8
534 # The EOFFTAB section contains a set of 32bit words, one per
535 # defined is-enabled probe.
536 eofftab_offset
=$BCOUNT
537 eofftab_sect_index
=$dof_secnum
538 dof_secnum
=$
((dof_secnum
+ 1))
540 f_gen_asm
"/* The EOFFTAB section. */"
541 f_gen_asm
".balign 8"
543 while read type provider name offset base base_sym
; do
544 test "$type" = "e" ||
continue
545 f_gen_asm
".4byte $offset\t/* is-enabled probe ${provider}:${name} */"
546 probes_dof_eoffsets
=$
(f_add_record
"$probes_dof_eoffsets" \
547 "$provider $name $base $off")
553 f_incr_bcount
$eofftab_size; f_align_bcount
8
555 # The ARGTAB section is empty, but nonetheless has a section
556 # header, so record its section index here.
558 argtab_sect_index
=$dof_secnum
559 dof_secnum
=$
((dof_secnum
+ 1))
561 # Generate a pair of sections PROBES and PROVIDER for each
564 # The PROBES section contains an array of `struct
567 # A `dtrace_dof_probe' entry characterizes the collection of
568 # probes and is-enabled probes sharing the same provider, name and
570 probes_sect_index
=$dof_secnum
571 dof_secnum
=$
((dof_secnum
+ 1))
572 probes_offset
=$BCOUNT
573 num_base_probes
=$
(printf %s
\\n
"$base_probes" |
$WC -l)
574 while read provider name base base_sym
; do
575 name_offset
=$
(printf %s
\\n
"$probes_dof_names" \
576 |
$EGREP "^$provider $name " |
$CUT -d' ' -f4)
578 num_offsets
=$
(printf %s
\\n
"$probes_dof_offsets" \
579 |
$EGREP "^$provider $name [0-9]+ " |
$WC -l)
582 test "$num_offsets" -gt 0 && \
583 first_offset
=$
(printf %s
\\n
"$probes_dof_offsets" \
584 |
$EGREP "^$provider $name " |
$CUT -d' ' -f4 |
$HEAD -1)
586 num_eoffsets
=$
(printf %s
\\n
"$probes_dof_eoffsets" \
587 |
$EGREP "^$provider $name [0-9]+ " |
$WC -l)
589 test "$num_eoffsets" -gt 0 && \
590 first_eoffset
=$
(printf %s
"$probes_dof_eoffsets" \
591 |
$EGREP "^$provider $name " |
$CUT -d' ' -f4 |
$HEAD -1)
593 num_args
=$
(printf %s
"$probes_args" \
594 |
$EGREP "^$provider:$name:" |
$CUT -d: -f3 |
$HEAD -1)
596 first_type
=$
(printf %s
"$probes_dof_types" \
597 |
$EGREP "^$provider $name $base " |
$CUT -d' ' -f4 |
$HEAD -1)
599 reloctype
=R_X86_64_GLOB_DAT
600 test "$objbits" = "32" && reloctype
=R_386_32
603 f_gen_asm
"/* dtrace_dof_probe for ${provider}:${name} at ${base_sym} */"
604 f_gen_asm
".balign 8"
605 f_gen_asm
".reloc ., $reloctype, $base_sym + 0"
606 f_gen_asm
".8byte ${base}\t/* uint64_t dofpr_addr */"
607 f_gen_asm
".4byte 0\t/* uint32_t dofpr_func */"
608 f_gen_asm
".4byte $name_offset\t/* uint32_t dofpr_name */"
609 f_gen_asm
".4byte $first_type\t/* uint32_t dofpr_nargv */"
610 f_gen_asm
".4byte 0\t/* uint32_t dofpr_xargv */"
611 f_gen_asm
".4byte 0\t/* uint32_t dofpr_argidx */"
612 f_gen_asm
".4byte $(($first_offset/4))\t/* uint32_t dofpr_offidx */"
613 f_gen_asm
".byte $num_args\t/* uint8_t dofpr_nargc */"
614 f_gen_asm
".byte 0\t/* uint8_t dofpr_xargc */"
615 f_gen_asm
".2byte $num_offsets\t/* uint16_t dofpr_noffs */"
616 f_gen_asm
".4byte $(($first_eoffset/4))\t/* uint32_t dofpr_enoffidx */"
617 f_gen_asm
".2byte $num_eoffsets\t/* uint16_t dofpr_nenoffs */"
618 f_gen_asm
".2byte 0\t/* uint16_t dofpr_pad1 */"
619 f_gen_asm
".4byte 0\t/* uint16_t dofpr_pad2 */"
621 f_incr_bcount
"$dof_probesize"
626 # The PROVIDER section contains a `struct dtrace_dof_provider'
627 # instance describing the provider for the probes above.
628 dof_secnum
=$
((dof_secnum
+ 1))
629 providers_offsets
=$
(f_add_record
"$providers_offsets" \
631 # The dtrace_dof_provider.
632 provider_name_offset
=$
(printf %s
"$providers_dof_names" \
633 |
$EGREP "^$prov " |
$CUT -d' ' -f2)
636 f_gen_asm
"/* dtrace_dof_provider for $prov */"
637 f_gen_asm
".balign 8"
638 # Links to several DOF sections.
639 f_gen_asm
".4byte $strtab_sect_index\t/* uint32_t dofpv_strtab */"
640 f_gen_asm
".4byte $probes_sect_index\t/* uint32_t dofpv_probes */"
641 f_gen_asm
".4byte $argtab_sect_index\t/* uint32_t dofpv_prargs */"
642 f_gen_asm
".4byte $offtab_sect_index\t/* uint32_t dofpv_proffs */"
643 # Offset of the provider name into the STRTAB section.
644 f_gen_asm
".4byte $provider_name_offset\t/* uint32_t dofpv_name */"
645 # The rest of fields can be 0 for our modest purposes :)
646 f_gen_asm
".4byte 0\t/* uint32_t dofpv_provattr */"
647 f_gen_asm
".4byte 0\t/* uint32_t dofpv_modattr */"
648 f_gen_asm
".4byte 0\t/* uint32_t dofpv_funcattr */"
649 f_gen_asm
".4byte 0\t/* uint32_t dofpv_nameattr */"
650 f_gen_asm
".4byte 0\t/* uint32_t dofpv_argsattr */"
651 # But not this one, of course...
652 f_gen_asm
".4byte $eofftab_sect_index\t/* uint32_t dofpv_prenoffs */"
654 f_incr_bcount
$dof_providersize
660 # The section headers follow, one per section defined above.
663 f_gen_dof_sect_header STRTAB \
664 $dof_sect_type_strtab \
665 1 1 $strtab_offset $strtab_size
666 f_incr_bcount
$dof_secsize; f_align_bcount
8
668 f_gen_dof_sect_header OFFTAB \
669 $dof_sect_type_proffs \
670 4 4 $offtab_offset $offtab_size
671 f_incr_bcount
$dof_secsize; f_align_bcount
8
673 f_gen_dof_sect_header EOFFTAB \
674 $dof_sect_type_prenoffs \
675 4 4 $eofftab_offset $eofftab_size
676 f_incr_bcount
$dof_secsize; f_align_bcount
8
678 f_gen_dof_sect_header ARGTAB \
679 $dof_sect_type_prargs \
681 f_incr_bcount
$dof_secsize; f_align_bcount
8
683 while read provider
; do
684 provider_offset
=$
(printf %s
"$providers_offsets" \
685 |
$EGREP "^$provider " |
$CUT -d' ' -f2)
686 num_base_probes
=$
(printf %s
\\n
"$base_probes" |
$WC -l)
688 f_gen_dof_sect_header
"$provider probes" \
689 $dof_sect_type_probes \
690 8 $dof_probesize $probes_offset \
691 $
((num_base_probes
* dof_probesize
))
692 f_incr_bcount
$dof_secsize; f_align_bcount
8
694 f_gen_dof_sect_header
"$provider provider" \
695 $dof_sect_type_provider \
696 8 1 $provider_offset $dof_providersize
697 f_incr_bcount
$dof_secsize; f_align_bcount
8
702 # Finally, cook the header.
703 asmbody
="$asmprogram"
705 f_gen_asm
"/* File generated by pdtrace. */"
708 f_gen_asm
".section .SUNW_dof,\"a\",\"progbits\""
709 f_gen_asm
".globl __SUNW_dof"
710 f_gen_asm
".hidden __SUNW_dof"
711 f_gen_asm
".size __SUNW_dof, ${BCOUNT}"
712 f_gen_asm
".type __SUNW_dof, @object"
713 f_gen_asm
"__SUNW_dof:"
716 f_gen_asm
"/* dtrace_dof_hdr */"
717 f_gen_asm
".balign 8"
718 f_gen_asm
".byte 0x7f, 'D, 'O, 'F\t/* dofh_ident[0..3] */"
719 f_gen_asm
".byte 2\t\t/* model: 1=ILP32, 2=LP64 */"
720 f_gen_asm
".byte 1\t\t/* encoding: 1: little-endian, 2: big-endian */"
721 f_gen_asm
".byte 2\t\t/* DOF version: 1 or 2. Latest is 2 */"
722 f_gen_asm
".byte 2\t\t/* DIF version: 1 or 2. Latest is 2 */"
723 f_gen_asm
".byte 8\t\t/* number of DIF integer registers */"
724 f_gen_asm
".byte 8\t\t/* number of DIF tuple registers */"
725 f_gen_asm
".byte 0, 0\t\t/* dofh_ident[10..11] */"
726 f_gen_asm
".4byte 0\t\t/* dofh_ident[12..15] */"
727 f_gen_asm
".4byte 0\t/* uint32_t dofh_flags */" # See Limitations above.
728 f_gen_asm
".4byte ${dof_hdrsize}\t/* uint32_t dofh_hdrsize */"
729 f_gen_asm
".4byte ${dof_secsize}\t/* uint32_t dofh_secsize */"
730 f_gen_asm
".4byte ${dof_secnum}\t/* uint32_t dofh_secnum */"
731 f_gen_asm
".8byte ${dof_secoff}\t/* uint64_t dofh_secoff */"
732 f_gen_asm
".8byte ${BCOUNT}\t/* uint64_t dofh_loadsz */"
733 f_gen_asm
".8byte ${BCOUNT}\t/* uint64_t dofh_filesz */"
734 f_gen_asm
".8byte 0\t/* uint64_t dofh_pad */"
737 # Ok, now assembly the program in OFILE
738 echo "$asmprogram$asmbody" |
$AS -$objbits -o $ofile
740 # Next step is to change the sh_type of the ".SUNW_dof" section
741 # headers to 0x6ffffff4 (SHT_SUNW_dof).
743 # Note that this code relies in the fact that readelf will list
744 # the sections ordered in the same order than the section headers
745 # in the section header table of the file.
746 elfinfo
=$
($READELF -a $ofile)
748 # Mind the endianness.
749 if printf %s
"$elfinfo" |
$EGREP -q "little endian"; then
750 sht_sunw_dof
=$
(printf %s
%s
%s
%s
\\364 \\377 \\377 \\157)
752 sht_sunw_dof
=$
(printf %s
%s
%s
%s
\\157 \\377 \\377 \\364)
755 shdr_start
=$
(printf %s
"$elfinfo" \
756 |
$EGREP "^[ \t]*Start of section headers:" \
757 |
$SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
758 test -n "$shdr_start" \
759 || f_panic
"could not extract the start of shdr from $ofile"
761 shdr_num_entries
=$
(printf %s
"$elfinfo" \
762 |
$EGREP "^[ \t]*Size of section headers:" \
763 |
$SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
764 test -n "$shdr_num_entries" \
765 || f_panic
"could not extract the number of shdr entries from $ofile"
767 shdr_entry_size
=$
(printf %s
"$elfinfo" \
768 |
$EGREP "^[ \t]*Size of section headers:" \
769 |
$SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
770 test -n "$shdr_entry_size" \
771 || f_panic
"could not fetch the size of section headers from $ofile"
774 data
=$
(printf %s
"$line" \
775 |
$SED -E -e 's/.*\[(.*)\][ \t]+([a-zA-Z_.]+).*/\1:\2/')
776 num
=$
(printf %s
"$data" |
$CUT -d: -f1)
777 name
=$
(printf %s
"$data" |
$CUT -d: -f2)
778 if test "$name" = ".SUNW_dof"; then
779 # Patch the new sh_type in the proper entry of the section
781 printf "$sht_sunw_dof" \
782 |
dd of
=$ofile conv
=notrunc count
=4 ibs
=1 bs
=1 \
783 seek
=$
((shdr_start
+ (shdr_entry_size
* num
) + 4)) \
788 $(printf %s "$elfinfo" | $EGREP "^[ \t]*\[[0-9 ]+\].*[A-Z]+.*PROGBITS")
793 # Patch the probed points in the given object file, replacing the
794 # function calls with NOPs.
796 # The probed points in the input object files are function calls.
797 # This function replaces these function calls by some other
798 # instruction sequences. Which replacement to use depends on several
799 # factors, as documented below.
802 # $1 is the object file to patch.
808 # Several x86_64 instruction opcodes, in octal.
809 x86_op_nop
=$
(printf \\220)
810 x86_op_ret
=$
(printf \\303)
811 x86_op_call
=$
(printf \\350)
812 x86_op_jmp32
=$
(printf \\351)
813 x86_op_rex_rax
=$
(printf \\110)
814 x86_op_xor_eax_0
=$
(printf \\063)
815 x86_op_xor_eax_1
=$
(printf \\300)
817 # Figure out the file offset of the text section in the object
819 text_off
=0x$
(objdump
-j .text
-h $objfile \
820 |
grep \.text |
$TR -s ' ' |
$CUT -d' ' -f 7)
822 while read type provider name offset base base_sym
; do
823 # Calculate the offset of the probed point in the object file.
824 # Note that the `offset' of is-enabled probes is tweaked in
825 # `f_collect_probes" to point ahead the patching point.
826 probe_off
=$
((text_off
+ base
+ offset
))
827 if test "$type" = "e"; then
828 if test "$objbits" -eq "32"; then
829 probe_off
=$
((probe_off
- 2))
831 probe_off
=$
((probe_off
- 3))
835 # The probed point can be either a CALL instruction or a JMP
836 # instruction (a tail call). This has an impact on the
837 # patching sequence. Fetch the first byte at the probed point
838 # and do the right thing.
840 byte
=$
(dd if=$objfile count
=1 ibs
=1 bs
=1 skip
=$probe_off 2> /dev
/null
)
841 test "$byte" = "$x86_op_jmp32" && nopret
="$x86_op_ret"
843 # Determine the patching sequence. It depends on the type of
844 # probe at hand (regular or is-enabled) and also if
845 # manipulating a 32bit or 64bit binary.
848 p
) patchseq
=$
(printf %s
%s
%s
%s
%s \
855 e
) test "$objbits" -eq 64 && \
856 patchseq
=$
(printf %s
%s
%s
%s
%s \
858 "$x86_op_xor_eax_0" \
859 "$x86_op_xor_eax_1" \
862 test "$objbits" -eq 32 && \
863 patchseq
=$
(printf %s
%s
%s
%s
%s \
864 "$x86_op_xor_eax_0" \
865 "$x86_op_xor_eax_1" \
870 *) f_panic
"internal error: wrong probe type $type";;
874 printf %s
"$patchseq" \
875 |
dd of
=$objfile conv
=notrunc count
=5 ibs
=1 bs
=1 seek
=$probe_off 2> /dev
/null
880 # Finally, we have to remove the __dtrace_* and __dtraceenabled_*
881 # symbols from the object file, along with their respective
884 # Note that the most obvious call:
885 # strip -v -N whatever -w foo.o
887 # strip: not stripping symbol `whatever' because it is named in a relocation
889 # Fortunately using `-K !whatever' instead tricks strip to do the
890 # right thing, but this is black magic and may eventually stop
892 $STRIP -K '!__dtrace_*' -w $objfile
893 $STRIP -K '!__dtraceenabled_*' -w $objfile
896 # Read the input .d file and print a header file with macros to
897 # invoke the probes defined in it.
901 guard
=$
(basename $ofile |
$TR - _ |
$CUT -d.
-f1 |
$TR a-z A-Z
)
902 printf "/*\n * Generated by pdtrace.\n */\n\n"
904 printf "#ifndef _${guard}_H\n"
905 printf "#define _${guard}_H\n\n"
907 printf "#include <unistd.h>\n"
908 printf "#include <inttypes.h>\n"
911 printf "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"
913 printf "#define _DTRACE_VERSION 1\n\n"
915 provider
=$
(cat $dfile |
$EGREP "^ *provider +([a-zA-Z_]+)" \
916 |
$SED -E -e 's/^ *provider +([a-zA-Z]+).*/\1/')
917 test -z "$provider" \
918 && f_panic
"unable to parse the provider name from $dfile."
919 u_provider
=$
(printf %s
"$provider" |
$TR a-z A-Z |
$TR -s _
)
921 cat $dfile |
$EGREP "^ *probe +[a-zA-Z_]+ *\(.*\);" | \
923 # Extract the probe name.
924 name
=$
(printf %s
"$line" \
925 |
$SED -E -e 's/^ *probe +([a-zA-Z_]+).*/\1/')
926 u_name
=$
(printf %s
"$name" |
$TR a-z A-Z |
$TR -s _
)
928 # Generate an arg1,arg2,...,argN line for the probe.
929 args
=""; nargs
=0; aline
=$
(printf %s
"$line" |
$SED -e 's/.*(\(.*\)).*/\1/')
931 for arg
in $aline; do
932 args
="${args}arg${nargs},"
938 echo "#if _DTRACE_VERSION"
941 # Emit the macros for the probe.
942 echo "#define ${u_provider}_${u_name}($args) \\"
943 echo " __dtrace_${provider}___${name}($args)"
944 echo "#define ${u_provider}_${u_name}_ENABLED() \\"
945 echo " __dtraceenabled_${provider}___${name}()"
947 # Emit the extern definitions for the probe dummy
950 printf %s
\\n
"$line" \
951 |
$SED -E -e "s/^ *probe +/extern void __dtrace_${provider}___/"
952 echo "extern int __dtraceenabled_${provider}___${name}(void);"
957 # Emit empty macros for the probe
958 echo "#define ${u_provider}_${u_name}($args)"
959 echo "#define ${u_provider}_${u_name}_ENABLED() (0)"
961 printf "\n#endif /* _DTRACE_VERSION */\n"
964 printf "#ifdef __cplusplus\n}\n#endif\n\n"
965 printf "#endif /* _${guard}_H */\n"
970 # Process command line arguments.
972 test "$#" -eq "0" && f_usage
979 while getopts VG3264hs
:o
: name
; do
983 test -f "$dfile" || f_panic
"cannot read $dfile";;
987 # Note the trick to support -32
989 2) test "$objbits" -eq 666 || f_usage
; objbits
=32;;
992 4) test "$objbits" -eq 777 || f_usage
; objbits
=64;;
996 shift $
(($OPTIND - 1))
998 test "$objbits" -eq "32" ||
test "$objbits" -eq "64" \
1001 test $
((genelf
+ genheader
)) -gt 1 && \
1002 { echo "Please use either -G or -h."; f_usage
; }
1004 test -n "$dfile" ||
{ echo "Please specify a .d file with -s."; exit 2; }
1006 if test "$genelf" -gt 0; then
1007 # In this mode there must be a remaining argument: the name of the
1008 # object file to inspect for probed points.
1009 test "$#" -ne "1" && f_usage
1010 test -f "$1" || f_panic
"cannot read $1"
1013 # Collect probe information from the input object file and the
1015 f_collect_probes
$objfile
1016 f_collect_probes_args
$dfile
1018 # Generate the assembly code and assemble the DOF program in
1019 # OFILE. Then patch OBJFILE to remove the dummy probe calls.
1021 f_patch_objfile
$objfile
1024 if test "$genheader" -gt 0; then
1025 test -n "$ofile" ||
{ echo "Please specify an output file with -o."; exit 2; }
1027 # In this mode no extra arguments shall be present.
1028 test "$#" -ne "0" && f_usage
1030 f_gen_header_file
> $ofile
1033 # pdtrace ends here.