mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-10-29 18:07:27 +01:00
Gemify metasm
This commit is contained in:
parent
d766484b1f
commit
d372a6a16d
@ -7,6 +7,7 @@ PATH
|
|||||||
bcrypt
|
bcrypt
|
||||||
jsobfu (~> 0.2.0)
|
jsobfu (~> 0.2.0)
|
||||||
json
|
json
|
||||||
|
metasm (~> 1.0.2)
|
||||||
metasploit-concern (= 1.0.0)
|
metasploit-concern (= 1.0.0)
|
||||||
metasploit-model (= 1.0.0)
|
metasploit-model (= 1.0.0)
|
||||||
metasploit-payloads (= 1.0.8)
|
metasploit-payloads (= 1.0.8)
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
# Load a slightly tweaked METASM stub
|
|
||||||
require 'metasm/metasm'
|
|
@ -1,3 +0,0 @@
|
|||||||
71374080fcf5e7be3322ce56f062c29c984c577b sstic07
|
|
||||||
f3bcc93471bf9186ed62edc1bef90bbe6614a0a3 metasm-0.1-rc1
|
|
||||||
13bead20e76be749ecdb67096d9cb0847d69ad59 version 0.1
|
|
@ -1,11 +0,0 @@
|
|||||||
List of known bugs/missing features, in no particular order:
|
|
||||||
|
|
||||||
PPC cpu cannot parse/encode code
|
|
||||||
Disassembler is sloooow
|
|
||||||
The GTK UI is quite sluggish too
|
|
||||||
Disassembler backtracker does weird things
|
|
||||||
Mach-O encoder does not work (binaries won't load on OSX)
|
|
||||||
ELF encoder may need tweaks to handle OpenBSD
|
|
||||||
Ia32 compile_c misses many features (divisions, bitfields), and needs a register allocator
|
|
||||||
Asm parser does not handle well ; comments (eg "foo ; */* blargimdead") (c-style comments are parsed before asm-style, so multiline /* after ; is bad)
|
|
||||||
The BUGS file is incomplete
|
|
@ -1,17 +0,0 @@
|
|||||||
N: Yoann GUILLOT
|
|
||||||
E: yoann at ofjj.net
|
|
||||||
D: Lead developper
|
|
||||||
|
|
||||||
N: Julien TINNES
|
|
||||||
E: julien at cr0.org
|
|
||||||
D: Senior Product Manager
|
|
||||||
D: Ideas, bug hunting, Yoann-slapping
|
|
||||||
D: Metasploit integration
|
|
||||||
|
|
||||||
N: Arnaud CORNET
|
|
||||||
E: arnaud.cornet at gmail.com
|
|
||||||
D: Initial ELF support
|
|
||||||
|
|
||||||
N: Raphael RIGO
|
|
||||||
E: raphael at cr0.org
|
|
||||||
D: Initial MIPS support and misc stuff
|
|
@ -1,61 +0,0 @@
|
|||||||
Metasm is a pure ruby lib, and the core (metasm subdir) does not depend on any
|
|
||||||
ruby library (except the metasm/gui, which can use gtk2/qt).
|
|
||||||
|
|
||||||
So the install is quite simple:
|
|
||||||
|
|
||||||
|
|
||||||
LOCAL INSTALLATION:
|
|
||||||
|
|
||||||
Either modify your RUBYLIB environment variable to include the directory
|
|
||||||
containing metasm.rb
|
|
||||||
e.g.
|
|
||||||
under linux or cygwin:
|
|
||||||
if you downloaded metasm to $HOME/metasm, you should have a file named
|
|
||||||
$HOME/metasm/metasm.rb and need to add "export RUBYLIB=$RUBYLIB:~/metasm" to
|
|
||||||
~/.bash_profile
|
|
||||||
|
|
||||||
under windows:
|
|
||||||
Add the path to the directory containing metasm.rb to the environment.
|
|
||||||
This is accessible through: rightclick on 'my computer', tab 'advanced',
|
|
||||||
environment.
|
|
||||||
If a line RUBYLIB exists, add ':/path/to/metasmdir' at the end, otherwise
|
|
||||||
create a new variable 'RUBYLIB' with value '/path/to/metasmdir'
|
|
||||||
|
|
||||||
|
|
||||||
SYSTEMWIDE INSTALLATION:
|
|
||||||
|
|
||||||
You can also copy the metasm.rb and the metasm subdirectory (the one containing
|
|
||||||
ia32.rb and so on) under the ruby library path.
|
|
||||||
e.g. cp -a metasm.rb metasm /usr/local/lib/site_ruby/1.8 (linux)
|
|
||||||
select metasm.rb and metasm ; copy ; goto D:\apps\ruby18\lib\ruby\1.8\ ; paste
|
|
||||||
(replace D:\apps\ruby18 with the directory where you installed ruby) (win)
|
|
||||||
|
|
||||||
Note that metasm may not work well with rubygems, so disable it if you have the
|
|
||||||
choice.
|
|
||||||
|
|
||||||
For a global installation, if you intend to use the DynLdr class (eg to call
|
|
||||||
the native windows API), you should also run the metasm/dynldr.rb script on
|
|
||||||
install/update, which will generate an up-to-date dynldr.so binary ruby module.
|
|
||||||
|
|
||||||
|
|
||||||
TESTING:
|
|
||||||
|
|
||||||
Once done, open a new session and type "ruby -r metasm -e 'p Metasm::VERSION'"
|
|
||||||
at a commandline prompt, it should print a single line with a number in it.
|
|
||||||
|
|
||||||
|
|
||||||
GUI:
|
|
||||||
|
|
||||||
If you want to use the graphical interface (dbg/dasm), you need the ruby-gtk2 library.
|
|
||||||
Under linux, use your package manager to install ruby-gtk2
|
|
||||||
e.g. for debian/ubuntu, type sudo apt-get install libgtk2-ruby
|
|
||||||
|
|
||||||
Under windows, you'll need to install the GTK2 and the ruby bindings manually.
|
|
||||||
Please follow the instructions at:
|
|
||||||
http://ruby-gnome2.sourceforge.jp/hiki.cgi?Install+Guide+for+Windows
|
|
||||||
|
|
||||||
To test, try to launch the script metasm/samples/disassemble-gui.rb (double
|
|
||||||
click on it), it should show a program selection window. Type 'c' to start
|
|
||||||
disassembling somewhere, and space to change to graph mode (see the script source
|
|
||||||
for more key bindings)
|
|
||||||
|
|
@ -1,458 +0,0 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 2.1, February 1999
|
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
[This is the first released version of the Lesser GPL. It also counts
|
|
||||||
as the successor of the GNU Library Public License, version 2, hence
|
|
||||||
the version number 2.1.]
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
Licenses are intended to guarantee your freedom to share and change
|
|
||||||
free software--to make sure the software is free for all its users.
|
|
||||||
|
|
||||||
This license, the Lesser General Public License, applies to some
|
|
||||||
specially designated software packages--typically libraries--of the
|
|
||||||
Free Software Foundation and other authors who decide to use it. You
|
|
||||||
can use it too, but we suggest you first think carefully about whether
|
|
||||||
this license or the ordinary General Public License is the better
|
|
||||||
strategy to use in any particular case, based on the explanations below.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom of use,
|
|
||||||
not price. Our General Public Licenses are designed to make sure that
|
|
||||||
you have the freedom to distribute copies of free software (and charge
|
|
||||||
for this service if you wish); that you receive source code or can get
|
|
||||||
it if you want it; that you can change the software and use pieces of
|
|
||||||
it in new free programs; and that you are informed that you can do
|
|
||||||
these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
distributors to deny you these rights or to ask you to surrender these
|
|
||||||
rights. These restrictions translate to certain responsibilities for
|
|
||||||
you if you distribute copies of the library or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of the library, whether gratis
|
|
||||||
or for a fee, you must give the recipients all the rights that we gave
|
|
||||||
you. You must make sure that they, too, receive or can get the source
|
|
||||||
code. If you link other code with the library, you must provide
|
|
||||||
complete object files to the recipients, so that they can relink them
|
|
||||||
with the library after making changes to the library and recompiling
|
|
||||||
it. And you must show them these terms so they know their rights.
|
|
||||||
|
|
||||||
We protect your rights with a two-step method: (1) we copyright the
|
|
||||||
library, and (2) we offer you this license, which gives you legal
|
|
||||||
permission to copy, distribute and/or modify the library.
|
|
||||||
|
|
||||||
To protect each distributor, we want to make it very clear that
|
|
||||||
there is no warranty for the free library. Also, if the library is
|
|
||||||
modified by someone else and passed on, the recipients should know
|
|
||||||
that what they have is not the original version, so that the original
|
|
||||||
author's reputation will not be affected by problems that might be
|
|
||||||
introduced by others.
|
|
||||||
|
|
||||||
Finally, software patents pose a constant threat to the existence of
|
|
||||||
any free program. We wish to make sure that a company cannot
|
|
||||||
effectively restrict the users of a free program by obtaining a
|
|
||||||
restrictive license from a patent holder. Therefore, we insist that
|
|
||||||
any patent license obtained for a version of the library must be
|
|
||||||
consistent with the full freedom of use specified in this license.
|
|
||||||
|
|
||||||
Most GNU software, including some libraries, is covered by the
|
|
||||||
ordinary GNU General Public License. This license, the GNU Lesser
|
|
||||||
General Public License, applies to certain designated libraries, and
|
|
||||||
is quite different from the ordinary General Public License. We use
|
|
||||||
this license for certain libraries in order to permit linking those
|
|
||||||
libraries into non-free programs.
|
|
||||||
|
|
||||||
When a program is linked with a library, whether statically or using
|
|
||||||
a shared library, the combination of the two is legally speaking a
|
|
||||||
combined work, a derivative of the original library. The ordinary
|
|
||||||
General Public License therefore permits such linking only if the
|
|
||||||
entire combination fits its criteria of freedom. The Lesser General
|
|
||||||
Public License permits more lax criteria for linking other code with
|
|
||||||
the library.
|
|
||||||
|
|
||||||
We call this license the "Lesser" General Public License because it
|
|
||||||
does Less to protect the user's freedom than the ordinary General
|
|
||||||
Public License. It also provides other free software developers Less
|
|
||||||
of an advantage over competing non-free programs. These disadvantages
|
|
||||||
are the reason we use the ordinary General Public License for many
|
|
||||||
libraries. However, the Lesser license provides advantages in certain
|
|
||||||
special circumstances.
|
|
||||||
|
|
||||||
For example, on rare occasions, there may be a special need to
|
|
||||||
encourage the widest possible use of a certain library, so that it becomes
|
|
||||||
a de-facto standard. To achieve this, non-free programs must be
|
|
||||||
allowed to use the library. A more frequent case is that a free
|
|
||||||
library does the same job as widely used non-free libraries. In this
|
|
||||||
case, there is little to gain by limiting the free library to free
|
|
||||||
software only, so we use the Lesser General Public License.
|
|
||||||
|
|
||||||
In other cases, permission to use a particular library in non-free
|
|
||||||
programs enables a greater number of people to use a large body of
|
|
||||||
free software. For example, permission to use the GNU C Library in
|
|
||||||
non-free programs enables many more people to use the whole GNU
|
|
||||||
operating system, as well as its variant, the GNU/Linux operating
|
|
||||||
system.
|
|
||||||
|
|
||||||
Although the Lesser General Public License is Less protective of the
|
|
||||||
users' freedom, it does ensure that the user of a program that is
|
|
||||||
linked with the Library has the freedom and the wherewithal to run
|
|
||||||
that program using a modified version of the Library.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow. Pay close attention to the difference between a
|
|
||||||
"work based on the library" and a "work that uses the library". The
|
|
||||||
former contains code derived from the library, whereas the latter must
|
|
||||||
be combined with the library in order to run.
|
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
|
||||||
program which contains a notice placed by the copyright holder or
|
|
||||||
other authorized party saying it may be distributed under the terms of
|
|
||||||
this Lesser General Public License (also called "this License").
|
|
||||||
Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
A "library" means a collection of software functions and/or data
|
|
||||||
prepared so as to be conveniently linked with application programs
|
|
||||||
(which use some of those functions and data) to form executables.
|
|
||||||
|
|
||||||
The "Library", below, refers to any such software library or work
|
|
||||||
which has been distributed under these terms. A "work based on the
|
|
||||||
Library" means either the Library or any derivative work under
|
|
||||||
copyright law: that is to say, a work containing the Library or a
|
|
||||||
portion of it, either verbatim or with modifications and/or translated
|
|
||||||
straightforwardly into another language. (Hereinafter, translation is
|
|
||||||
included without limitation in the term "modification".)
|
|
||||||
|
|
||||||
"Source code" for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For a library, complete source code means
|
|
||||||
all the source code for all modules it contains, plus any associated
|
|
||||||
interface definition files, plus the scripts used to control compilation
|
|
||||||
and installation of the library.
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running a program using the Library is not restricted, and output from
|
|
||||||
such a program is covered only if its contents constitute a work based
|
|
||||||
on the Library (independent of the use of the Library in a tool for
|
|
||||||
writing it). Whether that is true depends on what the Library does
|
|
||||||
and what the program that uses the Library does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Library's
|
|
||||||
complete source code as you receive it, in any medium, provided that
|
|
||||||
you conspicuously and appropriately publish on each copy an
|
|
||||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
|
||||||
all the notices that refer to this License and to the absence of any
|
|
||||||
warranty; and distribute a copy of this License along with the
|
|
||||||
Library.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy,
|
|
||||||
and you may at your option offer warranty protection in exchange for a
|
|
||||||
fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Library or any portion
|
|
||||||
of it, thus forming a work based on the Library, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The modified work must itself be a software library.
|
|
||||||
|
|
||||||
b) You must cause the files modified to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
c) You must cause the whole of the work to be licensed at no
|
|
||||||
charge to all third parties under the terms of this License.
|
|
||||||
|
|
||||||
d) If a facility in the modified Library refers to a function or a
|
|
||||||
table of data to be supplied by an application program that uses
|
|
||||||
the facility, other than as an argument passed when the facility
|
|
||||||
is invoked, then you must make a good faith effort to ensure that,
|
|
||||||
in the event an application does not supply such function or
|
|
||||||
table, the facility still operates, and performs whatever part of
|
|
||||||
its purpose remains meaningful.
|
|
||||||
|
|
||||||
(For example, a function in a library to compute square roots has
|
|
||||||
a purpose that is entirely well-defined independent of the
|
|
||||||
application. Therefore, Subsection 2d requires that any
|
|
||||||
application-supplied function or table used by this function must
|
|
||||||
be optional: if the application does not supply it, the square
|
|
||||||
root function must still compute square roots.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Library,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Library, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote
|
|
||||||
it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Library.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Library
|
|
||||||
with the Library (or with a work based on the Library) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
|
||||||
License instead of this License to a given copy of the Library. To do
|
|
||||||
this, you must alter all the notices that refer to this License, so
|
|
||||||
that they refer to the ordinary GNU General Public License, version 2,
|
|
||||||
instead of to this License. (If a newer version than version 2 of the
|
|
||||||
ordinary GNU General Public License has appeared, then you can specify
|
|
||||||
that version instead if you wish.) Do not make any other change in
|
|
||||||
these notices.
|
|
||||||
|
|
||||||
Once this change is made in a given copy, it is irreversible for
|
|
||||||
that copy, so the ordinary GNU General Public License applies to all
|
|
||||||
subsequent copies and derivative works made from that copy.
|
|
||||||
|
|
||||||
This option is useful when you wish to copy part of the code of
|
|
||||||
the Library into a program that is not a library.
|
|
||||||
|
|
||||||
4. You may copy and distribute the Library (or a portion or
|
|
||||||
derivative of it, under Section 2) in object code or executable form
|
|
||||||
under the terms of Sections 1 and 2 above provided that you accompany
|
|
||||||
it with the complete corresponding machine-readable source code, which
|
|
||||||
must be distributed under the terms of Sections 1 and 2 above on a
|
|
||||||
medium customarily used for software interchange.
|
|
||||||
|
|
||||||
If distribution of object code is made by offering access to copy
|
|
||||||
from a designated place, then offering equivalent access to copy the
|
|
||||||
source code from the same place satisfies the requirement to
|
|
||||||
distribute the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
5. A program that contains no derivative of any portion of the
|
|
||||||
Library, but is designed to work with the Library by being compiled or
|
|
||||||
linked with it, is called a "work that uses the Library". Such a
|
|
||||||
work, in isolation, is not a derivative work of the Library, and
|
|
||||||
therefore falls outside the scope of this License.
|
|
||||||
|
|
||||||
However, linking a "work that uses the Library" with the Library
|
|
||||||
creates an executable that is a derivative of the Library (because it
|
|
||||||
contains portions of the Library), rather than a "work that uses the
|
|
||||||
library". The executable is therefore covered by this License.
|
|
||||||
Section 6 states terms for distribution of such executables.
|
|
||||||
|
|
||||||
When a "work that uses the Library" uses material from a header file
|
|
||||||
that is part of the Library, the object code for the work may be a
|
|
||||||
derivative work of the Library even though the source code is not.
|
|
||||||
Whether this is true is especially significant if the work can be
|
|
||||||
linked without the Library, or if the work is itself a library. The
|
|
||||||
threshold for this to be true is not precisely defined by law.
|
|
||||||
|
|
||||||
If such an object file uses only numerical parameters, data
|
|
||||||
structure layouts and accessors, and small macros and small inline
|
|
||||||
functions (ten lines or less in length), then the use of the object
|
|
||||||
file is unrestricted, regardless of whether it is legally a derivative
|
|
||||||
work. (Executables containing this object code plus portions of the
|
|
||||||
Library will still fall under Section 6.)
|
|
||||||
|
|
||||||
Otherwise, if the work is a derivative of the Library, you may
|
|
||||||
distribute the object code for the work under the terms of Section 6.
|
|
||||||
Any executables containing that work also fall under Section 6,
|
|
||||||
whether or not they are linked directly with the Library itself.
|
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also combine or
|
|
||||||
link a "work that uses the Library" with the Library to produce a
|
|
||||||
work containing portions of the Library, and distribute that work
|
|
||||||
under terms of your choice, provided that the terms permit
|
|
||||||
modification of the work for the customer's own use and reverse
|
|
||||||
engineering for debugging such modifications.
|
|
||||||
|
|
||||||
You must give prominent notice with each copy of the work that the
|
|
||||||
Library is used in it and that the Library and its use are covered by
|
|
||||||
this License. You must supply a copy of this License. If the work
|
|
||||||
during execution displays copyright notices, you must include the
|
|
||||||
copyright notice for the Library among them, as well as a reference
|
|
||||||
directing the user to the copy of this License. Also, you must do one
|
|
||||||
of these things:
|
|
||||||
|
|
||||||
a) Accompany the work with the complete corresponding
|
|
||||||
machine-readable source code for the Library including whatever
|
|
||||||
changes were used in the work (which must be distributed under
|
|
||||||
Sections 1 and 2 above); and, if the work is an executable linked
|
|
||||||
with the Library, with the complete machine-readable "work that
|
|
||||||
uses the Library", as object code and/or source code, so that the
|
|
||||||
user can modify the Library and then relink to produce a modified
|
|
||||||
executable containing the modified Library. (It is understood
|
|
||||||
that the user who changes the contents of definitions files in the
|
|
||||||
Library will not necessarily be able to recompile the application
|
|
||||||
to use the modified definitions.)
|
|
||||||
|
|
||||||
b) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (1) uses at run time a
|
|
||||||
copy of the library already present on the user's computer system,
|
|
||||||
rather than copying library functions into the executable, and (2)
|
|
||||||
will operate properly with a modified version of the library, if
|
|
||||||
the user installs one, as long as the modified version is
|
|
||||||
interface-compatible with the version that the work was made with.
|
|
||||||
|
|
||||||
c) Accompany the work with a written offer, valid for at
|
|
||||||
least three years, to give the same user the materials
|
|
||||||
specified in Subsection 6a, above, for a charge no more
|
|
||||||
than the cost of performing this distribution.
|
|
||||||
|
|
||||||
d) If distribution of the work is made by offering access to copy
|
|
||||||
from a designated place, offer equivalent access to copy the above
|
|
||||||
specified materials from the same place.
|
|
||||||
|
|
||||||
e) Verify that the user has already received a copy of these
|
|
||||||
materials or that you have already sent this user a copy.
|
|
||||||
|
|
||||||
For an executable, the required form of the "work that uses the
|
|
||||||
Library" must include any data and utility programs needed for
|
|
||||||
reproducing the executable from it. However, as a special exception,
|
|
||||||
the materials to be distributed need not include anything that is
|
|
||||||
normally distributed (in either source or binary form) with the major
|
|
||||||
components (compiler, kernel, and so on) of the operating system on
|
|
||||||
which the executable runs, unless that component itself accompanies
|
|
||||||
the executable.
|
|
||||||
|
|
||||||
It may happen that this requirement contradicts the license
|
|
||||||
restrictions of other proprietary libraries that do not normally
|
|
||||||
accompany the operating system. Such a contradiction means you cannot
|
|
||||||
use both them and the Library together in an executable that you
|
|
||||||
distribute.
|
|
||||||
|
|
||||||
7. You may place library facilities that are a work based on the
|
|
||||||
Library side-by-side in a single library together with other library
|
|
||||||
facilities not covered by this License, and distribute such a combined
|
|
||||||
library, provided that the separate distribution of the work based on
|
|
||||||
the Library and of the other library facilities is otherwise
|
|
||||||
permitted, and provided that you do these two things:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work
|
|
||||||
based on the Library, uncombined with any other library
|
|
||||||
facilities. This must be distributed under the terms of the
|
|
||||||
Sections above.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library of the fact
|
|
||||||
that part of it is a work based on the Library, and explaining
|
|
||||||
where to find the accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
8. You may not copy, modify, sublicense, link with, or distribute
|
|
||||||
the Library except as expressly provided under this License. Any
|
|
||||||
attempt otherwise to copy, modify, sublicense, link with, or
|
|
||||||
distribute the Library is void, and will automatically terminate your
|
|
||||||
rights under this License. However, parties who have received copies,
|
|
||||||
or rights, from you under this License will not have their licenses
|
|
||||||
terminated so long as such parties remain in full compliance.
|
|
||||||
|
|
||||||
9. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Library or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Library (or any work based on the
|
|
||||||
Library), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Library or works based on it.
|
|
||||||
|
|
||||||
10. Each time you redistribute the Library (or any work based on the
|
|
||||||
Library), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute, link with or modify the Library
|
|
||||||
subject to these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties with
|
|
||||||
this License.
|
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Library at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Library by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Library.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under any
|
|
||||||
particular circumstance, the balance of the section is intended to apply,
|
|
||||||
and the section as a whole is intended to apply in other circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
12. If the distribution and/or use of the Library is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Library under this License may add
|
|
||||||
an explicit geographical distribution limitation excluding those countries,
|
|
||||||
so that distribution is permitted only in or among countries not thus
|
|
||||||
excluded. In such case, this License incorporates the limitation as if
|
|
||||||
written in the body of this License.
|
|
||||||
|
|
||||||
13. The Free Software Foundation may publish revised and/or new
|
|
||||||
versions of the Lesser General Public License from time to time.
|
|
||||||
Such new versions will be similar in spirit to the present version,
|
|
||||||
but may differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Library
|
|
||||||
specifies a version number of this License which applies to it and
|
|
||||||
"any later version", you have the option of following the terms and
|
|
||||||
conditions either of that version or of any later version published by
|
|
||||||
the Free Software Foundation. If the Library does not specify a
|
|
||||||
license version number, you may choose any version ever published by
|
|
||||||
the Free Software Foundation.
|
|
||||||
|
|
||||||
14. If you wish to incorporate parts of the Library into other free
|
|
||||||
programs whose distribution conditions are incompatible with these,
|
|
||||||
write to the author to ask for permission. For software which is
|
|
||||||
copyrighted by the Free Software Foundation, write to the Free
|
|
||||||
Software Foundation; we sometimes make exceptions for this. Our
|
|
||||||
decision will be guided by the two goals of preserving the free status
|
|
||||||
of all derivatives of our free software and of promoting the sharing
|
|
||||||
and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
|
||||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
||||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
|
||||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
|
||||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
|
||||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
|
||||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
|
||||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
|
||||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
|
||||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
|
||||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
|
||||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
|
||||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
|
||||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
|
||||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
@ -1,278 +0,0 @@
|
|||||||
Metasm, the Ruby assembly manipulation suite
|
|
||||||
============================================
|
|
||||||
|
|
||||||
* sample scripts in samples/ -- read comments at the beginning of the files
|
|
||||||
* all files are licensed under the terms of the LGPL
|
|
||||||
|
|
||||||
Author: Yoann Guillot <john at ofjj.net>
|
|
||||||
|
|
||||||
|
|
||||||
Basic overview:
|
|
||||||
|
|
||||||
Metasm allows you to interact with executables formats (ExeFormat):
|
|
||||||
PE, ELF, Mach-O, Shellcode, etc.
|
|
||||||
There are three approaches to an ExeFormat:
|
|
||||||
- compiling one up, from scratch
|
|
||||||
- decompiling an existing format
|
|
||||||
- manipulating the file structure
|
|
||||||
|
|
||||||
|
|
||||||
Ready-to-use scripts can be found in the samples/ subdirectory, check the
|
|
||||||
comments in the scripts headers. You can also try the --help argument if
|
|
||||||
you're feeling lucky.
|
|
||||||
|
|
||||||
For more information, check the doc/ subdirectory. The text files can be
|
|
||||||
compiled to html using the misc/txt2html.rb script.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Here is a short overview of the Metasm internals.
|
|
||||||
|
|
||||||
|
|
||||||
Assembly:
|
|
||||||
|
|
||||||
When compiling, you start from a source text (ruby String, consisting
|
|
||||||
mostly in a sequence of instructions/data/padding directive), which is parsed.
|
|
||||||
|
|
||||||
The string is handed to a Preprocessor instance (which handles #if, #ifdef,
|
|
||||||
#include, #define, /* */ etc, should be 100% compatible with gcc -E), which is
|
|
||||||
encapsulated in an AsmPreprocessor for assembler sources (to handles asm macro
|
|
||||||
definitions, 'equ' and asm ';' comments).
|
|
||||||
The interface to do that is ExeFormat#parse(text[, filename, lineno]) or
|
|
||||||
ExeFormat.assemble (which calls .new, #parse and #assemble).
|
|
||||||
|
|
||||||
The (Asm)Preprocessor returns tokens to the ExeFormat, which parses them as Data,
|
|
||||||
Padding, Labels or parser directives. Parser directives always start with a dot.
|
|
||||||
They can be generic (.pad, .offset...) or ExeFormat-specific (.section,
|
|
||||||
.import, .entrypoint...). They are handled by #parse_parser_instruction().
|
|
||||||
If the ExeFormat does not recognize a word, it is handed to its CPU instance,
|
|
||||||
which is responsible for parsing Instructions (or raise an exception).
|
|
||||||
All those tokens are stored in one or more arrays in the @source attribute of
|
|
||||||
the ExeFormat (Shellcode's @source is an Array, for PE/ELF it is a hash
|
|
||||||
[section name] => [Array of parsed data])
|
|
||||||
Every immediate value can be an arbitrary Expression (see later).
|
|
||||||
|
|
||||||
You can then assemble the source to binary sections using ExeFormat#assemble.
|
|
||||||
|
|
||||||
Once the section binaries are available, the whole binary executable can be
|
|
||||||
written to disk using ExeFormat#encode_file(filename[, format]).
|
|
||||||
|
|
||||||
PE and ELF include an autoimport feature that allows automatic creation of
|
|
||||||
import-related data for known OS-specific functions (e.g. unresolved calls to
|
|
||||||
'strcpy' will generate data so that the binary is linked against the libc
|
|
||||||
library at runtime).
|
|
||||||
|
|
||||||
The samples/{exe,pe,elf}encode.rb can take an asm source file as argument
|
|
||||||
and compile it to a working executable.
|
|
||||||
|
|
||||||
The CPU classes are responsible for parsing and encoding individual
|
|
||||||
instructions. The current Ia32 parser uses the Intel syntax (e.g. mov eax, 42).
|
|
||||||
The generic parser recognizes labels as a string at the beginning of a line
|
|
||||||
followed by a colon (e.g. 'some_label:'). GCC-style local labels may be used
|
|
||||||
(e.g. '1:', refered to using '1b' (backward) or '1f' (forward) ; may be
|
|
||||||
redefined as many times as needed.)
|
|
||||||
Data are specified using 'db'-style notation (e.g. 'dd 42h', 'db "blabla", 0')
|
|
||||||
See samples/asmsyntax.rb
|
|
||||||
|
|
||||||
|
|
||||||
EncodedData:
|
|
||||||
|
|
||||||
In Metasm all binary data is stored as an EncodedData.
|
|
||||||
EncodedData has 3 main attributes:
|
|
||||||
- #data which holds the raw binary data (generally a ruby String, but see
|
|
||||||
VirtualString)
|
|
||||||
- #export which is a hash associating an export name (label name) to an offset
|
|
||||||
within #data
|
|
||||||
- #reloc which is a hash whose keys are offsets within #data, and whose values
|
|
||||||
are Relocation objects.
|
|
||||||
A Relocation object has an endianness (:little/:big), a type (:u32 for unsigned
|
|
||||||
32bits) and a target (the intended value stored here).
|
|
||||||
The target is an arbitrary arithmetic/logic Expression.
|
|
||||||
|
|
||||||
EncodedData also has a #virtsize (for e.g. .bss sections), and a #ptr (internal
|
|
||||||
offset used when decoding things)
|
|
||||||
|
|
||||||
You can fixup an EncodedData, with a Hash variable name => value (value should
|
|
||||||
be an Expression or a numeric value). When you do that, each relocation's target
|
|
||||||
is bound using the binding, and if the result is calculable (no external variable
|
|
||||||
name used in the Expression), the result is encoded using the relocation's
|
|
||||||
size/sign/endianness information. If it overflows (try to store 128 in an 8bit
|
|
||||||
signed relocation), an EncodeError exception is raised. Use the :a32 type to
|
|
||||||
allow silent overflow truncating.
|
|
||||||
If the relocation's target is not numeric, the target is unchanged if you use
|
|
||||||
EncodedData#fixup, or it is replaced with the bound target with #fixup! .
|
|
||||||
|
|
||||||
|
|
||||||
Disassembly:
|
|
||||||
|
|
||||||
This code is found in the metasm/decode.rb source file, which defines the
|
|
||||||
Disassembler class.
|
|
||||||
|
|
||||||
The disassembler needs a decoded ExeFormat (to be able to say what data is at
|
|
||||||
which virtual address) and an entrypoint (a virtual address or export name).
|
|
||||||
It can then start to disassemble instructions. When it encounters an
|
|
||||||
Opcode marked as :setip, it asks the CPU for the jump destination (an
|
|
||||||
Expression that may involve register values, for e.g. jmp eax), and backtraces
|
|
||||||
instructions until it finds the numeric value.
|
|
||||||
|
|
||||||
On decoding, the Disassembler maintains a #decoded hash associating addresses
|
|
||||||
(expressions/integer #normalize()d) to DecodedInstructions.
|
|
||||||
|
|
||||||
The disassembly generates an InstructionBlock graph. Each block holds a list of
|
|
||||||
DecodedInstruction, and pointers to the next/previous block (by address).
|
|
||||||
|
|
||||||
The disassembler also traces data accesses by instructions, and stores Xrefs
|
|
||||||
for them.
|
|
||||||
The backtrace parameters can be tweaked, and the maximum depth to consider
|
|
||||||
can be specifically changed for :r/:w backtraces (instruction memory xrefs)
|
|
||||||
using #backtrace_maxblocks_data.
|
|
||||||
When an Expression is backtracked, each walked block is marked so that loops
|
|
||||||
are detected, and so that if a new code path is found to an existing block,
|
|
||||||
backtraces can be resumed using this new path.
|
|
||||||
|
|
||||||
The disassembler makes very few assumptions, and in particular does not
|
|
||||||
suppose that functions will return ; they will only if the backtrace of the
|
|
||||||
'ret' instructions is conclusive. This is quite powerful, but also implies
|
|
||||||
that any error in the backtracking process can lead to a full stop ; and also
|
|
||||||
means that the disassembler is quite slow.
|
|
||||||
|
|
||||||
The special method #disassemble_fast can be used to work around this when the
|
|
||||||
code is known to be well-formed (ie it assumes that all calls returns)
|
|
||||||
|
|
||||||
When a subfunction is found, a special DecodedFunction is created, which holds
|
|
||||||
a summary of the function's effects (like a DecodedInstruction on steroids).
|
|
||||||
This allows the backtracker to 'step over' subfunctions, which greatly improves
|
|
||||||
speed. The DecodedFunctions may be callback-based, to allow a very dynamic
|
|
||||||
behaviour.
|
|
||||||
External function calls create dedicated DecodedFunctions, which holds some
|
|
||||||
API information (e.g. stack fixup information, basic parameter accesses...)
|
|
||||||
This information may be derived from a C header parsed beforehand.
|
|
||||||
If no C function prototype is available, a special 'default' entry is used,
|
|
||||||
which assumes that the function has a standard ABI.
|
|
||||||
|
|
||||||
Ia32 implements a specific :default entry, which handles automatic stack fixup
|
|
||||||
resolution, by assuming that the last 'call' instruction returns. This may lead
|
|
||||||
to unexpected results ; for maximum accuracy a C header holding information for
|
|
||||||
all external functions is recommanded (see samples/factorize-headers-peimports
|
|
||||||
for a script to generate such a header from a full Visual Studio installation
|
|
||||||
and the target binary).
|
|
||||||
|
|
||||||
Ia32 also implements a specific GetProcAddress/dlsym callback, that will
|
|
||||||
yield the correct return value if the parameters can be backtraced.
|
|
||||||
|
|
||||||
The scripts implementing a full disassembler are samples/disassemble{-gui}.rb
|
|
||||||
See the comments for the GUI key bindings.
|
|
||||||
|
|
||||||
|
|
||||||
ExeFormat manipulation:
|
|
||||||
|
|
||||||
You can encode/decode an ExeFormat (ie decode sections, imports, headers etc)
|
|
||||||
|
|
||||||
Constructor: ExeFormat.decode_file(str), ExeFormat.decode_file_header(str)
|
|
||||||
Methods: ExeFormat#encode_file(filename), ExeFormat#encode_string
|
|
||||||
|
|
||||||
PE and ELF files have a LoadedPE/LoadedELF counterpart, that are able to work
|
|
||||||
with memory-mmaped versions of those formats (e.g. to debug running
|
|
||||||
processes)
|
|
||||||
|
|
||||||
|
|
||||||
VirtualString:
|
|
||||||
|
|
||||||
A VirtualString is a String-like object: you can read and may rewrite slices of
|
|
||||||
it. It can be used as EncodedData#data, and thus allows virtualization
|
|
||||||
of most Metasm algorithms.
|
|
||||||
You cannot change a VirtualString length.
|
|
||||||
Taking a slice of a VirtualString will return either a String (for small sizes)
|
|
||||||
or another VirtualString (a 'window' into the other). You can force getting a
|
|
||||||
small VirtualString using the #dup(offset, length) method.
|
|
||||||
Any unimplemented method called on it is forwarded to a frozen String which is
|
|
||||||
a full copy of the VirtualString (should be avoided if possible, the underlying
|
|
||||||
string may be very big & slow to access).
|
|
||||||
|
|
||||||
There are currently 3 VirtualStrings implemented:
|
|
||||||
- VirtualFile, whichs loads a file by page-sized chunks on demand,
|
|
||||||
- WindowsRemoteString, which maps another process' virtual memory (uses the
|
|
||||||
windows debug api through WinDbgAPI)
|
|
||||||
- LinuxRemoteString, which maps another process' virtual memory (need ptrace
|
|
||||||
rights, memory reading is done using /proc/pid/mem)
|
|
||||||
|
|
||||||
The Win/Lin version are quite powerful, and allow things like live process
|
|
||||||
disassembly/patching easily (using LoadedPE/LoadedELF as ExeFormat)
|
|
||||||
|
|
||||||
|
|
||||||
Debugging:
|
|
||||||
|
|
||||||
Metasm includes a few interfaces to handle debugging.
|
|
||||||
The WinOS and LinOS classes offer access to the underlying OS processes (e.g.
|
|
||||||
OS.current.find_process('foobar') will retrieve a running process with foobar
|
|
||||||
in its filename ; then process.mem can be used to access its memory.)
|
|
||||||
|
|
||||||
The Windows and Linux low-level debugging APIs have a basic ruby interface
|
|
||||||
(PTrace and WinAPI) ; which are used by the unified high-end Debugger class.
|
|
||||||
Remote debugging is supported through the GDB server wire protocol.
|
|
||||||
|
|
||||||
High-level debuggers can be created with the following ruby line:
|
|
||||||
Metasm::OS.current.create_debugger('foo')
|
|
||||||
|
|
||||||
Only one kind of host debugger class can exist at a time ; to debug multiple
|
|
||||||
processes, attach to other processes using the existing class. This is due
|
|
||||||
to the way the OS debugging API works on Windows and Linux.
|
|
||||||
|
|
||||||
The low-level backends are defined in the os/ subdirectory, the front-end is
|
|
||||||
defined in debug.rb.
|
|
||||||
|
|
||||||
A linux console debugging interface is available in samples/lindebug.rb ; it
|
|
||||||
uses a (simplified) SoftICE-like look and feel.
|
|
||||||
It can talk to a gdb-server socket ; use a [udp:]<host:port> target.
|
|
||||||
|
|
||||||
The disassembler-gui sample allow live process interaction when using as
|
|
||||||
target 'live:<pid or part of program name>'.
|
|
||||||
|
|
||||||
|
|
||||||
C Parser:
|
|
||||||
|
|
||||||
Metasm includes a hand-written C Parser.
|
|
||||||
It handles all the constructs i am aware of, except hex floats:
|
|
||||||
- static const L"bla"
|
|
||||||
- variable arguments
|
|
||||||
- incomplete types
|
|
||||||
- __attributes__(()), __declspec()
|
|
||||||
- #pragma once
|
|
||||||
- #pragma pack()
|
|
||||||
- C99 declarators - type bla = { [ 2 ... 14 ].toto = 28 };
|
|
||||||
- Nested functions
|
|
||||||
- __int8 etc native types
|
|
||||||
- Label addresses (&&label)
|
|
||||||
Also note that all those things are parsed, but most of them will fail to
|
|
||||||
compile on the Ia32/X64 backend (the only one implemented so far.)
|
|
||||||
|
|
||||||
Parsing C files should be done using an existing ExeFormat, with the
|
|
||||||
parse_c_file method. This ensures that format-specific macros/ABI are correctly
|
|
||||||
defined (ex: size of the 'long' type, ABI to pass parameters to functions, etc)
|
|
||||||
|
|
||||||
When you parse a C String using C::Parser.parse(text), you receive a Parser
|
|
||||||
object. It holds a #toplevel field, which is a C::Block, which holds #structs,
|
|
||||||
#symbols and #statements. The top-level functions are found in the #symbol hash
|
|
||||||
whose keys are the symbol names, associated to a C::Variable object holding
|
|
||||||
the functions. The function parameter/attributes are accessible through
|
|
||||||
func.type, and the code is in func.initializer, which is itself a C::Block.
|
|
||||||
Under it you'll find a tree-like structure of C::Statements (If, While, Asm,
|
|
||||||
CExpressions...)
|
|
||||||
|
|
||||||
A C::Parser may be #precompiled to transform it into a simplified version that
|
|
||||||
is easier to compile: typedefs are removed, control sequences are transformed
|
|
||||||
into 'if (XX) goto YY;' etc.
|
|
||||||
|
|
||||||
To compile a C program, use PE/ELF.compile_c, that will create a C::Parser with
|
|
||||||
exe-specific macros defined (eg __PE__ or __ELF__).
|
|
||||||
|
|
||||||
Vendor-specific headers may need to use either #pragma prepare_visualstudio
|
|
||||||
(to parse the Microsoft Visual Studio headers) or prepare_gcc (for gcc), the
|
|
||||||
latter may be auto-detected (or may not).
|
|
||||||
Vendor headers tested are VS2003 (incl. DDK) and gcc4 ; ymmv.
|
|
||||||
|
|
||||||
Currently the CPU#compilation of a C code will generate an asm source (text),
|
|
||||||
which may then be parsed & assembled to binary code.
|
|
||||||
|
|
||||||
See ExeFormat#compile_c, and samples/exeencode.rb
|
|
||||||
|
|
114
lib/metasm/TODO
114
lib/metasm/TODO
@ -1,114 +0,0 @@
|
|||||||
List of TODO items, by section, in random order
|
|
||||||
|
|
||||||
Ia32
|
|
||||||
emu fpu
|
|
||||||
AVX support
|
|
||||||
realmode
|
|
||||||
|
|
||||||
X86_64
|
|
||||||
decompiler
|
|
||||||
|
|
||||||
CPU
|
|
||||||
Arm
|
|
||||||
Sparc
|
|
||||||
Cell
|
|
||||||
|
|
||||||
Parser
|
|
||||||
Allow single-file multiplexer (C code + Asm + asm16bit + ...)
|
|
||||||
Fix the asm prepro comment issue: '; a /* b\n c ; */' should see 'c'
|
|
||||||
|
|
||||||
Assembler
|
|
||||||
Handle cpu pseudo-instrs (mips 'li' -> lui high + ori low)
|
|
||||||
SplitReloc? (for pseudo-instrs)
|
|
||||||
Ia32 GAS syntax
|
|
||||||
Make the autoimport depend on the target platform and not on the exeformat
|
|
||||||
Encode FPU constants
|
|
||||||
|
|
||||||
Disasm
|
|
||||||
DecodedData
|
|
||||||
Exe decoding generate decodeddata ?
|
|
||||||
Function variable names using stack analysis + ExpressionString
|
|
||||||
Fix thunk detection (thunk: mov ecx, 42 jmp [iat_thiscall] is not a thunk)
|
|
||||||
Test with ET_REL style exe
|
|
||||||
Store stuff out of mem (to handle big binaries)
|
|
||||||
Better :default usage
|
|
||||||
good on call eax, but not on <600k instrs> ret
|
|
||||||
use binary personality ? (uses call vs uses pushret..)
|
|
||||||
Improve 'backtrace => patch di.instr.args'
|
|
||||||
path-specific backtracking ( foo: call a ; a: jmp retloc ; bar: call b ; b: jmp retloc ; retloc: ret ; call foo ; ret : last ret trackback should only reach a:)
|
|
||||||
Decode pseudo/macro-instrs (mips 'li')
|
|
||||||
Deoptimizer (instr reordering for readability)
|
|
||||||
Optimizer (deobfuscating)
|
|
||||||
Per-instr context (allows to mix cell/ppc, x86 32/16bits, arm/armthumb..)
|
|
||||||
|
|
||||||
Compiler
|
|
||||||
Optimizer
|
|
||||||
Register allocator
|
|
||||||
Instr reordering
|
|
||||||
Asm intrinsics
|
|
||||||
Asm inline
|
|
||||||
inline functions
|
|
||||||
Separate partial compilation + linking (src1.c -> obj1.o, src2.c -> obj2.o, obj1.o+obj2.o -> bin)
|
|
||||||
Make generic compiler from cpu.instr_binding ?
|
|
||||||
create a cpu.what_instr_has_binding(:a => (:a + :b)) => 'add a, b' ?
|
|
||||||
Shellcode compiler (exit() => mov eax, 1 int 80h inline)
|
|
||||||
|
|
||||||
Decompiler
|
|
||||||
Fix decompiling on loaded savefile
|
|
||||||
Rewrite cpu-specific to really dumb
|
|
||||||
Just translate di.binding to C
|
|
||||||
maybe w/ trivial var dependency check for unused regs, but beware :incomplete instrs deps
|
|
||||||
Check interdependency ('xadd')
|
|
||||||
Move frame pointer checks / stack var detection to C code
|
|
||||||
Update asm listing from info in C (stack vars, stack var names..)
|
|
||||||
Handle renaming/retyping register vars / aliases
|
|
||||||
Handle switch() / computed goto
|
|
||||||
Fix inline asm reg dependencies
|
|
||||||
Handle direct syscalls (mov eax, 1 int 80h => exit())
|
|
||||||
Autodecode structs
|
|
||||||
FPU
|
|
||||||
Handle/hide compiler-generated stuff (getip, stack cookie setup/check..)
|
|
||||||
Handle call 1f ; 1: pop eax
|
|
||||||
More user control (force/forbid register arg, return type, etc)
|
|
||||||
Preserve C decompiled line association to range of asm decoded addrs
|
|
||||||
|
|
||||||
Debugger
|
|
||||||
OSX
|
|
||||||
Detour-style functionnality to patch binary code (also static to patch exe files?)
|
|
||||||
Move constants in a data/ folder (ptrace reg numbers, syscalls, etc)
|
|
||||||
Generic remote process manip
|
|
||||||
create blank state
|
|
||||||
linux virtualallocex
|
|
||||||
pax-compatible code patch through mmap
|
|
||||||
Remote debugging (small standalone C client)
|
|
||||||
Support dbghelp.dll (ms symbol server info)
|
|
||||||
Support debugee function call (gdb 'call')
|
|
||||||
|
|
||||||
ExeFormat
|
|
||||||
Handle minor editing without decode/reencode (eg patch ELF entrypoint)
|
|
||||||
|
|
||||||
ELF
|
|
||||||
test encoding openbsd binaries
|
|
||||||
handle symbol versions
|
|
||||||
LoadedELF.dump
|
|
||||||
Check relocation encoding (eg samples/dynamic_ruby with cpu.generate_PIC=false)
|
|
||||||
|
|
||||||
MachO
|
|
||||||
|
|
||||||
PE
|
|
||||||
resource editor ?
|
|
||||||
rc compiler ?
|
|
||||||
add simple accessor for resource stuff (manifest, icon, ...)
|
|
||||||
|
|
||||||
GUI
|
|
||||||
debugger
|
|
||||||
specialize widgets
|
|
||||||
show breakpoints
|
|
||||||
show jump direction from current flag values
|
|
||||||
have a console frontend
|
|
||||||
zoom font when zooming graph
|
|
||||||
text selection
|
|
||||||
map (part of) the binary & debug it (map a PE on a linux host & run it)
|
|
||||||
|
|
||||||
Ruby
|
|
||||||
write a fast ruby-like interpreter
|
|
@ -1,88 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
# root directory for metasm files
|
|
||||||
# used by some scripts, eg to find samples/dasm-plugin directory
|
|
||||||
Metasmdir = File.dirname(__FILE__)
|
|
||||||
# add it to the ruby library path
|
|
||||||
$: << Metasmdir
|
|
||||||
|
|
||||||
# constants defined in the same file as another
|
|
||||||
Const_autorequire_equiv = {
|
|
||||||
'X86' => 'Ia32', 'PPC' => 'PowerPC',
|
|
||||||
'X64' => 'X86_64', 'AMD64' => 'X86_64',
|
|
||||||
'MIPS64' => 'MIPS',
|
|
||||||
'UniversalBinary' => 'MachO', 'COFFArchive' => 'COFF',
|
|
||||||
'DEY' => 'DEX',
|
|
||||||
'PTrace' => 'LinOS', 'FatELF' => 'ELF',
|
|
||||||
'LoadedELF' => 'ELF', 'LoadedPE' => 'PE',
|
|
||||||
'LoadedAutoExe' => 'AutoExe',
|
|
||||||
'LinuxRemoteString' => 'LinOS',
|
|
||||||
'LinDebugger' => 'LinOS',
|
|
||||||
'WinAPI' => 'WinOS',
|
|
||||||
'WindowsRemoteString' => 'WinOS', 'WinDbgAPI' => 'WinOS',
|
|
||||||
'WinDebugger' => 'WinOS',
|
|
||||||
'GdbRemoteString' => 'GdbClient', 'GdbRemoteDebugger' => 'GdbClient',
|
|
||||||
'DecodedInstruction' => 'Disassembler', 'DecodedFunction' => 'Disassembler',
|
|
||||||
'InstructionBlock' => 'Disassembler',
|
|
||||||
}
|
|
||||||
|
|
||||||
# files to require to get the definition of those constants
|
|
||||||
Const_autorequire = {
|
|
||||||
'Ia32' => 'cpu/ia32', 'MIPS' => 'cpu/mips', 'PowerPC' => 'cpu/ppc', 'ARM' => 'cpu/arm',
|
|
||||||
'X86_64' => 'cpu/x86_64', 'Sh4' => 'cpu/sh4', 'Dalvik' => 'cpu/dalvik', 'ARC' => 'cpu/arc',
|
|
||||||
'Python' => 'cpu/python', 'Z80' => 'cpu/z80', 'CY16' => 'cpu/cy16', 'BPF' => 'cpu/bpf',
|
|
||||||
'MSP430' => 'cpu/msp430',
|
|
||||||
'C' => 'compile_c',
|
|
||||||
'MZ' => 'exe_format/mz', 'PE' => 'exe_format/pe',
|
|
||||||
'ELF' => 'exe_format/elf', 'COFF' => 'exe_format/coff',
|
|
||||||
'Shellcode' => 'exe_format/shellcode', 'AutoExe' => 'exe_format/autoexe',
|
|
||||||
'AOut' => 'exe_format/a_out', 'MachO' => 'exe_format/macho',
|
|
||||||
'DEX' => 'exe_format/dex',
|
|
||||||
'NDS' => 'exe_format/nds', 'XCoff' => 'exe_format/xcoff',
|
|
||||||
'GameBoyRom' => 'exe_format/gb',
|
|
||||||
'Bflt' => 'exe_format/bflt', 'Dol' => 'exe_format/dol',
|
|
||||||
'PYC' => 'exe_format/pyc', 'JavaClass' => 'exe_format/javaclass',
|
|
||||||
'SWF' => 'exe_format/swf', 'ZIP' => 'exe_format/zip',
|
|
||||||
'Shellcode_RWX' => 'exe_format/shellcode_rwx',
|
|
||||||
'Gui' => 'gui',
|
|
||||||
'WindowsExports' => 'os/windows_exports',
|
|
||||||
'GNUExports' => 'os/gnu_exports',
|
|
||||||
'Debugger' => 'debug',
|
|
||||||
'LinOS' => 'os/linux', 'WinOS' => 'os/windows',
|
|
||||||
'GdbClient' => 'os/remote',
|
|
||||||
'Disassembler' => 'disassemble',
|
|
||||||
'Decompiler' => 'decompile',
|
|
||||||
'DynLdr' => 'dynldr',
|
|
||||||
}
|
|
||||||
|
|
||||||
# use the Module.autoload ruby functionnality to load framework components on demand
|
|
||||||
Const_autorequire.each { |cst, file|
|
|
||||||
autoload cst, File.join('metasm', file)
|
|
||||||
}
|
|
||||||
|
|
||||||
Const_autorequire_equiv.each { |cst, eqv|
|
|
||||||
file = Const_autorequire[eqv]
|
|
||||||
autoload cst, File.join('metasm', file)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# load Metasm core files
|
|
||||||
%w[main encode decode render exe_format/main os/main].each { |f|
|
|
||||||
require File.join('metasm', f)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# remove an 1.9 warning, couldn't find a compatible way...
|
|
||||||
if Hash.new.respond_to?(:key)
|
|
||||||
puts "using ruby1.9 workaround for Hash#index warning" if $DEBUG
|
|
||||||
class Hash
|
|
||||||
alias index_premetasm index rescue nil
|
|
||||||
undef index rescue nil
|
|
||||||
alias index key
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/arc/decode'
|
|
@ -1,425 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/arc/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARC
|
|
||||||
def major_opcode(val, sz = 16)
|
|
||||||
return val >> (sz == 16 ? 0xB : 0x1B)
|
|
||||||
end
|
|
||||||
|
|
||||||
def sub_opcode(val)
|
|
||||||
return ((val >> 16) & 0x3f)
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_opcode_bin_mask(op, sz)
|
|
||||||
op.bin_mask = 0
|
|
||||||
op.args.each { |f| op.bin_mask |= @fields_mask[f] << @fields_shift[f]}
|
|
||||||
op.bin_mask = ((1 << sz)-1) ^ op.bin_mask
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
bin_lookaside = {}
|
|
||||||
opcode_list.each{|mode,oplist|
|
|
||||||
lookaside = {}
|
|
||||||
# 2nd level to speed up lookaside for major 5
|
|
||||||
lookaside[5] = {}
|
|
||||||
oplist.each { |op|
|
|
||||||
next if not op.bin.kind_of? Integer
|
|
||||||
build_opcode_bin_mask(op, mode)
|
|
||||||
mj = major_opcode(op.bin, mode)
|
|
||||||
if mode == 32 and mj == 5
|
|
||||||
(lookaside[mj][sub_opcode(op.bin)] ||= []) << op
|
|
||||||
else
|
|
||||||
(lookaside[mj] ||= []) << op
|
|
||||||
end
|
|
||||||
}
|
|
||||||
bin_lookaside[mode] = lookaside
|
|
||||||
}
|
|
||||||
bin_lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def instruction_size(edata)
|
|
||||||
val = major_opcode(edata.decode_imm(:u16, @endianness))
|
|
||||||
edata.ptr -= 2
|
|
||||||
(val >= 0xC) ? 16 : 32
|
|
||||||
end
|
|
||||||
|
|
||||||
def memref_size(di)
|
|
||||||
case di.opcode.name
|
|
||||||
when 'ldb_s', 'stb_s', 'extb_s', 'sexb_s'; 1
|
|
||||||
when 'ldw_s', 'stw_s', 'extw_s', 'sexw_s'; 2
|
|
||||||
else 4
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_bin(edata, sz)
|
|
||||||
case sz
|
|
||||||
when 16; edata.decode_imm(:u16, @endianness)
|
|
||||||
when 32
|
|
||||||
# wordswap
|
|
||||||
val = edata.decode_imm(:u32, :little)
|
|
||||||
((val >> 16) & 0xffff) | ((val & 0xffff) << 16)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
|
|
||||||
@instrlength = instruction_size(edata)
|
|
||||||
val = decode_bin(edata, @instrlength)
|
|
||||||
edata.ptr -= @instrlength/8
|
|
||||||
|
|
||||||
maj = major_opcode(val, @instrlength)
|
|
||||||
lookaside = @bin_lookaside[@instrlength][maj]
|
|
||||||
lookaside = lookaside[sub_opcode(val)] if @instrlength == 32 and maj == 5
|
|
||||||
|
|
||||||
op = lookaside.select { |opcode|
|
|
||||||
if $ARC_DEBUG and (val & opcode.bin_mask) == opcode.bin
|
|
||||||
puts "#{opcode.bin_mask.to_s(16)} - #{opcode.bin.to_s(16)} - #{(val & opcode.bin_mask).to_s(16)} - #{opcode.name} - #{opcode.args}"
|
|
||||||
end
|
|
||||||
(val & opcode.bin_mask) == opcode.bin
|
|
||||||
}
|
|
||||||
|
|
||||||
if op.size == 2 and op.first.name == 'mov' and op.last.name == 'nop'
|
|
||||||
op = op.last
|
|
||||||
elsif op == nil or op.size != 1
|
|
||||||
puts "[> I sense a disturbance in the force <]"
|
|
||||||
op.to_a.each { |opcode| puts "#{opcode.name} - #{opcode.args} - #{Expression[opcode.bin]} - #{Expression[opcode.bin_mask]}" }
|
|
||||||
puts "current value: #{Expression[val]}"
|
|
||||||
puts "current value: 0b#{val.to_s(2)}"
|
|
||||||
op = nil
|
|
||||||
else
|
|
||||||
op = op.first
|
|
||||||
end
|
|
||||||
|
|
||||||
di if di.opcode = op
|
|
||||||
end
|
|
||||||
|
|
||||||
Reduced_reg = [0, 1, 2, 3, 12, 13, 14, 15]
|
|
||||||
def reduced_reg_set(i)
|
|
||||||
Reduced_reg[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
val = decode_bin(edata, @instrlength)
|
|
||||||
|
|
||||||
field_val = lambda { |f|
|
|
||||||
r = (val >> @fields_shift[f]) & @fields_mask[f]
|
|
||||||
case f
|
|
||||||
|
|
||||||
# 16-bits instruction operands ------------------------------------------"
|
|
||||||
when :ca, :cb, :cb2, :cb3, :cc; r = reduced_reg_set(r)
|
|
||||||
when :ch
|
|
||||||
r = (((r & 7) << 3) | (r >> 5))
|
|
||||||
when :@cbu7, :@cbu6, :@cbu5
|
|
||||||
r = r & 0b11111
|
|
||||||
r = (f == :@cbu7) ? r << 2 : ( (f == :@cbu6) ? r << 1 : r)
|
|
||||||
when :cu5ee; r = r << 2
|
|
||||||
when :cdisps13
|
|
||||||
r = (Expression.make_signed(r,11) << 2) + ((di.address >> 2) << 2)
|
|
||||||
when :cdisps10
|
|
||||||
r = (Expression.make_signed(r, 9) << 1) + ((di.address >> 2) << 2)
|
|
||||||
when :cdisps8
|
|
||||||
r = (Expression.make_signed(r, 7) << 1) + ((di.address >> 2) << 2)
|
|
||||||
when :cdisps7
|
|
||||||
r = (Expression.make_signed(r, 6) << 1) + ((di.address >> 2) << 2)
|
|
||||||
when :cs9, :cs10, :cs11
|
|
||||||
r = Expression.make_signed(r, ((f== :cs11 ? 11 : (f == :cs10 ? 10 : 9) )))
|
|
||||||
r = (f == :cs11) ? r << 2 : ((f == :cs10) ? r << 1 : r)
|
|
||||||
when :@cspu7;
|
|
||||||
r = r << 2
|
|
||||||
|
|
||||||
# 32-bits instruction operands ------------------------------------------"
|
|
||||||
when :b
|
|
||||||
r = (r >> 12) | ((r & 0x7) << 3)
|
|
||||||
when :s8e
|
|
||||||
r = ((r & 0x1) << 7) | (r >> 2)
|
|
||||||
r = (Expression.make_signed(r, 8) << 1) + ((di.address >> 2) << 2)
|
|
||||||
|
|
||||||
when :u6e
|
|
||||||
r = (r << 1) + ((di.address >> 2) << 2)
|
|
||||||
when :s9
|
|
||||||
r = (Expression.make_signed(r, 7) << 1) + ((di.address >> 2) << 2)
|
|
||||||
|
|
||||||
when :s12
|
|
||||||
r = (r >> 6) | ((r & 0x3f) << 6)
|
|
||||||
r = Expression.make_signed(r, 12)
|
|
||||||
|
|
||||||
when :s12e
|
|
||||||
r = (r >> 6) | ((r & 0x3f) << 6)
|
|
||||||
r = (Expression.make_signed(r, 12) <<1 ) + ((di.address >> 2) << 2)
|
|
||||||
|
|
||||||
when :s21e
|
|
||||||
r = ((r & 0x3ff) << 10) | (r >> 11)
|
|
||||||
r = (Expression.make_signed(r, 20) << 1) + ((di.address >> 2) << 2)
|
|
||||||
|
|
||||||
when :s21ee # pc-relative
|
|
||||||
r = ((r & 0x3ff) << 9) | (r >> 12)
|
|
||||||
r = (Expression.make_signed(r, 19) << 2) + ((di.address >> 2) << 2)
|
|
||||||
|
|
||||||
when :s25e # pc-relative
|
|
||||||
r = ((r & 0xf) << 20) | (((r >> 6) & 0x3ff) << 10) | (r >> 17)
|
|
||||||
r = (Expression.make_signed(r, 24) << 1) + ((di.address >> 2) << 2)
|
|
||||||
|
|
||||||
when :s25ee # pc-relative
|
|
||||||
r = ((r & 0xf) << 19) | (((r >> 6) & 0x3ff) << 9) | (r >> 18)
|
|
||||||
r = (Expression.make_signed(r, 23) << 2) + ((di.address >> 2) << 2)
|
|
||||||
|
|
||||||
when :@bs9
|
|
||||||
r = r >> 3
|
|
||||||
s9 = ((r & 1) << 8) | ((r >> 1) & 0xff)
|
|
||||||
r = Expression.make_signed(s9, 9)
|
|
||||||
|
|
||||||
when :bext, :cext, :@cext
|
|
||||||
if ((r = field_val[(f == :bext) ? :b : :c]) == 0x3E)
|
|
||||||
tmp = edata.decode_imm(:u32, :little)
|
|
||||||
r = Expression[(tmp >> 16) | ((tmp & 0xffff) << 16)]
|
|
||||||
else
|
|
||||||
r = GPR.new(r)
|
|
||||||
end
|
|
||||||
|
|
||||||
else r
|
|
||||||
end
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
# decode properties fields
|
|
||||||
op.args.each { |a|
|
|
||||||
case a
|
|
||||||
when :flags15, :flags16
|
|
||||||
di.instruction.opname += '.f' if field_val[a] != 0
|
|
||||||
when :ccond
|
|
||||||
di.instruction.opname += ('.' + @cond_suffix[field_val[a]]) if field_val[a] != 0
|
|
||||||
when :delay5, :delay16
|
|
||||||
di.instruction.opname += '.d' if field_val[a] != 0
|
|
||||||
when :cache5, :cache11, :cache16
|
|
||||||
di.instruction.opname +='.di' if field_val[a] != 0
|
|
||||||
when :signext6, :signext16
|
|
||||||
di.instruction.opname += '.x' if field_val[a] != 0
|
|
||||||
when :wb3, :wb9, :wb22
|
|
||||||
case field_val[a]
|
|
||||||
when 1; di.instruction.opname += ((memref_size(di) == 2) ? '.ab' : '.a')
|
|
||||||
when 2; di.instruction.opname += '.ab'
|
|
||||||
when 3; di.instruction.opname += '.as'
|
|
||||||
end
|
|
||||||
when :sz1, :sz7, :sz16, :sz17
|
|
||||||
case field_val[a]
|
|
||||||
when 1; di.instruction.opname += 'b'
|
|
||||||
when 2; di.instruction.opname += 'w'
|
|
||||||
end
|
|
||||||
else
|
|
||||||
di.instruction.args << case a
|
|
||||||
|
|
||||||
# 16-bits instruction operands ------------------------------------------"
|
|
||||||
when :cr0; GPR.new 0
|
|
||||||
when :ca, :cb, :cb2, :cb3, :cc; GPR.new(field_val[a])
|
|
||||||
when :ch
|
|
||||||
if ((r = field_val[a]) == 0x3E)
|
|
||||||
tmp = edata.decode_imm(:u32, :little)
|
|
||||||
Expression[(tmp >> 16) | ((tmp & 0xffff) << 16)]
|
|
||||||
else
|
|
||||||
GPR.new(r)
|
|
||||||
end
|
|
||||||
|
|
||||||
when :@gps9, :@gps10, :@gps11
|
|
||||||
imm = (a == :@gps11) ? :cs11 : (a == :@gps10) ? :cs10 : :cs9
|
|
||||||
Memref.new(GPR.new(26), Expression[field_val[imm]], memref_size(di))
|
|
||||||
|
|
||||||
when :cu3, :cu5, :cu5ee, :cu6, :cu7, :cu7l, :cu8; Expression[field_val[a]]
|
|
||||||
when :cs9, :cs10, :cs11; Expression[field_val[a]]
|
|
||||||
when :cdisps7, :cdisps8, :cdisps10, :cdisps13; Expression[field_val[a]]
|
|
||||||
when :@cb; Memref.new(GPR.new(field_val[:cb]), nil, memref_size(di))
|
|
||||||
when :@cbu7, :@cbu6, :@cbu5; Memref.new(GPR.new(field_val[:cb]), Expression[field_val[a]], memref_size(di))
|
|
||||||
when :@cspu7; Memref.new(GPR.new(28), field_val[a], memref_size(di))
|
|
||||||
when :@cbcc; Memref.new(field_val[:cb], field_val[:cc], memref_size(di))
|
|
||||||
|
|
||||||
# 32-bits instruction operands ------------------------------------------"
|
|
||||||
when :a, :b
|
|
||||||
((r = field_val[a]) == 0x3E) ? :zero : GPR.new(r)
|
|
||||||
when :b2; GPR.new field_val[:b]
|
|
||||||
when :c; GPR.new field_val[a]
|
|
||||||
when :bext, :cext; field_val[a]
|
|
||||||
when :@cext
|
|
||||||
target = field_val[a]
|
|
||||||
(di.opcode.props[:setip] and target.kind_of? GPR) ? Memref.new(target, nil, memref_size(di)) : target
|
|
||||||
|
|
||||||
when :@bextcext
|
|
||||||
tmp = field_val[a]
|
|
||||||
#c = tmp & 0x3F
|
|
||||||
tmp = tmp >> 6
|
|
||||||
b = (tmp >> 12) | ((tmp & 0x7) << 3)
|
|
||||||
Memref.new(field_val[:bext], field_val[:cext], memref_size(di))
|
|
||||||
|
|
||||||
when :u6, :u6e, :s8e, :s9, :s12; Expression[field_val[a]]
|
|
||||||
when :s12e, :s21e, :s21ee, :s25e, :s25ee; Expression[field_val[a]]
|
|
||||||
when :auxs12; AUX.new field_val[:s12]
|
|
||||||
when :@c; Memref.new(GPR.new(field_val[a]), nil, memref_size(di))
|
|
||||||
when :@bcext; Memref.new(field_val[a], nil, memref_size(di))
|
|
||||||
when :@bcext; Memref.new(field_val[:b], field_val[:cext], memref_size(di))
|
|
||||||
when :@bs9
|
|
||||||
# [b,s9] or [limm] if b = 0x3E
|
|
||||||
base = field_val[:bext]
|
|
||||||
Memref.new(base, (base.kind_of? GPR) ? Expression[field_val[a]] : nil, memref_size(di))
|
|
||||||
|
|
||||||
# common instruction operands ------------------------------------------"
|
|
||||||
when :zero; Expression[0]
|
|
||||||
when :gp; GPR.new(26)
|
|
||||||
when :sp, :sp2; GPR.new(28)
|
|
||||||
when :blink; GPR.new(31)
|
|
||||||
when :@ilink1; Memref.new(GPR.new(29), nil, memref_size(di))
|
|
||||||
when :@ilink2; Memref.new(GPR.new(30), nil, memref_size(di))
|
|
||||||
when :@blink; Memref.new(GPR.new(31), nil, memref_size(di))
|
|
||||||
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
return if edata.ptr > edata.virtsize
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def disassembler_default_func
|
|
||||||
df = DecodedFunction.new
|
|
||||||
df.backtrace_binding = {}
|
|
||||||
15.times { |i|
|
|
||||||
df.backtrace_binding["r#{i}".to_sym] = Expression::Unknown
|
|
||||||
}
|
|
||||||
df.backtracked_for = []
|
|
||||||
df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
|
|
||||||
if funcaddr != :default
|
|
||||||
btfor
|
|
||||||
elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip]
|
|
||||||
btfor
|
|
||||||
else []
|
|
||||||
end
|
|
||||||
}
|
|
||||||
df
|
|
||||||
end
|
|
||||||
|
|
||||||
REG_SYMS = [:r26, :r27, :r28, :r29, :r30, :r31, :r60]
|
|
||||||
def register_symbols
|
|
||||||
REG_SYMS
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def opshift(op)
|
|
||||||
op[/\d/].to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
def with_res(arg)
|
|
||||||
arg != :zero
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
sp = :r28
|
|
||||||
blink = :r31
|
|
||||||
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
|
|
||||||
mask = lambda { |sz| (1 << sz)-1 } # 32bits => 0xffff_ffff
|
|
||||||
|
|
||||||
opcode_list.each{|mode, oplist|
|
|
||||||
oplist.map { |ol| ol.name }.uniq.each { |op|
|
|
||||||
binding = case op
|
|
||||||
when /^add/, /^sub/
|
|
||||||
lambda { |di, a0, a1, a2|
|
|
||||||
if (shift = opshift(op)) == 0
|
|
||||||
{ a0 => Expression[[a1, :+, a2], :&, mask[32]] }
|
|
||||||
else
|
|
||||||
{ a0 => Expression[[a1, :+, [a2, :<<, shift]], :&, mask[32]] }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
when /^and/
|
|
||||||
lambda { |di, a0, a1, a2| { a0 => Expression[a1, :&, a2] } }
|
|
||||||
when /^asl/
|
|
||||||
lambda { |di, *a| { a[0] => Expression[[a[1], :<<, (a[2] ? a[2]:1)], :&, mask[32]] } }
|
|
||||||
when /^bxor/
|
|
||||||
lambda { |di, a0, a1, a2| { a0 => Expression[a1, :^, [1, :<<, a2]] }}
|
|
||||||
when /^bclr/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :&, Expression[mask[32], :^, Expression[1, :<<, a2]]] } }
|
|
||||||
when /^bset/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :|, Expression[1, :<<, a2]] } }
|
|
||||||
when /^jl/; lambda { |di, a0| { blink => Expression[di.next_addr] } }
|
|
||||||
when 'bl', 'bl_s', /^bl\./
|
|
||||||
# FIXME handle delay slot
|
|
||||||
# "This address is taken either from the first instruction following the branch (current PC) or the
|
|
||||||
# instruction after that (next PC) according to the delay slot mode (.d)."
|
|
||||||
lambda { |di, a0| { blink => Expression[di.next_addr] } }
|
|
||||||
when /^mov/, /^lr/, /^ld/; lambda { |di, a0, a1| { a0 => a1 } }
|
|
||||||
when /^neg/; lambda { |di, a0, a1| { a0 => Expression[[0, :-, a1], :&, mask[32]] } }
|
|
||||||
when /^not/; lambda { |di, a0, a1| { a0 => Expression[[:~, a1], :&, mask[32]] } }
|
|
||||||
when /^or/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :|, a2] } }
|
|
||||||
when /^st/, /^sr/; lambda { |di, a0, a1| { a1 => a0 } }
|
|
||||||
when /^ex/; lambda { |di, a0, a1| { a1 => a0 , a0 => a1 } }
|
|
||||||
when 'push_s'
|
|
||||||
lambda { |di, a0| {
|
|
||||||
sp => Expression[sp, :-, 4],
|
|
||||||
Indirection[sp, @size/8, di.address] => Expression[a0]
|
|
||||||
} }
|
|
||||||
when 'pop_s'
|
|
||||||
lambda { |di, a0| {
|
|
||||||
a0 => Indirection[sp, @size/8, di.address],
|
|
||||||
sp => Expression[sp, :+, 4]
|
|
||||||
} }
|
|
||||||
end
|
|
||||||
@backtrace_binding[op] ||= binding if binding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when GPR; arg.symbolic
|
|
||||||
when Memref; arg.symbolic(di.address)
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.basename]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
{ :incomplete_binding => Expression[1] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
arg = case di.opcode.name
|
|
||||||
when 'b', 'b_s', /^j/, /^bl/, /^br/, 'lp'
|
|
||||||
expr = di.instruction.args.last
|
|
||||||
expr.kind_of?(Memref) ? expr.base : expr
|
|
||||||
else di.instruction.args.last
|
|
||||||
end
|
|
||||||
|
|
||||||
[Expression[(arg.kind_of?(Reg) ? arg.symbolic : arg)]]
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
Expression[expr].reduce == Expression[register_symbols[5]]
|
|
||||||
end
|
|
||||||
|
|
||||||
def delay_slot(di=nil)
|
|
||||||
return 0 if (not di) or (not di.opcode.props[:setip])
|
|
||||||
return 1 if di.opcode.props[:delay_slot]
|
|
||||||
(di.instruction.opname =~ /\.d/) ? 0 : 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,191 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARC < CPU
|
|
||||||
def initialize(e = :little)
|
|
||||||
super()
|
|
||||||
@endianness = e
|
|
||||||
@size = 32
|
|
||||||
end
|
|
||||||
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
o.class == self.class and o.i == i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# general purpose reg
|
|
||||||
# Result R0-R1
|
|
||||||
# Arguments R0-R7
|
|
||||||
# Caller Saved Registers R0-R12
|
|
||||||
# Callee Saved Registers R13-R25
|
|
||||||
# Static chain pointer (if required) R11
|
|
||||||
# Register for temp calculation R12
|
|
||||||
# Global Pointer R26 (GP)
|
|
||||||
# Frame Pointer R27 (FP)
|
|
||||||
# Stack Pointer R28 (SP)
|
|
||||||
# Interrupt Link Register 1 R29 (ILINK1)
|
|
||||||
# Interrupt Link Register 2 R30 (ILINK2)
|
|
||||||
# Branch Link Register R31 (BLINK)
|
|
||||||
class GPR < Reg
|
|
||||||
Sym = (0..64).map { |i| "r#{i}".to_sym }
|
|
||||||
def symbolic; Sym[@i] end
|
|
||||||
|
|
||||||
Render = {
|
|
||||||
26 => 'gp', # global pointer, used to point to small sets of shared data throughout execution of a program
|
|
||||||
27 => 'fp', # frame pointer
|
|
||||||
28 => 'sp', # stak pointer
|
|
||||||
29 => 'ilink1', # maskable interrupt link register
|
|
||||||
30 => 'ilink2', # maskable interrupt link register 2
|
|
||||||
31 => 'blink', # branch link register
|
|
||||||
60 => 'lp_count', # loop count register (24 bits)
|
|
||||||
# "When a destination register is set to r62 there is no destination for the result of the instruction so the
|
|
||||||
# result is discarded. Any flag updates will still occur according to the set flags directive (.F or implicit
|
|
||||||
# in the instruction)."
|
|
||||||
62 => 'zero'
|
|
||||||
}
|
|
||||||
|
|
||||||
def render
|
|
||||||
if s = Render[i]
|
|
||||||
[s]
|
|
||||||
else
|
|
||||||
# r0-r28 general purpose registers
|
|
||||||
# r32-r59 reserved for extentions
|
|
||||||
["r#@i"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
class AUX < Reg
|
|
||||||
def symbolic; "aux#{i}".to_sym end
|
|
||||||
|
|
||||||
Render = {
|
|
||||||
0x00 => 'status', # Status register (Original ARCtangent-A4 processor format)
|
|
||||||
0x01 => 'semaphore', # Inter-process/Host semaphore register
|
|
||||||
0x02 => 'lp_start', # Loop start address (32-bit)
|
|
||||||
0x03 => 'lp_end', # Loop end address (32-bit)
|
|
||||||
0x04 => 'identity', # Processor Identification register
|
|
||||||
0x05 => 'debug', # Debug register
|
|
||||||
0x06 => 'pc', # PC register (32-bit)
|
|
||||||
0x0A => 'status32', # Status register (32-bit)
|
|
||||||
0x0B => 'status32_l1', # Status register save for level 1 interrupts
|
|
||||||
0x0C => 'status32_l2', # Status register save for level 2 interrupts
|
|
||||||
0x10 => 'ic_ivic', # Cache invalidate
|
|
||||||
0x11 => 'ic_ctrl', # Mode bits for cache controller
|
|
||||||
0x12 => 'mulhi', # High part of Multiply
|
|
||||||
0x19 => 'ic_ivil',
|
|
||||||
0x21 => 'timer0_cnt', # Processor Timer 0 Count value
|
|
||||||
0x22 => 'timer0_ctrl', # Processor Timer 0 Control value
|
|
||||||
0x23 => 'timer0_limit', # Processor Timer 0 Limit value
|
|
||||||
0x25 => 'int_vector_base', # Interrupt Vector Base address
|
|
||||||
0x40 => 'im_set_dc_ctrl',
|
|
||||||
0x41 => 'aux_macmode', # Extended Arithmetic Status and Mode
|
|
||||||
0x43 => 'aux_irq_lv12', # Interrupt Level Status
|
|
||||||
0x47 => 'dc_ivdc', # Invalidate cache
|
|
||||||
0x48 => 'dc_ctrl', # Cache control register
|
|
||||||
0x49 => 'dc_ldl', # Lock data line
|
|
||||||
0x4A => 'dc_ivdl', # Invalidate data line
|
|
||||||
0x4B => 'dc_flsh', # Flush data cache
|
|
||||||
0x4C => 'dc_fldl', # Flush data line
|
|
||||||
0x58 => 'dc_ram_addr', # Access RAM address
|
|
||||||
0x59 => 'dc_tag', # Tag Access
|
|
||||||
0x5A => 'dc_wp', # Way Pointer Access
|
|
||||||
0x5B => 'dc_data', # Data Access
|
|
||||||
0x62 => 'crc_bcr',
|
|
||||||
0x64 => 'dvfb_bcr',
|
|
||||||
0x65 => 'extarith_bcr',
|
|
||||||
0x68 => 'vecbase_bcr',
|
|
||||||
0x69 => 'perbase_bcr',
|
|
||||||
0x6f => 'mmu_bcr',
|
|
||||||
0x72 => 'd_cache_build', # Build: Data Cache
|
|
||||||
0x73 => 'madi_build', # Build: Multiple ARC Debug I/F
|
|
||||||
0x74 => 'ldstram_build', # Build: LD/ST RAM
|
|
||||||
0x75 => 'timer_build', # Build: Timer
|
|
||||||
0x76 => 'ap_build', # Build: Actionpoints
|
|
||||||
0x77 => 'i_cache_build', # Build: I-Cache
|
|
||||||
0x78 => 'addsub_build', # Build: Saturated Add/Sub
|
|
||||||
0x79 => 'dspram_build', # Build: Scratch RAM & XY Memory
|
|
||||||
0x7B => 'multiply_build', # Build: Multiply
|
|
||||||
0x7C => 'swap_build', # Build: Swap
|
|
||||||
0x7D => 'norm_build', # Build: Normalise
|
|
||||||
0x7E => 'minmax_build', # Build: Min/Max
|
|
||||||
0x7F => 'barrel_build', # Build: Barrel Shift
|
|
||||||
0x100 => 'timer1_cnt', # Processor Timer 1 Count value
|
|
||||||
0x101 => 'timer1_ctrl', # Processor Timer 1 Control value
|
|
||||||
0x102 => 'timer1_limit', # Processor Timer 1 Limit value
|
|
||||||
0x200 => 'aux_irq_lev', # Interrupt Level Programming
|
|
||||||
0x201 => 'aux_irq_hint', # Software Triggered Interrupt
|
|
||||||
0x202 => 'aux_irq_mask', # Masked bits for Interrupts
|
|
||||||
0x203 => 'aux_irq_base', # Interrupt Vector base address
|
|
||||||
0x400 => 'eret', # Exception Return Address
|
|
||||||
0x401 => 'erbta', # Exception Return Branch Target Address
|
|
||||||
0x402 => 'erstatus', # Exception Return Status
|
|
||||||
0x403 => 'ecr', # Exception Cause Register
|
|
||||||
0x404 => 'efa', # Exception Fault Address
|
|
||||||
0x40A => 'icause1', # Level 1 Interrupt Cause Register
|
|
||||||
0x40B => 'icause2', # Level 2 Interrupt Cause Register
|
|
||||||
0x40C => 'aux_ienable', # Interrupt Mask Programming
|
|
||||||
0x40D => 'aux_itrigger', # Interrupt Sensitivity Programming
|
|
||||||
0x410 => 'xpu', # User Mode Extension Enables
|
|
||||||
0x412 => 'bta', # Branch Target Address
|
|
||||||
0x413 => 'bta_l1', # Level 1 Return Branch Target
|
|
||||||
0x414 => 'bta_l2', # Level 2 Return Branch Target
|
|
||||||
0x415 => 'aux_irq_pulse_cancel', # Interrupt Pulse Cancel
|
|
||||||
0x416 => 'aux_irq_pending', # Interrupt Pending Register
|
|
||||||
}
|
|
||||||
|
|
||||||
def render
|
|
||||||
if s = Render[i]
|
|
||||||
[s]
|
|
||||||
else
|
|
||||||
["aux#@i"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
attr_accessor :base, :disp
|
|
||||||
|
|
||||||
def initialize(base, disp, sz)
|
|
||||||
@base, @disp, @size = base, disp, sz
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig)
|
|
||||||
b = @base
|
|
||||||
b = b.symbolic if b.kind_of? Reg
|
|
||||||
|
|
||||||
if disp
|
|
||||||
o = @disp
|
|
||||||
o = o.symbolic if o.kind_of? Reg
|
|
||||||
e = Expression[b, :+, o].reduce
|
|
||||||
else
|
|
||||||
e = Expression[b].reduce
|
|
||||||
end
|
|
||||||
|
|
||||||
Indirection[e, @size, orig]
|
|
||||||
end
|
|
||||||
|
|
||||||
include Renderable
|
|
||||||
|
|
||||||
def render
|
|
||||||
if @disp and @disp != 0
|
|
||||||
['[', @base, ', ', @disp, ']']
|
|
||||||
else
|
|
||||||
['[', @base, ']']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,588 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/arc/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARC
|
|
||||||
def addop32(name, bin, *args)
|
|
||||||
addop(:ac32, name, bin, *args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop16(name, bin, *args)
|
|
||||||
addop(:ac16, name, bin, *args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop(mode, name, bin, *args)
|
|
||||||
o = Opcode.new(name)
|
|
||||||
o.bin = bin
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @fields_mask[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a]
|
|
||||||
}
|
|
||||||
(mode == :ac16) ? (@opcode_list16 << o) : (@opcode_list32 << o)
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
@opcode_list16 = []
|
|
||||||
@opcode_list32 = []
|
|
||||||
|
|
||||||
@valid_props.update :flag_update => true, :delay_slot => true
|
|
||||||
@cond_suffix = [''] + %w[z nz p n cs cc vs vc gt ge lt le hi ls pnz]
|
|
||||||
#The remaining 16 condition codes (10-1F) are available for extension
|
|
||||||
@cond_suffix += (0x10..0x1f).map{ |i| "extcc#{i.to_s(16)}" }
|
|
||||||
|
|
||||||
# Compact 16-bits operands field masks
|
|
||||||
fields_mask16 = {
|
|
||||||
:ca => 0x7, :cb => 0x7, :cb2 => 0x7, :cb3 => 0x7, :cc => 0x7,
|
|
||||||
:cu => 0x1f,
|
|
||||||
:ch => 0b11100111,
|
|
||||||
|
|
||||||
# immediate (un)signed
|
|
||||||
:cu3 => 0x7, :cu8 => 0xff,
|
|
||||||
# cu7 is 32-bit aligned, cu6 is 16-bit aligned, cu6 is 8-bit aligned
|
|
||||||
:cu5 => 0x1f, :cu5ee => 0x1f, :cu6 => 0x3f, :cu7 => 0x7f,
|
|
||||||
|
|
||||||
:cs9 => 0x1ff, :cs9ee => 0x1ff, :cs10 => 0x1ff, :cs11 => 0x1ff,
|
|
||||||
|
|
||||||
# signed displacement
|
|
||||||
:cdisps7=> 0x3f, :cdisps8 => 0x7f, :cdisps10 => 0x1ff, :cdisps13 => 0x7FF,
|
|
||||||
|
|
||||||
# memref [b+u], [sp,u], etc.
|
|
||||||
:@cb => 0x7, :@cbu7 => 0b11100011111, :@cbu6 => 0b11100011111, :@cbu5 => 0b11100011111,
|
|
||||||
:@cspu7 => 0b11111, :@cbcc => 0b111111,
|
|
||||||
:@gps9 => 0x1ff, :@gps10 => 0x1ff, :@gps11 => 0x1ff,
|
|
||||||
|
|
||||||
# implicit operands
|
|
||||||
:climm => 0x0, :cr0 => 0x0,
|
|
||||||
:blink => 0x0, :@blink => 0x0, :gp => 0x0, :sp => 0x0, :sp2 => 0x0, :zero => 0x0
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_shift16 = {
|
|
||||||
:ca => 0x0, :cb => 0x8, :cb2 => 0x8, :cb3 => 0x8, :cc => 0x5,
|
|
||||||
:cu => 0x0,
|
|
||||||
|
|
||||||
# immediate (un)signed
|
|
||||||
:ch => 0x0,
|
|
||||||
:cu3 => 0x0, :cu5 => 0, :cu5ee => 0, :cu6 => 5, :cu7 => 0x0, :cu8 => 0x0,
|
|
||||||
:cs9 => 0x0, :cs9ee => 0x0, :cs10 => 0x0, :cs11 => 0x0,
|
|
||||||
|
|
||||||
# signed displacement
|
|
||||||
:cdisps7=> 0x0, :cdisps8 => 0x0, :cdisps10 => 0x0, :cdisps13 => 0x0,
|
|
||||||
|
|
||||||
# memref [b+u]
|
|
||||||
:@cb => 0x8, :@cbu7 => 0x0, :@cbu6 => 0x0, :@cbu5 => 0x0,
|
|
||||||
:@cspu7 => 0x0, :@cbcc => 0x5,
|
|
||||||
:@gps9 => 0x0, :@gps10 => 0x0, :@gps11 => 0x0,
|
|
||||||
|
|
||||||
# implicit operands
|
|
||||||
:climm => 0x0, :cr0 => 0x0,
|
|
||||||
:blink => 0x0, :@blink => 0x0, :gp => 0x0, :sp => 0x0, :sp2 => 0x0, :zero => 0x0,
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_mask32 = {
|
|
||||||
:a => 0x3f, :b => 0b111000000000111, :bext => 0b111000000000111,
|
|
||||||
:c => 0x3f, :@c => 0x3f, :cext => 0x3f, :@cext => 0x3f,
|
|
||||||
|
|
||||||
:u6 => 0x3f, :u6e => 0x3f,
|
|
||||||
:s8e => 0x1fd, :s9 => 0x7f,
|
|
||||||
:s12 => 0xfff, :s12e => 0xfff,
|
|
||||||
:s21e => 0x1ffBff, :s21ee => 0x1ff3ff,
|
|
||||||
:s25e => 0x7feffcf, :s25ee => 0x7fcffcf,
|
|
||||||
|
|
||||||
:@bs9 => 0x7fff, :@bc => 0x1ff, :@bextcext => 0x1C01FF,
|
|
||||||
|
|
||||||
:limm => 0x0, :@limm => 0x0,
|
|
||||||
:@limmc => 0x3f, :@blimm => 0x7,
|
|
||||||
|
|
||||||
:auxlimm => 0x0, :auxs12 => 0xfff,
|
|
||||||
|
|
||||||
:ccond => 0x1f, #condition codes
|
|
||||||
:delay5 => 1, :delay16 => 1,# delay slot
|
|
||||||
:flags15 => 0x1, :flags16 => 0x1,
|
|
||||||
:signext6 => 0x1, :signext16 => 0x1,
|
|
||||||
:cache5 => 0x1, :cache11 => 0x1, :cache16 => 0x1, # data cache mode field
|
|
||||||
:sz1 => 0x3, :sz7 => 0x3, :sz16 => 0x3, :sz17 => 0x3, #data size field
|
|
||||||
:wb3 => 0x3, :wb9 => 0x3, :wb22 => 0x3, #write-back flag
|
|
||||||
:zero => 0x0, :b2 => 0x0, :@ilink1 => 0x0, :@ilink2 => 0x0
|
|
||||||
}
|
|
||||||
#FIXME
|
|
||||||
|
|
||||||
fields_shift32 = {
|
|
||||||
:a => 0x0, :b => 0xC, :bext => 0xC,
|
|
||||||
:c => 0x6, :@c => 0x6, :cext => 0x6, :@cext => 0x6,
|
|
||||||
|
|
||||||
:u6 => 0x6, :u6e =>0x6,
|
|
||||||
:s8e => 15, :s9 => 0x11,
|
|
||||||
:s12 => 0x0, :s12e => 0,
|
|
||||||
:s21e => 0x6, :s21ee => 0x6,
|
|
||||||
:s25e => 0, :s25ee => 0,
|
|
||||||
|
|
||||||
:limm => 0x0, :@limm => 0x0,
|
|
||||||
:@limmc => 0x6, :@blimm => 0x18,
|
|
||||||
|
|
||||||
:auxlimm => 0x0, :auxs12 => 0,
|
|
||||||
|
|
||||||
:@bs9 => 12, :@bc => 6, :@bextcext => 6,
|
|
||||||
|
|
||||||
:ccond => 0, #condition codes
|
|
||||||
:delay5 => 5, :delay16 => 16,# delay slot
|
|
||||||
:flags15 => 15, :flags16 => 16,
|
|
||||||
:signext6 => 6, :signext16 => 16,
|
|
||||||
:cache5 => 5, :cache11 => 11, :cache16 => 16, # data cache mode field
|
|
||||||
:sz1 => 1, :sz7 => 7, :sz16 => 16, :sz17 => 17, #data size field
|
|
||||||
:wb3 => 3, :wb9 => 9, :wb22 => 22, #write-back flag
|
|
||||||
:zero => 0x0, :b2 => 0x0, :@ilink1 => 0, :@ilink2 => 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
@fields_mask = fields_mask16.merge(fields_mask32)
|
|
||||||
@fields_shift = fields_shift16.merge(fields_shift32)
|
|
||||||
|
|
||||||
init_arc_compact16()
|
|
||||||
init_arc_compact32()
|
|
||||||
|
|
||||||
{16 => @opcode_list16, 32 => @opcode_list32}
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_artihm_op(op, majorcode, subcode, *flags)
|
|
||||||
# 0bxxxxxbbb00xxxxxxFBBBCCCCCCAAAAAA
|
|
||||||
addop32 op, 0b00000000000000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :bext, :cext, :flags15
|
|
||||||
# 0bxxxxxbbb01xxxxxxFBBBuuuuuuAAAAAA
|
|
||||||
addop32 op, 0b00000000010000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :b, :u6, :flags15
|
|
||||||
# 0bxxxxxbbb10xxxxxxFBBBssssssSSSSSS
|
|
||||||
addop32 op, 0b00000000100000000000000000000000 | majorcode << 0x1b | subcode << 16, :b, :b2, :s12, :flags15
|
|
||||||
# 0bxxxxxbbb11xxxxxxFBBBCCCCCC0QQQQQ
|
|
||||||
addop32 op, 0b00000000110000000000000000000000 | majorcode << 0x1b | subcode << 16, :b, :b2, :cext, :ccond, :flags15
|
|
||||||
# 0bxxxxxbbb11xxxxxxFBBBuuuuuu1QQQQQ
|
|
||||||
addop32 op, 0b00000000110000000000000000100000 | majorcode << 0x1b | subcode << 16, :b, :b2, :u6, :ccond, :flags15
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_logical_op(op, majorcode, subcode, *flags)
|
|
||||||
# 0b00100bbb00xxxxxxFBBBCCCCCCAAAAAA
|
|
||||||
addop32 op, 0b00100000000000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :bext, :c, :flags15
|
|
||||||
# 0b00100bbb01xxxxxxFBBBuuuuuuAAAAAA
|
|
||||||
addop32 op, 0b00100000010000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :b, :u6, :flags15
|
|
||||||
# 0b00100bbb11xxxxxxFBBBCCCCCC0QQQQQ
|
|
||||||
# WTF
|
|
||||||
addop32 op, 0b00100000110000000000000000000000 | majorcode << 0x1b | subcode << 16, :b, :b2, :c, :ccond, :flags15
|
|
||||||
# 0b00100bbb11xxxxxxFBBBuuuuuu1QQQQQ
|
|
||||||
addop32 op, 0b00100000110000000000000000100000 | majorcode << 0x1b | subcode << 16, :b, :b2, :u6, :ccond, :flags15
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_artihm_op_reduce(op, majorcode, subcode)
|
|
||||||
# 0bxxxxxbbb00101111FBBBCCCCCCxxxxxx
|
|
||||||
addop32 op, 0b00000000001011110000000000000000 | majorcode << 0x1b | subcode, :b, :cext, :flags15
|
|
||||||
# 0bxxxxxbbb01101111FBBBuuuuuuxxxxxx
|
|
||||||
addop32 op, 0b00000000011011110000000000000000 | majorcode << 0x1b | subcode, :b, :u6, :flags15
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_condbranch_op(op, ccond)
|
|
||||||
# 0b00001bbbsssssss1SBBBUUUUUUN0xxxx
|
|
||||||
addop32 op, 0b00001000000000010000000000000000 | ccond, :bext, :cext, :s8e, :setip, :delay5
|
|
||||||
# 0b00001bbbsssssss1SBBBUUUUUUN1xxxx
|
|
||||||
addop32 op, 0b00001000000000010000000000010000 | ccond, :b, :u6, :s8e, :setip, :delay5
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_condjmp_op()
|
|
||||||
# 0b00100RRR1110000D0RRRCCCCCC0QQQQQ
|
|
||||||
addop32 'j', 0b00100000111000000000000000000000, :@cext, :ccond, :setip, :delay16
|
|
||||||
# 0b00100RRR1110000D0RRRuuuuuu1QQQQQ
|
|
||||||
addop32 'j', 0b00100000111000000000000000100000, :u6, :ccond, :setip, :delay16
|
|
||||||
# 0b00100RRR111000001RRR0111010QQQQQ
|
|
||||||
addop32 'j', 0b00100000111000001000011101000000, :@ilink1, :ccond, :setip, :flag_update
|
|
||||||
# 0b00100RRR111000001RRR0111100QQQQQ
|
|
||||||
addop32 'j', 0b00100000111000001000011110000000, :@ilink2, :ccond, :setip, :flag_update
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_condjmplink_op()
|
|
||||||
# 0b00100RRR111000100RRRCCCCCC0QQQQQ
|
|
||||||
addop32 'jl', 0b00100000111000100000000000000000, :@cext, :ccond, :setip, :saveip, :delay16
|
|
||||||
# 0b00100RRR111000100RRRuuuuuu1QQQQQ
|
|
||||||
addop32 'jl', 0b00100000111000100000000000100000, :u6, :ccond, :setip, :saveip, :delay16
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_arc_compact32
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'abs', 0b00100, 0b001001
|
|
||||||
add_artihm_op_reduce 'abss', 0b00101, 0b000101
|
|
||||||
add_artihm_op_reduce 'abssw', 0b00101, 0b000100
|
|
||||||
|
|
||||||
add_artihm_op 'adc', 0b00100, 0b000001
|
|
||||||
add_artihm_op 'add', 0b00100, 0b000000
|
|
||||||
add_artihm_op 'add1', 0b00100, 0b010100
|
|
||||||
add_artihm_op 'add2', 0b00100, 0b010101
|
|
||||||
add_artihm_op 'add3', 0b00100, 0b010110
|
|
||||||
add_artihm_op 'adds', 0b00101, 0b000110
|
|
||||||
add_artihm_op 'addsw', 0b00101, 0b010101, :extended
|
|
||||||
add_artihm_op 'addsdw',0b00101, 0b101000, :extended
|
|
||||||
add_artihm_op 'and' ,0b00100, 0b000100
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'asl', 0b00100, 0b000000
|
|
||||||
|
|
||||||
add_artihm_op 'asl', 0b00101, 0b000000, :extended
|
|
||||||
add_artihm_op 'asls', 0b00101, 0b001010, :extended
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'asr', 0b00100, 0b000001
|
|
||||||
|
|
||||||
add_artihm_op 'asr', 0b00101, 0b000010
|
|
||||||
add_artihm_op 'asrs', 0b00101, 0b001011
|
|
||||||
|
|
||||||
# 0b00001bbbsssssss1SBBBCCCCCCN01110
|
|
||||||
addop32 'bbit0', 0b00001000000000010000000000001110, :b, :c, :s9, :delay5, :setip
|
|
||||||
# 0b00001bbbsssssss1SBBBuuuuuuN11110
|
|
||||||
addop32 'bbit0', 0b00001000000000010000000000011110, :b, :u6, :s9, :delay5, :setip
|
|
||||||
# 0b00001bbbsssssss1SBBBCCCCCCN01111
|
|
||||||
addop32 'bbit1', 0b00001000000000010000000000001111, :b, :c, :s9, :delay5, :setip
|
|
||||||
# 0b00001bbbsssssss1SBBBuuuuuuN11111
|
|
||||||
addop32 'bbit1', 0b00001000000000010000000000011111, :b, :u6, :s9, :delay5, :setip
|
|
||||||
|
|
||||||
# 0b00000ssssssssss0SSSSSSSSSSNQQQQQ
|
|
||||||
addop32 'b', 0b00000000000000000000000000000000, :s21e, :ccond, :delay5, :setip
|
|
||||||
# 0b00000ssssssssss1SSSSSSSSSSNRtttt
|
|
||||||
addop32 'b', 0b00000000000000010000000000000000, :s25e, :delay5, :setip, :stopexec
|
|
||||||
# WTF: unknown encoding, bit 5 should be reserved
|
|
||||||
addop32 'b', 0b00000000000000010000000000010000, :s25e, :delay5, :setip, :stopexec
|
|
||||||
|
|
||||||
add_logical_op 'bclr', 0b00100, 0b010000
|
|
||||||
add_artihm_op 'bic', 0b00100, 0b000110
|
|
||||||
|
|
||||||
# 0b00001sssssssss00SSSSSSSSSSNQQQQQ
|
|
||||||
addop32 'bl', 0b00001000000000000000000000000000, :s21ee, :ccond, :delay5, :setip, :saveip
|
|
||||||
# 0b00001sssssssss10SSSSSSSSSSNRtttt
|
|
||||||
addop32 'bl', 0b00001000000000100000000000000000, :s25ee, :delay5, :setip, :saveip, :stopexec
|
|
||||||
|
|
||||||
add_logical_op 'bmsk', 0b00100, 0b010011
|
|
||||||
|
|
||||||
add_condbranch_op 'breq', 0b0000
|
|
||||||
add_condbranch_op 'brne', 0b0001
|
|
||||||
add_condbranch_op 'brlt', 0b0010
|
|
||||||
add_condbranch_op 'brge', 0b0011
|
|
||||||
add_condbranch_op 'brlo', 0b0100
|
|
||||||
add_condbranch_op 'brhs', 0b0101
|
|
||||||
|
|
||||||
addop32 'brk', 0b00100101011011110000000000111111, :stopexec
|
|
||||||
|
|
||||||
add_logical_op 'bset', 0b00100, 0b001111
|
|
||||||
|
|
||||||
# 0b00100bbb110100011BBBCCCCCC0QQQQQ
|
|
||||||
addop32 'btst', 0b00100000110100011000000000000000, :bext, :c, :ccond
|
|
||||||
# 0b00100bbb110100011BBBuuuuuu1QQQQQ
|
|
||||||
addop32 'btst', 0b00100000110100011000000000100000, :b, :u6, :ccond
|
|
||||||
# WTF 0b00100bbb010100011BBBuuuuuu0QQQQQ
|
|
||||||
addop32 'btst', 0b00100000010100011000000000000000, :b, :u6, :ccond
|
|
||||||
|
|
||||||
add_logical_op 'bxor', 0b00100, 0b010010
|
|
||||||
|
|
||||||
# 0b00100bbb100011001BBBssssssSSSSSS
|
|
||||||
addop32 'cmp', 0b00100000100011001000000000000000, :b, :s12
|
|
||||||
# WTF unknown encoding ...
|
|
||||||
# 0b00100bbb010011001BBBssssssSSSSSS
|
|
||||||
addop32 'cmp', 0b00100000010011001000000000000000, :b, :s12
|
|
||||||
# 0b00100bbb110011001BBBuuuuuu1QQQQQ
|
|
||||||
addop32 'cmp', 0b00100000110011001000000000100000, :b, :u6, :ccond
|
|
||||||
# WTF unknown encoding ...
|
|
||||||
# 0b00100bbb010011001BBBssssssSSSSSS
|
|
||||||
addop32 'cmp', 0b00100000000011001000000000000000, :bext, :cext, :ccond
|
|
||||||
# 0b00100bbb110011001BBBCCCCCC0QQQQQ
|
|
||||||
addop32 'cmp', 0b00100000110011001000000000000000, :bext, :cext, :ccond
|
|
||||||
|
|
||||||
add_artihm_op 'divaw', 0b00101, 0b001000, :extended
|
|
||||||
|
|
||||||
# 0b00100bbb00101111DBBBCCCCCC001100
|
|
||||||
addop32 'ex', 0b00100000001011110000000000001100, :b, :@cext, :cache16
|
|
||||||
# 0b00100bbb01101111DBBBuuuuuu001100
|
|
||||||
addop32 'ex', 0b00100000011011110000000000001100, :b, :@u6, :cache16
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'extb', 0b00100, 0b000111
|
|
||||||
add_artihm_op_reduce 'extw', 0b00100, 0b001000
|
|
||||||
|
|
||||||
# WTF unknown encoding ...
|
|
||||||
# 0b00100rrr111010010RRRCCCCCC0QQQQQ
|
|
||||||
addop32 'flag', 0b00100000001010010000000000000000, :cext, :ccond, :flag_update
|
|
||||||
# 0b00100rrr111010010RRRuuuuuu1QQQQQ
|
|
||||||
addop32 'flag', 0b00100000001010010000000000100000, :u6, :ccond, :flag_update
|
|
||||||
# 0b00100rrr101010010RRRssssssSSSSSS
|
|
||||||
addop32 'flag', 0b00100000011010010000000000000000, :s12, :flag_update
|
|
||||||
|
|
||||||
add_condjmp_op()
|
|
||||||
add_condjmplink_op()
|
|
||||||
|
|
||||||
# 0b00100RRR001000000RRRCCCCCCRRRRRR
|
|
||||||
addop32 'j', 0b00100000001000000000000000000000, :@cext, :delay16, :setip, :stopexec
|
|
||||||
# 0b00100RRR011000000RRRuuuuuuRRRRRR
|
|
||||||
addop32 'j', 0b00100000011000000000000000000000, :u6, :delay16, :setip, :stopexec
|
|
||||||
# 0b00100RRR101000000RRRssssssSSSSSS
|
|
||||||
addop32 'j', 0b00100000101000000000000000000000, :s12, :delay16, :setip, :stopexec
|
|
||||||
# 0b00100RRR001000001RRR011101RRRRRR
|
|
||||||
addop32 'j.f', 0b00100000001000001000011101000000, :@ilink1, :flag_update, :setip, :stopexec
|
|
||||||
# 0b00100RRR001000001RRR011110RRRRRR
|
|
||||||
addop32 'j.f', 0b00100000001000001000011110000000, :@ilink2, :flag_update, :setip, :stopexec
|
|
||||||
|
|
||||||
# 0b00100RRR0010001D0RRRCCCCCCRRRRRR
|
|
||||||
addop32 'jl', 0b00100000001000100000000000000000, :@cext, :delay16, :setip, :saveip, :stopexec
|
|
||||||
# 0b00100RRR0110001D0RRRuuuuuuRRRRRR
|
|
||||||
addop32 'jl', 0b00100000011000100000000000000000, :u6, :delay16, :setip, :saveip, :stopexec
|
|
||||||
# 0b00100RRR1010001D0RRRssssssSSSSSS
|
|
||||||
addop32 'jl', 0b00100000101000100000000000000000, :s12, :delay16, :setip, :saveip, :stopexec
|
|
||||||
|
|
||||||
# 0b00010bbbssssssssSBBBDaaZZXAAAAAA
|
|
||||||
addop32 'ld', 0b00010000000000000000000000000000, :a, :@bs9, :sz7, :signext6, :wb9, :cache11
|
|
||||||
|
|
||||||
# 0b00100bbbaa110ZZXDBBBCCCCCCAAAAAA
|
|
||||||
addop32 'ld', 0b00100000001100000000000000000000, :a, :@bextcext, :sz17, :signext16, :wb22, :cache11
|
|
||||||
|
|
||||||
# 0b00100RRR111010000RRRuuuuuu1QQQQQ
|
|
||||||
addop32 'lp', 0b00100000111010000000000000100000, :u6e, :ccond, :setip
|
|
||||||
# 0b00100RRR101010000RRRssssssSSSSSS
|
|
||||||
addop32 'lp', 0b00100000101010000000000000000000, :s12e, :setip
|
|
||||||
|
|
||||||
# 0b00100bbb001010100BBBCCCCCCRRRRRR
|
|
||||||
addop32 'lr', 0b00100000101010100000000000000000, :b, :@c
|
|
||||||
# 0b00100bbb001010100BBB111110RRRRRR
|
|
||||||
addop32 'lr', 0b00100000001010100000111110000000, :b, :auxlimm
|
|
||||||
# 0b00100bbb101010100BBBssssssSSSSSS
|
|
||||||
addop32 'lr', 0b00100000011010100000000000000000, :b, :auxs12
|
|
||||||
# WTF unknown encoding ...
|
|
||||||
# 0b00100bbb101010100BBBssssssSSSSSS
|
|
||||||
addop32 'lr', 0b00100000101010100000000000000000, :b, :auxs12
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'lsr', 0b00100, 0b000010
|
|
||||||
|
|
||||||
add_artihm_op 'lsr', 0b00101, 0b000001
|
|
||||||
add_artihm_op 'max', 0b00100, 0b001000
|
|
||||||
add_artihm_op 'min', 0b00100, 0b001001
|
|
||||||
|
|
||||||
# 0b00100bbb10001010FBBBssssssSSSSSS
|
|
||||||
addop32 'mov', 0b00100000100010100000000000000000, :b, :s12, :flags15
|
|
||||||
# WTF unknown encoding ...
|
|
||||||
# 0b00100bbb01001010FBBBssssssSSSSSS
|
|
||||||
addop32 'mov', 0b00100000010010100000000000000000, :b, :s12, :flags15
|
|
||||||
# 0b00100bbb11001010FBBBCCCCCC0QQQQQ
|
|
||||||
addop32 'mov', 0b00100000110010100000000000000000, :b, :cext, :ccond , :flags15
|
|
||||||
# WTF unknown encoding ..
|
|
||||||
# 0b00100bbb00001010FBBBCCCCCC0QQQQQ
|
|
||||||
addop32 'mov', 0b00100000000010100000000000000000, :b, :cext, :ccond , :flags15
|
|
||||||
# 0b00100bbb11001010FBBBuuuuuu1QQQQQ
|
|
||||||
addop32 'mov', 0b00100000110010100000000000100000, :b, :u6, :ccond , :flags15
|
|
||||||
|
|
||||||
add_artihm_op 'mpy', 0b00100, 0b011010, :extended
|
|
||||||
add_artihm_op 'mpyh', 0b00100, 0b011011, :extended
|
|
||||||
add_artihm_op 'mpyhu', 0b00100, 0b011100, :extended
|
|
||||||
add_artihm_op 'mpyu', 0b00100, 0b011101, :extended
|
|
||||||
|
|
||||||
# WTF: neg instruction is not differenciated from a rsub :a, :b, :u6
|
|
||||||
# : 0b00100bbb01001110FBBB000000AAAAAA
|
|
||||||
#addop32 'neg', 0b00100000010011100000000000000000, :a, :b, :flags15
|
|
||||||
|
|
||||||
# WTF: neg instruction is not differenciated from a rsub :b, :b2, :u6
|
|
||||||
# 0b00100bbb11001110FBBB0000001QQQQQ
|
|
||||||
#addop32 'neg', 0b00100000110011100000000000100000, :b, :b2, :ccond , :flags15
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'negs', 0b00101, 0b000111
|
|
||||||
add_artihm_op_reduce 'negsw', 0b00101, 0b000110
|
|
||||||
|
|
||||||
# nop is an alias over mov null, 0 (mov - [:b, :s12, :flags15])
|
|
||||||
addop32 'nop', 0b00100110010010100111000000000000
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'norm', 0b00101, 0b000001
|
|
||||||
add_artihm_op_reduce 'normw', 0b00101, 0b001000
|
|
||||||
add_artihm_op_reduce 'not', 0b00100, 0b001010
|
|
||||||
|
|
||||||
add_artihm_op 'or', 0b00100, 0b000101
|
|
||||||
|
|
||||||
# 0b00010bbbssssssssSBBB0aa000111110
|
|
||||||
addop32 'prefetch', 0b00010000000000000000000000111110, :@bs9, :wb
|
|
||||||
# 0b00100bbbaa1100000BBBCCCCCC111110
|
|
||||||
addop32 'prefetch', 0b00100000001100000000000000111110, :@bextcext, :wb22
|
|
||||||
|
|
||||||
# 0b00100bbb100011011BBBssssssSSSSSS
|
|
||||||
addop32 'rcmp', 0b00100000100011011000000000000000, :b, :s12
|
|
||||||
# 0b00100bbb110011011BBBCCCCCC0QQQQQ
|
|
||||||
addop32 'rcmp', 0b00100000110011011000000000000000, :bext, :cext, :ccond
|
|
||||||
# 0b00100bbb110011011BBBuuuuuu1QQQQQ
|
|
||||||
addop32 'rcmp', 0b00100000110011011000000000100000, :b, :u6, :ccond
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'rlc', 0b00100, 0b001011
|
|
||||||
add_artihm_op_reduce 'rnd16', 0b00101, 0b000011
|
|
||||||
add_artihm_op_reduce 'ror', 0b00100, 0b000011
|
|
||||||
|
|
||||||
add_artihm_op 'ror', 0b00101, 0b000011, :extended
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'rrc', 0b00100, 0b000100
|
|
||||||
|
|
||||||
add_artihm_op 'rsub', 0b00100, 0b001110
|
|
||||||
|
|
||||||
addop32 'rtie', 0b00100100011011110000000000111111, :setip, :stopexec
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'sat16', 0b00101, 0b000010
|
|
||||||
|
|
||||||
add_artihm_op 'sbc', 0b00100, 0b000011
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'sexb', 0b00100, 0b000101
|
|
||||||
add_artihm_op_reduce 'sexbw', 0b00100, 0b000110
|
|
||||||
|
|
||||||
# 0b00100001011011110000uuuuuu111111
|
|
||||||
addop32 'sleep', 0b00100001011011110000000000111111, :u6
|
|
||||||
|
|
||||||
# 0b00100bbb001010110BBBCCCCCCRRRRRR
|
|
||||||
addop32 'sr', 0b00100000001010110000000000000000, :bext, :@cext
|
|
||||||
# 0b00100110101010110111CCCCCCRRRRRR
|
|
||||||
addop32 'sr', 0b00100000101010110000000000000000, :bext, :auxs12
|
|
||||||
# WTF: unknown encoding
|
|
||||||
addop32 'sr', 0b00100000011010110000000000000000, :bext, :auxs12
|
|
||||||
|
|
||||||
# 0b00011bbbssssssssSBBBCCCCCCDaaZZR
|
|
||||||
addop32 'st', 0b00011000000000000000000000000000, :cext, :@bs9, :sz1, :wb3, :cache5
|
|
||||||
|
|
||||||
add_artihm_op 'sub', 0b00100, 0b000010
|
|
||||||
add_artihm_op 'sub1', 0b00100, 0b010111
|
|
||||||
add_artihm_op 'sub2', 0b00100, 0b011000
|
|
||||||
add_artihm_op 'sub3', 0b00100, 0b011001
|
|
||||||
|
|
||||||
# WTF: same encoding as xor instructions
|
|
||||||
#add_artihm_op 'subs', 0b00100, 0b000111
|
|
||||||
|
|
||||||
add_artihm_op 'subsdw', 0b00101, 0b101001, :extended
|
|
||||||
|
|
||||||
add_artihm_op_reduce 'swap', 0b00101, 0b000000
|
|
||||||
|
|
||||||
addop32 'swi', 0b00100010011011110000000000111111, :setip, :stopexec
|
|
||||||
addop32 'sync', 0b00100011011011110000000000111111
|
|
||||||
|
|
||||||
# 0b00100bbb100010111BBBssssssSSSSSS
|
|
||||||
addop32 'tst', 0b00100000100010111000000000000000, :b, :s12
|
|
||||||
# 0b00100bbb110010111BBBCCCCCC0QQQQQ
|
|
||||||
addop32 'tst', 0b00100000110010111000000000000000, :bext, :cext, :ccond
|
|
||||||
# 0b00100bbb110010111BBBuuuuuu1QQQQQ
|
|
||||||
addop32 'tst', 0b00100000110010111000000000100000, :b, :u6, :ccond
|
|
||||||
|
|
||||||
add_artihm_op 'xor', 0b00100, 0b000111
|
|
||||||
end
|
|
||||||
|
|
||||||
# ARCompact 16-bit instructions
|
|
||||||
def init_arc_compact16
|
|
||||||
addop16 'abs_s', 0x7811, :cb, :cc
|
|
||||||
addop16 'add_s', 0x6018, :ca, :cb, :cc
|
|
||||||
addop16 'add_s', 0x7000, :cb, :cb2, :ch
|
|
||||||
addop16 'add_s', 0x6800, :cc, :cb, :cu3
|
|
||||||
addop16 'add_s', 0xe000, :cb, :cb2, :cu7
|
|
||||||
|
|
||||||
# same encoding as add_s b,b,h
|
|
||||||
#addop16 'add_s', 0x70c7, :cb, :cb2, :climm
|
|
||||||
|
|
||||||
addop16 'add_s', 0xc080, :cb, :sp, :cu5ee
|
|
||||||
addop16 'add_s', 0xc0a0, :sp, :sp2, :cu5ee
|
|
||||||
addop16 'add_s', 0xce00, :cr0, :gp, :cs9
|
|
||||||
addop16 'add1_s', 0x7814, :cb, :cb2, :cc
|
|
||||||
addop16 'add2_s', 0x7815, :cb, :cb2, :cc
|
|
||||||
addop16 'add3_s', 0x7816, :cb, :cb2, :cc
|
|
||||||
addop16 'and_s', 0x7804, :cb, :cb2, :cc
|
|
||||||
addop16 'asl_s', 0x7818, :cb, :cb2, :cc
|
|
||||||
addop16 'asl_s', 0x6810, :cc, :cb, :cu3
|
|
||||||
addop16 'asl_s', 0xb800, :cb, :cb2, :cu5
|
|
||||||
addop16 'asl_s', 0x781b, :cb, :cc
|
|
||||||
addop16 'asr_s', 0x781a, :cb, :cb2, :cc
|
|
||||||
addop16 'asr_s', 0x6818, :cc, :cb, :cu3
|
|
||||||
addop16 'asr_s', 0xb840, :cb, :cb2, :cu5
|
|
||||||
addop16 'asr_s', 0x781c, :cb, :cc
|
|
||||||
addop16 'b_s', 0xf000, :cdisps10, :setip, :stopexec
|
|
||||||
addop16 'beq_s', 0xf200, :cdisps10, :setip
|
|
||||||
addop16 'bne_s', 0xf400, :cdisps10, :setip
|
|
||||||
addop16 'bgt_s', 0xf600, :cdisps7, :setip
|
|
||||||
addop16 'bge_s', 0xf640, :cdisps7, :setip
|
|
||||||
addop16 'blt_s', 0xf680, :cdisps7, :setip
|
|
||||||
addop16 'ble_s', 0xf6c0, :cdisps7, :setip
|
|
||||||
addop16 'bhi_s', 0xf700, :cdisps7, :setip
|
|
||||||
addop16 'bhs_s', 0xf740, :cdisps7, :setip
|
|
||||||
addop16 'blo_s', 0xf780, :cdisps7, :setip
|
|
||||||
addop16 'bls_s', 0xf7c0, :cdisps7, :setip
|
|
||||||
addop16 'bclr_s', 0xb8a0, :cb, :cb2, :cu5
|
|
||||||
addop16 'bic_s', 0x7806, :cb, :cb2, :cc
|
|
||||||
addop16 'bl_s', 0xf800, :cdisps13, :setip, :saveip, :stopexec
|
|
||||||
addop16 'bmsk_s', 0xb8c0, :cb, :cb2, :cu5
|
|
||||||
addop16 'breq_s', 0xe800, :cb, :zero, :cdisps8, :setip
|
|
||||||
addop16 'brne_s', 0xe880, :cb, :zero, :cdisps8, :setip
|
|
||||||
addop16 'brk_s', 0x7fff
|
|
||||||
addop16 'bset_s', 0xb880, :cb, :cb2, :cu5
|
|
||||||
addop16 'btst_s', 0xb8e0, :cb, :cu5
|
|
||||||
addop16 'cmp_s', 0x7010, :cb, :ch
|
|
||||||
addop16 'cmp_s', 0xe080, :cb, :cu7
|
|
||||||
|
|
||||||
# encoded over cmp_s b,h
|
|
||||||
# addop16 'cmp_s', 0x70d7, :cb, :limm
|
|
||||||
|
|
||||||
addop16 'extb_s', 0x780f, :cb, :cc
|
|
||||||
addop16 'extw_s', 0x7810, :cb, :cc
|
|
||||||
addop16 'j_s', 0x7800, :@cb, :setip, :stopexec
|
|
||||||
addop16 'j_s.d', 0x7820, :@cb, :setip, :stopexec, :delay_slot
|
|
||||||
addop16 'j_s', 0x7ee0, :@blink, :setip, :stopexec
|
|
||||||
addop16 'j_s.d', 0x7fe0, :@blink, :setip, :stopexec, :delay_slot
|
|
||||||
addop16 'jeq_s', 0x7ce0, :@blink, :setip
|
|
||||||
addop16 'jne_s', 0x7de0, :@blink, :setip
|
|
||||||
addop16 'jl_s', 0x7840, :@cb, :setip, :saveip, :stopexec
|
|
||||||
addop16 'jl_s.d', 0x7860, :@cb, :setip, :saveip, :stopexec, :delay_slot
|
|
||||||
addop16 'ld_s', 0x6000, :ca, :@cbcc
|
|
||||||
addop16 'ldb_s', 0x6008, :ca, :@cbcc
|
|
||||||
addop16 'ldw_s', 0x6010, :ca, :@cbcc
|
|
||||||
addop16 'ld_s', 0x8000, :cc, :@cbu7
|
|
||||||
addop16 'ldb_s', 0x8800, :cc, :@cbu5
|
|
||||||
addop16 'ldw_s', 0x9000, :cc, :@cbu6
|
|
||||||
addop16 'ldw_s.x', 0x9800, :cc, :@cbu6
|
|
||||||
addop16 'ld_s', 0xc000, :cb, :@cspu7
|
|
||||||
addop16 'ldb_s', 0xc020, :cb, :@cspu7
|
|
||||||
addop16 'ld_s', 0xc800, :cr0, :@gps11
|
|
||||||
addop16 'ldb_s', 0xca00, :cr0, :@gps9
|
|
||||||
addop16 'ldw_s', 0xcc00, :cr0, :@gps10
|
|
||||||
addop16 'ld_s', 0xd000, :cb, :@pclu10
|
|
||||||
|
|
||||||
# FIXME: exact same encoding as asl_s instructions
|
|
||||||
#addop16 'lsl_s', 0x7818, :cb, :cb2, :cc
|
|
||||||
#addop16 'lsl_s', 0x6810, :cc, :cb, :cu3
|
|
||||||
#addop16 'lsl_s', 0xb800, :cb, :cb2, :cu5
|
|
||||||
#addop16 'lsl_s', 0x781d, :cb, :cc
|
|
||||||
|
|
||||||
addop16 'lsr_s', 0x7819, :cb, :cb2, :cc
|
|
||||||
addop16 'lsr_s', 0xb820, :cb, :cb2, :cu5
|
|
||||||
addop16 'lsr_s', 0x781d, :cb, :cc
|
|
||||||
addop16 'mov_s', 0x7008, :cb, :ch
|
|
||||||
|
|
||||||
# FIXME: same encoding as previous instruction
|
|
||||||
#addop16 'mov_s', 0x70cf, :cb, :limm
|
|
||||||
|
|
||||||
addop16 'mov_s', 0xd800, :cb, :cu8
|
|
||||||
addop16 'mov_s', 0x7018, :ch, :cb
|
|
||||||
|
|
||||||
# TODO seems to overlap with previous instruction
|
|
||||||
addop16 'mov_s', 0x70df, :zero, :cb
|
|
||||||
addop16 'mul64_s', 0x780c, :zero, :cb, :cc
|
|
||||||
addop16 'neg_s', 0x7813, :cb, :cc
|
|
||||||
addop16 'not_s', 0x7812, :cb, :cc
|
|
||||||
addop16 'nop_s',0x78e0
|
|
||||||
addop16 'unimp_s', 0x79e0
|
|
||||||
addop16 'or_s', 0x7805, :cb, :cb2, :cc
|
|
||||||
addop16 'pop_s', 0xc0c1, :cb
|
|
||||||
addop16 'pop_s', 0xc0d1, :blink
|
|
||||||
addop16 'push_s', 0xc0e1, :cb
|
|
||||||
addop16 'push_s', 0xc0f1, :blink
|
|
||||||
addop16 'sexb_s', 0x780d, :cb, :cc
|
|
||||||
addop16 'sexw_s', 0x780e, :cb, :cc
|
|
||||||
addop16 'st_s', 0xc040, :cb, :@cspu7
|
|
||||||
addop16 'stb_s', 0xc060, :cb, :@cspu7
|
|
||||||
addop16 'st_s', 0xa000, :cc, :@cbu7
|
|
||||||
addop16 'stb_s', 0xa800, :cc, :@cbu5
|
|
||||||
addop16 'stw_s', 0xb000, :cc, :@cbu6
|
|
||||||
addop16 'sub_s', 0x7802, :cb, :cb2, :cc
|
|
||||||
addop16 'sub_s', 0x6808, :cc, :cb, :cu3
|
|
||||||
addop16 'sub_s', 0xb860, :cb, :cb2, :cu5
|
|
||||||
addop16 'sub_s', 0xc1a0, :sp, :sp2, :cu5ee
|
|
||||||
addop16 'sub_s.ne', 0x78c0, :cb, :c2, :cb3
|
|
||||||
addop16 'trap_s', 0x781E, :cu6, :setip, :stopexec
|
|
||||||
addop16 'tst_s', 0x780b, :cb, :cc
|
|
||||||
addop16 'xor_s', 0x7807, :cb, :cb2, :cc
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,14 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
class Metasm::ARM < Metasm::CPU
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/arm/parse'
|
|
||||||
require 'metasm/cpu/arm/encode'
|
|
||||||
require 'metasm/cpu/arm/decode'
|
|
||||||
require 'metasm/cpu/arm/render'
|
|
||||||
require 'metasm/cpu/arm/debug'
|
|
@ -1,39 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/arm/opcodes'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARM
|
|
||||||
def dbg_register_pc
|
|
||||||
@dbg_register_pc ||= :pc
|
|
||||||
end
|
|
||||||
def dbg_register_flags
|
|
||||||
@dbg_register_flags ||= :flags
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_list
|
|
||||||
@dbg_register_list ||= [:r0, :r1, :r2, :r3, :r4, :r5, :r6, :r7, :r8, :r9, :r10, :r11, :r12, :sp, :lr, :pc]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_flag_list
|
|
||||||
@dbg_flag_list ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_size
|
|
||||||
@dbg_register_size ||= Hash.new(32)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_need_stepover(dbg, addr, di)
|
|
||||||
di and di.opcode.props[:saveip]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_end_stepout(dbg, addr, di)
|
|
||||||
di and di.opcode.name == 'foobar' # TODO
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,168 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/arm/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARM
|
|
||||||
# create the bin_mask for a given opcode
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
|
|
||||||
op.bin_mask = 0
|
|
||||||
op.fields.each { |k, (m, s)|
|
|
||||||
op.bin_mask |= m << s
|
|
||||||
}
|
|
||||||
op.bin_mask = 0xffffffff ^ op.bin_mask
|
|
||||||
end
|
|
||||||
|
|
||||||
# create the lookaside hash from the first byte of the opcode
|
|
||||||
def build_bin_lookaside
|
|
||||||
lookaside = Array.new(256) { [] }
|
|
||||||
|
|
||||||
opcode_list.each { |op|
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
|
|
||||||
b = (op.bin >> 20) & 0xff
|
|
||||||
msk = (op.bin_mask >> 20) & 0xff
|
|
||||||
b &= msk
|
|
||||||
|
|
||||||
for i in b..(b | (255^msk))
|
|
||||||
lookaside[i] << op if i & msk == b
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
return if edata.ptr+4 > edata.length
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
val = edata.decode_imm(:u32, @endianness)
|
|
||||||
di.instance_variable_set('@raw', val)
|
|
||||||
di if di.opcode = @bin_lookaside[(val >> 20) & 0xff].find { |op|
|
|
||||||
(not op.props[:cond] or
|
|
||||||
((val >> @fields_shift[:cond]) & @fields_mask[:cond]) != 0xf) and
|
|
||||||
(op.bin & op.bin_mask) == (val & op.bin_mask)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def disassembler_default_func
|
|
||||||
df = DecodedFunction.new
|
|
||||||
df
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
val = di.instance_variable_get('@raw')
|
|
||||||
|
|
||||||
field_val = lambda { |f|
|
|
||||||
r = (val >> @fields_shift[f]) & @fields_mask[f]
|
|
||||||
case f
|
|
||||||
when :i12; Expression.make_signed(r, 12)
|
|
||||||
when :i24; Expression.make_signed(r, 24)
|
|
||||||
when :i8_12; ((r >> 4) & 0xf0) | (r & 0xf)
|
|
||||||
when :stype; [:lsl, :lsr, :asr, :ror][r]
|
|
||||||
when :u; [:-, :+][r]
|
|
||||||
else r
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if op.props[:cond]
|
|
||||||
cd = %w[eq ne cs cc mi pl vs vc hi ls ge lt gt le al][field_val[:cond]]
|
|
||||||
if cd != 'al'
|
|
||||||
di.opcode = di.opcode.dup
|
|
||||||
di.instruction.opname = di.opcode.name.dup
|
|
||||||
di.instruction.opname[(op.props[:cond_name_off] || di.opcode.name.length), 0] = cd
|
|
||||||
if di.opcode.props[:stopexec]
|
|
||||||
di.opcode.props = di.opcode.props.dup
|
|
||||||
di.opcode.props.delete :stopexec
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :rd, :rn, :rm; Reg.new field_val[a]
|
|
||||||
when :rm_rs; Reg.new field_val[:rm], field_val[:stype], Reg.new(field_val[:rs])
|
|
||||||
when :rm_is; Reg.new field_val[:rm], field_val[:stype], field_val[:shifti]
|
|
||||||
when :i12; Expression[field_val[a]]
|
|
||||||
when :i24; Expression[field_val[a] << 2]
|
|
||||||
when :i8_r
|
|
||||||
i = field_val[:i8]
|
|
||||||
r = field_val[:rotate]*2
|
|
||||||
Expression[((i >> r) | (i << (32-r))) & 0xffff_ffff]
|
|
||||||
when :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12
|
|
||||||
b = Reg.new(field_val[:rn])
|
|
||||||
o = case a
|
|
||||||
when :mem_rn_rm; Reg.new(field_val[:rm])
|
|
||||||
when :mem_rn_i8_12; field_val[:i8_12]
|
|
||||||
when :mem_rn_rms; Reg.new(field_val[:rm], field_val[:stype], field_val[:shifti])
|
|
||||||
when :mem_rn_i12; field_val[:i12]
|
|
||||||
end
|
|
||||||
Memref.new(b, o, field_val[:u], op.props[:baseincr])
|
|
||||||
when :reglist
|
|
||||||
di.instruction.args.last.updated = true if op.props[:baseincr]
|
|
||||||
msk = field_val[a]
|
|
||||||
l = RegList.new((0..15).map { |n| Reg.new(n) if (msk & (1 << n)) > 0 }.compact)
|
|
||||||
l.usermoderegs = true if op.props[:usermoderegs]
|
|
||||||
l
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length = 4
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
if di.opcode.args[-1] == :i24
|
|
||||||
di.instruction.args[-1] = Expression[di.instruction.args[-1] + addr + 8]
|
|
||||||
end
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Reg; arg.symbolic
|
|
||||||
when Memref; arg.symbolic(di.address)
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.name]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
# assume nothing except the 1st arg is modified
|
|
||||||
case a[0]
|
|
||||||
when Indirection, Symbol; { a[0] => Expression::Unknown }
|
|
||||||
when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
|
|
||||||
else {}
|
|
||||||
end.update(:incomplete_binding => Expression[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
if di.opcode.props[:setip]
|
|
||||||
[di.instruction.args.last]
|
|
||||||
else
|
|
||||||
# TODO ldr pc, ..
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,92 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/arm/opcodes'
|
|
||||||
require 'metasm/encode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARM
|
|
||||||
def encode_instr_op(program, instr, op)
|
|
||||||
base = op.bin
|
|
||||||
set_field = lambda { |f, v|
|
|
||||||
v = v.reduce if v.kind_of?(Expression)
|
|
||||||
case f
|
|
||||||
when :i8_12
|
|
||||||
base = Expression[base, :|, [[v, :&, 0xf], :|, [[v, :<<, 4], :&, 0xf00]]]
|
|
||||||
next
|
|
||||||
when :stype; v = [:lsl, :lsr, :asr, :ror].index(v)
|
|
||||||
when :u; v = [:-, :+].index(v)
|
|
||||||
end
|
|
||||||
base = Expression[base, :|, [[v, :&, @fields_mask[f]], :<<, @fields_shift[f]]]
|
|
||||||
}
|
|
||||||
|
|
||||||
val, mask, shift = 0, 0, 0
|
|
||||||
|
|
||||||
if op.props[:cond]
|
|
||||||
coff = op.props[:cond_name_off] || op.name.length
|
|
||||||
cd = instr.opname[coff, 2]
|
|
||||||
cdi = %w[eq ne cs cc mi pl vs vc hi ls ge lt gt le al].index(cd) || 14 # default = al
|
|
||||||
set_field[:cond, cdi]
|
|
||||||
end
|
|
||||||
|
|
||||||
op.args.zip(instr.args).each { |sym, arg|
|
|
||||||
case sym
|
|
||||||
when :rd, :rs, :rn, :rm; set_field[sym, arg.i]
|
|
||||||
when :rm_rs
|
|
||||||
set_field[:rm, arg.i]
|
|
||||||
set_field[:stype, arg.stype]
|
|
||||||
set_field[:rs, arg.shift.i]
|
|
||||||
when :rm_is
|
|
||||||
set_field[:rm, arg.i]
|
|
||||||
set_field[:stype, arg.stype]
|
|
||||||
set_field[:shifti, arg.shift]
|
|
||||||
when :mem_rn_rm, :mem_rn_rms, :mem_rn_i8_12, :mem_rn_i12
|
|
||||||
set_field[:rn, arg.base.i]
|
|
||||||
case sym
|
|
||||||
when :mem_rn_rm
|
|
||||||
set_field[:rm, arg.offset.i]
|
|
||||||
when :mem_rn_rms
|
|
||||||
set_field[:rm, arg.offset.i]
|
|
||||||
set_field[:stype, arg.offset.stype]
|
|
||||||
set_field[:rs, arg.offset.shift.i]
|
|
||||||
when :mem_rn_i8_12
|
|
||||||
set_field[:i8_12, arg.offset]
|
|
||||||
when :mem_rn_i12
|
|
||||||
set_field[:i12, arg.offset]
|
|
||||||
end
|
|
||||||
# TODO set_field[:u] etc
|
|
||||||
when :reglist
|
|
||||||
set_field[sym, arg.list.inject(0) { |rl, r| rl | (1 << r.i) }]
|
|
||||||
when :i8_r
|
|
||||||
b = arg.reduce & 0xffffffff
|
|
||||||
r = (0..15).find {
|
|
||||||
next true if b < 0x100
|
|
||||||
b = ((b << 2) & 0xffff_ffff) | ((b >> 30) & 3)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
raise EncodeError, "Invalid constant" if not r
|
|
||||||
set_field[:i8, b]
|
|
||||||
set_field[:rotate, r]
|
|
||||||
when :i12, :i24
|
|
||||||
val, mask, shift = arg, @fields_mask[sym], @fields_shift[sym]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if op.args[-1] == :i24
|
|
||||||
# convert label name for branch to relative offset
|
|
||||||
label = program.new_label('l_'+op.name)
|
|
||||||
target = val
|
|
||||||
target = target.rexpr if target.kind_of?(Expression) and target.op == :+ and not target.lexpr
|
|
||||||
val = Expression[[target, :-, [label, :+, 8]], :>>, 2]
|
|
||||||
|
|
||||||
EncodedData.new('', :export => { label => 0 }) <<
|
|
||||||
Expression[base, :|, [[val, :<<, shift], :&, mask]].encode(:u32, @endianness)
|
|
||||||
else
|
|
||||||
Expression[base, :|, [[val, :<<, shift], :&, mask]].encode(:u32, @endianness)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,72 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARM < CPU
|
|
||||||
class Reg
|
|
||||||
class << self
|
|
||||||
attr_accessor :s_to_i, :i_to_s
|
|
||||||
end
|
|
||||||
@i_to_s = %w[r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 sp lr pc]
|
|
||||||
@s_to_i = { 'wr' => 7, 'sb' => 9, 'sl' => 10, 'fp' => 11, 'ip' => 12, 'sp' => 13, 'lr' => 14, 'pc' => 15 }
|
|
||||||
15.times { |i| @s_to_i["r#{i}"] = i }
|
|
||||||
4.times { |i| @s_to_i["a#{i+1}"] = i }
|
|
||||||
8.times { |i| @s_to_i["v#{i+1}"] = i+4 }
|
|
||||||
|
|
||||||
attr_accessor :i, :stype, :shift, :updated
|
|
||||||
def initialize(i, stype=:lsl, shift=0)
|
|
||||||
@i = i
|
|
||||||
@stype = stype
|
|
||||||
@shift = shift
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic
|
|
||||||
r = self.class.i_to_s[@i].to_sym
|
|
||||||
if @stype == :lsl and @shift == 0
|
|
||||||
r
|
|
||||||
else
|
|
||||||
r # TODO shift/rotate/...
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
attr_accessor :base, :offset, :sign, :incr
|
|
||||||
def initialize(base, offset, sign=:+, incr=nil)
|
|
||||||
@base, @offset, @sign, @incr = base, offset, sign, incr
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(len=4, orig=nil)
|
|
||||||
o = @offset
|
|
||||||
o = o.symbolic if o.kind_of? Reg
|
|
||||||
p = Expression[@base.symbolic, @sign, o].reduce
|
|
||||||
Indirection[p, len, orig]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RegList
|
|
||||||
attr_accessor :list, :usermoderegs
|
|
||||||
|
|
||||||
def initialize(l=[])
|
|
||||||
@list = l
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(endianness = :little)
|
|
||||||
super()
|
|
||||||
@endianness = endianness
|
|
||||||
@size = 32
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
init_latest
|
|
||||||
@opcode_list
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,323 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/arm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARM
|
|
||||||
private
|
|
||||||
|
|
||||||
# ARM MODE
|
|
||||||
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
args << :cond if not args.delete :uncond
|
|
||||||
|
|
||||||
suppl = nil
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
args.each { |a|
|
|
||||||
# Should Be One fields
|
|
||||||
if a == :sbo16 ; o.bin |= 0b1111 << 16 ; next ; end
|
|
||||||
if a == :sbo12 ; o.bin |= 0b1111 << 12 ; next ; end
|
|
||||||
if a == :sbo8 ; o.bin |= 0b1111 << 8 ; next ; end
|
|
||||||
if a == :sbo0 ; o.bin |= 0b1111 << 0 ; next ; end
|
|
||||||
|
|
||||||
o.args << a if @valid_args[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
o.props.update a if a.kind_of?(Hash)
|
|
||||||
# special args -> multiple fields
|
|
||||||
suppl ||= { :i8_r => [:i8, :rotate], :rm_is => [:rm, :stype, :shifti],
|
|
||||||
:rm_rs => [:rm, :stype, :rs], :mem_rn_rm => [:rn, :rm, :rsx, :u],
|
|
||||||
:mem_rn_i8_12 => [:rn, :i8_12, :u],
|
|
||||||
:mem_rn_rms => [:rn, :rm, :stype, :shifti, :i],
|
|
||||||
:mem_rn_i12 => [:rn, :i12, :u]
|
|
||||||
}[a]
|
|
||||||
}
|
|
||||||
|
|
||||||
args.concat suppl if suppl
|
|
||||||
|
|
||||||
args.each { |a| o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a] }
|
|
||||||
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_data_s(name, op, a1, a2, *h)
|
|
||||||
addop name, op | (1 << 25), a1, a2, :i8_r, :rotate, *h
|
|
||||||
addop name, op, a1, a2, :rm_is, *h
|
|
||||||
addop name, op | (1 << 4), a1, a2, :rm_rs, *h
|
|
||||||
end
|
|
||||||
def addop_data(name, op, a1, a2)
|
|
||||||
addop_data_s name, op << 21, a1, a2
|
|
||||||
addop_data_s name+'s', (op << 21) | (1 << 20), a1, a2, :cond_name_off => name.length
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_load_puw(name, op, *a)
|
|
||||||
addop name, op, {:baseincr => :post}, :rd, :u, *a
|
|
||||||
addop name, op | (1 << 24), :rd, :u, *a
|
|
||||||
addop name, op | (1 << 24) | (1 << 21), {:baseincr => :pre}, :rd, :u, *a
|
|
||||||
end
|
|
||||||
def addop_load_lsh_o(name, op)
|
|
||||||
addop_load_puw name, op, :rsz, :mem_rn_rm, {:cond_name_off => 3}
|
|
||||||
addop_load_puw name, op | (1 << 22), :mem_rn_i8_12, {:cond_name_off => 3}
|
|
||||||
end
|
|
||||||
def addop_load_lsh
|
|
||||||
op = 9 << 4
|
|
||||||
addop_load_lsh_o 'strh', op | (1 << 5)
|
|
||||||
addop_load_lsh_o 'ldrd', op | (1 << 6)
|
|
||||||
addop_load_lsh_o 'strd', op | (1 << 6) | (1 << 5)
|
|
||||||
addop_load_lsh_o 'ldrh', op | (1 << 20) | (1 << 5)
|
|
||||||
addop_load_lsh_o 'ldrsb', op | (1 << 20) | (1 << 6)
|
|
||||||
addop_load_lsh_o 'ldrsh', op | (1 << 20) | (1 << 6) | (1 << 5)
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_load_puwt(name, op, *a)
|
|
||||||
addop_load_puw name, op, *a
|
|
||||||
addop name+'t', op | (1 << 21), {:baseincr => :post, :cond_name_off => name.length}, :rd, :u, *a
|
|
||||||
end
|
|
||||||
def addop_load_o(name, op, *a)
|
|
||||||
addop_load_puwt name, op, :mem_rn_i12, *a
|
|
||||||
addop_load_puwt name, op | (1 << 25), :mem_rn_rms, *a
|
|
||||||
end
|
|
||||||
def addop_load(name, op)
|
|
||||||
addop_load_o name, op
|
|
||||||
addop_load_o name+'b', op | (1 << 22), :cond_name_off => name.length
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_ldm_go(name, op, *a)
|
|
||||||
addop name, op, :rn, :reglist, {:cond_name_off => 3}, *a
|
|
||||||
end
|
|
||||||
def addop_ldm_w(name, op, *a)
|
|
||||||
addop_ldm_go name, op, *a # base reg untouched
|
|
||||||
addop_ldm_go name, op | (1 << 21), {:baseincr => :post}, *a # base updated
|
|
||||||
end
|
|
||||||
def addop_ldm_s(name, op)
|
|
||||||
addop_ldm_w name, op # transfer regs
|
|
||||||
addop_ldm_w name, op | (1 << 22), :usermoderegs # transfer usermode regs
|
|
||||||
end
|
|
||||||
def addop_ldm_p(name, op)
|
|
||||||
addop_ldm_s name+'a', op # target memory included
|
|
||||||
addop_ldm_s name+'b', op | (1 << 24) # target memory excluded, transfer starts at next addr
|
|
||||||
end
|
|
||||||
def addop_ldm_u(name, op)
|
|
||||||
addop_ldm_p name+'d', op # transfer made downward
|
|
||||||
addop_ldm_p name+'i', op | (1 << 23) # transfer made upward
|
|
||||||
end
|
|
||||||
def addop_ldm(name, op)
|
|
||||||
addop_ldm_u name, op
|
|
||||||
end
|
|
||||||
|
|
||||||
# ARMv6 instruction set, aka arm7/arm9
|
|
||||||
def init_arm_v6
|
|
||||||
@opcode_list = []
|
|
||||||
|
|
||||||
[:baseincr, :cond, :cond_name_off, :usermoderegs, :tothumb, :tojazelle
|
|
||||||
].each { |p| @valid_props[p] = true }
|
|
||||||
|
|
||||||
[:rn, :rd, :rm, :crn, :crd, :crm, :cpn, :reglist, :i24, :rm_rs, :rm_is,
|
|
||||||
:i8_r, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12
|
|
||||||
].each { |p| @valid_args[p] = true }
|
|
||||||
|
|
||||||
@fields_mask.update :rn => 0xf, :rd => 0xf, :rs => 0xf, :rm => 0xf,
|
|
||||||
:crn => 0xf, :crd => 0xf, :crm => 0xf, :cpn => 0xf,
|
|
||||||
:rnx => 0xf, :rdx => 0xf, :rsx => 0xf,
|
|
||||||
:shifti => 0x1f, :stype => 3, :rotate => 0xf, :reglist => 0xffff,
|
|
||||||
:i8 => 0xff, :i12 => 0xfff, :i24 => 0xff_ffff, :i8_12 => 0xf0f,
|
|
||||||
:u => 1, :mask => 0xf, :sbo => 0xf, :cond => 0xf
|
|
||||||
|
|
||||||
@fields_shift.update :rn => 16, :rd => 12, :rs => 8, :rm => 0,
|
|
||||||
:crn => 16, :crd => 12, :crm => 0, :cpn => 8,
|
|
||||||
:rnx => 16, :rdx => 12, :rsx => 8,
|
|
||||||
:shifti => 7, :stype => 5, :rotate => 8, :reglist => 0,
|
|
||||||
:i8 => 0, :i12 => 0, :i24 => 0, :i8_12 => 0,
|
|
||||||
:u => 23, :mask => 16, :sbo => 12, :cond => 28
|
|
||||||
|
|
||||||
addop_data 'and', 0, :rd, :rn
|
|
||||||
addop_data 'eor', 1, :rd, :rn
|
|
||||||
addop_data 'xor', 1, :rd, :rn
|
|
||||||
addop_data 'sub', 2, :rd, :rn
|
|
||||||
addop_data 'rsb', 3, :rd, :rn
|
|
||||||
addop_data 'add', 4, :rd, :rn
|
|
||||||
addop_data 'adc', 5, :rd, :rn
|
|
||||||
addop_data 'sbc', 6, :rd, :rn
|
|
||||||
addop_data 'rsc', 7, :rd, :rn
|
|
||||||
addop_data_s 'tst', (8 << 21) | (1 << 20), :rdx, :rn
|
|
||||||
addop_data_s 'teq', (9 << 21) | (1 << 20), :rdx, :rn
|
|
||||||
addop_data_s 'cmp', (10 << 21) | (1 << 20), :rdx, :rn
|
|
||||||
addop_data_s 'cmn', (11 << 21) | (1 << 20), :rdx, :rn
|
|
||||||
addop_data 'orr', 12, :rd, :rn
|
|
||||||
addop_data 'or', 12, :rd, :rn
|
|
||||||
addop_data 'mov', 13, :rd, :rnx
|
|
||||||
addop_data 'bic', 14, :rd, :rn
|
|
||||||
addop_data 'mvn', 15, :rd, :rnx
|
|
||||||
|
|
||||||
addop 'b', 0b1010 << 24, :setip, :stopexec, :i24
|
|
||||||
addop 'bl', 0b1011 << 24, :setip, :stopexec, :i24, :saveip
|
|
||||||
addop 'bkpt', (0b00010010 << 20) | (0b0111 << 4) # other fields are available&unused, also cnd != AL is undef
|
|
||||||
addop 'blx', 0b1111101 << 25, :setip, :stopexec, :saveip, :tothumb, :h, :uncond, :i24
|
|
||||||
addop 'blx', (0b00010010 << 20) | (0b0011 << 4), :setip, :stopexec, :saveip, :tothumb, :rm, :sbo16, :sbo12, :sbo8
|
|
||||||
addop 'bx', (0b00010010 << 20) | (0b0001 << 4), :setip, :stopexec, :rm, :sbo16, :sbo12, :sbo8
|
|
||||||
addop 'bxj', (0b00010010 << 20) | (0b0010 << 4), :setip, :stopexec, :rm, :tojazelle, :sbo16, :sbo12, :sbo8
|
|
||||||
|
|
||||||
addop_load 'str', (1 << 26)
|
|
||||||
addop_load 'ldr', (1 << 26) | (1 << 20)
|
|
||||||
addop_load_lsh
|
|
||||||
addop_ldm 'stm', (1 << 27)
|
|
||||||
addop_ldm 'ldm', (1 << 27) | (1 << 20)
|
|
||||||
# TODO aliases (http://www.davespace.co.uk/arm/introduction-to-arm/stack.html)
|
|
||||||
# fd = full descending stmfd/ldmfd = stmdb/ldmia
|
|
||||||
# ed = empty descending stmed/ldmed = stmda/ldmib
|
|
||||||
# fa = full ascending stmfa/ldmfa = stmib/ldmda
|
|
||||||
# ea = empty ascending stmea/ldmea = stmia/ldmdb
|
|
||||||
|
|
||||||
# TODO mrs, [qus]add/sub*
|
|
||||||
addop 'clz', (0b00010110 << 20) | (0b0001 << 4), :rd, :rm, :sbo16, :sbo8
|
|
||||||
addop 'ldrex', (0b00011001 << 20) | (0b1001 << 4), :rd, :rn, :sbo8, :sbo0
|
|
||||||
addop 'strex', (0b00011000 << 20) | (0b1001 << 4), :rd, :rm, :rn, :sbo8
|
|
||||||
addop 'rev', (0b01101011 << 20) | (0b0011 << 4), :rd, :rm, :sbo16, :sbo8
|
|
||||||
addop 'rev16', (0b01101011 << 20) | (0b1011 << 4), :rd, :rm, :sbo16, :sbo8
|
|
||||||
addop 'revsh', (0b01101111 << 20) | (0b1011 << 4), :rd, :rm, :sbo16, :sbo8
|
|
||||||
addop 'sel', (0b01101000 << 20) | (0b1011 << 4), :rd, :rn, :rm, :sbo8
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# THUMB2 MODE
|
|
||||||
|
|
||||||
def addop_t(name, bin, *args)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @valid_args[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
o.props.update a if a.kind_of?(Hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
args.each { |a| o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a] }
|
|
||||||
|
|
||||||
@opcode_list_t << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_arm_thumb2
|
|
||||||
@opcode_list_t = []
|
|
||||||
@valid_props_t = {}
|
|
||||||
@valid_args_t = {}
|
|
||||||
@fields_mask_t = {}
|
|
||||||
@fields_shift_t = {}
|
|
||||||
|
|
||||||
[:i16, :i16_3_8, :i16_rd].each { |p| @valid_props_t[p] = true }
|
|
||||||
[:i5, :rm, :rn, :rd].each { |p| @valid_args_t[p] = true }
|
|
||||||
@fields_mask_t.update :i5 => 0x1f, :i3 => 7, :i51 => 0x5f,
|
|
||||||
:rm => 7, :rn => 7, :rd => 7, :rdn => 7, :rdn8 => 7
|
|
||||||
@fields_shift_t.update :i5 => 6, :i3 => 6, :i51 => 3,
|
|
||||||
:rm => 6, :rn => 3, :rd => 0, :rdn => 0, :rdn8 => 8
|
|
||||||
|
|
||||||
addop_t 'mov', 0b000_00 << 11, :rd, :rm
|
|
||||||
addop_t 'lsl', 0b000_00 << 11, :rd, :rm, :i5
|
|
||||||
addop_t 'lsr', 0b000_01 << 11, :rd, :rm, :i5
|
|
||||||
addop_t 'asr', 0b000_10 << 11, :rd, :rm, :i5
|
|
||||||
|
|
||||||
addop_t 'add', 0b000_1100 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'add', 0b000_1110 << 9, :rd, :rn, :i3
|
|
||||||
addop_t 'sub', 0b000_1101 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'sub', 0b000_1111 << 9, :rd, :rn, :i3
|
|
||||||
|
|
||||||
addop_t 'mov', 0b001_00 << 10, :rdn8, :i8
|
|
||||||
addop_t 'cmp', 0b001_01 << 10, :rdn8, :i8
|
|
||||||
addop_t 'add', 0b001_10 << 10, :rdn8, :i8
|
|
||||||
addop_t 'sub', 0b001_11 << 10, :rdn8, :i8
|
|
||||||
|
|
||||||
addop_t 'and', (0b010000 << 10) | ( 0 << 6), :rdn, :rm
|
|
||||||
addop_t 'eor', (0b010000 << 10) | ( 1 << 6), :rdn, :rm # xor
|
|
||||||
addop_t 'lsl', (0b010000 << 10) | ( 2 << 6), :rdn, :rm
|
|
||||||
addop_t 'lsr', (0b010000 << 10) | ( 3 << 6), :rdn, :rm
|
|
||||||
addop_t 'asr', (0b010000 << 10) | ( 4 << 6), :rdn, :rm
|
|
||||||
addop_t 'adc', (0b010000 << 10) | ( 5 << 6), :rdn, :rm
|
|
||||||
addop_t 'sbc', (0b010000 << 10) | ( 6 << 6), :rdn, :rm
|
|
||||||
addop_t 'ror', (0b010000 << 10) | ( 7 << 6), :rdn, :rm
|
|
||||||
addop_t 'tst', (0b010000 << 10) | ( 8 << 6), :rdn, :rm
|
|
||||||
addop_t 'rsb', (0b010000 << 10) | ( 9 << 6), :rdn, :rm
|
|
||||||
addop_t 'cmp', (0b010000 << 10) | (10 << 6), :rdn, :rm
|
|
||||||
addop_t 'cmn', (0b010000 << 10) | (11 << 6), :rdn, :rm
|
|
||||||
addop_t 'orr', (0b010000 << 10) | (12 << 6), :rdn, :rm # or
|
|
||||||
addop_t 'mul', (0b010000 << 10) | (13 << 6), :rdn, :rm
|
|
||||||
addop_t 'bic', (0b010000 << 10) | (14 << 6), :rdn, :rm
|
|
||||||
addop_t 'mvn', (0b010000 << 10) | (15 << 6), :rdn, :rm
|
|
||||||
|
|
||||||
addop_t 'add', 0b010001_00 << 8, :rdn, :rm, :dn
|
|
||||||
addop_t 'cmp', 0b010001_01 << 8, :rdn, :rm, :dn
|
|
||||||
addop_t 'mov', 0b010001_10 << 8, :rdn, :rm, :dn
|
|
||||||
|
|
||||||
addop_t 'bx', 0b010001_110 << 7, :rm
|
|
||||||
addop_t 'blx', 0b010001_111 << 7, :rm
|
|
||||||
|
|
||||||
addop_t 'ldr', 0b01001 << 11, :rd, :pc_i8
|
|
||||||
addop_t 'str', 0b0101_000 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'strh', 0b0101_001 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'strb', 0b0101_010 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'ldrsb', 0b0101_011 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'ldr', 0b0101_100 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'ldrh', 0b0101_101 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'ldrb', 0b0101_110 << 9, :rd, :rn, :rm
|
|
||||||
addop_t 'ldrsh', 0b0101_111 << 9, :rd, :rn, :rm
|
|
||||||
|
|
||||||
addop_t 'str', 0b01100 << 11, :rd, :rn, :i5
|
|
||||||
addop_t 'ldr', 0b01101 << 11, :rd, :rn, :i5
|
|
||||||
addop_t 'strb', 0b01110 << 11, :rd, :rn, :i5
|
|
||||||
addop_t 'ldrb', 0b01111 << 11, :rd, :rn, :i5
|
|
||||||
addop_t 'strh', 0b10000 << 11, :rd, :rn, :i5
|
|
||||||
addop_t 'ldrh', 0b10001 << 11, :rd, :rn, :i5
|
|
||||||
addop_t 'str', 0b10010 << 11, :rd, :sp_i8
|
|
||||||
addop_t 'ldr', 0b10011 << 11, :rd, :sp_i8
|
|
||||||
addop_t 'adr', 0b10100 << 11, :rd, :pc, :i8
|
|
||||||
addop_t 'add', 0b10101 << 11, :rd, :sp, :i8
|
|
||||||
|
|
||||||
# 0b1011 misc
|
|
||||||
addop_t 'add', 0b1011_0000_0 << 7, :sp, :i7
|
|
||||||
addop_t 'sub', 0b1011_0000_1 << 7, :sp, :i7
|
|
||||||
addop_t 'sxth', 0b1011_0010_00 << 6, :rd, :rn
|
|
||||||
addop_t 'sxtb', 0b1011_0010_01 << 6, :rd, :rn
|
|
||||||
addop_t 'uxth', 0b1011_0010_10 << 6, :rd, :rn
|
|
||||||
addop_t 'uxtb', 0b1011_0010_11 << 6, :rd, :rn
|
|
||||||
addop_t 'cbz', 0b1011_0001 << 8, :rd, :i51
|
|
||||||
addop_t 'cbnz', 0b1011_1001 << 8, :rd, :i51
|
|
||||||
addop_t 'push', 0b1011_0100 << 8, :rlist
|
|
||||||
addop_t 'push', 0b1011_0101 << 8, :rlist
|
|
||||||
addop_t 'pop', 0b1011_1100 << 8, :rlist
|
|
||||||
addop_t 'pop', 0b1011_1101 << 8, :rlist
|
|
||||||
#addop_t 'unpredictable', 0b1011_0110_0100_0000, :i4
|
|
||||||
addop_t 'setendle', 0b1011_0110_0101_0000
|
|
||||||
addop_t 'setendbe', 0b1011_0110_0101_1000
|
|
||||||
addop_t 'cps', 0b1011_0110_0110_0000
|
|
||||||
#addop_t 'unpredictable', 0b1011_0110_0110_1000, :msk_0001_0111
|
|
||||||
addop_t 'rev', 0b1011_1010_00 << 6, :rd, :rn
|
|
||||||
addop_t 'rev16', 0b1011_1010_01 << 6, :rd, :rn
|
|
||||||
addop_t 'revsh', 0b1011_1010_11 << 6, :rd, :rn
|
|
||||||
addop_t 'bkpt', 0b1011_1110 << 8, :i8
|
|
||||||
addop_t 'it', 0b1011_1111 << 8, :itcond, :itmsk
|
|
||||||
addop_t 'nop', 0b1011_1111_0000_0000
|
|
||||||
addop_t 'yield', 0b1011_1111_0000_0001
|
|
||||||
addop_t 'wfe', 0b1011_1111_0000_0010
|
|
||||||
addop_t 'wfi', 0b1011_1111_0000_0011
|
|
||||||
addop_t 'sev', 0b1011_1111_0000_0100
|
|
||||||
addop_t 'nop', 0b1011_1111_0000_0000, :i4
|
|
||||||
|
|
||||||
|
|
||||||
addop_t 'stmia', 0b11000 << 11, :rn, :rlist # stmea
|
|
||||||
addop_t 'ldmia', 0b11001 << 11, :rn, :rlist # ldmfd
|
|
||||||
addop_t 'undef', 0b1101_1110 << 8, :i8
|
|
||||||
addop_t 'svc', 0b1101_1111 << 8, :i8
|
|
||||||
addop_t 'b', 0b1101 << 12, :cond, :i8
|
|
||||||
addop_t 'b', 0b11100 << 11, :i11
|
|
||||||
|
|
||||||
# thumb-32
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_arm_v6_thumb2
|
|
||||||
init_arm_v6
|
|
||||||
init_arm_thumb2
|
|
||||||
end
|
|
||||||
alias init_latest init_arm_v6_thumb2
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,142 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/arm/opcodes'
|
|
||||||
require 'metasm/parse'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARM
|
|
||||||
def opcode_list_byname
|
|
||||||
@opcode_list_byname ||= opcode_list.inject({}) { |h, o|
|
|
||||||
(h[o.name] ||= []) << o
|
|
||||||
if o.props[:cond]
|
|
||||||
coff = o.props[:cond_name_off] || o.name.length
|
|
||||||
%w[eq ne cs cc mi pl vs vc hi ls ge lt gt le al].each { |cd|
|
|
||||||
n = o.name.dup
|
|
||||||
n[coff, 0] = cd
|
|
||||||
(h[n] ||= []) << o
|
|
||||||
}
|
|
||||||
end
|
|
||||||
h
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_arg_valid?(op, sym, arg)
|
|
||||||
case sym
|
|
||||||
when :rd, :rs, :rn, :rm; arg.kind_of?(Reg) and arg.shift == 0 and (arg.updated ? op.props[:baseincr] : !op.props[:baseincr])
|
|
||||||
when :rm_rs; arg.kind_of?(Reg) and arg.shift.kind_of?(Reg)
|
|
||||||
when :rm_is; arg.kind_of?(Reg) and arg.shift.kind_of?(Integer)
|
|
||||||
when :i12, :i24, :i8_12; arg.kind_of?(Expression)
|
|
||||||
when :i8_r
|
|
||||||
if arg.kind_of?(Expression)
|
|
||||||
b = arg.reduce
|
|
||||||
!b.kind_of?(Integer) or (0..15).find {
|
|
||||||
b = ((b << 2) & 0xffff_ffff) | ((b >> 30) & 3)
|
|
||||||
b < 0x100 }
|
|
||||||
end
|
|
||||||
when :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12
|
|
||||||
os = case sym
|
|
||||||
when :mem_rn_rm; :rm
|
|
||||||
when :mem_rn_i8_12; :i8_12
|
|
||||||
when :mem_rn_rms; :rm_rs
|
|
||||||
when :mem_rn_i12; :i12
|
|
||||||
end
|
|
||||||
arg.kind_of?(Memref) and parse_arg_valid?(op, os, arg.offset)
|
|
||||||
when :reglist; arg.kind_of?(RegList)
|
|
||||||
end
|
|
||||||
# TODO check flags on reglist, check int values
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_argument(lexer)
|
|
||||||
raise lexer, "unexpected EOS" if not lexer.nexttok
|
|
||||||
if Reg.s_to_i[lexer.nexttok.raw]
|
|
||||||
arg = Reg.new Reg.s_to_i[lexer.readtok.raw]
|
|
||||||
lexer.skip_space
|
|
||||||
case lexer.nexttok.raw.downcase
|
|
||||||
when 'lsl', 'lsr', 'asr', 'ror'
|
|
||||||
arg.stype = lexer.readtok.raw.downcase.to_sym
|
|
||||||
lexer.skip_space
|
|
||||||
if Reg.s_to_i[lexer.nexttok.raw]
|
|
||||||
arg.shift = Reg.new Reg.s_to_i[lexer.readtok.raw]
|
|
||||||
else
|
|
||||||
arg.shift = Expression.parse(lexer).reduce
|
|
||||||
end
|
|
||||||
when 'rrx'
|
|
||||||
lexer.readtok
|
|
||||||
arg.stype = :ror
|
|
||||||
when '!'
|
|
||||||
lexer.readtok
|
|
||||||
arg.updated = true
|
|
||||||
end if lexer.nexttok
|
|
||||||
elsif lexer.nexttok.raw == '{'
|
|
||||||
lexer.readtok
|
|
||||||
arg = RegList.new
|
|
||||||
loop do
|
|
||||||
lexer.skip_space
|
|
||||||
raise "unterminated reglist" if lexer.eos?
|
|
||||||
if Reg.s_to_i[lexer.nexttok.raw]
|
|
||||||
arg.list << Reg.new(Reg.s_to_i[lexer.readtok.raw])
|
|
||||||
lexer.skip_space
|
|
||||||
raise "unterminated reglist" if lexer.eos?
|
|
||||||
end
|
|
||||||
case lexer.nexttok.raw
|
|
||||||
when ','; lexer.readtok
|
|
||||||
when '-'
|
|
||||||
lexer.readtok
|
|
||||||
lexer.skip_space
|
|
||||||
raise "unterminated reglist" if lexer.eos?
|
|
||||||
if not r = Reg.s_to_i[lexer.nexttok.raw]
|
|
||||||
raise lexer, "reglist parse error: invalid range"
|
|
||||||
end
|
|
||||||
lexer.readtok
|
|
||||||
(arg.list.last.i+1..r).each { |v|
|
|
||||||
arg.list << Reg.new(v)
|
|
||||||
}
|
|
||||||
when '}'; lexer.readtok ; break
|
|
||||||
else raise lexer, "reglist parse error: ',' or '}' expected, got #{lexer.nexttok.raw.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if lexer.nexttok and lexer.nexttok.raw == '^'
|
|
||||||
lexer.readtok
|
|
||||||
arg.usermoderegs = true
|
|
||||||
end
|
|
||||||
elsif lexer.nexttok.raw == '['
|
|
||||||
lexer.readtok
|
|
||||||
raise "unexpected EOS" if lexer.eos?
|
|
||||||
if not base = Reg.s_to_i[lexer.nexttok.raw]
|
|
||||||
raise lexer, 'invalid mem base (reg expected)'
|
|
||||||
end
|
|
||||||
base = Reg.new Reg.s_to_i[lexer.readtok.raw]
|
|
||||||
raise "unexpected EOS" if lexer.eos?
|
|
||||||
if lexer.nexttok.raw == ']'
|
|
||||||
lexer.readtok
|
|
||||||
#closed = true
|
|
||||||
end
|
|
||||||
if !lexer.nexttok or lexer.nexttok.raw != ','
|
|
||||||
raise lexer, 'mem off expected'
|
|
||||||
end
|
|
||||||
lexer.readtok
|
|
||||||
off = parse_argument(lexer)
|
|
||||||
if not off.kind_of?(Expression) and not off.kind_of?(Reg)
|
|
||||||
raise lexer, 'invalid mem off (reg/imm expected)'
|
|
||||||
end
|
|
||||||
case lexer.nexttok and lexer.nexttok.raw
|
|
||||||
when ']'
|
|
||||||
when ','
|
|
||||||
end
|
|
||||||
lexer.readtok
|
|
||||||
arg = Memref.new(base, off)
|
|
||||||
if lexer.nexttok and lexer.nexttok.raw == '!'
|
|
||||||
lexer.readtok
|
|
||||||
arg.incr = :pre # TODO :post
|
|
||||||
end
|
|
||||||
else
|
|
||||||
arg = Expression.parse lexer
|
|
||||||
end
|
|
||||||
arg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,55 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/render'
|
|
||||||
require 'metasm/cpu/arm/opcodes'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ARM
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
r = self.class.i_to_s[@i]
|
|
||||||
r += '!' if updated
|
|
||||||
if @stype == :lsl and @shift == 0
|
|
||||||
[r]
|
|
||||||
elsif @stype == :ror and @shift == 0
|
|
||||||
["#{r} RRX"]
|
|
||||||
else
|
|
||||||
case s = @shift
|
|
||||||
when Integer; s = Expression[s == 0 ? 32 : s] # lsl and ror already accounted for
|
|
||||||
when Reg; s = self.class.i_to_s[s.i]
|
|
||||||
end
|
|
||||||
["#{r} #{@stype.to_s.upcase} #{s}"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
o = @offset
|
|
||||||
o = Expression[o] if o.kind_of? Integer
|
|
||||||
case @incr
|
|
||||||
when nil; ['[', @base, ', ', o, ']']
|
|
||||||
when :pre; ['[', @base, ', ', o, ']!']
|
|
||||||
when :post; ['[', @base, '], ', o]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RegList
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
r = ['{']
|
|
||||||
@list.each { |l| r << l << ', ' }
|
|
||||||
r[-1] = '}'
|
|
||||||
r << '^' if usermoderegs
|
|
||||||
r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/bpf/decode'
|
|
||||||
require 'metasm/cpu/bpf/render'
|
|
@ -1,142 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/bpf/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class BPF
|
|
||||||
def build_bin_lookaside
|
|
||||||
opcode_list.inject({}) { |h, op| h.update op.bin => op }
|
|
||||||
end
|
|
||||||
|
|
||||||
# tries to find the opcode encoded at edata.ptr
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
return if edata.ptr > edata.data.length-8
|
|
||||||
di = DecodedInstruction.new self
|
|
||||||
code = edata.data[edata.ptr, 2].unpack('v')[0]
|
|
||||||
return di if di.opcode = @bin_lookaside[code]
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
di.bin_length = 8
|
|
||||||
code, jt, jf, k = edata.read(8).unpack('vCCV')
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :k; Expression[k]
|
|
||||||
when :x; Reg.new(:x)
|
|
||||||
when :a; Reg.new(:a)
|
|
||||||
when :len; Reg.new(:len)
|
|
||||||
when :p_k; PktRef.new(nil, Expression[k], op.props[:msz])
|
|
||||||
when :p_xk; PktRef.new(Reg.new(:x), Expression[k], op.props[:msz])
|
|
||||||
when :m_k; MemRef.new(nil, Expression[4*k], 4)
|
|
||||||
when :jt; Expression[jt]
|
|
||||||
when :jf; Expression[jf]
|
|
||||||
else raise "unhandled arg #{a}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
# je a, x, 0, 12 -> jne a, x, 12
|
|
||||||
# je a, x, 12, 0 -> je a, x, 12
|
|
||||||
if op.args[2] == :jt and di.instruction.args[2] == Expression[0]
|
|
||||||
di.opcode = op.dup
|
|
||||||
di.opcode.props.delete :stopexec
|
|
||||||
di.instruction.opname = { 'jg' => 'jle', 'jge' => 'jl', 'je' => 'jne', 'jtest' => 'jntest' }[di.instruction.opname]
|
|
||||||
di.instruction.args.delete_at(2)
|
|
||||||
elsif op.args[3] == :jf and di.instruction.args[3] == Expression[0]
|
|
||||||
di.opcode = op.dup
|
|
||||||
di.opcode.props.delete :stopexec
|
|
||||||
di.instruction.args.delete_at(3)
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
if di.opcode.props[:setip]
|
|
||||||
delta = di.instruction.args[-1].reduce + 1
|
|
||||||
arg = Expression[addr, :+, 8*delta].reduce
|
|
||||||
di.instruction.args[-1] = Expression[arg]
|
|
||||||
|
|
||||||
if di.instruction.args.length == 4
|
|
||||||
delta = di.instruction.args[2].reduce + 1
|
|
||||||
arg = Expression[addr, :+, 8*delta].reduce
|
|
||||||
di.instruction.args[2] = Expression[arg]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
# hash opcode_name => lambda { |dasm, di, *symbolic_args| instr_binding }
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
def backtrace_binding=(b) @backtrace_binding = b end
|
|
||||||
|
|
||||||
# populate the @backtrace_binding hash with default values
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
|
|
||||||
opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
|
|
||||||
binding = case op
|
|
||||||
when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
|
|
||||||
when 'add'; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } }
|
|
||||||
when 'sub'; lambda { |di, a0, a1| { a0 => Expression[a0, :-, a1] } }
|
|
||||||
when 'mul'; lambda { |di, a0, a1| { a0 => Expression[a0, :*, a1] } }
|
|
||||||
when 'div'; lambda { |di, a0, a1| { a0 => Expression[a0, :/, a1] } }
|
|
||||||
when 'shl'; lambda { |di, a0, a1| { a0 => Expression[a0, :<<, a1] } }
|
|
||||||
when 'shr'; lambda { |di, a0, a1| { a0 => Expression[a0, :>>, a1] } }
|
|
||||||
when 'neg'; lambda { |di, a0| { a0 => Expression[:-, a0] } }
|
|
||||||
when 'msh'; lambda { |di, a0, a1| { a0 => Expression[[a1, :&, 0xf], :<<, 2] } }
|
|
||||||
when 'jmp', 'jg', 'jge', 'je', 'jtest', 'ret'; lambda { |di, *a| { } }
|
|
||||||
end
|
|
||||||
@backtrace_binding[op] ||= binding if binding
|
|
||||||
}
|
|
||||||
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when PktRef, MemRef, Reg; arg.symbolic(di)
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.name]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
{:incomplete_binding => Expression[1]}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
if di.instruction.args.length == 4
|
|
||||||
di.instruction.args[-2, 2]
|
|
||||||
else
|
|
||||||
di.instruction.args[-1, 1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# updates an instruction's argument replacing an expression with another (eg label renamed)
|
|
||||||
def replace_instr_arg_immediate(i, old, new)
|
|
||||||
i.args.map! { |a|
|
|
||||||
case a
|
|
||||||
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
|
||||||
else a
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,60 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class BPF < CPU
|
|
||||||
class Reg
|
|
||||||
attr_accessor :v
|
|
||||||
def initialize(v)
|
|
||||||
@v = v
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig=nil) ; @v ; end
|
|
||||||
end
|
|
||||||
|
|
||||||
class MemRef
|
|
||||||
attr_accessor :base, :offset, :msz
|
|
||||||
|
|
||||||
def memtype
|
|
||||||
:mem
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(base, offset, msz)
|
|
||||||
@base = base
|
|
||||||
@offset = offset
|
|
||||||
@msz = msz
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig)
|
|
||||||
p = Expression[memtype]
|
|
||||||
p = Expression[p, :+, @base.symbolic] if base
|
|
||||||
p = Expression[p, :+, @offset] if offset
|
|
||||||
Indirection[p, @msz, orig]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class PktRef < MemRef
|
|
||||||
def memtype
|
|
||||||
:pkt
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(family = :latest)
|
|
||||||
super()
|
|
||||||
@endianness = :big
|
|
||||||
@size = 32
|
|
||||||
@family = family
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
send("init_#@family")
|
|
||||||
@opcode_list
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/bpf/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
class BPF
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @valid_args[a]
|
|
||||||
o.props.update a if a.kind_of?(::Hash)
|
|
||||||
}
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_ldx(bin, src)
|
|
||||||
addop 'mov', bin | 0x00, :a, src
|
|
||||||
addop 'mov', bin | 0x01, :x, src
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_ldsz(bin, src)
|
|
||||||
addop 'mov', bin | 0x00, :a, src, :msz => 4
|
|
||||||
addop 'mov', bin | 0x08, :a, src, :msz => 2
|
|
||||||
addop 'mov', bin | 0x10, :a, src, :msz => 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_alu(name, bin)
|
|
||||||
addop name, bin | 0x04, :a, :k
|
|
||||||
addop name, bin | 0x0C, :a, :x
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_j(name, bin)
|
|
||||||
addop name, bin | 0x05 | 0x00, :a, :k, :jt, :jf, :setip => true, :stopexec => true
|
|
||||||
addop name, bin | 0x05 | 0x08, :a, :x, :jt, :jf, :setip => true, :stopexec => true
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_bpf
|
|
||||||
@opcode_list = []
|
|
||||||
[:a, :k, :x, :len, :m_k, :p_k, :p_xk, :jt, :jf].each { |a| @valid_args[a] = true }
|
|
||||||
|
|
||||||
# LD/ST
|
|
||||||
addop_ldx 0x00, :k
|
|
||||||
addop_ldsz 0x20, :p_k
|
|
||||||
addop_ldsz 0x40, :p_xk
|
|
||||||
addop_ldx 0x60, :m_k
|
|
||||||
addop_ldx 0x80, :len
|
|
||||||
addop 'msh', 0xB1, :x, :p_k, :msz => 1
|
|
||||||
addop 'mov', 0x02, :m_k, :a
|
|
||||||
addop 'mov', 0x03, :m_k, :x
|
|
||||||
|
|
||||||
# ALU
|
|
||||||
addop_alu 'add', 0x00
|
|
||||||
addop_alu 'sub', 0x10
|
|
||||||
addop_alu 'mul', 0x20
|
|
||||||
addop_alu 'div', 0x30
|
|
||||||
addop_alu 'or', 0x40
|
|
||||||
addop_alu 'and', 0x50
|
|
||||||
addop_alu 'shl', 0x60
|
|
||||||
addop_alu 'shr', 0x70
|
|
||||||
addop 'neg', 0x84, :a
|
|
||||||
|
|
||||||
# JMP
|
|
||||||
addop 'jmp', 0x05, :k, :setip => true, :stopexec => true
|
|
||||||
addop_j 'je', 0x10
|
|
||||||
addop_j 'jg', 0x20
|
|
||||||
addop_j 'jge', 0x30
|
|
||||||
addop_j 'jtest',0x40
|
|
||||||
addop 'ret', 0x06, :k, :stopexec => true
|
|
||||||
addop 'ret', 0x16, :a, :stopexec => true
|
|
||||||
|
|
||||||
addop 'mov', 0x07, :x, :a
|
|
||||||
addop 'mov', 0x87, :a, :x
|
|
||||||
end
|
|
||||||
|
|
||||||
alias init_latest init_bpf
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,41 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/bpf/opcodes'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class BPF
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
def render ; [@v.to_s] end
|
|
||||||
end
|
|
||||||
class MemRef
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
r = []
|
|
||||||
r << memtype
|
|
||||||
r << [nil, ' byte ', ' word ', nil, ' dword '][@msz]
|
|
||||||
r << '['
|
|
||||||
r << @base if @base
|
|
||||||
r << '+' if @base and @offset
|
|
||||||
r << @offset if @offset
|
|
||||||
r << ']'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_instruction(i)
|
|
||||||
r = []
|
|
||||||
r << i.opname
|
|
||||||
if not i.args.empty?
|
|
||||||
r << ' '
|
|
||||||
i.args.each { |a_| r << a_ << ', ' }
|
|
||||||
r.pop
|
|
||||||
end
|
|
||||||
r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,9 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/cy16/decode'
|
|
||||||
require 'metasm/cpu/cy16/render'
|
|
@ -1,253 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/cy16/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class CY16
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
|
|
||||||
op.bin_mask = 0
|
|
||||||
op.fields.each { |f, off|
|
|
||||||
op.bin_mask |= (@fields_mask[f] << off)
|
|
||||||
}
|
|
||||||
op.bin_mask ^= 0xffff
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
# sets up a hash byte value => list of opcodes that may match
|
|
||||||
# opcode.bin_mask is built here
|
|
||||||
lookaside = Array.new(256) { [] }
|
|
||||||
opcode_list.each { |op|
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
b = (op.bin >> 8) & 0xff
|
|
||||||
msk = (op.bin_mask >> 8) & 0xff
|
|
||||||
for i in b..(b | (255^msk))
|
|
||||||
lookaside[i] << op if i & msk == b & msk
|
|
||||||
end
|
|
||||||
}
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
di = DecodedInstruction.new self
|
|
||||||
return if edata.ptr+2 > edata.length
|
|
||||||
bin = edata.decode_imm(:u16, @endianness)
|
|
||||||
edata.ptr -= 2
|
|
||||||
return di if di.opcode = @bin_lookaside[(bin >> 8) & 0xff].find { |op|
|
|
||||||
bin & op.bin_mask == op.bin & op.bin_mask
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def decode_instr_op_r(val, edata)
|
|
||||||
bw = ((val & 0b1000) > 0 ? 1 : 2)
|
|
||||||
case val & 0b11_0000
|
|
||||||
when 0b00_0000
|
|
||||||
Reg.new(val)
|
|
||||||
when 0b01_0000
|
|
||||||
if val == 0b01_1111
|
|
||||||
Expression[edata.decode_imm(:u16, @endianness)]
|
|
||||||
else
|
|
||||||
Memref.new(Reg.new(8+(val&7)), nil, bw)
|
|
||||||
end
|
|
||||||
when 0b10_0000
|
|
||||||
if val & 7 == 7
|
|
||||||
Memref.new(nil, edata.decode_imm(:u16, @endianness), bw)
|
|
||||||
else
|
|
||||||
Memref.new(Reg.new(8+(val&7)), nil, bw, true)
|
|
||||||
end
|
|
||||||
when 0b11_0000
|
|
||||||
Memref.new(Reg.new(8+(val&7)), edata.decode_imm(:u16, @endianness), bw)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
bin = edata.decode_imm(:u16, @endianness)
|
|
||||||
|
|
||||||
field_val = lambda { |f|
|
|
||||||
if off = op.fields[f]
|
|
||||||
(bin >> off) & @fields_mask[f]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :rs, :rd; decode_instr_op_r(field_val[a], edata)
|
|
||||||
when :o7; Expression[2*Expression.make_signed(field_val[a], 7)]
|
|
||||||
when :x7; Expression[field_val[a]]
|
|
||||||
when :u3; Expression[field_val[a]+1]
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.instruction.args.reverse!
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
di
|
|
||||||
rescue InvalidRD
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
if di.opcode.props[:setip] and di.opcode.args.last == :o7
|
|
||||||
delta = di.instruction.args.last.reduce
|
|
||||||
arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce
|
|
||||||
di.instruction.args[-1] = Expression[arg]
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
# hash opcode_name => lambda { |dasm, di, *symbolic_args| instr_binding }
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
def backtrace_binding=(b) @backtrace_binding = b end
|
|
||||||
|
|
||||||
# populate the @backtrace_binding hash with default values
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
|
|
||||||
mask = 0xffff
|
|
||||||
|
|
||||||
opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
|
|
||||||
binding = case op
|
|
||||||
when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
|
|
||||||
when 'add', 'adc', 'sub', 'sbc', 'and', 'xor', 'or', 'addi', 'subi'
|
|
||||||
lambda { |di, a0, a1|
|
|
||||||
e_op = { 'add' => :+, 'adc' => :+, 'sub' => :-, 'sbc' => :-, 'and' => :&,
|
|
||||||
'xor' => :^, 'or' => :|, 'addi' => :+, 'subi' => :- }[op]
|
|
||||||
ret = Expression[a0, e_op, a1]
|
|
||||||
ret = Expression[ret, e_op, :flag_c] if op == 'adc' or op == 'sbb'
|
|
||||||
# optimises eax ^ eax => 0
|
|
||||||
# avoid hiding memory accesses (to not hide possible fault)
|
|
||||||
ret = Expression[ret.reduce] if not a0.kind_of? Indirection
|
|
||||||
{ a0 => ret }
|
|
||||||
}
|
|
||||||
when 'cmp', 'test'; lambda { |di, *a| {} }
|
|
||||||
when 'not'; lambda { |di, a0| { a0 => Expression[a0, :^, mask] } }
|
|
||||||
when 'call'
|
|
||||||
lambda { |di, a0| { :sp => Expression[:sp, :-, 2],
|
|
||||||
Indirection[:sp, 2, di.address] => Expression[di.next_addr] }
|
|
||||||
}
|
|
||||||
when 'ret'; lambda { |di, *a| { :sp => Expression[:sp, :+, 2] } }
|
|
||||||
# TODO callCC, retCC ...
|
|
||||||
when /^j/; lambda { |di, *a| {} }
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO flags ?
|
|
||||||
|
|
||||||
@backtrace_binding[op] ||= binding if binding
|
|
||||||
}
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Memref, Reg; arg.symbolic(di)
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.basename]
|
|
||||||
bd = {}
|
|
||||||
di.instruction.args.each { |aa| bd[aa.base.symbolic] = Expression[aa.base.symbolic, :+, aa.sz] if aa.kind_of?(Memref) and aa.autoincr }
|
|
||||||
bd.update binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
# assume nothing except the 1st arg is modified
|
|
||||||
case a[0]
|
|
||||||
when Indirection, Symbol; { a[0] => Expression::Unknown }
|
|
||||||
when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
|
|
||||||
else {}
|
|
||||||
end.update(:incomplete_binding => Expression[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# patch a forward binding from the backtrace binding
|
|
||||||
def fix_fwdemu_binding(di, fbd)
|
|
||||||
case di.opcode.name
|
|
||||||
when 'call'; fbd[Indirection[[:sp, :-, 2], 2]] = fbd.delete(Indirection[:sp, 2])
|
|
||||||
end
|
|
||||||
fbd
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
return [Indirection[:sp, 2, di.address]] if di.opcode.name =~ /^r/
|
|
||||||
|
|
||||||
case tg = di.instruction.args.first
|
|
||||||
when Memref; [Expression[tg.symbolic(di)]]
|
|
||||||
when Reg; [Expression[tg.symbolic(di)]]
|
|
||||||
when Expression, ::Integer; [Expression[tg]]
|
|
||||||
else
|
|
||||||
puts "unhandled setip at #{di.address} #{di.instruction}" if $DEBUG
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# checks if expr is a valid return expression matching the :saveip instruction
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
expr = Expression[expr].reduce_rec
|
|
||||||
expr.kind_of?(Indirection) and expr.len == 2 and expr.target == Expression[:sp]
|
|
||||||
end
|
|
||||||
|
|
||||||
# updates the function backtrace_binding
|
|
||||||
# if the function is big and no specific register is given, do nothing (the binding will be lazily updated later, on demand)
|
|
||||||
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
|
|
||||||
b = f.backtrace_binding
|
|
||||||
|
|
||||||
bt_val = lambda { |r|
|
|
||||||
next if not retaddrlist
|
|
||||||
b[r] = Expression::Unknown
|
|
||||||
bt = []
|
|
||||||
retaddrlist.each { |retaddr|
|
|
||||||
bt |= dasm.backtrace(Expression[r], retaddr, :include_start => true,
|
|
||||||
:snapshot_addr => faddr, :origin => retaddr)
|
|
||||||
}
|
|
||||||
if bt.length != 1
|
|
||||||
b[r] = Expression::Unknown
|
|
||||||
else
|
|
||||||
b[r] = bt.first
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if not wantregs.empty?
|
|
||||||
wantregs.each(&bt_val)
|
|
||||||
else
|
|
||||||
bt_val[:sp]
|
|
||||||
end
|
|
||||||
|
|
||||||
b
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns true if the expression is an address on the stack
|
|
||||||
def backtrace_is_stack_address(expr)
|
|
||||||
Expression[expr].expr_externals.include?(:sp)
|
|
||||||
end
|
|
||||||
|
|
||||||
# updates an instruction's argument replacing an expression with another (eg label renamed)
|
|
||||||
def replace_instr_arg_immediate(i, old, new)
|
|
||||||
i.args.map! { |a|
|
|
||||||
case a
|
|
||||||
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
|
||||||
when Memref
|
|
||||||
a.offset = (a.offset == old ? new : Expression[a.offset.bind(old => new).reduce]) if a.offset
|
|
||||||
a
|
|
||||||
else a
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,63 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class CY16 < CPU
|
|
||||||
class Reg
|
|
||||||
class << self
|
|
||||||
attr_accessor :s_to_i, :i_to_s
|
|
||||||
end
|
|
||||||
@i_to_s = (0..14).inject({}) { |h, i| h.update i => "r#{i}" }
|
|
||||||
@i_to_s[15] = 'sp'
|
|
||||||
@s_to_i = @i_to_s.invert
|
|
||||||
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig=nil) ; to_s.to_sym ; end
|
|
||||||
|
|
||||||
def self.from_str(s)
|
|
||||||
raise "Bad name #{s.inspect}" if not x = @s_to_i[s]
|
|
||||||
new(x)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
attr_accessor :base, :offset, :sz, :autoincr
|
|
||||||
def initialize(base, offset, sz=nil, autoincr=nil)
|
|
||||||
@base = base
|
|
||||||
offset = Expression[offset] if offset
|
|
||||||
@offset = offset
|
|
||||||
@sz = sz
|
|
||||||
@autoincr = autoincr
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig)
|
|
||||||
p = nil
|
|
||||||
p = Expression[p, :+, @base.symbolic] if base
|
|
||||||
p = Expression[p, :+, @offset] if offset
|
|
||||||
Indirection[p.reduce, @sz, orig]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(family = :latest)
|
|
||||||
super()
|
|
||||||
@endianness = :little
|
|
||||||
@size = 16
|
|
||||||
@family = family
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
send("init_#@family")
|
|
||||||
@opcode_list
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/cy16/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
class CY16
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @fields_mask[a] or @valid_args[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
o.fields[a] = @fields_shift[a] if @fields_mask[a]
|
|
||||||
raise "wtf #{a.inspect}" unless @valid_args[a] or @valid_props[a] or @fields_mask[a]
|
|
||||||
}
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_macrocc(name, bin, *args)
|
|
||||||
%w[z nz b ae s ns o no a be g ge l le].each_with_index { |cc, i|
|
|
||||||
dbin = bin
|
|
||||||
dbin |= i << 8
|
|
||||||
addop name + cc, dbin, *args
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_cy16
|
|
||||||
@opcode_list = []
|
|
||||||
@valid_args.update [:rs, :rd, :o7
|
|
||||||
].inject({}) { |h, v| h.update v => true }
|
|
||||||
@fields_mask.update :rs => 0x3f, :rd => 0x3f, :o7 => 0x7f, :x7 => 0x7f, :u3 => 7
|
|
||||||
@fields_shift.update :rs => 6, :rd => 0, :o7 => 0, :x7 => 0, :u3 => 6
|
|
||||||
|
|
||||||
addop 'mov', 0<<12, :rs, :rd
|
|
||||||
addop 'add', 1<<12, :rs, :rd
|
|
||||||
addop 'adc', 2<<12, :rs, :rd
|
|
||||||
addop 'addc',2<<12, :rs, :rd
|
|
||||||
addop 'sub', 3<<12, :rs, :rd
|
|
||||||
addop 'sbb', 4<<12, :rs, :rd
|
|
||||||
addop 'subb',4<<12, :rs, :rd
|
|
||||||
addop 'cmp', 5<<12, :rs, :rd
|
|
||||||
addop 'and', 6<<12, :rs, :rd
|
|
||||||
addop 'test',7<<12, :rs, :rd
|
|
||||||
addop 'or', 8<<12, :rs, :rd
|
|
||||||
addop 'xor', 9<<12, :rs, :rd
|
|
||||||
|
|
||||||
addop_macrocc 'int', (10<<12), :x7
|
|
||||||
addop 'int', (10<<12) | (15<<8), :x7
|
|
||||||
addop_macrocc 'c', (10<<12) | (1<<7), :setip, :saveip, :rd
|
|
||||||
addop 'call',(10<<12) | (15<<8) | (1<<7), :setip, :stopexec, :saveip, :rd
|
|
||||||
addop_macrocc 'r', (12<<12) | (1<<7) | 0b010111, :setip # must come before absolute jmp
|
|
||||||
addop 'ret', (12<<12) | (15<<8) | (1<<7) | 0b010111, :setip, :stopexec
|
|
||||||
addop_macrocc 'j', (12<<12), :setip, :o7 # relative
|
|
||||||
addop 'jmp', (12<<12) | (15<<8), :setip, :stopexec, :o7 # relative
|
|
||||||
addop_macrocc 'j', (12<<12) | (1<<7), :setip, :rd # absolute
|
|
||||||
addop 'jmp', (12<<12) | (15<<8) | (1<<7), :setip, :stopexec, :rd # absolute
|
|
||||||
|
|
||||||
addop 'shr', (13<<12) | (0<<9), :u3, :rd
|
|
||||||
addop 'shl', (13<<12) | (1<<9), :u3, :rd
|
|
||||||
addop 'ror', (13<<12) | (2<<9), :u3, :rd
|
|
||||||
addop 'rol', (13<<12) | (3<<9), :u3, :rd
|
|
||||||
addop 'addi',(13<<12) | (4<<9), :u3, :rd
|
|
||||||
addop 'subi',(13<<12) | (5<<9), :u3, :rd
|
|
||||||
addop 'not', (13<<12) | (7<<9) | (0<<6), :rd
|
|
||||||
addop 'neg', (13<<12) | (7<<9) | (1<<6), :rd
|
|
||||||
addop 'cbw', (13<<12) | (7<<9) | (4<<6), :rd
|
|
||||||
addop 'sti', (13<<12) | (7<<9) | (7<<6) | 0
|
|
||||||
addop 'cli', (13<<12) | (7<<9) | (7<<6) | 1
|
|
||||||
addop 'stc', (13<<12) | (7<<9) | (7<<6) | 2
|
|
||||||
addop 'clc', (13<<12) | (7<<9) | (7<<6) | 3
|
|
||||||
end
|
|
||||||
|
|
||||||
alias init_latest init_cy16
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,41 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/cy16/opcodes'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class CY16
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
def render ; [self.class.i_to_s[@i]] end
|
|
||||||
end
|
|
||||||
class Memref
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
r = []
|
|
||||||
r << (@sz == 1 ? 'byte ptr ' : 'word ptr ')
|
|
||||||
r << '['
|
|
||||||
r << @base if @base
|
|
||||||
r << '++' if @autoincr
|
|
||||||
r << ' + ' if @base and @offset
|
|
||||||
r << @offset if @offset
|
|
||||||
r << ']'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_instruction(i)
|
|
||||||
r = []
|
|
||||||
r << i.opname
|
|
||||||
if not i.args.empty?
|
|
||||||
r << ' '
|
|
||||||
i.args.each { |a_| r << a_ << ', ' }
|
|
||||||
r.pop
|
|
||||||
end
|
|
||||||
r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,11 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
class Metasm::Dalvik < Metasm::CPU
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/dalvik/decode'
|
|
@ -1,218 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/dalvik/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Dalvik
|
|
||||||
def build_bin_lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
return if edata.ptr+2 > edata.length
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
di.opcode = opcode_list[edata.decode_imm(:u16, @endianness) & 0xff]
|
|
||||||
edata.ptr -= 2
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
|
|
||||||
val = [edata.decode_imm(:u16, @endianness)]
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :i16
|
|
||||||
val << edata.decode_imm(:i16, @endianness)
|
|
||||||
Expression[val.last]
|
|
||||||
when :u16
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
Expression[val.last]
|
|
||||||
when :r16
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
Reg.new(val.last)
|
|
||||||
when :i16_32hi
|
|
||||||
val << edata.decode_imm(:i16, @endianness)
|
|
||||||
Expression[val.last << 16]
|
|
||||||
when :i16_64hi
|
|
||||||
val << edata.decode_imm(:i16, @endianness)
|
|
||||||
Expression[val.last << 48]
|
|
||||||
when :i32
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
val << edata.decode_imm(:i16, @endianness)
|
|
||||||
Expression[val[-2] | (val[-1] << 16)]
|
|
||||||
when :u32
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
Expression[val[-2] | (val[-1] << 16)]
|
|
||||||
when :u64
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
Expression[val[-4] | (val[-3] << 16) | (val[-2] << 32) | (val[-1] << 48)]
|
|
||||||
when :ra
|
|
||||||
Reg.new((val[0] >> 8) & 0xf)
|
|
||||||
when :rb
|
|
||||||
Reg.new((val[0] >> 12) & 0xf)
|
|
||||||
when :ib
|
|
||||||
Expression[Expression.make_signed((val[0] >> 12) & 0xf, 4)]
|
|
||||||
when :raa
|
|
||||||
Reg.new((val[0] >> 8) & 0xff)
|
|
||||||
when :iaa
|
|
||||||
Expression[Expression.make_signed((val[0] >> 8) & 0xff, 8)]
|
|
||||||
when :rbb
|
|
||||||
val[1] ||= edata.decode_imm(:u16, @endianness)
|
|
||||||
Reg.new(val[1] & 0xff)
|
|
||||||
when :ibb
|
|
||||||
val[1] ||= edata.decode_imm(:u16, @endianness)
|
|
||||||
Expression[Expression.make_signed(val[1] & 0xff, 8)]
|
|
||||||
when :rcc
|
|
||||||
val[1] ||= edata.decode_imm(:u16, @endianness)
|
|
||||||
Reg.new((val[1] >> 8) & 0xff)
|
|
||||||
when :icc
|
|
||||||
val[1] ||= edata.decode_imm(:u16, @endianness)
|
|
||||||
Expression[Expression.make_signed((val[1] >> 8) & 0xff, 8)]
|
|
||||||
when :rlist4, :rlist5
|
|
||||||
cnt = (val[0] >> 12) & 0xf
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
[cnt, 4].min.times {
|
|
||||||
di.instruction.args << Reg.new(val[-1] & 0xf)
|
|
||||||
val[-1] >>= 4
|
|
||||||
}
|
|
||||||
di.instruction.args << Reg.new((val[0] >> 8) & 0xf) if cnt > 4
|
|
||||||
next
|
|
||||||
when :rlist16
|
|
||||||
cnt = (val[0] >> 8) & 0xff
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
cnt.times { |c|
|
|
||||||
di.instruction.args << Reg.new(val[-1] + c)
|
|
||||||
}
|
|
||||||
next
|
|
||||||
when :m16
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
DexMethod.new(@dex, val.last)
|
|
||||||
when :fld16
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
DexField.new(@dex, val.last)
|
|
||||||
when :typ16
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
DexType.new(@dex, val.last)
|
|
||||||
when :str16
|
|
||||||
val << edata.decode_imm(:u16, @endianness)
|
|
||||||
DexString.new(@dex, val.last)
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length = val.length*2
|
|
||||||
|
|
||||||
return if edata.ptr > edata.length
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.instruction.opname =~ /^if|^goto/
|
|
||||||
arg = Expression[addr, :+, [di.instruction.args.last, :*, 2]].reduce
|
|
||||||
di.instruction.args[-1] = Expression[arg]
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
sz = @size/8
|
|
||||||
@opcode_list.each { |op|
|
|
||||||
case op.name
|
|
||||||
when /invoke/
|
|
||||||
@backtrace_binding[op.name] = lambda { |di, *args| {
|
|
||||||
:callstack => Expression[:callstack, :-, sz],
|
|
||||||
Indirection[:callstack, sz] => Expression[di.next_addr]
|
|
||||||
} }
|
|
||||||
when /return/
|
|
||||||
@backtrace_binding[op.name] = lambda { |di, *args| {
|
|
||||||
:callstack => Expression[:callstack, :+, sz]
|
|
||||||
} }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Reg; arg.symbolic
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.name]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
# assume nothing except the 1st arg is modified
|
|
||||||
case a[0]
|
|
||||||
when Indirection, Symbol; { a[0] => Expression::Unknown }
|
|
||||||
when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
|
|
||||||
else {}
|
|
||||||
end.update(:incomplete_binding => Expression[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
if di.opcode.props[:saveip]
|
|
||||||
m = di.instruction.args.first
|
|
||||||
if m.kind_of?(DexMethod) and m.off
|
|
||||||
[m.off]
|
|
||||||
else
|
|
||||||
[:default]
|
|
||||||
end
|
|
||||||
elsif di.opcode.props[:setip]
|
|
||||||
if di.opcode.name =~ /^return/
|
|
||||||
[Indirection[:callstack, @size/8]]
|
|
||||||
elsif di.opcode.name =~ /^if|^goto/
|
|
||||||
[di.instruction.args.last]
|
|
||||||
else
|
|
||||||
[] # [di.instruction.args.last]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns a DecodedFunction suitable for :default
|
|
||||||
# uses disassembler_default_bt{for/bind}_callback
|
|
||||||
def disassembler_default_func
|
|
||||||
df = DecodedFunction.new
|
|
||||||
ra = Indirection[:callstack, @size/8]
|
|
||||||
df.backtracked_for << BacktraceTrace.new(ra, :default, ra, :x, nil)
|
|
||||||
df.backtrace_binding[:callstack] = Expression[:callstack, :+, @size/8]
|
|
||||||
df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
|
|
||||||
if funcaddr != :default
|
|
||||||
btfor
|
|
||||||
elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip]
|
|
||||||
btfor
|
|
||||||
else []
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
df
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
expr and Expression[expr] == Expression[Indirection[:callstack, @size/8]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,109 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Dalvik < CPU
|
|
||||||
class Reg
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic
|
|
||||||
"r#@i".to_sym
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"r#@i"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DexMethod
|
|
||||||
attr_accessor :dex, :midx, :off
|
|
||||||
def initialize(dex, midx)
|
|
||||||
@dex = dex
|
|
||||||
@midx = midx
|
|
||||||
if @dex and m = @dex.methods[midx] and c = @dex.classes[m.classidx] and c.data and
|
|
||||||
me = (c.data.direct_methods+c.data.virtual_methods).find { |mm| mm.methodid == midx }
|
|
||||||
# FIXME this doesnt work
|
|
||||||
@off = me.codeoff + me.code.insns_off
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
if @dex and m = @dex.methods[@midx]
|
|
||||||
@dex.types[m.classidx] + '->' + @dex.strings[m.nameidx]
|
|
||||||
#dex.encoded.inv_export[@off]
|
|
||||||
else
|
|
||||||
"method_#@midx"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DexField
|
|
||||||
attr_accessor :dex, :fidx
|
|
||||||
def initialize(dex, fidx)
|
|
||||||
@dex = dex
|
|
||||||
@fidx = fidx
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
if @dex and f = @dex.fields[@fidx]
|
|
||||||
@dex.types[f.classidx] + '->' + @dex.strings[f.nameidx]
|
|
||||||
else
|
|
||||||
"field_#@fidx"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DexType
|
|
||||||
attr_accessor :dex, :tidx
|
|
||||||
def initialize(dex, tidx)
|
|
||||||
@dex = dex
|
|
||||||
@tidx = tidx
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
if @dex and f = @dex.types[@tidx]
|
|
||||||
f
|
|
||||||
else
|
|
||||||
"type_#@tidx"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DexString
|
|
||||||
attr_accessor :dex, :sidx
|
|
||||||
def initialize(dex, sidx)
|
|
||||||
@dex = dex
|
|
||||||
@sidx = sidx
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
if @dex and f = @dex.strings[@sidx]
|
|
||||||
f.inspect
|
|
||||||
else
|
|
||||||
"string_#@sidx"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(*args)
|
|
||||||
super()
|
|
||||||
@size = args.grep(Integer).first || 32
|
|
||||||
@dex = args.grep(ExeFormat).first
|
|
||||||
@endianness = args.delete(:little) || args.delete(:big) || (@dex ? @dex.endianness : :little)
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
init_latest
|
|
||||||
@opcode_list
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,374 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
# the Dalvik binary format, aka android java backend bytecode
|
|
||||||
# this file was generated using the android source tree, as reference,
|
|
||||||
# specifically dalvik/libdex/InstrUtils.c
|
|
||||||
|
|
||||||
# the binary opcode format is 16 bit word-based
|
|
||||||
# the opcode number is in the low-order byte, and determines the
|
|
||||||
# argument format, which may take up to 4 other words
|
|
||||||
|
|
||||||
require 'metasm/cpu/dalvik/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Dalvik
|
|
||||||
OPCODES = %w[nop move move_from16 move_16 move_wide move_wide_from16
|
|
||||||
move_wide_16 move_object move_object_from16 move_object_16 move_result
|
|
||||||
move_result_wide move_result_object move_exception
|
|
||||||
return_void return return_wide return_object
|
|
||||||
const_4 const_16 const const_high16 const_wide_16 const_wide_32
|
|
||||||
const_wide const_wide_high16 const_string const_string_jumbo const_class
|
|
||||||
monitor_enter monitor_exit check_cast instance_of array_length
|
|
||||||
new_instance new_array filled_new_array filled_new_array_range fill_array_data
|
|
||||||
throw goto goto_16 goto_32 packed_switch sparse_switch
|
|
||||||
cmpl_float cmpg_float cmpl_double cmpg_double cmp_long
|
|
||||||
if_eq if_ne if_lt if_ge if_gt if_le if_eqz if_nez if_ltz if_gez if_gtz if_lez
|
|
||||||
unused_3e unused_3f unused_40 unused_41 unused_42 unused_43
|
|
||||||
aget aget_wide aget_object aget_boolean aget_byte aget_char aget_short
|
|
||||||
aput aput_wide aput_object aput_boolean aput_byte aput_char aput_short
|
|
||||||
iget iget_wide iget_object iget_boolean iget_byte iget_char iget_short
|
|
||||||
iput iput_wide iput_object iput_boolean iput_byte iput_char iput_short
|
|
||||||
sget sget_wide sget_object sget_boolean sget_byte sget_char sget_short
|
|
||||||
sput sput_wide sput_object sput_boolean sput_byte sput_char sput_short
|
|
||||||
invoke_virtual invoke_super invoke_direct invoke_static invoke_interface
|
|
||||||
unused_73
|
|
||||||
invoke_virtual_range invoke_super_range invoke_direct_range invoke_static_range invoke_interface_range
|
|
||||||
unused_79 unused_7a
|
|
||||||
neg_int not_int neg_long not_long neg_float neg_double
|
|
||||||
int_to_long int_to_float int_to_double long_to_int long_to_float long_to_double
|
|
||||||
float_to_int float_to_long float_to_double double_to_int double_to_long
|
|
||||||
double_to_float int_to_byte int_to_char int_to_short
|
|
||||||
add_int sub_int mul_int div_int rem_int and_int or_int xor_int shl_int shr_int ushr_int
|
|
||||||
add_long sub_long mul_long div_long rem_long and_long or_long xor_long shl_long shr_long ushr_long
|
|
||||||
add_float sub_float mul_float div_float rem_float
|
|
||||||
add_double sub_double mul_double div_double rem_double
|
|
||||||
add_int_2addr sub_int_2addr mul_int_2addr div_int_2addr rem_int_2addr
|
|
||||||
and_int_2addr or_int_2addr xor_int_2addr shl_int_2addr shr_int_2addr ushr_int_2addr
|
|
||||||
add_long_2addr sub_long_2addr mul_long_2addr div_long_2addr rem_long_2addr
|
|
||||||
and_long_2addr or_long_2addr xor_long_2addr shl_long_2addr shr_long_2addr ushr_long_2addr
|
|
||||||
add_float_2addr sub_float_2addr mul_float_2addr div_float_2addr rem_float_2addr
|
|
||||||
add_double_2addr sub_double_2addr mul_double_2addr div_double_2addr rem_double_2addr
|
|
||||||
add_int_lit16 rsub_int mul_int_lit16 div_int_lit16 rem_int_lit16 and_int_lit16 or_int_lit16 xor_int_lit16
|
|
||||||
add_int_lit8 rsub_int_lit8 mul_int_lit8 div_int_lit8 rem_int_lit8 and_int_lit8 or_int_lit8 xor_int_lit8
|
|
||||||
shl_int_lit8 shr_int_lit8 ushr_int_lit8
|
|
||||||
unused_e3 unused_e4 unused_e5 unused_e6 unused_e7 unused_e8 unused_e9 unused_ea unused_eb unused_ec
|
|
||||||
throw_verification_error execute_inline unused_ef invoke_direct_empty unused_f1
|
|
||||||
iget_quick iget_wide_quick iget_object_quick iput_quick iput_wide_quick iput_object_quick
|
|
||||||
invoke_virtual_quick invoke_virtual_quick_range invoke_super_quick invoke_super_quick_range
|
|
||||||
unused_fc unused_fd unused_fe unused_ff]
|
|
||||||
|
|
||||||
def init_dalvik
|
|
||||||
@valid_props[:canthrow] = true
|
|
||||||
[:i16, :i16_32hi, :i16_64hi, :i32, :iaa, :ib, :icc, :u16, :u32, :u64,
|
|
||||||
:r16, :ra, :raa, :rb, :rbb, :rcc, :rlist16, :rlist4, :rlist5,
|
|
||||||
:m16, :fld16, :typ16, :str16
|
|
||||||
].each { |a| @valid_args[a] = true }
|
|
||||||
@opcode_list = []
|
|
||||||
|
|
||||||
OPCODES.each_with_index { |n, b|
|
|
||||||
op = Opcode.new(n, b)
|
|
||||||
addop_args(op)
|
|
||||||
addop_props(op)
|
|
||||||
@opcode_list << op
|
|
||||||
}
|
|
||||||
|
|
||||||
raise "Internal error #{@opcode_list.length}" if @opcode_list.length != 256
|
|
||||||
end
|
|
||||||
alias init_latest init_dalvik
|
|
||||||
|
|
||||||
def addop_args(op)
|
|
||||||
fmt = case op.name
|
|
||||||
when 'goto'
|
|
||||||
:fmt10t
|
|
||||||
when 'nop', 'return_void'
|
|
||||||
:fmt10x
|
|
||||||
when 'const_4'
|
|
||||||
:fmt11n
|
|
||||||
when 'const_high16'
|
|
||||||
:fmt21h
|
|
||||||
when 'const_wide_high16'
|
|
||||||
:fmt21hh
|
|
||||||
when 'move_result', 'move_result_wide', 'move_result_object',
|
|
||||||
'move_exception', 'return', 'return_wide',
|
|
||||||
'return_object', 'monitor_enter', 'monitor_exit',
|
|
||||||
'throw'
|
|
||||||
:fmt11x
|
|
||||||
when 'move', 'move_wide', 'move_object', 'array_length',
|
|
||||||
'neg_int', 'not_int', 'neg_long', 'not_long',
|
|
||||||
'neg_float', 'neg_double', 'int_to_long',
|
|
||||||
'int_to_float', 'int_to_double', 'long_to_int',
|
|
||||||
'long_to_float', 'long_to_double', 'float_to_int',
|
|
||||||
'float_to_long', 'float_to_double', 'double_to_int',
|
|
||||||
'double_to_long', 'double_to_float', 'int_to_byte',
|
|
||||||
'int_to_char', 'int_to_short', 'add_int_2addr',
|
|
||||||
'sub_int_2addr', 'mul_int_2addr', 'div_int_2addr',
|
|
||||||
'rem_int_2addr', 'and_int_2addr', 'or_int_2addr',
|
|
||||||
'xor_int_2addr', 'shl_int_2addr', 'shr_int_2addr',
|
|
||||||
'ushr_int_2addr', 'add_long_2addr', 'sub_long_2addr',
|
|
||||||
'mul_long_2addr', 'div_long_2addr', 'rem_long_2addr',
|
|
||||||
'and_long_2addr', 'or_long_2addr', 'xor_long_2addr',
|
|
||||||
'shl_long_2addr', 'shr_long_2addr', 'ushr_long_2addr',
|
|
||||||
'add_float_2addr', 'sub_float_2addr', 'mul_float_2addr',
|
|
||||||
'div_float_2addr', 'rem_float_2addr',
|
|
||||||
'add_double_2addr', 'sub_double_2addr',
|
|
||||||
'mul_double_2addr', 'div_double_2addr',
|
|
||||||
'rem_double_2addr'
|
|
||||||
:fmt12x
|
|
||||||
when 'goto_16'
|
|
||||||
:fmt20t
|
|
||||||
when 'goto_32'
|
|
||||||
:fmt30t
|
|
||||||
when 'const_string'
|
|
||||||
:fmt21c_str
|
|
||||||
when 'const_class', 'check_cast',
|
|
||||||
'new_instance'
|
|
||||||
:fmt21c_typ
|
|
||||||
when 'sget', 'sget_wide', 'sget_object',
|
|
||||||
'sget_boolean', 'sget_byte', 'sget_char', 'sget_short',
|
|
||||||
'sput', 'sput_wide', 'sput_object', 'sput_boolean',
|
|
||||||
'sput_byte', 'sput_char', 'sput_short'
|
|
||||||
:fmt21c_fld
|
|
||||||
when 'const_16', 'const_wide_16'
|
|
||||||
:fmt21s
|
|
||||||
when 'if_eqz', 'if_nez', 'if_ltz', 'if_gez', 'if_gtz', 'if_lez'
|
|
||||||
:fmt21t
|
|
||||||
when 'fill_array_data', 'packed_switch', 'sparse_switch'
|
|
||||||
:fmt31t
|
|
||||||
when 'add_int_lit8', 'rsub_int_lit8', 'mul_int_lit8',
|
|
||||||
'div_int_lit8', 'rem_int_lit8', 'and_int_lit8',
|
|
||||||
'or_int_lit8', 'xor_int_lit8', 'shl_int_lit8',
|
|
||||||
'shr_int_lit8', 'ushr_int_lit8'
|
|
||||||
:fmt22b
|
|
||||||
when 'instance_of', 'new_array', 'iget', 'iget_wide',
|
|
||||||
'iget_object', 'iget_boolean', 'iget_byte',
|
|
||||||
'iget_char', 'iget_short', 'iput', 'iput_wide',
|
|
||||||
'iput_object', 'iput_boolean', 'iput_byte',
|
|
||||||
'iput_char', 'iput_short'
|
|
||||||
:fmt22c
|
|
||||||
when 'add_int_lit16', 'rsub_int', 'mul_int_lit16',
|
|
||||||
'div_int_lit16', 'rem_int_lit16', 'and_int_lit16',
|
|
||||||
'or_int_lit16', 'xor_int_lit16'
|
|
||||||
:fmt22s
|
|
||||||
when 'if_eq', 'if_ne', 'if_lt', 'if_ge', 'if_gt', 'if_le'
|
|
||||||
:fmt22t
|
|
||||||
when 'move_from16', 'move_wide_from16', 'move_object_from16'
|
|
||||||
:fmt22x
|
|
||||||
when 'cmpl_float', 'cmpg_float', 'cmpl_double', 'cmpg_double',
|
|
||||||
'cmp_long', 'aget', 'aget_wide', 'aget_object',
|
|
||||||
'aget_boolean', 'aget_byte', 'aget_char', 'aget_short',
|
|
||||||
'aput', 'aput_wide', 'aput_object', 'aput_boolean',
|
|
||||||
'aput_byte', 'aput_char', 'aput_short', 'add_int',
|
|
||||||
'sub_int', 'mul_int', 'div_int', 'rem_int', 'and_int',
|
|
||||||
'or_int', 'xor_int', 'shl_int', 'shr_int', 'ushr_int',
|
|
||||||
'add_long', 'sub_long', 'mul_long', 'div_long',
|
|
||||||
'rem_long', 'and_long', 'or_long', 'xor_long',
|
|
||||||
'shl_long', 'shr_long', 'ushr_long', 'add_float',
|
|
||||||
'sub_float', 'mul_float', 'div_float', 'rem_float',
|
|
||||||
'add_double', 'sub_double', 'mul_double', 'div_double',
|
|
||||||
'rem_double'
|
|
||||||
:fmt23x
|
|
||||||
when 'const', 'const_wide_32'
|
|
||||||
:fmt31i
|
|
||||||
when 'const_string_jumbo'
|
|
||||||
:fmt31c
|
|
||||||
when 'move_16', 'move_wide_16', 'move_object_16'
|
|
||||||
:fmt32x
|
|
||||||
when 'filled_new_array'
|
|
||||||
:fmt35ca
|
|
||||||
when 'invoke_virtual', 'invoke_super',
|
|
||||||
'invoke_direct', 'invoke_static', 'invoke_interface'
|
|
||||||
:fmt35c
|
|
||||||
when 'filled_new_array_range', 'invoke_virtual_range',
|
|
||||||
'invoke_super_range', 'invoke_direct_range',
|
|
||||||
'invoke_static_range', 'invoke_interface_range'
|
|
||||||
:fmt3rc
|
|
||||||
when 'const_wide'
|
|
||||||
:fmt51l
|
|
||||||
when 'throw_verification_error'
|
|
||||||
:fmt20bc
|
|
||||||
when 'iget_quick', 'iget_wide_quick', 'iget_object_quick',
|
|
||||||
'iput_quick', 'iput_wide_quick', 'iput_object_quick'
|
|
||||||
:fmt22cs
|
|
||||||
when 'invoke_virtual_quick', 'invoke_super_quick'
|
|
||||||
:fmt35ms
|
|
||||||
when 'invoke_virtual_quick_range', 'invoke_super_quick_range'
|
|
||||||
:fmt3rms
|
|
||||||
when 'execute_inline'
|
|
||||||
:fmt3inline
|
|
||||||
when 'invoke_direct_empty'
|
|
||||||
:fmt35c
|
|
||||||
when 'unused_3e', 'unused_3f', 'unused_40', 'unused_41',
|
|
||||||
'unused_42', 'unused_43', 'unused_73', 'unused_79',
|
|
||||||
'unused_7a', 'unused_e3', 'unused_e4', 'unused_e5',
|
|
||||||
'unused_e6', 'unused_e7', 'unused_e8', 'unused_e9',
|
|
||||||
'unused_ea', 'unused_eb', 'unused_ec', 'unused_ef',
|
|
||||||
'unused_f1', 'unused_fc', 'unused_fd', 'unused_fe',
|
|
||||||
'unused_ff'
|
|
||||||
:fmtUnknown
|
|
||||||
else
|
|
||||||
raise "Internal error #{op.name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
case fmt
|
|
||||||
when :fmt10x; op.args << :iaa
|
|
||||||
when :fmt12x; op.args << :ra << :rb
|
|
||||||
when :fmt11n; op.args << :ra << :ib
|
|
||||||
when :fmt11x; op.args << :raa
|
|
||||||
when :fmt10t; op.args << :iaa
|
|
||||||
when :fmt20t; op.args << :i16
|
|
||||||
when :fmt20bc; op.args << :iaa << :u16
|
|
||||||
when :fmt21c_str; op.args << :raa << :str16
|
|
||||||
when :fmt21c_typ; op.args << :raa << :typ16
|
|
||||||
when :fmt21c_fld; op.args << :raa << :fld16
|
|
||||||
when :fmt22x; op.args << :raa << :r16
|
|
||||||
when :fmt21s, :fmt21t; op.args << :raa << :i16
|
|
||||||
when :fmt21h; op.args << :raa << :i16_32hi
|
|
||||||
when :fmt21hh; op.args << :raa << :i16_64hi
|
|
||||||
when :fmt23x; op.args << :raa << :rbb << :rcc
|
|
||||||
when :fmt22b; op.args << :raa << :rbb << :icc
|
|
||||||
when :fmt22s, :fmt22t; op.args << :ra << :rb << :i16
|
|
||||||
when :fmt22c, :fmt22cs; op.args << :ra << :rb << :fld16
|
|
||||||
when :fmt30t; op.args << :i32
|
|
||||||
when :fmt31t, :fmt31c; op.args << :raa << :u32
|
|
||||||
when :fmt32x; op.args << :r16 << :r16
|
|
||||||
when :fmt31i; op.args << :raa << :i32
|
|
||||||
when :fmt35ca
|
|
||||||
op.args << :r16 << :rlist5
|
|
||||||
when :fmt35c, :fmt35ms
|
|
||||||
# rlist:
|
|
||||||
# nr of regs in :ib (max 5)
|
|
||||||
# regs: :ib.times { reg :i16 & 0xf ; :i16 >>= 4 }
|
|
||||||
# reg :ra if :ib == 5
|
|
||||||
op.args << :m16 << :rlist5
|
|
||||||
when :fmt3inline
|
|
||||||
op.args << :r16 << :rlist4
|
|
||||||
when :fmt3rc, :fmt3rms
|
|
||||||
# rlist = :r16, :r16+1, :r16+2, ..., :r16+:iaa-1
|
|
||||||
op.args << :r16 << :rlist16
|
|
||||||
when :fmt51l
|
|
||||||
# u64 = u16 | (u16 << 16) | ...
|
|
||||||
op.args << :raa << :u64
|
|
||||||
when :fmtUnknown
|
|
||||||
op.args << :iaa
|
|
||||||
else
|
|
||||||
raise "Internal error #{fmt.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_props(op)
|
|
||||||
case op.name
|
|
||||||
when 'nop', 'move', 'move_from16', 'move_16', 'move_wide',
|
|
||||||
'move_wide_from16', 'move_wide_16', 'move_object',
|
|
||||||
'move_object_from16', 'move_object_16', 'move_result',
|
|
||||||
'move_result_wide', 'move_result_object',
|
|
||||||
'move_exception', 'const_4', 'const_16', 'const',
|
|
||||||
'const_high16', 'const_wide_16', 'const_wide_32',
|
|
||||||
'const_wide', 'const_wide_high16', 'fill_array_data',
|
|
||||||
'cmpl_float', 'cmpg_float', 'cmpl_double',
|
|
||||||
'cmpg_double', 'cmp_long', 'neg_int', 'not_int',
|
|
||||||
'neg_long', 'not_long', 'neg_float', 'neg_double',
|
|
||||||
'int_to_long', 'int_to_float', 'int_to_double',
|
|
||||||
'long_to_int', 'long_to_float', 'long_to_double',
|
|
||||||
'float_to_int', 'float_to_long', 'float_to_double',
|
|
||||||
'double_to_int', 'double_to_long', 'double_to_float',
|
|
||||||
'int_to_byte', 'int_to_char', 'int_to_short', 'add_int',
|
|
||||||
'sub_int', 'mul_int', 'and_int', 'or_int', 'xor_int',
|
|
||||||
'shl_int', 'shr_int', 'ushr_int', 'add_long',
|
|
||||||
'sub_long', 'mul_long', 'and_long', 'or_long',
|
|
||||||
'xor_long', 'shl_long', 'shr_long', 'ushr_long',
|
|
||||||
'add_float', 'sub_float', 'mul_float', 'div_float',
|
|
||||||
'rem_float', 'add_double', 'sub_double', 'mul_double',
|
|
||||||
'div_double', 'rem_double', 'add_int_2addr',
|
|
||||||
'sub_int_2addr', 'mul_int_2addr', 'and_int_2addr',
|
|
||||||
'or_int_2addr', 'xor_int_2addr', 'shl_int_2addr',
|
|
||||||
'shr_int_2addr', 'ushr_int_2addr', 'add_long_2addr',
|
|
||||||
'sub_long_2addr', 'mul_long_2addr', 'and_long_2addr',
|
|
||||||
'or_long_2addr', 'xor_long_2addr', 'shl_long_2addr',
|
|
||||||
'shr_long_2addr', 'ushr_long_2addr', 'add_float_2addr',
|
|
||||||
'sub_float_2addr', 'mul_float_2addr', 'div_float_2addr',
|
|
||||||
'rem_float_2addr', 'add_double_2addr',
|
|
||||||
'sub_double_2addr', 'mul_double_2addr',
|
|
||||||
'div_double_2addr', 'rem_double_2addr', 'add_int_lit16',
|
|
||||||
'rsub_int', 'mul_int_lit16', 'and_int_lit16',
|
|
||||||
'or_int_lit16', 'xor_int_lit16', 'add_int_lit8',
|
|
||||||
'rsub_int_lit8', 'mul_int_lit8', 'and_int_lit8',
|
|
||||||
'or_int_lit8', 'xor_int_lit8', 'shl_int_lit8',
|
|
||||||
'shr_int_lit8', 'ushr_int_lit8'
|
|
||||||
# normal opcode, continues to next, nothing raised
|
|
||||||
when 'const_string', 'const_string_jumbo', 'const_class',
|
|
||||||
'monitor_enter', 'monitor_exit', 'check_cast',
|
|
||||||
'instance_of', 'array_length', 'new_instance',
|
|
||||||
'new_array', 'filled_new_array',
|
|
||||||
'filled_new_array_range', 'aget', 'aget_boolean',
|
|
||||||
'aget_byte', 'aget_char', 'aget_short', 'aget_wide',
|
|
||||||
'aget_object', 'aput', 'aput_boolean', 'aput_byte',
|
|
||||||
'aput_char', 'aput_short', 'aput_wide', 'aput_object',
|
|
||||||
'iget', 'iget_boolean', 'iget_byte', 'iget_char',
|
|
||||||
'iget_short', 'iget_wide', 'iget_object', 'iput',
|
|
||||||
'iput_boolean', 'iput_byte', 'iput_char', 'iput_short',
|
|
||||||
'iput_wide', 'iput_object', 'sget', 'sget_boolean',
|
|
||||||
'sget_byte', 'sget_char', 'sget_short', 'sget_wide',
|
|
||||||
'sget_object', 'sput', 'sput_boolean', 'sput_byte',
|
|
||||||
'sput_char', 'sput_short', 'sput_wide', 'sput_object',
|
|
||||||
'div_int', 'rem_int', 'div_long', 'rem_long',
|
|
||||||
'div_int_2addr', 'rem_int_2addr', 'div_long_2addr',
|
|
||||||
'rem_long_2addr', 'div_int_lit16', 'rem_int_lit16',
|
|
||||||
'div_int_lit8', 'rem_int_lit8'
|
|
||||||
op.props[:canthrow] = true
|
|
||||||
when 'invoke_virtual', 'invoke_virtual_range', 'invoke_super',
|
|
||||||
'invoke_super_range', 'invoke_direct',
|
|
||||||
'invoke_direct_range', 'invoke_static',
|
|
||||||
'invoke_static_range', 'invoke_interface',
|
|
||||||
'invoke_interface_range'
|
|
||||||
op.props[:canthrow] = true
|
|
||||||
op.props[:saveip] = true
|
|
||||||
op.props[:setip] = true
|
|
||||||
op.props[:stopexec] = true
|
|
||||||
when 'return_void', 'return', 'return_wide', 'return_object'
|
|
||||||
op.props[:setip] = true
|
|
||||||
op.props[:stopexec] = true
|
|
||||||
when 'throw'
|
|
||||||
op.props[:canthrow] = true
|
|
||||||
op.props[:stopexec] = true
|
|
||||||
when 'goto', 'goto_16', 'goto_32'
|
|
||||||
op.props[:setip] = true
|
|
||||||
op.props[:stopexec] = true
|
|
||||||
when 'if_eq', 'if_ne', 'if_lt', 'if_ge', 'if_gt', 'if_le',
|
|
||||||
'if_eqz', 'if_nez', 'if_ltz', 'if_gez', 'if_gtz',
|
|
||||||
'if_lez'
|
|
||||||
op.props[:setip] = true
|
|
||||||
when 'packed_switch', 'sparse_switch'
|
|
||||||
op.props[:setip] = true # if no table match, nostopexec
|
|
||||||
op.props[:setip] = true
|
|
||||||
when 'throw_verification_error'
|
|
||||||
op.props[:canthrow] = true
|
|
||||||
op.props[:stopexec] = true
|
|
||||||
when 'execute_inline'
|
|
||||||
when 'iget_quick', 'iget_wide_quick', 'iget_object_quick',
|
|
||||||
'iput_quick', 'iput_wide_quick', 'iput_object_quick'
|
|
||||||
op.props[:canthrow] = true
|
|
||||||
when 'invoke_virtual_quick', 'invoke_virtual_quick_range',
|
|
||||||
'invoke_super_quick', 'invoke_super_quick_range',
|
|
||||||
'invoke_direct_empty'
|
|
||||||
op.props[:canthrow] = true
|
|
||||||
op.props[:saveip] = true
|
|
||||||
op.props[:setip] = true
|
|
||||||
op.props[:stopexec] = true
|
|
||||||
when 'unused_3e', 'unused_3f', 'unused_40', 'unused_41',
|
|
||||||
'unused_42', 'unused_43', 'unused_73', 'unused_79',
|
|
||||||
'unused_7a', 'unused_e3', 'unused_e4', 'unused_e5',
|
|
||||||
'unused_e6', 'unused_e7', 'unused_e8', 'unused_e9',
|
|
||||||
'unused_ea', 'unused_eb', 'unused_ec', 'unused_ef',
|
|
||||||
'unused_f1', 'unused_fc', 'unused_fd', 'unused_fe',
|
|
||||||
'unused_ff'
|
|
||||||
op.props[:stopexec] = true
|
|
||||||
else
|
|
||||||
raise "Internal error #{op.name}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
# fix autorequire warning
|
|
||||||
class Metasm::Ia32 < Metasm::CPU
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/ia32/parse'
|
|
||||||
require 'metasm/cpu/ia32/encode'
|
|
||||||
require 'metasm/cpu/ia32/decode'
|
|
||||||
require 'metasm/cpu/ia32/render'
|
|
||||||
require 'metasm/cpu/ia32/compile_c'
|
|
||||||
require 'metasm/cpu/ia32/decompile'
|
|
||||||
require 'metasm/cpu/ia32/debug'
|
|
File diff suppressed because it is too large
Load Diff
@ -1,193 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ia32/opcodes'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Ia32
|
|
||||||
def dbg_register_pc
|
|
||||||
@dbg_register_pc ||= :eip
|
|
||||||
end
|
|
||||||
def dbg_register_sp
|
|
||||||
@dbg_register_sp ||= dbg_register_list[7]
|
|
||||||
end
|
|
||||||
def dbg_register_flags
|
|
||||||
@dbg_register_flags ||= :eflags
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_list
|
|
||||||
@dbg_register_list ||= [:eax, :ebx, :ecx, :edx, :esi, :edi, :ebp, :esp, :eip]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_size
|
|
||||||
@dbg_register_size ||= Hash.new(32).update(:cs => 16, :ds => 16, :es => 16, :fs => 16, :gs => 16)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_flag_list
|
|
||||||
@dbg_flag_list ||= [:c, :p, :a, :z, :s, :i, :d, :o]
|
|
||||||
end
|
|
||||||
|
|
||||||
DBG_FLAGS = { :c => 0, :p => 2, :a => 4, :z => 6, :s => 7, :t => 8, :i => 9, :d => 10, :o => 11 }
|
|
||||||
def dbg_get_flag(dbg, f)
|
|
||||||
(dbg.get_reg_value(dbg_register_flags) >> DBG_FLAGS[f]) & 1
|
|
||||||
end
|
|
||||||
def dbg_set_flag(dbg, f)
|
|
||||||
fl = dbg.get_reg_value(dbg_register_flags)
|
|
||||||
fl |= 1 << DBG_FLAGS[f]
|
|
||||||
dbg.set_reg_value(dbg_register_flags, fl)
|
|
||||||
end
|
|
||||||
def dbg_unset_flag(dbg, f)
|
|
||||||
fl = dbg.get_reg_value(dbg_register_flags)
|
|
||||||
fl &= ~(1 << DBG_FLAGS[f])
|
|
||||||
dbg.set_reg_value(dbg_register_flags, fl)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_enable_singlestep(dbg)
|
|
||||||
dbg_set_flag(dbg, :t) if dbg_get_flag(dbg, :t) == 0
|
|
||||||
end
|
|
||||||
def dbg_disable_singlestep(dbg)
|
|
||||||
dbg_unset_flag(dbg, :t) if dbg_get_flag(dbg, :t) != 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_enable_bp(dbg, bp)
|
|
||||||
case bp.type
|
|
||||||
when :bpx; dbg_enable_bpx( dbg, bp)
|
|
||||||
else dbg_enable_bphw(dbg, bp)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_disable_bp(dbg, bp)
|
|
||||||
case bp.type
|
|
||||||
when :bpx; dbg_disable_bpx( dbg, bp)
|
|
||||||
else dbg_disable_bphw(dbg, bp)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_enable_bpx(dbg, bp)
|
|
||||||
bp.internal[:previous] ||= dbg.memory[bp.address, 1]
|
|
||||||
dbg.memory[bp.address, 1] = "\xcc"
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_disable_bpx(dbg, bp)
|
|
||||||
dbg.memory[bp.address, 1] = bp.internal[:previous]
|
|
||||||
end
|
|
||||||
|
|
||||||
# allocate a debug register for a hwbp by checking the list of hwbp existing in dbg
|
|
||||||
def dbg_alloc_bphw(dbg, bp)
|
|
||||||
if not bp.internal[:dr]
|
|
||||||
may = [0, 1, 2, 3]
|
|
||||||
dbg.breakpoint_thread.values.each { |bb| may.delete bb.internal[:dr] }
|
|
||||||
raise 'alloc_bphw: no free debugregister' if may.empty?
|
|
||||||
bp.internal[:dr] = may.first
|
|
||||||
end
|
|
||||||
bp.internal[:type] ||= :x
|
|
||||||
bp.internal[:len] ||= 1
|
|
||||||
bp.internal[:dr]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_enable_bphw(dbg, bp)
|
|
||||||
nr = dbg_alloc_bphw(dbg, bp)
|
|
||||||
dr7 = dbg[:dr7]
|
|
||||||
l = { 1 => 0, 2 => 1, 4 => 3, 8 => 2 }[bp.internal[:len]]
|
|
||||||
rw = { :x => 0, :w => 1, :r => 3 }[bp.internal[:type]]
|
|
||||||
raise "enable_bphw: invalid breakpoint #{bp.inspect}" if not l or not rw
|
|
||||||
dr7 &= ~((15 << (16+4*nr)) | (3 << (2*nr))) # clear
|
|
||||||
dr7 |= ((l << 2) | rw) << (16+4*nr) # set drN len/rw
|
|
||||||
dr7 |= 3 << (2*nr) # enable global/local drN
|
|
||||||
|
|
||||||
dbg["dr#{nr}"] = bp.address
|
|
||||||
dbg[:dr7] = dr7
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_disable_bphw(dbg, bp)
|
|
||||||
nr = bp.internal[:dr]
|
|
||||||
dr7 = dbg[:dr7]
|
|
||||||
dr7 &= ~(3 << (2*nr))
|
|
||||||
dbg[:dr7] = dr7
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_check_pre_run(dbg)
|
|
||||||
if dbg[:dr6] == 0 and dbg[:dr7] == 0
|
|
||||||
dbg[:dr7] = 0x10000 # some OS (eg Windows) only return dr6 if dr7 != 0
|
|
||||||
end
|
|
||||||
dbg[:dr6] = 0 if dbg[:dr6] & 0x400f != 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_evt_bpx(dbg, b)
|
|
||||||
if b.address == dbg.pc-1
|
|
||||||
dbg.pc -= 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_find_bpx(dbg)
|
|
||||||
return if dbg[:dr6] & 0x4000 != 0
|
|
||||||
pc = dbg.pc
|
|
||||||
dbg.breakpoint[pc-1] || dbg.breakpoint[pc]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_find_hwbp(dbg)
|
|
||||||
dr6 = dbg[:dr6]
|
|
||||||
return if dr6 & 0xf == 0
|
|
||||||
dn = (0..3).find { |n| dr6 & (1 << n) }
|
|
||||||
dbg.breakpoint_thread.values.find { |b| b.internal[:dr] == dn }
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_need_stepover(dbg, addr, di)
|
|
||||||
di and ((di.instruction.prefix and di.instruction.prefix[:rep]) or di.opcode.props[:saveip])
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_end_stepout(dbg, addr, di)
|
|
||||||
di and di.opcode.name == 'ret'
|
|
||||||
end
|
|
||||||
|
|
||||||
# return (yield) a list of [addr, symbolic name]
|
|
||||||
def dbg_stacktrace(dbg, rec=500)
|
|
||||||
ret = []
|
|
||||||
s = dbg.addrname!(dbg.pc)
|
|
||||||
yield(dbg.pc, s) if block_given?
|
|
||||||
ret << [dbg.pc, s]
|
|
||||||
fp = dbg.get_reg_value(dbg_register_list[6])
|
|
||||||
stack = dbg.get_reg_value(dbg_register_list[7]) - 8
|
|
||||||
while fp > stack and fp <= stack+0x10000 and rec != 0
|
|
||||||
rec -= 1
|
|
||||||
ra = dbg.resolve_expr Indirection[fp+4, 4]
|
|
||||||
s = dbg.addrname!(ra)
|
|
||||||
yield(ra, s) if block_given?
|
|
||||||
ret << [ra, s]
|
|
||||||
stack = fp # ensure we walk the stack upwards
|
|
||||||
fp = dbg.resolve_expr Indirection[fp, 4]
|
|
||||||
end
|
|
||||||
ret
|
|
||||||
end
|
|
||||||
|
|
||||||
# retrieve the current function return value
|
|
||||||
# only valid at function exit
|
|
||||||
def dbg_func_retval(dbg)
|
|
||||||
dbg.get_reg_value(dbg_register_list[0])
|
|
||||||
end
|
|
||||||
def dbg_func_retval_set(dbg, val)
|
|
||||||
dbg.set_reg_value(dbg_register_list[0], val)
|
|
||||||
end
|
|
||||||
|
|
||||||
# retrieve the current function return address
|
|
||||||
# to be called only on entry of the subfunction
|
|
||||||
def dbg_func_retaddr(dbg)
|
|
||||||
dbg.memory_read_int(dbg_register_list[7])
|
|
||||||
end
|
|
||||||
def dbg_func_retaddr_set(dbg, ret)
|
|
||||||
dbg.memory_write_int(dbg_register_list[7], ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
# retrieve the current function arguments
|
|
||||||
# only valid at function entry (eg right after the call)
|
|
||||||
def dbg_func_arg(dbg, argnr)
|
|
||||||
dbg.memory_read_int(Expression[:esp, :+, 4*(argnr+1)])
|
|
||||||
end
|
|
||||||
def dbg_func_arg_set(dbg, argnr, arg)
|
|
||||||
dbg.memory_write_int(Expression[:esp, :+, 4*(argnr+1)], arg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load Diff
@ -1,564 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ia32/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Ia32
|
|
||||||
# temporarily setup dasm.address_binding so that backtracking
|
|
||||||
# stack-related offsets resolve in :frameptr (relative to func start)
|
|
||||||
def decompile_makestackvars(dasm, funcstart, blocks)
|
|
||||||
oldfuncbd = dasm.address_binding[funcstart]
|
|
||||||
dasm.address_binding[funcstart] = { :esp => :frameptr } # this would suffice, the rest here is just optimisation
|
|
||||||
|
|
||||||
patched_binding = [funcstart] # list of addresses to cleanup later
|
|
||||||
ebp_frame = true
|
|
||||||
|
|
||||||
# pretrace esp and ebp for each function block (cleared later)
|
|
||||||
# TODO with more than 1 unknown __stdcall ext func per path, esp -> unknown, which makes very ugly C (*esp-- = 12...); add heuristics ?
|
|
||||||
blocks.each { |block|
|
|
||||||
blockstart = block.address
|
|
||||||
if not dasm.address_binding[blockstart]
|
|
||||||
patched_binding << blockstart
|
|
||||||
dasm.address_binding[blockstart] = {}
|
|
||||||
foo = dasm.backtrace(:esp, blockstart, :snapshot_addr => funcstart)
|
|
||||||
if foo.length == 1 and ee = foo.first and ee.kind_of? Expression and (ee == Expression[:frameptr] or
|
|
||||||
(ee.lexpr == :frameptr and ee.op == :+ and ee.rexpr.kind_of? ::Integer))
|
|
||||||
dasm.address_binding[blockstart][:esp] = ee
|
|
||||||
end
|
|
||||||
if ebp_frame
|
|
||||||
foo = dasm.backtrace(:ebp, blockstart, :snapshot_addr => funcstart)
|
|
||||||
if foo.length == 1 and ee = foo.first and ee.kind_of? Expression and (ee == Expression[:frameptr] or
|
|
||||||
(ee.lexpr == :frameptr and ee.op == :+ and ee.rexpr.kind_of? ::Integer))
|
|
||||||
dasm.address_binding[blockstart][:ebp] = ee
|
|
||||||
else
|
|
||||||
ebp_frame = false # func does not use ebp as frame ptr, no need to bt for later blocks
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
yield block
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure
|
|
||||||
patched_binding.each { |a| dasm.address_binding.delete a }
|
|
||||||
dasm.address_binding[funcstart] = oldfuncbd if oldfuncbd
|
|
||||||
end
|
|
||||||
|
|
||||||
# list variable dependency for each block, remove useless writes
|
|
||||||
# returns { blockaddr => [list of vars that are needed by a following block] }
|
|
||||||
def decompile_func_finddeps(dcmp, blocks, func)
|
|
||||||
deps_r = {} ; deps_w = {} ; deps_to = {}
|
|
||||||
deps_subfunc = {} # things read/written by subfuncs
|
|
||||||
|
|
||||||
# find read/writes by each block
|
|
||||||
blocks.each { |b, to|
|
|
||||||
deps_r[b] = [] ; deps_w[b] = [] ; deps_to[b] = to
|
|
||||||
deps_subfunc[b] = []
|
|
||||||
|
|
||||||
blk = dcmp.dasm.decoded[b].block
|
|
||||||
blk.list.each { |di|
|
|
||||||
a = di.backtrace_binding.values
|
|
||||||
w = []
|
|
||||||
di.backtrace_binding.keys.each { |k|
|
|
||||||
case k
|
|
||||||
when ::Symbol; w |= [k]
|
|
||||||
else a |= Expression[k].externals # if dword [eax] <- 42, eax is read
|
|
||||||
end
|
|
||||||
}
|
|
||||||
a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI
|
|
||||||
|
|
||||||
deps_r[b] |= a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - deps_w[b]
|
|
||||||
deps_w[b] |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
|
|
||||||
}
|
|
||||||
stackoff = nil
|
|
||||||
blk.each_to_normal { |t|
|
|
||||||
t = dcmp.backtrace_target(t, blk.list.last.address)
|
|
||||||
next if not t = dcmp.c_parser.toplevel.symbol[t]
|
|
||||||
t.type = C::Function.new(C::BaseType.new(:int)) if not t.type.kind_of? C::Function # XXX this may seem a bit extreme, and yes, it is.
|
|
||||||
stackoff ||= Expression[dcmp.dasm.backtrace(:esp, blk.list.last.address, :snapshot_addr => blocks.first[0]).first, :-, :esp].reduce
|
|
||||||
|
|
||||||
# things that are needed by the subfunction
|
|
||||||
if t.has_attribute('fastcall')
|
|
||||||
a = t.type.args.to_a
|
|
||||||
dep = [:ecx, :edx]
|
|
||||||
dep.shift if not a[0] or a[0].has_attribute('unused')
|
|
||||||
dep.pop if not a[1] or a[1].has_attribute('unused')
|
|
||||||
deps_subfunc[b] |= dep
|
|
||||||
end
|
|
||||||
t.type.args.to_a.each { |arg|
|
|
||||||
if reg = arg.has_attribute('register')
|
|
||||||
deps_subfunc[b] |= [reg.to_sym]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if stackoff # last block instr == subfunction call
|
|
||||||
deps_r[b] |= deps_subfunc[b] - deps_w[b]
|
|
||||||
deps_w[b] |= [:eax, :ecx, :edx] # standard ABI
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bt = blocks.transpose
|
|
||||||
roots = bt[0] - bt[1].flatten # XXX jmp 1stblock ?
|
|
||||||
|
|
||||||
# find regs read and never written (must have been set by caller and are part of the func ABI)
|
|
||||||
uninitialized = lambda { |b, r, done|
|
|
||||||
if not deps_r[b]
|
|
||||||
elsif deps_r[b].include?(r)
|
|
||||||
blk = dcmp.dasm.decoded[b].block
|
|
||||||
bw = []
|
|
||||||
rdi = blk.list.find { |di|
|
|
||||||
a = di.backtrace_binding.values
|
|
||||||
w = []
|
|
||||||
di.backtrace_binding.keys.each { |k|
|
|
||||||
case k
|
|
||||||
when ::Symbol; w |= [k]
|
|
||||||
else a |= Expression[k].externals # if dword [eax] <- 42, eax is read
|
|
||||||
end
|
|
||||||
}
|
|
||||||
a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI
|
|
||||||
|
|
||||||
next true if (a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - bw).include? r
|
|
||||||
bw |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
|
|
||||||
false
|
|
||||||
}
|
|
||||||
if r == :eax and (rdi || blk.list.last).opcode.name == 'ret'
|
|
||||||
func.type.type = C::BaseType.new(:void)
|
|
||||||
false
|
|
||||||
elsif rdi and rdi.backtrace_binding[r]
|
|
||||||
false # mov al, 42 ; ret -> don't regarg eax
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
elsif deps_w[b].include?(r)
|
|
||||||
else
|
|
||||||
done << b
|
|
||||||
(deps_to[b] - done).find { |tb| uninitialized[tb, r, done] }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
regargs = []
|
|
||||||
register_symbols.each { |r|
|
|
||||||
if roots.find { |root| uninitialized[root, r, []] }
|
|
||||||
regargs << r
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO honor user-defined prototype if available (eg no, really, eax is not read in this function returning al)
|
|
||||||
regargs.sort_by { |r| r.to_s }.each { |r|
|
|
||||||
a = C::Variable.new(r.to_s, C::BaseType.new(:int, :unsigned))
|
|
||||||
a.add_attribute("register(#{r})")
|
|
||||||
func.type.args << a
|
|
||||||
}
|
|
||||||
|
|
||||||
# remove writes from a block if no following block read the value
|
|
||||||
dw = {}
|
|
||||||
deps_w.each { |b, deps|
|
|
||||||
dw[b] = deps.reject { |dep|
|
|
||||||
ret = true
|
|
||||||
done = []
|
|
||||||
todo = deps_to[b].dup
|
|
||||||
while a = todo.pop
|
|
||||||
next if done.include? a
|
|
||||||
done << a
|
|
||||||
if not deps_r[a] or deps_r[a].include? dep
|
|
||||||
ret = false
|
|
||||||
break
|
|
||||||
elsif not deps_w[a].include? dep
|
|
||||||
todo.concat deps_to[a]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dw
|
|
||||||
end
|
|
||||||
|
|
||||||
def decompile_blocks(dcmp, myblocks, deps, func, nextaddr = nil)
|
|
||||||
scope = func.initializer
|
|
||||||
func.type.args.each { |a| scope.symbol[a.name] = a }
|
|
||||||
stmts = scope.statements
|
|
||||||
blocks_toclean = myblocks.dup
|
|
||||||
func_entry = myblocks.first[0]
|
|
||||||
until myblocks.empty?
|
|
||||||
b, to = myblocks.shift
|
|
||||||
if l = dcmp.dasm.get_label_at(b)
|
|
||||||
stmts << C::Label.new(l)
|
|
||||||
end
|
|
||||||
|
|
||||||
# list of assignments [[dest reg, expr assigned]]
|
|
||||||
ops = []
|
|
||||||
# reg binding (reg => value, values.externals = regs at block start)
|
|
||||||
binding = {}
|
|
||||||
# Expr => CExpr
|
|
||||||
ce = lambda { |*e| dcmp.decompile_cexpr(Expression[Expression[*e].reduce], scope) }
|
|
||||||
# Expr => Expr.bind(binding) => CExpr
|
|
||||||
ceb = lambda { |*e| ce[Expression[*e].bind(binding)] }
|
|
||||||
|
|
||||||
# dumps a CExprs that implements an assignment to a reg (uses ops[], patches op => [reg, nil])
|
|
||||||
commit = lambda {
|
|
||||||
deps[b].map { |k|
|
|
||||||
[k, ops.rindex(ops.reverse.find { |r, v| r == k })]
|
|
||||||
}.sort_by { |k, i| i.to_i }.each { |k, i|
|
|
||||||
next if not i or not binding[k]
|
|
||||||
e = k
|
|
||||||
final = []
|
|
||||||
ops[0..i].reverse_each { |r, v|
|
|
||||||
final << r if not v
|
|
||||||
e = Expression[e].bind(r => v).reduce if not final.include? r
|
|
||||||
}
|
|
||||||
ops[i][1] = nil
|
|
||||||
binding.delete k
|
|
||||||
stmts << ce[k, :'=', e] if k != e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# returns an array to use as funcall arguments
|
|
||||||
get_func_args = lambda { |di, f|
|
|
||||||
# XXX see remarks in #finddeps
|
|
||||||
bt = dcmp.dasm.backtrace(:esp, di.address, :snapshot_addr => func_entry, :include_start => true)
|
|
||||||
stackoff = Expression[[bt, :+, @size/8], :-, :esp].bind(:esp => :frameptr).reduce rescue nil
|
|
||||||
args_todo = f.type.args.to_a.dup
|
|
||||||
args = []
|
|
||||||
if f.has_attribute('fastcall') # XXX DRY
|
|
||||||
if a = args_todo.shift
|
|
||||||
mask = (1 << (8*dcmp.c_parser.sizeof(a))) - 1
|
|
||||||
mask = 0 if a.has_attribute('unused')
|
|
||||||
args << Expression[:ecx, :&, mask]
|
|
||||||
end
|
|
||||||
if a = args_todo.shift
|
|
||||||
mask = (1 << (8*dcmp.c_parser.sizeof(a))) - 1 # char => dl
|
|
||||||
mask = 0 if a.has_attribute('unused')
|
|
||||||
args << Expression[:edx, :&, mask]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
args_todo.each { |a_|
|
|
||||||
if r = a_.has_attribute_var('register')
|
|
||||||
args << Expression[r.to_sym]
|
|
||||||
elsif stackoff.kind_of? Integer
|
|
||||||
args << Indirection[[:frameptr, :+, stackoff], @size/8]
|
|
||||||
stackoff += [dcmp.sizeof(a_), @size/8].max
|
|
||||||
else
|
|
||||||
args << Expression[0]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.type.varargs and f.type.args.last.type.pointer? and stackoff.kind_of? Integer
|
|
||||||
# check if last arg is a fmtstring
|
|
||||||
bt = dcmp.dasm.backtrace(args.last, di.address, :snapshot_addr => func_entry, :include_start => true)
|
|
||||||
if bt.length == 1 and s = dcmp.dasm.get_section_at(bt.first)
|
|
||||||
fmt = s[0].read(512)
|
|
||||||
fmt = fmt.unpack('v*').pack('C*') if dcmp.sizeof(f.type.args.last.type.untypedef.type) == 2
|
|
||||||
if fmt.index(?\0)
|
|
||||||
fmt = fmt[0...fmt.index(?\0)]
|
|
||||||
fmt.gsub('%%', '').count('%').times { # XXX %.*s etc..
|
|
||||||
args << Indirection[[:frameptr, :+, stackoff], @size/8]
|
|
||||||
stackoff += @size/8
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
args.map { |e| ceb[e] }
|
|
||||||
}
|
|
||||||
|
|
||||||
# go !
|
|
||||||
dcmp.dasm.decoded[b].block.list.each_with_index { |di, didx|
|
|
||||||
a = di.instruction.args
|
|
||||||
if di.opcode.props[:setip] and not di.opcode.props[:stopexec]
|
|
||||||
# conditional jump
|
|
||||||
commit[]
|
|
||||||
n = dcmp.backtrace_target(get_xrefs_x(dcmp.dasm, di).first, di.address)
|
|
||||||
if di.opcode.name =~ /^loop(.+)?/
|
|
||||||
cx = C::CExpression[:'--', ceb[:ecx]]
|
|
||||||
cc = $1 ? C::CExpression[cx, :'&&', ceb[decode_cc_to_expr($1)]] : cx
|
|
||||||
else
|
|
||||||
cc = ceb[decode_cc_to_expr(di.opcode.name[1..-1])]
|
|
||||||
end
|
|
||||||
# XXX switch/indirect/multiple jmp
|
|
||||||
stmts << C::If.new(C::CExpression[cc], C::Goto.new(n))
|
|
||||||
to.delete dcmp.dasm.normalize(n)
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
if di.opcode.name == 'mov'
|
|
||||||
# mov cr0 etc
|
|
||||||
a1, a2 = di.instruction.args
|
|
||||||
case a1
|
|
||||||
when Ia32::CtrlReg, Ia32::DbgReg, Ia32::TstReg, Ia32::SegReg
|
|
||||||
sz = a1.kind_of?(Ia32::SegReg) ? 16 : 32
|
|
||||||
if not dcmp.c_parser.toplevel.symbol["intrinsic_set_#{a1}"]
|
|
||||||
dcmp.c_parser.parse("void intrinsic_set_#{a1}(__int#{sz});")
|
|
||||||
end
|
|
||||||
f = dcmp.c_parser.toplevel.symbol["intrinsic_set_#{a1}"]
|
|
||||||
a2 = a2.symbolic(di)
|
|
||||||
a2 = [a2, :&, 0xffff] if sz == 16
|
|
||||||
stmts << C::CExpression.new(f, :funcall, [ceb[a2]], f.type.type)
|
|
||||||
next
|
|
||||||
end
|
|
||||||
case a2
|
|
||||||
when Ia32::CtrlReg, Ia32::DbgReg, Ia32::TstReg, Ia32::SegReg
|
|
||||||
if not dcmp.c_parser.toplevel.symbol["intrinsic_get_#{a2}"]
|
|
||||||
sz = a2.kind_of?(Ia32::SegReg) ? 16 : 32
|
|
||||||
dcmp.c_parser.parse("__int#{sz} intrinsic_get_#{a2}(void);")
|
|
||||||
end
|
|
||||||
f = dcmp.c_parser.toplevel.symbol["intrinsic_get_#{a2}"]
|
|
||||||
t = f.type.type
|
|
||||||
binding.delete a1.symbolic(di)
|
|
||||||
stmts << C::CExpression.new(ce[a1.symbolic(di)], :'=', C::CExpression.new(f, :funcall, [], t), t)
|
|
||||||
next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
case di.opcode.name
|
|
||||||
when 'ret'
|
|
||||||
commit[]
|
|
||||||
ret = nil
|
|
||||||
ret = C::CExpression[ceb[:eax]] unless func.type.type.kind_of? C::BaseType and func.type.type.name == :void
|
|
||||||
stmts << C::Return.new(ret)
|
|
||||||
when 'call' # :saveip
|
|
||||||
n = dcmp.backtrace_target(get_xrefs_x(dcmp.dasm, di).first, di.address)
|
|
||||||
args = []
|
|
||||||
if f = dcmp.c_parser.toplevel.symbol[n] and f.type.kind_of? C::Function and f.type.args
|
|
||||||
args = get_func_args[di, f]
|
|
||||||
elsif defined? @dasm_func_default_off and o = @dasm_func_default_off[[dcmp.dasm, di.address]] and o.kind_of? Integer and o > @size/8
|
|
||||||
f = C::Variable.new
|
|
||||||
f.type = C::Function.new(C::BaseType.new(:int), [])
|
|
||||||
((o/(@size/8))-1).times { f.type.args << C::Variable.new(nil,C::BaseType.new(:int)) }
|
|
||||||
args = get_func_args[di, f]
|
|
||||||
end
|
|
||||||
commit[]
|
|
||||||
#next if not di.block.to_subfuncret
|
|
||||||
|
|
||||||
if not n.kind_of? ::String or (f and not f.type.kind_of? C::Function)
|
|
||||||
# indirect funcall
|
|
||||||
fptr = ceb[n]
|
|
||||||
binding.delete n
|
|
||||||
proto = C::Function.new(C::BaseType.new(:int))
|
|
||||||
proto = f.type if f and f.type.kind_of? C::Function
|
|
||||||
f = C::CExpression[[fptr], C::Pointer.new(proto)]
|
|
||||||
elsif not f
|
|
||||||
# internal functions are predeclared, so this one is extern
|
|
||||||
f = C::Variable.new
|
|
||||||
f.name = n
|
|
||||||
f.type = C::Function.new(C::BaseType.new(:int))
|
|
||||||
if dcmp.recurse > 0
|
|
||||||
dcmp.c_parser.toplevel.symbol[n] = f
|
|
||||||
dcmp.c_parser.toplevel.statements << C::Declaration.new(f)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
commit[]
|
|
||||||
binding.delete :eax
|
|
||||||
e = C::CExpression[f, :funcall, args]
|
|
||||||
e = C::CExpression[ce[:eax], :'=', e, f.type.type] if deps[b].include? :eax and f.type.type != C::BaseType.new(:void)
|
|
||||||
stmts << e
|
|
||||||
when 'jmp'
|
|
||||||
#if di.comment.to_a.include? 'switch'
|
|
||||||
# n = di.instruction.args.first.symbolic(di)
|
|
||||||
# fptr = ceb[n]
|
|
||||||
# binding.delete n
|
|
||||||
# commit[]
|
|
||||||
# sw = C::Switch.new(fptr, C::Block.new(scope))
|
|
||||||
# di.block.to_normal.to_a.each { |addr|
|
|
||||||
# addr = dcmp.dasm.normalize addr
|
|
||||||
# to.delete addr
|
|
||||||
# next if not l = dcmp.dasm.get_label_at(addr)
|
|
||||||
# sw.body.statements << C::Goto.new(l)
|
|
||||||
# }
|
|
||||||
# stmts << sw
|
|
||||||
a = di.instruction.args.first
|
|
||||||
if a.kind_of? Expression
|
|
||||||
elsif not a.respond_to? :symbolic
|
|
||||||
stmts << C::Asm.new(di.instruction.to_s, nil, [], [], nil, nil)
|
|
||||||
else
|
|
||||||
n = di.instruction.args.first.symbolic(di)
|
|
||||||
fptr = ceb[n]
|
|
||||||
binding.delete n
|
|
||||||
commit[]
|
|
||||||
if fptr.kind_of? C::CExpression and fptr.type.pointer? and fptr.type.untypedef.type.kind_of? C::Function
|
|
||||||
proto = fptr.type.untypedef.type
|
|
||||||
args = get_func_args[di, fptr.type]
|
|
||||||
else
|
|
||||||
proto = C::Function.new(C::BaseType.new(:void))
|
|
||||||
fptr = C::CExpression[[fptr], C::Pointer.new(proto)]
|
|
||||||
args = []
|
|
||||||
end
|
|
||||||
ret = C::Return.new(C::CExpression[fptr, :funcall, args])
|
|
||||||
class << ret ; attr_accessor :from_instr end
|
|
||||||
ret.from_instr = di
|
|
||||||
stmts << ret
|
|
||||||
to = []
|
|
||||||
end
|
|
||||||
when 'lgdt'
|
|
||||||
if not dcmp.c_parser.toplevel.struct['segment_descriptor']
|
|
||||||
dcmp.c_parser.parse('struct segment_descriptor { __int16 limit; __int16 base0_16; __int8 base16_24; __int8 flags1; __int8 flags2_limit_16_20; __int8 base24_32; };')
|
|
||||||
dcmp.c_parser.parse('struct segment_table { __int16 size; struct segment_descriptor *table; } __attribute__((pack(2)));')
|
|
||||||
end
|
|
||||||
if not dcmp.c_parser.toplevel.symbol['intrinsic_lgdt']
|
|
||||||
dcmp.c_parser.parse('void intrinsic_lgdt(struct segment_table *);')
|
|
||||||
end
|
|
||||||
# need a way to transform arg => :frameptr+12
|
|
||||||
arg = di.backtrace_binding.keys.grep(Indirection).first.pointer
|
|
||||||
stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol['intrinsic_lgdt'], :funcall, [ceb[arg]], C::BaseType.new(:void))
|
|
||||||
when 'lidt'
|
|
||||||
if not dcmp.c_parser.toplevel.struct['interrupt_descriptor']
|
|
||||||
dcmp.c_parser.parse('struct interrupt_descriptor { __int16 offset0_16; __int16 segment; __int16 flags; __int16 offset16_32; };')
|
|
||||||
dcmp.c_parser.parse('struct interrupt_table { __int16 size; struct interrupt_descriptor *table; } __attribute__((pack(2)));')
|
|
||||||
end
|
|
||||||
if not dcmp.c_parser.toplevel.symbol['intrinsic_lidt']
|
|
||||||
dcmp.c_parser.parse('void intrinsic_lidt(struct interrupt_table *);')
|
|
||||||
end
|
|
||||||
arg = di.backtrace_binding.keys.grep(Indirection).first.pointer
|
|
||||||
stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol['intrinsic_lidt'], :funcall, [ceb[arg]], C::BaseType.new(:void))
|
|
||||||
when 'ltr', 'lldt'
|
|
||||||
if not dcmp.c_parser.toplevel.symbol["intrinsic_#{di.opcode.name}"]
|
|
||||||
dcmp.c_parser.parse("void intrinsic_#{di.opcode.name}(int);")
|
|
||||||
end
|
|
||||||
arg = di.backtrace_binding.keys.first
|
|
||||||
stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol["intrinsic_#{di.opcode.name}"], :funcall, [ceb[arg]], C::BaseType.new(:void))
|
|
||||||
when 'out'
|
|
||||||
sz = di.instruction.args.find { |a_| a_.kind_of? Ia32::Reg and a_.val == 0 }.sz
|
|
||||||
if not dcmp.c_parser.toplevel.symbol["intrinsic_out#{sz}"]
|
|
||||||
dcmp.c_parser.parse("void intrinsic_out#{sz}(unsigned short port, __int#{sz} value);")
|
|
||||||
end
|
|
||||||
port = di.instruction.args.grep(Expression).first || :edx
|
|
||||||
stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol["intrinsic_out#{sz}"], :funcall, [ceb[port], ceb[:eax]], C::BaseType.new(:void))
|
|
||||||
when 'in'
|
|
||||||
sz = di.instruction.args.find { |a_| a_.kind_of? Ia32::Reg and a_.val == 0 }.sz
|
|
||||||
if not dcmp.c_parser.toplevel.symbol["intrinsic_in#{sz}"]
|
|
||||||
dcmp.c_parser.parse("__int#{sz} intrinsic_in#{sz}(unsigned short port);")
|
|
||||||
end
|
|
||||||
port = di.instruction.args.grep(Expression).first || :edx
|
|
||||||
f = dcmp.c_parser.toplevel.symbol["intrinsic_in#{sz}"]
|
|
||||||
binding.delete :eax
|
|
||||||
stmts << C::CExpression.new(ce[:eax], :'=', C::CExpression.new(f, :funcall, [ceb[port]], f.type.type), f.type.type)
|
|
||||||
when 'sti', 'cli'
|
|
||||||
stmts << C::Asm.new(di.instruction.to_s, nil, [], [], nil, nil)
|
|
||||||
when /^(mov|sto|lod)s([bwdq])/
|
|
||||||
op, sz = $1, $2
|
|
||||||
commit[]
|
|
||||||
sz = { 'b' => 1, 'w' => 2, 'd' => 4, 'q' => 8 }[sz]
|
|
||||||
pt = C::Pointer.new(C::BaseType.new("__int#{sz*8}".to_sym))
|
|
||||||
|
|
||||||
blk = C::Block.new(scope)
|
|
||||||
case op
|
|
||||||
when 'mov'
|
|
||||||
blk.statements << C::CExpression[[:*, [[ceb[:edi]], pt]], :'=', [:*, [[ceb[:esi]], pt]]]
|
|
||||||
blk.statements << C::CExpression[ceb[:edi], :'=', [ceb[:edi], :+, [sz]]]
|
|
||||||
blk.statements << C::CExpression[ceb[:esi], :'=', [ceb[:esi], :+, [sz]]]
|
|
||||||
when 'sto'
|
|
||||||
blk.statements << C::CExpression[[:*, [[ceb[:edi]], pt]], :'=', ceb[:eax]]
|
|
||||||
blk.statements << C::CExpression[ceb[:edi], :'=', [ceb[:edi], :+, [sz]]]
|
|
||||||
when 'lod'
|
|
||||||
blk.statements << C::CExpression[ceb[:eax], :'=', [:*, [[ceb[:esi]], pt]]]
|
|
||||||
blk.statements << C::CExpression[ceb[:esi], :'=', [ceb[:esi], :+, [sz]]]
|
|
||||||
#when 'sca'
|
|
||||||
#when 'cmp'
|
|
||||||
end
|
|
||||||
|
|
||||||
case (di.instruction.prefix || {})[:rep]
|
|
||||||
when nil
|
|
||||||
stmts.concat blk.statements
|
|
||||||
when 'rep'
|
|
||||||
blk.statements << C::CExpression[ceb[:ecx], :'=', [ceb[:ecx], :-, [1]]]
|
|
||||||
stmts << C::While.new(C::CExpression[ceb[:ecx]], blk)
|
|
||||||
#when 'repz' # sca/cmp only
|
|
||||||
#when 'repnz'
|
|
||||||
end
|
|
||||||
next
|
|
||||||
else
|
|
||||||
bd = get_fwdemu_binding(di)
|
|
||||||
if di.backtrace_binding[:incomplete_binding]
|
|
||||||
commit[]
|
|
||||||
stmts << C::Asm.new(di.instruction.to_s, nil, nil, nil, nil, nil)
|
|
||||||
else
|
|
||||||
update = {}
|
|
||||||
bd.each { |k, v|
|
|
||||||
if k.kind_of? ::Symbol and not deps[b].include? k
|
|
||||||
ops << [k, v]
|
|
||||||
update[k] = Expression[Expression[v].bind(binding).reduce]
|
|
||||||
else
|
|
||||||
stmts << ceb[k, :'=', v]
|
|
||||||
stmts.pop if stmts.last.kind_of? C::Variable # [:eflag_s, :=, :unknown].reduce
|
|
||||||
end
|
|
||||||
}
|
|
||||||
binding.update update
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
commit[]
|
|
||||||
|
|
||||||
case to.length
|
|
||||||
when 0
|
|
||||||
if not myblocks.empty? and not %w[ret jmp].include? dcmp.dasm.decoded[b].block.list.last.instruction.opname
|
|
||||||
puts " block #{Expression[b]} has no to and don't end in ret"
|
|
||||||
end
|
|
||||||
when 1
|
|
||||||
if (myblocks.empty? ? nextaddr != to[0] : myblocks.first.first != to[0])
|
|
||||||
stmts << C::Goto.new(dcmp.dasm.auto_label_at(to[0], 'unknown_goto'))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
puts " block #{Expression[b]} with multiple to"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# cleanup di.bt_binding (we set :frameptr etc in those, this may confuse the dasm)
|
|
||||||
blocks_toclean.each { |b_, to_|
|
|
||||||
dcmp.dasm.decoded[b_].block.list.each { |di|
|
|
||||||
di.backtrace_binding = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def decompile_check_abi(dcmp, entry, func)
|
|
||||||
a = func.type.args || []
|
|
||||||
a.delete_if { |arg| arg.has_attribute_var('register') and arg.has_attribute('unused') }
|
|
||||||
ra = a.map { |arg| arg.has_attribute_var('register') }.compact
|
|
||||||
if (a.length == 1 and ra == ['ecx']) or (a.length >= 2 and ra.sort == ['ecx', 'edx'])
|
|
||||||
func.add_attribute 'fastcall'
|
|
||||||
# reorder args
|
|
||||||
ecx = a.find { |arg| arg.has_attribute_var('register') == 'ecx' }
|
|
||||||
edx = a.find { |arg| arg.has_attribute_var('register') == 'edx' }
|
|
||||||
a.insert(0, a.delete(ecx))
|
|
||||||
a.insert(1, a.delete(edx)) if edx
|
|
||||||
end
|
|
||||||
|
|
||||||
if not f = dcmp.dasm.function[entry] or not f.return_address
|
|
||||||
#func.add_attribute 'noreturn'
|
|
||||||
else
|
|
||||||
adj = f.return_address.map { |ra_| dcmp.dasm.backtrace(:esp, ra_, :include_start => true, :stopaddr => entry) }.flatten.uniq
|
|
||||||
if adj.length == 1 and so = Expression[adj.first, :-, :esp].reduce and so.kind_of? ::Integer
|
|
||||||
argsz = a.map { |fa|
|
|
||||||
next if not fa.stackoff
|
|
||||||
(fa.stackoff + [dcmp.sizeof(fa), dcmp.c_parser.typesize[:ptr]].max-1) / dcmp.c_parser.typesize[:ptr]
|
|
||||||
}.compact.max.to_i
|
|
||||||
so /= dcmp.dasm.cpu.size/8
|
|
||||||
so -= 1
|
|
||||||
if so > argsz
|
|
||||||
aso = a.empty? ? 0 : a.last.stackoff.to_i + dcmp.c_parser.typesize[:ptr]
|
|
||||||
(so-argsz).times {
|
|
||||||
a << C::Variable.new(dcmp.stackoff_to_varname(aso), C::BaseType.new(:int))
|
|
||||||
a.last.add_attribute('unused')
|
|
||||||
aso += dcmp.sizeof(a.last)
|
|
||||||
}
|
|
||||||
argsz = so
|
|
||||||
end
|
|
||||||
case so
|
|
||||||
when 0
|
|
||||||
when argsz
|
|
||||||
func.add_attribute 'stdcall' if not func.has_attribute('fastcall')
|
|
||||||
else
|
|
||||||
func.add_attribute "stackoff:#{so*dcmp.dasm.cpu.size/8}"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
func.add_attribute "breakstack:#{adj.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,320 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ia32/opcodes'
|
|
||||||
require 'metasm/encode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Ia32
|
|
||||||
class InvalidModRM < Exception ; end
|
|
||||||
class ModRM
|
|
||||||
# returns the byte representing the register encoded as modrm
|
|
||||||
# works with Reg/SimdReg
|
|
||||||
def self.encode_reg(reg, mregval = 0)
|
|
||||||
0xc0 | (mregval << 3) | reg.val
|
|
||||||
end
|
|
||||||
|
|
||||||
# The argument is an integer representing the 'reg' field of the mrm
|
|
||||||
#
|
|
||||||
# caller is responsible for setting the adsz
|
|
||||||
# returns an array, 1 element per possible immediate size (for un-reduce()able Expression)
|
|
||||||
def encode(reg = 0, endianness = :little)
|
|
||||||
reg = reg.val if reg.kind_of? Argument
|
|
||||||
case @adsz
|
|
||||||
when 16; encode16(reg, endianness)
|
|
||||||
when 32; encode32(reg, endianness)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def encode16(reg, endianness)
|
|
||||||
if not b
|
|
||||||
# imm only
|
|
||||||
return [EncodedData.new << (6 | (reg << 3)) << @imm.encode(:u16, endianness)]
|
|
||||||
end
|
|
||||||
|
|
||||||
imm = @imm.reduce if self.imm
|
|
||||||
imm = nil if imm == 0
|
|
||||||
ret = EncodedData.new
|
|
||||||
ret <<
|
|
||||||
case [@b.val, (@i.val if i)]
|
|
||||||
when [3, 6], [6, 3]; 0
|
|
||||||
when [3, 7], [7, 3]; 1
|
|
||||||
when [5, 6], [6, 5]; 2
|
|
||||||
when [5, 7], [7, 5]; 3
|
|
||||||
when [6, nil]; 4
|
|
||||||
when [7, nil]; 5
|
|
||||||
when [5, nil]
|
|
||||||
imm ||= 0
|
|
||||||
6
|
|
||||||
when [3, nil]; 7
|
|
||||||
else raise InvalidModRM, 'invalid modrm16'
|
|
||||||
end
|
|
||||||
|
|
||||||
# add bits in the first octet of ret.data (1.9 compatibility layer)
|
|
||||||
or_bits = lambda { |v| # rape me
|
|
||||||
if ret.data[0].kind_of? Integer
|
|
||||||
ret.data[0] |= v
|
|
||||||
else
|
|
||||||
ret.data[0] = (ret.data[0].unpack('C').first | v).chr
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
or_bits[reg << 3]
|
|
||||||
|
|
||||||
if imm
|
|
||||||
case Expression.in_range?(imm, :i8)
|
|
||||||
when true
|
|
||||||
or_bits[1 << 6]
|
|
||||||
[ret << Expression.encode_imm(imm, :i8, endianness)]
|
|
||||||
when false
|
|
||||||
or_bits[2 << 6]
|
|
||||||
[ret << Expression.encode_imm(imm, :a16, endianness)]
|
|
||||||
when nil
|
|
||||||
rets = ret.dup
|
|
||||||
or_bits[1<<6]
|
|
||||||
ret << @imm.encode(:i8, endianness)
|
|
||||||
ret, rets = rets, ret # or_bits uses ret
|
|
||||||
or_bits[2<<6]
|
|
||||||
ret << @imm.encode(:a16, endianness)
|
|
||||||
[ret, rets]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
[ret]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode32(reg, endianness)
|
|
||||||
# 0 => [ [0 ], [1 ], [2 ], [3 ], [:sib ], [:i32 ], [6 ], [7 ] ], \
|
|
||||||
# 1 => [ [0, :i8 ], [1, :i8 ], [2, :i8 ], [3, :i8 ], [:sib, :i8 ], [5, :i8 ], [6, :i8 ], [7, :i8 ] ], \
|
|
||||||
# 2 => [ [0, :i32], [1, :i32], [2, :i32], [3, :i32], [:sib, :i32], [5, :i32], [6, :i32], [7, :i32] ]
|
|
||||||
#
|
|
||||||
# b => 0 1 2 3 4 5+i|i 6 7
|
|
||||||
# i => 0 1 2 3 nil 5 6 7
|
|
||||||
|
|
||||||
ret = EncodedData.new << (reg << 3)
|
|
||||||
|
|
||||||
# add bits in the first octet of ret.data (1.9 compatibility layer)
|
|
||||||
or_bits = lambda { |v| # rape me
|
|
||||||
if ret.data[0].kind_of? Integer
|
|
||||||
ret.data[0] |= v
|
|
||||||
else
|
|
||||||
ret.data[0] = (ret.data[0].unpack('C').first | v).chr
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if not self.b and not self.i
|
|
||||||
or_bits[5]
|
|
||||||
[ret << @imm.encode(:a32, endianness)]
|
|
||||||
|
|
||||||
elsif not self.b and self.s != 1
|
|
||||||
# sib with no b
|
|
||||||
raise EncodeError, "Invalid ModRM #{self}" if @i.val == 4
|
|
||||||
or_bits[4]
|
|
||||||
s = {8=>3, 4=>2, 2=>1}[@s]
|
|
||||||
imm = self.imm || Expression[0]
|
|
||||||
fu = (s << 6) | (@i.val << 3) | 5
|
|
||||||
fu = fu.chr if s >= 2 # rb1.9 encoding fix
|
|
||||||
[ret << fu << imm.encode(:a32, endianness)]
|
|
||||||
else
|
|
||||||
imm = @imm.reduce if self.imm
|
|
||||||
imm = nil if imm == 0
|
|
||||||
|
|
||||||
if not self.i or (not self.b and self.s == 1)
|
|
||||||
# no sib byte (except for [esp])
|
|
||||||
b = self.b || self.i
|
|
||||||
|
|
||||||
or_bits[b.val]
|
|
||||||
ret << 0x24 if b.val == 4
|
|
||||||
else
|
|
||||||
# sib
|
|
||||||
or_bits[4]
|
|
||||||
|
|
||||||
i, b = @i, @b
|
|
||||||
b, i = i, b if @s == 1 and (i.val == 4 or b.val == 5)
|
|
||||||
|
|
||||||
raise EncodeError, "Invalid ModRM #{self}" if i.val == 4
|
|
||||||
|
|
||||||
s = {8=>3, 4=>2, 2=>1, 1=>0}[@s]
|
|
||||||
fu = (s << 6) | (i.val << 3) | b.val
|
|
||||||
fu = fu.chr if s >= 2 # rb1.9 encoding fix
|
|
||||||
ret << fu
|
|
||||||
end
|
|
||||||
|
|
||||||
imm ||= 0 if b.val == 5
|
|
||||||
if imm
|
|
||||||
case Expression.in_range?(imm, :i8)
|
|
||||||
when true
|
|
||||||
or_bits[1<<6]
|
|
||||||
[ret << Expression.encode_imm(imm, :i8, endianness)]
|
|
||||||
when false
|
|
||||||
or_bits[2<<6]
|
|
||||||
[ret << Expression.encode_imm(imm, :a32, endianness)]
|
|
||||||
when nil
|
|
||||||
rets = ret.dup
|
|
||||||
or_bits[1<<6]
|
|
||||||
ret << @imm.encode(:i8, endianness)
|
|
||||||
rets, ret = ret, rets # or_bits[] modifies ret directly
|
|
||||||
or_bits[2<<6]
|
|
||||||
ret << @imm.encode(:a32, endianness)
|
|
||||||
[ret, rets]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
[ret]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Farptr
|
|
||||||
def encode(endianness, atype)
|
|
||||||
@addr.encode(atype, endianness) << @seg.encode(:u16, endianness)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns all forms of the encoding of instruction i using opcode op
|
|
||||||
# program may be used to create a new label for relative jump/call
|
|
||||||
def encode_instr_op(program, i, op)
|
|
||||||
base = op.bin.dup
|
|
||||||
oi = op.args.zip(i.args)
|
|
||||||
set_field = lambda { |f, v|
|
|
||||||
v ||= 0 # ST => ST(0)
|
|
||||||
fld = op.fields[f]
|
|
||||||
base[fld[0]] |= v << fld[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
size = i.prefix[:sz] || @size
|
|
||||||
|
|
||||||
#
|
|
||||||
# handle prefixes and bit fields
|
|
||||||
#
|
|
||||||
pfx = i.prefix.map { |k, v|
|
|
||||||
case k
|
|
||||||
when :jmp; {:jmp => 0x3e, :nojmp => 0x2e}[v]
|
|
||||||
when :lock; 0xf0
|
|
||||||
when :rep; {'repnz' => 0xf2, 'repz' => 0xf3, 'rep' => 0xf2}[v]
|
|
||||||
when :jmphint; {'hintjmp' => 0x3e, 'hintnojmp' => 0x2e}[v]
|
|
||||||
when :seg; [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][v.val]
|
|
||||||
end
|
|
||||||
}.compact.pack 'C*'
|
|
||||||
|
|
||||||
if op.name == 'movsx' or op.name == 'movzx'
|
|
||||||
pfx << 0x66 if size == 48-i.args[0].sz
|
|
||||||
elsif op.name == 'crc32'
|
|
||||||
pfx << 0x66 if size == 48-i.args[1].sz
|
|
||||||
else
|
|
||||||
opsz = op.props[:argsz]
|
|
||||||
oi.each { |oa, ia|
|
|
||||||
case oa
|
|
||||||
when :reg, :reg_eax, :modrm, :mrm_imm
|
|
||||||
raise EncodeError, "Incompatible arg size in #{i}" if ia.sz and opsz and opsz != ia.sz
|
|
||||||
opsz = ia.sz
|
|
||||||
end
|
|
||||||
}
|
|
||||||
pfx << 0x66 if (op.props[:opsz] and size == 48 - op.props[:opsz]) or
|
|
||||||
(not op.props[:argsz] and opsz and size == 48 - opsz)
|
|
||||||
opsz ||= op.props[:opsz]
|
|
||||||
end
|
|
||||||
opsz ||= size
|
|
||||||
|
|
||||||
if op.props[:adsz] and size == 48 - op.props[:adsz]
|
|
||||||
pfx << 0x67
|
|
||||||
adsz = 48 - size
|
|
||||||
end
|
|
||||||
adsz ||= size
|
|
||||||
# addrsize override / segment override
|
|
||||||
if mrm = i.args.grep(ModRM).first
|
|
||||||
if not op.props[:adsz] and ((mrm.b and mrm.b.sz == 48 - adsz) or (mrm.i and mrm.i.sz == 48 - adsz))
|
|
||||||
pfx << 0x67
|
|
||||||
adsz = 48 - adsz
|
|
||||||
end
|
|
||||||
pfx << [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][mrm.seg.val] if mrm.seg
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# encode embedded arguments
|
|
||||||
#
|
|
||||||
postponed = []
|
|
||||||
oi.each { |oa, ia|
|
|
||||||
case oa
|
|
||||||
when :reg, :seg3, :seg3A, :seg2, :seg2A, :eeec, :eeed, :eeet, :regfp, :regmmx, :regxmm, :regymm
|
|
||||||
# field arg
|
|
||||||
set_field[oa, ia.val]
|
|
||||||
pfx << 0x66 if oa == :regmmx and op.props[:xmmx] and ia.sz == 128
|
|
||||||
when :vexvreg, :vexvxmm, :vexvymm
|
|
||||||
set_field[:vex_vvvv, ia.val ^ 0xf]
|
|
||||||
when :imm_val1, :imm_val3, :reg_cl, :reg_eax, :reg_dx, :regfp0
|
|
||||||
# implicit
|
|
||||||
else
|
|
||||||
postponed << [oa, ia]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if !(op.args & [:modrm, :modrmmmx, :modrmxmm, :modrmymm]).empty?
|
|
||||||
# reg field of modrm
|
|
||||||
regval = (base[-1] >> 3) & 7
|
|
||||||
base.pop
|
|
||||||
end
|
|
||||||
|
|
||||||
# convert label name for jmp/call/loop to relative offset
|
|
||||||
if op.props[:setip] and op.name[0, 3] != 'ret' and i.args.first.kind_of? Expression
|
|
||||||
postlabel = program.new_label('post'+op.name)
|
|
||||||
target = postponed.first[1]
|
|
||||||
target = target.rexpr if target.kind_of? Expression and target.op == :+ and not target.lexpr
|
|
||||||
postponed.first[1] = Expression[target, :-, postlabel]
|
|
||||||
end
|
|
||||||
|
|
||||||
pfx << op.props[:needpfx] if op.props[:needpfx]
|
|
||||||
|
|
||||||
#
|
|
||||||
# append other arguments
|
|
||||||
#
|
|
||||||
ret = EncodedData.new(pfx + base.pack('C*'))
|
|
||||||
|
|
||||||
postponed.each { |oa, ia|
|
|
||||||
case oa
|
|
||||||
when :farptr; ed = ia.encode(@endianness, "a#{opsz}".to_sym)
|
|
||||||
when :modrm, :modrmmmx, :modrmxmm, :modrmymm
|
|
||||||
if ia.kind_of? ModRM
|
|
||||||
ed = ia.encode(regval, @endianness)
|
|
||||||
if ed.kind_of?(::Array)
|
|
||||||
if ed.length > 1
|
|
||||||
# we know that no opcode can have more than 1 modrm
|
|
||||||
ary = []
|
|
||||||
ed.each { |m|
|
|
||||||
ary << (ret.dup << m)
|
|
||||||
}
|
|
||||||
ret = ary
|
|
||||||
next
|
|
||||||
else
|
|
||||||
ed = ed.first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ed = ModRM.encode_reg(ia, regval)
|
|
||||||
end
|
|
||||||
when :mrm_imm; ed = ia.imm.encode("a#{adsz}".to_sym, @endianness)
|
|
||||||
when :i8, :u8, :u16; ed = ia.encode(oa, @endianness)
|
|
||||||
when :i; ed = ia.encode("a#{opsz}".to_sym, @endianness)
|
|
||||||
when :i4xmm, :i4ymm; ed = ia.val << 4 # u8
|
|
||||||
else raise SyntaxError, "Internal error: want to encode field #{oa.inspect} as arg in #{i}"
|
|
||||||
end
|
|
||||||
|
|
||||||
if ret.kind_of?(::Array)
|
|
||||||
ret.each { |e| e << ed }
|
|
||||||
else
|
|
||||||
ret << ed
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
# we know that no opcode with setip accept both modrm and immediate arg, so ret is not an ::Array
|
|
||||||
ret.add_export(postlabel, ret.virtsize) if postlabel
|
|
||||||
|
|
||||||
ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,281 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
# The ia32 aka x86 CPU
|
|
||||||
# currently limited to 16 and 32bit modes
|
|
||||||
class Ia32 < CPU
|
|
||||||
|
|
||||||
# some ruby magic to declare classes with index -> name association (registers)
|
|
||||||
class Argument
|
|
||||||
class << self
|
|
||||||
# for subclasses
|
|
||||||
attr_accessor :i_to_s, :s_to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
# index -> name, name -> index
|
|
||||||
def self.simple_map(a)
|
|
||||||
# { 1 => 'dr1' }
|
|
||||||
@i_to_s = Hash[*a.flatten]
|
|
||||||
# { 'dr1' => 1 }
|
|
||||||
@s_to_i = @i_to_s.invert
|
|
||||||
|
|
||||||
class_eval {
|
|
||||||
attr_accessor :val
|
|
||||||
def initialize(v)
|
|
||||||
raise Exception, "invalid #{self.class} #{v}" unless self.class.i_to_s[v]
|
|
||||||
@val = v
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
self.class == o.class and val == o.val
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.from_str(s) new(@s_to_i[s]) end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# size -> (index -> name), name -> [index, size]
|
|
||||||
def self.double_map(h)
|
|
||||||
# { 32 => { 1 => 'ecx' } }
|
|
||||||
@i_to_s = h
|
|
||||||
# { 'ecx' => [1, 32] }
|
|
||||||
@s_to_i = {} ; @i_to_s.each { |sz, hh| hh.each_with_index { |r, i| @s_to_i[r] = [i, sz] } }
|
|
||||||
|
|
||||||
class_eval {
|
|
||||||
attr_accessor :val, :sz
|
|
||||||
def initialize(v, sz)
|
|
||||||
raise Exception, "invalid #{self.class} #{sz}/#{v}" unless self.class.i_to_s[sz] and self.class.i_to_s[sz][v]
|
|
||||||
@val = v
|
|
||||||
@sz = sz
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
self.class == o.class and val == o.val and sz == o.sz
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.from_str(s)
|
|
||||||
raise "Bad #{name} #{s.inspect}" if not x = @s_to_i[s]
|
|
||||||
new(*x)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# segment register: es, cs, ss, ds, fs, gs and the theoretical segr6/7
|
|
||||||
class SegReg < Argument
|
|
||||||
simple_map((0..7).zip(%w(es cs ss ds fs gs segr6 segr7)))
|
|
||||||
end
|
|
||||||
|
|
||||||
# debug register (dr0..dr3, dr6, dr7), and theoretical dr4/5
|
|
||||||
class DbgReg < Argument
|
|
||||||
simple_map((0..7).map { |i| [i, "dr#{i}"] })
|
|
||||||
end
|
|
||||||
|
|
||||||
# control register (cr0, cr2, cr3, cr4) and theoretical cr1/5/6/7
|
|
||||||
class CtrlReg < Argument
|
|
||||||
simple_map((0..7).map { |i| [i, "cr#{i}"] })
|
|
||||||
end
|
|
||||||
|
|
||||||
# test registers (tr0..tr7) (undocumented)
|
|
||||||
class TstReg < Argument
|
|
||||||
simple_map((0..7).map { |i| [i, "tr#{i}"] })
|
|
||||||
end
|
|
||||||
|
|
||||||
# floating point registers
|
|
||||||
class FpReg < Argument
|
|
||||||
simple_map((0..7).map { |i| [i, "ST(#{i})"] } << [nil, 'ST'])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Single Instr Multiple Data register (mm0..mm7, xmm0..xmm7, ymm0..ymm7)
|
|
||||||
class SimdReg < Argument
|
|
||||||
double_map 64 => (0..7).map { |n| "mm#{n}" },
|
|
||||||
128 => (0..7).map { |n| "xmm#{n}" },
|
|
||||||
256 => (0..7).map { |n| "ymm#{n}" }
|
|
||||||
def symbolic(di=nil) ; to_s.to_sym end
|
|
||||||
end
|
|
||||||
|
|
||||||
# general purpose registers, all sizes
|
|
||||||
class Reg < Argument
|
|
||||||
double_map 8 => %w{ al cl dl bl ah ch dh bh},
|
|
||||||
16 => %w{ ax cx dx bx sp bp si di},
|
|
||||||
32 => %w{eax ecx edx ebx esp ebp esi edi}
|
|
||||||
|
|
||||||
Sym = @i_to_s[32].map { |s| s.to_sym }
|
|
||||||
|
|
||||||
# returns a symbolic representation of the register:
|
|
||||||
# eax => :eax
|
|
||||||
# cx => :ecx & 0xffff
|
|
||||||
# ah => (:eax >> 8) & 0xff
|
|
||||||
def symbolic(di=nil)
|
|
||||||
s = Sym[@val]
|
|
||||||
if @sz == 8 and to_s[-1] == ?h
|
|
||||||
Expression[[Sym[@val-4], :>>, 8], :&, 0xff]
|
|
||||||
elsif @sz == 8
|
|
||||||
Expression[s, :&, 0xff]
|
|
||||||
elsif @sz == 16
|
|
||||||
Expression[s, :&, 0xffff]
|
|
||||||
else
|
|
||||||
s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# checks if two registers have bits in common
|
|
||||||
def share?(other)
|
|
||||||
other.val % (other.sz >> 1) == @val % (@sz >> 1) and (other.sz != @sz or @sz != 8 or other.val == @val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# a far pointer
|
|
||||||
# an immediate (numeric) pointer and an immediate segment selector
|
|
||||||
class Farptr < Argument
|
|
||||||
attr_accessor :seg, :addr
|
|
||||||
def initialize(seg, addr)
|
|
||||||
@seg, @addr = seg, addr
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
self.class == o.class and seg == o.seg and addr == o.addr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ModRM represents indirections in x86 (eg dword ptr [eax+4*ebx+12h])
|
|
||||||
class ModRM < Argument
|
|
||||||
# valid combinaisons for a modrm
|
|
||||||
# ints are reg indexes, symbols are immediates, except :sib
|
|
||||||
Sum = {
|
|
||||||
16 => {
|
|
||||||
0 => [ [3, 6], [3, 7], [5, 6], [5, 7], [6], [7], [:i16], [3] ],
|
|
||||||
1 => [ [3, 6, :i8 ], [3, 7, :i8 ], [5, 6, :i8 ], [5, 7, :i8 ], [6, :i8 ], [7, :i8 ], [5, :i8 ], [3, :i8 ] ],
|
|
||||||
2 => [ [3, 6, :i16], [3, 7, :i16], [5, 6, :i16], [5, 7, :i16], [6, :i16], [7, :i16], [5, :i16], [3, :i16] ]
|
|
||||||
},
|
|
||||||
32 => {
|
|
||||||
0 => [ [0], [1], [2], [3], [:sib], [:i32], [6], [7] ],
|
|
||||||
1 => [ [0, :i8 ], [1, :i8 ], [2, :i8 ], [3, :i8 ], [:sib, :i8 ], [5, :i8 ], [6, :i8 ], [7, :i8 ] ],
|
|
||||||
2 => [ [0, :i32], [1, :i32], [2, :i32], [3, :i32], [:sib, :i32], [5, :i32], [6, :i32], [7, :i32] ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
attr_accessor :adsz, :sz
|
|
||||||
attr_accessor :seg
|
|
||||||
attr_accessor :s, :i, :b, :imm
|
|
||||||
|
|
||||||
# creates a new ModRM with the specified attributes:
|
|
||||||
# - adsz (16/32), sz (8/16/32: byte ptr, word ptr, dword ptr)
|
|
||||||
# - s, i, b, imm
|
|
||||||
# - segment selector override
|
|
||||||
def initialize(adsz, sz, s, i, b, imm, seg = nil)
|
|
||||||
@adsz, @sz = adsz, sz
|
|
||||||
@s, @i = s, i if i
|
|
||||||
@b = b if b
|
|
||||||
@imm = imm if imm
|
|
||||||
@seg = seg if seg
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the symbolic representation of the ModRM (ie an Indirection)
|
|
||||||
# segment selectors are represented as eg "segment_base_fs"
|
|
||||||
# not present when same as implicit (ds:edx, ss:esp)
|
|
||||||
def symbolic(di=nil)
|
|
||||||
p = nil
|
|
||||||
p = Expression[p, :+, @b.symbolic(di)] if b
|
|
||||||
p = Expression[p, :+, [@s, :*, @i.symbolic(di)]] if i
|
|
||||||
p = Expression[p, :+, @imm] if imm
|
|
||||||
p = Expression["segment_base_#@seg", :+, p] if seg and seg.val != ((b && (@b.val == 4 || @b.val == 5)) ? 2 : 3)
|
|
||||||
Indirection[p.reduce, @sz/8, (di.address if di)]
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
self.class == o.class and s == o.s and i == o.i and b == o.b and imm == o.imm and seg == o.seg and adsz == o.adsz and sz == o.sz
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Create a new instance of an Ia32 cpu
|
|
||||||
# arguments (any order)
|
|
||||||
# - size in bits (16, 32) [32]
|
|
||||||
# - instruction set (386, 486, pentium...) [latest]
|
|
||||||
# - endianness [:little]
|
|
||||||
def initialize(*a)
|
|
||||||
super()
|
|
||||||
@size = (a & [16, 32]).first || 32
|
|
||||||
a.delete @size
|
|
||||||
@endianness = (a & [:big, :little]).first || :little
|
|
||||||
a.delete @endianness
|
|
||||||
@family = a.pop || :latest
|
|
||||||
raise "Invalid arguments #{a.inspect}" if not a.empty?
|
|
||||||
raise "Invalid Ia32 family #{@family.inspect}" if not respond_to?("init_#@family")
|
|
||||||
end
|
|
||||||
|
|
||||||
# wrapper to transparently forward Ia32.new(64) to X86_64.new
|
|
||||||
def self.new(*a)
|
|
||||||
return X86_64.new(*a) if a.include? 64 and self == Ia32
|
|
||||||
super(*a)
|
|
||||||
end
|
|
||||||
|
|
||||||
# initializes the @opcode_list according to @family
|
|
||||||
def init_opcode_list
|
|
||||||
send("init_#@family")
|
|
||||||
@opcode_list
|
|
||||||
end
|
|
||||||
|
|
||||||
# defines some preprocessor macros to say who we are:
|
|
||||||
# _M_IX86 = 500, _X86_, __i386__
|
|
||||||
# pass any value in nodefine to just call super w/o defining anything of our own
|
|
||||||
def tune_prepro(pp, nodefine = false)
|
|
||||||
super(pp)
|
|
||||||
return if nodefine
|
|
||||||
pp.define_weak('_M_IX86', 500)
|
|
||||||
pp.define_weak('_X86_')
|
|
||||||
pp.define_weak('__i386__')
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns a Reg/SimdReg object if the arg is a valid register (eg 'ax' => Reg.new(0, 16))
|
|
||||||
# returns nil if str is invalid
|
|
||||||
def str_to_reg(str)
|
|
||||||
Reg.s_to_i.has_key?(str) ? Reg.from_str(str) : SimdReg.s_to_i.has_key?(str) ? SimdReg.from_str(str) : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the list of Regs in the instruction arguments
|
|
||||||
# may be converted into symbols through Reg#symbolic
|
|
||||||
def instr_args_regs(i)
|
|
||||||
i = i.instruction if i.kind_of?(DecodedInstruction)
|
|
||||||
i.args.grep(Reg)
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the list of ModRMs in the instruction arguments
|
|
||||||
# may be converted into Indirection through ModRM#symbolic
|
|
||||||
def instr_args_memoryptr(i)
|
|
||||||
i = i.instruction if i.kind_of?(DecodedInstruction)
|
|
||||||
i.args.grep(ModRM)
|
|
||||||
end
|
|
||||||
|
|
||||||
# return the 'base' of the ModRM (Reg/nil)
|
|
||||||
def instr_args_memoryptr_getbase(mrm)
|
|
||||||
mrm.b || (mrm.i if mrm.s == 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
# return the offset of the ModRM (Expression/nil)
|
|
||||||
def instr_args_memoryptr_getoffset(mrm)
|
|
||||||
mrm.imm
|
|
||||||
end
|
|
||||||
|
|
||||||
# define ModRM offset (eg to changing imm into an ExpressionString)
|
|
||||||
def instr_args_memoryptr_setoffset(mrm, imm)
|
|
||||||
mrm.imm = (imm ? Expression[imm] : imm)
|
|
||||||
end
|
|
||||||
|
|
||||||
def shortname
|
|
||||||
"ia32#{'_16' if @size == 16}#{'_be' if @endianness == :big}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
X86 = Ia32
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load Diff
@ -1,359 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ia32/opcodes'
|
|
||||||
require 'metasm/cpu/ia32/encode'
|
|
||||||
require 'metasm/parse'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Ia32
|
|
||||||
class ModRM
|
|
||||||
# may return a SegReg
|
|
||||||
# must be called before SegReg parser (which could match only the seg part of a modrm)
|
|
||||||
def self.parse(lexer, otok, cpu)
|
|
||||||
tok = otok
|
|
||||||
|
|
||||||
# read operand size specifier
|
|
||||||
if tok and tok.type == :string and tok.raw =~ /^(?:byte|[dqo]?word|_(\d+)bits)$/
|
|
||||||
ptsz =
|
|
||||||
if $1
|
|
||||||
$1.to_i
|
|
||||||
else
|
|
||||||
case tok.raw
|
|
||||||
when 'byte'; 8
|
|
||||||
when 'word'; 16
|
|
||||||
when 'dword'; 32
|
|
||||||
when 'qword'; 64
|
|
||||||
when 'oword'; 128
|
|
||||||
else raise otok, 'mrm: bad ptr size'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
lexer.skip_space
|
|
||||||
if tok = lexer.readtok and tok.type == :string and tok.raw == 'ptr'
|
|
||||||
lexer.skip_space
|
|
||||||
tok = lexer.readtok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# read segment selector
|
|
||||||
if tok and tok.type == :string and seg = SegReg.s_to_i[tok.raw]
|
|
||||||
lexer.skip_space
|
|
||||||
seg = SegReg.new(seg)
|
|
||||||
if not ntok = lexer.readtok or ntok.type != :punct or ntok.raw != ':'
|
|
||||||
raise otok, 'invalid modrm' if ptsz
|
|
||||||
lexer.unreadtok ntok
|
|
||||||
return seg
|
|
||||||
end
|
|
||||||
lexer.skip_space
|
|
||||||
tok = lexer.readtok
|
|
||||||
end
|
|
||||||
|
|
||||||
# ensure we have a modrm
|
|
||||||
if not tok or tok.type != :punct or tok.raw != '['
|
|
||||||
raise otok, 'invalid modrm' if ptsz or seg
|
|
||||||
return
|
|
||||||
end
|
|
||||||
lexer.skip_space_eol
|
|
||||||
|
|
||||||
# support fasm syntax [fs:eax] for segment selector
|
|
||||||
if tok = lexer.readtok and tok.type == :string and not seg and seg = SegReg.s_to_i[tok.raw]
|
|
||||||
raise otok, 'invalid modrm' if not ntok = lexer.readtok or ntok.type != :punct or ntok.raw != ':'
|
|
||||||
seg = SegReg.new(seg)
|
|
||||||
lexer.skip_space_eol
|
|
||||||
else
|
|
||||||
lexer.unreadtok tok
|
|
||||||
end
|
|
||||||
|
|
||||||
# read modrm content as generic expression
|
|
||||||
content = Expression.parse(lexer)
|
|
||||||
lexer.skip_space_eol
|
|
||||||
raise(otok, 'bad modrm') if not content or not ntok = lexer.readtok or ntok.type != :punct or ntok.raw != ']'
|
|
||||||
|
|
||||||
# converts matching externals to Regs in an expression
|
|
||||||
regify = lambda { |o|
|
|
||||||
case o
|
|
||||||
when Expression
|
|
||||||
o.lexpr = regify[o.lexpr]
|
|
||||||
o.rexpr = regify[o.rexpr]
|
|
||||||
o
|
|
||||||
when String
|
|
||||||
cpu.str_to_reg(o) || o
|
|
||||||
else o
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
s = i = b = imm = nil
|
|
||||||
|
|
||||||
# assigns the Regs in the expression to base or index field of the modrm
|
|
||||||
walker = lambda { |o|
|
|
||||||
case o
|
|
||||||
when nil
|
|
||||||
when Reg
|
|
||||||
if b
|
|
||||||
raise otok, 'mrm: too many regs' if i
|
|
||||||
i = o
|
|
||||||
s = 1
|
|
||||||
else
|
|
||||||
b = o
|
|
||||||
end
|
|
||||||
when SimdReg
|
|
||||||
raise otok, 'mrm: too many regs' if i
|
|
||||||
i = o
|
|
||||||
s = 1
|
|
||||||
when Expression
|
|
||||||
if o.op == :* and (o.rexpr.kind_of?(Reg) or o.lexpr.kind_of?(Reg))
|
|
||||||
# scaled index
|
|
||||||
raise otok, 'mrm: too many indexes' if i
|
|
||||||
s = o.lexpr
|
|
||||||
i = o.rexpr
|
|
||||||
s, i = i, s if s.kind_of? Reg
|
|
||||||
raise otok, "mrm: bad scale #{s}" unless [1, 2, 4, 8].include?(s)
|
|
||||||
elsif o.op == :+
|
|
||||||
# recurse
|
|
||||||
walker[o.lexpr]
|
|
||||||
walker[o.rexpr]
|
|
||||||
else
|
|
||||||
# found (a part of) the immediate
|
|
||||||
imm = Expression[imm, :+, o]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# found (a part of) the immediate
|
|
||||||
imm = Expression[imm, :+, o]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
# do it
|
|
||||||
walker[regify[content.reduce]]
|
|
||||||
|
|
||||||
# ensure found immediate is really an immediate
|
|
||||||
raise otok, 'mrm: reg in imm' if imm.kind_of?(Expression) and not imm.externals.grep(Reg).empty?
|
|
||||||
|
|
||||||
raise otok, 'mrm: bad reg size' if b.kind_of?(Reg) and i.kind_of?(Reg) and b.sz != i.sz
|
|
||||||
|
|
||||||
# find default address size
|
|
||||||
adsz = b ? b.sz : i ? i.sz : nil
|
|
||||||
# ptsz may be nil now, will be fixed up later (in parse_instr_fixup) to match another instruction argument's size
|
|
||||||
new adsz, ptsz, s, i, b, imm, seg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# handles cpu-specific parser instruction, falls back to Ancestor's version if unknown keyword
|
|
||||||
# XXX changing the cpu size in the middle of the code may have baaad effects...
|
|
||||||
def parse_parser_instruction(lexer, instr)
|
|
||||||
case instr.raw.downcase
|
|
||||||
when '.mode', '.bits'
|
|
||||||
lexer.skip_space
|
|
||||||
if tok = lexer.readtok and tok.type == :string and (tok.raw == '16' or tok.raw == '32')
|
|
||||||
@size = tok.raw.to_i
|
|
||||||
lexer.skip_space
|
|
||||||
raise instr, 'syntax error' if ntok = lexer.nexttok and ntok.type != :eol
|
|
||||||
else
|
|
||||||
raise instr, 'invalid cpu mode'
|
|
||||||
end
|
|
||||||
else super(lexer, instr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_prefix(i, pfx)
|
|
||||||
# implicit 'true' return value when assignment occur
|
|
||||||
i.prefix ||= {}
|
|
||||||
case pfx
|
|
||||||
when 'lock'; i.prefix[:lock] = true
|
|
||||||
when 'rep'; i.prefix[:rep] = 'rep'
|
|
||||||
when 'repe', 'repz'; i.prefix[:rep] = 'repz'
|
|
||||||
when 'repne', 'repnz'; i.prefix[:rep] = 'repnz'
|
|
||||||
when 'code16'; i.prefix[:sz] = 16
|
|
||||||
when 'code32'; i.prefix[:sz] = 32
|
|
||||||
when 'hintjmp', 'ht'; i.prefix[:jmphint] = 'hintjmp'
|
|
||||||
when 'hintnojmp', 'hnt';i.prefix[:jmphint] = 'hintnojmp'
|
|
||||||
when /^seg_([c-g]s)$/; i.prefix[:seg] = SegReg.new(SegReg.s_to_i[$1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_argregclasslist
|
|
||||||
[Reg, SimdReg, SegReg, DbgReg, TstReg, CtrlReg, FpReg]
|
|
||||||
end
|
|
||||||
def parse_modrm(lex, tok, cpu)
|
|
||||||
ModRM.parse(lex, tok, cpu)
|
|
||||||
end
|
|
||||||
|
|
||||||
# parses an arbitrary ia32 instruction argument
|
|
||||||
def parse_argument(lexer)
|
|
||||||
lexer = AsmPreprocessor.new(lexer) if lexer.kind_of? String
|
|
||||||
|
|
||||||
# reserved names (registers/segments etc)
|
|
||||||
@args_token ||= parse_argregclasslist.map { |a| a.s_to_i.keys }.flatten.inject({}) { |h, e| h.update e => true }
|
|
||||||
|
|
||||||
lexer.skip_space
|
|
||||||
return if not tok = lexer.readtok
|
|
||||||
|
|
||||||
if tok.type == :string and tok.raw == 'ST'
|
|
||||||
lexer.skip_space
|
|
||||||
if ntok = lexer.readtok and ntok.type == :punct and ntok.raw == '('
|
|
||||||
lexer.skip_space
|
|
||||||
if not nntok = lexer.readtok or nntok.type != :string or nntok.raw !~ /^[0-9]$/ or
|
|
||||||
not ntok = (lexer.skip_space; lexer.readtok) or ntok.type != :punct or ntok.raw != ')'
|
|
||||||
raise tok, 'invalid FP register'
|
|
||||||
else
|
|
||||||
tok.raw << '(' << nntok.raw << ')'
|
|
||||||
fpr = parse_argregclasslist.last
|
|
||||||
if fpr.s_to_i.has_key? tok.raw
|
|
||||||
return fpr.new(fpr.s_to_i[tok.raw])
|
|
||||||
else
|
|
||||||
raise tok, 'invalid FP register'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
lexer.unreadtok ntok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if ret = parse_modrm(lexer, tok, self)
|
|
||||||
ret
|
|
||||||
elsif @args_token[tok.raw]
|
|
||||||
parse_argregclasslist.each { |a|
|
|
||||||
return a.from_str(tok.raw) if a.s_to_i.has_key? tok.raw
|
|
||||||
}
|
|
||||||
raise tok, 'internal error'
|
|
||||||
else
|
|
||||||
lexer.unreadtok tok
|
|
||||||
expr = Expression.parse(lexer)
|
|
||||||
lexer.skip_space
|
|
||||||
|
|
||||||
# may be a farptr
|
|
||||||
if expr and ntok = lexer.readtok and ntok.type == :punct and ntok.raw == ':'
|
|
||||||
raise tok, 'invalid farptr' if not addr = Expression.parse(lexer)
|
|
||||||
Farptr.new expr, addr
|
|
||||||
else
|
|
||||||
lexer.unreadtok ntok
|
|
||||||
Expression[expr.reduce] if expr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if the argument matches the opcode's argument spec
|
|
||||||
def parse_arg_valid?(o, spec, arg)
|
|
||||||
if o.name == 'movsx' or o.name == 'movzx'
|
|
||||||
if not arg.kind_of?(Reg) and not arg.kind_of?(ModRM)
|
|
||||||
return
|
|
||||||
elsif not arg.sz
|
|
||||||
puts "ambiguous arg size for indirection in #{o.name}" if $VERBOSE
|
|
||||||
return
|
|
||||||
elsif spec == :reg # reg=dst, modrm=src (smaller)
|
|
||||||
return (arg.kind_of?(Reg) and arg.sz >= 16)
|
|
||||||
elsif o.props[:argsz]
|
|
||||||
return arg.sz == o.props[:argsz]
|
|
||||||
else
|
|
||||||
return arg.sz == 16
|
|
||||||
end
|
|
||||||
elsif o.name == 'crc32'
|
|
||||||
if not arg.kind_of?(Reg) and not arg.kind_of?(ModRM)
|
|
||||||
return
|
|
||||||
elsif not arg.sz
|
|
||||||
puts "ambiguous arg size for indirection in #{o.name}" if $VERBOSE
|
|
||||||
return
|
|
||||||
elsif spec == :reg
|
|
||||||
return (arg.kind_of?(Reg) and arg.sz >= 32)
|
|
||||||
elsif o.props[:argsz]
|
|
||||||
return arg.sz == o.props[:argsz]
|
|
||||||
else
|
|
||||||
return arg.sz >= 16
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false if arg.kind_of? ModRM and arg.adsz and o.props[:adsz] and arg.adsz != o.props[:adsz]
|
|
||||||
|
|
||||||
cond = true
|
|
||||||
if s = o.props[:argsz] and (arg.kind_of? Reg or arg.kind_of? ModRM)
|
|
||||||
cond = (!arg.sz or arg.sz == s or spec == :reg_dx)
|
|
||||||
end
|
|
||||||
|
|
||||||
cond and
|
|
||||||
case spec
|
|
||||||
when :reg; arg.kind_of? Reg and (arg.sz >= 16 or o.props[:argsz])
|
|
||||||
when :modrm; (arg.kind_of? ModRM or arg.kind_of? Reg) and (!arg.sz or arg.sz >= 16 or o.props[:argsz]) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? Reg)
|
|
||||||
when :i; arg.kind_of? Expression
|
|
||||||
when :imm_val1; arg.kind_of? Expression and arg.reduce == 1
|
|
||||||
when :imm_val3; arg.kind_of? Expression and arg.reduce == 3
|
|
||||||
when :reg_eax; arg.kind_of? Reg and arg.val == 0
|
|
||||||
when :reg_cl; arg.kind_of? Reg and arg.val == 1 and arg.sz == 8
|
|
||||||
when :reg_dx; arg.kind_of? Reg and arg.val == 2 and arg.sz == 16
|
|
||||||
when :seg3; arg.kind_of? SegReg
|
|
||||||
when :seg3A; arg.kind_of? SegReg and arg.val > 3
|
|
||||||
when :seg2; arg.kind_of? SegReg and arg.val < 4
|
|
||||||
when :seg2A; arg.kind_of? SegReg and arg.val < 4 and arg.val != 1
|
|
||||||
when :eeec; arg.kind_of? CtrlReg
|
|
||||||
when :eeed; arg.kind_of? DbgReg
|
|
||||||
when :eeet; arg.kind_of? TstReg
|
|
||||||
when :mrm_imm; arg.kind_of? ModRM and not arg.s and not arg.i and not arg.b
|
|
||||||
when :farptr; arg.kind_of? Farptr
|
|
||||||
when :regfp; arg.kind_of? FpReg
|
|
||||||
when :regfp0; arg.kind_of? FpReg and (arg.val == nil or arg.val == 0)
|
|
||||||
when :modrmmmx; arg.kind_of? ModRM or (arg.kind_of? SimdReg and (arg.sz == 64 or (arg.sz == 128 and o.props[:xmmx]))) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
|
|
||||||
when :regmmx; arg.kind_of? SimdReg and (arg.sz == 64 or (arg.sz == 128 and o.props[:xmmx]))
|
|
||||||
when :modrmxmm; arg.kind_of? ModRM or (arg.kind_of? SimdReg and arg.sz == 128) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
|
|
||||||
when :regxmm; arg.kind_of? SimdReg and arg.sz == 128
|
|
||||||
when :modrmymm; arg.kind_of? ModRM or (arg.kind_of? SimdReg and arg.sz == 256) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
|
|
||||||
when :regymm; arg.kind_of? SimdReg and arg.sz == 256
|
|
||||||
|
|
||||||
when :vexvreg; arg.kind_of? Reg and arg.sz == @size
|
|
||||||
when :vexvxmm, :i4xmm; arg.kind_of? SimdReg and arg.sz == 128
|
|
||||||
when :vexvymm, :i4ymm; arg.kind_of? SimdReg and arg.sz == 256
|
|
||||||
|
|
||||||
when :i8, :u8, :u16
|
|
||||||
arg.kind_of? Expression and
|
|
||||||
(o.props[:setip] or Expression.in_range?(arg, spec) != false) # true or nil allowed
|
|
||||||
# jz 0x28282828 may fit in :i8 depending on instr addr
|
|
||||||
else raise EncodeError, "Internal error: unknown argument specification #{spec.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_instruction_checkproto(i)
|
|
||||||
case i.opname
|
|
||||||
when 'imul'
|
|
||||||
if i.args.length == 2 and i.args.first.kind_of? Reg and i.args.last.kind_of? Expression
|
|
||||||
i.args.unshift i.args.first.dup
|
|
||||||
end
|
|
||||||
end
|
|
||||||
super(i)
|
|
||||||
end
|
|
||||||
|
|
||||||
# fixup the sz of a modrm argument, defaults to other argument size or current cpu mode
|
|
||||||
def parse_instruction_fixup(i)
|
|
||||||
if m = i.args.grep(ModRM).first and not m.sz
|
|
||||||
if i.opname == 'movzx' or i.opname == 'movsx'
|
|
||||||
m.sz = 8
|
|
||||||
else
|
|
||||||
if r = i.args.grep(Reg).first
|
|
||||||
m.sz = r.sz
|
|
||||||
elsif l = opcode_list_byname[i.opname].map { |o| o.props[:argsz] }.uniq and l.length == 1 and l.first
|
|
||||||
m.sz = l.first
|
|
||||||
else
|
|
||||||
# this is also the size of ctrlreg/dbgreg etc
|
|
||||||
# XXX fpu/simd ?
|
|
||||||
m.sz = i.prefix[:sz] || @size
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if m and not m.adsz
|
|
||||||
if opcode_list_byname[i.opname].all? { |o| o.props[:adsz] }
|
|
||||||
m.adsz = opcode_list_byname[i.opname].first.props[:adsz]
|
|
||||||
else
|
|
||||||
m.adsz = i.prefix[:sz] || @size
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_reserved_name(name)
|
|
||||||
Reg.s_to_i[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
def instr_uncond_jump_to(target)
|
|
||||||
parse_instruction("jmp #{target}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,118 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ia32/opcodes'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
# XXX move context in another file ?
|
|
||||||
module Metasm
|
|
||||||
class Ia32
|
|
||||||
class Argument
|
|
||||||
include Renderable
|
|
||||||
end
|
|
||||||
|
|
||||||
[SegReg, DbgReg, TstReg, CtrlReg, FpReg].each { |c| c.class_eval {
|
|
||||||
def render ; [self.class.i_to_s[@val]] end
|
|
||||||
} }
|
|
||||||
[Reg, SimdReg].each { |c| c.class_eval {
|
|
||||||
def render ; [self.class.i_to_s[@sz][@val]] end
|
|
||||||
def context ; {'set sz' => lambda { |s| @sz = s }} end
|
|
||||||
} }
|
|
||||||
|
|
||||||
class Farptr
|
|
||||||
def render
|
|
||||||
[@seg, ':', @addr]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ModRM
|
|
||||||
def qualifier(sz)
|
|
||||||
{
|
|
||||||
8 => 'byte',
|
|
||||||
16 => 'word',
|
|
||||||
32 => 'dword',
|
|
||||||
64 => 'qword',
|
|
||||||
128 => 'oword'
|
|
||||||
}.fetch(sz) { |k| "_#{sz}bits" }
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_accessor :instruction
|
|
||||||
def render
|
|
||||||
r = []
|
|
||||||
r << ( qualifier(@sz) << ' ptr ' ) if @sz and (not instruction or not @instruction.args.find { |a| a.kind_of? Reg and a.sz == @sz })
|
|
||||||
r << @seg << ':' if seg
|
|
||||||
|
|
||||||
e = nil
|
|
||||||
e = Expression[e, :+, @b] if b
|
|
||||||
e = Expression[e, :+, @imm] if imm
|
|
||||||
e = Expression[e, :+, (@s == 1 ? @i : [@s, :*, @i])] if s
|
|
||||||
r << '[' << e << ']'
|
|
||||||
end
|
|
||||||
|
|
||||||
def context
|
|
||||||
{'set targetsz' => lambda { |s| @sz = s },
|
|
||||||
'set seg' => lambda { |s| @seg = Seg.new s }}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_instruction(i)
|
|
||||||
r = []
|
|
||||||
if pfx = i.prefix
|
|
||||||
r << 'lock ' if pfx[:lock]
|
|
||||||
r << pfx[:rep] << ' ' if pfx[:rep]
|
|
||||||
r << pfx[:jmphint] << ' ' if pfx[:jmphint]
|
|
||||||
r << 'seg_' << pfx[:seg] << ' ' if pfx[:seg]
|
|
||||||
end
|
|
||||||
r << i.opname
|
|
||||||
sep = ' '
|
|
||||||
i.args.each { |a|
|
|
||||||
a.instruction = i if a.kind_of? ModRM
|
|
||||||
r << sep << a
|
|
||||||
sep = ', '
|
|
||||||
}
|
|
||||||
r
|
|
||||||
end
|
|
||||||
|
|
||||||
def instruction_context(i)
|
|
||||||
# XXX
|
|
||||||
h = {}
|
|
||||||
op = opcode_list_byname[i.opname].first
|
|
||||||
if i.prefix and i.prefix[:rep]
|
|
||||||
h['toogle repz'] = lambda { i.prefix[:rep] = {'repnz' => 'repz', 'repz' => 'repnz'}[i.prefix[:rep]] } if op.props[:stropz]
|
|
||||||
h['rm rep'] = lambda { i.prefix.delete :rep }
|
|
||||||
else
|
|
||||||
h['set rep'] = lambda { (i.prefix ||= {})[:rep] = 'rep' } if op.props[:strop]
|
|
||||||
h['set rep'] = lambda { (i.prefix ||= {})[:rep] = 'repz' } if op.props[:stropz]
|
|
||||||
end
|
|
||||||
if i.args.find { |a| a.kind_of? ModRM and a.seg }
|
|
||||||
h['rm seg'] = lambda { i.args.find { |a| a.kind_of? ModRM and a.seg }.seg = nil }
|
|
||||||
end
|
|
||||||
h['toggle lock'] = lambda { (i.prefix ||= {})[:lock] = !i.prefix[:lock] }
|
|
||||||
h
|
|
||||||
end
|
|
||||||
|
|
||||||
def gui_hilight_word_regexp_init
|
|
||||||
ret = {}
|
|
||||||
|
|
||||||
%w[a b c d].each { |r|
|
|
||||||
ret["#{r}l"] = "e?#{r}x|#{r}l"
|
|
||||||
ret["#{r}h"] = "e?#{r}x|#{r}h"
|
|
||||||
ret["#{r}x"] = ret["e#{r}x"] = "e?#{r}x|#{r}[hl]"
|
|
||||||
}
|
|
||||||
|
|
||||||
%w[sp bp si di].each { |r|
|
|
||||||
ret[r] = ret["e#{r}"] = "e?#{r}"
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
end
|
|
||||||
|
|
||||||
def gui_hilight_word_regexp(word)
|
|
||||||
@gui_hilight_word_hash ||= gui_hilight_word_regexp_init
|
|
||||||
@gui_hilight_word_hash[word] or super(word)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,14 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
class Metasm::MIPS < Metasm::CPU
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/mips/parse'
|
|
||||||
require 'metasm/cpu/mips/encode'
|
|
||||||
require 'metasm/cpu/mips/decode'
|
|
||||||
require 'metasm/cpu/mips/render'
|
|
||||||
require 'metasm/cpu/mips/debug'
|
|
@ -1,7 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/mips/parse'
|
|
||||||
require 'metasm/compile_c'
|
|
@ -1,42 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MIPS
|
|
||||||
def dbg_register_pc
|
|
||||||
@dbg_register_pc ||= :pc
|
|
||||||
end
|
|
||||||
def dbg_register_flags
|
|
||||||
@dbg_register_flags ||= :flags
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_list
|
|
||||||
@dbg_register_list ||= %w[z0 at v0 v1 a0 a1 a2 a3
|
|
||||||
t0 t1 t2 t3 t4 t5 t6 t7
|
|
||||||
s0 s1 s2 s3 s4 s5 s6 s7
|
|
||||||
t8 t9 k0 k1 gp sp fp ra
|
|
||||||
sr mullo mulhi badva cause pc].map { |r| r.to_sym }
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_flag_list
|
|
||||||
@dbg_flag_list ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_size
|
|
||||||
@dbg_register_size ||= Hash.new(@size)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_need_stepover(dbg, addr, di)
|
|
||||||
di and di.opcode.props[:saveip]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_end_stepout(dbg, addr, di)
|
|
||||||
di and di.opcode.name == 'foobar' # TODO
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,284 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/mips/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MIPS
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
|
|
||||||
op.bin_mask = 0
|
|
||||||
op.args.each { |f|
|
|
||||||
op.bin_mask |= @fields_mask[f] << @fields_shift[f]
|
|
||||||
}
|
|
||||||
op.bin_mask = 0xffffffff ^ op.bin_mask
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
lookaside = Array.new(256) { [] }
|
|
||||||
opcode_list.each { |op|
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
|
|
||||||
b = op.bin >> 24
|
|
||||||
msk = op.bin_mask >> 24
|
|
||||||
|
|
||||||
for i in b..(b | (255^msk))
|
|
||||||
next if i & msk != b & msk
|
|
||||||
lookaside[i] << op
|
|
||||||
end
|
|
||||||
}
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
val = edata.decode_imm(:u32, @endianness)
|
|
||||||
edata.ptr -= 4
|
|
||||||
if val.kind_of?(Expression)
|
|
||||||
# relocations
|
|
||||||
hval = Expression[val, :&, 0xff000000].reduce
|
|
||||||
if hval.kind_of?(Expression)
|
|
||||||
# reloc_i26
|
|
||||||
if hval.kind_of?(Expression) and pat = hval.match(Expression[['a', :&, 0x300_0000], :|, 'b'], 'a', 'b')
|
|
||||||
hval = pat['b']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
di if di.opcode = @bin_lookaside[hval >> 24].find { |op|
|
|
||||||
(op.bin & op.bin_mask) == Expression[val, :&, op.bin_mask].reduce
|
|
||||||
}
|
|
||||||
else
|
|
||||||
di if di.opcode = @bin_lookaside[val >> 24].find { |op|
|
|
||||||
(op.bin & op.bin_mask) == (val & op.bin_mask)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
val = edata.decode_imm(:u32, @endianness)
|
|
||||||
|
|
||||||
field_val = lambda { |f|
|
|
||||||
if val.kind_of?(Expression)
|
|
||||||
r = Expression[[val, :>>, @fields_shift[f]], :&, @fields_mask[f]].reduce
|
|
||||||
else
|
|
||||||
r = (val >> @fields_shift[f]) & @fields_mask[f]
|
|
||||||
end
|
|
||||||
|
|
||||||
next r if r.kind_of?(Expression)
|
|
||||||
case f
|
|
||||||
when :msbd; r += 1
|
|
||||||
when :i16; r = Expression.make_signed(r, 16)
|
|
||||||
when :i20; r = Expression.make_signed(r, 20)
|
|
||||||
when :i26; r = Expression.make_signed(r, 26)
|
|
||||||
else r
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :rs, :rt, :rd; Reg.new field_val[a]
|
|
||||||
when :sa, :i16, :i20, :i26, :it, :msbd, :sel, :idb; Expression[field_val[a]]
|
|
||||||
when :rs_i16
|
|
||||||
len = 32
|
|
||||||
len = 64 if op.props[:m64]
|
|
||||||
len = 16 if op.props[:mi16] or op.props[:mu16]
|
|
||||||
len = 8 if op.props[:mi8 ] or op.props[:mu8]
|
|
||||||
Memref.new Reg.new(field_val[:rs]), Expression[field_val[:i16]], len
|
|
||||||
when :ft; FpReg.new field_val[a]
|
|
||||||
when :idm1; Expression['unsupported']
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
return false if edata.ptr > edata.length
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
# converts relative branch offsets to absolute addresses
|
|
||||||
# else just add the offset +off+ of the instruction + its length (off may be an Expression)
|
|
||||||
# assumes edata.ptr points just after the instruction (as decode_instr_op left it)
|
|
||||||
# do not call twice on the same di !
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.opcode.name[0] != ?t
|
|
||||||
delta = Expression[di.instruction.args.last, :<<, 2].reduce
|
|
||||||
if di.opcode.args.include? :i26
|
|
||||||
# absolute jump in the 0x3ff_ffff region surrounding next_pc
|
|
||||||
if delta.kind_of? Expression and delta.op == :& and delta.rexpr == 0x3ff_fffc
|
|
||||||
# relocated arg: assume the linker mapped so that instr&target are in the same region
|
|
||||||
arg = Expression[delta.lexpr].reduce
|
|
||||||
else
|
|
||||||
arg = Expression[[[addr, :+, di.bin_length], :&, 0xfc00_0000], :+, delta].reduce
|
|
||||||
end
|
|
||||||
else
|
|
||||||
arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce
|
|
||||||
end
|
|
||||||
di.instruction.args[-1] = Expression[arg]
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
# hash opname => lambda { |di, *sym_args| binding }
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
def backtrace_binding=(b) @backtrace_binding = b end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
opcode_list.map { |ol| ol.name }.uniq.each { |op|
|
|
||||||
binding = case op
|
|
||||||
when 'break'
|
|
||||||
when 'bltzal', 'bgezal'; lambda { |di, *a|
|
|
||||||
# XXX $ra is set only if branch is taken...
|
|
||||||
{ :$ra => Expression[Expression[di.address, :+, 2*di.bin_length].reduce] }
|
|
||||||
}
|
|
||||||
when 'nop', 'j', 'jr', /^b/; lambda { |di, *a| {} }
|
|
||||||
when 'lui'; lambda { |di, a0, a1| { a0 => Expression[[a1, :&, 0xffff], :<<, 16] } }
|
|
||||||
when 'add', 'addu', 'addi', 'addiu'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :+, a2] } } # XXX addiu $sp, -40h should be addiu $sp, 0xffc0 from the books, but..
|
|
||||||
when 'sub', 'subu'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :-, a2] } }
|
|
||||||
when 'slt', 'slti'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :<, a2] } }
|
|
||||||
when 'and', 'andi'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :&, a2] } }
|
|
||||||
when 'or', 'ori'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :|, a2] } }
|
|
||||||
when 'nor'; lambda { |di, a0, a1, a2| { a0 => Expression[:~, [a1, :|, a2]] } }
|
|
||||||
when 'xor'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :^, a2] } }
|
|
||||||
when 'sll', 'sllv'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :>>, a2] } }
|
|
||||||
when 'srl', 'srlv', 'sra', 'srav'; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :<<, a2] } } # XXX sign-extend
|
|
||||||
when 'lw'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
|
|
||||||
when 'sw'; lambda { |di, a0, a1| { a1 => Expression[a0] } }
|
|
||||||
when 'lh', 'lhu'; lambda { |di, a0, a1| { a0 => Expression[a1] } } # XXX sign-extend
|
|
||||||
when 'sh'; lambda { |di, a0, a1| { a1 => Expression[a0] } }
|
|
||||||
when 'lb', 'lbu'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
|
|
||||||
when 'sb'; lambda { |di, a0, a1| { a1 => Expression[a0] } }
|
|
||||||
when /^slti?u?/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :<, a2] } } # XXX signedness
|
|
||||||
when 'mfhi'; lambda { |di, a0| { a0 => Expression[:hi] } }
|
|
||||||
when 'mflo'; lambda { |di, a0| { a0 => Expression[:lo] } }
|
|
||||||
when 'mult'; lambda { |di, a0, a1| { :hi => Expression[[a0, :*, a1], :>>, 32], :lo => Expression[[a0, :*, a1], :&, 0xffff_ffff] } }
|
|
||||||
when 'div'; lambda { |di, a0, a1| { :hi => Expression[a0, :%, a1], :lo => Expression[a0, :/, a1] } }
|
|
||||||
when 'jal', 'jalr'; lambda { |di, a0| { :$ra => Expression[Expression[di.address, :+, 2*di.bin_length].reduce] } }
|
|
||||||
when 'li', 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
|
|
||||||
when 'syscall'; lambda { |di, *a| { :$v0 => Expression::Unknown } }
|
|
||||||
end
|
|
||||||
|
|
||||||
@backtrace_binding[op] ||= binding if binding
|
|
||||||
}
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Memref; arg.symbolic(di.address)
|
|
||||||
when Reg; arg.symbolic
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
binding = if binding = backtrace_binding[di.instruction.opname]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
if di.instruction.opname[0] == ?b and di.opcode.props[:setip]
|
|
||||||
else
|
|
||||||
puts "unknown instruction to emu #{di}" if $VERBOSE
|
|
||||||
end
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
|
|
||||||
binding.delete 0 # allow add $zero, 42 => nop
|
|
||||||
|
|
||||||
binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
arg = di.instruction.args.last
|
|
||||||
[Expression[
|
|
||||||
case arg
|
|
||||||
when Memref; Indirection[[arg.base.to_s.to_sym, :+, arg.offset], @size/8, di.address]
|
|
||||||
when Reg; arg.to_s.to_sym
|
|
||||||
else arg
|
|
||||||
end]]
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
|
|
||||||
retaddrlist.map! { |retaddr| dasm.decoded[retaddr] ? dasm.decoded[retaddr].block.list.last.address : retaddr } if retaddrlist
|
|
||||||
b = f.backtrace_binding
|
|
||||||
|
|
||||||
bt_val = lambda { |r|
|
|
||||||
next if not retaddrlist
|
|
||||||
bt = []
|
|
||||||
b[r] = Expression::Unknown # break recursive dep
|
|
||||||
retaddrlist.each { |retaddr|
|
|
||||||
bt |= dasm.backtrace(Expression[r], retaddr,
|
|
||||||
:include_start => true, :snapshot_addr => faddr, :origin => retaddr)
|
|
||||||
}
|
|
||||||
b[r] = ((bt.length == 1) ? bt.first : Expression::Unknown)
|
|
||||||
}
|
|
||||||
wantregs = Reg.i_to_s.values if wantregs.empty?
|
|
||||||
wantregs.map { |r| r.to_sym }.each(&bt_val)
|
|
||||||
|
|
||||||
puts "update_func_bind: #{Expression[faddr]} has sp -> #{b[:$sp]}" if not Expression[b[:$sp], :-, :$sp].reduce.kind_of?(::Integer) if $VERBOSE
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
expr.reduce_rec == :$ra
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_stack_address(expr)
|
|
||||||
Expression[expr].expr_externals.include? :$sp
|
|
||||||
end
|
|
||||||
|
|
||||||
def replace_instr_arg_immediate(i, old, new)
|
|
||||||
i.args.map! { |a|
|
|
||||||
case a
|
|
||||||
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
|
||||||
when Memref
|
|
||||||
a.offset = (a.offset == old ? new : Expression[a.offset.bind(old => new).reduce]) if a.offset
|
|
||||||
a
|
|
||||||
else a
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# make the target of the call know the value of $t9 (specified by the ABI)
|
|
||||||
# XXX hackish
|
|
||||||
def backtrace_found_result(dasm, di, expr, type, len)
|
|
||||||
if di.opcode.name == 'jalr' and di.instruction.args == [:$t9]
|
|
||||||
expr = dasm.normalize(expr)
|
|
||||||
(dasm.address_binding[expr] ||= {})[:$t9] ||= expr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def delay_slot(di=nil)
|
|
||||||
# branch.*likely has no delay slot
|
|
||||||
# bltzal/bgezal are 'link', not 'likely', hence the check for -2
|
|
||||||
(di and di.opcode.props[:setip] and (di.opcode.name[-1] != ?l or di.opcode.name[-2] == ?a)) ? 1 : 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def disassembler_default_func
|
|
||||||
df = DecodedFunction.new
|
|
||||||
df.backtrace_binding = %w[v0 v1 a0 a1 a2 a3 t0 t1 t2 t3 t4 t5 t6 t7 t8 t9 at k0 k1].inject({}) { |h, r| h.update "$#{r}".to_sym => Expression::Unknown }
|
|
||||||
df.backtrace_binding.update %w[gp sp fp ra s0 s1 s2 s3 s4 s5 s6 s7].inject({}) { |h, r| h.update "$#{r}".to_sym => "$#{r}".to_sym }
|
|
||||||
df.backtracked_for = [BacktraceTrace.new(Expression[:$ra], :default, Expression[:$ra], :x)]
|
|
||||||
df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
|
|
||||||
if funcaddr != :default
|
|
||||||
btfor
|
|
||||||
elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip] and di.instruction.to_s != 'jr $ra'
|
|
||||||
btfor
|
|
||||||
else []
|
|
||||||
end
|
|
||||||
}
|
|
||||||
df
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,52 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/mips/opcodes'
|
|
||||||
require 'metasm/encode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MIPS
|
|
||||||
private
|
|
||||||
def encode_instr_op(exe, instr, op)
|
|
||||||
base = op.bin
|
|
||||||
set_field = lambda { |f, v|
|
|
||||||
base |= (v & @fields_mask[f]) << @fields_shift[f]
|
|
||||||
}
|
|
||||||
|
|
||||||
val, mask, shift = 0, 0, 0
|
|
||||||
|
|
||||||
# convert label name for jmp/call/loop to relative offset
|
|
||||||
if op.props[:setip] and op.name[0] != ?t and instr.args.last.kind_of? Expression
|
|
||||||
postlabel = exe.new_label('jmp_offset')
|
|
||||||
instr = instr.dup
|
|
||||||
if op.args.include? :i26
|
|
||||||
pl = Expression[postlabel, :&, 0xfc00_0000]
|
|
||||||
else
|
|
||||||
pl = postlabel
|
|
||||||
end
|
|
||||||
instr.args[-1] = Expression[[instr.args[-1], :-, pl], :>>, 2]
|
|
||||||
postdata = EncodedData.new '', :export => {postlabel => 0}
|
|
||||||
else
|
|
||||||
postdata = ''
|
|
||||||
end
|
|
||||||
|
|
||||||
op.args.zip(instr.args).each { |sym, arg|
|
|
||||||
case sym
|
|
||||||
when :rs, :rt, :rd, :ft
|
|
||||||
set_field[sym, arg.i]
|
|
||||||
when :rs_i16
|
|
||||||
set_field[:rs, arg.base.i]
|
|
||||||
val, mask, shift = arg.offset, @fields_mask[:i16], @fields_shift[:i16]
|
|
||||||
when :sa, :i16, :i20, :i26, :it, :msbd, :sel, :idb
|
|
||||||
val, mask, shift = arg, @fields_mask[sym], @fields_shift[sym]
|
|
||||||
val = Expression[val, :-, 1] if sym == :msbd
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression[base, :|, [[val, :&, mask], :<<, shift]].encode(:u32, @endianness) << postdata
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,79 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MIPS < CPU
|
|
||||||
class Reg
|
|
||||||
class << self
|
|
||||||
attr_accessor :s_to_i, :i_to_s
|
|
||||||
end
|
|
||||||
@s_to_i = {}
|
|
||||||
@i_to_s = {}
|
|
||||||
(0..31).each { |i| @s_to_i["r#{i}"] = @s_to_i["$r#{i}"] = @s_to_i["$#{i}"] = i }
|
|
||||||
%w[zero at v0 v1 a0 a1 a2 a3
|
|
||||||
t0 t1 t2 t3 t4 t5 t6 t7
|
|
||||||
s0 s1 s2 s3 s4 s5 s6 s7
|
|
||||||
t8 t9 k0 k1 gp sp fp ra].each_with_index { |r, i| @s_to_i[r] = @s_to_i['$'+r] = i ; @i_to_s[i] = '$'+r }
|
|
||||||
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
Sym = @i_to_s.sort.map { |k, v| v.to_sym }
|
|
||||||
def symbolic ; @i == 0 ? 0 : Sym[@i] end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FpReg
|
|
||||||
class << self
|
|
||||||
attr_accessor :s_to_i, :i_to_s
|
|
||||||
end
|
|
||||||
@i_to_s = (0..31).map { |i| "$f#{i}" }
|
|
||||||
@s_to_i = (0..31).inject({}) { |h, i| h.update "f#{i}" => i, "$f#{i}" => i }
|
|
||||||
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
attr_accessor :base, :offset, :sz
|
|
||||||
def initialize(base, offset, sz=32)
|
|
||||||
@base, @offset, @sz = base, offset, sz
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig)
|
|
||||||
p = nil
|
|
||||||
p = Expression[p, :+, @base.symbolic] if base
|
|
||||||
p = Expression[p, :+, @offset] if offset
|
|
||||||
Indirection[p.reduce, @sz/8, orig]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(endianness = :big, family = :latest)
|
|
||||||
super()
|
|
||||||
@endianness = endianness
|
|
||||||
@size = 32
|
|
||||||
@family = family
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
send("init_#@family")
|
|
||||||
@opcode_list
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class MIPS64 < MIPS
|
|
||||||
def initialize(endianness = :big, family = :latest)
|
|
||||||
super(endianness, family)
|
|
||||||
@size = 64
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,512 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/mips/main'
|
|
||||||
|
|
||||||
# TODO coprocessors, floating point, 64bits, thumb mode
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
class MIPS
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @fields_mask[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
}
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_mips32_obsolete
|
|
||||||
addop 'beql', 0b010100 << 26, :rt, :rs, :i16, :setip # == , exec delay slot only if jump taken
|
|
||||||
addop 'bnel', 0b010101 << 26, :rt, :rs, :i16, :setip # !=
|
|
||||||
addop 'blezl',0b010110 << 26, :rt_z, :rs, :i16, :setip # <= 0
|
|
||||||
addop 'bgtzl',0b010111 << 26, :rt_z, :rs, :i16, :setip # > 0
|
|
||||||
addop 'bltzl',1 << 26 | 0b00010 << 16, :rs, :i16, :setip
|
|
||||||
addop 'bgezl',1 << 26 | 0b00011 << 16, :rs, :i16, :setip
|
|
||||||
addop 'bltzall', 1 << 26 | 0b10010 << 16, :rs, :i16, :setip
|
|
||||||
addop 'bgezall', 1 << 26 | 0b10011 << 16, :rs, :i16, :setip
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_mips32_reserved
|
|
||||||
addop 'future111011', 0b111011 << 26, :i26
|
|
||||||
|
|
||||||
%w[011000 011001 011010 011011 100111 101100 101101 110100 110111 111100 111111].each { |b|
|
|
||||||
addop "reserved#{b}", b.to_i(2) << 26, :i26
|
|
||||||
}
|
|
||||||
|
|
||||||
addop 'ase_jalx', 0b011101 << 26, :i26
|
|
||||||
addop 'ase011110', 0b011110 << 26, :i26
|
|
||||||
# TODO add all special/regimm/...
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_mips32
|
|
||||||
@opcode_list = []
|
|
||||||
@fields_mask.update :rs => 0x1f, :rt => 0x1f, :rd => 0x1f, :sa => 0x1f,
|
|
||||||
:i16 => 0xffff, :i26 => 0x3ffffff, :rs_i16 => 0x3e0ffff, :it => 0x1f,
|
|
||||||
:ft => 0x1f, :idm1 => 0x1f, :idb => 0x1f, :sel => 7, :i20 => 0xfffff
|
|
||||||
@fields_shift.update :rs => 21, :rt => 16, :rd => 11, :sa => 6,
|
|
||||||
:i16 => 0, :i26 => 0, :rs_i16 => 0, :it => 16,
|
|
||||||
:ft => 16, :idm1 => 11, :idb => 11, :sel => 0, :i20 => 6
|
|
||||||
@valid_props.update :mi8 => true, :mu8 => true, :mi16 => true, :mu16 => true
|
|
||||||
|
|
||||||
init_mips32_obsolete
|
|
||||||
init_mips32_reserved
|
|
||||||
|
|
||||||
addop 'j', 0b000010 << 26, :i26, :setip, :stopexec # sets the program counter to (i26 << 2) | ((pc+4) & 0xfc000000) ie i26*4 in the 256M-aligned section containing the instruction in the delay slot
|
|
||||||
addop 'jal', 0b000011 << 26, :i26, :setip, :stopexec, :saveip # same thing, saves return addr in r31
|
|
||||||
|
|
||||||
addop 'mov', 0b001000 << 26, :rt, :rs # rt <- rs+0
|
|
||||||
addop 'addi', 0b001000 << 26, :rt, :rs, :i16 # add rt <- rs+i
|
|
||||||
addop 'li', 0b001001 << 26, :rt, :i16 # addiu rt <- zero+i
|
|
||||||
addop 'addiu',0b001001 << 26, :rt, :rs, :i16 # add unsigned
|
|
||||||
addop 'slti', 0b001010 << 26, :rt, :rs, :i16 # set on less than
|
|
||||||
addop 'sltiu',0b001011 << 26, :rt, :rs, :i16 # set on less than unsigned
|
|
||||||
addop 'andi', 0b001100 << 26, :rt, :rs, :i16 # and
|
|
||||||
addop 'ori', 0b001101 << 26, :rt, :rs, :i16 # or
|
|
||||||
addop 'xori', 0b001110 << 26, :rt, :rs, :i16 # xor
|
|
||||||
addop 'lui', 0b001111 << 26, :rt, :i16 # load upper
|
|
||||||
# addop 'li', (0b001111 << 26) << 32 | (0b001101 << 26), :rt_64, :i32 # lui + ori
|
|
||||||
|
|
||||||
addop 'b', 0b000100 << 26, :i16, :setip, :stopexec # bz $zero
|
|
||||||
addop 'bz', 0b000100 << 26, :rs, :i16, :setip # == 0 (beq $0)
|
|
||||||
addop 'bz', 0b000100 << 26, :rt, :i16, :setip # == 0
|
|
||||||
addop 'bnz', 0b000101 << 26, :rs, :i16, :setip # != 0
|
|
||||||
addop 'bnz', 0b000101 << 26, :rt, :i16, :setip # != 0
|
|
||||||
|
|
||||||
addop 'beq', 0b000100 << 26, :rt, :rs, :i16, :setip # ==
|
|
||||||
addop 'bne', 0b000101 << 26, :rt, :rs, :i16, :setip # !=
|
|
||||||
addop 'blez', 0b000110 << 26, :rs, :i16, :setip # <= 0
|
|
||||||
addop 'bgtz', 0b000111 << 26, :rs, :i16, :setip # > 0
|
|
||||||
|
|
||||||
addop 'lb', 0b100000 << 26, :rt, :rs_i16, :mi8 # load byte rs <- [rt+i]
|
|
||||||
addop 'lh', 0b100001 << 26, :rt, :rs_i16, :mi16 # load halfword
|
|
||||||
addop 'lwl', 0b100010 << 26, :rt, :rs_i16 # load word left
|
|
||||||
addop 'lw', 0b100011 << 26, :rt, :rs_i16 # load word
|
|
||||||
addop 'lbu', 0b100100 << 26, :rt, :rs_i16, :mu8 # load byte unsigned
|
|
||||||
addop 'lhu', 0b100101 << 26, :rt, :rs_i16, :mu16 # load halfword unsigned
|
|
||||||
addop 'lwr', 0b100110 << 26, :rt, :rs_i16 # load word right
|
|
||||||
|
|
||||||
addop 'sb', 0b101000 << 26, :rt, :rs_i16, :mi8 # store byte
|
|
||||||
addop 'sh', 0b101001 << 26, :rt, :rs_i16, :mi16 # store halfword
|
|
||||||
addop 'swl', 0b101010 << 26, :rt, :rs_i16 # store word left
|
|
||||||
addop 'sw', 0b101011 << 26, :rt, :rs_i16 # store word
|
|
||||||
addop 'swr', 0b101110 << 26, :rt, :rs_i16 # store word right
|
|
||||||
|
|
||||||
addop 'll', 0b110000 << 26, :rt, :rs_i16 # load linked word (read for atomic r/modify/w, sc does the w)
|
|
||||||
addop 'sc', 0b111000 << 26, :rt, :rs_i16 # store conditional word
|
|
||||||
|
|
||||||
addop 'lwc1', 0b110001 << 26, :ft, :rs_i16 # load word in fpreg low
|
|
||||||
addop 'swc1', 0b111001 << 26, :ft, :rs_i16 # store low fpreg word
|
|
||||||
addop 'lwc2', 0b110010 << 26, :rt, :rs_i16 # load word to copro2 register low
|
|
||||||
addop 'swc2', 0b111010 << 26, :rt, :rs_i16 # store low coproc2 register
|
|
||||||
|
|
||||||
addop 'ldc1', 0b110101 << 26, :ft, :rs_i16 # load dword in fpreg low
|
|
||||||
addop 'sdc1', 0b111101 << 26, :ft, :rs_i16 # store fpreg
|
|
||||||
addop 'ldc2', 0b110110 << 26, :rt, :rs_i16 # load dword to copro2 register
|
|
||||||
addop 'sdc2', 0b111110 << 26, :rt, :rs_i16 # store coproc2 register
|
|
||||||
|
|
||||||
addop 'pref', 0b110011 << 26, :it, :rs_i16 # prefetch (it = %w[load store r2 r3 load_streamed store_streamed load_retained store_retained
|
|
||||||
# r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 writeback_invalidate
|
|
||||||
# id26 id27 id28 id29 prepare_for_store id31]
|
|
||||||
addop 'cache',0b101111 << 26, :it, :rs_i16 # do things with the proc cache
|
|
||||||
|
|
||||||
# special
|
|
||||||
addop 'nop', 0
|
|
||||||
addop 'ssnop',1<<6
|
|
||||||
addop 'ehb', 3<<6
|
|
||||||
addop 'sll', 0b000000, :rd, :rt, :sa
|
|
||||||
addop 'movf', 0b000001, :rd, :rs, :cc
|
|
||||||
addop 'movt', 0b000001 | (1<<16), :rd, :rs, :cc
|
|
||||||
addop 'srl', 0b000010, :rd, :rt, :sa
|
|
||||||
addop 'sra', 0b000011, :rd, :rt, :sa
|
|
||||||
addop 'sllv', 0b000100, :rd, :rt, :rs
|
|
||||||
addop 'srlv', 0b000110, :rd, :rt, :rs
|
|
||||||
addop 'srav', 0b000111, :rd, :rt, :rs
|
|
||||||
|
|
||||||
addop 'jr', 0b001000, :rs, :setip, :stopexec # hint field ?
|
|
||||||
addop 'jr.hb',0b001000 | (1<<10), :rs, :setip, :stopexec
|
|
||||||
addop 'jalr', 0b001001 | (31<<11), :rs, :setip, :stopexec, :saveip # rd = r31 implicit
|
|
||||||
addop 'jalr', 0b001001, :rd, :rs, :setip, :stopexec, :saveip
|
|
||||||
addop 'jalr.hb', 0b001001 | (1<<10) | (31<<11), :rs, :setip, :stopexec, :saveip
|
|
||||||
addop 'jalr.hb', 0b001001 | (1<<10), :rd, :rs, :setip, :stopexec, :saveip
|
|
||||||
addop 'movz', 0b001010, :rd, :rs, :rt # rt == 0 ? rd <- rs
|
|
||||||
addop 'movn', 0b001011, :rd, :rs, :rt
|
|
||||||
addop 'syscall', 0b001100, :i20
|
|
||||||
addop 'break',0b001101, :i20, :stopexec
|
|
||||||
addop 'sync', 0b001111 # type 0 implicit
|
|
||||||
addop 'sync', 0b001111, :sa
|
|
||||||
|
|
||||||
addop 'mfhi', 0b010000, :rd # copies special reg HI to reg
|
|
||||||
addop 'mthi', 0b010001, :rs # copies reg to special reg HI
|
|
||||||
addop 'mflo', 0b010010, :rd # copies special reg LO to reg
|
|
||||||
addop 'mtlo', 0b010011, :rs # copies reg to special reg LO
|
|
||||||
|
|
||||||
addop 'mult', 0b011000, :rs, :rt # multiplies the registers and store the result in HI:LO
|
|
||||||
addop 'multu',0b011001, :rs, :rt
|
|
||||||
addop 'div', 0b011010, :rs, :rt
|
|
||||||
addop 'divu', 0b011011, :rs, :rt
|
|
||||||
addop 'add', 0b100000, :rd, :rs, :rt
|
|
||||||
addop 'addu', 0b100001, :rd, :rs, :rt
|
|
||||||
addop 'sub', 0b100010, :rd, :rs, :rt
|
|
||||||
addop 'subu', 0b100011, :rd, :rs, :rt
|
|
||||||
addop 'and', 0b100100, :rd, :rs, :rt
|
|
||||||
addop 'or', 0b100101, :rd, :rs, :rt
|
|
||||||
addop 'xor', 0b100110, :rd, :rs, :rt
|
|
||||||
addop 'not', 0b100111, :rd, :rt # nor $0
|
|
||||||
addop 'not', 0b100111, :rd, :rs
|
|
||||||
addop 'nor', 0b100111, :rd, :rs, :rt
|
|
||||||
|
|
||||||
addop 'slt', 0b101010, :rd, :rs, :rt # rs<rt ? rd<-1 : rd<-0
|
|
||||||
addop 'sltu', 0b101011, :rd, :rs, :rt
|
|
||||||
|
|
||||||
addop 'tge', 0b110000, :rs, :rt # rs >= rt ? trap
|
|
||||||
addop 'tgeu', 0b110001, :rs, :rt
|
|
||||||
addop 'tlt', 0b110010, :rs, :rt
|
|
||||||
addop 'tltu', 0b110011, :rs, :rt
|
|
||||||
addop 'teq', 0b110100, :rs, :rt
|
|
||||||
addop 'tne', 0b110110, :rs, :rt
|
|
||||||
|
|
||||||
|
|
||||||
# regimm
|
|
||||||
addop 'bltz', (1<<26) | (0b00000<<16), :rs, :i16, :setip
|
|
||||||
addop 'bgez', (1<<26) | (0b00001<<16), :rs, :i16, :setip
|
|
||||||
addop 'tgei', (1<<26) | (0b01000<<16), :rs, :i16, :setip
|
|
||||||
addop 'tgfiu',(1<<26) | (0b01001<<16), :rs, :i16, :setip
|
|
||||||
addop 'tlti', (1<<26) | (0b01010<<16), :rs, :i16, :setip
|
|
||||||
addop 'tltiu',(1<<26) | (0b01011<<16), :rs, :i16, :setip
|
|
||||||
addop 'teqi', (1<<26) | (0b01100<<16), :rs, :i16, :setip
|
|
||||||
addop 'tnei', (1<<26) | (0b01110<<16), :rs, :i16, :setip
|
|
||||||
addop 'bltzal', (1<<26) | (0b10000<<16), :rs, :i16, :setip, :saveip
|
|
||||||
addop 'bgezal', (1<<26) | (0b10001<<16), :i16, :setip, :stopexec, :saveip # bgezal $zero => unconditionnal
|
|
||||||
addop 'bgezal', (1<<26) | (0b10001<<16), :rs, :i16, :setip, :saveip
|
|
||||||
|
|
||||||
|
|
||||||
# special2
|
|
||||||
addop 'madd', (0b011100<<26) | 0b000000, :rs, :rt
|
|
||||||
addop 'maddu',(0b011100<<26) | 0b000001, :rs, :rt
|
|
||||||
addop 'mul', (0b011100<<26) | 0b000010, :rd, :rs, :rt
|
|
||||||
addop 'msub', (0b011100<<26) | 0b000100, :rs, :rt
|
|
||||||
addop 'msubu',(0b011100<<26) | 0b000101, :rs, :rt
|
|
||||||
addop 'clz', (0b011100<<26) | 0b100000, :rd, :rs, :rt # must have rs == rt
|
|
||||||
addop 'clo', (0b011100<<26) | 0b100001, :rd, :rs, :rt # must have rs == rt
|
|
||||||
addop 'sdbbp',(0b011100<<26) | 0b111111, :i20
|
|
||||||
|
|
||||||
|
|
||||||
# cp0
|
|
||||||
addop 'mfc0', (0b010000<<26) | (0b00000<<21), :rt, :idb
|
|
||||||
addop 'mfc0', (0b010000<<26) | (0b00000<<21), :rt, :idb, :sel
|
|
||||||
addop 'mtc0', (0b010000<<26) | (0b00100<<21), :rt, :idb
|
|
||||||
addop 'mtc0', (0b010000<<26) | (0b00100<<21), :rt, :idb, :sel
|
|
||||||
|
|
||||||
addop 'tlbr', (0b010000<<26) | (1<<25) | 0b000001
|
|
||||||
addop 'tlbwi',(0b010000<<26) | (1<<25) | 0b000010
|
|
||||||
addop 'tlbwr',(0b010000<<26) | (1<<25) | 0b000110
|
|
||||||
addop 'tlbp', (0b010000<<26) | (1<<25) | 0b001000
|
|
||||||
addop 'eret', (0b010000<<26) | (1<<25) | 0b011000
|
|
||||||
addop 'deret',(0b010000<<26) | (1<<25) | 0b011111
|
|
||||||
addop 'wait', (0b010000<<26) | (1<<25) | 0b100000 # mode field ?
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_mips32r2
|
|
||||||
init_mips32
|
|
||||||
|
|
||||||
addop 'rotr', 0b000010 | (1<<21), :rd, :rt, :sa
|
|
||||||
addop 'rotrv',0b000110 | (1<<6), :rd, :rt, :rs
|
|
||||||
|
|
||||||
addop 'synci',(1<<26) | (0b11111<<16), :rs_i16
|
|
||||||
|
|
||||||
# special3
|
|
||||||
addop 'ext', (0b011111<<26) | 0b000000, :rt, :rs, :sa, :idm1
|
|
||||||
addop 'ins', (0b011111<<26) | 0b000100, :rt, :rs, :sa, :idb
|
|
||||||
addop 'rdhwr',(0b011111<<26)| 0b111011, :rt, :rd
|
|
||||||
addop 'wsbh',(0b011111<<26) | (0b00010<<6) | 0b100000, :rd, :rt
|
|
||||||
addop 'seb', (0b011111<<26) | (0b10000<<6) | 0b100000, :rd, :rt
|
|
||||||
addop 'seh', (0b011111<<26) | (0b11000<<6) | 0b100000, :rd, :rt
|
|
||||||
|
|
||||||
# cp0
|
|
||||||
addop 'rdpgpr', (0b010000<<26) | (0b01010<<21), :rd, :rt
|
|
||||||
addop 'wrpgpr', (0b010000<<26) | (0b01110<<21), :rd, :rt
|
|
||||||
addop 'di', (0b010000<<26) | (0b01011<<21) | (0b01100<<11) | (0<<5)
|
|
||||||
addop 'di', (0b010000<<26) | (0b01011<<21) | (0b01100<<11) | (0<<5), :rt
|
|
||||||
addop 'ei', (0b010000<<26) | (0b01011<<21) | (0b01100<<11) | (1<<5)
|
|
||||||
addop 'ei', (0b010000<<26) | (0b01011<<21) | (0b01100<<11) | (1<<5), :rt
|
|
||||||
end
|
|
||||||
alias init_latest init_mips32r2
|
|
||||||
end
|
|
||||||
|
|
||||||
class MIPS64
|
|
||||||
def init_mips64
|
|
||||||
init_mips32r2
|
|
||||||
@valid_props.update :mi64 => true
|
|
||||||
|
|
||||||
addop 'ld', 0b110111 << 26, :rt, :rs_i16, :m64
|
|
||||||
addop 'lwu', 0b100111 << 26, :rt, :rs_i16
|
|
||||||
addop 'sd', 0b111111 << 26, :rt, :rs_i16, :m64
|
|
||||||
addop 'scd', 0b111100 << 26, :rt, :rs_i16, :m64
|
|
||||||
addop 'ldl', 0b011010 << 26, :rt, :rs_i16
|
|
||||||
addop 'ldr', 0b011011 << 26, :rt, :rs_i16
|
|
||||||
addop 'sdl', 0b101100 << 26, :rt, :rs_i16
|
|
||||||
addop 'sdr', 0b101101 << 26, :rt, :rs_i16
|
|
||||||
addop 'lld', 0b110100 << 26, :rt, :rs_i16
|
|
||||||
addop 'daddi', 0b011000 << 26, :rt, :rs, :i16
|
|
||||||
addop 'daddiu', 0b011001 << 26, :rt, :rs, :i16
|
|
||||||
|
|
||||||
addop 'dclo', (0b011100 << 26) | (0b100101), :rd, :rt, :rs
|
|
||||||
addop 'dclz', (0b011100 << 26) | (0b100100), :rd, :rt, :rs
|
|
||||||
|
|
||||||
addop 'dadd', 0b101100, :rd, :rs, :rt
|
|
||||||
addop 'daddu', 0b101101, :rd, :rs, :rt
|
|
||||||
addop 'dsub', 0b101110, :rd, :rs, :rt
|
|
||||||
addop 'dsubu', 0b101111, :rd, :rs, :rt
|
|
||||||
addop 'dsll', 0b111000, :rd, :rt, :sa
|
|
||||||
addop 'dsll32', 0b111100, :rd, :rt, :sa
|
|
||||||
addop 'dsllv', 0b010100, :rd, :rt, :rs
|
|
||||||
addop 'dsra', 0b111011, :rd, :rt, :sa
|
|
||||||
addop 'dsra32', 0b111111, :rd, :rt, :sa
|
|
||||||
addop 'dsrav', 0b010111, :rd, :rt, :rs
|
|
||||||
addop 'dsrl', 0b111010, :rd, :rt, :sa
|
|
||||||
addop 'dsrl32', 0b111110, :rd, :rt, :sa
|
|
||||||
addop 'dsrlv', 0b010110, :rd, :rt, :rs
|
|
||||||
addop 'ddiv', 0b011110, :rs, :rt
|
|
||||||
addop 'ddivu', 0b011111, :rs, :rt
|
|
||||||
addop 'dmult', 0b011100, :rs, :rt
|
|
||||||
addop 'dmultu', 0b011101, :rs, :rt
|
|
||||||
|
|
||||||
addop 'dmfc0', (0b010000<<26) | (0b00001<<21), :rt, :idb
|
|
||||||
addop 'dmfc0', (0b010000<<26) | (0b00001<<21), :rt, :idb, :sel
|
|
||||||
addop 'dmtc0', (0b010000<<26) | (0b00101<<21), :rt, :idb
|
|
||||||
addop 'dmtc0', (0b010000<<26) | (0b00101<<21), :rt, :idb, :sel
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_mips64r2
|
|
||||||
init_mips64
|
|
||||||
@fields_mask.update :msbd => 0x1f
|
|
||||||
@fields_shift.update :msbd => 11
|
|
||||||
|
|
||||||
addop 'dext', (0b011111 << 26) | 0b000011, :rt, :rs, :sa, :msbd # sa => lsb
|
|
||||||
addop 'dextm', (0b011111 << 26) | 0b000001, :rt, :rs, :sa, :msbd
|
|
||||||
addop 'dextu', (0b011111 << 26) | 0b000010, :rt, :rs, :sa, :msbd
|
|
||||||
addop 'dins', (0b011111 << 26) | 0b000111, :rt, :rs, :sa, :msbd
|
|
||||||
addop 'dinsm', (0b011111 << 26) | 0b000101, :rt, :rs, :sa, :msbd
|
|
||||||
addop 'dinsu', (0b011111 << 26) | 0b000110, :rt, :rs, :sa, :msbd
|
|
||||||
|
|
||||||
addop 'drotr', (1 << 21) | 0b111010, :rd, :rt, :sa
|
|
||||||
addop 'drotr32', (1 << 21) | 0b111110, :rd, :rt, :sa
|
|
||||||
addop 'drotrv', (1 << 6) | 0b010110, :rd, :rt, :rs
|
|
||||||
|
|
||||||
addop 'dsbh', (0b011111 << 26) | (0b00010 << 6) | 0b100100, :rd, :rt
|
|
||||||
addop 'dshd', (0b011111 << 26) | (0b00101 << 6) | 0b100100, :rd, :rt
|
|
||||||
end
|
|
||||||
|
|
||||||
alias init_latest init_mips64r2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
__END__
|
|
||||||
def macro_addop_cop1(name, bin, *aprops)
|
|
||||||
flds = [ :rt, :fs ]
|
|
||||||
addop name, :cop1, bin, 'rt, fs', flds, *aprops
|
|
||||||
end
|
|
||||||
|
|
||||||
def macro_addop_cop1_precision(name, type, bin, fmt, *aprops)
|
|
||||||
flds = [ :ft, :fs, :fd ]
|
|
||||||
addop name+'.'+(type.to_s[5,7]), type, bin, fmt, flds, *aprops
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
# Initialize the instruction set with the MIPS32 Instruction Set
|
|
||||||
def init_mips32
|
|
||||||
:cc => [7, 18, :fpcc],
|
|
||||||
:op => [0x1F, 16, :op ], :cp2_rt => [0x1F, 16, :cp2_reg ],
|
|
||||||
:stype => [0x1F, 6, :imm ],
|
|
||||||
:code => [0xFFFFF, 6, :code ],
|
|
||||||
:sel => [3, 0, :sel ]})
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# COP0, field rs
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
addop 'mfc0', :cop0, 0b00000, 'rt, rd, sel', [ :rt, :rd, :sel ]
|
|
||||||
addop 'mtc0', :cop0, 0b00100, 'rt, rd, sel', [ :rt, :rd, :sel ]
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# COP0 when rs=C0
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
macro_addop_cop0_c0 'tlbr', 0b000001
|
|
||||||
macro_addop_cop0_c0 'tlbwi', 0b000010
|
|
||||||
macro_addop_cop0_c0 'tlwr', 0b000110
|
|
||||||
macro_addop_cop0_c0 'tlbp', 0b001000
|
|
||||||
macro_addop_cop0_c0 'eret', 0b011000
|
|
||||||
macro_addop_cop0_c0 'deret', 0b011111
|
|
||||||
macro_addop_cop0_c0 'wait', 0b100000
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# COP1, field rs
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
macro_addop_cop1 'mfc1', 0b00000
|
|
||||||
macro_addop_cop1 'cfc1', 0b00010
|
|
||||||
macro_addop_cop1 'mtc1', 0b00100
|
|
||||||
macro_addop_cop1 'ctc1', 0b00110
|
|
||||||
|
|
||||||
addop "bc1f", :cop1, 0b01000, 'cc, off', [ :cc, :off ], :diff_bits, [ 16, 3, 0 ]
|
|
||||||
addop "bc1fl", :cop1, 0b01000, 'cc, off', [ :cc, :off ], :diff_bits, [ 16, 3, 2 ]
|
|
||||||
addop "bc1t", :cop1, 0b01000, 'cc, off', [ :cc, :off ], :diff_bits, [ 16, 3, 1 ]
|
|
||||||
addop "bc1tl", :cop1, 0b01000, 'cc, off', [ :cc, :off ], :diff_bits, [ 16, 3, 3 ]
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# COP1, field rs=S/D
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
[ :cop1_s, :cop1_d ].each do |type|
|
|
||||||
type_str = type.to_s[5,7]
|
|
||||||
|
|
||||||
macro_addop_cop1_precision 'add', type, 0b000000, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'sub', type, 0b000001, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'mul', type, 0b000010, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'abs', type, 0b000101, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'mov', type, 0b000110, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'neg', type, 0b000111, 'fd, fs', :ft_zero
|
|
||||||
|
|
||||||
macro_addop_cop1_precision 'movz', type, 0b010010, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'movn', type, 0b010011, 'fd, fs, ft'
|
|
||||||
|
|
||||||
addop "movf.#{type_str}", type, 0b010001, 'fd, fs, cc', [ :cc, :fs, :fd ], :diff_bits, [ 16, 1, 0 ]
|
|
||||||
addop "movt.#{type_str}", type, 0b010001, 'fd, fs, cc', [ :cc, :fs, :fd ], :diff_bits, [ 16, 1, 1 ]
|
|
||||||
|
|
||||||
%w(f un eq ueq olt ult ole ule sf ngle seq ngl lt nge le ngt).each_with_index do |cond, index|
|
|
||||||
addop "c.#{cond}.#{type_str}", type, 0b110000+index, 'cc, fs, ft',
|
|
||||||
[ :ft, :fs, :cc ]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# S and D Without PS
|
|
||||||
|
|
||||||
[:cop1_s, :cop1_d].each do |type|
|
|
||||||
macro_addop_cop1_precision 'div', type, 0b000011, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'sqrt', type, 0b000100, 'fd, fs', :ft_zero
|
|
||||||
|
|
||||||
macro_addop_cop1_precision 'round.w', type, 0b001100, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'trunc.w', type, 0b001101, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'ceil.w', type, 0b001110, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'floor.w', type, 0b001111, 'fd, fs', :ft_zero
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# COP2 is not decoded (pretty useless)
|
|
||||||
|
|
||||||
[:cop1_d,:cop1_w].each { |type| macro_addop_cop1_precision 'cvt.s', type, 0b100000, 'fd, fs', :ft_zero }
|
|
||||||
[:cop1_s,:cop1_w].each { |type| macro_addop_cop1_precision 'cvt.d', type, 0b100001, 'fd, fs', :ft_zero }
|
|
||||||
[:cop1_s,:cop1_d].each { |type| macro_addop_cop1_precision 'cvt.w', type, 0b100100, 'fd, fs', :ft_zero }
|
|
||||||
|
|
||||||
[ :normal, :special, :regimm, :special2, :cop0, :cop0_c0, :cop1, :cop1_s,
|
|
||||||
:cop1_d, :cop1_w ].each \
|
|
||||||
{ |t| @@opcodes_by_class[t] = opcode_list.find_all { |o| o.type == t } }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Initialize the instruction set with the MIPS32 Instruction Set Release 2
|
|
||||||
def init_mips64
|
|
||||||
init_mips32
|
|
||||||
|
|
||||||
#SPECIAL
|
|
||||||
macro_addop_special "rotr", 0b000010, 'rd, rt, sa', :diff_bits, [ 26, 1, 1 ]
|
|
||||||
macro_addop_special "rotrv", 0b000110, 'rd, rt, rs', :diff_bits, [ 6, 1, 1 ]
|
|
||||||
|
|
||||||
# REGIMM
|
|
||||||
addop "synci", :regimm, 0b11111, '', {:base => [5,21], :off => [16, 0] }
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# SPECIAL3 opcode encoding of function field
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
addop "ext", :special3, 0b00000, 'rt, rs, pos, size', { :rs => [5, 21], :rt => [5, 16],
|
|
||||||
:msbd => [5, 11], :lsb => [5, 6] }
|
|
||||||
addop "ins", :special3, 0b00100, 'rt, rs, pos, size', { :rs => [5, 21], :rt => [5, 16],
|
|
||||||
:msb => [5, 11], :lsb => [5, 6] }
|
|
||||||
|
|
||||||
addop "rdhwr", :special3, 0b111011, 'rt, rd', { :rt => [5, 16], :rd => [5, 11] }
|
|
||||||
|
|
||||||
addop "wsbh", :bshfl, 0b00010, 'rd, rt', { :rt => [5, 16], :rd => [5, 11] }
|
|
||||||
addop "seb", :bshfl, 0b10000, 'rd, rt', { :rt => [5, 16], :rd => [5, 11] }
|
|
||||||
addop "seh", :bshfl, 0b11000, 'rd, rt', { :rt => [5, 16], :rd => [5, 11] }
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# COP0
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
addop "rdpgpr", :cop0, 0b01010, 'rt, rd', {:rt => [5, 16], :rd => [5, 11] }
|
|
||||||
addop "wdpgpr", :cop0, 0b01110, 'rt, rd', {:rt => [5, 16], :rd => [5, 11] }
|
|
||||||
addop "di", :cop0, 0b01011, '', {}, :diff_bits, [ 5, 1 , 0]
|
|
||||||
addop "ei", :cop0, 0b01011, '', {}, :diff_bits, [ 5, 1 , 1]
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# COP1, field rs
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
macro_addop_cop1 "mfhc1", 0b00011
|
|
||||||
macro_addop_cop1 "mthc1", 0b00111
|
|
||||||
|
|
||||||
# Floating point
|
|
||||||
|
|
||||||
[:cop1_s, :cop1_d].each do |type|
|
|
||||||
macro_addop_cop1_precision 'round.l', type, 0b001000, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'trunc.l', type, 0b001001, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'ceil.l', type, 0b001010, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'floor.l', type, 0b001011, 'fd, fs', :ft_zero
|
|
||||||
|
|
||||||
macro_addop_cop1_precision 'recip', type, 0b010101, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'rsqrt', type, 0b010110, 'fd, fs', :ft_zero
|
|
||||||
|
|
||||||
macro_addop_cop1_precision 'cvt.l', type, 0b100101, 'fd, fs', :ft_zero
|
|
||||||
end
|
|
||||||
macro_addop_cop1_precision 'cvt.ps', :cop1_s, 0b100110, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'cvt.s', :cop1_l, 0b100000, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'cvt.d', :cop1_l, 0b100000, 'fd, fs', :ft_zero
|
|
||||||
|
|
||||||
macro_addop_cop1_precision 'add', :cop1_ps, 0b000000, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'sub', :cop1_ps, 0b000001, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'mul', :cop1_ps, 0b000010, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'abs', :cop1_ps, 0b000101, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'mov', :cop1_ps, 0b000110, 'fd, fs', :ft_zero
|
|
||||||
macro_addop_cop1_precision 'neg', :cop1_ps, 0b000111, 'fd, fs', :ft_zero
|
|
||||||
|
|
||||||
macro_addop_cop1_precision 'movz', :cop1_ps, 0b010010, 'fd, fs, ft'
|
|
||||||
macro_addop_cop1_precision 'movn', :cop1_ps, 0b010011, 'fd, fs, ft'
|
|
||||||
|
|
||||||
addop "movf.#{:cop1_ps_str}", :cop1_ps, 0b010001, 'fd, fs, cc', [ :cc, :fs, :fd ]
|
|
||||||
addop "movt.#{:cop1_ps_str}", :cop1_ps, 0b010001, 'fd, fs, cc', [ :cc, :fs, :fd ]
|
|
||||||
|
|
||||||
%w(f un eq ueq olt ult ole ule sf ngle seq ngl lt nge le ngt).each_with_index do |cond, index|
|
|
||||||
addop "c.#{cond}.ps", :cop1_cond, 0b110000+index, 'cc, fs, ft',
|
|
||||||
[ :ft, :fs, :cc ]
|
|
||||||
|
|
||||||
# TODO: COP1X
|
|
||||||
|
|
||||||
[ :special3, :bshfl, :cop1_l, :cop1_ps ].each \
|
|
||||||
{ |t| @@opcodes_by_class[t] = opcode_list.find_all { |o| o.type == t } }
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reset all instructions
|
|
||||||
def reset
|
|
||||||
metaprops_allowed.clear
|
|
||||||
args_allowed.clear
|
|
||||||
props_allowed.clear
|
|
||||||
fields_spec.clear
|
|
||||||
opcode_list.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
# Array containing all the supported opcodes
|
|
||||||
attr_accessor :opcode_list
|
|
||||||
|
|
||||||
init_mips32
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,51 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/mips/opcodes'
|
|
||||||
require 'metasm/parse'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MIPS
|
|
||||||
def parse_arg_valid?(op, sym, arg)
|
|
||||||
# special case for lw reg, imm32(reg) ? (pseudo-instr, need to convert to 'lui t0, up imm32 ori t0 down imm32 add t0, reg lw reg, 0(t0)
|
|
||||||
case sym
|
|
||||||
when :rs, :rt, :rd; arg.kind_of? Reg
|
|
||||||
when :sa, :i16, :i20, :i26; arg.kind_of? Expression
|
|
||||||
when :rs_i16; arg.kind_of? Memref
|
|
||||||
when :ft; arg.kind_of? FpReg
|
|
||||||
else raise "internal error: mips arg #{sym.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_argument(pgm)
|
|
||||||
pgm.skip_space
|
|
||||||
return if not tok = pgm.nexttok
|
|
||||||
if tok.type == :string and Reg.s_to_i[tok.raw]
|
|
||||||
pgm.readtok
|
|
||||||
arg = Reg.new Reg.s_to_i[tok.raw]
|
|
||||||
elsif tok.type == :string and FpReg.s_to_i[tok.raw]
|
|
||||||
pgm.readtok
|
|
||||||
arg = FpReg.new FpReg.s_to_i[tok.raw]
|
|
||||||
else
|
|
||||||
arg = Expression.parse pgm
|
|
||||||
pgm.skip_space
|
|
||||||
# check memory indirection: 'off(base reg)' # XXX scaled index ?
|
|
||||||
if arg and pgm.nexttok and pgm.nexttok.type == :punct and pgm.nexttok.raw == '('
|
|
||||||
pgm.readtok
|
|
||||||
pgm.skip_space_eol
|
|
||||||
ntok = pgm.readtok
|
|
||||||
raise tok, "Invalid base #{ntok}" unless ntok and ntok.type == :string and Reg.s_to_i[ntok.raw]
|
|
||||||
base = Reg.new Reg.s_to_i[ntok.raw]
|
|
||||||
pgm.skip_space_eol
|
|
||||||
ntok = pgm.readtok
|
|
||||||
raise tok, "Invalid memory reference, ')' expected" if not ntok or ntok.type != :punct or ntok.raw != ')'
|
|
||||||
arg = Memref.new base, arg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
arg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,43 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/mips/opcodes'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MIPS
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
def render ; [self.class.i_to_s[@i]] end
|
|
||||||
end
|
|
||||||
class FpReg
|
|
||||||
include Renderable
|
|
||||||
def render ; [self.class.i_to_s[@i]] end
|
|
||||||
end
|
|
||||||
class Memref
|
|
||||||
include Renderable
|
|
||||||
def render ; [@offset, '(', @base, ')'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_instruction(i)
|
|
||||||
r = []
|
|
||||||
r << i.opname
|
|
||||||
if not i.args.empty?
|
|
||||||
r << ' '
|
|
||||||
if (a = i.args.first).kind_of? Expression and a.op == :- and a.lexpr.kind_of? String and a.rexpr.kind_of? String and opcode_list_byname[i.opname].first.props[:setip]
|
|
||||||
# jmp foo is stored as jmp foo - bar ; bar:
|
|
||||||
r << a.lexpr
|
|
||||||
else
|
|
||||||
i.args.each { |a_|
|
|
||||||
r << a_ << ', '
|
|
||||||
}
|
|
||||||
r.pop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,8 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/msp430/decode'
|
|
@ -1,247 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/msp430/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MSP430
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
op.bin_mask = 0
|
|
||||||
op.fields.each_key { |f|
|
|
||||||
op.bin_mask |= @fields_mask[f] << @fields_shift[f]
|
|
||||||
}
|
|
||||||
op.bin_mask ^= 0xffff
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
lookaside = Array.new(256) { [] }
|
|
||||||
opcode_list.each { |op|
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
b = (op.bin >> 8) & 255
|
|
||||||
msk = (op.bin_mask >> 8) & 255
|
|
||||||
|
|
||||||
for i in b..(b | (255^msk))
|
|
||||||
lookaside[i] << op if i & msk == b & msk
|
|
||||||
end
|
|
||||||
}
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
val = edata.decode_imm(:u16, @endianness)
|
|
||||||
edata.ptr -= 2
|
|
||||||
di.opcode = @bin_lookaside[(val >> 8) & 0xff].find { |opcode| (val & opcode.bin_mask) == opcode.bin }
|
|
||||||
di if di.opcode
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
val = edata.decode_imm(:u16, @endianness)
|
|
||||||
|
|
||||||
field_val = lambda{ |f|
|
|
||||||
(val >> @fields_shift[f]) & @fields_mask[f]
|
|
||||||
}
|
|
||||||
|
|
||||||
# must decode rs first
|
|
||||||
vals = {}
|
|
||||||
([:rs, :rd, :r_pc] & op.args).each { |a|
|
|
||||||
mod = { :rs => :as, :rd => :ad, :r_pc => :ad }[a]
|
|
||||||
mod = :as if mod == :ad and not op.fields[mod] # addop_macro1 -> rs + ad
|
|
||||||
|
|
||||||
if a == :r_pc
|
|
||||||
r = Reg.new(0)
|
|
||||||
else
|
|
||||||
r = Reg.new(field_val[a])
|
|
||||||
end
|
|
||||||
|
|
||||||
w = op.props[:byte] ? 1 : 2
|
|
||||||
|
|
||||||
case field_val[mod]
|
|
||||||
when 0
|
|
||||||
if r.i == 3 and a == :rs
|
|
||||||
vals[a] = Expression[0]
|
|
||||||
else
|
|
||||||
vals[a] = r
|
|
||||||
end
|
|
||||||
when 1
|
|
||||||
if r.i == 3 and a == :rs
|
|
||||||
vals[a] = Expression[1]
|
|
||||||
else
|
|
||||||
imm = edata.decode_imm(:u16, @endianness)
|
|
||||||
r = nil if r.i == 2 # [imm]
|
|
||||||
vals[a] = Memref.new(r, imm, w)
|
|
||||||
end
|
|
||||||
when 2
|
|
||||||
if r.i == 3
|
|
||||||
vals[a] = Expression[2]
|
|
||||||
elsif r.i == 2
|
|
||||||
vals[a] = Expression[4]
|
|
||||||
else
|
|
||||||
vals[a] = Memref.new(r, 0, w)
|
|
||||||
end
|
|
||||||
when 3
|
|
||||||
if r.i == 3
|
|
||||||
vals[a] = Expression[-1]
|
|
||||||
elsif r.i == 2
|
|
||||||
vals[a] = Expression[8]
|
|
||||||
elsif r.i == 0 # pc++
|
|
||||||
# XXX order wrt other edata.decode_imm ?
|
|
||||||
vals[a] = Expression[edata.decode_imm(:u16, @endianness)]
|
|
||||||
else
|
|
||||||
vals[a] = Memref.new(r, 0, w, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :joff; Expression[2 * Expression.make_signed(field_val[a], 10)]
|
|
||||||
when :rs, :rd, :r_pc; vals[a]
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
return if edata.ptr > edata.length
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
if di.opcode.props[:setip] and di.opcode.name =~ /^j/
|
|
||||||
delta = di.instruction.args.last.reduce
|
|
||||||
arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce
|
|
||||||
di.instruction.args[-1] = Expression[arg]
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
|
|
||||||
opcode_list.map { |ol| ol.name }.uniq.each { |op|
|
|
||||||
@backtrace_binding[op] ||= case op
|
|
||||||
when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] }}
|
|
||||||
when 'cmp', 'test'; lambda { |di, *a| {} } # TODO
|
|
||||||
when 'add', 'adc' ; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } }
|
|
||||||
when 'sub', 'sbc'; lambda { |di, a0, a1| { a0 => Expression[a0, :-, a1] } }
|
|
||||||
when 'and'; lambda { |di, a0, a1| { a0 => Expression[a0, :&, a1] } }
|
|
||||||
when 'or'; lambda { |di, a0, a1| { a0 => Expression[a0, :|, a1] } }
|
|
||||||
when 'xor'; lambda { |di, a0, a1| { a0 => Expression[a0, :^, a1] } }
|
|
||||||
when 'push'; lambda { |di, a0| { Indirection[:sp, 2] => Expression[a0],
|
|
||||||
:sp => Expression[:sp, :-, 2] } }
|
|
||||||
when 'call'; lambda { |di, a0| { Indirection[:sp, 2] => Expression[di.next_addr],
|
|
||||||
:sp => Expression[:sp, :-, 2] } }
|
|
||||||
when 'pop'; lambda { |di, a0| { a0 => Expression[Indirection[:sp, 2]],
|
|
||||||
:sp => Expression[:sp, :+, 2] } }
|
|
||||||
when 'ret'; lambda { |di| { :sp => Expression[:sp, :+, 2] } }
|
|
||||||
when 'reti'; lambda { |di| { :sp => Expression[:sp, :+, 4] } }
|
|
||||||
when /^j/; lambda { |di, a0| {} }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Reg; arg.symbolic
|
|
||||||
when Memref; arg.symbolic(di.address)
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.basename]
|
|
||||||
bd = binding[di, *a] || {}
|
|
||||||
di.instruction.args.grep(Memref).each { |m|
|
|
||||||
next unless r = m.base and m.postincr
|
|
||||||
r = m.base.symbolic
|
|
||||||
bd[r] ||= Expression[r, :+, m.size]
|
|
||||||
}
|
|
||||||
bd
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
{ :incomplete_binding => Expression[1] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
case di.instruction.opname
|
|
||||||
when 'ret'
|
|
||||||
return [Indirection[:sp, 2, di.address]]
|
|
||||||
when 'reti'
|
|
||||||
return [Indirection[[:sp, :+, 2], 2, di.address]]
|
|
||||||
end
|
|
||||||
|
|
||||||
# XXX add pc, 42 ?
|
|
||||||
val = di.instruction.args[0]
|
|
||||||
case val
|
|
||||||
when Reg; val = val.symbolic
|
|
||||||
when Memref; val = val.symbolic(di.address)
|
|
||||||
end
|
|
||||||
|
|
||||||
[Expression[val]]
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
expr = Expression[expr].reduce_rec
|
|
||||||
expr.kind_of?(Indirection) and expr.len == 2 and expr.target == Expression[:sp]
|
|
||||||
end
|
|
||||||
|
|
||||||
# updates the function backtrace_binding
|
|
||||||
# if the function is big and no specific register is given, do nothing (the binding will be lazily updated later, on demand)
|
|
||||||
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
|
|
||||||
b = f.backtrace_binding
|
|
||||||
|
|
||||||
bt_val = lambda { |r|
|
|
||||||
next if not retaddrlist
|
|
||||||
b[r] = Expression::Unknown
|
|
||||||
bt = []
|
|
||||||
retaddrlist.each { |retaddr|
|
|
||||||
bt |= dasm.backtrace(Expression[r], retaddr, :include_start => true,
|
|
||||||
:snapshot_addr => faddr, :origin => retaddr)
|
|
||||||
}
|
|
||||||
if bt.length != 1
|
|
||||||
b[r] = Expression::Unknown
|
|
||||||
else
|
|
||||||
b[r] = bt.first
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if not wantregs.empty?
|
|
||||||
wantregs.each(&bt_val)
|
|
||||||
else
|
|
||||||
bt_val[:sp]
|
|
||||||
end
|
|
||||||
|
|
||||||
b
|
|
||||||
end
|
|
||||||
|
|
||||||
def replace_instr_arg_immediate(i, old, new)
|
|
||||||
i.args.map! { |a|
|
|
||||||
case a
|
|
||||||
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
|
||||||
when Memref
|
|
||||||
a.base = (a.base == old ? new : Expression[a.base.bind(old => new).reduce]) if a.base.kind_of?(Expression)
|
|
||||||
a
|
|
||||||
else a
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,62 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
class MSP430 < CPU
|
|
||||||
def initialize(e = :little)
|
|
||||||
super()
|
|
||||||
@endianness = e
|
|
||||||
@size = 16
|
|
||||||
end
|
|
||||||
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
Sym = (4..15).inject(0 => :pc, 1 => :sp, 2 => :flags, 3 => :rzero) { |h, i| h.update i => "r#{i}".to_sym }
|
|
||||||
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i) ; @i = i end
|
|
||||||
def symbolic ; Sym[@i] end
|
|
||||||
def render ; [Sym[@i].to_s] end
|
|
||||||
def ==(o) ; o.class == self.class and o.i == @i end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
attr_accessor :base, :offset, :size, :postincr
|
|
||||||
|
|
||||||
def initialize(base, offset = 0, size = nil, postincr = false)
|
|
||||||
@base = base
|
|
||||||
@offset = Expression[offset]
|
|
||||||
@size = size
|
|
||||||
@postincr = postincr
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig=nil)
|
|
||||||
r = @base.symbolic if @base
|
|
||||||
e = Expression[r, :+, @offset].reduce
|
|
||||||
Indirection[e, (@size || 1), orig]
|
|
||||||
end
|
|
||||||
|
|
||||||
include Renderable
|
|
||||||
|
|
||||||
def render
|
|
||||||
b = @base
|
|
||||||
b = @base.to_s + '++' if @base and @postincr
|
|
||||||
p = Expression[b, :+, @offset].reduce
|
|
||||||
Indirection[p, @size].render
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
init
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_list
|
|
||||||
@dbg_register_list ||= Reg::Sym.sort.transpose.last
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,101 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/msp430/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class MSP430
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @valid_args[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a]
|
|
||||||
}
|
|
||||||
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def init
|
|
||||||
@opcode_list = []
|
|
||||||
|
|
||||||
@fields_mask = {
|
|
||||||
:as => 3, # adressing mode
|
|
||||||
:ad => 1, # adressing mode
|
|
||||||
:rd => 0xf,
|
|
||||||
:rs => 0xf,
|
|
||||||
:joff => 0x3ff, # signed offset for jumps
|
|
||||||
}
|
|
||||||
@fields_shift = {
|
|
||||||
:as => 4,
|
|
||||||
:ad => 7,
|
|
||||||
:rd => 0,
|
|
||||||
:rs => 8,
|
|
||||||
:joff => 0,
|
|
||||||
}
|
|
||||||
@valid_args = { :r_pc => true, :rd => true, :rs => true, :joff => true }
|
|
||||||
@valid_props = { :setip => true, :stopexec => true, :saveip => true, :byte => true }
|
|
||||||
|
|
||||||
# https://en.wikipedia.org/wiki/TI_MSP430
|
|
||||||
|
|
||||||
addop_macro1 'rrc', 0, :byte
|
|
||||||
addop_macro1 'swpb', 1
|
|
||||||
addop_macro1 'rra', 2, :byte
|
|
||||||
addop_macro1 'sxt', 3
|
|
||||||
addop_macro1 'push', 4, :byte
|
|
||||||
addop_macro1 'call', 5, :setip, :stopexec, :saveip
|
|
||||||
|
|
||||||
addop 'reti', 0b000100_110_0000000
|
|
||||||
|
|
||||||
addop_macro2 'jnz', 0
|
|
||||||
addop_macro2 'jz', 1
|
|
||||||
addop_macro2 'jnc', 2
|
|
||||||
addop_macro2 'jc', 3
|
|
||||||
addop_macro2 'jb', 4 # 'jn' jump if negative => jl unsigned ?
|
|
||||||
addop_macro2 'jge', 5
|
|
||||||
addop_macro2 'jl', 6
|
|
||||||
addop_macro2 'jmp', 7, :stopexec
|
|
||||||
|
|
||||||
addop 'ret', 0x4130, :setip, :stopexec # mov pc, [sp++]
|
|
||||||
addop 'pop', 0x4130, :rd, :ad # mov rd, [sp++]
|
|
||||||
|
|
||||||
addop_macro3 'mov', 4
|
|
||||||
addop_macro3 'add', 5
|
|
||||||
addop_macro3 'adc', 6 # 'addc'
|
|
||||||
addop_macro3 'sbc', 7
|
|
||||||
addop_macro3 'sub', 8
|
|
||||||
addop_macro3 'cmp', 9
|
|
||||||
addop_macro3 'dadd',10 # decimal add with carry
|
|
||||||
addop_macro3 'test',11 # 'bit'
|
|
||||||
addop_macro3 'andn',12 # 'bic'
|
|
||||||
addop_macro3 'or', 13 # 'bis'
|
|
||||||
addop_macro3 'xor', 14
|
|
||||||
addop_macro3 'and', 15
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_macro1(name, bin, *props)
|
|
||||||
if props.delete :byte
|
|
||||||
addop_byte name, (0b000100 << 10) | (bin << 7), :as, :rd, *props
|
|
||||||
else
|
|
||||||
addop name, (0b000100 << 10) | (bin << 7), :as, :rd, *props
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_macro2(name, bin, *props)
|
|
||||||
addop name, (0b001 << 13) | (bin << 10), :joff, :setip, *props
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_macro3(name, bin, *props)
|
|
||||||
addop_byte name, (bin << 12), :r_pc, :ad, :as, :rs, :setip, :stopexec # dst == pc
|
|
||||||
addop_byte name, (bin << 12), :rd, :ad, :as, :rs
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_byte(name, bin, *props)
|
|
||||||
addop name, bin, *props
|
|
||||||
addop name + '.b', bin | (1 << 6), :byte, *props
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,41 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/pic16c/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Pic16c
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
|
|
||||||
op.bin_mask = Array.new(op.bin.length, 0)
|
|
||||||
op.fields.each { |f, (oct, off)|
|
|
||||||
op.bin_mask[oct] |= (@fields_mask[f] << off)
|
|
||||||
}
|
|
||||||
op.bin_mask.map! { |v| 255 ^ v }
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
# sets up a hash byte value => list of opcodes that may match
|
|
||||||
# opcode.bin_mask is built here
|
|
||||||
lookaside = Array.new(256) { [] }
|
|
||||||
@opcode_list.each { |op|
|
|
||||||
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
|
|
||||||
b = op.bin[0]
|
|
||||||
msk = op.bin_mask[0]
|
|
||||||
|
|
||||||
for i in b..(b | (255^msk))
|
|
||||||
ext if i & msk != b & msk
|
|
||||||
|
|
||||||
lookaside[i] << op
|
|
||||||
end
|
|
||||||
}
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,17 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Pic16c < CPU
|
|
||||||
def initialize(endianness = :big)
|
|
||||||
super()
|
|
||||||
@endianness = endianness
|
|
||||||
init
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,68 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/pic16c/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Pic16c
|
|
||||||
def addop(name, bin, *l)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
l.each { |ll|
|
|
||||||
if @props_allowed[ll]
|
|
||||||
o.props[ll] = true
|
|
||||||
else
|
|
||||||
o.args << ll
|
|
||||||
o.fields[ll] = @fields_off[ll]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def init
|
|
||||||
@fields_mask = {:f => 0x7f, :b => 0x7, :k => 0xff, :klong => 0x3ff, :d => 1 }
|
|
||||||
@props_allowed = {:setip => true, :saveip => true, :stopexec => true }
|
|
||||||
@fields_off = { :f => 0, :b => 7, :k => 0, :klong => 0, :d => 7, :d => 7 }
|
|
||||||
|
|
||||||
addop 'addwf', 0b00_0111_0000_0000, :f, :d
|
|
||||||
addop 'andwf', 0b00_0101_0000_0000, :f, :d
|
|
||||||
addop 'clrf', 0b00_0001_1000_0000, :f
|
|
||||||
addop 'clrw', 0b00_0001_0000_0000 # 00_0001_0xxx_xxxx
|
|
||||||
addop 'comf', 0b00_1001_0000_0000, :f, :d
|
|
||||||
addop 'decf', 0b00_0011_0000_0000, :f, :d
|
|
||||||
addop 'decfsz',0b00_1011_0000_0000, :f, :d
|
|
||||||
addop 'incf', 0b00_1010_0000_0000, :f, :d
|
|
||||||
addop 'incfsz',0b00_1111_0000_0000, :f, :d
|
|
||||||
addop 'iorwf', 0b00_0100_0000_0000, :f, :d
|
|
||||||
addop 'movf', 0b00_1000_0000_0000, :f, :d
|
|
||||||
addop 'movwf', 0b00_0000_1000_0000, :f
|
|
||||||
addop 'nop', 0b00_0000_0000_0000 # 00_0000_0xx0_0000
|
|
||||||
addop 'rlf', 0b00_1101_0000_0000, :f, :d
|
|
||||||
addop 'rrf', 0b00_1100_0000_0000, :f, :d
|
|
||||||
addop 'subwf', 0b00_0010_0000_0000, :f, :d
|
|
||||||
addop 'swapf', 0b00_1110_0000_0000, :f, :d
|
|
||||||
addop 'xorwf', 0b00_0110_0000_0000, :f, :d
|
|
||||||
|
|
||||||
addop 'bcf', 0b01_0000_0000_0000, :f, :b
|
|
||||||
addop 'bsf', 0b01_0100_0000_0000, :f, :b
|
|
||||||
addop 'btfsc', 0b01_1000_0000_0000, :f, :b, :setip
|
|
||||||
addop 'btfss', 0b01_1100_0000_0000, :f, :b, :setip
|
|
||||||
|
|
||||||
addop 'addlw', 0b11_1110_0000_0000, :k # 00_000x_0000_0000
|
|
||||||
addop 'andlw', 0b11_1001_0000_0000, :k
|
|
||||||
addop 'call', 0b10_0000_0000_0000, :klong, :setip, :stopexec, :saveip
|
|
||||||
addop 'clrwdt',0b00_0000_0110_0100
|
|
||||||
addop 'goto', 0b10_1000_0000_0000, :klong, :setip, :stopexec
|
|
||||||
addop 'iorlw', 0b11_1000_0000_0000, :k
|
|
||||||
addop 'movlw', 0b11_0000_0000_0000, :k # 00_00xx_0000_0000
|
|
||||||
addop 'retfie',0b00_0000_0000_1001, :setip, :stopexec
|
|
||||||
addop 'retlw', 0b11_0100_0000_0000, :k, :setip, :stopexec # 00_00xx_0000_0000
|
|
||||||
addop 'return',0b00_0000_0000_1000, :setip, :stopexec
|
|
||||||
addop 'sleep', 0b00_0000_0110_0011
|
|
||||||
addop 'sublw', 0b11_1100_0000_0000, :k # 00_000x_0000_0000
|
|
||||||
addop 'xorlw', 0b11_1010_0000_0000, :k
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,11 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/ppc/parse'
|
|
||||||
require 'metasm/cpu/ppc/encode'
|
|
||||||
require 'metasm/cpu/ppc/decode'
|
|
||||||
require 'metasm/cpu/ppc/decompile'
|
|
@ -1,270 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ppc/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class PowerPC
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
|
|
||||||
return if not op.bin.kind_of? Integer
|
|
||||||
op.bin_mask = 0
|
|
||||||
op.fields.each { |k, (m, s)|
|
|
||||||
op.bin_mask |= m << s
|
|
||||||
}
|
|
||||||
op.bin_mask = 0xffff_ffff ^ op.bin_mask
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
lookaside = Array.new(256) { [] }
|
|
||||||
opcode_list.each { |op|
|
|
||||||
next if not op.bin.kind_of? Integer
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
|
|
||||||
b = op.bin >> 24
|
|
||||||
msk = op.bin_mask >> 24
|
|
||||||
|
|
||||||
for i in b..(b | (255^msk))
|
|
||||||
next if i & msk != b & msk
|
|
||||||
lookaside[i] << op
|
|
||||||
end
|
|
||||||
}
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
return if edata.ptr+4 > edata.length
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
val = edata.decode_imm(:u32, @endianness)
|
|
||||||
edata.ptr -= 4
|
|
||||||
di if di.opcode = @bin_lookaside[val >> 24].find { |op|
|
|
||||||
(op.bin & op.bin_mask) == (val & op.bin_mask)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
val = edata.decode_imm(:u32, @endianness)
|
|
||||||
|
|
||||||
field_val = lambda { |f|
|
|
||||||
r = (val >> @fields_shift[f]) & @fields_mask[f]
|
|
||||||
case f
|
|
||||||
when :bd, :d, :ds, :dq, :si, :ui; r = Expression.make_signed(r<<@fields_shift[f], 16)
|
|
||||||
when :li; r = Expression.make_signed(r<<@fields_shift[f], 26)
|
|
||||||
else r
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :ra, :rb, :rs, :rt; GPR.new field_val[a]
|
|
||||||
when :fra, :frb, :frc, :frs, :frt; FPR.new field_val[a]
|
|
||||||
when :ra_i16, :ra_i16s, :ra_i16q
|
|
||||||
i = field_val[{:ra_i16 => :d, :ra_i16s => :ds, :ra_i16q => :dq}[a]]
|
|
||||||
Memref.new GPR.new(field_val[:ra]), Expression[i]
|
|
||||||
when :bd, :d, :ds, :dq, :si, :ui, :li, :sh, :mb, :me, :mb_, :me_, :u; Expression[field_val[a]]
|
|
||||||
when :ba, :bf, :bfa, :bt; CR.new field_val[a]
|
|
||||||
when :bb, :bh, :flm, :fxm, :l_, :l__, :lev, :nb, :sh_, :spr, :sr, :tbr, :th, :to
|
|
||||||
puts "PPC.decode: unsupported argument #{a.inspect}" if $VERBOSE # TODO
|
|
||||||
Expression[field_val[a]]
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
return if edata.ptr > edata.length
|
|
||||||
|
|
||||||
decode_aliases(di.instruction)
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_aliases(i)
|
|
||||||
case i.opname
|
|
||||||
when /^n?or\.?$/
|
|
||||||
if i.args[1] == i.args[2]
|
|
||||||
i.args.pop
|
|
||||||
i.opname = {'or' => 'mr', 'or.' => 'mr.', 'nor' => 'not', 'nor.' => 'not.'}[i.opname]
|
|
||||||
end
|
|
||||||
when /^addi/
|
|
||||||
if a = i.args[2].reduce and a.kind_of? Integer and a < 0
|
|
||||||
i.args[2] = Expression[-a]
|
|
||||||
i.opname = i.opname.sub('addi', 'subi')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
case i.opname
|
|
||||||
when /^(add|sub|xor|and|or|div|mul|nand)/
|
|
||||||
if i.args.length == 3 and i.args[0] == i.args[1]
|
|
||||||
i.args.shift
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# converts relative branch offsets to absolute addresses
|
|
||||||
# else just add the offset +off+ of the instruction + its length (off may be an Expression)
|
|
||||||
# assumes edata.ptr points just after the instruction (as decode_instr_op left it)
|
|
||||||
# do not call twice on the same di !
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.opcode.name[0] != ?t and di.opcode.name[-1] != ?a
|
|
||||||
arg = Expression[addr, :+, di.instruction.args.last].reduce
|
|
||||||
di.instruction.args[-1] = Expression[arg]
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
|
|
||||||
retaddrlist.to_a.map! { |retaddr| dasm.decoded[retaddr] ? dasm.decoded[retaddr].block.list.last.address : retaddr }
|
|
||||||
b = f.backtrace_binding
|
|
||||||
|
|
||||||
bt_val = lambda { |r|
|
|
||||||
bt = []
|
|
||||||
retaddrlist.to_a.each { |retaddr|
|
|
||||||
bt |= dasm.backtrace(Expression[r], retaddr,
|
|
||||||
:include_start => true, :snapshot_addr => faddr, :origin => retaddr)
|
|
||||||
}
|
|
||||||
b[r] = ((bt.length == 1) ? bt.first : Expression::Unknown)
|
|
||||||
}
|
|
||||||
wantregs = GPR::Sym if wantregs.empty?
|
|
||||||
wantregs.map { |r| r.to_sym }.each(&bt_val)
|
|
||||||
|
|
||||||
#puts "update_func_bind: #{Expression[faddr]} has sp -> #{b[:$sp]}" if not Expression[b[:$sp], :-, :$sp].reduce.kind_of?(::Integer) if $VERBOSE
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
expr.reduce_rec == :lr
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_stack_address(expr)
|
|
||||||
Expression[expr].expr_externals.include? :sp
|
|
||||||
end
|
|
||||||
|
|
||||||
def replace_instr_arg_immediate(i, old, new)
|
|
||||||
i.args.map! { |a|
|
|
||||||
case a
|
|
||||||
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
|
||||||
when Memref
|
|
||||||
a.offset = (a.offset == old ? new : Expression[a.offset.bind(old => new).reduce]) if a.offset.kind_of? Expression
|
|
||||||
a
|
|
||||||
else a
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def disassembler_default_func
|
|
||||||
df = DecodedFunction.new
|
|
||||||
df.backtrace_binding = (0..31).inject({}) { |h, r| r != 1 ? h.update("r#{r}".to_sym => Expression::Unknown) : h }
|
|
||||||
df.backtracked_for = [BacktraceTrace.new(Expression[:lr], :default, Expression[:lr], :x)]
|
|
||||||
df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
|
|
||||||
if funcaddr != :default
|
|
||||||
btfor
|
|
||||||
elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip]
|
|
||||||
btfor
|
|
||||||
else []
|
|
||||||
end
|
|
||||||
}
|
|
||||||
df
|
|
||||||
end
|
|
||||||
|
|
||||||
# hash opname => lambda { |di, *sym_args| binding }
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
def backtrace_binding=(b) @backtrace_binding = b end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
opcode_list.map { |ol| ol.name }.uniq.each { |op|
|
|
||||||
binding = case op
|
|
||||||
when 'mr', 'li', 'la'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
|
|
||||||
when 'lis'; lambda { |di, a0, a1| { a0 => Expression[a1, :<<, 16] } }
|
|
||||||
when 'mtctr'; lambda { |di, a0| { :ctr => Expression[a0] } }
|
|
||||||
when 'mfctr'; lambda { |di, a0| { a0 => Expression[:ctr] } }
|
|
||||||
when 'mtlr'; lambda { |di, a0| { :lr => Expression[a0] } }
|
|
||||||
when 'mflr'; lambda { |di, a0| { a0 => Expression[:lr] } }
|
|
||||||
when 'lwzu'; lambda { |di, a0, m|
|
|
||||||
ret = { a0 => Expression[m] }
|
|
||||||
ptr = m.pointer.externals.grep(Symbol).first
|
|
||||||
ret[ptr] = m.pointer if ptr != a0
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
when 'lwz'; lambda { |di, a0, m| { a0 => Expression[m] } }
|
|
||||||
when 'stwu'; lambda { |di, a0, m|
|
|
||||||
{ m => Expression[a0], m.pointer.externals.grep(Symbol).first => m.pointer }
|
|
||||||
}
|
|
||||||
when 'stw'; lambda { |di, a0, m| { m => Expression[a0] } }
|
|
||||||
when 'rlwinm'; lambda { |di, a0, a1, sh, mb, me|
|
|
||||||
mb, me = mb.reduce, me.reduce
|
|
||||||
cpmsk = (1<<@size) - 1
|
|
||||||
a1 = Expression[a1, :&, cpmsk]
|
|
||||||
rol = Expression[[a1, :<<, sh], :|, [a1, :>>, [@size, :-, sh]]]
|
|
||||||
if mb == me+1
|
|
||||||
msk = cpmsk
|
|
||||||
elsif mb < me+1
|
|
||||||
msk = (((1 << ((me+1)-mb)) - 1) << (@size-(me+1)))
|
|
||||||
else
|
|
||||||
msk = (((1 << (mb-(me+1))) - 1) << (@size-mb)) ^ cpmsk
|
|
||||||
end
|
|
||||||
{ a0 => Expression[Expression[rol, :&, msk].reduce] }
|
|
||||||
}
|
|
||||||
|
|
||||||
when 'add', 'addi', 'add.', 'addi.'; lambda { |di, *a| { a[0] => Expression[a[-2], :+, a[-1]] } }
|
|
||||||
when 'addis', 'addis.'; lambda { |di, *a| { a[0] => Expression[a[-2], :+, [a[-1], :<<, 16]] } }
|
|
||||||
when 'sub', 'subi', 'sub.', 'subi.'; lambda { |di, *a| { a[0] => Expression[a[-2], :-, a[-1]] } }
|
|
||||||
when 'subis', 'subis.'; lambda { |di, *a| { a[0] => Expression[a[-2], :-, [a[-1], :<<, 16]] } }
|
|
||||||
when /^b.*la?$/; lambda { |di, *a| { :lr => Expression[di.next_addr] } }
|
|
||||||
when 'nop', /^cmp/, /^b/; lambda { |di, *a| {} }
|
|
||||||
end
|
|
||||||
|
|
||||||
@backtrace_binding[op] ||= binding if binding
|
|
||||||
}
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Memref; arg.symbolic(di.address)
|
|
||||||
when Reg; arg.symbolic
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
binding = if binding = backtrace_binding[di.instruction.opname]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unknown instruction to emu #{di}" if $VERBOSE
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
|
|
||||||
binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
arg = case di.instruction.opname
|
|
||||||
when 'bctr', 'bctrl'; :ctr
|
|
||||||
when 'blr', 'blrl'; :lr
|
|
||||||
else di.instruction.args.last
|
|
||||||
end
|
|
||||||
|
|
||||||
[Expression[
|
|
||||||
case arg
|
|
||||||
when Memref; Indirection[[arg.base.to_s.to_sym, :+, arg.offset], @size/8, di.address]
|
|
||||||
when Reg; arg.to_s.to_sym
|
|
||||||
else arg
|
|
||||||
end]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,251 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ppc/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class PowerPC
|
|
||||||
# temporarily setup dasm.address_binding so that backtracking
|
|
||||||
# stack-related offsets resolve in :frameptr (relative to func start)
|
|
||||||
def decompile_makestackvars(dasm, funcstart, blocks)
|
|
||||||
oldfuncbd = dasm.address_binding[funcstart]
|
|
||||||
dasm.address_binding[funcstart] = { :sp => :frameptr } # this would suffice, the rest here is just optimisation
|
|
||||||
|
|
||||||
blocks.each { |block|
|
|
||||||
yield block
|
|
||||||
}
|
|
||||||
|
|
||||||
dasm.address_binding[funcstart] = oldfuncbd if oldfuncbd
|
|
||||||
end
|
|
||||||
|
|
||||||
# list variable dependency for each block, remove useless writes
|
|
||||||
# returns { blockaddr => [list of vars that are needed by a following block] }
|
|
||||||
def decompile_func_finddeps(dcmp, blocks, func)
|
|
||||||
deps_r = {} ; deps_w = {} ; deps_to = {}
|
|
||||||
deps_subfunc = {} # things read/written by subfuncs
|
|
||||||
|
|
||||||
# find read/writes by each block
|
|
||||||
blocks.each { |b, to|
|
|
||||||
deps_r[b] = [] ; deps_w[b] = [] ; deps_to[b] = to
|
|
||||||
deps_subfunc[b] = []
|
|
||||||
|
|
||||||
blk = dcmp.dasm.decoded[b].block
|
|
||||||
blk.list.each { |di|
|
|
||||||
a = di.backtrace_binding.values
|
|
||||||
w = []
|
|
||||||
di.backtrace_binding.keys.each { |k|
|
|
||||||
case k
|
|
||||||
when ::Symbol; w |= [k]
|
|
||||||
else a |= Expression[k].externals # if dword [eax] <- 42, eax is read
|
|
||||||
end
|
|
||||||
}
|
|
||||||
#a << :eax if di.opcode.name == 'ret' # standard ABI
|
|
||||||
|
|
||||||
deps_r[b] |= a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - deps_w[b]
|
|
||||||
deps_w[b] |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
|
|
||||||
}
|
|
||||||
stackoff = nil
|
|
||||||
blk.each_to_normal { |t|
|
|
||||||
t = dcmp.backtrace_target(t, blk.list.last.address)
|
|
||||||
next if not t = dcmp.c_parser.toplevel.symbol[t]
|
|
||||||
t.type = C::Function.new(C::BaseType.new(:int)) if not t.type.kind_of? C::Function # XXX this may seem a bit extreme, and yes, it is.
|
|
||||||
stackoff ||= Expression[dcmp.dasm.backtrace(:sp, blk.list.last.address, :snapshot_addr => blocks.first[0]).first, :-, :sp].reduce
|
|
||||||
}
|
|
||||||
if stackoff # last block instr == subfunction call
|
|
||||||
deps_r[b] |= deps_subfunc[b] - deps_w[b]
|
|
||||||
#deps_w[b] |= [:eax, :ecx, :edx] # standard ABI
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# find regs read and never written (must have been set by caller and are part of the func ABI)
|
|
||||||
uninitialized = lambda { |b, r, done|
|
|
||||||
from = deps_to.keys.find_all { |f| deps_to[f].include? b } - done
|
|
||||||
from.empty? or from.find { |f|
|
|
||||||
!deps_w[f].include?(r) and uninitialized[f, r, done + [b]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# remove writes from a block if no following block read the value
|
|
||||||
dw = {}
|
|
||||||
deps_w.each { |b, deps|
|
|
||||||
dw[b] = deps.reject { |dep|
|
|
||||||
ret = true
|
|
||||||
done = []
|
|
||||||
todo = deps_to[b].dup
|
|
||||||
while a = todo.pop
|
|
||||||
next if done.include? a
|
|
||||||
done << a
|
|
||||||
if not deps_r[a] or deps_r[a].include? dep
|
|
||||||
ret = false
|
|
||||||
break
|
|
||||||
elsif not deps_w[a].include? dep
|
|
||||||
todo.concat deps_to[a]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dw
|
|
||||||
end
|
|
||||||
|
|
||||||
def decompile_blocks(dcmp, myblocks, deps, func, nextaddr = nil)
|
|
||||||
scope = func.initializer
|
|
||||||
func.type.args.each { |a| scope.symbol[a.name] = a }
|
|
||||||
stmts = scope.statements
|
|
||||||
func_entry = myblocks.first[0]
|
|
||||||
until myblocks.empty?
|
|
||||||
b, to = myblocks.shift
|
|
||||||
if l = dcmp.dasm.get_label_at(b)
|
|
||||||
stmts << C::Label.new(l)
|
|
||||||
end
|
|
||||||
|
|
||||||
# list of assignments [[dest reg, expr assigned]]
|
|
||||||
ops = []
|
|
||||||
# reg binding (reg => value, values.externals = regs at block start)
|
|
||||||
binding = {}
|
|
||||||
# Expr => CExpr
|
|
||||||
ce = lambda { |*e| dcmp.decompile_cexpr(Expression[Expression[*e].reduce], scope) }
|
|
||||||
# Expr => Expr.bind(binding) => CExpr
|
|
||||||
ceb = lambda { |*e| ce[Expression[*e].bind(binding)] }
|
|
||||||
|
|
||||||
# dumps a CExprs that implements an assignment to a reg (uses ops[], patches op => [reg, nil])
|
|
||||||
commit = lambda {
|
|
||||||
deps[b].map { |k|
|
|
||||||
[k, ops.rindex(ops.reverse.find { |r, v| r == k })]
|
|
||||||
}.sort_by { |k, i| i.to_i }.each { |k, i|
|
|
||||||
next if not i or not binding[k]
|
|
||||||
e = k
|
|
||||||
final = []
|
|
||||||
ops[0..i].reverse_each { |r, v|
|
|
||||||
final << r if not v
|
|
||||||
e = Expression[e].bind(r => v).reduce if not final.include? r
|
|
||||||
}
|
|
||||||
ops[i][1] = nil
|
|
||||||
binding.delete k
|
|
||||||
stmts << ce[k, :'=', e] if k != e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# go !
|
|
||||||
dcmp.dasm.decoded[b].block.list.each_with_index { |di, didx|
|
|
||||||
a = di.instruction.args
|
|
||||||
if di.opcode.props[:setip] and not di.opcode.props[:stopexec]
|
|
||||||
# conditional jump
|
|
||||||
commit[]
|
|
||||||
n = dcmp.backtrace_target(get_xrefs_x(dcmp.dasm, di).first, di.address)
|
|
||||||
#cc = ceb[decode_cc_to_expr(di.opcode.name[1..-1])]
|
|
||||||
cc = ceb[:condjmp]
|
|
||||||
stmts << C::If.new(C::CExpression[cc], C::Goto.new(n))
|
|
||||||
to.delete dcmp.dasm.normalize(n)
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
case di.opcode.name
|
|
||||||
when 'blr'
|
|
||||||
commit[]
|
|
||||||
stmts << C::Return.new(nil)
|
|
||||||
when 'bl' # :saveip
|
|
||||||
n = dcmp.backtrace_target(get_xrefs_x(dcmp.dasm, di).first, di.address)
|
|
||||||
args = []
|
|
||||||
if t = dcmp.c_parser.toplevel.symbol[n] and t.type.args
|
|
||||||
stackoff = Expression[dcmp.dasm.backtrace(:sp, di.address, :snapshot_addr => func_entry), :-, :sp].bind(:sp => :frameptr).reduce rescue nil
|
|
||||||
args_todo = t.type.args.dup
|
|
||||||
args = []
|
|
||||||
args_todo.each {
|
|
||||||
if stackoff.kind_of? Integer
|
|
||||||
var = Indirection[[:frameptr, :+, stackoff], @size/8]
|
|
||||||
stackoff += @size/8
|
|
||||||
else
|
|
||||||
var = 0
|
|
||||||
end
|
|
||||||
args << ceb[var]
|
|
||||||
binding.delete var
|
|
||||||
}
|
|
||||||
end
|
|
||||||
commit[]
|
|
||||||
#next if not di.block.to_subfuncret
|
|
||||||
|
|
||||||
if n.kind_of? ::String
|
|
||||||
if not f = dcmp.c_parser.toplevel.symbol[n]
|
|
||||||
# internal functions are predeclared, so this one is extern
|
|
||||||
f = dcmp.c_parser.toplevel.symbol[n] = C::Variable.new
|
|
||||||
f.name = n
|
|
||||||
f.type = C::Function.new(C::BaseType.new(:int))
|
|
||||||
dcmp.c_parser.toplevel.statements << C::Declaration.new(f)
|
|
||||||
end
|
|
||||||
commit[]
|
|
||||||
else
|
|
||||||
# indirect funcall
|
|
||||||
fptr = ceb[n]
|
|
||||||
binding.delete n
|
|
||||||
commit[]
|
|
||||||
proto = C::Function.new(C::BaseType.new(:int))
|
|
||||||
f = C::CExpression[[fptr], proto]
|
|
||||||
end
|
|
||||||
binding.delete :eax
|
|
||||||
e = C::CExpression[f, :funcall, args]
|
|
||||||
e = C::CExpression[ce[:eax], :'=', e, f.type.type] if deps[b].include? :eax and f.type.type != C::BaseType.new(:void)
|
|
||||||
stmts << e
|
|
||||||
when 'b'
|
|
||||||
a = di.instruction.args.first
|
|
||||||
if a.kind_of? Expression
|
|
||||||
else
|
|
||||||
# indirect jmp, convert to return (*fptr)();
|
|
||||||
n = di.instruction.args.first.symbolic
|
|
||||||
fptr = ceb[n]
|
|
||||||
binding.delete n
|
|
||||||
commit[]
|
|
||||||
proto = C::Function.new(C::BaseType.new(:void))
|
|
||||||
ret = C::Return.new(C::CExpression[[[fptr], C::Pointer.new(proto)], :funcall, []])
|
|
||||||
class << ret ; attr_accessor :from_instr end
|
|
||||||
ret.from_instr = di
|
|
||||||
stmts << ret
|
|
||||||
to = []
|
|
||||||
end
|
|
||||||
else
|
|
||||||
bd = get_fwdemu_binding(di)
|
|
||||||
if di.backtrace_binding[:incomplete_binding]
|
|
||||||
commit[]
|
|
||||||
stmts << C::Asm.new(di.instruction.to_s, nil, nil, nil, nil, nil)
|
|
||||||
else
|
|
||||||
bd.each { |k, v|
|
|
||||||
if k.kind_of? ::Symbol
|
|
||||||
ops << [k, v]
|
|
||||||
else # memory
|
|
||||||
stmts << ceb[k, :'=', v]
|
|
||||||
binding.delete k
|
|
||||||
end
|
|
||||||
}
|
|
||||||
update = {}
|
|
||||||
bd.each { |k, v|
|
|
||||||
next if not k.kind_of? ::Symbol
|
|
||||||
update[k] = Expression[Expression[v].bind(binding).reduce]
|
|
||||||
}
|
|
||||||
binding.update update
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
commit[]
|
|
||||||
|
|
||||||
case to.length
|
|
||||||
when 0
|
|
||||||
if not myblocks.empty? and not %w[ret jmp].include? dcmp.dasm.decoded[b].block.list.last.instruction.opname
|
|
||||||
puts " block #{Expression[b]} has no to and don't end in ret"
|
|
||||||
end
|
|
||||||
when 1
|
|
||||||
if (myblocks.empty? ? nextaddr != to[0] : myblocks.first.first != to[0])
|
|
||||||
stmts << C::Goto.new(dcmp.dasm.auto_label_at(to[0], 'unknown_goto'))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
puts " block #{Expression[b]} with multiple to"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,51 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ppc/opcodes'
|
|
||||||
require 'metasm/encode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class PowerPC
|
|
||||||
private
|
|
||||||
def encode_instr_op(exe, instr, op)
|
|
||||||
base = op.bin
|
|
||||||
set_field = lambda { |f, v|
|
|
||||||
base |= (v & @fields_mask[f]) << @fields_shift[f]
|
|
||||||
}
|
|
||||||
|
|
||||||
val, mask, shift = 0, 0, 0
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
# convert label name for jmp/call/loop to relative offset
|
|
||||||
if op.props[:setip] and op.name[0] != ?t and instr.args.last.kind_of? Expression
|
|
||||||
postlabel = exe.new_label('jmp_offset')
|
|
||||||
instr = instr.dup
|
|
||||||
instr.args[-1] = Expression[[instr.args[-1], :-, postlabel], :>>, 2]
|
|
||||||
postdata = EncodedData.new '', :export => {postlabel => 0}
|
|
||||||
else
|
|
||||||
postdata = ''
|
|
||||||
end
|
|
||||||
|
|
||||||
op.args.zip(instr.args).each { |sym, arg|
|
|
||||||
case sym
|
|
||||||
when :rs, :rt, :rd, :ba, :bf, :bfa, :bt
|
|
||||||
set_field[sym, arg.i]
|
|
||||||
when :ft
|
|
||||||
set_field[sym, arg.i]
|
|
||||||
when :rs_i16
|
|
||||||
set_field[:rs, arg.base.i]
|
|
||||||
val, mask, shift = arg.offset, @fields_mask[:i16], @fields_shift[:i16]
|
|
||||||
when :sa, :i16, :i20
|
|
||||||
val, mask, shift = arg, @fields_mask[sym], @fields_shift[sym]
|
|
||||||
when :i26
|
|
||||||
val, mask, shift = Expression[arg, :>>, 2], @fields_mask[sym], @fields_shift[sym]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression[base, :+, [[val, :&, mask], :<<, shift]].encode(:u32, @endianness) << postdata
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,134 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class PowerPC < CPU
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
class << self
|
|
||||||
attr_accessor :s_to_i, :i_to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
o.class == self.class and (not respond_to?(:i) or o.i == i)
|
|
||||||
end
|
|
||||||
|
|
||||||
def render ; [self.class.i_to_s[@i]] ; end
|
|
||||||
end
|
|
||||||
|
|
||||||
# general purpose reg
|
|
||||||
class GPR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
@s_to_i = (0..31).inject({}) { |h, i| h.update((i == 1 ? 'sp' : "r#{i}") => i) }
|
|
||||||
@i_to_s = @s_to_i.invert
|
|
||||||
Sym = @s_to_i.sort.transpose.last
|
|
||||||
def symbolic ; Sym[@i] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# special purpose reg
|
|
||||||
class SPR < Reg
|
|
||||||
@s_to_i = {'xer' => 1, 'lr' => 8, 'ctr' => 9, 'dec' => 22, 'srr0' => 26, 'srr1' => 27,
|
|
||||||
'sprg0' => 272, 'sprg1' => 273, 'sprg2' => 274, 'sprg3' => 275, 'pvr' => 287}
|
|
||||||
@i_to_s = @s_to_i.invert
|
|
||||||
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
Sym = @i_to_s.sort.inject({}) { |h, (k, v)| h.update k => v.to_sym }
|
|
||||||
def symbolic ; Sym[@i] end
|
|
||||||
def render ; [self.class.i_to_s[@i] || "spr#@i"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# floating point
|
|
||||||
class FPR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
@s_to_i = (0..31).inject({}) { |h, i| h.update "fp#{i}" => i }
|
|
||||||
@i_to_s = @s_to_i.invert
|
|
||||||
Sym = @s_to_i.sort.transpose.last
|
|
||||||
end
|
|
||||||
|
|
||||||
# machine state reg
|
|
||||||
class MSR < Reg
|
|
||||||
def symbolic ; :msr end
|
|
||||||
def render ; ['msr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# condition reg (7 regs * 4 bits : lt, gt, eq, of)
|
|
||||||
class CR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
def initialize(i)
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
@s_to_i = (0..31).inject({}) { |h, i| h.update "cr#{i}" => i }
|
|
||||||
@i_to_s = @s_to_i.invert
|
|
||||||
Sym = @s_to_i.sort.transpose.last
|
|
||||||
def symbolic ; "cr#@i".to_sym end
|
|
||||||
end
|
|
||||||
|
|
||||||
# indirection : reg+reg or reg+16b_off
|
|
||||||
# r0 may mean 0 in some cases (stwx)
|
|
||||||
class Memref
|
|
||||||
attr_accessor :base, :offset
|
|
||||||
def initialize(base, offset)
|
|
||||||
@base, @offset = base, offset
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig)
|
|
||||||
b = @base.symbolic
|
|
||||||
b = nil if b == :r0 # XXX is it true ?
|
|
||||||
o = @offset
|
|
||||||
o = o.symbolic if o.kind_of?(Reg)
|
|
||||||
Indirection[Expression[b, :+, o].reduce, 4, orig]
|
|
||||||
end
|
|
||||||
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
if @offset.kind_of?(Reg)
|
|
||||||
['(', @base, ' + ', @offset, ')']
|
|
||||||
else
|
|
||||||
[@offset, '(', @base, ')']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
super()
|
|
||||||
@endianness = :big
|
|
||||||
@size = 32
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
init
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_instruction(i)
|
|
||||||
r = [i.opname]
|
|
||||||
if not i.args.empty?
|
|
||||||
r << ' '
|
|
||||||
i.args.each { |a|
|
|
||||||
r << a << ', '
|
|
||||||
}
|
|
||||||
r.pop
|
|
||||||
end
|
|
||||||
r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
PPC = PowerPC
|
|
||||||
end
|
|
@ -1,416 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ppc/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class PowerPC
|
|
||||||
def addop(name, bin, *argprops)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
argprops.each { |a|
|
|
||||||
o.args << a if @valid_args[a]
|
|
||||||
o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
}
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
# generate l/a variations, add :setip/:saveip, include lr/ctr in opname
|
|
||||||
def addop_branch(nbase, bin, *argprops)
|
|
||||||
nbase += 'ctr' if argprops.delete :ctr
|
|
||||||
nbase += 'lr' if argprops.delete :lr
|
|
||||||
addop(nbase, bin, :setip, *argprops)
|
|
||||||
addop(nbase+'l', bin|1, :setip, :saveip, *argprops)
|
|
||||||
return if nbase[-2, 2] == 'lr' or nbase[-3, 3] == 'ctr'
|
|
||||||
|
|
||||||
addop(nbase+'a', bin|2, :setip, *argprops)
|
|
||||||
addop(nbase+'la', bin|3, :setip, :saveip, *argprops)
|
|
||||||
end
|
|
||||||
|
|
||||||
# generate condition variations, passes to addop_branch
|
|
||||||
def addop_branchcond(nbase, bin, *argprops)
|
|
||||||
# :bi & 0b11100 is the condition register to use, shift&mask == :bfa. Defaults to cr0
|
|
||||||
# bo values
|
|
||||||
# no cc (10000 != 0)
|
|
||||||
addop_branch(nbase, bin|(0b10100<<21), :ign_bo_zzz, :stopexec, *argprops)
|
|
||||||
addop_branch(nbase+'dz', bin|(0b10010<<21), :ign_bo_at2, :stopexec, *argprops) if not argprops.include? :ctr
|
|
||||||
addop_branch(nbase+'dnz', bin|(0b10000<<21), :ign_bo_at2, :stopexec, *argprops) if not argprops.include? :ctr
|
|
||||||
|
|
||||||
# conditionnal
|
|
||||||
%w[lt gt eq so].each_with_index { |cd, i|
|
|
||||||
ncd = {'lt' => 'gte', 'gt' => 'lte', 'eq' => 'ne', 'so' => 'nso'}[cd]
|
|
||||||
addop_branch(nbase+cd, bin|(0b1100<<21)|(i<<16), :ign_bo_at, *argprops)
|
|
||||||
addop_branch(nbase+cd, bin|(0b1100<<21)|(i<<16), :ign_bo_at, :bfa, *argprops)
|
|
||||||
addop_branch(nbase+ncd, bin|(0b100<<21)|(i<<16), :ign_bo_at, *argprops)
|
|
||||||
addop_branch(nbase+ncd, bin|(0b100<<21)|(i<<16), :ign_bo_at, :bfa, *argprops)
|
|
||||||
next if argprops.include? :ctr
|
|
||||||
|
|
||||||
addop_branch(nbase+'dz'+cd, bin|(0b1010<<21)|(i<<16), :ign_bo_z, *argprops)
|
|
||||||
addop_branch(nbase+'dz'+cd, bin|(0b1010<<21)|(i<<16), :ign_bo_z, :bfa, *argprops)
|
|
||||||
addop_branch(nbase+'dnz'+cd, bin|(0b1000<<21)|(i<<16), :ign_bo_z, *argprops)
|
|
||||||
addop_branch(nbase+'dnz'+cd, bin|(0b1000<<21)|(i<<16), :ign_bo_z, :bfa, *argprops)
|
|
||||||
addop_branch(nbase+'dz'+ncd, bin|(0b010<<21)|(i<<16), :ign_bo_z, *argprops)
|
|
||||||
addop_branch(nbase+'dz'+ncd, bin|(0b010<<21)|(i<<16), :ign_bo_z, :bfa, *argprops)
|
|
||||||
addop_branch(nbase+'dnz'+ncd, bin|(0b000<<21)|(i<<16), :ign_bo_z, *argprops)
|
|
||||||
addop_branch(nbase+'dnz'+ncd, bin|(0b000<<21)|(i<<16), :ign_bo_z, :bfa, *argprops)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_trap(nbase, bin, *argprops)
|
|
||||||
addop nbase+'trap', bin|(0b11111<<21), *argprops
|
|
||||||
addop nbase+'lt', bin|(0b10000<<21), *argprops
|
|
||||||
addop nbase+'le', bin|(0b10100<<21), *argprops
|
|
||||||
addop nbase+'eq', bin|(0b00100<<21), *argprops
|
|
||||||
addop nbase+'ge', bin|(0b01100<<21), *argprops
|
|
||||||
addop nbase+'gt', bin|(0b01000<<21), *argprops
|
|
||||||
addop nbase+'ne', bin|(0b11000<<21), *argprops
|
|
||||||
addop nbase+'llt', bin|(0b00010<<21), *argprops
|
|
||||||
addop nbase+'lle', bin|(0b00110<<21), *argprops
|
|
||||||
addop nbase+'lge', bin|(0b00101<<21), *argprops
|
|
||||||
addop nbase+'lgt', bin|(0b00001<<21), *argprops
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# generate cmp variations (default cr0, w/d)
|
|
||||||
def addop_cmp(nbase, bin, *argprops)
|
|
||||||
addop nbase.sub(/(cmpl?)/, '\\1w'), bin, *(argprops-[:bf])
|
|
||||||
addop nbase.sub(/(cmpl?)/, '\\1w'), bin, *argprops
|
|
||||||
addop nbase.sub(/(cmpl?)/, '\\1d'), bin|(1<<@fields_shift[:l]), *(argprops-[:bf])
|
|
||||||
addop nbase.sub(/(cmpl?)/, '\\1d'), bin|(1<<@fields_shift[:l]), *argprops
|
|
||||||
end
|
|
||||||
|
|
||||||
# adds op and 'op.' with last bit of bin set
|
|
||||||
def addop_(base, bin, *argprops)
|
|
||||||
addop(base, bin, *argprops)
|
|
||||||
addop(base+'.', bin|1, *argprops)
|
|
||||||
end
|
|
||||||
|
|
||||||
# adds op and 'opo'
|
|
||||||
def addop_o(base, bin, *argprops)
|
|
||||||
addop(base, bin, *argprops)
|
|
||||||
addop(base+'o', bin|0x400, *argprops)
|
|
||||||
end
|
|
||||||
|
|
||||||
def init
|
|
||||||
@opcode_list = []
|
|
||||||
@fields_shift.update :aa => 1, :ba => 16, :bb => 11, :bd => 2, :bf => 23,
|
|
||||||
:bfa => 18, :bh => 11, :bt => 21, :d => 0, :dq => 4,
|
|
||||||
:ds => 2, :flm => 17, :fra => 16, :frb => 11, :frc => 6, :frs => 21,
|
|
||||||
:frt => 21, :fxm => 12, :l => 21, :l_ => 21, :l__ => 16, :lev => 5,
|
|
||||||
:li => 2, :lk => 0, :mb => 5, :mb_ => 6, :me => 5, :me_ => 1,
|
|
||||||
:nb => 11, :oe => 10, :ra => 16, :rb => 11, :rc => 0, :rs => 21,
|
|
||||||
:rt => 21, :sh => 11, :sh_ => 1, :si => 0, :spr => 11, :sr => 16,
|
|
||||||
:tbr => 11, :th => 21, :to => 21, :u => 12, :ui => 0,
|
|
||||||
:ign_bo_zzz => 16, :ign_bo_z => 21, :ign_bo_at => 21, :ign_bo_at2 => 16
|
|
||||||
|
|
||||||
@fields_mask.update :aa => 1, :ba => 31, :bb => 31, :bd => 0x3FFF, :bf => 7,
|
|
||||||
:bfa => 7, :bh => 3, :bt => 31, :d => 0xFFFF, :dq => 0xFFF,
|
|
||||||
:ds => 0x3FFF, :flm => 255, :fra => 31, :frb => 31, :frc => 31, :frs => 31,
|
|
||||||
:frt => 31, :fxm => 255, :l => 1, :l_ => 3, :l__ => 1, :lev => 127,
|
|
||||||
:li => 0xFFFFFF, :lk => 1, :mb => 63, :mb_ => 31, :me => 63, :me_ => 31,
|
|
||||||
:nb => 31, :oe => 1, :ra => 31, :rb => 31, :rc => 1, :rs => 31,
|
|
||||||
:rt => 31, :sh => 31, :sh_ => 1, :si => 0xFFFF, :spr => 0x3FF, :sr => 15,
|
|
||||||
:tbr => 0x3FF, :th => 15, :to => 31, :u => 15, :ui => 0xFFFF,
|
|
||||||
:ign_bo_zzz => 0b101111111, :ign_bo_z => 1, :ign_bo_at => 3, :ign_bo_at2 => 0b100111111
|
|
||||||
|
|
||||||
@valid_args = @fields_mask.dup
|
|
||||||
[:ign_bo_zzz, :ign_bo_z, :ign_bo_at, :ign_bo_at2, :aa, :lk, :oe, :rc, :l].each { |k| @valid_args.delete k }
|
|
||||||
|
|
||||||
@fields_shift[:ra_i16] = @fields_shift[:ra_i16s] = @fields_shift[:ra_i16q] = 0
|
|
||||||
@fields_mask[:ra_i16] = (@fields_mask[:d] << @fields_shift[:d]) | (@fields_mask[:ra] << @fields_shift[:ra])
|
|
||||||
@fields_mask[:ra_i16s] = (@fields_mask[:ds] << @fields_shift[:d]) | (@fields_mask[:ra] << @fields_shift[:ra])
|
|
||||||
@fields_mask[:ra_i16q] = (@fields_mask[:dq] << @fields_shift[:d]) | (@fields_mask[:ra] << @fields_shift[:ra])
|
|
||||||
|
|
||||||
|
|
||||||
addop_branch 'b', 0x48000000, :li, :stopexec
|
|
||||||
addop_branchcond 'b', 0x40000000, :bd
|
|
||||||
addop_branchcond 'b', 0x4C000020, :lr
|
|
||||||
addop_branchcond 'b', 0x4C000420, :ctr
|
|
||||||
|
|
||||||
addop 'sc', 0x44000002, :lev
|
|
||||||
addop 'crand', 0x4C000202, :bt, :ba, :bb
|
|
||||||
addop 'crxor', 0x4C000182, :bt, :ba, :bb
|
|
||||||
# alias crclr bx -> crxor bx, bx, bx
|
|
||||||
addop 'cror', 0x4C000382, :bt, :ba, :bb
|
|
||||||
# alias crmove bx, by -> cror bx, by, by
|
|
||||||
addop 'crnand', 0x4C0001C2, :bt, :ba, :bb
|
|
||||||
addop 'crnor', 0x4C000042, :bt, :ba, :bb
|
|
||||||
# alias crnot bx, by -> crnor bx, by, by
|
|
||||||
addop 'crandc', 0x4C000102, :bt, :ba, :bb
|
|
||||||
addop 'creqv', 0x4C000242, :bt, :ba, :bb
|
|
||||||
# alias crset bx -> creqv bx, bx, bx
|
|
||||||
addop 'crorc', 0x4C000342, :bt, :ba, :bb
|
|
||||||
addop 'mcrf', 0x4C000000, :bf, :bfa
|
|
||||||
addop 'lbz', 0x88000000, :rt, :ra_i16
|
|
||||||
addop 'lbzu', 0x8C000000, :rt, :ra_i16
|
|
||||||
addop 'lbzx', 0x7C0000AE, :rt, :ra, :rb
|
|
||||||
addop 'lbzux', 0x7C0000EE, :rt, :ra, :rb
|
|
||||||
addop 'lhz', 0xA0000000, :rt, :ra_i16
|
|
||||||
addop 'lhzu', 0xA4000000, :rt, :ra_i16
|
|
||||||
addop 'lhzx', 0x7C00022E, :rt, :ra, :rb
|
|
||||||
addop 'lhzux', 0x7C00026E, :rt, :ra, :rb
|
|
||||||
addop 'lha', 0xA8000000, :rt, :ra_i16
|
|
||||||
addop 'lhau', 0xAC000000, :rt, :ra_i16
|
|
||||||
addop 'lhax', 0x7C0002AE, :rt, :ra, :rb
|
|
||||||
addop 'lhaux', 0x7C0002EE, :rt, :ra, :rb
|
|
||||||
addop 'lwz', 0x80000000, :rt, :ra_i16
|
|
||||||
addop 'lwzu', 0x84000000, :rt, :ra_i16
|
|
||||||
addop 'lwzx', 0x7C00002E, :rt, :ra, :rb
|
|
||||||
addop 'lwzux', 0x7C00006E, :rt, :ra, :rb
|
|
||||||
addop 'lwa', 0xE8000002, :rt, :ra_i16s
|
|
||||||
addop 'lwax', 0x7C0002AA, :rt, :ra, :rb
|
|
||||||
addop 'lwaux', 0x7C0002EA, :rt, :ra, :rb
|
|
||||||
addop 'ld', 0xE8000000, :rt, :ra_i16s
|
|
||||||
addop 'ldu', 0xE8000001, :rt, :ra_i16s
|
|
||||||
addop 'ldx', 0x7C00002A, :rt, :ra, :rb
|
|
||||||
addop 'ldux', 0x7C00006A, :rt, :ra, :rb
|
|
||||||
addop 'stb', 0x98000000, :rs, :ra_i16
|
|
||||||
addop 'stbu', 0x9C000000, :rs, :ra_i16
|
|
||||||
addop 'stbx', 0x7C0001AE, :rs, :ra, :rb
|
|
||||||
addop 'stbux', 0x7C0001EE, :rs, :ra, :rb
|
|
||||||
addop 'sth', 0xB0000000, :rs, :ra_i16
|
|
||||||
addop 'sthu', 0xB4000000, :rs, :ra_i16
|
|
||||||
addop 'sthx', 0x7C00032E, :rs, :ra, :rb
|
|
||||||
addop 'sthux', 0x7C00036E, :rs, :ra, :rb
|
|
||||||
addop 'stw', 0x90000000, :rs, :ra_i16
|
|
||||||
addop 'stwu', 0x94000000, :rs, :ra_i16
|
|
||||||
addop 'stwx', 0x7C00012E, :rs, :ra, :rb
|
|
||||||
addop 'stwux', 0x7C00016E, :rs, :ra, :rb
|
|
||||||
addop 'std', 0xF8000000, :rs, :ra_i16s
|
|
||||||
addop 'stdu', 0xF8000001, :rs, :ra_i16s
|
|
||||||
addop 'stdx', 0x7C00012A, :rs, :ra, :rb
|
|
||||||
addop 'stdux', 0x7C00016A, :rs, :ra, :rb
|
|
||||||
addop 'lhbrx', 0x7C00062C, :rt, :ra, :rb
|
|
||||||
addop 'lwbrx', 0x7C00042C, :rt, :ra, :rb
|
|
||||||
addop 'sthbrx', 0x7C00072C, :rs, :ra, :rb
|
|
||||||
addop 'stwbrx', 0x7C00052C, :rs, :ra, :rb
|
|
||||||
addop 'lmw', 0xB8000000, :rt, :ra_i16
|
|
||||||
addop 'stmw', 0xBC000000, :rs, :ra_i16
|
|
||||||
addop 'lswi', 0x7C0004AA, :rt, :ra, :nb
|
|
||||||
addop 'lswx', 0x7C00042A, :rt, :ra, :rb
|
|
||||||
addop 'stswi', 0x7C0005AA, :rs, :ra, :nb
|
|
||||||
addop 'stswx', 0x7C00052A, :rs, :ra, :rb
|
|
||||||
addop 'li', 0x38000000, :rt, :si # alias li rx, value -> addi rx, 0, value
|
|
||||||
addop 'addi', 0x38000000, :rt, :ra, :si
|
|
||||||
addop 'la', 0x38000000, :rt, :ra_i16 # alias la rx, disp(ry) -> addi rx, ry, disp
|
|
||||||
addop 'lis', 0x3C000000, :rt, :si # alias lis rx, value -> addis rx, 0, value
|
|
||||||
addop 'addis', 0x3C000000, :rt, :ra, :si
|
|
||||||
addop_o 'add', 0x7C000214, :rt, :ra, :rb
|
|
||||||
addop 'addic', 0x30000000, :rt, :ra, :si
|
|
||||||
addop_o 'sub', 0x7C000050, :rt, :rb, :ra # alias sub rx, ry, rz -> subf rx, rz, ry
|
|
||||||
addop_o 'subf', 0x7C000050, :rt, :ra, :rb
|
|
||||||
addop 'addic.', 0x34000000, :rt, :ra, :si
|
|
||||||
addop 'subfic', 0x20000000, :rt, :ra, :si
|
|
||||||
addop_o 'addc', 0x7C000014, :rt, :ra, :rb
|
|
||||||
addop_o 'subc', 0x7C000010, :rt, :rb, :ra # alias subc rx, ry, rz -> subfc rx, rz, ry
|
|
||||||
addop_o 'subfc',0x7C000010, :rt, :ra, :rb
|
|
||||||
addop_o 'adde', 0x7C000114, :rt, :ra, :rb
|
|
||||||
addop_o 'addme',0x7C0001D4, :rt, :ra
|
|
||||||
addop_o 'subfe',0x7C000110, :rt, :ra, :rb
|
|
||||||
addop_o 'subfme',0x7C0001D0,:rt, :ra
|
|
||||||
addop_o 'addze',0x7C000194, :rt, :ra
|
|
||||||
addop_o 'subfze',0x7C000190,:rt, :ra
|
|
||||||
addop_o 'neg', 0x7C0000D0, :rt, :ra
|
|
||||||
addop 'mulli', 0x1C000000, :rt, :ra, :si
|
|
||||||
addop_o 'mulld',0x7C0001D2, :rt, :ra, :rb
|
|
||||||
addop_o 'mullw',0x7C0001D6, :rt, :ra, :rb
|
|
||||||
addop_ 'mulhd', 0x7C000092, :rt, :ra, :rb
|
|
||||||
addop_ 'mulhdu',0x7C000012, :rt, :ra, :rb
|
|
||||||
addop_ 'mulhw', 0x7C000096, :rt, :ra, :rb
|
|
||||||
addop_ 'mulhwu',0x7C000016, :rt, :ra, :rb
|
|
||||||
addop_o 'divd', 0x7C0003D2, :rt, :ra, :rb
|
|
||||||
addop_o 'divw', 0x7C0003D6, :rt, :ra, :rb
|
|
||||||
addop_o 'divdu',0x7C000392, :rt, :ra, :rb
|
|
||||||
addop_o 'divwu',0x7C000396, :rt, :ra, :rb
|
|
||||||
addop_cmp 'cmpi', 0x2C000000, :bf, :ra, :si
|
|
||||||
addop_cmp 'cmp', 0x7C000000, :bf, :ra, :rb
|
|
||||||
addop_cmp 'cmpli', 0x28000000, :bf, :ra, :ui
|
|
||||||
addop_cmp 'cmpl', 0x7C000040, :bf, :ra, :rb
|
|
||||||
addop 'andi.', 0x70000000, :ra, :rs, :ui
|
|
||||||
addop 'andis.', 0x74000000, :ra, :rs, :ui
|
|
||||||
addop 'nop', 0x60000000
|
|
||||||
addop 'ori', 0x60000000, :ra, :rs, :ui
|
|
||||||
addop 'oris', 0x64000000, :ra, :rs, :ui
|
|
||||||
addop 'xori', 0x68000000, :ra, :rs, :ui
|
|
||||||
addop 'xoris', 0x6C000000, :ra, :rs, :ui
|
|
||||||
addop_ 'and', 0x7C000038, :ra, :rs, :rb
|
|
||||||
addop_ 'xor', 0x7C000278, :ra, :rs, :rb
|
|
||||||
addop_ 'or', 0x7C000378, :ra, :rs, :rb
|
|
||||||
# alias mr rx, ry -> or rx, ry, ry
|
|
||||||
addop_ 'nand', 0x7C0003B8, :ra, :rs, :rb
|
|
||||||
addop_ 'nor', 0x7C0000F8, :ra, :rs, :rb
|
|
||||||
# alias not rx, ry -> nor rx, ry, ry
|
|
||||||
addop_ 'andc', 0x7C000078, :ra, :rs, :rb
|
|
||||||
addop_ 'eqv', 0x7C000238, :ra, :rs, :rb
|
|
||||||
addop_ 'orc', 0x7C000338, :ra, :rs, :rb
|
|
||||||
addop_ 'extsb', 0x7C000774, :ra, :rs
|
|
||||||
addop_ 'extsw', 0x7C0007B4, :ra, :rs
|
|
||||||
addop_ 'extsh', 0x7C000734, :ra, :rs
|
|
||||||
addop_ 'cntlzd',0x7C000074, :ra, :rs
|
|
||||||
addop_ 'cntlzw',0x7C000034, :ra, :rs
|
|
||||||
addop 'popcntb',0x7C0000F4, :ra, :rs
|
|
||||||
addop 'clrldi', 0x78000000, :ra, :rs, :mb # alias clrldi rx, ry, n -> rldicl rx, ry, 0, n
|
|
||||||
addop_ 'rldicl',0x78000000, :ra, :rs, :sh, :mb, :sh_
|
|
||||||
# alias extrdi rx, ry, n, b -> rldicl rx, ry, b+n, 64 - n
|
|
||||||
# alias srdi rx, ry, n -> rldicl rx, ry, 64 - n, n
|
|
||||||
addop_ 'rldicr',0x78000004, :ra, :rs, :sh, :me, :sh_
|
|
||||||
# alias extldi rx, ry, n, b -> rldicr rx, ry, b, n - 1
|
|
||||||
# alias sldi rx, ry, n -> rldicr rx, ry, n, 63 - n
|
|
||||||
# alias clrrdi rx, ry, n -> rldicr rx, ry, 0, 63 - n
|
|
||||||
addop_ 'rldic', 0x78000008, :ra, :rs, :sh, :mb, :sh_
|
|
||||||
# alias clrlsldi rx, ry, b, n -> rldic rx, ry, n, b - n
|
|
||||||
addop_ 'rlwinm',0x54000000, :ra, :rs, :sh, :mb_, :me_
|
|
||||||
# alias extlwi rx, ry, n, b -> rlwinm rx, ry, b, 0, n - 1
|
|
||||||
# alias srwi rx, ry, n -> rlwinm rx, ry, 32 - n, n, 31
|
|
||||||
# alias clrrwi rx, ry, n -> rlwinm rx, ry, 0, 0, 31 - n
|
|
||||||
addop 'rotld', 0x78000010, :ra, :rs, :rb # alias rotld rx, ry, rz -> rldcl rx, ry, rz, 0
|
|
||||||
addop_ 'rldcl', 0x78000010, :ra, :rs, :rb, :mb
|
|
||||||
addop_ 'rldcr', 0x78000012, :ra, :rs, :rb, :me
|
|
||||||
addop 'rotlw', 0x5C000000|(31<<@fields_shift[:me_]), :ra, :rs, :rb # alias rotlw rx, ry, rz -> rlwnm rx, ry, rz, 0, 31
|
|
||||||
addop_ 'rlwnm', 0x5C000000, :ra, :rs, :rb, :mb_, :me_
|
|
||||||
addop_ 'rldimi',0x7800000C, :ra, :rs, :sh, :mb, :sh_
|
|
||||||
# alias insrdi rx, ry, n, b -> rldimi rx, ry, 64 - (b+n), b
|
|
||||||
addop_ 'rlwimi',0x50000000, :ra, :rs, :sh, :mb_, :me_
|
|
||||||
# alias inslwi rx, ry, n, b -> rlwimi rx, ry, 32-b, b, b+n - 1
|
|
||||||
addop_ 'sld', 0x7C000036, :ra, :rs, :rb
|
|
||||||
addop_ 'slw', 0x7C000030, :ra, :rs, :rb
|
|
||||||
addop_ 'srd', 0x7C000436, :ra, :rs, :rb
|
|
||||||
addop_ 'srw', 0x7C000430, :ra, :rs, :rb
|
|
||||||
addop_ 'sradi', 0x7C000674, :ra, :rs, :sh, :sh_
|
|
||||||
addop_ 'srawi', 0x7C000670, :ra, :rs, :sh
|
|
||||||
addop_ 'srad', 0x7C000634, :ra, :rs, :rb
|
|
||||||
addop_ 'sraw', 0x7C000630, :ra, :rs, :rb
|
|
||||||
#addop 'mtspr', 0x7C0003A6, :spr, :rs
|
|
||||||
addop 'mtxer', 0x7C0003A6|(1<<16), :rs
|
|
||||||
addop 'mtlr', 0x7C0003A6|(8<<16), :rs
|
|
||||||
addop 'mtctr', 0x7C0003A6|(9<<16), :rs
|
|
||||||
#addop 'mfspr', 0x7C0002A6, :rt, :spr
|
|
||||||
addop 'mfxer', 0x7C0002A6|(1<<16), :rt
|
|
||||||
addop 'mflr', 0x7C0002A6|(8<<16), :rt
|
|
||||||
addop 'mfctr', 0x7C0002A6|(9<<16), :rt
|
|
||||||
addop 'mtcrf', 0x7C000120, :fxm, :rs
|
|
||||||
# alias mtcr rx -> mtcrf 0xff, rx
|
|
||||||
addop 'mfcr', 0x7C000026, :rt
|
|
||||||
addop 'lfs', 0xC0000000, :frt, :ra_i16
|
|
||||||
addop 'lfsu', 0xC4000000, :frt, :ra_i16
|
|
||||||
addop 'lfsx', 0x7C00042E, :frt, :ra, :rb
|
|
||||||
addop 'lfsux', 0x7C00046E, :frt, :ra, :rb
|
|
||||||
addop 'lfd', 0xC8000000, :frt, :ra_i16
|
|
||||||
addop 'lfdu', 0xCC000000, :frt, :ra_i16
|
|
||||||
addop 'lfdx', 0x7C0004AE, :frt, :ra, :rb
|
|
||||||
addop 'lfdux', 0x7C0004EE, :frt, :ra, :rb
|
|
||||||
addop 'stfs', 0xD0000000, :frs, :ra_i16
|
|
||||||
addop 'stfsu', 0xD4000000, :frs, :ra_i16
|
|
||||||
addop 'stfsx', 0x7C00052E, :frs, :ra, :rb
|
|
||||||
addop 'stfsux', 0x7C00056E, :frs, :ra, :rb
|
|
||||||
addop 'stfd', 0xD8000000, :frs, :ra_i16
|
|
||||||
addop 'stfdu', 0xDC000000, :frs, :ra_i16
|
|
||||||
addop 'stfdx', 0x7C0005AE, :frs, :ra, :rb
|
|
||||||
addop 'stfdux', 0x7C0005EE, :frs, :ra, :rb
|
|
||||||
addop 'stfiwx', 0x7C0007AE, :frs, :ra, :rb
|
|
||||||
addop_ 'fmr', 0xFC000090, :frt, :frb
|
|
||||||
addop_ 'fabs', 0xFC000210, :frt, :frb
|
|
||||||
addop_ 'fneg', 0xFC000050, :frt, :frb
|
|
||||||
addop_ 'fnabs', 0xFC000110, :frt, :frb
|
|
||||||
addop_ 'fadd', 0xFC00002A, :frt, :fra, :frb
|
|
||||||
addop_ 'fadds', 0xEC00002A, :frt, :fra, :frb
|
|
||||||
addop_ 'fsub', 0xFC000028, :frt, :fra, :frb
|
|
||||||
addop_ 'fsubs', 0xEC000028, :frt, :fra, :frb
|
|
||||||
addop_ 'fmul', 0xFC000032, :frt, :fra, :frc
|
|
||||||
addop_ 'fmuls', 0xEC000032, :frt, :fra, :frc
|
|
||||||
addop_ 'fdiv', 0xFC000024, :frt, :fra, :frb
|
|
||||||
addop_ 'fdivs', 0xEC000024, :frt, :fra, :frb
|
|
||||||
addop_ 'fmadd', 0xFC00003A, :frt, :fra, :frc, :frb
|
|
||||||
addop_ 'fmadds',0xEC00003A, :frt, :fra, :frc, :frb
|
|
||||||
addop_ 'fmsub', 0xFC000038, :frt, :fra, :frc, :frb
|
|
||||||
addop_ 'fmsubs',0xEC000038, :frt, :fra, :frc, :frb
|
|
||||||
addop_ 'fnmadd',0xFC00003E, :frt, :fra, :frc, :frb
|
|
||||||
addop_ 'fnmadds',0xEC00003E,:frt, :fra, :frc, :frb
|
|
||||||
addop_ 'fnmsub',0xFC00003C, :frt, :fra, :frc, :frb
|
|
||||||
addop_ 'fnmsubs',0xEC00003C,:frt, :fra, :frc, :frb
|
|
||||||
addop_ 'frsp', 0xFC000018, :frt, :frb
|
|
||||||
addop_ 'fctid', 0xFC00065C, :frt, :frb
|
|
||||||
addop_ 'fctidz',0xFC00065E, :frt, :frb
|
|
||||||
addop_ 'fctiw', 0xFC00001C, :frt, :frb
|
|
||||||
addop_ 'fctiwz',0xFC00001E, :frt, :frb
|
|
||||||
addop_ 'fcfid', 0xFC00069C, :frt, :frb
|
|
||||||
addop 'fcmpu', 0xFC000000, :bf, :fra, :frb
|
|
||||||
addop 'fcmpo', 0xFC000040, :bf, :fra, :frb
|
|
||||||
addop_ 'mffs', 0xFC00048E, :frt
|
|
||||||
addop 'mcrfs', 0xFC000080, :bf, :bfa
|
|
||||||
addop_ 'mtfsfi',0xFC00010C, :bf, :u
|
|
||||||
addop_ 'mtfsf', 0xFC00058E, :flm, :frb
|
|
||||||
addop_ 'mtfsb0',0xFC00008C, :bt
|
|
||||||
addop_ 'mtfsb1',0xFC00004C, :bt
|
|
||||||
addop 'mtocrf', 0x7C100120, :fxm, :rs
|
|
||||||
addop_ 'fsqrt', 0xFC00002C, :frt, :frb
|
|
||||||
addop_ 'fsqrts',0xEC00002C, :frt, :frb
|
|
||||||
addop_ 'fre', 0xFC000030, :frt, :frb
|
|
||||||
addop_ 'fres', 0xEC000030, :frt, :frb
|
|
||||||
addop_ 'frsqrte',0xFC000034,:frt, :frb
|
|
||||||
addop_ 'frsqrtes',0xEC000034, :frt, :frb
|
|
||||||
addop_ 'fsel', 0xFC00002E, :frt, :fra, :frc, :frb
|
|
||||||
addop 'mcrxr', 0x7C000400, :bf
|
|
||||||
addop 'icbi', 0x7C0007AC, :ra, :rb
|
|
||||||
addop 'dcbt', 0x7C00022C, :ra, :rb
|
|
||||||
addop 'dcbtst', 0x7C0001EC, :ra, :rb
|
|
||||||
addop 'dcbz', 0x7C0007EC, :ra, :rb
|
|
||||||
addop 'dcbst', 0x7C00006C, :ra, :rb
|
|
||||||
addop 'dcbf', 0x7C0000AC, :ra, :rb
|
|
||||||
addop 'isync', 0x4C00012C
|
|
||||||
addop 'lwarx', 0x7C000028, :rt, :ra, :rb
|
|
||||||
addop 'ldarx', 0x7C0000A8, :rt, :ra, :rb
|
|
||||||
addop 'stwcx.', 0x7C00012D, :rs, :ra, :rb
|
|
||||||
addop 'stdcx.', 0x7C0001AD, :rs, :ra, :rb
|
|
||||||
addop 'sync', 0x7C0004AC, :l_
|
|
||||||
addop 'eieio', 0x7C0006AC
|
|
||||||
addop 'mftb', 0x7C0002E6, :rt, :tbr
|
|
||||||
addop 'eciwx', 0x7C00026C, :rt, :ra, :rb
|
|
||||||
addop 'ecowx', 0x7C00036C, :rs, :ra, :rb
|
|
||||||
addop 'dcbt', 0x7C00022C, :ra, :rb, :th
|
|
||||||
addop 'dcbf', 0x7C0000AC, :ra, :rb
|
|
||||||
addop 'dcbf', 0x7C0000AC, :ra, :rb, :l
|
|
||||||
addop 'sc', 0x44000002, :lev
|
|
||||||
addop 'rfid', 0x4C000024
|
|
||||||
addop 'hrfid', 0x4C000224
|
|
||||||
addop 'mtmsrd', 0x7C000164, :rs, :l__
|
|
||||||
addop 'mfmsr', 0x7C0000A6, :rt
|
|
||||||
addop 'slbie', 0x7C000364, :rb
|
|
||||||
addop 'slbmte', 0x7C000324, :rs, :rb
|
|
||||||
addop 'slbmfev',0x7C0006A6, :rt, :rb
|
|
||||||
addop 'slbmfee',0x7C000726, :rt, :rb
|
|
||||||
addop 'tlbie', 0x7C000264, :rb, :l
|
|
||||||
addop 'tlbiel', 0x7C000224, :rb, :l
|
|
||||||
addop 'tlbia', 0x7C0002E4
|
|
||||||
addop 'tlbsync',0x7C00046C
|
|
||||||
addop 'mtmsr', 0x7C000124, :rs, :l__
|
|
||||||
addop 'lq', 0xE0000000, :rt, :ra_i16q
|
|
||||||
addop 'stq', 0xF8000002, :rs, :ra_i16s
|
|
||||||
addop 'mtsr', 0x7C0001A4, :sr, :rs
|
|
||||||
addop 'mtsrin', 0x7C0001E4, :rs, :rb
|
|
||||||
addop 'mfsr', 0x7C0004A6, :rt, :sr
|
|
||||||
addop 'mfsrin', 0x7C000526, :rt, :rb
|
|
||||||
|
|
||||||
addop_trap 'tw', 0x7C000008, :ra, :rb
|
|
||||||
addop_trap 'twi', 0xC0000000, :ra, :si
|
|
||||||
addop_trap 'td', 0x7C000088, :ra, :rb
|
|
||||||
addop_trap 'tdi', 0x08000000, :ra, :si
|
|
||||||
|
|
||||||
# pseudo-instructions
|
|
||||||
addop 'mr', :pseudo, :ra, :rb
|
|
||||||
addop 'not', :pseudo, :ra
|
|
||||||
addop 'not', :pseudo, :ra, :rb
|
|
||||||
@opcode_list.each { |op|
|
|
||||||
if op.name =~ /^addi/
|
|
||||||
addop op.name.sub('add', 'sub'), :pseudo, *op.args
|
|
||||||
end
|
|
||||||
if op.name =~ /^(add|sub|xor|and|or|div|mul|nand)/ and op.args.length == 3
|
|
||||||
addop op.name, :pseudo, *op.args[1..-1]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,55 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/ppc/opcodes'
|
|
||||||
require 'metasm/parse'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class PowerPC
|
|
||||||
# TODO
|
|
||||||
def parse_arg_valid?(op, sym, arg)
|
|
||||||
case sym
|
|
||||||
when :ra, :rb, :rs, :rt; arg.kind_of?(GPR)
|
|
||||||
when :fra, :frb, :frc, :frs, :frt; arg.kind_of?(FPR)
|
|
||||||
when :ra_i16, :ra_i16s, :ra_i16q; arg.kind_of?(Memref)
|
|
||||||
when :bd, :d, :ds, :dq, :si, :ui, :li, :sh, :mb, :me, :mb_, :me_, :u; arg.kind_of?(Expression)
|
|
||||||
when :ba, :bf, :bfa, :bt; arg.kind_of?(CR)
|
|
||||||
when :ign_bo_zzz, :ign_bo_z, :ign_bo_at, :ign_bo_at2, :aa, :lk, :oe, :rc, :l; # ?
|
|
||||||
when :bb, :bh, :flm, :fxm, :l_, :l__, :lev, :nb, :sh_, :spr, :sr, :tbr, :th, :to
|
|
||||||
# TODO
|
|
||||||
else raise "internal error: mips arg #{sym.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_argument(pgm)
|
|
||||||
pgm.skip_space
|
|
||||||
return if not tok = pgm.readtok
|
|
||||||
if tok.type == :string
|
|
||||||
return GPR.new(GPR.s_to_i[tok.raw]) if GPR.s_to_i[tok.raw]
|
|
||||||
return SPR.new(SPR.s_to_i[tok.raw]) if SPR.s_to_i[tok.raw]
|
|
||||||
return FPR.new(FPR.s_to_i[tok.raw]) if FPR.s_to_i[tok.raw]
|
|
||||||
return CR.new(CR.s_to_i[tok.raw]) if CR.s_to_i[tok.raw]
|
|
||||||
return MSR.new if tok.raw == 'msr'
|
|
||||||
end
|
|
||||||
pgm.unreadtok tok
|
|
||||||
arg = Expression.parse pgm
|
|
||||||
pgm.skip_space
|
|
||||||
# check memory indirection: 'off(base reg)' # XXX scaled index ?
|
|
||||||
if arg and pgm.nexttok and pgm.nexttok.type == :punct and pgm.nexttok.raw == '('
|
|
||||||
pgm.readtok
|
|
||||||
pgm.skip_space_eol
|
|
||||||
ntok = pgm.readtok
|
|
||||||
raise tok, "Invalid base #{ntok}" unless ntok and ntok.type == :string and GPR.s_to_i[ntok.raw]
|
|
||||||
base = GPR.new GPR.s_to_i[ntok.raw]
|
|
||||||
pgm.skip_space_eol
|
|
||||||
ntok = pgm.readtok
|
|
||||||
raise tok, "Invalid memory reference, ')' expected" if not ntok or ntok.type != :punct or ntok.raw != ')'
|
|
||||||
arg = Memref.new base, arg
|
|
||||||
end
|
|
||||||
arg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,8 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/python/decode'
|
|
@ -1,136 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/python/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Python
|
|
||||||
def build_bin_lookaside
|
|
||||||
opcode_list.inject({}) { |la, op| la.update op.bin => op }
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
|
|
||||||
byte = edata.decode_imm(:u8, :little)
|
|
||||||
|
|
||||||
di if di.opcode = @bin_lookaside[byte]
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
di.bin_length = 1
|
|
||||||
|
|
||||||
di.instruction.opname = di.opcode.name
|
|
||||||
|
|
||||||
di.opcode.args.each { |a|
|
|
||||||
case a
|
|
||||||
when :cmp
|
|
||||||
di.bin_length += 2
|
|
||||||
v = edata.decode_imm(:i16, @endianness)
|
|
||||||
di.instruction.args << (CMP_OP[v] || Expression[v])
|
|
||||||
when :i16
|
|
||||||
di.bin_length += 2
|
|
||||||
di.instruction.args << Expression[edata.decode_imm(:i16, @endianness)]
|
|
||||||
when :u8
|
|
||||||
di.bin_length += 1
|
|
||||||
di.instruction.args << Expression[edata.decode_imm(:u8, @endianness)]
|
|
||||||
else
|
|
||||||
raise "unsupported arg #{a.inspect}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
return if edata.ptr > edata.length
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
case di.opcode.name
|
|
||||||
when 'LOAD_CONST'
|
|
||||||
if c = prog_code(addr)
|
|
||||||
cst = c[:consts][di.instruction.args.first.reduce]
|
|
||||||
if cst.kind_of? Hash and cst[:type] == :code
|
|
||||||
di.add_comment "lambda #{Expression[cst[:fileoff]]}"
|
|
||||||
else
|
|
||||||
di.add_comment cst.inspect
|
|
||||||
end
|
|
||||||
end
|
|
||||||
when 'LOAD_NAME', 'LOAD_ATTR', 'LOAD_GLOBAL', 'STORE_NAME', 'IMPORT_NAME', 'LOAD_FAST'
|
|
||||||
if c = prog_code(addr)
|
|
||||||
di.add_comment c[:names][di.instruction.args.first.reduce].inspect
|
|
||||||
end
|
|
||||||
end
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
|
|
||||||
opcode_list.each { |op|
|
|
||||||
binding = case op
|
|
||||||
when 'nop'; lambda { |*a| {} }
|
|
||||||
end
|
|
||||||
@backtrace_binding[op] ||= binding if binding
|
|
||||||
}
|
|
||||||
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Var; arg.symbolic
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.basename]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
{ :incomplete_binding => Expression[1] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
arg = case di.opcode.name
|
|
||||||
when 'JUMP_FORWARD', 'FOR_ITER'
|
|
||||||
# relative offset
|
|
||||||
di.instruction.args.last.reduce + di.next_addr
|
|
||||||
when 'CALL_FUNCTION_VAR'
|
|
||||||
'lol'
|
|
||||||
when /CALL/
|
|
||||||
:unknown
|
|
||||||
else
|
|
||||||
# absolute offset from :code start
|
|
||||||
off = di.instruction.args.last.reduce
|
|
||||||
if c = prog_code(di)
|
|
||||||
off += c[:fileoff]
|
|
||||||
end
|
|
||||||
off
|
|
||||||
end
|
|
||||||
|
|
||||||
[Expression[(arg.kind_of?(Var) ? arg.symbolic : arg)]]
|
|
||||||
end
|
|
||||||
|
|
||||||
def prog_code(addr)
|
|
||||||
addr = addr.address if addr.kind_of? DecodedInstruction
|
|
||||||
@last_prog_code ||= nil
|
|
||||||
return @last_prog_code if @last_prog_code and @last_prog_code[:fileoff] <= addr and @last_prog_code[:fileoff] + @last_prog_code[:code].length > addr
|
|
||||||
@last_prog_code = @program.code_at_off(addr) if @program
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
#Expression[expr].reduce == Expression['wtf']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,36 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Python < CPU
|
|
||||||
def initialize(prog = nil)
|
|
||||||
super()
|
|
||||||
@program = prog
|
|
||||||
@endianness = (prog.respond_to?(:endianness) ? prog.endianness : :little)
|
|
||||||
@size = (prog.respond_to?(:size) ? prog.size : 32)
|
|
||||||
end
|
|
||||||
|
|
||||||
class Var
|
|
||||||
include Renderable
|
|
||||||
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
o.class == self.class and o.i == i
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic; "var_#{@i}".to_sym end
|
|
||||||
|
|
||||||
def render
|
|
||||||
["var_#@i"]
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,180 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/python/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Python
|
|
||||||
CMP_OP = %w[< <= == != > >= in not_in is is_not exch]
|
|
||||||
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
o = Opcode.new(name)
|
|
||||||
o.bin = bin
|
|
||||||
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @valid_args[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
}
|
|
||||||
o.args << :i16 if o.bin >= 90 and o.props.empty? # HAVE_ARGUMENT
|
|
||||||
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
@opcode_list = []
|
|
||||||
|
|
||||||
@valid_args[:u8] = true
|
|
||||||
@valid_args[:i16] = true
|
|
||||||
@valid_args[:cmp] = true
|
|
||||||
|
|
||||||
addop 'STOP_CODE', 0, :stopexec
|
|
||||||
addop 'POP_TOP', 1
|
|
||||||
addop 'ROT_TWO', 2
|
|
||||||
addop 'ROT_THREE', 3
|
|
||||||
addop 'DUP_TOP', 4
|
|
||||||
addop 'ROT_FOUR', 5
|
|
||||||
addop 'NOP', 9
|
|
||||||
|
|
||||||
addop 'UNARY_POSITIVE', 10
|
|
||||||
addop 'UNARY_NEGATIVE', 11
|
|
||||||
addop 'UNARY_NOT', 12
|
|
||||||
addop 'UNARY_CONVERT', 13
|
|
||||||
|
|
||||||
addop 'UNARY_INVERT', 15
|
|
||||||
|
|
||||||
addop 'BINARY_POWER', 19
|
|
||||||
|
|
||||||
addop 'BINARY_MULTIPLY', 20
|
|
||||||
addop 'BINARY_DIVIDE', 21
|
|
||||||
addop 'BINARY_MODULO', 22
|
|
||||||
addop 'BINARY_ADD', 23
|
|
||||||
addop 'BINARY_SUBTRACT', 24
|
|
||||||
addop 'BINARY_SUBSCR', 25
|
|
||||||
addop 'BINARY_FLOOR_DIVIDE', 26
|
|
||||||
addop 'BINARY_TRUE_DIVIDE', 27
|
|
||||||
addop 'INPLACE_FLOOR_DIVIDE', 28
|
|
||||||
addop 'INPLACE_TRUE_DIVIDE', 29
|
|
||||||
|
|
||||||
addop 'SLICE', 30
|
|
||||||
addop 'SLICE_1', 31
|
|
||||||
addop 'SLICE_2', 32
|
|
||||||
addop 'SLICE_3', 33
|
|
||||||
|
|
||||||
addop 'STORE_SLICE', 40
|
|
||||||
addop 'STORE_SLICE_1', 41
|
|
||||||
addop 'STORE_SLICE_2', 42
|
|
||||||
addop 'STORE_SLICE_3', 43
|
|
||||||
|
|
||||||
addop 'DELETE_SLICE', 50
|
|
||||||
addop 'DELETE_SLICE_1', 51
|
|
||||||
addop 'DELETE_SLICE_2', 52
|
|
||||||
addop 'DELETE_SLICE_3', 53
|
|
||||||
|
|
||||||
addop 'STORE_MAP', 54
|
|
||||||
addop 'INPLACE_ADD', 55
|
|
||||||
addop 'INPLACE_SUBTRACT', 56
|
|
||||||
addop 'INPLACE_MULTIPLY', 57
|
|
||||||
addop 'INPLACE_DIVIDE', 58
|
|
||||||
addop 'INPLACE_MODULO', 59
|
|
||||||
addop 'STORE_SUBSCR', 60
|
|
||||||
addop 'DELETE_SUBSCR', 61
|
|
||||||
|
|
||||||
addop 'BINARY_LSHIFT', 62
|
|
||||||
addop 'BINARY_RSHIFT', 63
|
|
||||||
addop 'BINARY_AND', 64
|
|
||||||
addop 'BINARY_XOR', 65
|
|
||||||
addop 'BINARY_OR', 66
|
|
||||||
addop 'INPLACE_POWER', 67
|
|
||||||
addop 'GET_ITER', 68
|
|
||||||
|
|
||||||
addop 'PRINT_EXPR', 70
|
|
||||||
addop 'PRINT_ITEM', 71
|
|
||||||
addop 'PRINT_NEWLINE', 72
|
|
||||||
addop 'PRINT_ITEM_TO', 73
|
|
||||||
addop 'PRINT_NEWLINE_TO', 74
|
|
||||||
addop 'INPLACE_LSHIFT', 75
|
|
||||||
addop 'INPLACE_RSHIFT', 76
|
|
||||||
addop 'INPLACE_AND', 77
|
|
||||||
addop 'INPLACE_XOR', 78
|
|
||||||
addop 'INPLACE_OR', 79
|
|
||||||
addop 'BREAK_LOOP', 80
|
|
||||||
addop 'WITH_CLEANUP', 81
|
|
||||||
addop 'LOAD_LOCALS', 82
|
|
||||||
addop 'RETURN_VALUE', 83
|
|
||||||
addop 'IMPORT_STAR', 84
|
|
||||||
addop 'EXEC_STMT', 85
|
|
||||||
addop 'YIELD_VALUE', 86
|
|
||||||
addop 'POP_BLOCK', 87
|
|
||||||
addop 'END_FINALLY', 88
|
|
||||||
addop 'BUILD_CLASS', 89
|
|
||||||
|
|
||||||
#addop 'HAVE_ARGUMENT', 90 #/* Opcodes from here have an argument: */
|
|
||||||
|
|
||||||
addop 'STORE_NAME', 90 #/* Index in name list */
|
|
||||||
addop 'DELETE_NAME', 91 #/* "" */
|
|
||||||
addop 'UNPACK_SEQUENCE', 92 #/* Number of sequence items */
|
|
||||||
addop 'FOR_ITER', 93, :setip
|
|
||||||
addop 'LIST_APPEND', 94
|
|
||||||
|
|
||||||
addop 'STORE_ATTR', 95 #/* Index in name list */
|
|
||||||
addop 'DELETE_ATTR', 96 #/* "" */
|
|
||||||
addop 'STORE_GLOBAL', 97 #/* "" */
|
|
||||||
addop 'DELETE_GLOBAL', 98 #/* "" */
|
|
||||||
addop 'DUP_TOPX', 99 #/* number of items to duplicate */
|
|
||||||
addop 'LOAD_CONST', 100 #/* Index in const list */
|
|
||||||
addop 'LOAD_NAME', 101 #/* Index in name list */
|
|
||||||
addop 'BUILD_TUPLE', 102 #/* Number of tuple items */
|
|
||||||
addop 'BUILD_LIST', 103 #/* Number of list items */
|
|
||||||
addop 'BUILD_SET', 104 #/* Number of set items */
|
|
||||||
addop 'BUILD_MAP', 105 #/* Always zero for now */
|
|
||||||
addop 'LOAD_ATTR', 106 #/* Index in name list */
|
|
||||||
addop 'COMPARE_OP', 107, :cmp #/* Comparison operator */
|
|
||||||
addop 'IMPORT_NAME', 108 #/* Index in name list */
|
|
||||||
addop 'IMPORT_FROM', 109 #/* Index in name list */
|
|
||||||
addop 'JUMP_FORWARD', 110, :setip, :stopexec #/* Number of bytes to skip */
|
|
||||||
|
|
||||||
addop 'JUMP_IF_FALSE_OR_POP', 111, :setip #/* Target byte offset from beginning of code */
|
|
||||||
addop 'JUMP_IF_TRUE_OR_POP', 112, :setip #/* "" */
|
|
||||||
addop 'JUMP_ABSOLUTE', 113, :setip, :stopexec #/* "" */
|
|
||||||
addop 'POP_JUMP_IF_FALSE', 114, :setip #/* "" */
|
|
||||||
addop 'POP_JUMP_IF_TRUE', 115, :setip #/* "" */
|
|
||||||
|
|
||||||
addop 'LOAD_GLOBAL', 116 #/* Index in name list */
|
|
||||||
|
|
||||||
addop 'CONTINUE_LOOP', 119 #/* Start of loop (absolute) */
|
|
||||||
addop 'SETUP_LOOP', 120 #/* Target address (relative) */
|
|
||||||
addop 'SETUP_EXCEPT', 121 #/* "" */
|
|
||||||
addop 'SETUP_FINALLY', 122 #/* "" */
|
|
||||||
|
|
||||||
addop 'LOAD_FAST', 124 #/* Local variable number */
|
|
||||||
addop 'STORE_FAST', 125 #/* Local variable number */
|
|
||||||
addop 'DELETE_FAST', 126 #/* Local variable number */
|
|
||||||
|
|
||||||
addop 'RAISE_VARARGS', 130 #/* Number of raise arguments (1, 2 or 3) */
|
|
||||||
#/* CALL_FUNCTION_XXX opcodes defined below depend on this definition */
|
|
||||||
addop 'CALL_FUNCTION', 131, :u8, :u8, :setip #/* #args + (#kwargs<<8) */
|
|
||||||
addop 'MAKE_FUNCTION', 132 #/* #defaults */
|
|
||||||
addop 'BUILD_SLICE', 133 #/* Number of items */
|
|
||||||
|
|
||||||
addop 'MAKE_CLOSURE', 134 #/* #free vars */
|
|
||||||
addop 'LOAD_CLOSURE', 135 #/* Load free variable from closure */
|
|
||||||
addop 'LOAD_DEREF', 136 #/* Load and dereference from closure cell */
|
|
||||||
addop 'STORE_DEREF', 137 #/* Store into cell */
|
|
||||||
|
|
||||||
#/* The next 3 opcodes must be contiguous and satisfy (CALL_FUNCTION_VAR - CALL_FUNCTION) & 3 == 1 */
|
|
||||||
addop 'CALL_FUNCTION_VAR', 140, :u8, :u8, :setip #/* #args + (#kwargs<<8) */
|
|
||||||
addop 'CALL_FUNCTION_KW', 141, :u8, :u8, :setip #/* #args + (#kwargs<<8) */
|
|
||||||
addop 'CALL_FUNCTION_VAR_KW', 142, :u8, :u8, :setip #/* #args + (#kwargs<<8) */
|
|
||||||
|
|
||||||
addop 'SETUP_WITH', 143
|
|
||||||
|
|
||||||
#/* Support for opargs more than 16 bits long */
|
|
||||||
addop 'EXTENDED_ARG', 145
|
|
||||||
|
|
||||||
addop 'SET_ADD', 146
|
|
||||||
addop 'MAP_ADD', 147
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,8 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/sh4/decode'
|
|
@ -1,367 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/sh4/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Sh4
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
op.bin_mask = 0
|
|
||||||
op.args.each { |f|
|
|
||||||
op.bin_mask |= @fields_mask[f] << @fields_shift[f]
|
|
||||||
}
|
|
||||||
op.bin_mask ^= 0xffff
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
lookaside = (0..0xf).inject({}) { |h, i| h.update i => [] }
|
|
||||||
opcode_list.each { |op|
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
lookaside[(op.bin >> 12) & 0xf] << op
|
|
||||||
}
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
# depending on transfert size mode (sz flag), fmov instructions manipulate single ou double precision values
|
|
||||||
# instruction aliasing appears when sz is not handled
|
|
||||||
def transfer_size_mode(list)
|
|
||||||
return list if list.find { |op| not op.name.include? 'mov' }
|
|
||||||
@transfersz == 0 ? list.find_all { |op| op.name.include? 'fmov.s' } : list.reject { |op| op.name.include? 'fmov.s' }
|
|
||||||
end
|
|
||||||
|
|
||||||
# when pr flag is set, floating point instructions are executed as double-precision operations
|
|
||||||
# thus register pair is used (DRn registers)
|
|
||||||
def precision_mode(list)
|
|
||||||
@fpprecision == 0 ? list.reject { |op| op.args.include? :drn } : list.find_all { |op| op.args.include? :frn }
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
return if edata.ptr >= edata.length
|
|
||||||
|
|
||||||
di = DecodedInstruction.new(self)
|
|
||||||
val = edata.decode_imm(:u16, @endianness)
|
|
||||||
edata.ptr -= 2
|
|
||||||
op = @bin_lookaside[(val >> 12) & 0xf].find_all { |opcode| (val & opcode.bin_mask) == opcode.bin }
|
|
||||||
|
|
||||||
op = transfer_size_mode(op) if op.length == 2
|
|
||||||
op = precision_mode(op) if op.length == 2
|
|
||||||
|
|
||||||
if op.length > 1
|
|
||||||
puts "current value: #{Expression[val]}, ambiguous matches:",
|
|
||||||
op.map { |opcode| " #{opcode.name} - #{opcode.args.inspect} - #{Expression[opcode.bin]} - #{Expression[opcode.bin_mask]}" }
|
|
||||||
#raise "Sh4 - Internal error"
|
|
||||||
end
|
|
||||||
|
|
||||||
if not op.empty?
|
|
||||||
di.opcode = op.first
|
|
||||||
di
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
di.opcode.props[:memsz] = (op.name =~ /\.l|mova/ ? 32 : (op.name =~ /\.w/ ? 16 : 8))
|
|
||||||
val = edata.decode_imm(:u16, @endianness)
|
|
||||||
|
|
||||||
field_val = lambda{ |f|
|
|
||||||
r = (val >> @fields_shift[f]) & @fields_mask[f]
|
|
||||||
case f
|
|
||||||
when :@rm, :@rn ,:@_rm, :@_rn, :@rm_, :@rn_; GPR.new(r)
|
|
||||||
when :@disppc
|
|
||||||
# The effective address is formed by calculating PC+4,
|
|
||||||
# clearing the lowest 2 bits, and adding the zero-extended 8-bit immediate i
|
|
||||||
# multiplied by 4 (32-bit)/ 2 (16-bit) / 1 (8-bit).
|
|
||||||
curaddr = di.address+4
|
|
||||||
curaddr = (curaddr & 0xffff_fffc) if di.opcode.props[:memsz] == 32
|
|
||||||
curaddr+r*(di.opcode.props[:memsz]/8)
|
|
||||||
when :@disprm, :@dispr0rn; (r & 0xf) * (di.opcode.props[:memsz]/8)
|
|
||||||
when :@disprmrn; (r & 0xf) * 4
|
|
||||||
when :@dispgbr; Expression.make_signed(r, 16)
|
|
||||||
when :disp8; di.address+4+2*Expression.make_signed(r, 8)
|
|
||||||
when :disp12; di.address+4+2*Expression.make_signed(r, 12)
|
|
||||||
when :s8; Expression.make_signed(r, 8)
|
|
||||||
else r
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :r0; GPR.new 0
|
|
||||||
when :rm, :rn; GPR.new field_val[a]
|
|
||||||
when :rm_bank, :rn_bank; RBANK.new field_val[a]
|
|
||||||
when :drm, :drn; DR.new field_val[a]
|
|
||||||
when :frm, :frn; FR.new field_val[a]
|
|
||||||
when :xdm, :xdn; XDR.new field_val[a]
|
|
||||||
when :fvm, :fvn; FVR.new field_val[a]
|
|
||||||
when :vbr; VBR.new
|
|
||||||
when :gbr; GBR.new
|
|
||||||
when :sr; SR.new
|
|
||||||
when :ssr; SSR.new
|
|
||||||
when :spc; SPC.new
|
|
||||||
when :sgr; SGR.new
|
|
||||||
when :dbr; DBR.new
|
|
||||||
when :mach; MACH.new
|
|
||||||
when :macl; MACL.new
|
|
||||||
when :pr; PR.new
|
|
||||||
when :fpul; FPUL.new
|
|
||||||
when :fpscr; FPSCR.new
|
|
||||||
when :pc; PC.new
|
|
||||||
|
|
||||||
when :@rm, :@rn, :@disppc
|
|
||||||
Memref.new(field_val[a], nil)
|
|
||||||
when :@_rm, :@_rn
|
|
||||||
Memref.new(field_val[a], nil, :pre)
|
|
||||||
when :@rm_, :@rn_
|
|
||||||
Memref.new(field_val[a], nil, :post)
|
|
||||||
when :@r0rm
|
|
||||||
Memref.new(GPR.new(0), GPR.new(field_val[:rm]))
|
|
||||||
when :@r0rn, :@dispr0rn
|
|
||||||
Memref.new(GPR.new(0), GPR.new(field_val[:rn]))
|
|
||||||
when :@disprm
|
|
||||||
Memref.new(field_val[a], GPR.new(field_val[:rm]))
|
|
||||||
when :@disprmrn
|
|
||||||
Memref.new(field_val[a], GPR.new(field_val[:rn]))
|
|
||||||
|
|
||||||
when :disppc; Expression[field_val[:@disppc]]
|
|
||||||
when :s8, :disp8, :disp12; Expression[field_val[a]]
|
|
||||||
when :i16, :i8, :i5; Expression[field_val[a]]
|
|
||||||
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
return if edata.ptr > edata.length
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def disassembler_default_func
|
|
||||||
df = DecodedFunction.new
|
|
||||||
df.backtrace_binding = {}
|
|
||||||
(0..7 ).each { |i| r = "r#{i}".to_sym ; df.backtrace_binding[r] = Expression::Unknown }
|
|
||||||
(8..15).each { |i| r = "r#{i}".to_sym ; df.backtrace_binding[r] = Expression[r] }
|
|
||||||
df.backtracked_for = [BacktraceTrace.new(Expression[:pr], :default, Expression[:pr], :x)]
|
|
||||||
df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
|
|
||||||
if funcaddr != :default
|
|
||||||
btfor
|
|
||||||
elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip]
|
|
||||||
btfor
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
df
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
|
|
||||||
retaddrlist.map! { |retaddr| dasm.decoded[retaddr] ? dasm.decoded[retaddr].block.list.last.address : retaddr } if retaddrlist
|
|
||||||
b = f.backtrace_binding
|
|
||||||
|
|
||||||
bt_val = lambda { |r|
|
|
||||||
next if not retaddrlist
|
|
||||||
bt = []
|
|
||||||
b[r] = Expression::Unknown # break recursive dep
|
|
||||||
retaddrlist.each { |retaddr|
|
|
||||||
bt |= dasm.backtrace(Expression[r], retaddr,
|
|
||||||
:include_start => true, :snapshot_addr => faddr, :origin => retaddr)
|
|
||||||
}
|
|
||||||
b[r] = ((bt.length == 1) ? bt.first : Expression::Unknown)
|
|
||||||
}
|
|
||||||
wantregs = GPR::Sym if wantregs.empty?
|
|
||||||
wantregs.map { |r| r.to_sym }.each(&bt_val)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# interprets a condition code (in an opcode name) as an expression
|
|
||||||
def decode_cmp_expr(di, a0, a1)
|
|
||||||
case di.opcode.name
|
|
||||||
when 'cmp/eq'; Expression[a0, :'==', a1]
|
|
||||||
when 'cmp/ge'; Expression[a0, :'>=', a1] # signed
|
|
||||||
when 'cmp/gt'; Expression[a0, :'>', a1] # signed
|
|
||||||
when 'cmp/hi'; Expression[a0, :'>', a1] # unsigned
|
|
||||||
when 'cmp/hs'; Expression[a0, :'>=', a1] # unsigned
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_cmp_cst(di, a0)
|
|
||||||
case di.opcode.name
|
|
||||||
when 'cmp/pl'; Expression[a0, :'>', 0] # signed
|
|
||||||
when 'cmp/pz'; Expression[a0, :'>=', 0] # signed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def opsz(di)
|
|
||||||
ret = @size
|
|
||||||
ret = 8 if di and di.opcode.name =~ /\.b/
|
|
||||||
ret = 16 if di and di.opcode.name =~ /\.w/
|
|
||||||
ret
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
|
|
||||||
mask = lambda { |di| (1 << opsz(di)) - 1 } # 32bits => 0xffff_ffff
|
|
||||||
|
|
||||||
opcode_list.map { |ol| ol.name }.uniq.each { |op|
|
|
||||||
@backtrace_binding[op] ||= case op
|
|
||||||
when 'ldc', 'ldc.l', 'lds', 'lds.l', 'stc', 'stc.l', 'mov', 'mov.l', 'sts', 'sts.l'
|
|
||||||
lambda { |di, a0, a1| { a1 => Expression[a0] }}
|
|
||||||
when 'stc.w', 'stc.b', 'mov.w', 'mov.b'
|
|
||||||
lambda { |di, a0, a1| { a1 => Expression[a0, :&, mask[di]] }}
|
|
||||||
when 'movt'; lambda { |di, a0| { a0 => :t_bit }}
|
|
||||||
when 'mova'; lambda { |di, a0, a1| { a1 => Expression[a0] }}
|
|
||||||
when 'exts.b', 'exts.w', 'extu.w'
|
|
||||||
lambda { |di, a0, a1| { a1 => Expression[a0, :&, mask[di]] }}
|
|
||||||
when 'cmp/eq', 'cmp/ge', 'cmp/ge', 'cmp/gt', 'cmp/hi', 'cmp/hs'
|
|
||||||
lambda { |di, a0, a1| { :t_bit => decode_cmp_expr(di, a0, a1) }}
|
|
||||||
when 'cmp/pl', 'cmp/pz'
|
|
||||||
lambda { |di, a0| { :t_bit => decode_cmp_cst(di, a0) }}
|
|
||||||
when 'tst'; lambda { |di, a0, a1| { :t_bit => Expression[[a0, :&, mask[di]], :==, [a1, :&, mask[di]]] }}
|
|
||||||
when 'rte'; lambda { |di| { :pc => :spc , :sr => :ssr }}
|
|
||||||
when 'rts'; lambda { |di| { :pc => :pr }}
|
|
||||||
when 'sets'; lambda { |di| { :s_bit => 1 }}
|
|
||||||
when 'sett'; lambda { |di| { :t_bit => 1 }}
|
|
||||||
when 'clrs'; lambda { |di| { :s_bit => 0 }}
|
|
||||||
when 'clrt'; lambda { |di| { :t_bit => 0 }}
|
|
||||||
when 'clrmac'; lambda { |di| { :macl => 0, :mach => 0 }}
|
|
||||||
when 'jmp'; lambda { |di, a0| { :pc => a0 }}
|
|
||||||
when 'jsr', 'bsr', 'bsrf'; lambda { |di, a0| { :pc => Expression[a0], :pr => Expression[di.address, :+, 2*2] }}
|
|
||||||
when 'dt'; lambda { |di, a0|
|
|
||||||
res = Expression[a0, :-, 1]
|
|
||||||
{ :a0 => res, :t_bit => Expression[res, :==, 0] }
|
|
||||||
}
|
|
||||||
when 'add' ; lambda { |di, a0, a1| { a1 => Expression[[a0, :+, a1], :&, 0xffff_ffff] }}
|
|
||||||
when 'addc' ; lambda { |di, a0, a1|
|
|
||||||
res = Expression[[a0, :&, mask[di]], :+, [[a1, :&, mask[di]], :+, :t_bit]]
|
|
||||||
{ a1 => Expression[a0, :+, [a1, :+, :t_bit]], :t_bit => Expression[res, :>, mask[di]] }
|
|
||||||
}
|
|
||||||
when 'addv' ; lambda { |di, a0, a1|
|
|
||||||
res = Expression[[a0, :&, mask[di]], :+, [[a1, :&, mask[di]]]]
|
|
||||||
{ a1 => Expression[a0, :+, [a1, :+, :t_bit]], :t_bit => Expression[res, :>, mask[di]] }
|
|
||||||
}
|
|
||||||
when 'shll16', 'shll8', 'shll2', 'shll' ; lambda { |di, a0|
|
|
||||||
shift = { 'shll16' => 16, 'shll8' => 8, 'shll2' => 2, 'shll' => 1 }[op]
|
|
||||||
{ a0 => Expression[[a0, :<<, shift], :&, 0xffff] }
|
|
||||||
}
|
|
||||||
when 'shlr16', 'shlr8', 'shlr2','shlr'; lambda { |di, a0|
|
|
||||||
shift = { 'shlr16' => 16, 'shlr8' => 8, 'shlr2' => 2, 'shlr' => 1 }[op]
|
|
||||||
{ a0 => Expression[a0, :>>, shift] }
|
|
||||||
}
|
|
||||||
when 'rotcl'; lambda { |di, a0| { a0 => Expression[[a0, :<<, 1], :|, :t_bit], :t_bit => Expression[a0, :>>, [opsz[di], :-, 1]] }}
|
|
||||||
when 'rotcr'; lambda { |di, a0| { a0 => Expression[[a0, :>>, 1], :|, :t_bit], :t_bit => Expression[a0, :&, 1] }}
|
|
||||||
when 'rotl'; lambda { |di, a0|
|
|
||||||
shift_bit = [a0, :<<, [opsz[di], :-, 1]]
|
|
||||||
{ a0 => Expression[[a0, :<<, 1], :|, shift_bit], :t_bit => shift_bit }
|
|
||||||
}
|
|
||||||
when 'rotr'; lambda { |di, a0|
|
|
||||||
shift_bit = [a0, :>>, [opsz[di], :-, 1]]
|
|
||||||
{ a0 => Expression[[a0, :>>, 1], :|, shift_bit], :t_bit => shift_bit }
|
|
||||||
}
|
|
||||||
when 'shal'; lambda { |di, a0|
|
|
||||||
shift_bit = [a0, :<<, [opsz[di], :-, 1]]
|
|
||||||
{ a0 => Expression[a0, :<<, 1], :t_bit => shift_bit }
|
|
||||||
}
|
|
||||||
when 'shar'; lambda { |di, a0|
|
|
||||||
shift_bit = Expression[a0, :&, 1]
|
|
||||||
{ a0 => Expression[a0, :>>, 1], :t_bit => shift_bit }
|
|
||||||
}
|
|
||||||
when 'sub'; lambda { |di, a0, a1| { a1 => Expression[[a1, :-, a0], :&, 0xffff_ffff] }}
|
|
||||||
when 'subc'; lambda { |di, a0, a1| { a1 => Expression[a1, :-, [a0, :-, :t_bit]] }}
|
|
||||||
when 'and', 'and.b'; lambda { |di, a0, a1| { a1 => Expression[[a0, :&, mask[di]], :|, [[a1, :&, mask[di]]]] }}
|
|
||||||
when 'or', 'or.b'; lambda { |di, a0, a1| { a1 => Expression[[a0, :|, mask[di]], :|, [[a1, :&, mask[di]]]] }}
|
|
||||||
when 'xor', 'xor.b'; lambda { |di, a0, a1| { a1 => Expression[[a0, :|, mask[di]], :^, [[a1, :&, mask[di]]]] }}
|
|
||||||
when 'neg' ; lambda { |di, a0, a1| { a1 => Expression[mask[di], :-, a0] }}
|
|
||||||
when 'negc' ; lambda { |di, a0, a1| { a1 => Expression[[[mask[di], :-, a0], :-, :t_bit], :&, mask[di]] }}
|
|
||||||
when 'not'; lambda { |di, a0, a1| { a1 => Expression[a0, :^, mask[di]] }}
|
|
||||||
when 'nop'; lambda { |*a| {} }
|
|
||||||
when /^b/; lambda { |*a| {} } # branches
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when GPR, XFR, XDR, FVR, DR, FR, XMTRX; arg.symbolic
|
|
||||||
when MACH, MACL, PR, FPUL, PC, FPSCR; arg.symbolic
|
|
||||||
when SR, SSR, SPC, GBR, VBR, SGR, DBR; arg.symbolic
|
|
||||||
when Memref; arg.symbolic(di.address, di.opcode.props[:memsz]/8)
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.basename]
|
|
||||||
bd = binding[di, *a] || {}
|
|
||||||
di.instruction.args.grep(Memref).each { |m|
|
|
||||||
next unless r = m.base and r.kind_of?(GPR)
|
|
||||||
r = r.symbolic
|
|
||||||
case m.action
|
|
||||||
when :post
|
|
||||||
bd[r] ||= Expression[r, :+, di.opcode.props[:memsz]/8]
|
|
||||||
when :pre
|
|
||||||
bd[r] ||= Expression[r, :-, di.opcode.props[:memsz]/8]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
bd
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
{:incomplete_binding => Expression[1]}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
val = case di.instruction.opname
|
|
||||||
when 'rts'; :pr
|
|
||||||
else di.instruction.args.last
|
|
||||||
end
|
|
||||||
|
|
||||||
val = case val
|
|
||||||
when Reg; val.symbolic
|
|
||||||
when Memref; arg.symbolic(di.address, 4)
|
|
||||||
else val
|
|
||||||
end
|
|
||||||
|
|
||||||
val = case di.instruction.opname
|
|
||||||
when 'braf', 'bsrf'; Expression[[di.address, :+, 4], :+, val]
|
|
||||||
else val
|
|
||||||
end
|
|
||||||
|
|
||||||
[Expression[val]]
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
expr.reduce_rec == :pr
|
|
||||||
end
|
|
||||||
|
|
||||||
def delay_slot(di=nil)
|
|
||||||
(di and di.opcode.props[:delay_slot]) ? 1 : 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def replace_instr_arg_immediate(i, old, new)
|
|
||||||
i.args.map! { |a|
|
|
||||||
case a
|
|
||||||
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
|
||||||
when Memref
|
|
||||||
a.base = (a.base == old ? new : Expression[a.base.bind(old => new).reduce]) if a.base.kind_of?(Expression)
|
|
||||||
a
|
|
||||||
else a
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,301 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
class Sh4 < CPU
|
|
||||||
def initialize(e = :little, transfersz = 0, fpprecision = 0)
|
|
||||||
super()
|
|
||||||
@endianness = e
|
|
||||||
|
|
||||||
# transfer size mode
|
|
||||||
# When SZ = 1 and big endian mode is selected, FMOV can
|
|
||||||
# be used for double-precision floating-point data load or
|
|
||||||
# store operations. In little endian mode, two 32-bit data size
|
|
||||||
# moves must be executed, with SZ = 0, to load or store a
|
|
||||||
# double-precision floating-point number.
|
|
||||||
transfersz = 0 if @endianness == :little
|
|
||||||
@transfersz = transfersz
|
|
||||||
|
|
||||||
# PR = 0 : Floating point instructions are executed as single
|
|
||||||
# precision operations.
|
|
||||||
# PR = 1 : Floating point instructions are executed as double-
|
|
||||||
# precision operations (the result of instructions for
|
|
||||||
# which double-precision is not supported is undefined).
|
|
||||||
# Setting [transfersz = fpprecision = 1] is reserved.
|
|
||||||
# FPU operations are undefined in this mode.
|
|
||||||
@fpprecision = fpprecision
|
|
||||||
|
|
||||||
@size = 32
|
|
||||||
end
|
|
||||||
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
|
|
||||||
def ==(o)
|
|
||||||
o.class == self.class and (not respond_to?(:i) or o.i == i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# general purpose reg
|
|
||||||
class GPR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
Sym = (0..15).map { |i| "r#{i}".to_sym }
|
|
||||||
|
|
||||||
def symbolic ; Sym[@i] end
|
|
||||||
|
|
||||||
def render ; ["r#@i"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RBANK < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
Sym = (0..7).map { |i| "r#{i}_bank".to_sym }
|
|
||||||
|
|
||||||
def symbolic ; Sym[@i] end
|
|
||||||
|
|
||||||
def render ; ["r#{@i}_bank"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# floatting-point registers
|
|
||||||
class FR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
Sym = (0..15).map { |i| "fr#{i}".to_sym }
|
|
||||||
|
|
||||||
def symbolic ; Sym[@i] end
|
|
||||||
|
|
||||||
def render ; ["fr#@i"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# DR registers: double-precision floating-point registers
|
|
||||||
# DR0 = {FR0, FR1}
|
|
||||||
# DR2 = {FR2, FR3}
|
|
||||||
# DR4 = {FR4, FR5}
|
|
||||||
# DR6 = {FR6, FR7}
|
|
||||||
# DR8 = {FR8, FR9}
|
|
||||||
# DR10 = {FR10, FR11}
|
|
||||||
# DR12 = {FR12, FR13}
|
|
||||||
# DR14 = {FR14, FR15}
|
|
||||||
class DR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
Sym = (0..7).map { |i| "dr#{i*2}".to_sym }
|
|
||||||
|
|
||||||
def symbolic ; Sym[@i/2] end
|
|
||||||
|
|
||||||
def render ; ["dr#@i"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Single-precision floating-point vector registers
|
|
||||||
# FV0 = {FR0, FR1, FR2, FR3}
|
|
||||||
# FV4 = {FR4, FR5, FR6, FR7},
|
|
||||||
# FV8 = {FR8, FR9, FR10, FR11}
|
|
||||||
# FV12 = {FR12, FR13, FR14, FR15}
|
|
||||||
class FVR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
Sym = (0..3).map { |i| "fv#{i*4}".to_sym }
|
|
||||||
|
|
||||||
def symbolic ; Sym[@i/4] end
|
|
||||||
|
|
||||||
def render ; ["fv#@i"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Single-precision floating-point extended registers
|
|
||||||
class XFR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
Sym = (0..15).map { |i| "xf#{i}".to_sym }
|
|
||||||
|
|
||||||
def symbolic ; Sym[@i] end
|
|
||||||
|
|
||||||
def render ; ["xf#@i"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# XD registers: single-precision floating-point vector registers
|
|
||||||
# XD0 = {XF0, XF1}
|
|
||||||
# XD2 = {XF2, XF3}
|
|
||||||
# XD4 = {XF4, XF5}
|
|
||||||
# XD6 = {XF6, XF7}
|
|
||||||
# XD8 = {XF8, XF9}
|
|
||||||
# XD10 = {XF10, XF11}
|
|
||||||
# XD12 = {XF12, XF13}
|
|
||||||
# XD14 = {XF14, XF15}
|
|
||||||
class XDR < Reg
|
|
||||||
attr_accessor :i
|
|
||||||
|
|
||||||
def initialize(i); @i = i end
|
|
||||||
Sym = (0..7).map { |i| "xd#{i*2}".to_sym }
|
|
||||||
|
|
||||||
def symbolic ; Sym[@i/2] end
|
|
||||||
|
|
||||||
def render ; ["xd#@i"] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Single-precision floating-point extended register matrix
|
|
||||||
class XMTRX < Reg
|
|
||||||
def symbolic ; :xmtrx ; end
|
|
||||||
def render ; ['xmtrx'] ; end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Multiply-and-accumulate register high
|
|
||||||
class MACH < Reg
|
|
||||||
|
|
||||||
def symbolic ; :mach end
|
|
||||||
def render ; ['mach'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Multiply-and-accumulate register low
|
|
||||||
class MACL < Reg
|
|
||||||
|
|
||||||
def symbolic ; :macl end
|
|
||||||
def render ; ['macl'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Procedure register
|
|
||||||
class PR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :pr end
|
|
||||||
def render ; ['pr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Floating-point communication register
|
|
||||||
class FPUL < Reg
|
|
||||||
|
|
||||||
def symbolic ; :fpul end
|
|
||||||
def render ; ['fpul'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Program counter
|
|
||||||
class PC < Reg
|
|
||||||
|
|
||||||
def symbolic ; :pc end
|
|
||||||
def render ; ['pc'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Floating-point status/control register
|
|
||||||
class FPSCR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :fpscr end
|
|
||||||
def render ; ['fpscr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
#----------------------- Control registers -----------------------------
|
|
||||||
|
|
||||||
# Status register
|
|
||||||
class SR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :sr end
|
|
||||||
def render ; ['sr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Saved status register
|
|
||||||
class SSR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :ssr end
|
|
||||||
def render ; ['ssr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Saved program counter
|
|
||||||
class SPC < Reg
|
|
||||||
|
|
||||||
def symbolic ; :spc end
|
|
||||||
def render ; ['spc'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Global base register
|
|
||||||
class GBR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :spc end
|
|
||||||
def render ; ['gbr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Vector base register
|
|
||||||
class VBR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :spc end
|
|
||||||
def render ; ['vbr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Saved general register
|
|
||||||
class SGR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :sgr end
|
|
||||||
def render ; ['sgr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Debug base register
|
|
||||||
class DBR < Reg
|
|
||||||
|
|
||||||
def symbolic ; :dbr end
|
|
||||||
def render ; ['dbr'] end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
# action: pre/post (inc/dec)rement
|
|
||||||
attr_accessor :base, :disp, :action
|
|
||||||
|
|
||||||
def initialize(base, offset, action = nil)
|
|
||||||
base = Expression[base] if base.kind_of? Integer
|
|
||||||
@base, @disp, @action = base, offset, action
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig=nil, sz=32)
|
|
||||||
b = @base
|
|
||||||
b = b.symbolic if b.kind_of? Reg
|
|
||||||
|
|
||||||
b = Expression[b, :-, sz/8] if @action == :pre
|
|
||||||
|
|
||||||
if disp
|
|
||||||
o = @disp
|
|
||||||
o = o.symbolic if o.kind_of? Reg
|
|
||||||
e = Expression[b, :+, o].reduce
|
|
||||||
else
|
|
||||||
e = Expression[b].reduce
|
|
||||||
end
|
|
||||||
|
|
||||||
Indirection[e, sz, orig]
|
|
||||||
end
|
|
||||||
|
|
||||||
include Renderable
|
|
||||||
|
|
||||||
def render
|
|
||||||
if @disp
|
|
||||||
#['@(', @base, ',', @disp, ')']
|
|
||||||
['[', @base, '+', @disp, ']']
|
|
||||||
else
|
|
||||||
case @action
|
|
||||||
when :pre then ['[--', @base, ']']
|
|
||||||
when :post then ['[', @base, '++]']
|
|
||||||
else ['[', @base, ']']
|
|
||||||
#when :pre then ['@-', @base]
|
|
||||||
#when :post then ['@', @base, '+']
|
|
||||||
#else ['@', @base]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
init
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_list
|
|
||||||
@dbg_register_list ||= GPR::Sym
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,380 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2010 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/sh4/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Sh4
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @fields_mask[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a]
|
|
||||||
}
|
|
||||||
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def init
|
|
||||||
@opcode_list = []
|
|
||||||
|
|
||||||
# :@rm_ is used for @Rm+
|
|
||||||
# :@_rn is used for @-Rn
|
|
||||||
# :@r0rm is used for @(R0, Rm) (same for r0rn)
|
|
||||||
# :@r0gbr is used for @(R0, GBR)
|
|
||||||
@fields_mask = {
|
|
||||||
:rm => 0xf, :rn => 0xf,
|
|
||||||
:@rm => 0xf, :@rn => 0xf,
|
|
||||||
:@rm_ => 0xf, :@rn_ => 0xf,
|
|
||||||
:@_rn => 0xf,
|
|
||||||
|
|
||||||
:frm => 0xf, :frn => 0xf,
|
|
||||||
:xdm => 0x7, :xdn => 0x7,
|
|
||||||
:drm => 0x7, :drn => 0x7,
|
|
||||||
:fvm => 0x3, :fvn => 0x3,
|
|
||||||
|
|
||||||
:@r0rm => 0xf, :@r0rn => 0xf,
|
|
||||||
:rm_bank => 0x7, :rn_bank => 0x7,
|
|
||||||
|
|
||||||
:@disprm => 0xff, :@dispr0rn => 0xff, :@disprmrn => 0xf0f,
|
|
||||||
:@dispgbr => 0xff, :@disppc => 0xff,
|
|
||||||
:disp8 => 0xff, :disp12 => 0xfff, :disppc => 0xff,
|
|
||||||
|
|
||||||
:i8 => 0xff, # zero-extendded 8-bit immediate
|
|
||||||
:s8 => 0xff, # 8-bit displacement s is sign-extended, doubled and added to PC+4
|
|
||||||
}
|
|
||||||
|
|
||||||
@fields_shift = {
|
|
||||||
:rm => 4, :rn => 8,
|
|
||||||
:@rm => 4, :@rn => 8,
|
|
||||||
:@rm_ => 4, :@rn_ => 8,
|
|
||||||
:@_rn => 8,
|
|
||||||
|
|
||||||
:frm => 4, :frn => 8,
|
|
||||||
:xdm => 5, :xdn => 9,
|
|
||||||
:drm => 5, :drn => 9,
|
|
||||||
:fvm => 8, :fvn => 10,
|
|
||||||
|
|
||||||
:@r0rm => 4, :@r0rn => 8,
|
|
||||||
:rm_bank => 7, :rn_bank => 4,
|
|
||||||
|
|
||||||
:@disprm => 0, :@dispr0rn => 0, :@disprmrn => 0,
|
|
||||||
:@dispgbr => 0, :@disppc => 0,
|
|
||||||
:disp8 => 0, :disp12 => 0, :disppc => 0,
|
|
||||||
|
|
||||||
:i8 => 0,
|
|
||||||
:s8 => 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
# implicit operands
|
|
||||||
[:vbr, :gbr, :sr, :ssr, :spc, :sgr, :dbr, :mach, :macl, :pr, :fpul, :fpscr, :dbr, :pc, :r0].each { |a| @fields_mask[a] = @fields_shift[a] = 0 }
|
|
||||||
|
|
||||||
@valid_props[:delay_slot] = true
|
|
||||||
|
|
||||||
addop 'add', 0b0011 << 12 | 0b1100, :rm, :rn
|
|
||||||
addop 'add', 0b0111 << 12, :s8, :rn
|
|
||||||
addop 'addc', 0b0011 << 12 | 0b1110, :rm, :rn
|
|
||||||
addop 'addv', 0b0011 << 12 | 0b1111, :rm, :rn
|
|
||||||
|
|
||||||
addop 'and', 0b0010 << 12 | 0b1001, :rm, :rn
|
|
||||||
addop 'and', 0b11001001 << 8, :i8, :r0
|
|
||||||
addop 'and.b', 0b11001101 << 8, :i8, :@r0gbr
|
|
||||||
|
|
||||||
addop 'bf', 0b10001011 << 8, :disp8, :setip
|
|
||||||
addop 'bf/s', 0b10001111 << 8, :disp8, :setip, :delay_slot
|
|
||||||
addop 'bra', 0b1010 << 12, :disp12, :setip, :stopexec, :delay_slot
|
|
||||||
addop 'braf', 0b0000 << 12 | 0b00100011, :rn, :setip, :stopexec, :delay_slot
|
|
||||||
addop 'brk', 0b0000000000111011, :stopexec # causes a pre-execution BREAK exception
|
|
||||||
addop 'bsr', 0b1011 << 12, :disp12, :setip, :saveip, :stopexec, :delay_slot
|
|
||||||
addop 'bsrf', 0b0000 << 12 | 0b00000011, :rn, :setip, :saveip, :stopexec, :delay_slot
|
|
||||||
addop 'bt', 0b10001001 << 8, :disp8, :setip
|
|
||||||
addop 'bt/s', 0b10001101 << 8, :disp8, :setip, :delay_slot
|
|
||||||
|
|
||||||
addop 'clrmac', 0b0000000000101000
|
|
||||||
addop 'clrs', 0b0000000001001000
|
|
||||||
addop 'clrt', 0b0000000000001000
|
|
||||||
|
|
||||||
addop 'cmp/eq', 0b0011 << 12 | 0b0000, :rm, :rn
|
|
||||||
addop 'cmp/eq', 0b10001000 << 8, :s8, :r0
|
|
||||||
addop 'cmp/ge', 0b0011 << 12 | 0b0011, :rm, :rn
|
|
||||||
addop 'cmp/gt', 0b0011 << 12 | 0b0111, :rm, :rn
|
|
||||||
addop 'cmp/hi', 0b0011 << 12 | 0b0110, :rm, :rn
|
|
||||||
addop 'cmp/hs', 0b0011 << 12 | 0b0010, :rm, :rn
|
|
||||||
addop 'cmp/pl', 0b0100 << 12 | 0b00010101, :rn
|
|
||||||
addop 'cmp/pz', 0b0100 << 12 | 0b00010001, :rn
|
|
||||||
addop 'cmp/str', 0b0010 << 12 | 0b1100, :rm, :rn
|
|
||||||
|
|
||||||
addop 'div0s', 0b0010 << 12 | 0b0111, :rm, :rn
|
|
||||||
addop 'div0u', 0b0000000000011001
|
|
||||||
addop 'div1', 0b0011 << 12 | 0b0100, :rm, :rn
|
|
||||||
|
|
||||||
addop 'dmuls.l', 0b0011 << 12 | 0b1101, :rm, :rn
|
|
||||||
addop 'dmulu.l', 0b0011 << 12 | 0b0101, :rm, :rn
|
|
||||||
|
|
||||||
addop 'dt', 0b0100 << 12 | 0b00010000, :rn
|
|
||||||
|
|
||||||
addop 'exts.b', 0b0110 << 12 | 0b1110, :rm, :rn
|
|
||||||
addop 'exts.w', 0b0110 << 12 | 0b1111, :rm, :rn
|
|
||||||
addop 'extu.b', 0b0110 << 12 | 0b1100, :rm, :rn
|
|
||||||
addop 'extu.w', 0b0110 << 12 | 0b1101, :rm, :rn
|
|
||||||
|
|
||||||
# fpu instructions
|
|
||||||
addop 'fabs', 0b1111 << 12 | 0b001011101, :drn
|
|
||||||
addop 'fabs', 0b1111 << 12 | 0b01011101, :frn
|
|
||||||
|
|
||||||
addop 'fadd', 0b1111 << 12 | 0b0 << 8 | 0b00000, :drm, :drn
|
|
||||||
addop 'fadd', 0b1111 << 12 | 0b0000, :frm, :frn
|
|
||||||
|
|
||||||
addop 'fcmp/eq', 0b1111 << 12 | 0b0 << 8 | 0b00100, :drm, :drn
|
|
||||||
addop 'fcmp/eq', 0b1111 << 12 | 0b0100, :frm, :frn
|
|
||||||
|
|
||||||
addop 'fcmp/gt', 0b1111 << 12 | 0b0 << 8 | 0b00101, :drm, :drn
|
|
||||||
addop 'fcmp/gt', 0b1111 << 12 | 0b0101, :frm, :frn
|
|
||||||
|
|
||||||
addop 'fcnvds', 0b1111 << 12 | 0b010111101, :drn, :fpul
|
|
||||||
addop 'fcnvsd', 0b1111 << 12 | 0b010101101, :fpul, :drn
|
|
||||||
|
|
||||||
addop 'fdiv', 0b1111 << 12 | 0b0 << 8 | 0b00011, :drm, :drn
|
|
||||||
addop 'fdiv', 0b1111 << 12 | 0b0011, :frm, :frn
|
|
||||||
addop 'fipr', 0b1111 << 12 | 0b11101101, :fvm, :fvn
|
|
||||||
|
|
||||||
addop 'flds', 0b1111 << 12 | 0b00011101, :frn, :fpul
|
|
||||||
addop 'fldi0', 0b1111 << 12 | 0b10001101, :frn
|
|
||||||
addop 'fldi1', 0b1111 << 12 | 0b10011101, :frn
|
|
||||||
|
|
||||||
addop 'float', 0b1111 << 12 | 0b000101101, :fpul, :drn
|
|
||||||
addop 'float', 0b1111 << 12 | 0b00101101, :fpul, :frn
|
|
||||||
|
|
||||||
addop 'fmac', 0b1111 << 12 | 0b1110, :fr0, :frm, :frn
|
|
||||||
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b0 << 8 | 0b01100, :drm, :drn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b1 << 8 | 0b01100, :drm, :xdn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b01010, :drm, :@rn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b01011, :drm, :@_rn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b00111, :drm, :@r0rn
|
|
||||||
|
|
||||||
addop 'fmov.s', 0b1111 << 12 | 0b1100, :frm, :frn
|
|
||||||
addop 'fmov.s', 0b1111 << 12 | 0b1010, :frm, :@rn
|
|
||||||
addop 'fmov.s', 0b1111 << 12 | 0b1011, :frm, :@_rn
|
|
||||||
addop 'fmov.s', 0b1111 << 12 | 0b0111, :frm, :@r0rn
|
|
||||||
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b0 << 8 | 0b11100, :xdm, :drn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b1 << 8 | 0b11100, :xdm, :xdn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b11010, :xdm, :@rn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b11011, :xdm, :@_rn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b10111, :xdm, :@r0rn
|
|
||||||
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b0 << 8 | 0b1000, :@rm, :drn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b0 << 8 | 0b1001, :@rm_, :drn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b0 << 8 | 0b0110, :@r0rm, :drn
|
|
||||||
|
|
||||||
addop 'fmov.s', 0b1111 << 12 | 0b1000, :@rm, :frn
|
|
||||||
addop 'fmov.s', 0b1111 << 12 | 0b1001, :@rm_, :frn
|
|
||||||
addop 'fmov.s', 0b1111 << 12 | 0b0110, :@r0rm, :frn
|
|
||||||
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b1 << 8 | 0b1000, :@rm, :xdn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b1 << 8 | 0b1001, :@rm_, :xdn
|
|
||||||
addop 'fmov', 0b1111 << 12 | 0b1 << 8 | 0b0110, :@r0rm, :xdn
|
|
||||||
|
|
||||||
addop 'fmul', 0b1111 << 12 | 0b0 << 8 | 0b00010, :drm, :drn
|
|
||||||
addop 'fmul', 0b1111 << 12 | 0b0010, :frm, :frn
|
|
||||||
|
|
||||||
addop 'fneg', 0b1111 << 12 | 0b001001101, :drn
|
|
||||||
addop 'fneg', 0b1111 << 12 | 0b01001101, :frn
|
|
||||||
|
|
||||||
addop 'frchg', 0b1111101111111101
|
|
||||||
addop 'fschg', 0b1111001111111101
|
|
||||||
|
|
||||||
addop 'fsqrt', 0b1111 << 12 | 0b001101101, :drn
|
|
||||||
addop 'fsqrt', 0b1111 << 12 | 0b01101101, :frn
|
|
||||||
addop 'fsts', 0b1111 << 12 | 0b00001101, :fpul, :frn
|
|
||||||
|
|
||||||
addop 'fsub', 0b1111 << 12 | 0b0 << 8 | 0b00001, :@drm, :drn
|
|
||||||
addop 'fsub', 0b1111 << 12 | 0b0001, :frm, :frn
|
|
||||||
|
|
||||||
addop 'ftrc', 0b1111 << 12 | 0b000111101, :drn, :fpul
|
|
||||||
addop 'ftrc', 0b1111 << 12 | 0b00111101, :frn, :fpul
|
|
||||||
addop 'ftrv', 0b1111 << 12 | 0b0111111101, :xmtrx, :fvn
|
|
||||||
|
|
||||||
addop 'jmp', 0b0100 << 12 | 0b00101011, :rn, :setip, :stopexec, :delay_slot
|
|
||||||
addop 'jsr', 0b0100 << 12 | 0b00001011, :rn, :setip, :saveip, :stopexec, :delay_slot
|
|
||||||
|
|
||||||
addop 'ldc', 0b0100 << 12 | 0b00011110, :rn, :gbr
|
|
||||||
addop 'ldc', 0b0100 << 12 | 0b00001110, :rn, :sr # privileged instruction
|
|
||||||
addop 'ldc', 0b0100 << 12 | 0b00101110, :rn, :vbr # privileged instruction
|
|
||||||
addop 'ldc', 0b0100 << 12 | 0b00111110, :rn, :ssr # privileged instruction
|
|
||||||
addop 'ldc', 0b0100 << 12 | 0b01001110, :rn, :spc # privileged instruction
|
|
||||||
addop 'ldc', 0b0100 << 12 | 0b11111010, :rn, :dbr # privileged instruction
|
|
||||||
addop 'ldc', 0b0100 << 12 | 0b1 << 7 | 0b1110, :rn, :rn_bank # privileged instruction
|
|
||||||
|
|
||||||
addop 'ldc.l', 0b0100 << 12 | 0b00010111, :@rn_, :gbr
|
|
||||||
addop 'ldc.l', 0b0100 << 12 | 0b00000111, :@rn_, :sr # privileged instruction
|
|
||||||
addop 'ldc.l', 0b0100 << 12 | 0b00100111, :@rn_, :vbr # privileged instruction
|
|
||||||
addop 'ldc.l', 0b0100 << 12 | 0b00110111, :@rn_, :ssr # privileged instruction
|
|
||||||
addop 'ldc.l', 0b0100 << 12 | 0b01000111, :@rn_, :spc # privileged instruction
|
|
||||||
addop 'ldc.l', 0b0100 << 12 | 0b11110110, :@rn_, :dbr # privileged instruction
|
|
||||||
addop 'ldc.l', 0b0100 << 12 | 0b1 << 7 | 0b0111, :@rn_, :rn_bank # privileged instruction
|
|
||||||
|
|
||||||
addop 'lds', 0b0100 << 12 | 0b01101010, :rn, :fpscr
|
|
||||||
addop 'lds.l', 0b0100 << 12 | 0b01100110, :@rn_, :fpscr
|
|
||||||
addop 'lds', 0b0100 << 12 | 0b01011010, :rn, :fpul
|
|
||||||
addop 'lds.l', 0b0100 << 12 | 0b01010110, :@rn_, :fpul
|
|
||||||
addop 'lds', 0b0100 << 12 | 0b00001010, :rn, :mach
|
|
||||||
addop 'lds.l', 0b0100 << 12 | 0b00000110, :@rn_, :mach
|
|
||||||
addop 'lds', 0b0100 << 12 | 0b00011010, :rn, :macl
|
|
||||||
addop 'lds.l', 0b0100 << 12 | 0b00010110, :@rn_, :macl
|
|
||||||
addop 'lds', 0b0100 << 12 | 0b00101010, :rn, :pr
|
|
||||||
addop 'lds.l', 0b0100 << 12 | 0b00100110, :@rn_, :pr
|
|
||||||
|
|
||||||
addop 'ldtlb', 0b0000000000111000
|
|
||||||
|
|
||||||
addop 'mac.l', 0b0000 << 12 | 0b1111, :@rm_, :@rn_
|
|
||||||
addop 'mac.w', 0b0100 << 12 | 0b1111, :@rm_, :@rn_
|
|
||||||
|
|
||||||
addop 'mov', 0b0110 << 12 | 0b0011, :rm, :rn
|
|
||||||
addop 'mov', 0b1110 << 12, :s8, :rn
|
|
||||||
|
|
||||||
addop 'mov.b', 0b0010 << 12 | 0b0000, :rm, :@rn
|
|
||||||
addop 'mov.b', 0b0010 << 12 | 0b0100, :rm, :@_rn
|
|
||||||
addop 'mov.b', 0b0000 << 12 | 0b0100, :rm, :@r0rn
|
|
||||||
addop 'mov.b', 0b11000000 << 8, :r0, :@dispgbr
|
|
||||||
addop 'mov.b', 0b10000000 << 8, :r0, :@dispr0rn
|
|
||||||
addop 'mov.b', 0b0110 << 12 | 0b0000, :@rm, :rn
|
|
||||||
addop 'mov.b', 0b0110 << 12 | 0b0100, :@rm_, :rn
|
|
||||||
addop 'mov.b', 0b0000 << 12 | 0b1100, :@r0rm, :rn
|
|
||||||
addop 'mov.b', 0b11000100 << 8, :@dispgbr, :r0
|
|
||||||
addop 'mov.b', 0b10000100 << 8, :@dispr0rn, :r0
|
|
||||||
|
|
||||||
addop 'mov.l', 0b0010 << 12 | 0b0010, :rm, :@rn
|
|
||||||
addop 'mov.l', 0b0010 << 12 | 0b0110, :rm, :@_rn
|
|
||||||
addop 'mov.l', 0b0000 << 12 | 0b0110, :rm, :@r0rn
|
|
||||||
addop 'mov.l', 0b11000010 << 8, :r0, :@dispgbr
|
|
||||||
addop 'mov.l', 0b0001 << 12, :rm, :@disprmrn
|
|
||||||
addop 'mov.l', 0b0110 << 12 | 0b0010, :@rm, :rn
|
|
||||||
addop 'mov.l', 0b0110 << 12 | 0b0110, :@rm_, :rn
|
|
||||||
addop 'mov.l', 0b0000 << 12 | 0b1110, :@r0rm, :rn
|
|
||||||
addop 'mov.l', 0b11000110 << 8, :@dispgbr, :r0
|
|
||||||
addop 'mov.l', 0b1101 << 12, :@disppc, :rn
|
|
||||||
addop 'mov.l', 0b0101 << 12, :@disprm, :rn
|
|
||||||
|
|
||||||
addop 'mov.w', 0b0010 << 12 | 0b0001, :rm, :@rn
|
|
||||||
addop 'mov.w', 0b0010 << 12 | 0b0101, :rm, :@_rn
|
|
||||||
addop 'mov.w', 0b0000 << 12 | 0b0101, :rm, :@r0rn
|
|
||||||
addop 'mov.w', 0b11000001 << 8, :r0, :@dispgbr
|
|
||||||
addop 'mov.w', 0b10000001 << 8, :r0, :@dispr0rn
|
|
||||||
addop 'mov.w', 0b0110 << 12 | 0b0001, :@rm, :rn
|
|
||||||
addop 'mov.w', 0b0110 << 12 | 0b0101, :@rm_, :rn
|
|
||||||
addop 'mov.w', 0b0000 << 12 | 0b1101, :@r0rm, :rn
|
|
||||||
addop 'mov.w', 0b11000101 << 8, :@dispgbr, :r0
|
|
||||||
addop 'mov.w', 0b1001 << 12, :@disppc, :rn
|
|
||||||
addop 'mov.w', 0b10000101 << 8, :@disprm, :r0
|
|
||||||
|
|
||||||
addop 'mova', 0b11000111 << 8, :disppc, :r0 # calculates an effective address using PC-relative with displacement addressing
|
|
||||||
addop 'movca.l', 0b0000 << 12 | 11000011, :r0, :@rn # stores the long-word in R0 to memory at the effective address specified in Rn.
|
|
||||||
|
|
||||||
addop 'movt', 0b0000 << 12 | 0b00101001, :rn # copies the T-bit to Rn
|
|
||||||
|
|
||||||
addop 'mul.l', 0b0000 << 12 | 0b0111, :rm, :rn
|
|
||||||
addop 'muls.w', 0b0010 << 12 | 0b1111, :rm, :rn
|
|
||||||
addop 'mulu.w', 0b0010 << 12 | 0b1110, :rm, :rn
|
|
||||||
|
|
||||||
addop 'neg', 0b0110 << 12 | 0b1011, :rm, :rn
|
|
||||||
addop 'negc', 0b0110 << 12 | 0b1010, :rm, :rn
|
|
||||||
|
|
||||||
addop 'nop', 0b0000000000001001
|
|
||||||
|
|
||||||
addop 'not', 0b0110 << 12 | 0b0111, :rm, :rn
|
|
||||||
|
|
||||||
addop 'ocbi', 0b0000 << 12 | 0b10010011, :@rn # invalidates an operand cache block
|
|
||||||
addop 'ocbp', 0b0000 << 12 | 0b10100011, :@rn # purges an operand cache block
|
|
||||||
addop 'ocbwb', 0b0000 << 12 | 0b10110011, :@rn # write-backs an operand cache block
|
|
||||||
|
|
||||||
addop 'or', 0b0010 << 12 | 0b1011, :rm, :rn
|
|
||||||
addop 'or', 0b11001011 << 8, :i8, :r0
|
|
||||||
addop 'or.b', 0b11001111 << 8, :i8, :@r0gbr
|
|
||||||
|
|
||||||
addop 'pref', 0b0000 | 0b10000011, :@rn # indicates a software-directed data prefetch
|
|
||||||
|
|
||||||
addop 'rotcl', 0b0100 | 0b00100100, :rn
|
|
||||||
addop 'rotcr', 0b0100 | 0b00100101, :rn
|
|
||||||
addop 'rotl', 0b0100 | 0b00000100, :rn
|
|
||||||
addop 'rotr', 0b0100 | 0b00000101, :rn
|
|
||||||
|
|
||||||
addop 'rte', 0b0000000000101011, :setip, :stopexec, :delay_slot # returns from an exception or interrupt handling routine, privileged instruction
|
|
||||||
addop 'rts', 0b0000000000001011, :setip, :stopexec, :delay_slot # returns from a subroutine
|
|
||||||
|
|
||||||
addop 'sets', 0b0000000001011000
|
|
||||||
addop 'sett', 0b0000000000011000
|
|
||||||
|
|
||||||
addop 'shad', 0b0100 << 12 | 0b1100, :rm, :rn
|
|
||||||
addop 'shal', 0b0100 << 12 | 0b00100000, :rn
|
|
||||||
addop 'shar', 0b0100 << 12 | 0b00100001, :rn
|
|
||||||
addop 'shld', 0b0100 << 12 | 0b1101, :rm, :rn
|
|
||||||
addop 'shll', 0b0100 << 12 | 0b00000000, :rn
|
|
||||||
addop 'shll2', 0b0100 << 12 | 0b00001000, :rn
|
|
||||||
addop 'shll8', 0b0100 << 12 | 0b00011000, :rn
|
|
||||||
addop 'shll16', 0b0100 << 12 | 0b00101000, :rn
|
|
||||||
addop 'shlr', 0b0100 << 12 | 0b00000001, :rn
|
|
||||||
addop 'shlr2', 0b0100 << 12 | 0b00001001, :rn
|
|
||||||
addop 'shlr8', 0b0100 << 12 | 0b00011001, :rn
|
|
||||||
addop 'shlr16', 0b0100 << 12 | 0b00101001, :rn
|
|
||||||
|
|
||||||
addop 'sleep', 0b0000000000011011 # privileged instruction
|
|
||||||
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b00000010, :sr, :rn
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b00100010, :vbr, :rn
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b00110010, :ssr, :rn
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b01000010, :spc, :rn
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b00111010, :sgr, :rn
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b11111010, :dbr, :rn
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b1 << 7 | 0b0010, :rm_bank, :@_rn
|
|
||||||
addop 'stc', 0b0000 << 12 | 0b00010010, :gbr, :rn
|
|
||||||
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b00000011, :sr, :@_rn
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b00100011, :vbr, :@_rn
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b00110011, :ssr, :@_rn
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b01000011, :spc, :@_rn
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b00110010, :sgr, :@_rn
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b11110010, :dbr, :@_rn
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b1 << 7 | 0b0011, :rm_bank, :@_rn
|
|
||||||
addop 'stc.l', 0b0100 << 12 | 0b00010011, :gbr, :@_rn
|
|
||||||
|
|
||||||
addop 'sts', 0b0000 << 12 | 0b01101010, :fpscr, :rn
|
|
||||||
addop 'sts.l', 0b0100 << 12 | 0b01100010, :fpscr, :@_rn
|
|
||||||
addop 'sts', 0b0000 << 12 | 0b01011010, :fpul, :rn
|
|
||||||
addop 'sts.l', 0b0100 << 12 | 0b01010010, :fpul, :@_rn
|
|
||||||
addop 'sts', 0b0000 << 12 | 0b00001010, :mach, :rn
|
|
||||||
addop 'sts.l', 0b0100 << 12 | 0b00000010, :mach, :@_rn
|
|
||||||
addop 'sts', 0b0000 << 12 | 0b00011010, :macl, :rn
|
|
||||||
addop 'sts.l', 0b0100 << 12 | 0b00010010, :macl, :@_rn
|
|
||||||
addop 'sts', 0b0000 << 12 | 0b00101010, :pr, :rn
|
|
||||||
addop 'sts.l', 0b0100 << 12 | 0b00100010, :pr, :@_rn
|
|
||||||
|
|
||||||
addop 'sub', 0b0011 << 12 | 0b1000, :rm, :rn
|
|
||||||
addop 'subc', 0b0011 << 12 | 0b1010, :rm, :rn
|
|
||||||
addop 'subv', 0b0011 << 12 | 0b1011, :rm, :rn
|
|
||||||
|
|
||||||
addop 'swap.b', 0b0110 << 12 | 0b1000, :rm, :rn
|
|
||||||
addop 'swap.w', 0b0110 << 12 | 0b1001, :rm, :rn
|
|
||||||
|
|
||||||
addop 'tas.b', 0b0100 << 12 | 0b00011011, :@rn
|
|
||||||
addop 'trapa', 0b11000011 << 8, :i8, :setip, :stopexec # This instruction causes a pre-execution trap.
|
|
||||||
|
|
||||||
addop 'tst', 0b0010 << 12 | 0b1000, :rm, :rn
|
|
||||||
addop 'tst', 0b11001000 << 8, :i8, :r0
|
|
||||||
addop 'tst.b', 0b11001100 << 8, :i8, :@r0gbr
|
|
||||||
|
|
||||||
addop 'xor', 0b0010 << 12 | 0b1010, :rm, :rn
|
|
||||||
addop 'xor', 0b11001010 << 8, :i8, :r0
|
|
||||||
addop 'xob.b', 0b11001110 << 8, :i8, :@r0gbr
|
|
||||||
|
|
||||||
addop 'xtrct', 0b0010 << 12 | 0b1101, :rm, :rn
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,15 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
class Metasm::X86_64 < Metasm::Ia32
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/x86_64/parse'
|
|
||||||
require 'metasm/cpu/x86_64/encode'
|
|
||||||
require 'metasm/cpu/x86_64/decode'
|
|
||||||
require 'metasm/cpu/x86_64/render'
|
|
||||||
require 'metasm/cpu/x86_64/debug'
|
|
||||||
require 'metasm/cpu/x86_64/compile_c'
|
|
File diff suppressed because it is too large
Load Diff
@ -1,59 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/x86_64/opcodes'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class X86_64
|
|
||||||
def dbg_register_pc
|
|
||||||
@dbg_register_pc ||= :rip
|
|
||||||
end
|
|
||||||
def dbg_register_flags
|
|
||||||
@dbg_register_flags ||= :rflags
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_list
|
|
||||||
@dbg_register_list ||= [:rax, :rbx, :rcx, :rdx, :rsi, :rdi, :rbp, :rsp, :r8, :r9, :r10, :r11, :r12, :r13, :r14, :r15, :rip]
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_register_size
|
|
||||||
@dbg_register_size ||= Hash.new(64).update(:cs => 16, :ds => 16, :es => 16, :fs => 16, :gs => 16)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dbg_func_arg(dbg, argnr)
|
|
||||||
if dbg.class.name =~ /win/i
|
|
||||||
list = [:rcx, :rdx, :r8, :r9]
|
|
||||||
off = 0x20
|
|
||||||
else
|
|
||||||
list = [:rdi, :rsi, :rdx, :rcx, :r8, :r9]
|
|
||||||
off = 0
|
|
||||||
end
|
|
||||||
if r = list[argnr]
|
|
||||||
dbg.get_reg_value(r)
|
|
||||||
else
|
|
||||||
argnr -= list.length
|
|
||||||
dbg.memory_read_int(Expression[:esp, :+, off + 8 + 8*argnr])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def dbg_func_arg_set(dbg, argnr, arg)
|
|
||||||
if dbg.class.name =~ /win/i
|
|
||||||
list = [:rcx, :rdx, :r8, :r9]
|
|
||||||
off = 0x20
|
|
||||||
else
|
|
||||||
list = [:rdi, :rsi, :rdx, :rcx, :r8, :r9]
|
|
||||||
off = 0
|
|
||||||
end
|
|
||||||
if r = list[argnr]
|
|
||||||
dbg.set_reg_value(r, arg)
|
|
||||||
else
|
|
||||||
argnr -= list.length
|
|
||||||
dbg.memory_write_int(Expression[:esp, :+, off + 8 + 8*argnr], arg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# what's left is inherited from Ia32
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,311 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/x86_64/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class X86_64
|
|
||||||
class ModRM
|
|
||||||
def self.decode(edata, byte, endianness, adsz, opsz, seg=nil, regclass=Reg, pfx={})
|
|
||||||
m = (byte >> 6) & 3
|
|
||||||
rm = byte & 7
|
|
||||||
|
|
||||||
if m == 3
|
|
||||||
rm |= 8 if pfx[:rex_b] and (regclass != SimdReg or opsz != 64) # mm8 -> mm0
|
|
||||||
return regclass.new(rm, opsz)
|
|
||||||
end
|
|
||||||
|
|
||||||
adsz ||= 64
|
|
||||||
|
|
||||||
# mod 0/1/2 m 4 => sib
|
|
||||||
# mod 0 m 5 => rip+imm
|
|
||||||
# sib: i 4 => no index, b 5 => no base
|
|
||||||
|
|
||||||
s = i = b = imm = nil
|
|
||||||
if rm == 4
|
|
||||||
sib = edata.get_byte.to_i
|
|
||||||
|
|
||||||
ii = (sib >> 3) & 7
|
|
||||||
ii |= 8 if pfx[:rex_x]
|
|
||||||
if ii != 4
|
|
||||||
s = 1 << ((sib >> 6) & 3)
|
|
||||||
if pfx[:mrmvex]
|
|
||||||
i = SimdReg.new(ii, pfx[:mrmvex])
|
|
||||||
else
|
|
||||||
i = Reg.new(ii, adsz)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
bb = sib & 7
|
|
||||||
if bb == 5 and m == 0
|
|
||||||
m = 2 # :i32 follows
|
|
||||||
else
|
|
||||||
bb |= 8 if pfx[:rex_b]
|
|
||||||
b = Reg.new(bb, adsz)
|
|
||||||
end
|
|
||||||
elsif rm == 5 and m == 0
|
|
||||||
b = Reg.new(16, adsz)
|
|
||||||
m = 2 # :i32 follows
|
|
||||||
else
|
|
||||||
rm |= 8 if pfx[:rex_b]
|
|
||||||
b = Reg.new(rm, adsz)
|
|
||||||
end
|
|
||||||
|
|
||||||
case m
|
|
||||||
when 1; itype = :i8
|
|
||||||
when 2; itype = :i32
|
|
||||||
end
|
|
||||||
imm = Expression[edata.decode_imm(itype, endianness)] if itype
|
|
||||||
|
|
||||||
if imm and imm.reduce.kind_of? Integer and imm.reduce < -0x100_0000
|
|
||||||
# probably a base address -> unsigned
|
|
||||||
imm = Expression[imm.reduce & ((1 << adsz) - 1)]
|
|
||||||
end
|
|
||||||
|
|
||||||
opsz = pfx[:argsz] if pfx[:argsz]
|
|
||||||
new adsz, opsz, s, i, b, imm, seg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_prefix(instr, byte)
|
|
||||||
x = super(instr, byte)
|
|
||||||
if instr.prefix.delete :rex
|
|
||||||
# rex ignored if not last
|
|
||||||
instr.prefix.delete :rex_b
|
|
||||||
instr.prefix.delete :rex_x
|
|
||||||
instr.prefix.delete :rex_r
|
|
||||||
instr.prefix.delete :rex_w
|
|
||||||
end
|
|
||||||
if byte & 0xf0 == 0x40
|
|
||||||
x = instr.prefix[:rex] = byte
|
|
||||||
instr.prefix[:rex_b] = 1 if byte & 1 > 0
|
|
||||||
instr.prefix[:rex_x] = 1 if byte & 2 > 0
|
|
||||||
instr.prefix[:rex_r] = 1 if byte & 4 > 0
|
|
||||||
instr.prefix[:rex_w] = 1 if byte & 8 > 0
|
|
||||||
end
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
bseq = edata.read(op.bin.length).unpack('C*') # decode_findopcode ensures that data >= op.length
|
|
||||||
pfx = di.instruction.prefix || {}
|
|
||||||
|
|
||||||
field_val = lambda { |f|
|
|
||||||
if fld = op.fields[f]
|
|
||||||
(bseq[fld[0]] >> fld[1]) & @fields_mask[f]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
field_val_r = lambda { |f|
|
|
||||||
v = field_val[f]
|
|
||||||
v |= 8 if v and (op.fields[f][1] == 3 ? pfx[:rex_r] : pfx[:rex_b]) # gruick ?
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
pfx[:rex_r] = 1 if op.fields[:vex_r] and field_val[:vex_r] == 0
|
|
||||||
pfx[:rex_b] = 1 if op.fields[:vex_b] and field_val[:vex_b] == 0
|
|
||||||
pfx[:rex_x] = 1 if op.fields[:vex_x] and field_val[:vex_x] == 0
|
|
||||||
pfx[:rex_w] = 1 if op.fields[:vex_w] and field_val[:vex_w] == 1
|
|
||||||
di.instruction.prefix = pfx if not di.instruction.prefix and not pfx.empty? # for opsz(di) + vex_w
|
|
||||||
|
|
||||||
case op.props[:needpfx]
|
|
||||||
when 0x66; pfx.delete :opsz
|
|
||||||
when 0x67; pfx.delete :adsz
|
|
||||||
when 0xF2, 0xF3; pfx.delete :rep
|
|
||||||
end
|
|
||||||
|
|
||||||
if op.props[:setip] and not op.props[:stopexec] and pfx[:seg]
|
|
||||||
case pfx.delete(:seg).val
|
|
||||||
when 1; pfx[:jmphint] = 'hintnojmp'
|
|
||||||
when 3; pfx[:jmphint] = 'hintjmp'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
opsz = op.props[:argsz] || opsz(di)
|
|
||||||
adsz = pfx[:adsz] ? 32 : 64
|
|
||||||
mmxsz = (op.props[:xmmx] && pfx[:opsz]) ? 128 : 64
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :reg; Reg.new field_val_r[a], opsz
|
|
||||||
when :eeec; CtrlReg.new field_val_r[a]
|
|
||||||
when :eeed; DbgReg.new field_val_r[a]
|
|
||||||
when :eeet; TstReg.new field_val_r[a]
|
|
||||||
when :seg2, :seg2A, :seg3, :seg3A; SegReg.new field_val[a]
|
|
||||||
when :regmmx; SimdReg.new field_val[a], mmxsz # rex_r ignored
|
|
||||||
when :regxmm; SimdReg.new field_val_r[a], 128
|
|
||||||
when :regymm; SimdReg.new field_val_r[a], 256
|
|
||||||
|
|
||||||
when :farptr; Farptr.decode edata, @endianness, opsz
|
|
||||||
when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64; Expression[edata.decode_imm(a, @endianness)]
|
|
||||||
when :i # 64bit constants are sign-extended from :i32
|
|
||||||
type = (opsz == 64 ? op.props[:imm64] ? :a64 : :i32 : "#{op.props[:unsigned_imm] ? 'a' : 'i'}#{opsz}".to_sym )
|
|
||||||
v = edata.decode_imm(type, @endianness)
|
|
||||||
v &= 0xffff_ffff_ffff_ffff if opsz == 64 and op.props[:unsigned_imm] and v.kind_of? Integer
|
|
||||||
Expression[v]
|
|
||||||
|
|
||||||
when :mrm_imm; ModRM.new(adsz, opsz, nil, nil, nil, Expression[edata.decode_imm("a#{adsz}".to_sym, @endianness)], pfx.delete(:seg))
|
|
||||||
when :modrm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, opsz, pfx.delete(:seg), Reg, pfx
|
|
||||||
when :modrmmmx; ModRM.decode edata, field_val[:modrm], @endianness, adsz, mmxsz, pfx.delete(:seg), SimdReg, pfx.merge(:argsz => op.props[:argsz])
|
|
||||||
when :modrmxmm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 128, pfx.delete(:seg), SimdReg, pfx.merge(:argsz => op.props[:argsz], :mrmvex => op.props[:mrmvex])
|
|
||||||
when :modrmymm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 256, pfx.delete(:seg), SimdReg, pfx.merge(:argsz => op.props[:argsz], :mrmvex => op.props[:mrmvex])
|
|
||||||
|
|
||||||
when :vexvreg; Reg.new((field_val[:vex_vvvv] ^ 0xf), opsz)
|
|
||||||
when :vexvxmm; SimdReg.new((field_val[:vex_vvvv] ^ 0xf), 128)
|
|
||||||
when :vexvymm; SimdReg.new((field_val[:vex_vvvv] ^ 0xf), 256)
|
|
||||||
when :i4xmm; SimdReg.new(edata.decode_imm(:u8, @endianness) >> 4, 128)
|
|
||||||
when :i4ymm; SimdReg.new(edata.decode_imm(:u8, @endianness) >> 4, 256)
|
|
||||||
|
|
||||||
when :regfp; FpReg.new field_val[a]
|
|
||||||
when :imm_val1; Expression[1]
|
|
||||||
when :imm_val3; Expression[3]
|
|
||||||
when :reg_cl; Reg.new 1, 8
|
|
||||||
when :reg_eax; Reg.new 0, opsz
|
|
||||||
when :reg_dx; Reg.new 2, 16
|
|
||||||
when :regfp0; FpReg.new nil
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
return if edata.ptr > edata.length
|
|
||||||
|
|
||||||
if op.name == 'movsx' or op.name == 'movzx' or op.name == 'movsxd'
|
|
||||||
if op.name == 'movsxd'
|
|
||||||
di.instruction.args[1].sz = 32
|
|
||||||
elsif opsz == 8
|
|
||||||
di.instruction.args[1].sz = 8
|
|
||||||
else
|
|
||||||
di.instruction.args[1].sz = 16
|
|
||||||
end
|
|
||||||
if pfx[:rex_w]
|
|
||||||
di.instruction.args[0].sz = 64
|
|
||||||
elsif pfx[:opsz]
|
|
||||||
di.instruction.args[0].sz = 16
|
|
||||||
else
|
|
||||||
di.instruction.args[0].sz = 32
|
|
||||||
end
|
|
||||||
elsif op.name == 'crc32'
|
|
||||||
di.instruction.args[0].sz = 32
|
|
||||||
end
|
|
||||||
|
|
||||||
# sil => bh
|
|
||||||
di.instruction.args.each { |a| a.val += 12 if a.kind_of? Reg and a.sz == 8 and not pfx[:rex] and a.val >= 4 and a.val <= 8 }
|
|
||||||
|
|
||||||
case pfx.delete(:rep)
|
|
||||||
when :nz
|
|
||||||
if di.opcode.props[:strop]
|
|
||||||
pfx[:rep] = 'rep'
|
|
||||||
elsif di.opcode.props[:stropz]
|
|
||||||
pfx[:rep] = 'repnz'
|
|
||||||
end
|
|
||||||
when :z
|
|
||||||
if di.opcode.props[:strop]
|
|
||||||
pfx[:rep] = 'rep'
|
|
||||||
elsif di.opcode.props[:stropz]
|
|
||||||
pfx[:rep] = 'repz'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
super(di, addr)
|
|
||||||
|
|
||||||
# [rip + 42] => [rip - addr + foo]
|
|
||||||
if m = di.instruction.args.grep(ModRM).first and
|
|
||||||
((m.b and m.b.val == 16) or (m.i and m.i.val == 16)) and
|
|
||||||
m.imm and m.imm.reduce.kind_of?(Integer)
|
|
||||||
m.imm = Expression[[:-, di.address + di.bin_length], :+, di.address+di.bin_length+m.imm.reduce]
|
|
||||||
end
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
def opsz(di, op=nil)
|
|
||||||
if di and di.instruction.prefix and di.instruction.prefix[:rex_w]; 64
|
|
||||||
elsif di and di.instruction.prefix and di.instruction.prefix[:opsz] and (op || di.opcode).props[:needpfx] != 0x66; 16
|
|
||||||
elsif di and (op || di.opcode).props[:auto64]; 64
|
|
||||||
else 32
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def adsz(di, op=nil)
|
|
||||||
if di and di.instruction.prefix and di.instruction.prefix[:adsz] and (op || di.opcode).props[:needpfx] != 0x67; 32
|
|
||||||
else 64
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def register_symbols
|
|
||||||
[:rax, :rcx, :rdx, :rbx, :rsp, :rbp, :rsi, :rdi, :r8, :r9, :r10, :r11, :r12, :r13, :r14, :r15]
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns a DecodedFunction from a parsed C function prototype
|
|
||||||
def decode_c_function_prototype(cp, sym, orig=nil)
|
|
||||||
sym = cp.toplevel.symbol[sym] if sym.kind_of?(::String)
|
|
||||||
df = DecodedFunction.new
|
|
||||||
orig ||= Expression[sym.name]
|
|
||||||
|
|
||||||
new_bt = lambda { |expr, rlen|
|
|
||||||
df.backtracked_for << BacktraceTrace.new(expr, orig, expr, rlen ? :r : :x, rlen)
|
|
||||||
}
|
|
||||||
|
|
||||||
# return instr emulation
|
|
||||||
if sym.has_attribute 'noreturn' or sym.has_attribute '__noreturn__'
|
|
||||||
df.noreturn = true
|
|
||||||
else
|
|
||||||
new_bt[Indirection[:rsp, @size/8, orig], nil]
|
|
||||||
end
|
|
||||||
|
|
||||||
# register dirty (MS standard ABI)
|
|
||||||
[:rax, :rcx, :rdx, :r8, :r9, :r10, :r11].each { |r|
|
|
||||||
df.backtrace_binding.update r => Expression::Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
if cp.lexer.definition['__MS_X86_64_ABI__']
|
|
||||||
reg_args = [:rcx, :rdx, :r8, :r9]
|
|
||||||
else
|
|
||||||
reg_args = [:rdi, :rsi, :rdx, :rcx, :r8, :r9]
|
|
||||||
end
|
|
||||||
|
|
||||||
al = cp.typesize[:ptr]
|
|
||||||
df.backtrace_binding[:rsp] = Expression[:rsp, :+, al]
|
|
||||||
|
|
||||||
# scan args for function pointers
|
|
||||||
# TODO walk structs/unions..
|
|
||||||
stackoff = al
|
|
||||||
sym.type.args.to_a.zip(reg_args).each { |a, r|
|
|
||||||
if not r
|
|
||||||
r = Indirection[[:rsp, :+, stackoff], al, orig]
|
|
||||||
stackoff += (cp.sizeof(a) + al - 1) / al * al
|
|
||||||
end
|
|
||||||
if a.type.untypedef.kind_of? C::Pointer
|
|
||||||
pt = a.type.untypedef.type.untypedef
|
|
||||||
if pt.kind_of? C::Function
|
|
||||||
new_bt[r, nil]
|
|
||||||
df.backtracked_for.last.detached = true
|
|
||||||
elsif pt.kind_of? C::Struct
|
|
||||||
new_bt[r, al]
|
|
||||||
else
|
|
||||||
new_bt[r, cp.sizeof(nil, pt)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
df
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_update_function_binding_check(dasm, faddr, f, b)
|
|
||||||
# TODO save regs according to ABI
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,293 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/x86_64/opcodes'
|
|
||||||
require 'metasm/encode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class X86_64
|
|
||||||
class ModRM
|
|
||||||
def self.encode_reg(reg, mregval = 0)
|
|
||||||
v = reg.kind_of?(Reg) ? reg.val_enc : reg.val & 7
|
|
||||||
0xc0 | (mregval << 3) | v
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode(reg = 0, endianness = :little)
|
|
||||||
reg = reg.val if reg.kind_of? Ia32::Argument
|
|
||||||
|
|
||||||
ret = EncodedData.new << (reg << 3)
|
|
||||||
|
|
||||||
# add bits in the first octet of ret.data (1.9 compatibility layer)
|
|
||||||
or_bits = lambda { |v| # rape me
|
|
||||||
if ret.data[0].kind_of? Integer
|
|
||||||
ret.data[0] |= v
|
|
||||||
else
|
|
||||||
ret.data[0] = (ret.data[0].unpack('C').first | v).chr
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if not self.b and not self.i
|
|
||||||
# imm only, use sib
|
|
||||||
or_bits[4]
|
|
||||||
imm = self.imm || Expression[0]
|
|
||||||
[ret << ((4 << 3) | 5) << imm.encode(:i32, endianness)]
|
|
||||||
|
|
||||||
elsif (self.b and self.b.val == 16) or (self.i and self.i.val == 16) # rip+imm (rip == addr of the octet after the current instr)
|
|
||||||
# should have been filtered by #parse, but just in case
|
|
||||||
raise "invalid rip addressing #{self}" if (self.i and self.b) or (self.s and self.s != 1)
|
|
||||||
or_bits[5]
|
|
||||||
imm = self.imm || Expression[0]
|
|
||||||
[ret << imm.encode(:i32, endianness)]
|
|
||||||
|
|
||||||
elsif not self.b and self.s != 1
|
|
||||||
# sib with no b
|
|
||||||
raise EncodeError, "Invalid ModRM #{self}" if @i.val == 4 # XXX 12 ?
|
|
||||||
or_bits[4]
|
|
||||||
s = {8=>3, 4=>2, 2=>1}[@s]
|
|
||||||
imm = self.imm || Expression[0]
|
|
||||||
fu = (s << 6) | (@i.val_enc << 3) | 5
|
|
||||||
fu = fu.chr if s >= 2 # rb1.9 encoding fix
|
|
||||||
[ret << fu << imm.encode(:i32, endianness)]
|
|
||||||
else
|
|
||||||
imm = @imm.reduce if self.imm
|
|
||||||
imm = nil if imm == 0
|
|
||||||
|
|
||||||
if not self.i or (not self.b and self.s == 1)
|
|
||||||
# no sib byte (except for [esp])
|
|
||||||
@s, @i, @b = nil, nil, @s if not self.b
|
|
||||||
or_bits[@b.val_enc]
|
|
||||||
ret << 0x24 if @b.val_enc == 4 # XXX val_enc ?
|
|
||||||
else
|
|
||||||
# sib
|
|
||||||
or_bits[4]
|
|
||||||
|
|
||||||
@b, @i = @i, @b if @s == 1 and @i.kind_of?(Reg) and (@i.val_enc == 4 or @b.val_enc == 5)
|
|
||||||
|
|
||||||
raise EncodeError, "Invalid ModRM #{self}" if @i.val == 4
|
|
||||||
|
|
||||||
s = {8=>3, 4=>2, 2=>1, 1=>0}[@s]
|
|
||||||
fu = (s << 6) | (@i.val_enc << 3) | @b.val_enc
|
|
||||||
fu = fu.chr if s >= 2 # rb1.9 encoding fix
|
|
||||||
ret << fu
|
|
||||||
end
|
|
||||||
|
|
||||||
imm ||= 0 if @b.val_enc == 5
|
|
||||||
if imm
|
|
||||||
case Expression.in_range?(imm, :i8)
|
|
||||||
when true
|
|
||||||
or_bits[1<<6]
|
|
||||||
[ret << Expression.encode_imm(imm, :i8, endianness)]
|
|
||||||
when false
|
|
||||||
or_bits[2<<6]
|
|
||||||
[ret << Expression.encode_imm(imm, :a32, endianness)]
|
|
||||||
when nil
|
|
||||||
rets = ret.dup
|
|
||||||
or_bits[1<<6]
|
|
||||||
ret << @imm.encode(:i8, endianness)
|
|
||||||
rets, ret = ret, rets # or_bits[] modifies ret directly
|
|
||||||
or_bits[2<<6]
|
|
||||||
ret << @imm.encode(:a32, endianness)
|
|
||||||
[ret, rets]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
[ret]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns all forms of the encoding of instruction i using opcode op
|
|
||||||
# program may be used to create a new label for relative jump/call
|
|
||||||
def encode_instr_op(program, i, op)
|
|
||||||
base = op.bin.dup
|
|
||||||
oi = op.args.zip(i.args)
|
|
||||||
set_field = lambda { |f, v|
|
|
||||||
fld = op.fields[f]
|
|
||||||
base[fld[0]] |= v << fld[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# handle prefixes and bit fields
|
|
||||||
#
|
|
||||||
pfx = i.prefix.map { |k, v|
|
|
||||||
case k
|
|
||||||
when :jmp; {:jmp => 0x3e, :nojmp => 0x2e}[v]
|
|
||||||
when :lock; 0xf0
|
|
||||||
when :rep; {'repnz' => 0xf2, 'repz' => 0xf3, 'rep' => 0xf2}[v]
|
|
||||||
when :jmphint; {'hintjmp' => 0x3e, 'hintnojmp' => 0x2e}[v]
|
|
||||||
when :seg; [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][v.val]
|
|
||||||
end
|
|
||||||
}.compact.pack 'C*'
|
|
||||||
|
|
||||||
rex_w = rex_r = rex_x = rex_b = 0
|
|
||||||
if op.name == 'movsx' or op.name == 'movzx' or op.name == 'movsxd'
|
|
||||||
case i.args[0].sz
|
|
||||||
when 64; rex_w = 1
|
|
||||||
when 32
|
|
||||||
when 16; pfx << 0x66
|
|
||||||
end
|
|
||||||
elsif op.name == 'crc32'
|
|
||||||
case i.args[1].sz
|
|
||||||
when 64; rex_w = 1
|
|
||||||
when 32;
|
|
||||||
when 16; pfx << 0x66
|
|
||||||
end
|
|
||||||
else
|
|
||||||
opsz = op.props[:argsz] || i.prefix[:sz]
|
|
||||||
oi.each { |oa, ia|
|
|
||||||
case oa
|
|
||||||
when :reg, :reg_eax, :modrm, :mrm_imm
|
|
||||||
raise EncodeError, "Incompatible arg size in #{i}" if ia.sz and opsz and opsz != ia.sz
|
|
||||||
opsz = ia.sz
|
|
||||||
end
|
|
||||||
}
|
|
||||||
opsz ||= 64 if op.props[:auto64]
|
|
||||||
opsz = op.props[:opsz] if op.props[:opsz] # XXX ?
|
|
||||||
case opsz
|
|
||||||
when 64; rex_w = 1 if not op.props[:auto64] and (not op.props[:argsz] or op.props[:opsz] == 64)
|
|
||||||
when 32; raise EncodeError, "Incompatible arg size in #{i}" if op.props[:auto64]
|
|
||||||
when 16; pfx << 0x66
|
|
||||||
end
|
|
||||||
end
|
|
||||||
opsz ||= @size
|
|
||||||
|
|
||||||
# addrsize override / segment override / rex_bx
|
|
||||||
if mrm = i.args.grep(ModRM).first
|
|
||||||
mrm.encode(0, @endianness) if mrm.b or mrm.i # may reorder b/i, which must be correct for rex
|
|
||||||
rex_b = 1 if mrm.b and mrm.b.val_rex.to_i > 0
|
|
||||||
rex_x = 1 if mrm.i and mrm.i.val_rex.to_i > 0
|
|
||||||
pfx << 0x67 if (mrm.b and mrm.b.sz == 32) or (mrm.i and mrm.i.sz == 32) or op.props[:adsz] == 32
|
|
||||||
pfx << [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][mrm.seg.val] if mrm.seg
|
|
||||||
elsif op.props[:adsz] == 32
|
|
||||||
pfx << 0x67
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# encode embedded arguments
|
|
||||||
#
|
|
||||||
postponed = []
|
|
||||||
oi.each { |oa, ia|
|
|
||||||
case oa
|
|
||||||
when :reg
|
|
||||||
set_field[oa, ia.val_enc]
|
|
||||||
if op.fields[:reg][1] == 3
|
|
||||||
rex_r = ia.val_rex || 0
|
|
||||||
else
|
|
||||||
rex_b = ia.val_rex || 0
|
|
||||||
end
|
|
||||||
when :seg3, :seg3A, :seg2, :seg2A, :eeec, :eeed, :eeet, :regfp, :regmmx, :regxmm, :regymm
|
|
||||||
set_field[oa, ia.val & 7]
|
|
||||||
rex_r = 1 if ia.val > 7
|
|
||||||
pfx << 0x66 if oa == :regmmx and op.props[:xmmx] and ia.sz == 128
|
|
||||||
when :vexvreg, :vexvxmm, :vexvymm
|
|
||||||
set_field[:vex_vvvv, ia.val ^ 0xf]
|
|
||||||
when :imm_val1, :imm_val3, :reg_cl, :reg_eax, :reg_dx, :regfp0
|
|
||||||
# implicit
|
|
||||||
when :modrm, :modrmmmx, :modrmxmm, :modrmymm
|
|
||||||
# postpone, but we must set rex now
|
|
||||||
case ia
|
|
||||||
when ModRM
|
|
||||||
ia.encode(0, @endianness) # could swap b/i
|
|
||||||
rex_x = ia.i.val_rex || 0 if ia.i
|
|
||||||
rex_b = ia.b.val_rex || 0 if ia.b
|
|
||||||
when Reg
|
|
||||||
rex_b = ia.val_rex || 0
|
|
||||||
else
|
|
||||||
rex_b = ia.val >> 3
|
|
||||||
end
|
|
||||||
postponed << [oa, ia]
|
|
||||||
else
|
|
||||||
postponed << [oa, ia]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if !(op.args & [:modrm, :modrmmmx, :modrmxmm, :modrmymm]).empty?
|
|
||||||
# reg field of modrm
|
|
||||||
regval = (base[-1] >> 3) & 7
|
|
||||||
base.pop
|
|
||||||
end
|
|
||||||
|
|
||||||
# convert label name for jmp/call/loop to relative offset
|
|
||||||
if op.props[:setip] and op.name[0, 3] != 'ret' and i.args.first.kind_of? Expression
|
|
||||||
postlabel = program.new_label('post'+op.name)
|
|
||||||
target = postponed.first[1]
|
|
||||||
target = target.rexpr if target.kind_of? Expression and target.op == :+ and not target.lexpr
|
|
||||||
postponed.first[1] = Expression[target, :-, postlabel]
|
|
||||||
end
|
|
||||||
|
|
||||||
pfx << op.props[:needpfx] if op.props[:needpfx]
|
|
||||||
|
|
||||||
if op.fields[:vex_r]
|
|
||||||
set_field[:vex_r, rex_r ^ 1]
|
|
||||||
set_field[:vex_x, rex_x ^ 1] if op.fields[:vex_x]
|
|
||||||
set_field[:vex_b, rex_b ^ 1] if op.fields[:vex_b]
|
|
||||||
set_field[:vex_w, rex_w] if op.fields[:vex_w]
|
|
||||||
elsif rex_r + rex_x + rex_b + rex_w >= 1 or i.args.grep(Reg).find { |r| r.sz == 8 and r.val >= 4 and r.val < 8 }
|
|
||||||
rex = 0x40
|
|
||||||
rex |= 1 if rex_b == 1
|
|
||||||
rex |= 2 if rex_x == 1
|
|
||||||
rex |= 4 if rex_r == 1
|
|
||||||
rex |= 8 if rex_w == 1
|
|
||||||
pfx << rex
|
|
||||||
end
|
|
||||||
ret = EncodedData.new(pfx + base.pack('C*'))
|
|
||||||
|
|
||||||
postponed.each { |oa, ia|
|
|
||||||
case oa
|
|
||||||
when :modrm, :modrmmmx, :modrmxmm, :modrmymm
|
|
||||||
if ia.kind_of? ModRM
|
|
||||||
ed = ia.encode(regval, @endianness)
|
|
||||||
if ed.kind_of?(::Array)
|
|
||||||
if ed.length > 1
|
|
||||||
# we know that no opcode can have more than 1 modrm
|
|
||||||
ary = []
|
|
||||||
ed.each { |m| ary << (ret.dup << m) }
|
|
||||||
ret = ary
|
|
||||||
next
|
|
||||||
else
|
|
||||||
ed = ed.first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ed = ModRM.encode_reg(ia, regval)
|
|
||||||
end
|
|
||||||
when :mrm_imm; ed = ia.imm.encode("a#{op.props[:adsz] || 64}".to_sym, @endianness)
|
|
||||||
when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64; ed = ia.encode(oa, @endianness)
|
|
||||||
when :i
|
|
||||||
type = if opsz == 64
|
|
||||||
if op.props[:imm64]
|
|
||||||
:a64
|
|
||||||
else
|
|
||||||
if _ia = ia.reduce and _ia.kind_of?(Integer) and _ia > 0 and (_ia >> 63) == 1
|
|
||||||
# handle 0xffffffff_ffffffff -> -1, which should fit in i32
|
|
||||||
ia = Expression[_ia - (1 << 64)]
|
|
||||||
end
|
|
||||||
:i32
|
|
||||||
end
|
|
||||||
else
|
|
||||||
"a#{opsz}".to_sym
|
|
||||||
end
|
|
||||||
ed = ia.encode(type, @endianness)
|
|
||||||
when :i4xmm, :i4ymm
|
|
||||||
ed = ia.val << 4 # u8
|
|
||||||
else raise SyntaxError, "Internal error: want to encode field #{oa.inspect} as arg in #{i}"
|
|
||||||
end
|
|
||||||
|
|
||||||
if ret.kind_of?(::Array)
|
|
||||||
ret.each { |e| e << ed }
|
|
||||||
else
|
|
||||||
ret << ed
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
# we know that no opcode with setip accept both modrm and immediate arg, so ret is not an ::Array
|
|
||||||
ret.add_export(postlabel, ret.virtsize) if postlabel
|
|
||||||
|
|
||||||
ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,147 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/ia32'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
# The x86_64, 64-bit extension of the x86 CPU (x64, em64t, amd64...)
|
|
||||||
class X86_64 < Ia32
|
|
||||||
# FpReg, SegReg, Farptr unchanged
|
|
||||||
|
|
||||||
# XMM extended to 16 regs, YMM
|
|
||||||
class SimdReg < Ia32::SimdReg
|
|
||||||
double_map 64 => (0..7).map { |n| "mm#{n}" },
|
|
||||||
128 => (0..15).map { |n| "xmm#{n}" },
|
|
||||||
256 => (0..15).map { |n| "ymm#{n}" }
|
|
||||||
|
|
||||||
def val_enc
|
|
||||||
@val & 7
|
|
||||||
end
|
|
||||||
|
|
||||||
def val_rex
|
|
||||||
@val >> 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# general purpose registers, all sizes
|
|
||||||
# 8 new gprs (r8..r15), set bit R in the REX prefix to reference them (or X/B if in ModRM)
|
|
||||||
# aonethusaontehsanothe with 8bit subreg: with no rex prefix, refers to ah ch dh bh (as usual)
|
|
||||||
# but whenever the prefix is present, those become unavailable and encodie spl..dil (low byte of rsp/rdi)
|
|
||||||
class Reg < Ia32::Reg
|
|
||||||
double_map 8 => %w{ al cl dl bl spl bpl sil dil r8b r9b r10b r11b r12b r13b r14b r15b ah ch dh bh},
|
|
||||||
16 => %w{ ax cx dx bx sp bp si di r8w r9w r10w r11w r12w r13w r14w r15w},
|
|
||||||
32 => %w{eax ecx edx ebx esp ebp esi edi r8d r9d r10d r11d r12d r13d r14d r15d eip},
|
|
||||||
64 => %w{rax rcx rdx rbx rsp rbp rsi rdi r8 r9 r10 r11 r12 r13 r14 r15 rip}
|
|
||||||
|
|
||||||
Sym = @i_to_s[64].map { |s| s.to_sym }
|
|
||||||
|
|
||||||
# returns a symbolic representation of the register:
|
|
||||||
# cx => :rcx & 0xffff
|
|
||||||
# ah => (:rax >> 8) & 0xff
|
|
||||||
# XXX in x64, 32bits operations are zero-extended to 64bits (eg mov rax, 0x1234_ffff_ffff ; add eax, 1 => rax == 0
|
|
||||||
def symbolic(di=nil)
|
|
||||||
s = Sym[@val]
|
|
||||||
s = di.next_addr if s == :rip and di
|
|
||||||
if @sz == 8 and to_s[-1] == ?h
|
|
||||||
Expression[[Sym[@val-16], :>>, 8], :&, 0xff]
|
|
||||||
elsif @sz == 8
|
|
||||||
Expression[s, :&, 0xff]
|
|
||||||
elsif @sz == 16
|
|
||||||
Expression[s, :&, 0xffff]
|
|
||||||
elsif @sz == 32
|
|
||||||
Expression[s, :&, 0xffffffff]
|
|
||||||
else
|
|
||||||
s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# checks if two registers have bits in common
|
|
||||||
def share?(other)
|
|
||||||
raise 'TODO'
|
|
||||||
# XXX TODO wtf does formula this do ?
|
|
||||||
other.val % (other.sz >> 1) == @val % (@sz >> 1) and (other.sz != @sz or @sz != 8 or other.val == @val)
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the part of @val to encode in an instruction field
|
|
||||||
def val_enc
|
|
||||||
if @sz == 8 and @val >= 16; @val-12 # ah, bh, ch, dh
|
|
||||||
elsif @val >= 16 # rip
|
|
||||||
else @val & 7 # others
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the part of @val to encode in an instruction's rex prefix
|
|
||||||
def val_rex
|
|
||||||
if @sz == 8 and @val >= 16 # ah, bh, ch, dh: rex forbidden
|
|
||||||
elsif @val >= 16 # rip
|
|
||||||
else @val >> 3 # others
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ModRM represents indirections (eg dword ptr [eax+4*ebx+12h])
|
|
||||||
# 16bit mode unavailable in x64
|
|
||||||
# opcodes use 64bit addressing by default, use adsz override (67h) prefix to switch to 32
|
|
||||||
# immediate values are encoded as :i32 sign-extended to 64bits
|
|
||||||
class ModRM < Ia32::ModRM
|
|
||||||
# mod 0/1/2 m 4 => sib
|
|
||||||
# mod 0 m 5 => rip+imm
|
|
||||||
# sib: i 4 => no index, b 5 => no base
|
|
||||||
end
|
|
||||||
|
|
||||||
class DbgReg < Ia32::DbgReg
|
|
||||||
simple_map((0..15).map { |i| [i, "dr#{i}"] })
|
|
||||||
end
|
|
||||||
|
|
||||||
class CtrlReg < Ia32::CtrlReg
|
|
||||||
simple_map((0..15).map { |i| [i, "cr#{i}"] })
|
|
||||||
end
|
|
||||||
|
|
||||||
class TstReg < Ia32::TstReg
|
|
||||||
simple_map((0..15).map { |i| [i, "tr#{i}"] })
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a new instance of an X86 cpu
|
|
||||||
# arguments (any order)
|
|
||||||
# - instruction set (386, 486, sse2...) [latest]
|
|
||||||
# - endianness [:little]
|
|
||||||
def initialize(*a)
|
|
||||||
super(:latest)
|
|
||||||
@size = 64
|
|
||||||
a.delete @size
|
|
||||||
@endianness = (a & [:big, :little]).first || :little
|
|
||||||
a.delete @endianness
|
|
||||||
@family = a.pop || :latest
|
|
||||||
raise "Invalid arguments #{a.inspect}" if not a.empty?
|
|
||||||
raise "Invalid X86_64 family #{@family.inspect}" if not respond_to?("init_#@family")
|
|
||||||
end
|
|
||||||
|
|
||||||
# defines some preprocessor macros to say who we are:
|
|
||||||
# TODO
|
|
||||||
def tune_prepro(pp)
|
|
||||||
super(pp, :itsmeX64) # ask Ia32's to just call super()
|
|
||||||
pp.define_weak('_M_AMD64')
|
|
||||||
pp.define_weak('_M_X64')
|
|
||||||
pp.define_weak('__amd64__')
|
|
||||||
pp.define_weak('__x86_64__')
|
|
||||||
end
|
|
||||||
|
|
||||||
def str_to_reg(str)
|
|
||||||
# X86_64::Reg != Ia32::Reg
|
|
||||||
Reg.s_to_i.has_key?(str) ? Reg.from_str(str) : SimdReg.s_to_i.has_key?(str) ? SimdReg.from_str(str) : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def shortname
|
|
||||||
"x64#{'_be' if @endianness == :big}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
X64 = X86_64
|
|
||||||
AMD64 = X86_64
|
|
||||||
|
|
||||||
end
|
|
@ -1,136 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/x86_64/main'
|
|
||||||
require 'metasm/cpu/ia32/opcodes'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class X86_64
|
|
||||||
def init_cpu_constants
|
|
||||||
super()
|
|
||||||
[:i32, :u32, :i64, :u64].each { |a| @valid_args[a] = true }
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_386_common_only
|
|
||||||
super()
|
|
||||||
# :imm64 => accept a real int64 as :i argument
|
|
||||||
# :auto64 => ignore rex_w, always 64-bit op
|
|
||||||
# :op32no64 => if write to a 32-bit reg, dont zero the top 32-bits of dest
|
|
||||||
[:imm64, :auto64, :op32no64].each { |a| @valid_props[a] = true }
|
|
||||||
@opcode_list.delete_if { |o| o.bin[0].to_i & 0xf0 == 0x40 } # now REX prefix
|
|
||||||
@opcode_list.each { |o|
|
|
||||||
o.props[:imm64] = true if o.bin == [0xB8] # mov reg, <true imm64>
|
|
||||||
o.props[:auto64] = true if o.name =~ /^(j.*|loop.*|call|enter|leave|push|pop|ret)$/
|
|
||||||
}
|
|
||||||
addop 'movsxd', [0x63], :mrm
|
|
||||||
addop('cdqe', [0x98]) { |o| o.props[:opsz] = 64 }
|
|
||||||
addop('cqo', [0x99]) { |o| o.props[:opsz] = 64 }
|
|
||||||
end
|
|
||||||
|
|
||||||
# all x86_64 cpu understand <= sse2 instrs
|
|
||||||
def init_x8664_only
|
|
||||||
init_386_common_only
|
|
||||||
init_386_only
|
|
||||||
init_387_only
|
|
||||||
init_486_only
|
|
||||||
init_pentium_only
|
|
||||||
init_p6_only
|
|
||||||
init_sse_only
|
|
||||||
init_sse2_only
|
|
||||||
|
|
||||||
@opcode_list.delete_if { |o|
|
|
||||||
o.args.include?(:seg2) or
|
|
||||||
o.args.include?(:seg2A) or
|
|
||||||
o.args.include?(:farptr) or
|
|
||||||
%w[aaa aad aam aas bound daa das into jcxz jecxz
|
|
||||||
lds les loadall arpl pusha pushad popa
|
|
||||||
popad].include?(o.name.split('.')[0])
|
|
||||||
# split needed for lds.a32
|
|
||||||
}
|
|
||||||
|
|
||||||
@opcode_list.each { |o|
|
|
||||||
o.props[:auto64] = true if o.name =~ /^(enter|leave|[sl]gdt|[sl]idt|[sl]ldt|[sl]tr|push|pop|syscall)$/
|
|
||||||
}
|
|
||||||
|
|
||||||
addop('cmpxchg16b', [0x0F, 0xC7], 1) { |o| o.props[:opsz] = 64 ; o.props[:argsz] = 128 }
|
|
||||||
addop('iretq', [0xCF], nil, :stopexec, :setip) { |o| o.props[:opsz] = 64 } ; opcode_list.unshift opcode_list.pop
|
|
||||||
addop 'swapgs', [0x0F, 0x01, 0xF8]
|
|
||||||
|
|
||||||
addop('movq', [0x0F, 0x6E], :mrmmmx, {:d => [1, 4]}) { |o| o.args = [:modrm, :regmmx] ; o.props[:opsz] = o.props[:argsz] = 64 }
|
|
||||||
addop('movq', [0x0F, 0x6E], :mrmxmm, {:d => [1, 4]}) { |o| o.args = [:modrm, :regxmm] ; o.props[:opsz] = o.props[:argsz] = 64 ; o.props[:needpfx] = 0x66 }
|
|
||||||
addop('jecxz', [0xE3], nil, :setip, :i8) { |o| o.props[:adsz] = 32 } # actually 16 (cx), but x64 in general says pfx 0x67 => adsz = 32
|
|
||||||
addop('jrcxz', [0xE3], nil, :setip, :i8) { |o| o.props[:adsz] = 64 }
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_sse3
|
|
||||||
init_x8664_only
|
|
||||||
init_sse3_only
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_sse41_only
|
|
||||||
super()
|
|
||||||
addop('pextrq', [0x0F, 0x3A, 0x16], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args[o.args.index(:modrmxmm)] = :modrm; o.props[:opsz] = o.props[:argsz] = 64 }
|
|
||||||
addop('pinsrq', [0x0F, 0x3A, 0x22], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args[o.args.index(:modrmxmm)] = :modrm; o.props[:opsz] = o.props[:argsz] = 64 }
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_avx_only
|
|
||||||
super()
|
|
||||||
addop('rdfsbase', [0x0F, 0xAE], 0, :modrmR) { |o| o.props[:needpfx] = 0xF3 }
|
|
||||||
addop('rdgsbase', [0x0F, 0xAE], 1, :modrmR) { |o| o.props[:needpfx] = 0xF3 }
|
|
||||||
addop('wrfsbase', [0x0F, 0xAE], 2, :modrmR) { |o| o.props[:needpfx] = 0xF3 }
|
|
||||||
addop('wrgsbase', [0x0F, 0xAE], 3, :modrmR) { |o| o.props[:needpfx] = 0xF3 }
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_macrostr(name, bin, type)
|
|
||||||
super(name, bin, type)
|
|
||||||
bin = bin.dup
|
|
||||||
bin[0] |= 1
|
|
||||||
addop(name+'q', bin) { |o| o.props[:opsz] = 64 ; o.props[type] = true }
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_macroret(name, bin, *args)
|
|
||||||
addop(name + '.i64', bin, nil, :stopexec, :setip, *args) { |o| o.props[:opsz] = 64 }
|
|
||||||
super(name, bin, *args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_post(op)
|
|
||||||
if op.fields[:d] or op.fields[:w] or op.fields[:s] or op.args.first == :regfp0
|
|
||||||
return super(op)
|
|
||||||
end
|
|
||||||
|
|
||||||
if op.props[:needpfx]
|
|
||||||
@opcode_list.unshift op
|
|
||||||
else
|
|
||||||
@opcode_list << op
|
|
||||||
end
|
|
||||||
|
|
||||||
if op.args == [:i] or op.name == 'ret'
|
|
||||||
# define opsz-override version for ambiguous opcodes
|
|
||||||
op16 = op.dup
|
|
||||||
op16.name << '.i16'
|
|
||||||
op16.props[:opsz] = 16
|
|
||||||
@opcode_list << op16
|
|
||||||
# push call ret jz can't 32bit
|
|
||||||
op64 = op.dup
|
|
||||||
op64.name << '.i64'
|
|
||||||
op64.props[:opsz] = 64
|
|
||||||
@opcode_list << op64
|
|
||||||
elsif op.props[:strop] or op.props[:stropz] or op.args.include? :mrm_imm or
|
|
||||||
op.args.include? :modrm or op.name =~ /loop|xlat/
|
|
||||||
# define adsz-override version for ambiguous opcodes (movsq)
|
|
||||||
# XXX loop pfx 67 = rip+ecx, 66/rex ignored
|
|
||||||
op32 = op.dup
|
|
||||||
op32.name << '.a32'
|
|
||||||
op32.props[:adsz] = 32
|
|
||||||
@opcode_list << op32
|
|
||||||
op64 = op.dup
|
|
||||||
op64.name << '.a64'
|
|
||||||
op64.props[:adsz] = 64
|
|
||||||
@opcode_list << op64
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,76 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/x86_64/opcodes'
|
|
||||||
require 'metasm/cpu/x86_64/encode'
|
|
||||||
require 'metasm/parse'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class X86_64
|
|
||||||
def parse_parser_instruction(lexer, instr)
|
|
||||||
case instr.raw.downcase
|
|
||||||
when '.mode', '.bits'
|
|
||||||
if tok = lexer.readtok and tok.type == :string and tok.raw == '64'
|
|
||||||
lexer.skip_space
|
|
||||||
raise instr, 'syntax error' if ntok = lexer.nexttok and ntok.type != :eol
|
|
||||||
else
|
|
||||||
raise instr, 'invalid cpu mode, 64bit only'
|
|
||||||
end
|
|
||||||
else super(lexer, instr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_prefix(i, pfx)
|
|
||||||
super(i, pfx) or (i.prefix[:sz] = 64 if pfx == 'code64')
|
|
||||||
end
|
|
||||||
|
|
||||||
# needed due to how ruby inheritance works wrt constants
|
|
||||||
def parse_argregclasslist
|
|
||||||
[Reg, SimdReg, SegReg, DbgReg, TstReg, CtrlReg, FpReg]
|
|
||||||
end
|
|
||||||
# same inheritance sh*t
|
|
||||||
def parse_modrm(lex, tok, cpu)
|
|
||||||
ModRM.parse(lex, tok, cpu)
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_instruction_checkproto(i)
|
|
||||||
# check ah vs rex prefix
|
|
||||||
return if i.args.find { |a| a.kind_of? Reg and a.sz == 8 and a.val >= 16 and
|
|
||||||
op = opcode_list.find { |op_| op_.name == i.opname } and
|
|
||||||
((not op.props[:auto64] and i.args.find { |aa| aa.respond_to? :sz and aa.sz == 64 }) or
|
|
||||||
i.args.find { |aa| aa.kind_of? Reg and aa.val >= 8 and aa.val < 16 } or # XXX mov ah, cr12...
|
|
||||||
i.args.grep(ModRM).find { |aa| (aa.b and aa.b.val >= 8 and aa.b.val < 16) or (aa.i and aa.i.val >= 8 and aa.i.val < 16) })
|
|
||||||
}
|
|
||||||
super(i)
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if the argument matches the opcode's argument spec
|
|
||||||
def parse_arg_valid?(o, spec, arg)
|
|
||||||
return if arg.kind_of? ModRM and ((arg.b and arg.b.val == 16 and arg.i) or (arg.i and arg.i.val == 16 and (arg.b or arg.s != 1)))
|
|
||||||
return if arg.kind_of? Reg and arg.sz >= 32 and arg.val == 16 # eip/rip only in modrm
|
|
||||||
return if o.props[:auto64] and arg.respond_to? :sz and arg.sz == 32
|
|
||||||
# vex c4/c5
|
|
||||||
return if o.fields[:vex_r] and not o.fields[:vex_b] and (spec == :modrm or spec == :modrmxmm or spec == :modrmymm) and (((arg.kind_of?(SimdReg) or arg.kind_of?(Reg)) and arg.val >= 8) or (arg.kind_of?(ModRM) and ((arg.b and arg.b.val >= 8) or (arg.i and arg.i.val >= 8))))
|
|
||||||
if o.name == 'movsxd'
|
|
||||||
return if not arg.kind_of? Reg and not arg.kind_of? ModRM
|
|
||||||
arg.sz ||= 32
|
|
||||||
if spec == :reg
|
|
||||||
return if not arg.kind_of? Reg
|
|
||||||
return arg.sz >= 32
|
|
||||||
else
|
|
||||||
return arg.sz == 32
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return if o.name == 'xchg' and spec == :reg and o.args.include?(:reg_eax) and arg.kind_of?(Reg) and arg.sz == 32 and arg.val == 0
|
|
||||||
|
|
||||||
super(o, spec, arg)
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_reserved_name(name)
|
|
||||||
Reg.s_to_i[name]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,35 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/x86_64/opcodes'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class X86_64
|
|
||||||
def gui_hilight_word_regexp_init
|
|
||||||
ret = {}
|
|
||||||
|
|
||||||
%w[a b c d].each { |r|
|
|
||||||
ret["#{r}l"] = "[re]?#{r}x|#{r}l"
|
|
||||||
ret["#{r}h"] = "[re]?#{r}x|#{r}h"
|
|
||||||
ret["#{r}x"] = ret["e#{r}x"] = ret["r#{r}x"] = "[re]?#{r}x|#{r}[hl]"
|
|
||||||
}
|
|
||||||
|
|
||||||
%w[sp bp si di].each { |r|
|
|
||||||
ret["#{r}l"] = ret[r] = ret["e#{r}"] = ret["r#{r}"] = "[re]?#{r}|#{r}l"
|
|
||||||
}
|
|
||||||
|
|
||||||
(8..15).each { |i|
|
|
||||||
r = "r#{i}"
|
|
||||||
ret[r+'b'] = ret[r+'w'] = ret[r+'d'] = ret[r] = "#{r}[bwd]?"
|
|
||||||
}
|
|
||||||
|
|
||||||
ret['eip'] = ret['rip'] = '[re]ip'
|
|
||||||
|
|
||||||
ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,9 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/cpu/z80/decode'
|
|
||||||
require 'metasm/cpu/z80/render'
|
|
@ -1,313 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/z80/opcodes'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Z80
|
|
||||||
def build_opcode_bin_mask(op)
|
|
||||||
# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
|
|
||||||
op.bin_mask = Array.new(op.bin.length, 0)
|
|
||||||
op.fields.each { |f, (oct, off)|
|
|
||||||
op.bin_mask[oct] |= (@fields_mask[f] << off)
|
|
||||||
}
|
|
||||||
op.bin_mask.map! { |v| 255 ^ v }
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_bin_lookaside
|
|
||||||
# sets up a hash byte value => list of opcodes that may match
|
|
||||||
# opcode.bin_mask is built here
|
|
||||||
lookaside = Array.new(256) { [] }
|
|
||||||
opcode_list.each { |op|
|
|
||||||
build_opcode_bin_mask op
|
|
||||||
b = op.bin[0]
|
|
||||||
msk = op.bin_mask[0]
|
|
||||||
next @unknown_opcode = op if not b
|
|
||||||
for i in b..(b | (255^msk))
|
|
||||||
lookaside[i] << op if i & msk == b & msk
|
|
||||||
end
|
|
||||||
}
|
|
||||||
lookaside
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_prefix(instr, byte)
|
|
||||||
case byte
|
|
||||||
when 0xDD; instr.prefix = 0xDD
|
|
||||||
when 0xFD; instr.prefix = 0xFD
|
|
||||||
# implicit 'else return false'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# tries to find the opcode encoded at edata.ptr
|
|
||||||
# if no match, tries to match a prefix (update di.instruction.prefix)
|
|
||||||
# on match, edata.ptr points to the first byte of the opcode (after prefixes)
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
di = DecodedInstruction.new self
|
|
||||||
while edata.ptr < edata.data.length
|
|
||||||
byte = edata.data[edata.ptr]
|
|
||||||
byte = byte.unpack('C').first if byte.kind_of?(::String)
|
|
||||||
return di if di.opcode = @bin_lookaside[byte].find { |op|
|
|
||||||
# fetch the relevant bytes from edata
|
|
||||||
bseq = edata.data[edata.ptr, op.bin.length].unpack('C*')
|
|
||||||
# check against full opcode mask
|
|
||||||
op.bin.zip(bseq, op.bin_mask).all? { |b1, b2, m| b2 and ((b1 & m) == (b2 & m)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if decode_prefix(di.instruction, edata.get_byte)
|
|
||||||
nb = edata.data[edata.ptr]
|
|
||||||
nb = nb.unpack('C').first if nb.kind_of?(::String)
|
|
||||||
case nb
|
|
||||||
when 0xCB
|
|
||||||
# DD CB <disp8> <opcode_pfxCB> [<args>]
|
|
||||||
di.instruction.prefix |= edata.get_byte << 8
|
|
||||||
di.bin_length += 2
|
|
||||||
opc = edata.data[edata.ptr+1]
|
|
||||||
opc = opc.unpack('C').first if opc.kind_of?(::String)
|
|
||||||
bseq = [0xCB, opc]
|
|
||||||
# XXX in decode_instr_op, byte[0] is the immediate displacement instead of cb
|
|
||||||
return di if di.opcode = @bin_lookaside[nb].find { |op|
|
|
||||||
op.bin.zip(bseq, op.bin_mask).all? { |b1, b2, m| b2 and ((b1 & m) == (b2 & m)) }
|
|
||||||
}
|
|
||||||
when 0xED
|
|
||||||
di.instruction.prefix = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
di.opcode = @unknown_opcode
|
|
||||||
return di
|
|
||||||
end
|
|
||||||
di.bin_length += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
before_ptr = edata.ptr
|
|
||||||
op = di.opcode
|
|
||||||
di.instruction.opname = op.name
|
|
||||||
bseq = edata.read(op.bin.length).unpack('C*') # decode_findopcode ensures that data >= op.length
|
|
||||||
pfx = di.instruction.prefix
|
|
||||||
|
|
||||||
field_val = lambda { |f|
|
|
||||||
if fld = op.fields[f]
|
|
||||||
(bseq[fld[0]] >> fld[1]) & @fields_mask[f]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
op.args.each { |a|
|
|
||||||
di.instruction.args << case a
|
|
||||||
when :i8, :u8, :i16, :u16; Expression[edata.decode_imm(a, @endianness)]
|
|
||||||
when :iy; Expression[field_val[a]]
|
|
||||||
when :iy8; Expression[field_val[a]*8]
|
|
||||||
|
|
||||||
when :rp
|
|
||||||
v = field_val[a]
|
|
||||||
Reg.new(16, v)
|
|
||||||
when :rp2
|
|
||||||
v = field_val[a]
|
|
||||||
v = 4 if v == 3
|
|
||||||
Reg.new(16, v)
|
|
||||||
when :ry, :rz
|
|
||||||
v = field_val[a]
|
|
||||||
if v == 6
|
|
||||||
Memref.new(Reg.from_str('HL'), nil, 1)
|
|
||||||
else
|
|
||||||
Reg.new(8, v)
|
|
||||||
end
|
|
||||||
|
|
||||||
when :r_a; Reg.from_str('A')
|
|
||||||
when :r_af; Reg.from_str('AF')
|
|
||||||
when :r_hl; Reg.from_str('HL')
|
|
||||||
when :r_de; Reg.from_str('DE')
|
|
||||||
when :r_sp; Reg.from_str('SP')
|
|
||||||
when :r_i; Reg.from_str('I')
|
|
||||||
|
|
||||||
when :m16; Memref.new(nil, edata.decode_imm(:u16, @endianness), nil)
|
|
||||||
when :m_bc; Memref.new(Reg.from_str('BC'), nil, 1)
|
|
||||||
when :m_de; Memref.new(Reg.from_str('DE'), nil, 1)
|
|
||||||
when :m_sp; Memref.new(Reg.from_str('SP'), nil, 2)
|
|
||||||
when :m_hl; Memref.new(Reg.from_str('HL'), nil, 1)
|
|
||||||
when :mf8; Memref.new(nil, 0xff00 + edata.decode_imm(:u8, @endianness), 1)
|
|
||||||
when :mfc; Memref.new(Reg.from_str('C'), 0xff00, 1)
|
|
||||||
|
|
||||||
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
case pfx
|
|
||||||
when 0xDD
|
|
||||||
when 0xFD
|
|
||||||
when 0xCBDD
|
|
||||||
when 0xCBFD
|
|
||||||
end
|
|
||||||
|
|
||||||
di.bin_length += edata.ptr - before_ptr
|
|
||||||
|
|
||||||
return if edata.ptr > edata.length
|
|
||||||
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
# hash opcode_name => lambda { |dasm, di, *symbolic_args| instr_binding }
|
|
||||||
def backtrace_binding
|
|
||||||
@backtrace_binding ||= init_backtrace_binding
|
|
||||||
end
|
|
||||||
def backtrace_binding=(b) @backtrace_binding = b end
|
|
||||||
|
|
||||||
# populate the @backtrace_binding hash with default values
|
|
||||||
def init_backtrace_binding
|
|
||||||
@backtrace_binding ||= {}
|
|
||||||
|
|
||||||
mask = 0xffff
|
|
||||||
|
|
||||||
opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
|
|
||||||
binding = case op
|
|
||||||
when 'ld'; lambda { |di, a0, a1, *aa| a2 = aa[0] ; a2 ? { a0 => Expression[a1, :+, a2] } : { a0 => Expression[a1] } }
|
|
||||||
when 'ldi'; lambda { |di, a0, a1| hl = (a0 == :a ? a1 : a0) ; { a0 => Expression[a1], hl => Expression[hl, :+, 1] } }
|
|
||||||
when 'ldd'; lambda { |di, a0, a1| hl = (a0 == :a ? a1 : a0) ; { a0 => Expression[a1], hl => Expression[hl, :-, 1] } }
|
|
||||||
when 'add', 'adc', 'sub', 'sbc', 'and', 'xor', 'or'
|
|
||||||
lambda { |di, a0, a1|
|
|
||||||
e_op = { 'add' => :+, 'adc' => :+, 'sub' => :-, 'sbc' => :-, 'and' => :&, 'xor' => :^, 'or' => :| }[op]
|
|
||||||
ret = Expression[a0, e_op, a1]
|
|
||||||
ret = Expression[ret, e_op, :flag_c] if op == 'adc' or op == 'sbc'
|
|
||||||
ret = Expression[ret.reduce] if not a0.kind_of? Indirection
|
|
||||||
{ a0 => ret }
|
|
||||||
}
|
|
||||||
when 'cp', 'cmp'; lambda { |di, *a| {} }
|
|
||||||
when 'inc'; lambda { |di, a0| { a0 => Expression[a0, :+, 1] } }
|
|
||||||
when 'dec'; lambda { |di, a0| { a0 => Expression[a0, :-, 1] } }
|
|
||||||
when 'not'; lambda { |di, a0| { a0 => Expression[a0, :^, mask] } }
|
|
||||||
when 'push'
|
|
||||||
lambda { |di, a0| { :sp => Expression[:sp, :-, 2],
|
|
||||||
Indirection[:sp, 2, di.address] => Expression[a0] } }
|
|
||||||
when 'pop'
|
|
||||||
lambda { |di, a0| { :sp => Expression[:sp, :+, 2],
|
|
||||||
a0 => Indirection[:sp, 2, di.address] } }
|
|
||||||
when 'call'
|
|
||||||
lambda { |di, a0| { :sp => Expression[:sp, :-, 2],
|
|
||||||
Indirection[:sp, 2, di.address] => Expression[di.next_addr] }
|
|
||||||
}
|
|
||||||
when 'ret', 'reti'; lambda { |di, *a| { :sp => Expression[:sp, :+, 2] } }
|
|
||||||
# TODO callCC, retCC ...
|
|
||||||
when 'bswap'
|
|
||||||
lambda { |di, a0| { a0 => Expression[
|
|
||||||
[[a0, :&, 0xff00], :>>, 8], :|,
|
|
||||||
[[a0, :&, 0x00ff], :<<, 8]] } }
|
|
||||||
when 'nop', /^j/; lambda { |di, *a| {} }
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO flags ?
|
|
||||||
|
|
||||||
@backtrace_binding[op] ||= binding if binding
|
|
||||||
}
|
|
||||||
@backtrace_binding
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_backtrace_binding(di)
|
|
||||||
a = di.instruction.args.map { |arg|
|
|
||||||
case arg
|
|
||||||
when Memref, Reg; arg.symbolic(di)
|
|
||||||
else arg
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding = backtrace_binding[di.opcode.basename]
|
|
||||||
binding[di, *a]
|
|
||||||
else
|
|
||||||
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
|
|
||||||
# assume nothing except the 1st arg is modified
|
|
||||||
case a[0]
|
|
||||||
when Indirection, Symbol; { a[0] => Expression::Unknown }
|
|
||||||
when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
|
|
||||||
else {}
|
|
||||||
end.update(:incomplete_binding => Expression[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# patch a forward binding from the backtrace binding
|
|
||||||
def fix_fwdemu_binding(di, fbd)
|
|
||||||
case di.opcode.name
|
|
||||||
when 'push', 'call'; fbd[Indirection[[:sp, :-, 2], 2]] = fbd.delete(Indirection[:sp, 2])
|
|
||||||
end
|
|
||||||
fbd
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xrefs_x(dasm, di)
|
|
||||||
return [] if not di.opcode.props[:setip]
|
|
||||||
|
|
||||||
case di.opcode.basename
|
|
||||||
when 'ret', 'reti'
|
|
||||||
return [Indirection[:sp, 2, di.address]]
|
|
||||||
when /^jr|^djnz/
|
|
||||||
# jmp/call are absolute addrs, only jr/djnz are relative
|
|
||||||
# also, the asm source should display the relative offset
|
|
||||||
return [Expression[[di.address, :+, di.bin_length], :+, di.instruction.args.first]]
|
|
||||||
end
|
|
||||||
|
|
||||||
case tg = di.instruction.args.first
|
|
||||||
when Memref; [Expression[tg.symbolic(di)]]
|
|
||||||
when Reg; [Expression[tg.symbolic(di)]]
|
|
||||||
when Expression, ::Integer; [Expression[tg]]
|
|
||||||
else
|
|
||||||
puts "unhandled setip at #{di.address} #{di.instruction}" if $DEBUG
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# checks if expr is a valid return expression matching the :saveip instruction
|
|
||||||
def backtrace_is_function_return(expr, di=nil)
|
|
||||||
expr = Expression[expr].reduce_rec
|
|
||||||
expr.kind_of?(Indirection) and expr.len == 2 and expr.target == Expression[:sp]
|
|
||||||
end
|
|
||||||
|
|
||||||
# updates the function backtrace_binding
|
|
||||||
# if the function is big and no specific register is given, do nothing (the binding will be lazily updated later, on demand)
|
|
||||||
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
|
|
||||||
b = f.backtrace_binding
|
|
||||||
|
|
||||||
bt_val = lambda { |r|
|
|
||||||
next if not retaddrlist
|
|
||||||
b[r] = Expression::Unknown
|
|
||||||
bt = []
|
|
||||||
retaddrlist.each { |retaddr|
|
|
||||||
bt |= dasm.backtrace(Expression[r], retaddr, :include_start => true,
|
|
||||||
:snapshot_addr => faddr, :origin => retaddr)
|
|
||||||
}
|
|
||||||
if bt.length != 1
|
|
||||||
b[r] = Expression::Unknown
|
|
||||||
else
|
|
||||||
b[r] = bt.first
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
if not wantregs.empty?
|
|
||||||
wantregs.each(&bt_val)
|
|
||||||
else
|
|
||||||
bt_val[:sp]
|
|
||||||
end
|
|
||||||
|
|
||||||
b
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns true if the expression is an address on the stack
|
|
||||||
def backtrace_is_stack_address(expr)
|
|
||||||
Expression[expr].expr_externals.include?(:sp)
|
|
||||||
end
|
|
||||||
|
|
||||||
# updates an instruction's argument replacing an expression with another (eg label renamed)
|
|
||||||
def replace_instr_arg_immediate(i, old, new)
|
|
||||||
i.args.map! { |a|
|
|
||||||
case a
|
|
||||||
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
|
||||||
when Memref
|
|
||||||
a.offset = (a.offset == old ? new : Expression[a.offset.bind(old => new).reduce]) if a.offset
|
|
||||||
a
|
|
||||||
else a
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,67 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Z80 < CPU
|
|
||||||
class Reg
|
|
||||||
class << self
|
|
||||||
attr_accessor :s_to_i, :i_to_s
|
|
||||||
end
|
|
||||||
@i_to_s = { 8 => { 0 => 'B', 1 => 'C', 2 => 'D', 3 => 'E',
|
|
||||||
4 => 'H', 5 => 'L', 7 => 'A' },
|
|
||||||
16 => { 0 => 'BC', 1 => 'DE', 2 => 'HL', 3 => 'SP',
|
|
||||||
4 => 'AF' } } # AF is 3 too
|
|
||||||
@s_to_i = @i_to_s.inject({}) { |h, (sz, rh)|
|
|
||||||
h.update rh.inject({}) { |hh, (i, n)|
|
|
||||||
hh.update n => [sz, i] } }
|
|
||||||
|
|
||||||
attr_accessor :sz, :i
|
|
||||||
def initialize(sz, i)
|
|
||||||
@sz = sz
|
|
||||||
@i = i
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig=nil) ; to_s.to_sym ; end
|
|
||||||
|
|
||||||
def self.from_str(s)
|
|
||||||
raise "Bad name #{s.inspect}" if not x = @s_to_i[s]
|
|
||||||
new(*x)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Memref
|
|
||||||
attr_accessor :base, :offset, :sz
|
|
||||||
def initialize(base, offset, sz=nil)
|
|
||||||
@base = base
|
|
||||||
offset = Expression[offset] if offset
|
|
||||||
@offset = offset
|
|
||||||
@sz = sz
|
|
||||||
end
|
|
||||||
|
|
||||||
def symbolic(orig)
|
|
||||||
p = nil
|
|
||||||
p = Expression[p, :+, @base.symbolic] if base
|
|
||||||
p = Expression[p, :+, @offset] if offset
|
|
||||||
Indirection[p.reduce, @sz, orig]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(family = :latest)
|
|
||||||
super()
|
|
||||||
@endianness = :little
|
|
||||||
@size = 16
|
|
||||||
@family = family
|
|
||||||
end
|
|
||||||
|
|
||||||
def init_opcode_list
|
|
||||||
send("init_#@family")
|
|
||||||
@opcode_list
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,224 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/cpu/z80/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
|
|
||||||
class Z80
|
|
||||||
def addop(name, bin, *args)
|
|
||||||
o = Opcode.new name, bin
|
|
||||||
args.each { |a|
|
|
||||||
o.args << a if @fields_mask[a] or @valid_args[a]
|
|
||||||
o.props[a] = true if @valid_props[a]
|
|
||||||
o.fields[a] = [bin.length-1, @fields_shift[a]] if @fields_mask[a]
|
|
||||||
raise "wtf #{a.inspect}" unless @valid_args[a] or @valid_props[a] or @fields_mask[a]
|
|
||||||
}
|
|
||||||
@opcode_list << o
|
|
||||||
end
|
|
||||||
|
|
||||||
def addop_macrocc(name, bin, *args)
|
|
||||||
%w[nz z nc c po pe p m].each_with_index { |cc, i|
|
|
||||||
dbin = bin.dup
|
|
||||||
dbin[0] |= i << 3
|
|
||||||
addop name + cc, dbin, *args
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# data from http://www.z80.info/decoding.htm
|
|
||||||
def init_z80_common
|
|
||||||
@opcode_list = []
|
|
||||||
@valid_args.update [:i8, :u8, :i16, :u16, :m16,
|
|
||||||
:r_a, :r_af, :r_hl, :r_de, :r_sp, :r_i,
|
|
||||||
:m_bc, :m_de, :m_sp, :m_hl, :mf8, :mfc
|
|
||||||
].inject({}) { |h, v| h.update v => true }
|
|
||||||
@fields_mask.update :rz => 7, :ry => 7, :rp => 3, :rp2 => 3, :iy => 7, :iy8 => 7
|
|
||||||
@fields_shift.update :rz => 0, :ry => 3, :rp => 4, :rp2 => 4, :iy => 3, :iy8 => 3
|
|
||||||
|
|
||||||
# some opcodes are in init_z80 when they are not part of the GB ABI
|
|
||||||
addop 'nop', [0b00_000_000]
|
|
||||||
addop 'jr', [0b00_011_000], :setip, :stopexec, :i8
|
|
||||||
%w[nz z nc c].each_with_index { |cc, i|
|
|
||||||
addop 'jr' + cc, [0b00_100_000 | (i << 3)], :setip, :i8
|
|
||||||
}
|
|
||||||
addop 'ld', [0b00_000_001], :rp, :i16
|
|
||||||
addop 'add', [0b00_001_001], :r_hl, :rp
|
|
||||||
|
|
||||||
addop 'ld', [0b00_000_010], :m_bc, :r_a
|
|
||||||
addop 'ld', [0b00_001_010], :r_a, :m_bc
|
|
||||||
addop 'ld', [0b00_010_010], :m_de, :r_a
|
|
||||||
addop 'ld', [0b00_011_010], :r_a, :m_de
|
|
||||||
|
|
||||||
addop 'inc', [0b00_000_011], :rp
|
|
||||||
addop 'dec', [0b00_001_011], :rp
|
|
||||||
addop 'inc', [0b00_000_100], :ry
|
|
||||||
addop 'dec', [0b00_000_101], :ry
|
|
||||||
addop 'ld', [0b00_000_110], :ry, :i8
|
|
||||||
|
|
||||||
addop 'rlca', [0b00_000_111] # rotate
|
|
||||||
addop 'rrca', [0b00_001_111]
|
|
||||||
addop 'rla', [0b00_010_111]
|
|
||||||
addop 'rra', [0b00_011_111]
|
|
||||||
|
|
||||||
addop 'daa', [0b00_100_111]
|
|
||||||
addop 'cpl', [0b00_101_111]
|
|
||||||
addop 'scf', [0b00_110_111]
|
|
||||||
addop 'ccf', [0b00_111_111]
|
|
||||||
|
|
||||||
addop 'halt', [0b01_110_110] # ld (HL), (HL)
|
|
||||||
addop 'ld', [0b01_000_000], :ry, :rz
|
|
||||||
|
|
||||||
addop 'add', [0b10_000_000], :r_a, :rz
|
|
||||||
addop 'adc', [0b10_001_000], :r_a, :rz
|
|
||||||
addop 'sub', [0b10_010_000], :r_a, :rz
|
|
||||||
addop 'sbc', [0b10_011_000], :r_a, :rz
|
|
||||||
addop 'and', [0b10_100_000], :r_a, :rz
|
|
||||||
addop 'xor', [0b10_101_000], :r_a, :rz
|
|
||||||
addop 'or', [0b10_110_000], :r_a, :rz
|
|
||||||
addop 'cmp', [0b10_111_000], :r_a, :rz # alias cp
|
|
||||||
addop 'cp', [0b10_111_000], :r_a, :rz # compare
|
|
||||||
|
|
||||||
addop_macrocc 'ret', [0b11_000_000], :setip
|
|
||||||
addop 'pop', [0b11_000_001], :rp2
|
|
||||||
addop 'ret', [0b11_001_001], :stopexec, :setip
|
|
||||||
addop 'jmp', [0b11_101_001], :r_hl, :setip, :stopexec # alias jp
|
|
||||||
addop 'jp', [0b11_101_001], :r_hl, :setip, :stopexec
|
|
||||||
addop 'ld', [0b11_111_001], :r_sp, :r_hl
|
|
||||||
addop_macrocc 'j', [0b11_000_010], :setip, :u16 # alias jp
|
|
||||||
addop_macrocc 'jp', [0b11_000_010], :setip, :u16
|
|
||||||
addop 'jmp', [0b11_000_011], :setip, :stopexec, :u16 # alias jp
|
|
||||||
addop 'jp', [0b11_000_011], :setip, :stopexec, :u16
|
|
||||||
|
|
||||||
addop 'di', [0b11_110_011] # disable interrupts
|
|
||||||
addop 'ei', [0b11_111_011]
|
|
||||||
addop_macrocc 'call', [0b11_000_100], :u16, :setip, :saveip
|
|
||||||
addop 'push', [0b11_000_101], :rp2
|
|
||||||
addop 'call', [0b11_001_101], :u16, :setip, :saveip, :stopexec
|
|
||||||
|
|
||||||
addop 'add', [0b11_000_110], :r_a, :i8
|
|
||||||
addop 'adc', [0b11_001_110], :r_a, :i8
|
|
||||||
addop 'sub', [0b11_010_110], :r_a, :i8
|
|
||||||
addop 'sbc', [0b11_011_110], :r_a, :i8
|
|
||||||
addop 'and', [0b11_100_110], :r_a, :i8
|
|
||||||
addop 'xor', [0b11_101_110], :r_a, :i8
|
|
||||||
addop 'or', [0b11_110_110], :r_a, :i8
|
|
||||||
addop 'cp', [0b11_111_110], :r_a, :i8
|
|
||||||
|
|
||||||
addop 'rst', [0b11_000_111], :iy8 # call off in page 0
|
|
||||||
|
|
||||||
addop 'rlc', [0xCB, 0b00_000_000], :rz # rotate
|
|
||||||
addop 'rrc', [0xCB, 0b00_001_000], :rz
|
|
||||||
addop 'rl', [0xCB, 0b00_010_000], :rz
|
|
||||||
addop 'rr', [0xCB, 0b00_011_000], :rz
|
|
||||||
addop 'sla', [0xCB, 0b00_100_000], :rz # shift
|
|
||||||
addop 'sra', [0xCB, 0b00_101_000], :rz
|
|
||||||
addop 'srl', [0xCB, 0b00_111_000], :rz
|
|
||||||
addop 'bit', [0xCB, 0b01_000_000], :iy, :rz # bit test
|
|
||||||
addop 'res', [0xCB, 0b10_000_000], :iy, :rz # bit reset
|
|
||||||
addop 'set', [0xCB, 0b11_000_000], :iy, :rz # bit set
|
|
||||||
end
|
|
||||||
|
|
||||||
# standard z80
|
|
||||||
def init_z80
|
|
||||||
init_z80_common
|
|
||||||
|
|
||||||
addop 'ex', [0b00_001_000], :r_af # XXX really ex AF, AF' ...
|
|
||||||
addop 'djnz', [0b00_010_000], :setip, :i8
|
|
||||||
|
|
||||||
addop 'ld', [0b00_100_010], :m16, :r_hl
|
|
||||||
addop 'ld', [0b00_101_010], :r_hl, :m16
|
|
||||||
addop 'ld', [0b00_110_010], :m16, :r_a
|
|
||||||
addop 'ld', [0b00_111_010], :r_a, :m16
|
|
||||||
|
|
||||||
addop 'exx', [0b11_011_001]
|
|
||||||
addop 'out', [0b11_010_011], :i8, :r_a
|
|
||||||
addop 'in', [0b11_011_011], :r_a, :i8
|
|
||||||
|
|
||||||
addop 'ex', [0b11_100_011], :m_sp, :r_hl
|
|
||||||
addop 'ex', [0b11_101_011], :r_de, :r_hl
|
|
||||||
|
|
||||||
addop 'sll', [0xCB, 0b00_110_000], :rz
|
|
||||||
|
|
||||||
addop 'in', [0xED, 0b01_110_000], :u16
|
|
||||||
addop 'in', [0xED, 0b01_000_000], :ry, :u16
|
|
||||||
addop 'out', [0xED, 0b01_110_001], :u16
|
|
||||||
addop 'out', [0xED, 0b01_000_001], :u16, :ry
|
|
||||||
addop 'sbc', [0xED, 0b01_000_010], :r_hl, :rp
|
|
||||||
addop 'adc', [0xED, 0b01_001_010], :r_hl, :rp
|
|
||||||
addop 'ld', [0xED, 0b01_000_011], :m16, :rp
|
|
||||||
addop 'ld', [0xED, 0b01_001_011], :rp, :m16
|
|
||||||
addop 'neg', [0xED, 0b01_000_100], :r_a, :iy # dummy int field
|
|
||||||
addop 'retn', [0xED, 0b01_000_101], :stopexec # dummy int != 1 ? (1 = reti)
|
|
||||||
addop 'reti', [0xED, 0b01_001_101], :stopexec, :setip
|
|
||||||
addop 'im', [0xED, 0b01_000_110], :iy
|
|
||||||
addop 'ld', [0xED, 0b01_000_111], :r_i, :r_a
|
|
||||||
addop 'ld', [0xED, 0b01_001_111], :r_r, :r_a
|
|
||||||
addop 'ld', [0xED, 0b01_010_111], :r_a, :r_i
|
|
||||||
addop 'ld', [0xED, 0b01_011_111], :r_a, :r_r
|
|
||||||
addop 'rrd', [0xED, 0b01_100_111]
|
|
||||||
addop 'rld', [0xED, 0b01_101_111]
|
|
||||||
|
|
||||||
addop 'ldi', [0xED, 0b10_100_000]
|
|
||||||
addop 'ldd', [0xED, 0b10_101_000]
|
|
||||||
addop 'ldir', [0xED, 0b10_110_000]
|
|
||||||
addop 'lddr', [0xED, 0b10_111_000]
|
|
||||||
addop 'cpi', [0xED, 0b10_100_001]
|
|
||||||
addop 'cpd', [0xED, 0b10_101_001]
|
|
||||||
addop 'cpir', [0xED, 0b10_110_001]
|
|
||||||
addop 'cpdr', [0xED, 0b10_111_001]
|
|
||||||
addop 'ini', [0xED, 0b10_100_010]
|
|
||||||
addop 'ind', [0xED, 0b10_101_010]
|
|
||||||
addop 'inir', [0xED, 0b10_110_010]
|
|
||||||
addop 'indr', [0xED, 0b10_111_010]
|
|
||||||
addop 'outi', [0xED, 0b10_100_011]
|
|
||||||
addop 'outd', [0xED, 0b10_101_011]
|
|
||||||
addop 'otir', [0xED, 0b10_110_011]
|
|
||||||
addop 'otdr', [0xED, 0b10_111_011]
|
|
||||||
|
|
||||||
addop 'unk_ed', [0xED], :i8
|
|
||||||
|
|
||||||
addop 'unk_nop', [], :i8 # undefined opcode = nop
|
|
||||||
@unknown_opcode = @opcode_list.last
|
|
||||||
end
|
|
||||||
|
|
||||||
# gameboy processor
|
|
||||||
# from http://nocash.emubase.de/pandocs.htm#cpucomparisionwithz80
|
|
||||||
def init_gb
|
|
||||||
init_z80_common
|
|
||||||
|
|
||||||
addop 'ld', [0x08], :m16, :r_sp
|
|
||||||
addop 'stop', [0x10]
|
|
||||||
|
|
||||||
addop 'ldi', [0x22], :m_hl, :r_a # (hl++) <- a
|
|
||||||
addop 'ldi', [0x2A], :r_a, :m_hl
|
|
||||||
addop 'ldd', [0x32], :m_hl, :r_a # (hl--) <- a
|
|
||||||
addop 'ldd', [0x3A], :r_a, :m_hl
|
|
||||||
|
|
||||||
addop 'reti', [0xD9], :setip, :stopexec
|
|
||||||
|
|
||||||
# override retpo/jpo
|
|
||||||
@opcode_list.delete_if { |op| op.bin[0] & 0xE5 == 0xE0 } # rm E0 E2 E8 EA F0 F2 F8 FA
|
|
||||||
addop 'ld', [0xE0], :mf8, :r_a # (0xff00 + :i8)
|
|
||||||
addop 'ld', [0xE2], :mfc, :r_a # (0xff00 + :r_c)
|
|
||||||
addop 'add', [0xE8], :r_sp, :i8
|
|
||||||
addop 'ld', [0xEA], :m16, :r_a
|
|
||||||
addop 'ld', [0xF0], :r_a, :mf8
|
|
||||||
addop 'ld', [0xF2], :r_a, :mfc
|
|
||||||
addop 'ld', [0xF8], :r_hl, :r_sp, :i8 # hl <- sp+:i8
|
|
||||||
addop 'ld', [0xFA], :r_a, :m16
|
|
||||||
|
|
||||||
addop 'swap', [0xCB, 0x30], :rz
|
|
||||||
|
|
||||||
addop 'inv_dd', [0xDD], :stopexec # invalid prefixes
|
|
||||||
addop 'inv_ed', [0xED], :stopexec
|
|
||||||
addop 'inv_fd', [0xFD], :stopexec
|
|
||||||
|
|
||||||
addop 'unk_nop', [], :i8 # undefined opcode = nop
|
|
||||||
@unknown_opcode = @opcode_list.last
|
|
||||||
end
|
|
||||||
|
|
||||||
alias init_latest init_z80
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,59 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/cpu/z80/opcodes'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class Z80
|
|
||||||
class Reg
|
|
||||||
include Renderable
|
|
||||||
def render ; [self.class.i_to_s[@sz][@i]] end
|
|
||||||
end
|
|
||||||
class Memref
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
r = ['(']
|
|
||||||
r << @base if @base
|
|
||||||
r << '+' if @base and @offset
|
|
||||||
r << @offset if @offset
|
|
||||||
r << ')'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_instruction(i)
|
|
||||||
r = []
|
|
||||||
r << i.opname
|
|
||||||
if not i.args.empty?
|
|
||||||
r << ' '
|
|
||||||
i.args.each { |a_| r << a_ << ', ' }
|
|
||||||
r.pop
|
|
||||||
end
|
|
||||||
r
|
|
||||||
end
|
|
||||||
|
|
||||||
def gui_hilight_word_regexp_init
|
|
||||||
ret = {}
|
|
||||||
|
|
||||||
# { 'B' => 'B|BC', 'BC' => 'B|C|BC' }
|
|
||||||
|
|
||||||
%w[BC DE HL].each { |w|
|
|
||||||
l0, l1 = w.split(//)
|
|
||||||
ret[l0] = "#{l0}#{l1}?"
|
|
||||||
ret[l1] = "#{l0}?#{l1}"
|
|
||||||
ret[w] = "#{l0}|#{l0}?#{l1}"
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
end
|
|
||||||
|
|
||||||
def gui_hilight_word_regexp(word)
|
|
||||||
@gui_hilight_word_hash ||= gui_hilight_word_regexp_init
|
|
||||||
@gui_hilight_word_hash[word] or super(word)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load Diff
@ -1,244 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
require 'metasm/render'
|
|
||||||
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
# symbolic pointer dereference
|
|
||||||
# API similar to Expression
|
|
||||||
class Indirection < ExpressionType
|
|
||||||
# Expression (the pointer)
|
|
||||||
attr_accessor :target
|
|
||||||
alias pointer target
|
|
||||||
alias pointer= target=
|
|
||||||
# length in bytes of data referenced
|
|
||||||
attr_accessor :len
|
|
||||||
# address of the instruction who generated the indirection
|
|
||||||
attr_accessor :origin
|
|
||||||
|
|
||||||
def initialize(target, len, origin)
|
|
||||||
@target, @len, @origin = target, len, origin
|
|
||||||
end
|
|
||||||
|
|
||||||
def reduce_rec
|
|
||||||
ptr = Expression[@target.reduce]
|
|
||||||
(ptr == Expression::Unknown) ? ptr : Indirection.new(ptr, @len, @origin)
|
|
||||||
end
|
|
||||||
|
|
||||||
def bind(h)
|
|
||||||
h[self] || Indirection.new(@target.bind(h), @len, @origin)
|
|
||||||
end
|
|
||||||
|
|
||||||
def hash ; @target.hash^@len.to_i end
|
|
||||||
def eql?(o) o.class == self.class and [o.target, o.len] == [@target, @len] end
|
|
||||||
alias == eql?
|
|
||||||
|
|
||||||
include Renderable
|
|
||||||
def render
|
|
||||||
ret = []
|
|
||||||
qual = {1 => 'byte', 2 => 'word', 4 => 'dword', 8 => 'qword'}[len] || "_#{len*8}bits" if len
|
|
||||||
ret << "#{qual} ptr " if qual
|
|
||||||
ret << '[' << @target << ']'
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the complexity of the expression (number of externals +1 per indirection)
|
|
||||||
def complexity
|
|
||||||
1+@target.complexity
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.[](t, l, o=nil)
|
|
||||||
new(Expression[*t], l, o)
|
|
||||||
end
|
|
||||||
|
|
||||||
def inspect
|
|
||||||
"Indirection[#{@target.inspect.sub(/^Expression/, '')}, #{@len.inspect}#{', '+@origin.inspect if @origin}]"
|
|
||||||
end
|
|
||||||
|
|
||||||
def externals
|
|
||||||
@target.externals
|
|
||||||
end
|
|
||||||
|
|
||||||
def match_rec(target, vars)
|
|
||||||
return false if not target.kind_of? Indirection
|
|
||||||
t = target.target
|
|
||||||
if vars[t]
|
|
||||||
return false if @target != vars[t]
|
|
||||||
elsif vars.has_key? t
|
|
||||||
vars[t] = @target
|
|
||||||
elsif t.kind_of? ExpressionType
|
|
||||||
return false if not @target.match_rec(t, vars)
|
|
||||||
else
|
|
||||||
return false if targ != @target
|
|
||||||
end
|
|
||||||
if vars[target.len]
|
|
||||||
return false if @len != vars[target.len]
|
|
||||||
elsif vars.has_key? target.len
|
|
||||||
vars[target.len] = @len
|
|
||||||
else
|
|
||||||
return false if target.len != @len
|
|
||||||
end
|
|
||||||
vars
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Expression
|
|
||||||
# returns the complexity of the expression (number of externals +1 per indirection)
|
|
||||||
def complexity
|
|
||||||
case @lexpr
|
|
||||||
when ExpressionType; @lexpr.complexity
|
|
||||||
when nil, ::Numeric; 0
|
|
||||||
else 1
|
|
||||||
end +
|
|
||||||
case @rexpr
|
|
||||||
when ExpressionType; @rexpr.complexity
|
|
||||||
when nil, ::Numeric; 0
|
|
||||||
else 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def expr_indirections
|
|
||||||
ret = case @lexpr
|
|
||||||
when Indirection; [@lexpr]
|
|
||||||
when ExpressionType; @lexpr.expr_indirections
|
|
||||||
else []
|
|
||||||
end
|
|
||||||
case @rexpr
|
|
||||||
when Indirection; ret << @rexpr
|
|
||||||
when ExpressionType; ret.concat @rexpr.expr_indirections
|
|
||||||
else ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class EncodedData
|
|
||||||
# returns an ::Integer from self.ptr, advances ptr
|
|
||||||
# bytes from rawsize to virtsize = 0
|
|
||||||
# ignores self.relocations
|
|
||||||
def get_byte
|
|
||||||
@ptr += 1
|
|
||||||
if @ptr <= @data.length
|
|
||||||
b = @data[ptr-1]
|
|
||||||
b = b.unpack('C').first if b.kind_of? ::String # 1.9
|
|
||||||
b
|
|
||||||
elsif @ptr <= @virtsize
|
|
||||||
0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# reads len bytes from self.data, advances ptr
|
|
||||||
# bytes from rawsize to virtsize are returned as zeroes
|
|
||||||
# ignores self.relocations
|
|
||||||
def read(len=@virtsize-@ptr)
|
|
||||||
vlen = len
|
|
||||||
vlen = @virtsize-@ptr if len > @virtsize-@ptr
|
|
||||||
str = (@ptr < @data.length) ? @data[@ptr, vlen] : ''
|
|
||||||
str = str.to_str.ljust(vlen, "\0") if str.length < vlen
|
|
||||||
@ptr += len
|
|
||||||
str
|
|
||||||
end
|
|
||||||
|
|
||||||
# decodes an immediate value from self.ptr, advances ptr
|
|
||||||
# returns an Expression on relocation, or an ::Integer
|
|
||||||
# if ptr has a relocation but the type/endianness does not match, the reloc is ignored and a warning is issued
|
|
||||||
# TODO arg type => sign+len
|
|
||||||
def decode_imm(type, endianness)
|
|
||||||
raise "invalid imm type #{type.inspect}" if not isz = Expression::INT_SIZE[type]
|
|
||||||
if rel = @reloc[@ptr]
|
|
||||||
if Expression::INT_SIZE[rel.type] == isz and rel.endianness == endianness
|
|
||||||
@ptr += rel.length
|
|
||||||
return rel.target
|
|
||||||
end
|
|
||||||
puts "W: Immediate type/endianness mismatch, ignoring relocation #{rel.target.inspect} (wanted #{type.inspect})" if $DEBUG
|
|
||||||
end
|
|
||||||
Expression.decode_imm(read(isz/8), type, endianness)
|
|
||||||
end
|
|
||||||
alias decode_immediate decode_imm
|
|
||||||
end
|
|
||||||
|
|
||||||
class Expression
|
|
||||||
# decodes an immediate from a raw binary string
|
|
||||||
# type may be a length in bytes, interpreted as unsigned, or an expression type (eg :u32)
|
|
||||||
# endianness is either an endianness or an object than responds to endianness
|
|
||||||
def self.decode_imm(str, type, endianness, off=0)
|
|
||||||
type = INT_SIZE.keys.find { |k| k.to_s[0] == ?a and INT_SIZE[k] == 8*type } if type.kind_of? ::Integer
|
|
||||||
endianness = endianness.endianness if not endianness.kind_of? ::Symbol
|
|
||||||
str = str[off, INT_SIZE[type]/8].to_s
|
|
||||||
str = str.reverse if endianness == :little
|
|
||||||
val = str.unpack('C*').inject(0) { |val_, b| (val_ << 8) | b }
|
|
||||||
val = make_signed(val, INT_SIZE[type]) if type.to_s[0] == ?i
|
|
||||||
val
|
|
||||||
end
|
|
||||||
class << self
|
|
||||||
alias decode_immediate decode_imm
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class CPU
|
|
||||||
# decodes the instruction at edata.ptr, mapped at virtual address off
|
|
||||||
# returns a DecodedInstruction or nil
|
|
||||||
def decode_instruction(edata, addr)
|
|
||||||
@bin_lookaside ||= build_bin_lookaside
|
|
||||||
di = decode_findopcode edata if edata.ptr <= edata.length
|
|
||||||
di.address = addr if di
|
|
||||||
di = decode_instr_op(edata, di) if di
|
|
||||||
decode_instr_interpret(di, addr) if di
|
|
||||||
end
|
|
||||||
|
|
||||||
# matches the binary opcode at edata.ptr
|
|
||||||
# returns di or nil
|
|
||||||
def decode_findopcode(edata)
|
|
||||||
DecodedInstruction.new self
|
|
||||||
end
|
|
||||||
|
|
||||||
# decodes di.instruction
|
|
||||||
# returns di or nil
|
|
||||||
def decode_instr_op(edata, di)
|
|
||||||
end
|
|
||||||
|
|
||||||
# may modify di.instruction.args for eg jump offset => absolute address
|
|
||||||
# returns di or nil
|
|
||||||
def decode_instr_interpret(di, addr)
|
|
||||||
di
|
|
||||||
end
|
|
||||||
|
|
||||||
# number of instructions following a jump that are still executed
|
|
||||||
def delay_slot(di=nil)
|
|
||||||
0
|
|
||||||
end
|
|
||||||
|
|
||||||
def disassembler_default_func
|
|
||||||
DecodedFunction.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# return something like backtrace_binding in the forward direction
|
|
||||||
# set pc_reg to some reg name (eg :pc) to include effects on the instruction pointer
|
|
||||||
def get_fwdemu_binding(di, pc_reg=nil)
|
|
||||||
fdi = di.backtrace_binding ||= get_backtrace_binding(di)
|
|
||||||
fdi = fix_fwdemu_binding(di, fdi)
|
|
||||||
if pc_reg
|
|
||||||
if di.opcode.props[:setip]
|
|
||||||
xr = get_xrefs_x(nil, di)
|
|
||||||
if xr and xr.length == 1
|
|
||||||
fdi[pc_reg] = xr[0]
|
|
||||||
else
|
|
||||||
fdi[:incomplete_binding] = Expression[1]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
fdi[pc_reg] = Expression[pc_reg, :+, di.bin_length]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
fdi
|
|
||||||
end
|
|
||||||
|
|
||||||
# patch a forward binding from the backtrace binding
|
|
||||||
# useful only on specific instructions that update a register *and* dereference that register (eg push)
|
|
||||||
def fix_fwdemu_binding(di, fbd)
|
|
||||||
fbd
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,342 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
|
|
||||||
require 'metasm/main'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class ExeFormat
|
|
||||||
# encodes an Array of source (Label/Data/Instruction etc) to an EncodedData
|
|
||||||
# resolves ambiguities using +encode_resolve+
|
|
||||||
def assemble_sequence(seq, cpu)
|
|
||||||
# an array of edata or sub-array of ambiguous edata
|
|
||||||
# its last element is always an edata
|
|
||||||
ary = [EncodedData.new]
|
|
||||||
|
|
||||||
seq.each { |e|
|
|
||||||
case e
|
|
||||||
when Label; ary.last.add_export(e.name, ary.last.virtsize)
|
|
||||||
when Data; ary.last << e.encode(cpu.endianness)
|
|
||||||
when Align, Padding
|
|
||||||
e.fillwith = e.fillwith.encode(cpu.endianness) if e.fillwith and not e.fillwith.kind_of? EncodedData
|
|
||||||
ary << e << EncodedData.new
|
|
||||||
when Offset; ary << e << EncodedData.new
|
|
||||||
when Instruction
|
|
||||||
case i = cpu.encode_instruction(self, e)
|
|
||||||
when Array
|
|
||||||
case i.length
|
|
||||||
when 0; raise EncodeError, "failed to encode #{e}"
|
|
||||||
when 1; ary.last << i.first
|
|
||||||
else ary << i << EncodedData.new # to solve later
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ary.last << i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
edata = (ary.length > 1) ? assemble_resolve(ary) : ary.shift
|
|
||||||
edata.fixup edata.binding
|
|
||||||
edata
|
|
||||||
end
|
|
||||||
|
|
||||||
# chose among multiple possible sub-EncodedData
|
|
||||||
# assumes all ambiguous edata have the equivallent relocations in the same order
|
|
||||||
def assemble_resolve(ary)
|
|
||||||
startlabel = new_label('section_start')
|
|
||||||
|
|
||||||
# create two bindings where all elements are the shortest/longest possible
|
|
||||||
minbinding = {}
|
|
||||||
minoff = 0
|
|
||||||
maxbinding = {}
|
|
||||||
maxoff = 0
|
|
||||||
|
|
||||||
ary.each { |elem|
|
|
||||||
case elem
|
|
||||||
when Array
|
|
||||||
if elem.all? { |ed| ed.kind_of? EncodedData and ed.reloc.empty? }
|
|
||||||
elem = [elem.sort_by { |ed| ed.length }.first]
|
|
||||||
end
|
|
||||||
elem.each { |e|
|
|
||||||
e.export.each { |label, off|
|
|
||||||
minbinding[label] = Expression[startlabel, :+, minoff + off]
|
|
||||||
maxbinding[label] = Expression[startlabel, :+, maxoff + off]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
minoff += elem.map { |e| e.virtsize }.min
|
|
||||||
maxoff += elem.map { |e| e.virtsize }.max
|
|
||||||
|
|
||||||
when EncodedData
|
|
||||||
elem.export.each { |label, off|
|
|
||||||
minbinding[label] = Expression[startlabel, :+, minoff + off]
|
|
||||||
maxbinding[label] = Expression[startlabel, :+, maxoff + off]
|
|
||||||
}
|
|
||||||
minoff += elem.virtsize
|
|
||||||
maxoff += elem.virtsize
|
|
||||||
|
|
||||||
when Align
|
|
||||||
minoff += 0
|
|
||||||
maxoff += elem.val - 1
|
|
||||||
|
|
||||||
when Padding
|
|
||||||
# find the surrounding Offsets and compute the largest/shortest edata sizes to determine min/max length for the padding
|
|
||||||
prevoff = ary[0..ary.index(elem)].grep(Offset).last
|
|
||||||
nextoff = ary[ary.index(elem)..-1].grep(Offset).first
|
|
||||||
raise elem, 'need .offset after .pad' if not nextoff
|
|
||||||
|
|
||||||
# find all elements between the surrounding Offsets
|
|
||||||
previdx = prevoff ? ary.index(prevoff) + 1 : 0
|
|
||||||
surround = ary[previdx..ary.index(nextoff)-1]
|
|
||||||
surround.delete elem
|
|
||||||
if surround.find { |nelem| nelem.kind_of? Padding }
|
|
||||||
raise elem, 'need .offset beetween two .pad'
|
|
||||||
end
|
|
||||||
if surround.find { |nelem| nelem.kind_of? Align and ary.index(nelem) > ary.index(elem) }
|
|
||||||
raise elem, 'cannot .align after a .pad' # XXX really ?
|
|
||||||
end
|
|
||||||
|
|
||||||
# lenmin/lenmax are the extrem length of the Padding
|
|
||||||
nxt = Expression[nextoff.val]
|
|
||||||
ext = nxt.externals
|
|
||||||
raise elem, "bad offset #{nxt}" if ext.length > 1 or (ext.length == 1 and not minbinding[ext.first])
|
|
||||||
nxt = Expression[nxt, :-, startlabel] if not nxt.bind(minbinding).reduce.kind_of? ::Integer
|
|
||||||
prv = Expression[prevoff ? prevoff.val : 0]
|
|
||||||
ext = prv.externals
|
|
||||||
raise elem, "bad offset #{prv}" if ext.length > 1 or (ext.length == 1 and not minbinding[ext.first])
|
|
||||||
prv = Expression[prv, :-, startlabel] if not prv.bind(minbinding).reduce.kind_of? ::Integer
|
|
||||||
|
|
||||||
lenmin = Expression[nxt.bind(minbinding), :-, prv.bind(maxbinding)].reduce
|
|
||||||
lenmax = Expression[nxt.bind(maxbinding), :-, prv.bind(minbinding)].reduce
|
|
||||||
raise elem, "bad labels: #{lenmin}" if not lenmin.kind_of? ::Integer or not lenmax.kind_of? ::Integer
|
|
||||||
surround.each { |nelem|
|
|
||||||
case nelem
|
|
||||||
when Array
|
|
||||||
lenmin -= nelem.map { |e| e.virtsize }.max
|
|
||||||
lenmax -= nelem.map { |e| e.virtsize }.min
|
|
||||||
when EncodedData
|
|
||||||
lenmin -= nelem.virtsize
|
|
||||||
lenmax -= nelem.virtsize
|
|
||||||
when Align
|
|
||||||
lenmin -= nelem.val - 1
|
|
||||||
lenmax -= 0
|
|
||||||
end
|
|
||||||
}
|
|
||||||
raise elem, "no room for .pad before '.offset #{nextoff.val}' at #{Backtrace.backtrace_str(nextoff.backtrace)}, need at least #{-lenmax} more bytes" if lenmax < 0
|
|
||||||
minoff += [lenmin, 0].max
|
|
||||||
maxoff += lenmax
|
|
||||||
|
|
||||||
when Offset
|
|
||||||
# nothing to do for now
|
|
||||||
else
|
|
||||||
raise "Internal error: bad object #{elem.inspect} in encode_resolve"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
# checks an expression linearity
|
|
||||||
check_linear = lambda { |expr|
|
|
||||||
expr = expr.reduce if expr.kind_of? Expression
|
|
||||||
while expr.kind_of? Expression
|
|
||||||
case expr.op
|
|
||||||
when :*
|
|
||||||
if expr.lexpr.kind_of? Numeric; expr = expr.rexpr
|
|
||||||
elsif expr.rexpr.kind_of? Numeric; expr = expr.lexpr
|
|
||||||
else break
|
|
||||||
end
|
|
||||||
when :/, :>>, :<<
|
|
||||||
if expr.rexpr.kind_of? Numeric; expr = expr.lexpr
|
|
||||||
else break
|
|
||||||
end
|
|
||||||
when :+, :-
|
|
||||||
if not expr.lexpr; expr = expr.rexpr
|
|
||||||
elsif expr.lexpr.kind_of? Numeric; expr = expr.rexpr
|
|
||||||
elsif expr.rexpr.kind_of? Numeric; expr = expr.lexpr
|
|
||||||
else
|
|
||||||
break if not check_linear[expr.rexpr]
|
|
||||||
expr = expr.lexpr
|
|
||||||
end
|
|
||||||
else break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
not expr.kind_of? Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
# now we can resolve all relocations
|
|
||||||
# for linear expressions of internal variables (ie differences of labels from the ary):
|
|
||||||
# - calc target numeric bounds, and reject relocs not accepting worst case value
|
|
||||||
# - else reject all but largest place available
|
|
||||||
# then chose the shortest overall EData left
|
|
||||||
ary.map! { |elem|
|
|
||||||
case elem
|
|
||||||
when Array
|
|
||||||
# for each external, compute numeric target values using minbinding[external] and maxbinding[external]
|
|
||||||
# this gives us all extrem values for linear expressions
|
|
||||||
target_bounds = {}
|
|
||||||
rec_checkminmax = lambda { |idx, target, binding, extlist|
|
|
||||||
if extlist.empty?
|
|
||||||
(target_bounds[idx] ||= []) << target.bind(binding).reduce
|
|
||||||
else
|
|
||||||
rec_checkminmax[idx, target, binding.merge(extlist.last => minbinding[extlist.last]), extlist[0...-1]]
|
|
||||||
rec_checkminmax[idx, target, binding.merge(extlist.last => maxbinding[extlist.last]), extlist[0...-1]]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
# biggest size disponible for this relocation (for non-linear/external)
|
|
||||||
wantsize = {}
|
|
||||||
|
|
||||||
elem.each { |e|
|
|
||||||
e.reloc.sort.each_with_index { |r_, i|
|
|
||||||
r = r_[1]
|
|
||||||
# has external ref
|
|
||||||
if not r.target.bind(minbinding).reduce.kind_of?(Numeric) or not check_linear[r.target]
|
|
||||||
# find the biggest relocation type for the current target
|
|
||||||
wantsize[i] = elem.map { |edata|
|
|
||||||
edata.reloc.sort[i][1].type
|
|
||||||
}.sort_by { |type| Expression::INT_SIZE[type] }.last # XXX do not use rel.length
|
|
||||||
else
|
|
||||||
rec_checkminmax[i, r.target, {}, r.target.externals]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# reject candidates with reloc type too small
|
|
||||||
acceptable = elem.find_all { |edata|
|
|
||||||
r = edata.reloc.sort
|
|
||||||
(0...r.length).all? { |i|
|
|
||||||
if wantsize[i]
|
|
||||||
r[i][1].type == wantsize[i]
|
|
||||||
else
|
|
||||||
target_bounds[i].all? { |b| Expression.in_range?(b, r[i][1].type) }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
raise EncodeError, "cannot find candidate in #{elem.inspect}, immediate too big #{wantsize.inspect} #{target_bounds.inspect}" if acceptable.empty?
|
|
||||||
|
|
||||||
# keep the shortest
|
|
||||||
acceptable.sort_by { |edata| edata.virtsize }.first
|
|
||||||
else
|
|
||||||
elem
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
# assemble all parts, resolve padding sizes, check offset directives
|
|
||||||
edata = EncodedData.new
|
|
||||||
|
|
||||||
# fills edata with repetitions of data until targetsize
|
|
||||||
fillwith = lambda { |targetsize, data|
|
|
||||||
if data
|
|
||||||
if data.reloc.empty? and not data.data.empty? # avoid useless iterations
|
|
||||||
nr = (targetsize-edata.virtsize) / data.length - 1
|
|
||||||
if nr > 0
|
|
||||||
dat = data.data.ljust(data.virtsize, 0.chr)
|
|
||||||
edata << (dat * nr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while edata.virtsize + data.virtsize <= targetsize
|
|
||||||
edata << data
|
|
||||||
end
|
|
||||||
if edata.virtsize < targetsize
|
|
||||||
edata << data[0, targetsize - edata.virtsize]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
edata.virtsize = targetsize
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
ary.each { |elem|
|
|
||||||
case elem
|
|
||||||
when EncodedData
|
|
||||||
edata << elem
|
|
||||||
when Align
|
|
||||||
fillwith[EncodedData.align_size(edata.virtsize, elem.val), elem.fillwith]
|
|
||||||
when Offset
|
|
||||||
raise EncodeError, "could not enforce .offset #{elem.val} #{elem.backtrace}: offset now #{edata.virtsize}" if edata.virtsize != Expression[elem.val].bind(edata.binding(0)).reduce
|
|
||||||
when Padding
|
|
||||||
nextoff = ary[ary.index(elem)..-1].grep(Offset).first
|
|
||||||
targetsize = Expression[nextoff.val].bind(edata.binding(0)).reduce
|
|
||||||
ary[ary.index(elem)+1..ary.index(nextoff)-1].each { |nelem| targetsize -= nelem.virtsize }
|
|
||||||
raise EncodeError, "no room for .pad #{elem.backtrace_str} before .offset #{nextoff.val}, would be #{targetsize-edata.length} bytes long" if targetsize < edata.length
|
|
||||||
fillwith[targetsize, elem.fillwith]
|
|
||||||
else raise "Internal error: #{elem.inspect}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
edata
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Expression
|
|
||||||
def encode(type, endianness, backtrace=nil)
|
|
||||||
case val = reduce
|
|
||||||
when Integer; EncodedData.new Expression.encode_imm(val, type, endianness, backtrace)
|
|
||||||
else
|
|
||||||
str = case INT_SIZE[type]
|
|
||||||
when 8; "\0"
|
|
||||||
when 16; "\0\0"
|
|
||||||
when 32; "\0\0\0\0"
|
|
||||||
when 64; "\0\0\0\0\0\0\0\0"
|
|
||||||
else [0].pack('C')*(INT_SIZE[type]/8)
|
|
||||||
end
|
|
||||||
str = str.force_encoding('BINARY') if str.respond_to?(:force_encoding)
|
|
||||||
EncodedData.new(str, :reloc => {0 => Relocation.new(self, type, endianness, backtrace)})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def encode_imm(val, type, endianness, backtrace=nil)
|
|
||||||
type = INT_SIZE.keys.find { |k| k.to_s[0] == ?a and INT_SIZE[k] == 8*type } if type.kind_of? ::Integer
|
|
||||||
endianness = endianness.endianness if not endianness.kind_of? ::Symbol
|
|
||||||
raise "unsupported endianness #{endianness.inspect}" unless [:big, :little].include? endianness
|
|
||||||
raise(EncodeError, "immediate overflow #{type.inspect} #{Expression[val]} #{(Backtrace::backtrace_str(backtrace) if backtrace)}") if not in_range?(val, type)
|
|
||||||
s = (0...INT_SIZE[type]/8).map { |i| (val >> (8*i)) & 0xff }.pack('C*')
|
|
||||||
endianness != :little ? s.reverse : s
|
|
||||||
end
|
|
||||||
alias encode_immediate encode_imm
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Data
|
|
||||||
def encode(endianness)
|
|
||||||
edata = case @data
|
|
||||||
when :uninitialized
|
|
||||||
EncodedData.new('', :virtsize => Expression::INT_SIZE[INT_TYPE[@type]]/8)
|
|
||||||
when String
|
|
||||||
# db 'foo' => 'foo' # XXX could be optimised, but should not be significant
|
|
||||||
# dw 'foo' => "f\0o\0o\0" / "\0f\0o\0o"
|
|
||||||
@data.unpack('C*').inject(EncodedData.new) { |ed, chr| ed << Expression.encode_imm(chr, INT_TYPE[@type], endianness, @backtrace) }
|
|
||||||
when Expression
|
|
||||||
@data.encode INT_TYPE[@type], endianness, @backtrace
|
|
||||||
when Array
|
|
||||||
@data.inject(EncodedData.new) { |ed, d| ed << d.encode(endianness) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# n times
|
|
||||||
(0...@count).inject(EncodedData.new) { |ed, cnt| ed << edata }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class CPU
|
|
||||||
# returns an EncodedData or an ary of them
|
|
||||||
# uses +#parse_arg_valid?+ to find the opcode whose signature matches with the instruction
|
|
||||||
# uses +encode_instr_op+ (arch-specific)
|
|
||||||
def encode_instruction(program, i)
|
|
||||||
errmsg = ''
|
|
||||||
oplist = opcode_list_byname[i.opname].to_a.find_all { |o|
|
|
||||||
o.args.length == i.args.length and
|
|
||||||
o.args.zip(i.args).all? { |f, a| parse_arg_valid?(o, f, a) }
|
|
||||||
}.map { |op|
|
|
||||||
begin
|
|
||||||
encode_instr_op(program, i, op)
|
|
||||||
rescue EncodeError
|
|
||||||
errmsg = " (#{$!.message})"
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
}.compact.flatten
|
|
||||||
raise EncodeError, "no matching opcode found for #{i}#{errmsg}" if oplist.empty?
|
|
||||||
oplist.each { |ed| ed.reloc.each_value { |v| v.backtrace = i.backtrace } }
|
|
||||||
oplist
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,197 +0,0 @@
|
|||||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
||||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
||||||
#
|
|
||||||
# Licence is LGPL, see LICENCE in the top-level directory
|
|
||||||
|
|
||||||
require 'metasm/exe_format/main'
|
|
||||||
require 'metasm/encode'
|
|
||||||
require 'metasm/decode'
|
|
||||||
|
|
||||||
module Metasm
|
|
||||||
class AOut < ExeFormat
|
|
||||||
MAGIC = { 0407 => 'OMAGIC', 0410 => 'NMAGIC', 0413 => 'ZMAGIC',
|
|
||||||
0314 => 'QMAGIC', 0421 => 'CMAGIC'
|
|
||||||
}
|
|
||||||
MACHINE_TYPE = { 0 => 'OLDSUN2', 1 => '68010', 2 => '68020',
|
|
||||||
3 => 'SPARC', 100 => 'PC386', 134 => 'I386', 135 => 'M68K',
|
|
||||||
136 => 'M68K4K', 137 => 'NS32532', 138 => 'SPARC',
|
|
||||||
139 => 'PMAX', 140 => 'VAX', 141 => 'ALPHA', 142 => 'MIPS',
|
|
||||||
143 => 'ARM6', 151 => 'MIPS1', 152 => 'MIPS2', 300 => 'HP300',
|
|
||||||
0x20B => 'HPUX800', 0x20C => 'HPUX'
|
|
||||||
}
|
|
||||||
FLAGS = { 0x10 => 'PIC', 0x20 => 'DYNAMIC' }
|
|
||||||
SYMBOL_TYPE = { 0 => 'UNDF', 1 => 'ABS', 2 => 'TEXT',
|
|
||||||
3 => 'DATA', 4 => 'BSS', 5 => 'INDR', 6 => 'SIZE',
|
|
||||||
9 => 'COMM', 10=> 'SETA', 11=> 'SETT', 12=> 'SETD',
|
|
||||||
13=> 'SETB', 14=> 'SETV', 15=> 'FN'
|
|
||||||
}
|
|
||||||
|
|
||||||
attr_accessor :endianness, :header, :text, :data, :symbols, :textrel, :datarel
|
|
||||||
|
|
||||||
class Header < SerialStruct
|
|
||||||
bitfield :word, 0 => :magic, 16 => :machtype, 24 => :flags
|
|
||||||
fld_enum(:magic, MAGIC)
|
|
||||||
fld_enum(:machtype, MACHINE_TYPE)
|
|
||||||
fld_bits(:flags, FLAGS)
|
|
||||||
words :text, :data, :bss, :syms, :entry, :trsz, :drsz
|
|
||||||
|
|
||||||
def decode(aout)
|
|
||||||
super(aout)
|
|
||||||
|
|
||||||
case @magic
|
|
||||||
when 'OMAGIC', 'NMAGIC', 'ZMAGIC', 'QMAGIC'
|
|
||||||
else raise InvalidExeFormat, "Bad A.OUT signature #@magic"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_default_values(aout)
|
|
||||||
@magic ||= 'QMAGIC'
|
|
||||||
@machtype ||= 'PC386'
|
|
||||||
@flags ||= []
|
|
||||||
@text ||= aout.text.length + (@magic == 'QMAGIC' ? 32 : 0) if aout.text
|
|
||||||
@data ||= aout.data.length if aout.data
|
|
||||||
|
|
||||||
super(aout)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Relocation < SerialStruct
|
|
||||||
word :address
|
|
||||||
bitfield :word, 0 => :symbolnum, 24 => :pcrel, 25 => :length,
|
|
||||||
27 => :extern, 28 => :baserel, 29 => :jmptable, 30 => :relative, 31 => :rtcopy
|
|
||||||
fld_enum :length, 0 => 1, 1 => 2, 2 => 4, 3 => 8
|
|
||||||
fld_default :length, 4
|
|
||||||
end
|
|
||||||
|
|
||||||
class Symbol < SerialStruct
|
|
||||||
word :name_p
|
|
||||||
bitfield :byte, 0 => :extern, 1 => :type, 5 => :stab
|
|
||||||
byte :other
|
|
||||||
half :desc
|
|
||||||
word :value
|
|
||||||
attr_accessor :name
|
|
||||||
|
|
||||||
def decode(aout, strings=nil)
|
|
||||||
super(aout)
|
|
||||||
@name = strings[@name_p...(strings.index(?\0, @name_p))] if strings
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_default_values(aout, strings=nil)
|
|
||||||
if strings and name and @name != ''
|
|
||||||
if not @name_p or strings[@name_p, @name.length] != @name
|
|
||||||
@name_p = strings.length
|
|
||||||
strings << @name << 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
super(aout, strings)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_byte(edata = @encoded) edata.decode_imm(:u8 , @endianness) end
|
|
||||||
def decode_half(edata = @encoded) edata.decode_imm(:u16, @endianness) end
|
|
||||||
def decode_word(edata = @encoded) edata.decode_imm(:u32, @endianness) end
|
|
||||||
def encode_byte(w) Expression[w].encode(:u8 , @endianness) end
|
|
||||||
def encode_half(w) Expression[w].encode(:u16, @endianness) end
|
|
||||||
def encode_word(w) Expression[w].encode(:u32, @endianness) end
|
|
||||||
def sizeof_byte ; 1 ; end
|
|
||||||
def sizeof_half ; 2 ; end
|
|
||||||
def sizeof_word ; 4 ; end
|
|
||||||
|
|
||||||
def initialize(cpu = nil)
|
|
||||||
@endianness = cpu ? cpu.endianness : :little
|
|
||||||
@header = Header.new
|
|
||||||
@text = EncodedData.new
|
|
||||||
@data = EncodedData.new
|
|
||||||
super(cpu)
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_header
|
|
||||||
@encoded.ptr = 0
|
|
||||||
@header.decode(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode
|
|
||||||
decode_header
|
|
||||||
|
|
||||||
tlen = @header.text
|
|
||||||
case @header.magic
|
|
||||||
when 'ZMAGIC'; @encoded.ptr = 1024
|
|
||||||
when 'QMAGIC'; tlen -= 32 # header is included in .text
|
|
||||||
end
|
|
||||||
@text = EncodedData.new << @encoded.read(tlen)
|
|
||||||
|
|
||||||
@data = EncodedData.new << @encoded.read(@header.data)
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
#textrel = @encoded.read @header.trsz
|
|
||||||
#datarel = @encoded.read @header.drsz
|
|
||||||
#syms = @encoded.read @header.syms
|
|
||||||
#strings = @encoded.read
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode
|
|
||||||
# non mmapable on linux anyway
|
|
||||||
# could support OMAGIC..
|
|
||||||
raise EncodeError, 'cannot encode non-QMAGIC a.out' if @header.magic and @header.magic != 'QMAGIC'
|
|
||||||
|
|
||||||
# data must be 4096-aligned
|
|
||||||
# 32 bytes of header included in .text
|
|
||||||
@text.virtsize = (@text.virtsize + 32 + 4096 - 1) / 4096 * 4096 - 32
|
|
||||||
if @data.rawsize % 4096 != 0
|
|
||||||
@data[(@data.rawsize + 4096 - 1) / 4096 * 4096 - 1] = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
@header.text = @text.length+32
|
|
||||||
@header.data = @data.rawsize
|
|
||||||
@header.bss = @data.virtsize - @data.rawsize
|
|
||||||
|
|
||||||
@encoded = EncodedData.new
|
|
||||||
@encoded << @header.encode(self)
|
|
||||||
binding = @text.binding(4096+32).merge @data.binding(4096 + @header.text)
|
|
||||||
@encoded << @text << @data
|
|
||||||
@encoded.fixup! binding
|
|
||||||
@encoded.data
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_init
|
|
||||||
@textsrc ||= []
|
|
||||||
@datasrc ||= []
|
|
||||||
@cursource ||= @textsrc
|
|
||||||
super()
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_parser_instruction(instr)
|
|
||||||
case instr.raw.downcase
|
|
||||||
when '.text'; @cursource = @textsrc
|
|
||||||
when '.data'; @cursource = @datasrc
|
|
||||||
when '.entrypoint'
|
|
||||||
# ".entrypoint <somelabel/expression>" or ".entrypoint" (here)
|
|
||||||
@lexer.skip_space
|
|
||||||
if tok = @lexer.nexttok and tok.type == :string
|
|
||||||
raise instr if not entrypoint = Expression.parse(@lexer)
|
|
||||||
else
|
|
||||||
entrypoint = new_label('entrypoint')
|
|
||||||
@cursource << Label.new(entrypoint, instr.backtrace.dup)
|
|
||||||
end
|
|
||||||
@header.entry = entrypoint
|
|
||||||
else super(instr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assemble(*a)
|
|
||||||
parse(*a) if not a.empty?
|
|
||||||
@text << assemble_sequence(@textsrc, @cpu)
|
|
||||||
@textsrc.clear
|
|
||||||
@data << assemble_sequence(@datasrc, @cpu)
|
|
||||||
@datasrc.clear
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
def each_section
|
|
||||||
tva = 0
|
|
||||||
tva = 4096+32 if @header.magic == 'QMAGIC'
|
|
||||||
yield @text, tva
|
|
||||||
yield @data, tva + @text.virtsize
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user