Skip to content

Commit

Permalink
i#1569 AArch64: Add dis-a64 pre-commit test.
Browse files Browse the repository at this point in the history
In dis-a64.txt there is a list of encodings and corresponding disassemblies.
This is checked by dis-a64.c, while dis-a64.pl is a script to facilitate
generating dis-a64.txt.

Review-URL: https://codereview.appspot.com/300540043
  • Loading branch information
egrimley-arm committed Jul 8, 2016
1 parent 9edd275 commit 3e3bba6
Show file tree
Hide file tree
Showing 6 changed files with 488 additions and 3 deletions.
3 changes: 1 addition & 2 deletions core/arch/aarch64/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ decode(dcontext_t *dcontext, byte *pc, instr_t *instr)
byte *
decode_from_copy(dcontext_t *dcontext, byte *copy_pc, byte *orig_pc, instr_t *instr)
{
ASSERT_NOT_IMPLEMENTED(false); /* FIXME i#1569 */
return NULL;
return decode_common(dcontext, copy_pc, orig_pc, instr);
}

byte *
Expand Down
7 changes: 6 additions & 1 deletion suite/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# **********************************************************
# Copyright (c) 2010-2016 Google, Inc. All rights reserved.
# Copyright (c) 2009-2010 VMware, Inc. All rights reserved.
# Copyright (c) 2016 ARM Limited. All rights reserved.
# **********************************************************

# Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -2172,7 +2173,11 @@ if (CLIENT_INTERFACE)
"${CMAKE_CURRENT_SOURCE_DIR}/api/dis-armT32-randtest.raw;-thumb" OFF)
set(api.disT32_expectbase "dis-armT32")
endif ()
else (ARM)
elseif (AARCH64)
add_api_exe(api.dis-a64 api/dis-a64.c OFF)
torunonly_api(api.dis-a64 api.dis-a64 api/dis-a64.c ""
"-q;${CMAKE_CURRENT_SOURCE_DIR}/api/dis-a64.txt" OFF)
elseif (X86)
tobuild_api(api.dis api/dis.c "-syntax_intel"
"${CMAKE_CURRENT_SOURCE_DIR}/api/dis-udis86-randtest.raw" OFF)
if (X64)
Expand Down
221 changes: 221 additions & 0 deletions suite/tests/api/dis-a64.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/* **********************************************************
* Copyright (c) 2016 ARM Limited. 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 ARM Limited 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 ARM LIMITED 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.
*/

/* Test the disassembler, decoder and encoder. */

#include "configure.h"
#include "dr_api.h"
#include <stdlib.h>
#include <string.h>

/* An arbitrary PC for more readable disassembly of PC-relative operands. */
#define ORIG_PC ((byte *)0x10000000)

bool
map_file(const char *file_name, byte **map_base, size_t *map_size)
{
file_t f;
uint64 file_size;
void *base;
size_t size;

f = dr_open_file(file_name, DR_FILE_READ);
if (f == INVALID_FILE)
return false;
if (!dr_file_size(f, &file_size))
return false;
size = (size_t)file_size;
base = dr_map_file(f, &size, 0, NULL, DR_MEMPROT_READ, DR_MAP_PRIVATE);
if (base == NULL || size < file_size)
return false;
*map_base = base;
*map_size = size;
return true;
}

void
check_inst(void *dc, uint enc, const char *dis, size_t len, bool verbose, bool *failed)
{
size_t buflen = (len < 100 ? 100 : len) + 2;
char *buf = malloc(buflen);
char *end;
instr_t instr;
byte *pc2;
uint enc2;

if (verbose) {
dr_printf("> ");
dr_write_file(STDOUT, dis, len);
dr_printf("\n");
}

/* Test disassembler. */
disassemble_to_buffer(dc, (byte *)&enc, ORIG_PC, false, false,
buf, buflen, NULL);
end = buf + strlen(buf);
if (end > buf && *(end - 1) == '\n')
--end;
if (end - buf != len || memcmp(buf, dis, len) != 0) {
if (verbose)
dr_printf("\n");
dr_printf("Error: Disassembly differs:\n%08x ", enc);
dr_write_file(STDOUT, dis, len);
dr_printf(" .\n ");
dr_write_file(STDOUT, buf, end - buf);
dr_printf(" .\n\n");
*failed = true;
}

/* Test decode and reencode. */
instr_init(dc, &instr);
decode_from_copy(dc, (byte *)&enc, ORIG_PC, &instr);
pc2 = instr_encode_to_copy(dc, &instr, (byte *)&enc2, ORIG_PC);
if (pc2 != (byte *)&enc2 + 4 || enc2 != enc) {
if (verbose)
dr_printf("\n");
dr_printf("Error: Reencoding differs:\n%08x ", enc);
dr_write_file(STDOUT, dis, len);
dr_printf("\n%08x ", enc2);
disassemble_from_copy(dc, (byte *)&enc2, ORIG_PC, STDOUT, false, false);
dr_printf("\n");
*failed = true;
}
}

void
error(const char *line, size_t len, const char *s)
{
dr_printf("Error: %s\n", s);
dr_write_file(STDOUT, line, len);
dr_printf("\n");
exit(1);
}

