mirror of
https://github.com/DynamoRIO/dynamorio.git
synced 2026-10-12 02:02:04 +00:00
Moves all of the IR-related files (instruction generation, encoding, decoding, disassembly, instructions, operands, instruction lists) from core/arch to core/ir, mirroring the arch-specific subdirectories under core/ir. This is a code cleanup step toward properly isolating the drdecode library, as well as moving us toward the ability to build for a separate target architecture from the host architecture and eventually perhaps building in multiple target architectures in the same binary for decoding and IR manipulation. Also renames mangle_utils.c to ir_utils.c to better fit its purpose and location. Issue: #1684
324 lines
12 KiB
Perl
Executable File
324 lines
12 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
# **********************************************************
|
|
# Copyright (c) 2014-2020 Google, 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 Google, 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.
|
|
|
|
# Pass this the file with the op_instr[] encoding chain starting points
|
|
# array along with headers containing the table shortcuts and the files
|
|
# containing the decoding table files to chain.
|
|
# If -o OP_<opc> is passed it will only consider that opcode; else it will
|
|
# process all opcodes.
|
|
# It will construct each encoding chain and in-place edit the table files.
|
|
# It has assumptions on the precise format of the decoding table
|
|
# and of the op_instr* starting point array.
|
|
#
|
|
# To run on one A32 opcode:
|
|
#
|
|
# tools/arm_table_chain.pl -v -o <opc> core/ir/arm/table_{private,encode,a32}*.[ch]
|
|
#
|
|
# To run on all A32 opcodes:
|
|
#
|
|
# tools/arm_table_chain.pl core/ir/arm/table_{private,encode,a32}*.[ch]
|
|
#
|
|
# To run on one T32 opcode:
|
|
#
|
|
# tools/arm_table_chain.pl -v -o <opc> core/ir/arm/table_{private,encode,t32}*.[ch]
|
|
#
|
|
# To run on all T32 opcodes for outside of IT blocks:
|
|
#
|
|
# tools/arm_table_chain.pl core/ir/arm/table_{private,encode,t32}*.[ch]
|
|
#
|
|
# To run on all T32 opcodes for inside of IT blocks:
|
|
#
|
|
# tools/arm_table_chain.pl -it core/ir/arm/table_{private,encode,t32}*.[ch]
|
|
#
|
|
# Assuming the .n format are always first and thus always the entries,
|
|
# we can run arm_table_chain.pl twice for inside and outside of IT blocks
|
|
# with T32 tables without affecting the in-table chaining.
|
|
|
|
|
|
my $verbose = 0;
|
|
|
|
die "Usage: $0 [-o OP_<opcode>] [-it] <table-files>\n" if ($#ARGV < 1);
|
|
my $single_op = '';
|
|
while ($ARGV[0] eq '-v') {
|
|
shift;
|
|
$verbose++;
|
|
}
|
|
if ($ARGV[0] eq '-o') {
|
|
shift;
|
|
$single_op = shift;
|
|
}
|
|
if ($ARGV[0] eq '-it') {
|
|
$it_block = 1;
|
|
shift;
|
|
}
|
|
my @allfiles = @ARGV;
|
|
my $table = "";
|
|
my $shape = "";
|
|
my $major = 0;
|
|
my $minor = 0;
|
|
my $instance = 0;
|
|
my $t32 = 0;
|
|
my @infiles;
|
|
|
|
# Remove table_t32_16.c or table_t32_16_it.c
|
|
foreach $infile (@allfiles) {
|
|
if ($it_block) {
|
|
push(@infiles, $infile) if ($infile !~/t32_16\.c/);
|
|
} else {
|
|
push(@infiles, $infile) if ($infile !~/t32_16_it\.c/);
|
|
}
|
|
}
|
|
|
|
# Must process headers first
|
|
@infiles = sort({return -1 if($a =~ /\.h$/);return 1 if($b =~ /\.h$/); return 0;}
|
|
@infiles);
|
|
|
|
foreach $infile (@infiles) {
|
|
print "Processing $infile\n" if ($verbose > 0);
|
|
$t32 = 1 if ($infile =~ /_t32_/);
|
|
|
|
open(INFILE, "< $infile") || die "Couldn't open $file\n";
|
|
while (<INFILE>) {
|
|
print "xxx $_\n" if ($verbose > 2);
|
|
if (/^#define (\w+)\s+\(ptr_int_t\)&(\w+)/) {
|
|
$shorthand{$2} = $1;
|
|
print "shorthand for $2 = $1\n" if ($verbose > 1);
|
|
}
|
|
if (/^const instr_info_t (\w+)([^=]+)=/) {
|
|
$table = $1;
|
|
$shape = $2;
|
|
$major = -1;
|
|
$minor = 0;
|
|
}
|
|
if (/{\s*\/\*\s*(\d+)\s*\*\/\s*$/) {
|
|
$major++;
|
|
$minor = 0;
|
|
}
|
|
if (/^\s*{(OP_\w+)[ ,]/ && ($single_op eq '' || $single_op eq $1) &&
|
|
$1 ne 'OP_CONTD') {
|
|
my $opc = $1;
|
|
$instance{$opc} = 0 if (!defined($instance{$opc}));
|
|
|
|
# Ignore duplicate encodings
|
|
my $is_new = 1;
|
|
my $instance_next = $instance{$opc} + 1;
|
|
my $encoding = extract_encoding($_);
|
|
my $hex = extract_hex($_);
|
|
for (my $i = 0; $i < @{$entry{$opc}}; $i++) {
|
|
if ($encoding eq $entry{$opc}[$i]{'encoding'}) {
|
|
$is_new = 0;
|
|
$dup{$opc}{$encoding} = 1;
|
|
# We must place the entry with the fewest required bits into the
|
|
# encoding chain, to allow for entries to set distinguishing
|
|
# immed and other varying bits on duplicate entries (e.g., b vs f
|
|
# for the SIMD D bit) for easier decode table reading.
|
|
if (hex($hex) < hex($hex{$opc}{$encoding})) {
|
|
$hex{$opc}{$encoding} = $hex;
|
|
# Replace the stored instance with this one
|
|
$is_new = 1;
|
|
$instance_next = $instance{$opc} + 1;
|
|
$instance{$opc} = $i;
|
|
}
|
|
print "Duplicate $hex $encoding => $hex{$opc}{$encoding}\n"
|
|
if ($verbose > 1);
|
|
last;
|
|
}
|
|
}
|
|
goto dup_line if (!$is_new);
|
|
|
|
$entry{$opc}[$instance{$opc}]{'line'} = $_;
|
|
$entry{$opc}[$instance{$opc}]{'encoding'} = $encoding;
|
|
$entry{$opc}[$instance{$opc}]{'hex'} = $hex;
|
|
$hex{$opc}{$encoding} = $hex;
|
|
die "Error: no shorthand for $table\n" if ($shorthand{$table} eq '');
|
|
if ($shape =~ /\d/) {
|
|
$entry{$opc}[$instance{$opc}]{'addr_short'} =
|
|
sprintf "$shorthand{$table}\[$major][0x%02x]", $minor;
|
|
$entry{$opc}[$instance{$opc}]{'addr_long'} =
|
|
sprintf "$table\[$major][0x%02x]", $minor;
|
|
} else {
|
|
$entry{$opc}[$instance{$opc}]{'addr_short'} =
|
|
sprintf "$shorthand{$table}\[0x%02x]", $minor;
|
|
$entry{$opc}[$instance{$opc}]{'addr_long'} =
|
|
sprintf "$table\[0x%02x]", $minor;
|
|
}
|
|
# Order for sorting:
|
|
# + Prefer no-shift over shift
|
|
# + Prefer shift via immed over shift via reg
|
|
# + Prefer P=1 and U=1
|
|
# + Prefer 8-byte over 16-byte and over 4-byte
|
|
my $priority = $instance{$opc};
|
|
$priority-=10 if (/sh2, i/);
|
|
$priority-=10 if (/sh2, R/);
|
|
$priority-=10 if (/xop_shift/);
|
|
$priority+=10 if (/, [in]/);
|
|
$priority+=10 if (/, M.\d/);
|
|
$priority-=10 if (/, M.S/);
|
|
$priority+=50 if (/PUW=1../);
|
|
$priority+=100 if (/PUW=1.0/);
|
|
$priority+=10 if (/PUW=.1./);
|
|
$priority+=10 if (/[VW][ABC]q,/);
|
|
$priority-=50 if (/[VW][ABC]d,/);
|
|
# exop must be final member of chain
|
|
$priority-=1000 if (/exop\[\w+\]},/);
|
|
# + Prefer 16-bit Thumb code over 32-bit Thumb code
|
|
$priority+=500 if (/0x([\da-f]){4},/);
|
|
$priority+=10 if (/\bSPw\b|\bPCw\b/);
|
|
$priority+=10 if (/\bMSPP8w\b|\bMPCP8w\b/);
|
|
$priority+=10 if (/\bMSPDBl\b|\bMSPl\b/);
|
|
# + We have to take PC special-case first, b/c in some cases
|
|
# it has a larger immed which conflicts w/ other bits in non-PC
|
|
# (e.g., T32 OP_ldr with MPCN12w opnd). I don't think this
|
|
# generalization of that one known case as any negatives.
|
|
$priority+=200 if ($table =~ /PC$/);
|
|
|
|
$entry{$opc}[$instance{$opc}]{'priority'} = $priority;
|
|
if ($verbose > 0) {
|
|
my $tmp = $entry{$opc}[$instance{$opc}]{'addr_long'};
|
|
print "$priority $tmp $_";
|
|
}
|
|
$instance{$opc} = $instance_next;
|
|
}
|
|
dup_line:
|
|
$minor++ if (/^\s*{[A-Z]/);
|
|
}
|
|
close(INFILE);
|
|
}
|
|
|
|
foreach my $opc (keys %entry) {
|
|
@{$entry{$opc}} = sort({$b->{'priority'} <=> $a->{'priority'}} @{$entry{$opc}});
|
|
if ($verbose > 1) {
|
|
print "Sorted:\n";
|
|
for (my $i = 0; $i < @{$entry{$opc}}; $i++) {
|
|
my $tmp = $entry{$opc}[$i]{'addr_long'};
|
|
my $pri = $entry{$opc}[$i]{'priority'};
|
|
print "$pri $tmp\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
# Now edit the files in place
|
|
$^I = '.bak';
|
|
foreach $infile (@infiles) {
|
|
@ARGV = ($infile);
|
|
while (<>) {
|
|
my $encoding = extract_encoding($_);
|
|
my $hex = extract_hex($_);
|
|
my $handled = 0;
|
|
if (/^\s+\/\* (OP_\w+)[ ,]/ && ($single_op eq '' || $single_op eq $1)) {
|
|
my $opc = $1;
|
|
if (defined($entry{$opc}[0]{'addr_long'})) {
|
|
my $start = $entry{$opc}[0]{'addr_long'};
|
|
if ($t32) {
|
|
if ($it_block) {
|
|
# Assuming the .n format are always first and thus always
|
|
# the entries, we can implement here for IT block and
|
|
# not inside the in-table chaining.
|
|
if ($opc !~ /OP_cps/ && $opc !~ /OP_setend$/) {
|
|
# OP_cps* and OP_setend are not permitted in IT block.
|
|
# We exclude OP_cps* and OP_setend to avoid creating
|
|
# separate table for T32.32 IT block instructions.
|
|
s/{(&.*,\s*&.*,\s*)&.*}/{\1&$start}/;
|
|
}
|
|
} else {
|
|
s/{(&.*,\s*)&.*(,\s*&.*)}/{\1&$start\2}/;
|
|
}
|
|
} else {
|
|
s/{&.*,(\s*&.*\s*&.*)}/{&$start,\1}/;
|
|
}
|
|
}
|
|
}
|
|
if (/^\s*{(OP_\w+)[ ,]/ && ($single_op eq '' || $single_op eq $1) &&
|
|
$1 ne 'OP_CONTD') {
|
|
my $opc = $1;
|
|
if (defined($dup{$opc}{$encoding})) {
|
|
# Keep the one with the fewest bits, to allow for optional bits
|
|
# (e.g., immeds or reg bits) to be set to make the tables easier to
|
|
# read.
|
|
# For duplicate hex values, take 1st one.
|
|
if ($hex eq $hex{$opc}{$encoding} &&
|
|
!defined($printed_dup{$opc}{$encoding})) {
|
|
$printed_dup{$opc}{$encoding} = 1;
|
|
if (@{$entry{$opc}} == 1) {
|
|
s/, *[\w\[\]_]+},/, END_LIST},/ unless /exop\[\w+\]},/;
|
|
$handled = 1;
|
|
}
|
|
} else {
|
|
s/, *[\w\[\]_]+},/, DUP_ENTRY},/ unless /exop\[\w+\]},/;
|
|
$handled = 1;
|
|
}
|
|
}
|
|
if (!$handled) {
|
|
for (my $i = 0; $i < @{$entry{$opc}}; $i++) {
|
|
if ($_ eq $entry{$opc}[$i]{'line'}) {
|
|
if ($i == @{$entry{$opc}} - 1) {
|
|
s/, *[\w\[\]_]+},/, END_LIST},/ unless /exop\[\w+\]},/;
|
|
} else {
|
|
if (/exop\[\w+\]},/) {
|
|
print STDERR
|
|
"ERROR: exop must be final element in chain: $_\n";
|
|
} else {
|
|
my $chain = $entry{$opc}[$i+1]{'addr_short'};
|
|
s/, *[\w\[\]_]+},/, $chain},/;
|
|
}
|
|
}
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
print;
|
|
}
|
|
}
|
|
|
|
sub extract_encoding($)
|
|
{
|
|
my ($line) = @_;
|
|
|
|
# Remove up through opcode name (to remove encoding hexes) and
|
|
# final field and comments
|
|
$line =~ s/^[^"]+"/"/;
|
|
$line =~ s/,[^,]+}.*$//;
|
|
$line =~ s/\s*$//;
|
|
return $line;
|
|
}
|
|
|
|
sub extract_hex($)
|
|
{
|
|
my ($line) = @_;
|
|
if ($line =~ /{OP_\w+,\s*0x([0-9a-fA-F]+),/) {
|
|
return $1;
|
|
} else {
|
|
return "not found";
|
|
}
|
|
}
|