diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index b26baad..1e36b45 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,16 @@
 2015-02-17  Jose E. Marchesi  <jose.marchesi@oracle.com>
 
+	* lib/dtrace.exp: New file.
+	* gdb.base/dtrace-probe.exp: Likewise.
+	* gdb.base/dtrace-probe.d: Likewise.
+	* gdb.base/dtrace-probe.c: Likewise.
+	* lib/pdtrace.in: Likewise.
+	* configure.ac: Output variables with the transformed names of
+	the strip, readelf, as and nm tools.  AC_SUBST lib/pdtrace.in.
+	* configure: Regenerated.
+
+2015-02-17  Jose E. Marchesi  <jose.marchesi@oracle.com>
+
 	* gdb.base/stap-probe.exp (stap_test): Remove "SystemTap" from
 	expected message when trying to access $_probe_* convenience
 	variables while not on a probe.
diff --git a/gdb/testsuite/configure b/gdb/testsuite/configure
index ca033c3..b593cd3 100755
--- a/gdb/testsuite/configure
+++ b/gdb/testsuite/configure
@@ -591,6 +591,10 @@ ac_includes_default="\
 
 ac_subst_vars='LTLIBOBJS
 LIBOBJS
+NM_TRANSFORM_NAME
+GAS_TRANSFORM_NAME
+READELF_TRANSFORM_NAME
+STRIP_TRANSFORM_NAME
 EXTRA_RULES
 EGREP
 GREP
@@ -1272,6 +1276,11 @@ _ACEOF
 
   cat <<\_ACEOF
 
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
 System types:
   --build=BUILD     configure for building on BUILD [guessed]
   --host=HOST       cross-compile to build programs to run on HOST [BUILD]
@@ -3458,6 +3467,42 @@ if test "${build}" = "${host}" -a "${host}" = "${target}"; then
 fi
 
 
+# Transform the name of some programs and generate the lib/pdtrace
+# test tool.
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
+STRIP_TRANSFORM_NAME=`echo strip | sed -e "$transform"`
+if test "x$STRIP_TRANSFORM_NAME" = x; then
+  STRIP_TRANSFORM_NAME=strip
+fi
+
+READELF_TRANSFORM_NAME=`echo readelf | sed -e "$transform"`
+if test "x$READELF_TRANSFORM_NAME" = x; then
+  READELF_TRANSFORM_NAME=readelf
+fi
+
+GAS_TRANSFORM_NAME=`echo as | sed -e "$transform"`
+if test "x$GAS_TRANSFORM_NAME" = x; then
+  GAS_TRANSFORM_NAME=as
+fi
+
+NM_TRANSFORM_NAME=`echo nm | sed -e "$transform"`
+if test "x$NM_TRANSFORM_NAME" = x; then
+  NM_TRANSFORM_NAME=nm
+fi
+
+ac_config_files="$ac_config_files lib/pdtrace"
+
+
 ac_config_files="$ac_config_files Makefile gdb.ada/Makefile gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.btrace/Makefile gdb.cell/Makefile gdb.compile/Makefile gdb.cp/Makefile gdb.disasm/Makefile gdb.dwarf2/Makefile gdb.dlang/Makefile gdb.fortran/Makefile gdb.gdb/Makefile gdb.go/Makefile gdb.server/Makefile gdb.java/Makefile gdb.hp/Makefile gdb.hp/gdb.objdbg/Makefile gdb.hp/gdb.base-hp/Makefile gdb.hp/gdb.aCC/Makefile gdb.hp/gdb.compat/Makefile gdb.hp/gdb.defects/Makefile gdb.guile/Makefile gdb.linespec/Makefile gdb.mi/Makefile gdb.modula2/Makefile gdb.multi/Makefile gdb.objc/Makefile gdb.opencl/Makefile gdb.opt/Makefile gdb.pascal/Makefile gdb.perf/Makefile gdb.python/Makefile gdb.reverse/Makefile gdb.stabs/Makefile gdb.threads/Makefile gdb.trace/Makefile gdb.xml/Makefile"
 
 cat >confcache <<\_ACEOF
@@ -4158,6 +4203,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 for ac_config_target in $ac_config_targets
 do
   case $ac_config_target in
+    "lib/pdtrace") CONFIG_FILES="$CONFIG_FILES lib/pdtrace" ;;
     "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
     "gdb.ada/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.ada/Makefile" ;;
     "gdb.arch/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.arch/Makefile" ;;
@@ -4599,6 +4645,11 @@ which seems to be undefined.  Please make sure it is defined." >&2;}
 
   esac
 
+
+  case $ac_file$ac_mode in
+    "lib/pdtrace":F) chmod +x lib/pdtrace ;;
+
+  esac
 done # for ac_tag
 
 
diff --git a/gdb/testsuite/configure.ac b/gdb/testsuite/configure.ac
index 5037723..eed0191 100644
--- a/gdb/testsuite/configure.ac
+++ b/gdb/testsuite/configure.ac
@@ -96,6 +96,32 @@ if test "${build}" = "${host}" -a "${host}" = "${target}"; then
 fi
 AC_SUBST(EXTRA_RULES)
 
