1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-29 18:07:27 +01:00

Gemify metasm

This commit is contained in:
James Lee 2014-12-22 20:26:30 -06:00
parent d766484b1f
commit d372a6a16d
No known key found for this signature in database
GPG Key ID: 2D6094C7CEA0A321
253 changed files with 3 additions and 79165 deletions

View File

@ -7,6 +7,7 @@ PATH
bcrypt
jsobfu (~> 0.2.0)
json
metasm (~> 1.0.2)
metasploit-concern (= 1.0.0)
metasploit-model (= 1.0.0)
metasploit-payloads (= 1.0.8)

View File

@ -1,2 +0,0 @@
# Load a slightly tweaked METASM stub
require 'metasm/metasm'

View File

@ -1,3 +0,0 @@
71374080fcf5e7be3322ce56f062c29c984c577b sstic07
f3bcc93471bf9186ed62edc1bef90bbe6614a0a3 metasm-0.1-rc1
13bead20e76be749ecdb67096d9cb0847d69ad59 version 0.1

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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