void
do_line(void *dc, const char *line, size_t len, bool verbose, bool *failed)
{
const char *end = line + len;
const char *s = line;
uint enc = 0;
const char *t1, *t2;

while (s < end && strchr(" \t", *s) != NULL)
++s;
if (s >= end || *s == '#')
return; /* blank line or comment */
t1 = s;
while (s < end) {
const char *hex = "0123456789abcdef";
char *t = strchr(hex, *s);
if (t == NULL)
break;
enc = enc << 4 | (t - hex);
++s;
}
t2 = s;
while (s < end && strchr(" \t", *s) != NULL)
++s;
if (t1 == t2 || s >= end || *s != ':')
error(line, len, "First colon-separated field should contain lower-case hex.");
++s;
while (s < end && *s != ':')
++s;
if (s >= end)
error(line, len, "Third colons-separated field is missing.");
++s;
while (s < end && strchr(" \t", *s) != NULL)
++s;
check_inst(dc, enc, s, end - s, verbose, failed);
}

int
run_test(void *dc, const char *file, bool verbose)
{
bool failed = false;
byte *map_base;
size_t map_size;
byte *s, *end;

if (!map_file(file, &map_base, &map_size)) {
dr_printf("Failed to map file '%s'\n", file);
return 1;
}

s = map_base;
end = map_base + map_size;
while (s < end) {
byte *t = memchr(s, '\n', end - s);
do_line(dc, s, (t == NULL ? end : t) - s, verbose, &failed);
s = (t == NULL ? end : t + 1);
}

if (failed) {
dr_printf("FAIL\n");
return 1;
} else {
dr_printf("PASS\n");
return 0;
}
}

void
run_decode(void *dc, const char *encoding)
{
uint enc = strtol(encoding, NULL, 16);
disassemble_from_copy(dc, (byte *)&enc, ORIG_PC, STDOUT, false, false);
}

int
main(int argc, char *argv[])
{
void *dc = dr_standalone_init();

if (argc != 3 || (strcmp(argv[1], "-q") != 0 &&
strcmp(argv[1], "-v") != 0 &&
strcmp(argv[1], "-d") != 0)) {
dr_printf("Usage: %s [-q |- v] FILE\n", argv[0]);
dr_printf(" Or: %s -d NUMBER\n", argv[0]);
dr_printf("Test the disassembler, decoder and encoder on a set of test cases.\n"
"\n"
" -q FILE Run test quietly.\n"
" -v FILE Run test verbosely.\n"
" -d NUMBER Disassemble a single instruction.\n");
return 0;
}

if (strcmp(argv[1], "-d") == 0) {
run_decode(dc, argv[2]);
return 0;
}

return run_test(dc, argv[2], (strcmp(argv[1], "-v") == 0));
}
1 change: 1 addition & 0 deletions suite/tests/api/dis-a64.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PASS
121 changes: 121 additions & 0 deletions suite/tests/api/dis-a64.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/usr/bin/perl -w

# **********************************************************
# Copyright (c) 2016 ARM Limited. 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 ARM Limited 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 ARM LIMITED 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.

# Generate a new draft of "dis-a64.txt" from an existing "dis-a64.txt" and
# "codec.txt". (Use "/dev/null" if you do not already have those files.)
# Review the output carefully before relying on it.

use strict;

if ($#ARGV != 1) {
print "Usage: dis-a64.pl .../dis-a64.txt .../codec.txt\n";
exit 0;
}

my $dis_file = $ARGV[0];
my $codec_file = $ARGV[1];
my $asmfile = "tmp-dis-a64.s";
my $objfile = "tmp-dis-a64.exe";

my $out = "";
my %enc;

# Get encodings from old "dis-a64.txt".
{
open(my $fd, "<", $dis_file) || die;
foreach (<$fd>) {
if (/^[ \t]*(#.*)?$/) {
$out .= $_;
} else {
/^[ \t]*([0-9a-f]{8})[: \t]/ || die;
$enc{hex($1)} = 1;
}
}
}

# Get encoding from "codec.txt".
{
open(my $fd, "<", $codec_file) || die;
foreach (<$fd>) {
next if /^[ \t]*(#.*)?$/;
/^([0-9a-f]{8}) ([0-9a-f]{8})/ || die;
my $t1 = hex($1);
my $t2 = hex($2);
# Set every variable bit.
$enc{$t1 | $t2} = 1;
# Put a different value in each of the four register fields.
# 0x81041 == 0b01000_0_00100_00010_00001:
$enc{$t1 | ($t2 & 0x81041)} = 1;
}
}

foreach my $e (sort {$a <=> $b} keys %enc) {
$out .= sprintf("%08x : %-31s: %s\n", $e, &dis_objdump($e), &dis_dynamorio($e));
}

print $out;
exit;

sub dis_objdump {
my ($e) = @_;
my $fd;
open($fd, ">", $asmfile) || die;
print $fd " .align 28\n"; # This gets us to 0x10000000.
print $fd " .global _start\n";
print $fd "_start:\n";
print $fd sprintf(" .inst 0x%08x\n", $e);
system("gcc", $asmfile, "-o", $objfile, "-nostartfiles", "-nodefaultlibs") && die;
open($fd, "-|", "objdump", "-d", $objfile) || die;
my $hex = sprintf("%08x", $e);
if (join("", <$fd>) =~ /\n\s*10000000:\s+$hex\s+(.*)/) {
my $dis = $1;
$dis =~ s/\s+/ /g;
$dis =~ s/;.*//;
$dis =~ s/\/\/.*//;
$dis =~ s/<\S+> ?$//;
$dis =~ s/ $//;
if ($dis =~ / /) {
$dis = sprintf("%-6s %s", "$`", "$'");
}
return $dis;
}
return "";
}

sub dis_dynamorio {
my ($e) = @_;
my $hex = sprintf("%08x", $e);
my $dis =
`LD_LIBRARY_PATH=lib64/debug/:lib64/release suite/tests/bin/api.dis-a64 -d $hex`;
$dis =~ s/\n//;
return $dis;
}
Loading

0 comments on commit 3e3bba6

Please sign in to comment.