update copyright year range in GDB files
[external/binutils.git] / gdb / testsuite / lib / pdtrace.in
1 #!/bin/sh
2
3 # A Poor(but Free)'s Man dtrace
4 #
5 # Copyright (C) 2014-2017 Free Software Foundation, Inc.
6 #
7 # Contributed by Oracle, Inc.
8 #
9 # This file is part of GDB.
10 #
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.
15 #
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.
20 #
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/>.
24
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.
29
30 # that said...
31 #
32 # pdtrace is a limited dtrace program, implementing a subset of its
33 # functionality:
34 #
35 # - The generation of an ELF file containing an embedded dtrace
36 #   program.  Equivalent to dtrace -G.
37 #
38 # - The generation of a header file with definitions for static
39 #   probes.  Equivalent to dtrace -h.
40 #
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.
45 #
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
54 #   a single line.
55 # - strip -K removes the debugging information from the input object
56 #   file.
57 # - The supported target platforms are i[3456]86 and x86_64.
58 #
59 # Please keep this code as portable as possible.  Restrict yourself to
60 # POSIX sh.
61
62 # This script uses the following external programs, defined in
63 # variables.  Some of them are substituted by autoconf.
64
65 TR=tr
66 NM=@NM_TRANSFORM_NAME@
67 EGREP=egrep
68 SED=sed
69 CUT=cut
70 READELF=@READELF_TRANSFORM_NAME@
71 SORT=sort
72 EXPR=expr
73 WC=wc
74 UNIQ=uniq
75 HEAD=head
76 SEQ=seq
77 AS=@GAS_TRANSFORM_NAME@
78 STRIP=@STRIP_TRANSFORM_NAME@
79 TRUE=true
80
81 # Sizes for several DOF structures, in bytes.
82 #
83 # See linux/dtrace/dof.h for the definition of the referred
84 # structures.
85
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)
90
91 # Types for the several DOF sections.
92 #
93 # See linux/dtrace/dof_defines.h for a complete list of section types
94 # along with their values.
95
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
102
103 ### Functions
104
105 # Write a message to the standard error output and exit with an error
106 # status.
107 #
108 # Arguments:
109 #   $1 error message.
110
111 f_panic()
112 {
113     echo "error: $1" 1>&2; exit 1
114 }
115
116 # Write a usage message to the standard output and exit with an error
117 # status.
118
119 f_usage()
120 {
121     printf "Usage: pdtrace [-32|-64] [-GhV] [-o output] [-s script] [ args ... ]\n\n"
122
123     printf "\t-32 generate 32-bit ELF files\n"
124     printf "\t-64 generate 64-bit ELF files\n\n"
125
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"
131     exit 2
132 }
133
134 # Write a version message to the standard output and exit with a
135 # successful status.
136
137 f_version()
138 {
139     echo "pdtrace: Sun D 1.6.3"
140     exit
141 }
142
143 # Add a new record to a list and return it.
144 #
145 # Arguments:
146 # $1 is the list.
147 # $2 is the new record
148
149 f_add_record()
150 {
151     rec=$1
152     test -n "$rec" && \
153         { rec=$(printf %s\\n "$rec"; echo x); rec=${rec%x}; }
154     printf %s "$rec$2"
155 }
156
157 # Collect the providers and probes information from the input object
158 # file.
159 #
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.
164 #
165 # The type codes in the line format descriptors are:
166 # S: string, D: decimal number
167 #
168 # probes
169 #   Regular probes and is-enabled probes.
170 #   TYPE(S) PROVIDER(S) NAME(S) OFFSET(D) BASE(D) BASE_SYM(S)
171 # base_probes
172 #   Base probes, i.e. probes sharing provider, name and container.
173 #   PROVIDER(S) NAME(S) BASE(D) BASE_SYM(S)
174 # providers
175 #   List of providers.
176 #   PROVIDER(S)
177 # All the offsets are expressed in bytes.
178 #
179 # Input globals:
180 #  objfile
181 # Output globals:
182 #  probes, base_probes, providers
183
184 probes=
185 base_probes=
186 providers=
187 probes_args=
188
189 f_collect_probes()
190 {
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_]+))"
195
196     while read type symbol provider name; do
197           test -z "$type" && f_panic "No probe points found in $objfile"
198
199           provider=$(printf %s $provider | $TR -s _)
200           name=$(printf %s $name | $TR -s _)
201
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)
207           do
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))
215
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))
223                   else # 64 bits
224                       offset=$((offset + 3))
225                   fi
226               fi
227               
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; }
240               done
241               test -n "$base" || \
242                 f_panic "could not find base address for probe at $objfile($o)"
243
244               # Emit the record for the probe.
245               probes=$(f_add_record "$probes" \
246                                     "$type $provider $name $(($offset - $base)) $base $func_sym")
247           done
248       done <<EOF
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/")
253 EOF
254
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")
259     done <<EOF
260 $probes
261 EOF
262     providers=$(printf %s\\n "$providers" | $SORT | $UNIQ)
263     base_probes=$(printf %s\\n "$base_probes" | $SORT | $UNIQ)
264 }
265
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.
269 #
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.
274 #
275 # The type codes in the line format descriptors are:
276 # S: string, D: decimal number
277 #
278 # probes_args
279 #   Probes arguments.
280 #   PROVIDER(S):NAME(S):NARGS(D):ARG1(S):ARG2(S):...:ARGn(S)
281 #
282 # Input globals:
283 #  probes
284 # Output globals:
285 #  probes_args
286 # Arguments:
287 #   $1 is the d-script file from which to extract the arguments
288 #      information.
289
290 f_collect_probes_args()
291 {
292     dscript=$1
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
296         # argument.
297         test "$type" = "p" || continue
298         
299         # Names are mangled in d-script files to make it possible to
300         # have underscore characters as part of the provider name and
301         # probe name.
302         m_provider=$(printf %s $provider | $SED -e 's/_/__/g')
303         m_name=$(printf %s $name | $SED -e 's/_/__/g')
304         
305         # Ignore this probe if the d-script file does not describe its
306         # provider.
307         $EGREP -q "provider +$m_provider" $dscript || continue
308         
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
313         
314         # Ok, extract the argument types from the probe prototype.
315         # This is fragile as hell as it requires the prototype to be
316         # in a single line.
317         args=""; nargs=0; line=$(printf %s "$line" | $SED -e 's/.*(\(.*\)).*/\1/')
318         set -f; IFS=,
319         for arg in $line; do
320             args="$args:$arg"
321             nargs=$((nargs + 1))
322         done
323         set +f; unset IFS
324
325         # Emit the record for the probe arguments.
326         probes_args=$(f_add_record "$probes_args" "$provider:$name:$nargs$args")
327     done <<EOF
328 $probes
329 EOF
330 }
331
332 # Functions to manipulate the global BCOUNT.
333
334 BCOUNT=0
335
336 f_incr_bcount()
337 {
338     BCOUNT=$((BCOUNT + $1))
339 }
340
341 f_align_bcount()
342 {
343     test $((BCOUNT % $1)) -eq 0 || BCOUNT=$((BCOUNT + ($1 - (BCOUNT % $1))))
344 }
345
346 # Generate a line of assembly code and add it to the asmprogram global
347 # variable.
348 #
349 # Arguments:
350 #   $1 string to generate in a line.
351
352 asmprogram=
353
354 f_gen_asm()
355 {
356     line=$(printf "\t$1")
357     asmprogram=$(f_add_record "$asmprogram" "$line")
358 }
359
360 # Helper function to generate the assembly code of a DOF section
361 # header.
362 #
363 # This function is used by `f_gen_dof_program'.
364 #
365 # Arguments:
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.
372
373 f_gen_dof_sect_header()
374 {
375     f_gen_asm ""
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  */"
385 }
386
387 # Generate a DOF program and assembly it in the output file.
388 #
389 # The DOF program generated by this function has the following
390 # structure:
391 #
392 # HEADER
393 # STRTAB OFFTAB EOFFTAB [PROBES PROVIDER]...
394 # STRTAB_SECT OFFTAB_SECT EOFFTAB_SECT ARGTAB_SECT [PROBES_SECT PROVIDER_SECT]...
395 #
396 # Input globals:
397 #   probes, base_probes, providers, probes_args, BCOUNT
398
399 f_gen_dof_program()
400 {   
401     ###### Variables used to cache information needed later.
402     
403     # Number of section headers in the generated DOF program.
404     dof_secnum=0
405     # Offset of section headers in the generated DOF program, in bytes.
406     dof_secoff=0
407
408     # Sizes of the STRTAB, OFFTAB and EOFFTAB sections, in bytes.
409     strtab_size=0
410     offtab_size=0
411     eofftab_size=0
412     
413     # Offsets of the STRTAB, OFFTAB EOFFTAB and PROBES sections in the
414     # generated DOF program.  In bytes.
415     strtab_offset=0
416     offtab_offset=0
417     eofftab_offset=0
418     argtab_offset=0
419     probes_offset=0
420     
421     # Indexes of the section headers of the STRTAB, OFFTAB, EOFFTAB and
422     # PROBES sections in the sections array.
423     strtab_sect_index=0
424     offtab_sect_index=0
425     eofftab_sect_index=0
426     argtab_sect_index=0
427     probes_sect_index=0
428
429     # First offsets and eoffsets of the base-probes.
430     # Lines: PROVIDER(S) NAME(S) BASE(D) (DOF_OFFSET(D)|DOF_EOFFSET(D))
431     probes_dof_offsets=
432     probes_dof_eoffsets=
433     
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)
436     probes_dof_types=
437
438
439     # Offsets of the provider names in the provider's STRTAB section.
440     # Lines: PROVIDER(S) OFFSET(D)
441     providers_dof_names=
442
443     # Offsets of the base-probe names in the provider's STRTAB section.
444     # Lines: PROVIDER(S) NAME(S) BASE(D) OFFSET(D)
445     probes_dof_names=
446     
447     # Offsets of the provider sections in the DOF program.
448     # Lines: PROVIDER(S) OFFSET(D)
449     providers_offsets=
450
451     ###### Generation phase.
452     
453     # The header of the DOF program contains a `struct
454     # dtrace_dof_hdr'.  Record its size, but it is written at the end
455     # of the function.
456     f_incr_bcount $dof_hdrsize; f_align_bcount 8
457
458     # The STRTAB section immediately follows the header.  It contains
459     # the following set of packed null-terminated strings:
460     #
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))
465     f_gen_asm ""
466     f_gen_asm "/* The STRTAB section.  */"
467     f_gen_asm ".balign 8"
468     # Add the provider names.
469     off=0
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" \
475                                            "$provider $off")
476         off=$(($off + ${#provider} + 1))
477
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))
486             while read args; do
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" \
491                     || continue
492
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))
500                 done                
501             done <<EOF
502 $probes_args
503 EOF
504         done <<EOF
505 $base_probes
506 EOF
507     done <<EOF
508 $providers
509 EOF
510     strtab_size=$off
511     f_incr_bcount $strtab_size; f_align_bcount 8
512
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))
518     f_gen_asm ""
519     f_gen_asm "/* The OFFTAB section.  */"
520     f_gen_asm ".balign 8"
521     off=0
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")
527         off=$(($off + 4))
528     done <<EOF
529 $probes
530 EOF
531     offtab_size=$off
532     f_incr_bcount $offtab_size; f_align_bcount 8
533
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))
539     f_gen_asm ""
540     f_gen_asm "/* The EOFFTAB section.  */"
541     f_gen_asm ".balign 8"
542     off=0
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")
548         off=$(($off + 4))
549     done <<EOF
550 $probes
551 EOF
552     eofftab_size=$off
553     f_incr_bcount $eofftab_size; f_align_bcount 8
554
555     # The ARGTAB section is empty, but nonetheless has a section
556     # header, so record its section index here.
557     argtab_offset=0
558     argtab_sect_index=$dof_secnum
559     dof_secnum=$((dof_secnum + 1))
560
561     # Generate a pair of sections PROBES and PROVIDER for each
562     # provider.
563     while read prov; do
564         # The PROBES section contains an array of `struct
565         # dtrace_dof_probe'.
566         #
567         # A `dtrace_dof_probe' entry characterizes the collection of
568         # probes and is-enabled probes sharing the same provider, name and
569         # base address.
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)
577
578             num_offsets=$(printf %s\\n "$probes_dof_offsets" \
579                           | $EGREP "^$provider $name [0-9]+ " | $WC -l)
580             
581             first_offset=0
582             test "$num_offsets" -gt 0 && \
583               first_offset=$(printf %s\\n "$probes_dof_offsets" \
584                              | $EGREP "^$provider $name " | $CUT -d' ' -f4 | $HEAD -1)
585
586             num_eoffsets=$(printf %s\\n "$probes_dof_eoffsets" \
587                            | $EGREP "^$provider $name [0-9]+ " | $WC -l)
588             first_eoffset=0
589             test "$num_eoffsets" -gt 0 && \
590               first_eoffset=$(printf %s "$probes_dof_eoffsets" \
591                               | $EGREP "^$provider $name " | $CUT -d' ' -f4 | $HEAD -1)
592
593             num_args=$(printf %s "$probes_args" \
594                        | $EGREP "^$provider:$name:" | $CUT -d: -f3 | $HEAD -1)
595  
596             first_type=$(printf %s "$probes_dof_types" \
597                          | $EGREP "^$provider $name $base " | $CUT -d' ' -f4 | $HEAD -1)
598
599             reloctype=R_X86_64_GLOB_DAT
600             test "$objbits" = "32" && reloctype=R_386_32
601             
602             f_gen_asm ""
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  */"
620
621             f_incr_bcount "$dof_probesize"
622         done <<EOF
623 $base_probes
624 EOF
625
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" \
630                                          "$prov $BCOUNT")
631         # The dtrace_dof_provider.
632         provider_name_offset=$(printf %s "$providers_dof_names" \
633                                       | $EGREP "^$prov " | $CUT -d' ' -f2)
634
635         f_gen_asm ""
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  */"
653
654         f_incr_bcount $dof_providersize
655     done<<EOF
656 $providers
657 EOF
658     f_align_bcount 8
659
660     # The section headers follow, one per section defined above.
661     dof_secoff=$BCOUNT
662
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
667
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
672
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
677
678     f_gen_dof_sect_header ARGTAB \
679                           $dof_sect_type_prargs \
680                           4 1 $argtab_offset 0
681     f_incr_bcount $dof_secsize; f_align_bcount 8
682     
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)
687
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
693
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
698     done <<EOF
699 $providers
700 EOF
701
702     # Finally, cook the header.
703     asmbody="$asmprogram"
704     asmprogram=""
705     f_gen_asm "/* File generated by pdtrace.  */"
706     f_gen_asm ""
707
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:"
714
715     f_gen_asm ""
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  */"
735     f_gen_asm ""
736
737     # Ok, now assembly the program in OFILE
738     echo "$asmprogram$asmbody" | $AS -$objbits -o $ofile
739
740     # Next step is to change the sh_type of the ".SUNW_dof" section
741     # headers to 0x6ffffff4 (SHT_SUNW_dof).
742     #
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)
747
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)
751     else
752         sht_sunw_dof=$(printf %s%s%s%s \\157 \\377 \\377 \\364)
753     fi
754
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"
760
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"
766
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"
772
773     while read line; do
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
780             # header table.
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)) \
784                         2> /dev/null
785             break
786         fi
787     done <<EOF
788 $(printf %s "$elfinfo" | $EGREP "^[ \t]*\[[0-9 ]+\].*[A-Z]+.*PROGBITS")
789 EOF
790
791 }
792
793 # Patch the probed points in the given object file, replacing the
794 # function calls with NOPs.
795 #
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.
800 #
801 # Arguments:
802 #  $1 is the object file to patch.
803
804 f_patch_objfile()
805 {
806     objfile=$1
807     
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)
816     
817     # Figure out the file offset of the text section in the object
818     # file.
819     text_off=0x$(objdump -j .text -h $objfile \
820                  | grep \.text | $TR -s ' ' | $CUT -d' ' -f 7)
821
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))
830             else # 64 bits
831                 probe_off=$((probe_off - 3))
832             fi
833         fi
834
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.
839         nopret="$x86_op_nop"
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"
842
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.
846         patchseq=
847         case $type in
848             p) patchseq=$(printf %s%s%s%s%s \
849                                  "$nopret" \
850                                  "$x86_op_nop" \
851                                  "$x86_op_nop" \
852                                  "$x86_op_nop" \
853                                  "$x86_op_nop")
854                ;;
855             e) test "$objbits" -eq 64 && \
856                  patchseq=$(printf %s%s%s%s%s \
857                                    "$x86_op_rex_rax" \
858                                    "$x86_op_xor_eax_0" \
859                                    "$x86_op_xor_eax_1" \
860                                    "$nopret" \
861                                    "$x86_op_nop")
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" \
866                                    "$nopret" \
867                                    "$x86_op_nop" \
868                                    "$x86_op_nop")
869                ;;
870             *) f_panic "internal error: wrong probe type $type";;
871         esac
872
873         # Patch!
874         printf %s "$patchseq" \
875                | dd of=$objfile conv=notrunc count=5 ibs=1 bs=1 seek=$probe_off 2> /dev/null
876     done <<EOF
877 $probes
878 EOF
879     
880     # Finally, we have to remove the __dtrace_* and __dtraceenabled_*
881     # symbols from the object file, along with their respective
882     # relocations.
883     #
884     # Note that the most obvious call:
885     #   strip -v -N whatever -w foo.o
886     # will not work:
887     #   strip: not stripping symbol `whatever' because it is named in a relocation
888     #
889     # Fortunately using `-K !whatever' instead tricks strip to do the
890     # right thing, but this is black magic and may eventually stop
891     # working...
892     $STRIP -K '!__dtrace_*' -w $objfile
893     $STRIP -K '!__dtraceenabled_*' -w $objfile
894 }
895
896 # Read the input .d file and print a header file with macros to
897 # invoke the probes defined in it.
898
899 f_gen_header_file()
900 {
901     guard=$(basename $ofile | $TR - _ | $CUT -d. -f1 | $TR a-z A-Z)
902     printf "/*\n * Generated by pdtrace.\n */\n\n"
903
904     printf "#ifndef _${guard}_H\n"
905     printf "#define _${guard}_H\n\n"
906
907     printf "#include <unistd.h>\n"
908     printf "#include <inttypes.h>\n"
909     printf \\n\\n
910
911     printf "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"
912
913     printf "#define _DTRACE_VERSION 1\n\n"
914
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 _)
920     
921     cat $dfile | $EGREP "^ *probe +[a-zA-Z_]+ *\(.*\);" | \
922         while read line; do
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 _)
927
928             # Generate an arg1,arg2,...,argN line for the probe.
929             args=""; nargs=0; aline=$(printf %s "$line" | $SED -e 's/.*(\(.*\)).*/\1/')
930             set -f; IFS=,
931             for arg in $aline; do
932                 args="${args}arg${nargs},"
933                 nargs=$((nargs + 1))
934             done
935             set +f; unset IFS
936             args=${args%,}
937
938             echo "#if _DTRACE_VERSION"
939             echo ""
940             
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}()"
946
947             # Emit the extern definitions for the probe dummy
948             # functions.
949             echo ""
950             printf %s\\n "$line" \
951                 | $SED -E -e "s/^ *probe +/extern void __dtrace_${provider}___/"
952             echo "extern int __dtraceenabled_${provider}___${name}(void);"
953
954
955             printf "\n#else\n"
956
957             # Emit empty macros for the probe
958             echo "#define ${u_provider}_${u_name}($args)"
959             echo "#define ${u_provider}_${u_name}_ENABLED() (0)"
960
961             printf "\n#endif /* _DTRACE_VERSION */\n"
962         done
963
964     printf "#ifdef __cplusplus\n}\n#endif\n\n"
965     printf "#endif /* _${guard}_H */\n"
966 }
967
968 ### Main program.
969
970 # Process command line arguments.
971
972 test "$#" -eq "0" && f_usage
973
974 genelf=0
975 genheader=0
976 objbits=64
977 ofile=
978 dfile=
979 while getopts VG3264hs:o: name; do
980     case $name in
981         V) f_version;;
982         s) dfile="$OPTARG";
983            test -f "$dfile" || f_panic "cannot read $dfile";;
984         o) ofile="$OPTARG";;
985         G) genelf=1;;
986         h) genheader=1;;
987         # Note the trick to support -32
988         3) objbits=666;;
989         2) test "$objbits" -eq 666 || f_usage; objbits=32;;
990         # Likewise for -64
991         6) objbits=777;;
992         4) test "$objbits" -eq 777 || f_usage; objbits=64;;
993         ?) f_usage;;
994     esac
995 done
996 shift $(($OPTIND - 1))
997
998 test "$objbits" -eq "32" || test "$objbits" -eq "64" \
999     || f_usage
1000
1001 test $((genelf + genheader)) -gt 1 && \
1002     { echo "Please use either -G or -h."; f_usage; }
1003
1004 test -n "$dfile" || { echo "Please specify a .d file with -s."; exit 2; }
1005
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"
1011     objfile=$1
1012
1013     # Collect probe information from the input object file and the
1014     # d-script.
1015     f_collect_probes $objfile    
1016     f_collect_probes_args $dfile
1017
1018     # Generate the assembly code and assemble the DOF program in
1019     # OFILE.  Then patch OBJFILE to remove the dummy probe calls.
1020     f_gen_dof_program
1021     f_patch_objfile $objfile
1022 fi
1023
1024 if test "$genheader" -gt 0; then
1025     test -n "$ofile" || { echo "Please specify an output file with -o."; exit 2; }
1026     
1027     # In this mode no extra arguments shall be present.
1028     test "$#" -ne "0" && f_usage
1029
1030     f_gen_header_file > $ofile
1031 fi
1032
1033 # pdtrace ends here.