blob: 4e5d06f672a5ada4d2a41fb60bdd7220c60e49fc [file] [log] [blame]
#!/usr/local/bin/perl
# **********************************************************
# Copyright (c) 2012-2014 Google, Inc. All rights reserved.
# Copyright (c) 2002-2010 VMware, Inc. All rights reserved.
# **********************************************************
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
# Copyright (c) 2003-2007 Determina Corp.
# Copyright (c) 2002-2003 Massachusetts Institute of Technology
# Copyright (c) 2002 Hewlett Packard Company
### genapi.pl
###
### generates header files and filter files for our exported data and routines
### N.B.: this script assumes several things about the layout of declarations:
### 1) comments are not sprinkled in weird places
### 2) multiple declarations do not share the same line
###
$usage = "Usage: $0 [-core <core dir>] -header <destinationdir> <defines> | -filter <defines> | -debug\n";
$header = 0;
$debug = 0;
$filter = 0;
$core = ".";
if ($#ARGV < 0) {
print "$usage";
exit 1;
}
while ($#ARGV >= 0) {
if ($ARGV[0] eq '-core') {
# Take the core source dir path on the command line.
shift;
if ($#ARGV < 1) {
print "$usage";
print "Expected path after -core\n";
exit 1;
}
$core = shift;
if (! -d $core) {
print "$core is not a directory\n";
print "$usage";
exit 1;
}
}
if ($ARGV[0] eq '-header') {
$header = 1;
shift;
if ($#ARGV != 1) {
print "$usage";
print "Incorrect number of arguments for -header\n";
exit 1;
}
$dir = $ARGV[0];
if (! -d $dir) {
print "$dir is not a directory\n";
print "$usage";
exit 1;
}
shift;
$define_str = $ARGV[0];
$define_str =~ s/^[-\/]D//;
$define_str =~ s/ [-\/]D/ /g;
@define_els = split(' ', $define_str);
foreach $i (@define_els) {
$defines{$i} = 1;
}
} elsif ($ARGV[0] eq '-filter') {
$filter = 1;
shift;
$define_str = $ARGV[0];
$define_str =~ s/^[-\/]D//;
$define_str =~ s/ [-\/]D/ /g;
@define_els = split(' ', $define_str);
foreach $i (@define_els) {
$defines{$i} = 1;
}
} elsif ($ARGV[0] eq '-debug') {
$debug = 1;
} else {
print "$usage";
print "Unrecognized argument: $ARGV[0]\n";
exit 1;
}
shift;
}
# I was using File::Copy but native-windows perl
# "v5.8.8 built for MSWin32-x86-multi-thread" maintains modtime, and
# even doing utime() after the copy() wasn't updating.
# With the old time we have a rebuild of header files on every make.
# So just doing my own copy rather than waste any more time on
# perl variant issues. This gives us consistent line endings with
# the generated files, as well.
sub copy_file
{
my ($src, $dst) = @_;
open(SRC, "< $src") || die "Error: Couldn't open $src for copy\n";
open(DST, "> $dst") || die "Error: Couldn't open $dst for copy\n";
while (<SRC>) {
print DST $_;
}
close(SRC);
close(DST);
}
if ($header) {
# clean up existing files first
# since the set of files we're creating is dynamic it's not clear how
# we can avoid this auto-clean and have something nicer
@existing = glob("$dir/dr_*.h");
if ($#existing >= 0) {
# dr_api.h is now created at configure time
foreach $index (0 .. $#existing) {
if ("$existing[$index]" eq "$dir/dr_api.h") {
delete $existing[$index];
}
}
if ($#existing >= 0) {
unlink(@existing);
}
}
if (defined($defines{"CLIENT_INTERFACE"})) {
# dr_api.h is copied by top-level CMakeLists.txt, for easier
# substitution of VERSION_NUMBER_INTEGER
# dr_app.h is copied verbatim
# We used to have #ifdefs (LOGPC I think) in the func declarations
# and we had a complex series of commands in core/Makefile to strip
# them out while leaving the ifdefs at the top of the file.
copy_file("$core/lib/dr_app.h", "$dir/dr_app.h");
} else {
if (!defined($defines{"APP_EXPORTS"})) {
die "Should not be invoked w/o APP_EXPORTS or CLIENT_INTERFACE\n";
}
# dr_app.h is copied verbatim
copy_file("$core/lib/dr_app.h", "$dir/dr_app.h");
}
}
$arch = defined($defines{"ARM"}) ? "arm" : "x86";
# I used to just do
# open(FIND, "find . -name \\*.h |")
# but there are dependencies between header files, and certain orders
# look nicer than others, so we have an explicit list here:
# Also, we now send output to multiple files, and their names are indicated
# by comments in the header files.
@headers =
(
"$core/arch/instrlist.h",
"$core/lib/globals_shared.h", # defs
"$core/globals.h",
"$core/arch/arch_exports.h", # encode routines
"$core/arch/proc.h",
"$core/os_shared.h", # before instrument.h
"$core/module_shared.h", # before instrument.h
"$core/lib/instrument.h",
"$core/arch/x86/opcode.h",
"$core/arch/arm/opcode.h",
"$core/arch/opnd.h",
"$core/arch/instr.h",
"$core/arch/instr_inline.h",
"$core/arch/instr_create_shared.h",
"$core/arch/x86/instr_create.h",
"$core/arch/arm/instr_create.h",
"$core/arch/decode.h", # OPSZ_ consts, decode routines
"$core/arch/decode_fast.h", # decode routines
"$core/arch/disassemble.h", # disassemble routines
"$core/fragment.h", # binary tracedump format
"$core/win32/os_private.h", # rsrc section walking
"$core/hotpatch.c", # probe api
"$core/lib/dr_config.h",
"$core/lib/dr_inject.h",
"$core/../libutil/dr_frontend.h",
);
if (defined($defines{"ANNOTATIONS"})) {
push(@headers, "$core/annotations.h");
}
# PR 214947: VMware retroactively holds the copyright.
$copyright = q+/* **********************************************************
* Copyright (c) 2010-2011 Google, Inc. All rights reserved.
* Copyright (c) 2002-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
+;
if ($header) {
$outfile_init = 0;
} else {
open(OUT, ">-"); # stdout
if ($filter) {
# linker version script w/ no versions, just global and local symbol lists
# except old ld requires version -- ok, we'll put in one
print OUT "DYNAMORIO_0.9.5 {\nglobal:\n";
}
}
sub keep_define($)
{
my ($def) = @_;
return ($def eq "WINDOWS" || $def eq "LINUX" || $def eq "UNIX" ||
$def eq "MACOS" || $def eq "X64" ||
$def eq "X86" || $def eq "ARM" || $def eq "X86_32" ||
$def eq "X86_64" || $def eq "USE_VISIBILITY_ATTRIBUTES" ||
$def eq "DR_FAST_IR");
}
foreach $file (@headers) {
open(IN, "< $file") || die "Error: Couldn't open $file for input\n";
if ($debug) {
print stderr "Working on $file\n";
print OUT "\n/* from $file ----------------------------- */\n";
}
$output_routine = 0;
$output_directly = 0;
$output_verbatim = 0;
$did_output_something = 0;
$prev_define = 0;
$in_define = 0;
while (<IN>) {
# support mcxtx.h include. can generalize if necessary.
if ($_ =~ /#include "mcxtx.h"/) {
open(INC, "< $core/lib/mcxtx.h") || die "Error: couldn't open mcxtx.h\n";
my $start_inc = 0;
while (<INC>) {
# skip copyright and first comment
if (/START INCLUDE/) {
$start_inc = 1;
next;
}
next unless ($start_inc);
&process_header_line($_);
}
close(INC);
} else {
&process_header_line($_);
}
}
if ($did_output_something && !$filter) {
print OUT "\n";
}
close(IN);
}
if ($header) {
foreach $fname (keys(%files)) {
# FIXME: should store original filehandle, and close it here
open(OUT, ">> $dir/$files{$fname}") ||
die "Error: Couldn't open $dir/$files{$fname} for append\n";
print OUT "\n#endif /* $wrapdef{$fname} */\n";
close(OUT);
}
if (!defined($defines{"HOT_PATCHING_INTERFACE"})) {
# Need to add appropriate ifdefs for the core to get this auto-removed.
# For now this is the easiest method.
unlink("$dir/dr_probe.h");
}
} else {
if ($filter) {
if (defined($defines{DR_APP_EXPORTS})) {
# now add in the APP_API routines -- ok to list symbol not found,
# so we need no hacks to remove the LOGPC ones based on the defines:
$app = `grep 'DR_APP_API.*;' ./lib/dr_app.h`;
$app =~ s/DR_APP_API [a-z]+ ([A-Za-z_]+)\(void\);/\1;/g;
print OUT $app;
}
# dynamorio_app_init and dynamorio_app_take_over are used
# by the linux injector
print OUT "dynamorio_app_init;\n";
print OUT "dynamorio_app_take_over;\n";
# add in our_std*, which are hard to match w/ DR_API and I'm in a hurry
# (should switch to visibility attributes now that we upgraded gcc)
print OUT "our_stdout;\n";
print OUT "our_stderr;\n";
# linker version script w/ no versions, just global and local symbol lists
print OUT "\nlocal: *;\n};\n";
}
close(OUT);
}
###########################################################################
sub process_header_line($)
{
my ($l) = @_;
chop $l;
# handle DOS end-of-line:
if ($l =~ /\r$/) { chop; };
if ($output_routine || $output_directly || $output_verbatim) {
# Enforce the rename to DR_REG_ and DR_SEG_
if (($l =~ /[^_]REG_/ || $l =~ /[^_]SEG_/) &&
# We have certain exceptions
($l !~ /^# *define [RS]EG_/ &&
$l !~ /DR_REG_ENUM_COMPATIBILITY/ &&
$l !~ /REG_SPECIFIER_BITS/ &&
$l !~ /REG_kind/ &&
$l !~ /conflict/ &&
$l !~ /compatibility/ &&
$l !~ /weird errors/)) {
die "Error: update to DR_{REG,SEG} constants:\n$l\n";
}
}
if ($l =~ /^DR_API/) {
$output_routine = 1;
$did_output_something = 1;
$type = "";
} elsif ($header && $l =~ /DR_API EXPORT TOFILE ([A-Za-z_0-9\.]+)/) {
$outfile_init = 1;
$outfile = $1;
if (!defined($files{$outfile})) {
$files{$outfile} = $outfile;
$wrapdef{$outfile} = $outfile;
$wrapdef{$outfile} =~ s/\./_/g;
$wrapdef{$outfile} = "_" . uc($wrapdef{$outfile}) . "_";
if (! -e "$dir/$outfile") {
open(OUT, "> $dir/$outfile") ||
die "Error: Couldn't open $dir/$outfile for output\n";
print OUT "$copyright";
print OUT "#ifndef $wrapdef{$outfile}\n#define $wrapdef{$outfile} 1\n\n";
} else {
open(OUT, ">> $dir/$outfile") ||
die "Error: Couldn't open $dir/$outfile for append\n";
}
} else {
# FIXME: should store original filehandle and re-use it
open(OUT, ">> $dir/$outfile") ||
die "Error: Couldn't open $dir/$outfile for append\n";
}
} elsif ($header && $l =~ /DR_API EXPORT BEGIN/) {
$output_directly = 1;
$did_output_something = 1;
} elsif ($header && $l =~ /DR_API EXPORT VERBATIM/) {
$output_verbatim = 1;
$did_output_something = 1;
}
# if outputting verbatim, just output and grab the next line
# we can skip the following logic that interprets the #ifdefs,
# #ifndefs, etc.
elsif ($output_verbatim) {
if ($l =~ /DR_API EXPORT END/) {
$output_verbatim = 0;
}
else {
print OUT "$l\n";
}
next;
}
# to handle defines (yes a hack...need a better solution):
# only a define on immediately prior line to DR_API or to
# EXPORT BEGIN will kill following output, until the very next
# endif
# assumption: only those defines passed to us count,
# and they're only used in simple "#ifdef" statements
# (can't use cpp b/c it also removes #if's around entire .h file,
# plus we want to keep #ifdef UNIX and #ifdef WINDOWS stuff)
if ($prev_define && ($output_routine || $output_directly)) {
$in_undefined = 1;
if ($debug) {
print stderr "\tSkipping!\n";
}
}
if ($in_undefined) {
if ($l =~ /^\#\s*endif/ || $l =~ /^\#\s*else/) {
$in_undefined = 0;
if ($l =~ /^\#\s*else/) {
$kill_endif = 1;
}
}
# assumption: nothing else on endif line
$skip = 1;
# have to check for closings here
if ($output_routine && $l =~ /;/) {
$output_routine = 0;
}
if ($output_directly && $l =~ /DR_API EXPORT END/) {
$output_directly = 0;
}
} else {
$skip = 0;
}
if ($kill_endif && $l =~ /^\#\s*endif/) {
$kill_endif = 0;
$skip = 1;
}
if ($in_define || $in_define_keep) {
if ($l =~ /^\#\s*endif/) {
# we only support keep-defines being innermost
if ($in_define_keep) {
$in_define_keep = 0;
} else {
$in_define = 0;
$skip = 1;
}
}
}
if ($l =~ /^\#\s*ifdef (\S+)/ || $l =~ /^\#\s*if defined\((\S+)\)/) {
# we want to keep all WINDOWS, UNIX, and X64 defines, so ignore those.
if (&keep_define($1)) {
$in_define_keep = 1;
} else {
if ($debug) {
print stderr "Found ifdef $1 => $defines{$1}\n";
}
if (!defined($defines{$1})) {
$prev_define = 1;
} elsif ($output_routine || $output_directly) {
$in_define = 1;
}
$skip = 1;
}
} elsif ($l =~ /^\#\s*ifndef (\S+)/) {
# we want to keep all WINDOWS, UNIX, and X64 defines, so ignore those.
if (&keep_define($1)) {
$in_define_keep = 1;
} else {
if ($debug) {
print stderr "Found ifndef $1 => $defines{$1}\n";
}
if (defined($defines{$1})) {
$prev_define = 1;
} elsif ($output_routine || $output_directly) {
$in_define = 1;
}
$skip = 1;
}
} else {
$prev_define = 0;
}
if ($skip) {
next;
}
if ($output_routine) {
if ($filter) {
# only export these guys for DYNAMORIO_IR_EXPORTS
if (defined($defines{DYNAMORIO_IR_EXPORTS})) {
# symbol export list (-filter option)
if ($l =~ /^([A-Za-z0-9_]+)\s*\(/ ||
# order is important: 2nd line might pick up _IF_X64
# inside param list
$l =~ /^[a-zA-Z_].*\s+([A-Za-z0-9_]+)\s*\(/) {
print OUT "$1;\n";
}
}
if ($l =~ /;\s*$/ &&
$l !~ /^\s*\*/ && $l !~ /^\s*\/\*/) { # ignore ; inside comment
$output_routine = 0;
}
} else {
if ($header && !$outfile_init) {
die "Error: no initial output file specified\n";
}
unless ($debug) {
$l =~ s/^DR_API\s*//;
}
if ($type eq "") {
if ($l =~ /^\s*[A-Za-z_]+/) {
$l =~ /^\s*([A-Za-z0-9_]+\s*\*?)/;
$type = $1;
$type =~ s/\s*$//;
}
}
$l =~ s/dcontext_t *\*dcontext/void *drcontext/;
if ($l =~ /\);\s*$/) {
$output_routine = 0;
if ($debug || $header) {
print OUT "$l\n";
} else {
$l =~ s/;//;
print OUT "$l\n";
if ($type =~ /\*/) {
$ret = "\treturn NULL;\n";
} elsif ($type eq "void") {
$ret = "";
} elsif ($type eq "bool") {
$ret = "\treturn false;\n";
} elsif ($type eq "float") {
$ret = "\treturn 0.f;\n";
} elsif ($type eq "int" || $type eq "uint") {
$ret = "\treturn 0;\n";
} else {
# static to avoid "uninitialized var" compiler warnings
$ret = "\tstatic $type bogus;\n\treturn bogus;\n";
}
print OUT "{\n$ret}\n";
}
} else {
print OUT "$l\n";
}
}
} elsif ($output_directly) {
# instr_create.h needs this in its raw output for x64, but keep var name
$l =~ s/dcontext_t *\*dcontext/void *dcontext/;
# let's keep blank lines if we didn't create them by stripping
# everything out
if ($l =~ /^\s*$/) {
print OUT "\n";
}
elsif ($l !~ /DR_API/) {
if ($l =~ /OP_/) {
# remove pointers into decode tables
$l =~ s/(OP_[a-zA-Z0-9_]*,) *\/\*[^\*]*\*\/(.*)/\1\2/;
}
# PR 227381: auto-insert doxygen comments for DR_REG_ enum lines w/o any
if ($file =~ "/opnd.h" &&
$l =~ /^ *DR_[RS]EG_/ && $l !~ /\/\*\*</) {
$l =~ s|(DR_[RS]EG_)(\w+), *|\1\2, /**< The "\L\2" register. */\n |g;
}
# Strip out FIXME comments
$l =~ s/\/\* FIXME.*\*\///;
if ($l !~ /^\s*$/) {
print OUT "$l\n";
}
}
if ($l =~ /DR_API EXPORT END/) {
$output_directly = 0;
print OUT "\n";
}
}
}