+# Transform the name of some programs and generate the lib/pdtrace
+# test tool.
+AC_ARG_PROGRAM
+transform=`echo "$program_transform_name" | sed -e 's/[\\$][\\$]/\\$/g'`
+STRIP_TRANSFORM_NAME=`echo strip | sed -e "$transform"`
+if test "x$STRIP_TRANSFORM_NAME" = x; then
+  STRIP_TRANSFORM_NAME=strip
+fi
+AC_SUBST(STRIP_TRANSFORM_NAME)
+READELF_TRANSFORM_NAME=`echo readelf | sed -e "$transform"`
+if test "x$READELF_TRANSFORM_NAME" = x; then
+  READELF_TRANSFORM_NAME=readelf
+fi
+AC_SUBST(READELF_TRANSFORM_NAME)
+GAS_TRANSFORM_NAME=`echo as | sed -e "$transform"`
+if test "x$GAS_TRANSFORM_NAME" = x; then
+  GAS_TRANSFORM_NAME=as
+fi
+AC_SUBST(GAS_TRANSFORM_NAME)
+NM_TRANSFORM_NAME=`echo nm | sed -e "$transform"`
+if test "x$NM_TRANSFORM_NAME" = x; then
+  NM_TRANSFORM_NAME=nm
+fi
+AC_SUBST(NM_TRANSFORM_NAME)
+AC_CONFIG_FILES([lib/pdtrace], [chmod +x lib/pdtrace])
+
 AC_OUTPUT([Makefile \
   gdb.ada/Makefile \
   gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.btrace/Makefile \
diff --git a/gdb/testsuite/gdb.base/dtrace-probe.c b/gdb/testsuite/gdb.base/dtrace-probe.c
new file mode 100644
index 0000000..29933ad
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dtrace-probe.c
@@ -0,0 +1,38 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014, 2015 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "dtrace-probe.h"
+
+int
+main ()
+{
+  char *name = "application";
+
+  TEST_TWO_LOCATIONS ();
+  
+  int i = 0;
+  while (i < 10)
+    {
+      i++;
+      if (TEST_PROGRESS_COUNTER_ENABLED ())
+	TEST_PROGRESS_COUNTER (name, i);
+      else
+	TEST_TWO_LOCATIONS ();
+    }
+      
+  return 0; /* last break here */
+}
diff --git a/gdb/testsuite/gdb.base/dtrace-probe.d b/gdb/testsuite/gdb.base/dtrace-probe.d
new file mode 100644
index 0000000..6bcbf34
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dtrace-probe.d
@@ -0,0 +1,21 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014, 2015 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+provider test {
+  probe progress__counter (char *, int);
+  probe two__locations  ();
+};
diff --git a/gdb/testsuite/gdb.base/dtrace-probe.exp b/gdb/testsuite/gdb.base/dtrace-probe.exp
new file mode 100644
index 0000000..e42e948
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dtrace-probe.exp
@@ -0,0 +1,106 @@
+# Copyright (C) 2014, 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib "dtrace.exp"
+
+# Run the tests.
+# This returns -1 on failure to compile or start, 0 otherwise.
+proc dtrace_test {} {
+    global testfile hex srcfile binfile
+
+    standard_testfile
+    
+    if {[dtrace_build_usdt_test_program] == -1} {
+        untested "could not compile test program"
+        return -1
+    }
+
+    clean_restart ${binfile}
+    
+    if ![runto_main] {
+        return -1
+    }
+
+    gdb_test "print \$_probe_argc" "No probe at PC $hex" \
+        "check argument not at probe point"
+
+    # Test the 'info probes' command.
+    gdb_test "info probes dtrace" \
+        "test *progress-counter *$hex +no.*test *two-locations *$hex +always.*test *two-locations *$hex +always.*" \
+        "info probes dtrace"
+
+    # Disabling the probe test:two-locations shall have no effect,
+    # since no is-enabled probes are defined for it in the object
+    # file.
+
+    gdb_test "disable probe test two-locations" \
+	"Probe test:two-locations cannot be disabled.*" \
+	"disable probe test two-locations"
+
+    # On the other hand, the probe test:progress-counter can be
+    # enabled and then disabled again.
+
+    gdb_test "enable probe test progress-counter" \
+	"Probe test:progress-counter enabled.*" \
+	"enable probe test progress-counter"
+
+    gdb_test "disable probe test progress-counter" \
+	"Probe test:progress-counter disabled.*" \
+	"disable probe test progress-counter"
+
+    # Since test:progress-counter is disabled we can run to the second
+    # instance of the test:two-locations probe.
+
+    if {![runto "-probe-dtrace test:two-locations"]} {
+	fail "run to the first test:two-locations probe point"
+    }
+    if {![runto "-probe-dtrace test:two-locations"]} {
+	fail "run to the second test:two-locations probe point"
+    }
+
+    # Go back to the breakpoint on main() and enable the
+    # test:progress-counter probe.  Set a breakpoint on it and see
+    # that it gets reached.
+
+    if ![runto_main] {
+	return -1
+    }
+
+    gdb_test "enable probe test progress-counter" \
+	"Probe test:progress-counter enabled.*" \
+	"enable probe test progress-counter"
+
+    gdb_test "break -probe-dtrace test:progress-counter" \
+	".*Breakpoint \[0-9\]+ .*" "set breakpoint in test:progress-counter"
+    gdb_continue_to_breakpoint "test:progress-counter"
+
+    # Test probe arguments.
+    gdb_test "print \$_probe_argc" " = 2" \
+        "print \$_probe_argc for probe progress-counter"
+    gdb_test "print \$_probe_arg0" \
+        " = $hex \"application\"" \
+        "print \$_probe_arg0 for probe progress-counter"
+    gdb_test "print \$_probe_arg1" " = 1" \
+        "print \$_probe_arg1 for probe progress-counter"
+
+    # Set a breakpoint with multiple probe locations.
+    gdb_test "break -pdtrace test:two-locations" \
+        "Breakpoint \[0-9\]+ at $hex.*2 locations.*" \
+        "set multi-location probe breakpoint (probe two-locations)"
+
+    return 0
+}
+
+dtrace_test
diff --git a/gdb/testsuite/lib/dtrace.exp b/gdb/testsuite/lib/dtrace.exp
new file mode 100644
index 0000000..e323b08
--- /dev/null
+++ b/gdb/testsuite/lib/dtrace.exp
@@ -0,0 +1,71 @@
+# Copyright 2014, 2015 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Generate a test program containing DTrace USDT probes, whose sources
+# are ${srcfile} and ${testfile}.d.  The sequence of commands used to
+# generate the test program is:
+#
+# 1. Generate a header file from ${testfile}.d using dtrace -h.
+# 2. Compile ${srcfile}.c.
+# 3. Generate an object file containing a DOF program using dtrace -G.
+# 4. Link everything together to get the test program.
+#
+# Note that if DTrace is not found in the host system then this
+# function uses the pdtrace implementation, which is located at
+# testsuite/lib/pdtrace.
+#
+# This function requires 'testfile', 'srcfile' and 'binfile' to be
+# properly set.
+#
+# This function returns -1 on failure, 0 otherwise
+proc dtrace_build_usdt_test_program {} {
+    global testfile hex objdir srcdir srcfile subdir binfile
+    
+    # Make sure that dtrace is installed, it is the real one (not the
+    # script installed by SystemTap, for example) and of the right
+    # version (>= 0.4.0).  If it is not then use pdtrace instead.
+    set dtrace "dtrace"
+    set result [remote_exec host "$dtrace -V"]
+    if {[lindex $result 0] != 0 || ![regexp {^dtrace: Sun D [0-9]\.[0-9]\.[0-9]} [lindex $result 1]]} {
+	set dtrace "${objdir}/lib/pdtrace"
+    }
+    set dscript_file "${srcdir}/${subdir}/${testfile}.d"
+
+    # 1. Generate a header file from testprogram.d using dtrace -h.
+    set out_header_file [standard_output_file "${testfile}.h"]
+    set result [remote_exec host "$dtrace -h -s $dscript_file -o $out_header_file"]
+    verbose -log [lindex $result 1]
+    if {[lindex $result 0] != 0} {
+        return -1
+    }
+
+    # 2. Compile testprogram.c.
+    set options [list debug additional_flags=-I[file dirname $out_header_file]]
+    if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}.o" object ${options}] != ""} {
+        return -1
+    }
+
+    # 3. Generate an object file containing a DOF program using dtrace -G.
+    set result [remote_exec host "$dtrace -G -s $dscript_file -o ${binfile}-p.o ${binfile}.o"]
+    verbose -log [lindex $result 1]
+    if {[lindex $result 0] != 0} {
+        return -1
+    }
+
+    # 4. Link everything together to get the test program.
+    if {[gdb_compile "${binfile}.o ${binfile}-p.o" ${binfile} executable {debug}] != ""} {
+        return -1
+    }
+}
diff --git a/gdb/testsuite/lib/pdtrace.in b/gdb/testsuite/lib/pdtrace.in
new file mode 100755
index 0000000..118b017
--- /dev/null
+++ b/gdb/testsuite/lib/pdtrace.in
@@ -0,0 +1,1033 @@
+#!/bin/sh
+
+# A Poor(but Free)'s Man dtrace
+#
+# Copyright (C) 2014, 2015 Free Software Foundation, Inc.
+#
+# Contributed by Oracle, Inc.
+#
+# This file is part of GDB.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+# DISCLAIMER DISCLAIMER DISCLAIMER
+# This script is a test tool.  As such it is in no way intended to
+# replace the "real" dtrace command for any practical purpose, apart
+# from testing the DTrace USDT probes support in GDB.
+
+# that said...
+#
+# pdtrace is a limited dtrace program, implementing a subset of its
+# functionality:
+#
+# - The generation of an ELF file containing an embedded dtrace
+#   program.  Equivalent to dtrace -G.
+#
+# - The generation of a header file with definitions for static
+#   probes.  Equivalent to dtrace -h.
+#
+# This allows to generate DTrace static probes without having to use
+# the user-level DTrace components.  The generated objects are 100%
+# compatible with DTrace and can be traced by the dtrace kernel module
+# like if they were generated by dtrace.
+#
+# Some of the known limitations of this implementation are:
+# - The input d-script must describe one provider, and only one.
+# - The "probe " directives in the d-file must not include argument
+#   names, just the types.  Thus something like `char *' is valid, but
+#   `char *name' is not.
+# - The command line options must precede other arguments, since the
+#   script uses the (more) portable getopts.
+# - Each probe header in the d-script must be contained in
+#   a single line.
+# - strip -K removes the debugging information from the input object
+#   file.
+# - The supported target platforms are i[3456]86 and x86_64.
+#
+# Please keep this code as portable as possible.  Restrict yourself to
+# POSIX sh.
+
+# This script uses the following external programs, defined in
+# variables.  Some of them are substituted by autoconf.
+
+TR=tr
+NM=@NM_TRANSFORM_NAME@
+EGREP=egrep
+SED=sed
+CUT=cut
+READELF=@READELF_TRANSFORM_NAME@
+SORT=sort
+EXPR=expr
+WC=wc
+UNIQ=uniq
+HEAD=head
+SEQ=seq
+AS=@GAS_TRANSFORM_NAME@
+STRIP=@STRIP_TRANSFORM_NAME@
+TRUE=true
+
+# Sizes for several DOF structures, in bytes.
+#
+# See linux/dtrace/dof.h for the definition of the referred
+# structures.
+
+dof_hdrsize=64      # sizeof(dtrace_dof_hdr)
+dof_secsize=32      # sizeof(dtrace_dof_sect)
+dof_probesize=48    # sizeof(dtrace_dof_probe)
+dof_providersize=44 # sizeof(dtrace_dof_provider)
+
+# Types for the several DOF sections.
+#
+# See linux/dtrace/dof_defines.h for a complete list of section types
+# along with their values.
+
+dof_sect_type_strtab=8
+dof_sect_type_provider=15
+dof_sect_type_probes=16
+dof_sect_type_prargs=17
+dof_sect_type_proffs=18
+dof_sect_type_prenoffs=26
+
+### Functions
+
+# Write a message to the standard error output and exit with an error
+# status.
+#
+# Arguments:
+#   $1 error message.
+
+f_panic()
+{
+    echo "error: $1" 1>&2; exit 1
+}
+
+# Write a usage message to the standard output and exit with an error
+# status.
+
+f_usage()
+{
+    printf "Usage: pdtrace [-32|-64] [-GhV] [-o output] [-s script] [ args ... ]\n\n"
+
+    printf "\t-32 generate 32-bit ELF files\n"
+    printf "\t-64 generate 64-bit ELF files\n\n"
+
+    printf "\t-G  generate an ELF file containing embedded dtrace program\n"
+    printf "\t-h  generate a header file with definitions for static probes\n"
+    printf "\t-o  set output file\n"
+    printf "\t-s  handle probes according to the specified D script\n"
+    printf "\t-V  report the DTrace API version implemented by the tool\n"
+    exit 2
+}
+
+# Write a version message to the standard output and exit with a
+# successful status.
+
+f_version()
+{
+    echo "pdtrace: Sun D 1.6.3"
+    exit
+}
+
+# Add a new record to a list and return it.
+#
+# Arguments:
+# $1 is the list.
+# $2 is the new record
+
+f_add_record()
+{
+    rec=$1
+    test -n "$rec" && \
+        { rec=$(printf %s\\n "$rec"; echo x); rec=${rec%x}; }
+    printf %s "$rec$2"
+}
+
+# Collect the providers and probes information from the input object
+# file.
+#
+# This function sets the values of the following global variables.
+# The values are structured in records, each record in a line.  The
+# fields of each record are separated in some cases by white
+# characters and in other cases by colon (:) characters.
+#
+# The type codes in the line format descriptors are:
+# S: string, D: decimal number
+#
+# probes
+#   Regular probes and is-enabled probes.
+#   TYPE(S) PROVIDER(S) NAME(S) OFFSET(D) BASE(D) BASE_SYM(S)
+# base_probes
+#   Base probes, i.e. probes sharing provider, name and container.
+#   PROVIDER(S) NAME(S) BASE(D) BASE_SYM(S)
+# providers
+#   List of providers.
+#   PROVIDER(S)
+# All the offsets are expressed in bytes.
+#
+# Input globals:
+#  objfile
+# Output globals:
+#  probes, base_probes, providers
+
+probes=
+base_probes=
+providers=
+probes_args=
+
+f_collect_probes()
+{
+    # Probe points are function calls to undefined functions featuring
+    # distinct names for both normal probes and is-enabled probes.
+    PROBE_REGEX="(__dtrace_([a-zA-Z_]+)___([a-zA-Z_]+))"
+    EPROBE_REGEX="(__dtraceenabled_([a-zA-Z_]+)___([a-zA-Z_]+))"
+
+    while read type symbol provider name; do
+          test -z "$type" && f_panic "No probe points found in $objfile"
+
+          provider=$(printf %s $provider | $TR -s _)
+          name=$(printf %s $name | $TR -s _)
+
+          # Search the object file for relocations defined for the
+          # probe symbols.  Then calculate the base address of the
+          # probe (along with the symbol associated with that base
+          # address) and the offset of the probe point.
+          for offset in $($READELF -W -r $objfile | $EGREP $symbol | $CUT -d' ' -f1)
+          do
+              # Figure out the base address for the probe.  This is
+              # done finding the function name in the text section of
+              # the object file located above the probed point.  But
+              # note that the relocation is for the address operand of
+              # the call instruction, so we have to subtract 1 to find
+              # the real probed point.
+              offset=$((0x$offset - 1))
+
+              # The addresses of is-enabled probes must point to the
+              # first NOP instruction in their patched instructions
+              # sequences, so modify them (see f_patch_objfile for the
+              # instruction sequences).
+              if test "$type" = "e"; then
+                  if test "$objbits" -eq "32"; then
+                      offset=$((offset + 2))
+                  else # 64 bits
+                      offset=$((offset + 3))
+                  fi
+              fi
+              
+              # Determine the base address of the probe and its
+              # corresponding function name.
+              funcs=$($NM -td $objfile | $EGREP "^[0-9]+ T " \
+                      | $CUT -d' ' -f1,3 | $SORT -n -r | $TR ' ' :)
+              for fun in $funcs; do
+                  func_off=$(printf %s $fun | $CUT -d: -f1)
+                  func_sym=$(printf %s $fun | $CUT -d: -f2)
+                  # Note that `expr' is used to remove leading zeros
+                  # to avoid FUNC_OFF to be interpreted as an octal
+                  # number in arithmetic contexts.
+                  test "$func_off" -le "$offset" && \
+                      { base=$($EXPR $func_off + 0); break; }
+              done
+              test -n "$base" || \
+                f_panic "could not find base address for probe at $objfile($o)"
+
+              # Emit the record for the probe.
+              probes=$(f_add_record "$probes" \
+                                    "$type $provider $name $(($offset - $base)) $base $func_sym")
+          done
+      done <<EOF
+$($NM $objfile | $EGREP " U $PROBE_REGEX" \
+            | $SED -E -e "s/.*$PROBE_REGEX.*/p \1 \2 \3/";
+     $NM $objfile | $EGREP " U $EPROBE_REGEX" \
+         | $SED -E -e "s/.*$EPROBE_REGEX.*/e \1 \2 \3/")
+EOF
+
+    # Build the list of providers and of base probes from the probes.
+    while read type provider name offset base base_sym; do
+        providers=$(f_add_record "$providers" "$provider")
+        base_probes=$(f_add_record "$base_probes" "$provider $name $base $base_sym")
+    done <<EOF
+$probes
+EOF
+    providers=$(printf %s\\n "$providers" | $SORT | $UNIQ)
+    base_probes=$(printf %s\\n "$base_probes" | $SORT | $UNIQ)
+}
+
+# Collect the argument counts and type strings for all the probes
+# described in the `probes' global variable.  This is done by
+# inspecting the d-script file provided by the user.
+#
+# This function sets the values of the following global variables.
+# The values are structured in records, each record in a line.  The
+# fields of each record are separated in some cases by white
+# characters and in other cases by colon (:) characters.
+#
+# The type codes in the line format descriptors are:
+# S: string, D: decimal number
+#
+# probes_args
+#   Probes arguments.
+#   PROVIDER(S):NAME(S):NARGS(D):ARG1(S):ARG2(S):...:ARGn(S)
+#
+# Input globals:
+#  probes
+# Output globals:
+#  probes_args
+# Arguments:
+#   $1 is the d-script file from which to extract the arguments
+#      information.
+
+f_collect_probes_args()
+{
+    dscript=$1
+    while read type provider name offset base base_sym; do
+        # Process normal probes only.  Is-enabled probes are not
+        # described in the d-script file and they don't receive any
+        # argument.
+        test "$type" = "p" || continue
+        
+        # Names are mangled in d-script files to make it possible to
+        # have underscore characters as part of the provider name and
+        # probe name.
+        m_provider=$(printf %s $provider | $SED -e 's/_/__/g')
+        m_name=$(printf %s $name | $SED -e 's/_/__/g')
+        
+        # Ignore this probe if the d-script file does not describe its
+        # provider.
+        $EGREP -q "provider +$m_provider" $dscript || continue
+        
+        # Look for the line containing the description of the probe.
+        # If we can't find it then ignore this probe.
+        line=$($EGREP "^ *probe +$m_name *\(.*\);" $dscript)
+        test -n "$line" || continue
+        
+        # Ok, extract the argument types from the probe prototype.
+        # This is fragile as hell as it requires the prototype to be
+        # in a single line.
+        args=""; nargs=0; line=$(printf %s "$line" | $SED -e 's/.*(\(.*\)).*/\1/')
+        set -f; IFS=,
+        for arg in $line; do
+            args="$args:$arg"
+            nargs=$((nargs + 1))
+        done
+        set +f; unset IFS
+
+        # Emit the record for the probe arguments.
+        probes_args=$(f_add_record "$probes_args" "$provider:$name:$nargs$args")
+    done <<EOF
+$probes
+EOF
+}
+
+# Functions to manipulate the global BCOUNT.
+
+BCOUNT=0
+
+f_incr_bcount()
+{
+    BCOUNT=$((BCOUNT + $1))
+}
+
+f_align_bcount()
+{
+    test $((BCOUNT % $1)) -eq 0 || BCOUNT=$((BCOUNT + ($1 - (BCOUNT % $1))))
+}
+
+# Generate a line of assembly code and add it to the asmprogram global
+# variable.
+#
+# Arguments:
+#   $1 string to generate in a line.
+
+asmprogram=
+
+f_gen_asm()
+{
+    line=$(printf "\t$1")
+    asmprogram=$(f_add_record "$asmprogram" "$line")
+}
+
+# Helper function to generate the assembly code of a DOF section
+# header.
+#
+# This function is used by `f_gen_dof_program'.
+#
+# Arguments:
+#   $1 is the name of the described section.
+#   $2 is the type of the described section.
+#   $3 is the alignment of the described section.
+#   $4 is the number of entities stored in the described section.
+#   $5 is the offset in the DOF program of the described section.
+#   $6 is the size of the described section, in bytes.
+
+f_gen_dof_sect_header()
+{
+    f_gen_asm ""
+    f_gen_asm "/* dtrace_dof_sect for the $1 section.  */"
+    f_gen_asm ".balign 8"
+    f_gen_asm ".4byte $2\t/* uint32_t dofs_type  */"
+    f_gen_asm ".4byte $3\t/* uint32_t dofs_align  */"
+    # The DOF_SECF_LOAD flag is 1 => loadable section.
+    f_gen_asm ".4byte 1\t/* uint32_t dofs_flags  */"
+    f_gen_asm ".4byte $4\t/* uint32_t dofs_entsize  */"
+    f_gen_asm ".8byte $5\t/* uint64_t dofs_offset  */"
+    f_gen_asm ".8byte $6\t/* uint64_t dofs_size  */"
+}
+
+# Generate a DOF program and assembly it in the output file.
+#
+# The DOF program generated by this function has the following
+# structure:
+#
+# HEADER
+# STRTAB OFFTAB EOFFTAB [PROBES PROVIDER]...
+# STRTAB_SECT OFFTAB_SECT EOFFTAB_SECT ARGTAB_SECT [PROBES_SECT PROVIDER_SECT]...
+#
+# Input globals:
+#   probes, base_probes, providers, probes_args, BCOUNT
+
+f_gen_dof_program()
+{   
+    ###### Variables used to cache information needed later.
+    
+    # Number of section headers in the generated DOF program.
+    dof_secnum=0
+    # Offset of section headers in the generated DOF program, in bytes.
+    dof_secoff=0
+
+    # Sizes of the STRTAB, OFFTAB and EOFFTAB sections, in bytes.
+    strtab_size=0
+    offtab_size=0
+    eofftab_size=0
+    
+    # Offsets of the STRTAB, OFFTAB EOFFTAB and PROBES sections in the
+    # generated DOF program.  In bytes.
+    strtab_offset=0
+    offtab_offset=0
+    eofftab_offset=0
+    argtab_offset=0
+    probes_offset=0
+    
+    # Indexes of the section headers of the STRTAB, OFFTAB, EOFFTAB and
+    # PROBES sections in the sections array.
+    strtab_sect_index=0
+    offtab_sect_index=0
+    eofftab_sect_index=0
+    argtab_sect_index=0
+    probes_sect_index=0
+
+    # First offsets and eoffsets of the base-probes.
+    # Lines: PROVIDER(S) NAME(S) BASE(D) (DOF_OFFSET(D)|DOF_EOFFSET(D))
+    probes_dof_offsets=
+    probes_dof_eoffsets=
+    
+    # Offsets in the STRTAB section for the first type of base probes.
+    # Record per line: PROVIDER(S) NAME(S) BASE(D) OFFSET(D)
+    probes_dof_types=
+
+
+    # Offsets of the provider names in the provider's STRTAB section.
+    # Lines: PROVIDER(S) OFFSET(D)
+    providers_dof_names=
+
+    # Offsets of the base-probe names in the provider's STRTAB section.
+    # Lines: PROVIDER(S) NAME(S) BASE(D) OFFSET(D)
+    probes_dof_names=
+    
+    # Offsets of the provider sections in the DOF program.
+    # Lines: PROVIDER(S) OFFSET(D)
+    providers_offsets=
+
+    ###### Generation phase.
+    
+    # The header of the DOF program contains a `struct
+    # dtrace_dof_hdr'.  Record its size, but it is written at the end
+    # of the function.
+    f_incr_bcount $dof_hdrsize; f_align_bcount 8
+
+    # The STRTAB section immediately follows the header.  It contains
+    # the following set of packed null-terminated strings:
+    #
+    # [PROVIDER [BASE_PROBE_NAME [BASE_PROBE_ARG_TYPE...]]...]...
+    strtab_offset=$BCOUNT
+    strtab_sect_index=$dof_secnum
+    dof_secnum=$((dof_secnum + 1))
+    f_gen_asm ""
+    f_gen_asm "/* The STRTAB section.  */"
+    f_gen_asm ".balign 8"
+    # Add the provider names.
+    off=0
+    while read provider; do
+        strtab_size=$(($strtab_size + ${#prov} + 1))
+        # Note the funny mangling...
+        f_gen_asm ".asciz \"$(printf %s $provider | $TR _ -)\""
+        providers_dof_names=$(f_add_record "$providers_dof_names" \
+                                           "$provider $off")
+        off=$(($off + ${#provider} + 1))
+
+        # Add the base-probe names.
+        while read p_provider name base base_sym; do
+            test "$p_provider" = "$provider" || continue
+            # And yes, more funny mangling...
+            f_gen_asm ".asciz \"$(printf %s $name | $TR _ -)\""
+            probes_dof_names=$(f_add_record "$probes_dof_names" \
+                                            "$p_provider $name $base $off")
+            off=$(($off + ${#name} + 1))
+            while read args; do
+                a_provider=$(printf %s "$args" | $CUT -d: -f1)
+                a_name=$(printf %s "$args" | $CUT -d: -f2)
+                test "$a_provider" = "$p_provider" \
+                    && test "$a_name" = "$name" \
+                    || continue
+
+                probes_dof_types=$(f_add_record "$probes_dof_types" \
+                                                "$a_provider $name $base $off")
+                nargs=$(printf %s "$args" | $CUT -d: -f3)
+                for n in $($SEQ $nargs); do
+                    arg=$(printf %s "$args" | $CUT -d: -f$(($n + 3)))
+                    f_gen_asm ".asciz \"${arg}\""
+                    off=$(($off + ${#arg} + 1))
+                done                
+            done <<EOF
+$probes_args
+EOF
+        done <<EOF
+$base_probes
+EOF
+    done <<EOF
+$providers
+EOF
+    strtab_size=$off
+    f_incr_bcount $strtab_size; f_align_bcount 8
+
+    # The OFFTAB section contains a set of 32bit words, one per
+    # defined regular probe.
+    offtab_offset=$BCOUNT
+    offtab_sect_index=$dof_secnum
+    dof_secnum=$((dof_secnum + 1))
+    f_gen_asm ""
+    f_gen_asm "/* The OFFTAB section.  */"
+    f_gen_asm ".balign 8"
+    off=0
+    while read type provider name offset base base_sym; do
+        test "$type" = "p" || continue
+        f_gen_asm ".4byte $offset\t/* probe ${provider}:${name}  */"
+        probes_dof_offsets=$(f_add_record "$probes_dof_offsets" \
+                                          "$provider $name $base $off")
+        off=$(($off + 4))
+    done <<EOF
+$probes
+EOF
+    offtab_size=$off
+    f_incr_bcount $offtab_size; f_align_bcount 8
+
+    # The EOFFTAB section contains a set of 32bit words, one per
+    # defined is-enabled probe.
+    eofftab_offset=$BCOUNT
+    eofftab_sect_index=$dof_secnum
+    dof_secnum=$((dof_secnum + 1))
+    f_gen_asm ""
+    f_gen_asm "/* The EOFFTAB section.  */"
+    f_gen_asm ".balign 8"
+    off=0
+    while read type provider name offset base base_sym; do
+        test "$type" = "e" || continue
+        f_gen_asm ".4byte $offset\t/* is-enabled probe ${provider}:${name}  */"
+        probes_dof_eoffsets=$(f_add_record "$probes_dof_eoffsets" \
+                                           "$provider $name $base $off")
+        off=$(($off + 4))
+    done <<EOF
+$probes
+EOF
+    eofftab_size=$off
+    f_incr_bcount $eofftab_size; f_align_bcount 8
+
+    # The ARGTAB section is empty, but nonetheless has a section
+    # header, so record its section index here.
+    argtab_offset=0
+    argtab_sect_index=$dof_secnum
+    dof_secnum=$((dof_secnum + 1))
+
+    # Generate a pair of sections PROBES and PROVIDER for each
+    # provider.
+    while read prov; do
+        # The PROBES section contains an array of `struct
+        # dtrace_dof_probe'.
+        #
+        # A `dtrace_dof_probe' entry characterizes the collection of
+        # probes and is-enabled probes sharing the same provider, name and
+        # base address.
+        probes_sect_index=$dof_secnum
+        dof_secnum=$((dof_secnum + 1))
+        probes_offset=$BCOUNT        
+        num_base_probes=$(printf %s\\n "$base_probes" | $WC -l)
+        while read provider name base base_sym; do
+            name_offset=$(printf %s\\n "$probes_dof_names" \
+                          | $EGREP "^$provider $name " | $CUT -d' ' -f4)
+
+            num_offsets=$(printf %s\\n "$probes_dof_offsets" \
+                          | $EGREP "^$provider $name [0-9]+ " | $WC -l)
+            
+            first_offset=0
+            test "$num_offsets" -gt 0 && \
+              first_offset=$(printf %s\\n "$probes_dof_offsets" \
+                             | $EGREP "^$provider $name " | $CUT -d' ' -f4 | $HEAD -1)
+
+            num_eoffsets=$(printf %s\\n "$probes_dof_eoffsets" \
+                           | $EGREP "^$provider $name [0-9]+ " | $WC -l)
+            first_eoffset=0
+            test "$num_eoffsets" -gt 0 && \
+              first_eoffset=$(printf %s "$probes_dof_eoffsets" \
+                              | $EGREP "^$provider $name " | $CUT -d' ' -f4 | $HEAD -1)
+
+            num_args=$(printf %s "$probes_args" \
+                       | $EGREP "^$provider:$name:" | $CUT -d: -f3 | $HEAD -1)
+ 
+            first_type=$(printf %s "$probes_dof_types" \
+                         | $EGREP "^$provider $name $base " | $CUT -d' ' -f4 | $HEAD -1)
+
+            reloctype=R_X86_64_GLOB_DAT
+            test "$objbits" = "32" && reloctype=R_386_32
+            
+            f_gen_asm ""
+            f_gen_asm "/* dtrace_dof_probe for ${provider}:${name} at ${base_sym}  */"
+            f_gen_asm ".balign 8"
+            f_gen_asm ".reloc ., $reloctype, $base_sym + 0"
+            f_gen_asm ".8byte ${base}\t/* uint64_t dofpr_addr  */"
+            f_gen_asm ".4byte 0\t/* uint32_t dofpr_func  */"
+            f_gen_asm ".4byte $name_offset\t/* uint32_t dofpr_name   */"
+            f_gen_asm ".4byte $first_type\t/* uint32_t dofpr_nargv  */"
+            f_gen_asm ".4byte 0\t/* uint32_t dofpr_xargv  */"
+            f_gen_asm ".4byte 0\t/* uint32_t dofpr_argidx */"
+            f_gen_asm ".4byte $(($first_offset/4))\t/* uint32_t dofpr_offidx  */"
+            f_gen_asm ".byte  $num_args\t/* uint8_t dofpr_nargc  */"
+            f_gen_asm ".byte  0\t/* uint8_t dofpr_xargc  */"
+            f_gen_asm ".2byte $num_offsets\t/* uint16_t dofpr_noffs  */"
+            f_gen_asm ".4byte $(($first_eoffset/4))\t/* uint32_t dofpr_enoffidx  */"
+            f_gen_asm ".2byte $num_eoffsets\t/* uint16_t dofpr_nenoffs  */"
+            f_gen_asm ".2byte 0\t/* uint16_t dofpr_pad1  */"
+            f_gen_asm ".4byte 0\t/* uint16_t dofpr_pad2  */"
+
+            f_incr_bcount "$dof_probesize"
+        done <<EOF
+$base_probes
+EOF
+
+        # The PROVIDER section contains a `struct dtrace_dof_provider'
+        # instance describing the provider for the probes above.
+        dof_secnum=$((dof_secnum + 1))
+        providers_offsets=$(f_add_record "$providers_offsets" \
+                                         "$prov $BCOUNT")
+        # The dtrace_dof_provider.
+        provider_name_offset=$(printf %s "$providers_dof_names" \
+                                      | $EGREP "^$prov " | $CUT -d' ' -f2)
+
+        f_gen_asm ""
+        f_gen_asm "/* dtrace_dof_provider for $prov  */"
+        f_gen_asm ".balign 8"
+        # Links to several DOF sections.
+        f_gen_asm ".4byte $strtab_sect_index\t/* uint32_t dofpv_strtab  */"
+        f_gen_asm ".4byte $probes_sect_index\t/* uint32_t dofpv_probes  */"
+        f_gen_asm ".4byte $argtab_sect_index\t/* uint32_t dofpv_prargs  */"
+        f_gen_asm ".4byte $offtab_sect_index\t/* uint32_t dofpv_proffs  */"
+        # Offset of the provider name into the STRTAB section.
+        f_gen_asm ".4byte $provider_name_offset\t/* uint32_t dofpv_name  */"
+        # The rest of fields can be 0 for our modest purposes :)
+        f_gen_asm ".4byte 0\t/* uint32_t dofpv_provattr  */"
+        f_gen_asm ".4byte 0\t/* uint32_t dofpv_modattr  */"
+        f_gen_asm ".4byte 0\t/* uint32_t dofpv_funcattr  */"
+        f_gen_asm ".4byte 0\t/* uint32_t dofpv_nameattr  */"
+        f_gen_asm ".4byte 0\t/* uint32_t dofpv_argsattr  */"
+        # But not this one, of course...
+        f_gen_asm ".4byte $eofftab_sect_index\t/* uint32_t dofpv_prenoffs  */"
+
+        f_incr_bcount $dof_providersize
+    done<<EOF
+$providers
+EOF
+    f_align_bcount 8
+
+    # The section headers follow, one per section defined above.
+    dof_secoff=$BCOUNT
+
+    f_gen_dof_sect_header STRTAB \
+                          $dof_sect_type_strtab \
+                          1 1 $strtab_offset $strtab_size
+    f_incr_bcount $dof_secsize; f_align_bcount 8
+
+    f_gen_dof_sect_header OFFTAB \
+                          $dof_sect_type_proffs \
+                          4 4 $offtab_offset $offtab_size
+    f_incr_bcount $dof_secsize; f_align_bcount 8
+
+    f_gen_dof_sect_header EOFFTAB \
+                          $dof_sect_type_prenoffs \
+                          4 4 $eofftab_offset $eofftab_size
+    f_incr_bcount $dof_secsize; f_align_bcount 8
+
+    f_gen_dof_sect_header ARGTAB \
+                          $dof_sect_type_prargs \
+                          4 1 $argtab_offset 0
+    f_incr_bcount $dof_secsize; f_align_bcount 8
+    
+    while read provider; do
+        provider_offset=$(printf %s "$providers_offsets" \
+                          | $EGREP "^$provider " | $CUT -d' ' -f2)
+        num_base_probes=$(printf %s\\n "$base_probes" | $WC -l)
+
+        f_gen_dof_sect_header "$provider probes" \
+                              $dof_sect_type_probes \
+                              8 $dof_probesize $probes_offset \
+                              $((num_base_probes * dof_probesize))
+        f_incr_bcount $dof_secsize; f_align_bcount 8
+
+        f_gen_dof_sect_header "$provider provider" \
+                              $dof_sect_type_provider \
+                              8 1 $provider_offset $dof_providersize
+        f_incr_bcount $dof_secsize; f_align_bcount 8
+    done <<EOF
+$providers
+EOF
+
+    # Finally, cook the header.
+    asmbody="$asmprogram"
+    asmprogram=""
+    f_gen_asm "/* File generated by pdtrace.  */"
+    f_gen_asm ""
+
+    f_gen_asm ".section .SUNW_dof,\"a\",\"progbits\""
+    f_gen_asm ".globl __SUNW_dof"
+    f_gen_asm ".hidden __SUNW_dof"
+    f_gen_asm ".size __SUNW_dof, ${BCOUNT}"
+    f_gen_asm ".type __SUNW_dof, @object"
+    f_gen_asm "__SUNW_dof:"
+
+    f_gen_asm ""
+    f_gen_asm "/* dtrace_dof_hdr */"
+    f_gen_asm ".balign 8"
+    f_gen_asm ".byte  0x7f, 'D, 'O, 'F\t/* dofh_ident[0..3] */"
+    f_gen_asm ".byte  2\t\t/* model: 1=ILP32, 2=LP64 */"
+    f_gen_asm ".byte  1\t\t/* encoding: 1: little-endian, 2: big-endian */"
+    f_gen_asm ".byte  2\t\t/* DOF version: 1 or 2.  Latest is 2 */"
+    f_gen_asm ".byte  2\t\t/* DIF version: 1 or 2.  Latest is 2 */"
+    f_gen_asm ".byte  8\t\t/* number of DIF integer registers */"
+    f_gen_asm ".byte  8\t\t/* number of DIF tuple registers */"
+    f_gen_asm ".byte  0, 0\t\t/* dofh_ident[10..11] */"
+    f_gen_asm ".4byte 0\t\t/* dofh_ident[12..15] */"
+    f_gen_asm ".4byte 0\t/* uint32_t dofh_flags  */"  # See Limitations above.
+    f_gen_asm ".4byte ${dof_hdrsize}\t/* uint32_t dofh_hdrsize  */"
+    f_gen_asm ".4byte ${dof_secsize}\t/* uint32_t dofh_secsize */"
+    f_gen_asm ".4byte ${dof_secnum}\t/* uint32_t dofh_secnum  */"
+    f_gen_asm ".8byte ${dof_secoff}\t/* uint64_t dofh_secoff  */"
+    f_gen_asm ".8byte ${BCOUNT}\t/* uint64_t dofh_loadsz  */"
+    f_gen_asm ".8byte ${BCOUNT}\t/* uint64_t dofh_filesz  */"
+    f_gen_asm ".8byte 0\t/* uint64_t dofh_pad  */"
+    f_gen_asm ""
+
+    # Ok, now assembly the program in OFILE
+    echo "$asmprogram$asmbody" | $AS -$objbits -o $ofile
+
+    # Next step is to change the sh_type of the ".SUNW_dof" section
+    # headers to 0x6ffffff4 (SHT_SUNW_dof).
+    #
+    # Note that this code relies in the fact that readelf will list
+    # the sections ordered in the same order than the section headers
+    # in the section header table of the file.
+    elfinfo=$($READELF -a $ofile)
+
+    # Mind the endianness.
+    if printf %s "$elfinfo" | $EGREP -q "little endian"; then
+        sht_sunw_dof=$(printf %s%s%s%s \\364 \\377 \\377 \\157)
+    else
+        sht_sunw_dof=$(printf %s%s%s%s \\157 \\377 \\377 \\364)
+    fi
+
+    shdr_start=$(printf %s "$elfinfo" \
+                | $EGREP "^[ \t]*Start of section headers:" \
+                | $SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
+    test -n "$shdr_start" \
+        || f_panic "could not extract the start of shdr from $ofile"
+
+    shdr_num_entries=$(printf %s "$elfinfo" \
+                       | $EGREP "^[ \t]*Size of section headers:" \
+                       | $SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
+    test -n "$shdr_num_entries" \
+         || f_panic "could not extract the number of shdr entries from $ofile"
+
+    shdr_entry_size=$(printf %s "$elfinfo" \
+                      | $EGREP "^[ \t]*Size of section headers:" \
+                      | $SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
+    test -n "$shdr_entry_size" \
+         || f_panic "could not fetch the size of section headers from $ofile"
+
+    while read line; do
+        data=$(printf %s "$line" \
+               | $SED -E -e 's/.*\[(.*)\][ \t]+([a-zA-Z_.]+).*/\1:\2/')
+        num=$(printf %s "$data" | $CUT -d: -f1)
+        name=$(printf %s "$data" | $CUT -d: -f2)
+        if test "$name" = ".SUNW_dof"; then
+            # Patch the new sh_type in the proper entry of the section
+            # header table.
+            printf "$sht_sunw_dof" \
+                   | dd of=$ofile conv=notrunc count=4 ibs=1 bs=1 \
+                        seek=$((shdr_start + (shdr_entry_size * num) + 4)) \
+                        2> /dev/null
+            break
+        fi
+    done <<EOF
+$(printf %s "$elfinfo" | $EGREP "^[ \t]*\[[0-9 ]+\].*[A-Z]+.*PROGBITS")
+EOF
+
+}
+
+# Patch the probed points in the given object file, replacing the
+# function calls with NOPs.
+#
+# The probed points in the input object files are function calls.
+# This function replaces these function calls by some other
+# instruction sequences.  Which replacement to use depends on several
+# factors, as documented below.
+#
+# Arguments:
+#  $1 is the object file to patch.
+
+f_patch_objfile()
+{
+    objfile=$1
+    
+    # Several x86_64 instruction opcodes, in octal.
+    x86_op_nop=$(printf \\220)
+    x86_op_ret=$(printf \\303)
+    x86_op_call=$(printf \\350)
+    x86_op_jmp32=$(printf \\351)
+    x86_op_rex_rax=$(printf \\110)
+    x86_op_xor_eax_0=$(printf \\063)
+    x86_op_xor_eax_1=$(printf \\300)
+    
+    # Figure out the file offset of the text section in the object
+    # file.
+    text_off=0x$(objdump -j .text -h $objfile \
+                 | grep \.text | $TR -s ' ' | $CUT -d' ' -f 7)
+
+    while read type provider name offset base base_sym; do
+        # Calculate the offset of the probed point in the object file.
+        # Note that the `offset' of is-enabled probes is tweaked in
+        # `f_collect_probes" to point ahead the patching point.
+        probe_off=$((text_off + base + offset))
+        if test "$type" = "e"; then
+            if test "$objbits" -eq "32"; then
+                probe_off=$((probe_off - 2))
+            else # 64 bits
+                probe_off=$((probe_off - 3))
+            fi
+        fi
+
+        # The probed point can be either a CALL instruction or a JMP
+        # instruction (a tail call).  This has an impact on the
+        # patching sequence.  Fetch the first byte at the probed point
+        # and do the right thing.
+        nopret="$x86_op_nop"
+        byte=$(dd if=$objfile count=1 ibs=1 bs=1 skip=$probe_off 2> /dev/null)
+        test "$byte" = "$x86_op_jmp32" && nopret="$x86_op_ret"
+
+        # Determine the patching sequence.  It depends on the type of
+        # probe at hand (regular or is-enabled) and also if
+        # manipulating a 32bit or 64bit binary.
+        patchseq=
+        case $type in
+            p) patchseq=$(printf %s%s%s%s%s \
+                                 "$nopret" \
+                                 "$x86_op_nop" \
+                                 "$x86_op_nop" \
+                                 "$x86_op_nop" \
+                                 "$x86_op_nop")
+               ;;
+            e) test "$objbits" -eq 64 && \
+                 patchseq=$(printf %s%s%s%s%s \
+                                   "$x86_op_rex_rax" \
+                                   "$x86_op_xor_eax_0" \
+                                   "$x86_op_xor_eax_1" \
+                                   "$nopret" \
+                                   "$x86_op_nop")
+               test "$objbits" -eq 32 && \
+                 patchseq=$(printf %s%s%s%s%s \
+                                   "$x86_op_xor_eax_0" \
+                                   "$x86_op_xor_eax_1" \
+                                   "$nopret" \
+                                   "$x86_op_nop" \
+                                   "$x86_op_nop")
+               ;;
+            *) f_panic "internal error: wrong probe type $type";;
+        esac
+
+        # Patch!
+        printf %s "$patchseq" \
+               | dd of=$objfile conv=notrunc count=5 ibs=1 bs=1 seek=$probe_off 2> /dev/null
+    done <<EOF
+$probes
+EOF
+    
+    # Finally, we have to remove the __dtrace_* and __dtraceenabled_*
+    # symbols from the object file, along with their respective
+    # relocations.
+    #
+    # Note that the most obvious call:
+    #   strip -v -N whatever -w foo.o
+    # will not work:
+    #   strip: not stripping symbol `whatever' because it is named in a relocation
+    #
+    # Fortunately using `-K !whatever' instead tricks strip to do the
+    # right thing, but this is black magic and may eventually stop
+    # working...
+    $STRIP -K '!__dtrace_*' -w $objfile
+    $STRIP -K '!__dtraceenabled_*' -w $objfile
+}
+
+# Read the input .d file and print a header file with macros to
+# invoke the probes defined in it.
+
+f_gen_header_file()
+{
+    guard=$(basename $ofile | $TR - _ | $CUT -d. -f1 | $TR a-z A-Z)
+    printf "/*\n * Generated by pdtrace.\n */\n\n"
+
+    printf "#ifndef _${guard}_H\n"
+    printf "#define _${guard}_H\n\n"
+
+    printf "#include <unistd.h>\n"
+    printf "#include <inttypes.h>\n"
+    printf \\n\\n
+
+    printf "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"
+
+    printf "#define _DTRACE_VERSION 1\n\n"
+
+    provider=$(cat $dfile | $EGREP "^ *provider +([a-zA-Z_]+)" \
+               | $SED -E -e 's/^ *provider +([a-zA-Z]+).*/\1/')
+    test -z "$provider" \
+        && f_panic "unable to parse the provider name from $dfile."
+    u_provider=$(printf %s "$provider" | $TR a-z A-Z | $TR -s _)
+    
+    cat $dfile | $EGREP "^ *probe +[a-zA-Z_]+ *\(.*\);" | \
+        while read line; do
+            # Extract the probe name.
+            name=$(printf %s "$line" \
+                   | $SED -E -e 's/^ *probe +([a-zA-Z_]+).*/\1/')
+            u_name=$(printf %s "$name" | $TR a-z A-Z | $TR -s _)
+
+            # Generate an arg1,arg2,...,argN line for the probe.
+            args=""; nargs=0; aline=$(printf %s "$line" | $SED -e 's/.*(\(.*\)).*/\1/')
+            set -f; IFS=,
+            for arg in $aline; do
+                args="${args}arg${nargs},"
+                nargs=$((nargs + 1))
+            done
+            set +f; unset IFS
+            args=${args%,}
+
+            echo "#if _DTRACE_VERSION"
+            echo ""
+            
+            # Emit the macros for the probe.
+            echo "#define ${u_provider}_${u_name}($args) \\"
+            echo "   __dtrace_${provider}___${name}($args)"
+            echo "#define ${u_provider}_${u_name}_ENABLED() \\"
+            echo "   __dtraceenabled_${provider}___${name}()"
+
+            # Emit the extern definitions for the probe dummy
+            # functions.
+            echo ""
+            printf %s\\n "$line" \
+                | $SED -E -e "s/^ *probe +/extern void __dtrace_${provider}___/"
+            echo "extern int __dtraceenabled_${provider}___${name}(void);"
+
+
+            printf "\n#else\n"
+
+            # Emit empty macros for the probe
+            echo "#define ${u_provider}_${u_name}($args)"
+            echo "#define ${u_provider}_${u_name}_ENABLED() (0)"
+
+            printf "\n#endif /* _DTRACE_VERSION */\n"
+        done
+
+    printf "#ifdef __cplusplus\n}\n#endif\n\n"
+    printf "#endif /* _${guard}_H */\n"
+}
+
+### Main program.
+
+# Process command line arguments.
+
+test "$#" -eq "0" && f_usage
+
+genelf=0
+genheader=0
+objbits=64
+ofile=
+dfile=
+while getopts VG3264hs:o: name; do
+    case $name in
+        V) f_version;;
+        s) dfile="$OPTARG";
+           test -f "$dfile" || f_panic "cannot read $dfile";;
+        o) ofile="$OPTARG";;
+        G) genelf=1;;
+        h) genheader=1;;
+        # Note the trick to support -32
+        3) objbits=666;;
+        2) test "$objbits" -eq 666 || f_usage; objbits=32;;
+        # Likewise for -64
+        6) objbits=777;;
+        4) test "$objbits" -eq 777 || f_usage; objbits=64;;
+        ?) f_usage;;
+    esac
+done
+shift $(($OPTIND - 1))
+
+test "$objbits" -eq "32" || test "$objbits" -eq "64" \
+    || f_usage
+
+test $((genelf + genheader)) -gt 1 && \
+    { echo "Please use either -G or -h."; f_usage; }
+
+test -n "$dfile" || { echo "Please specify a .d file with -s."; exit 2; }
+
+if test "$genelf" -gt 0; then
+    # In this mode there must be a remaining argument: the name of the
+    # object file to inspect for probed points.
+    test "$#" -ne "1" && f_usage
+    test -f "$1" || f_panic "cannot read $1"
+    objfile=$1
+
+    # Collect probe information from the input object file and the
+    # d-script.
+    f_collect_probes $objfile    
+    f_collect_probes_args $dfile
+
+    # Generate the assembly code and assemble the DOF program in
+    # OFILE.  Then patch OBJFILE to remove the dummy probe calls.
+    f_gen_dof_program
+    f_patch_objfile $objfile
+fi
+
+if test "$genheader" -gt 0; then
+    test -n "$ofile" || { echo "Please specify an output file with -o."; exit 2; }
+    
+    # In this mode no extra arguments shall be present.
+    test "$#" -ne "0" && f_usage
+
+    f_gen_header_file > $ofile
+fi
+
+# pdtrace ends here.
