Compare commits
153 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
df0f11a03b | ||
|
2d92f0b8f0 | ||
|
aad13cd131 | ||
|
a513fe19ac | ||
|
f4beb510a4 | ||
|
d731dae8e3 | ||
|
c9087c2a60 | ||
|
14ae3ba7f9 | ||
|
5a91de8c90 | ||
|
e3b32540df | ||
|
a37904dd86 | ||
|
cf25629d1e | ||
|
0ca790b92e | ||
|
d1fe2b2459 | ||
|
d4e8164f7e | ||
|
08351fb37a | ||
|
85e53d4108 | ||
|
aa05ae6fec | ||
|
1565b7bcd7 | ||
|
b409186b8d | ||
|
418a97afa1 | ||
|
5132455efe | ||
|
c0ad5542a8 | ||
|
3a27ad0b57 | ||
|
2b413144dc | ||
|
3ebcc707d2 | ||
|
7775e9ecc2 | ||
|
03d843ddf2 | ||
|
eb51d102bb | ||
|
25eb44841e | ||
|
b333af0666 | ||
|
76c8b7710b | ||
|
70e198602b | ||
|
206f0fa759 | ||
|
fd6ce8f660 | ||
|
727d01d4f6 | ||
|
ae22853141 | ||
|
d418c81eff | ||
|
2a29ca73c9 | ||
|
54936004fd | ||
|
74c95119f2 | ||
|
366c1b8bfa | ||
|
a993ba85cf | ||
|
226c91327d | ||
|
b8bf3e3aac | ||
|
288426fe3c | ||
|
72cc388104 | ||
|
378180d8dc | ||
|
78c34e98cd | ||
|
2792c4f2af | ||
|
447db2139a | ||
|
564c8f9978 | ||
|
c50c0c3fbf | ||
|
cabb4d616d | ||
|
631271d716 | ||
|
9d27abd94f | ||
|
148dfc2a8b | ||
|
3acace1333 | ||
|
0221cfcd71 | ||
|
f351077efb | ||
|
e84be9dbca | ||
|
46ddf5511d | ||
|
89e957e7a2 | ||
|
982b431579 | ||
|
bf7c65bdf4 | ||
|
8e5a0667f8 | ||
|
19b84f3c35 | ||
|
08fc60898b | ||
|
082391983e | ||
|
504e56ebdc | ||
|
455b761956 | ||
|
b56dad1c7b | ||
|
9ba5695ce5 | ||
|
66099dd9af | ||
|
b689bc57d6 | ||
|
a69d83b60b | ||
|
86840ae241 | ||
|
3c51961e0e | ||
|
d014c98c8d | ||
|
a98fd896cd | ||
|
d6cdca958e | ||
|
efdea7bf19 | ||
|
0d3301964d | ||
|
fe1e3ce3e9 | ||
|
bb326a3749 | ||
|
27725c1d74 | ||
|
e026db5893 | ||
|
43f04c233c | ||
|
a8baa8c555 | ||
|
728584be27 | ||
|
b9adb4a6bc | ||
|
ae48a07313 | ||
|
956034d7e5 | ||
|
6cd9f35b9b | ||
|
689f936f7e | ||
|
6977fbfd8b | ||
|
77e4672d8d | ||
|
d34720fd7d | ||
|
d9c4d1cc1a | ||
|
f644caa51a | ||
|
1eb87257da | ||
|
32ce63371a | ||
|
ec86b0fb3a | ||
|
1d346ae63a | ||
|
74cd30b811 | ||
|
7fb9a24e39 | ||
|
afeb6ee377 | ||
|
f29042b531 | ||
|
09bfb054fb | ||
|
2677e107e6 | ||
|
66cd58461d | ||
|
bb0ebb1f2d | ||
|
27c75a9a90 | ||
|
d0cd3b8d84 | ||
|
9af9eaaa76 | ||
|
8c8f42f76c | ||
|
51fe68905b | ||
|
7fe70ecc56 | ||
|
d03cda5923 | ||
|
30ac07d4f0 | ||
|
8857052055 | ||
|
ce11fedc6e | ||
|
43d4145a98 | ||
|
295defa5f1 | ||
|
f801f97e04 | ||
|
f48c3dd51a | ||
|
62296fe351 | ||
|
32f36bcefc | ||
|
bc8a22cc30 | ||
|
f631ef9bd2 | ||
|
f7341ff400 | ||
|
fd429f2f6c | ||
|
fb3e5849bb | ||
|
7854b05654 | ||
|
500dab07e8 | ||
|
f6630e791b | ||
|
168485b75b | ||
|
5cd4393b14 | ||
|
7ed601b782 | ||
|
d1f2367bc0 | ||
|
851e67a1b4 | ||
|
fc2b4c4879 | ||
|
9c605cb135 | ||
|
24f9e90b0e | ||
|
a4a0ffdb2b | ||
|
0ea00c9a3c | ||
|
e1d4294a45 | ||
|
c3c7c29246 | ||
|
31bb950be6 | ||
|
8083a3e508 | ||
|
644c433cb3 | ||
|
d691f66983 | ||
|
386405f786 |
222
COPYING.LIB
222
COPYING.LIB
@@ -1,36 +1,14 @@
|
||||
------------------------------------------------------------------------------
|
||||
NOTE:
|
||||
Some code of the Twin package was modified for DOSEMU by the DOSEMU-team.
|
||||
The original is 'Copyright 1997 Willows Software, Inc.' and generously
|
||||
was put under the GNU Library General Public License.
|
||||
( for more information see http://www.willows.com/ )
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
We make use of section 3 of the GNU Library General Public License
|
||||
('...opt to apply the terms of the ordinary GNU General Public License...'),
|
||||
because the resulting product is an integrated part of DOSEMU and
|
||||
can not be considered to be a 'library' in the terms of Library License.
|
||||
|
||||
Therefore, the below GNU LIBRARY GENERAL PUBLIC LICENSE applies only to the
|
||||
_unchanged_ Twin package from Willows. For the DOSEMU-changed parts the normal
|
||||
GNU GENERAL PUBLIC LICENSE applies. This GPL (file COPYING) can be found in
|
||||
the root directory of the DOSEMU distribution.
|
||||
|
||||
The act of transformation to GPL was indicated to the maintainer of the Twin
|
||||
package (Rob Penrose <rob@Canopy.Com>) and he acknowledge agreement.
|
||||
|
||||
Nov. 1 1997, The DOSEMU team.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
[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
|
||||
|
||||
@@ -39,97 +17,109 @@ 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 Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, too.
|
||||
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, 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 or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
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
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the library, or if you modify it.
|
||||
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 a program 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
|
||||
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.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
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.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
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, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
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, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary 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.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
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.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
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.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
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, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
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
|
||||
@@ -278,7 +268,7 @@ 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 compile or
|
||||
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
|
||||
@@ -305,23 +295,31 @@ of these things:
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
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.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
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.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
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 source code distributed need not include anything that is normally
|
||||
distributed (in either source or binary form) with the major
|
||||
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.
|
||||
@@ -370,7 +368,7 @@ 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 to
|
||||
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
|
||||
@@ -413,7 +411,7 @@ 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 Library General Public License from time to time.
|
||||
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.
|
||||
|
||||
@@ -459,7 +457,7 @@ DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Libraries
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
@@ -476,18 +474,18 @@ convey the exclusion of warranty; and each file should have at least the
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -502,3 +500,5 @@ necessary. Here is a sample; alter the names:
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
|
79
Changelog
79
Changelog
@@ -1,3 +1,82 @@
|
||||
version 0.2:
|
||||
|
||||
- PowerPC disassembly and ELF symbols output (Rusty Russel)
|
||||
- flock support (Rusty Russel)
|
||||
- ugetrlimit support (Rusty Russel)
|
||||
- fstat64 fix (Rusty Russel)
|
||||
- initial Alpha port (Falk Hueffner)
|
||||
- initial IA64 port (Matt Wilson)
|
||||
- initial Sparc and Sparc64 port (David S. Miller)
|
||||
- added HLT instruction
|
||||
- LRET instruction fix.
|
||||
- added GPF generation for I/Os.
|
||||
- added INT3 and TF flag support.
|
||||
- SHL instruction C flag fix.
|
||||
- mmap emulation for host page size > 4KB
|
||||
- self-modifying code support
|
||||
- better VM86 support (dosemu works on non trivial programs)
|
||||
- precise exception support (EIP is computed correctly in most cases)
|
||||
- more precise LDT/GDT/IDT emulation
|
||||
- faster segment load in vm86 mode
|
||||
- direct chaining of basic blocks (faster emulation)
|
||||
|
||||
version 0.1.6:
|
||||
|
||||
- automatic library search system. QEMU can now work with unpatched
|
||||
ELF dynamic loader and libc (Rusty Russell).
|
||||
- ISO C warning fixes (Alistair Strachan)
|
||||
- first self-virtualizable version (works only as long as the
|
||||
translation cache is not flushed)
|
||||
- RH9 fixes
|
||||
|
||||
version 0.1.5:
|
||||
|
||||
- ppc64 support + personality() patch (Rusty Russell)
|
||||
- first Alpha CPU patches (Falk Hueffner)
|
||||
- removed bfd.h dependancy
|
||||
- fixed shrd, shld, idivl and divl on PowerPC.
|
||||
- fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC).
|
||||
|
||||
version 0.1.4:
|
||||
|
||||
- more accurate VM86 emulation (can launch small DOS 16 bit
|
||||
executables in wine).
|
||||
- fixed push/pop fs/gs
|
||||
- added iret instruction.
|
||||
- added times() syscall and SIOCATMARK ioctl.
|
||||
|
||||
version 0.1.3:
|
||||
|
||||
- S390 support (Ulrich Weigand)
|
||||
- glibc 2.3.x compile fix (Ulrich Weigand)
|
||||
- socketcall endian fix (Ulrich Weigand)
|
||||
- struct sockaddr endian fix (Ulrich Weigand)
|
||||
- sendmsg/recvmsg endian fix (Ulrich Weigand)
|
||||
- execve endian fix (Ulrich Weigand)
|
||||
- fdset endian fix (Ulrich Weigand)
|
||||
- partial setsockopt syscall support (Ulrich Weigand)
|
||||
- more accurate pushf/popf emulation
|
||||
- first partial vm86() syscall support (can be used with runcom example).
|
||||
- added bound, cmpxchg8b, cpuid instructions
|
||||
- added 16 bit addressing support/override for string operations
|
||||
- poll() fix
|
||||
|
||||
version 0.1.2:
|
||||
|
||||
- compile fixes
|
||||
- xlat instruction
|
||||
- xchg instruction memory lock
|
||||
- added simple vm86 example (not working with QEMU yet). The 54 byte
|
||||
DOS executable 'pi_10.com' program was released by Bertram
|
||||
Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html).
|
||||
|
||||
version 0.1.1:
|
||||
|
||||
- glibc 2.2 compilation fixes
|
||||
- added -s and -L options
|
||||
- binary distribution of x86 glibc and wine
|
||||
- big endian fixes in ELF loader and getdents.
|
||||
|
||||
version 0.1:
|
||||
|
||||
- initial public release.
|
||||
|
98
Makefile
98
Makefile
@@ -13,10 +13,49 @@ OP_CFLAGS+= -falign-functions=0
|
||||
else
|
||||
OP_CFLAGS+= -malign-functions=0
|
||||
endif
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-Wl,-T,i386.ld
|
||||
else
|
||||
# WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object
|
||||
# that the kernel ELF loader considers as an executable. I think this
|
||||
# is the simplest way to make it self virtualizable!
|
||||
LDFLAGS+=-Wl,-shared
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,ppc.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),s390)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,s390.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc)
|
||||
CFLAGS+=-m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
|
||||
LDFLAGS+=-m32
|
||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
CFLAGS+=-m64 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
|
||||
LDFLAGS+=-m64
|
||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),alpha)
|
||||
# -msmall-data is not used because we want two-instruction relocations
|
||||
# for the constant constructions
|
||||
OP_CFLAGS=-Wall -O2 -g
|
||||
# Ensure there's only a single GP
|
||||
CFLAGS += -msmall-data -msmall-text
|
||||
LDFLAGS+=-Wl,-T,alpha.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(GCC_MAJOR),3)
|
||||
@@ -27,7 +66,6 @@ endif
|
||||
#########################################################
|
||||
|
||||
DEFINES+=-D_GNU_SOURCE
|
||||
LDSCRIPT=$(ARCH).ld
|
||||
LIBS+=-lm
|
||||
|
||||
# profiling code
|
||||
@@ -36,18 +74,34 @@ LDFLAGS+=-p
|
||||
main.o: CFLAGS+=-p
|
||||
endif
|
||||
|
||||
OBJS= elfload.o main.o syscall.o signal.o
|
||||
OBJS= elfload.o main.o syscall.o mmap.o signal.o vm86.o path.o
|
||||
SRCS:= $(OBJS:.o=.c)
|
||||
OBJS+= libqemu.a
|
||||
|
||||
LIBOBJS+=thunk.o translate-i386.o op-i386.o exec-i386.o
|
||||
LIBOBJS+=thunk.o translate-i386.o op-i386.o exec-i386.o exec.o
|
||||
|
||||
# NOTE: the disassembler code is only needed for debugging
|
||||
LIBOBJS+=i386-dis.o dis-buf.o
|
||||
LIBOBJS+=disas.o i386-dis.o dis-buf.o
|
||||
ifeq ($(ARCH),alpha)
|
||||
LIBOBJS+=alpha-dis.o
|
||||
endif
|
||||
ifeq ($(ARCH),ppc)
|
||||
LIBOBJS+=ppc-dis.o
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
OBJS += ia64-syscall.o
|
||||
endif
|
||||
|
||||
all: qemu qemu-doc.html
|
||||
|
||||
qemu: $(OBJS)
|
||||
$(CC) -Wl,-T,$(LDSCRIPT) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
ifeq ($(ARCH),alpha)
|
||||
# Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of
|
||||
# the address space (31 bit so sign extending doesn't matter)
|
||||
echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc
|
||||
endif
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $^ 1>.depend
|
||||
@@ -61,11 +115,14 @@ libqemu.a: $(LIBOBJS)
|
||||
dyngen: dyngen.c
|
||||
$(HOST_CC) -O2 -Wall -g $< -o $@
|
||||
|
||||
translate-i386.o: translate-i386.c op-i386.h cpu-i386.h
|
||||
translate-i386.o: translate-i386.c op-i386.h opc-i386.h cpu-i386.h
|
||||
|
||||
op-i386.h: op-i386.o dyngen
|
||||
./dyngen -o $@ $<
|
||||
|
||||
opc-i386.h: op-i386.o dyngen
|
||||
./dyngen -c -o $@ $<
|
||||
|
||||
op-i386.o: op-i386.c opreg_template.h ops_template.h
|
||||
$(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
@@ -87,26 +144,28 @@ test speed: qemu
|
||||
make -C tests $@
|
||||
|
||||
TAGS:
|
||||
etags *.[ch] i386/*.[ch]
|
||||
etags *.[ch] tests/*.[ch]
|
||||
|
||||
# documentation
|
||||
qemu-doc.html: qemu-doc.texi
|
||||
texi2html -monolithic -number $<
|
||||
|
||||
FILES= \
|
||||
README COPYING COPYING.LIB TODO Changelog VERSION \
|
||||
dyngen.c ioctls.h ops_template.h syscall_types.h\
|
||||
Makefile elf.h linux_bin.h segment.h thunk.c\
|
||||
elfload.c main.c signal.c thunk.h\
|
||||
cpu-i386.h qemu.h op-i386.c opc-i386.h syscall-i386.h translate-i386.c\
|
||||
dis-asm.h gen-i386.h op-i386.h syscall.c\
|
||||
dis-buf.c i386-dis.c opreg_template.h syscall_defs.h\
|
||||
i386.ld ppc.ld exec-i386.h exec-i386.c configure \
|
||||
README README.distrib COPYING COPYING.LIB TODO Changelog VERSION \
|
||||
dyngen.c ioctls.h ops_template.h op_string.h syscall_types.h\
|
||||
Makefile elf.h thunk.c\
|
||||
elfload.c main.c signal.c thunk.h exec.h\
|
||||
cpu-i386.h qemu.h op-i386.c syscall-i386.h translate-i386.c\
|
||||
syscall.c opreg_template.h syscall_defs.h vm86.c\
|
||||
dis-asm.h dis-buf.c disas.c disas.h alpha-dis.c ppc-dis.c i386-dis.c\
|
||||
ppc.ld s390.ld exec-i386.h exec-i386.c path.c exec.c mmap.c configure \
|
||||
tests/Makefile\
|
||||
tests/test-i386.c tests/test-i386-shift.h tests/test-i386.h\
|
||||
tests/test-i386-muldiv.h tests/test-i386-code16.S\
|
||||
tests/hello.c tests/hello tests/sha1.c \
|
||||
tests/testsig.c tests/testclone.c tests/testthread.c \
|
||||
tests/runcom.c tests/pi_10.com \
|
||||
tests/test_path.c \
|
||||
qemu-doc.texi qemu-doc.html
|
||||
|
||||
FILE=qemu-$(VERSION)
|
||||
@@ -118,6 +177,15 @@ tar:
|
||||
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) )
|
||||
rm -rf /tmp/$(FILE)
|
||||
|
||||
# generate a binary distribution including the test binary environnment
|
||||
BINPATH=/usr/local/qemu-i386
|
||||
|
||||
tarbin:
|
||||
tar zcvf /tmp/qemu-$(VERSION)-i386-glibc21.tar.gz \
|
||||
$(BINPATH)/etc $(BINPATH)/lib $(BINPATH)/bin $(BINPATH)/usr
|
||||
tar zcvf /tmp/qemu-$(VERSION)-i386-wine.tar.gz \
|
||||
$(BINPATH)/wine
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
||||
|
22
README
22
README
@@ -6,7 +6,7 @@ INSTALLATION
|
||||
|
||||
Type
|
||||
|
||||
./configure
|
||||
./configure --interp-prefix=/usr/local/qemu-i386
|
||||
make
|
||||
|
||||
to build qemu and libqemu.a.
|
||||
@@ -15,8 +15,26 @@ Type
|
||||
|
||||
make install
|
||||
|
||||
to install qemu in /usr/local/bin
|
||||
to install QEMU in /usr/local/bin
|
||||
|
||||
* On x86 you should be able to launch any program by using the
|
||||
libraries installed on your PC. For example:
|
||||
|
||||
./qemu -L / /bin/ls
|
||||
|
||||
* On non x86 CPUs, you need first to download at least an x86 glibc
|
||||
(qemu-XXX-i386-glibc21.tar.gz on the qemu web page). Ensure that
|
||||
LD_LIBRARY_PATH is not set:
|
||||
|
||||
unset LD_LIBRARY_PATH
|
||||
|
||||
Then you can launch the precompiled 'ls' x86 executable:
|
||||
|
||||
./qemu /usr/local/qemu-i386/bin/ls-i386
|
||||
|
||||
You can look at /usr/local/qemu-i386/bin/qemu-conf.sh so that QEMU is
|
||||
automatically launched by the Linux kernel when you try to launch x86
|
||||
executables.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
16
README.distrib
Normal file
16
README.distrib
Normal file
@@ -0,0 +1,16 @@
|
||||
Information about the various packages used to build the current qemu
|
||||
x86 binary distribution:
|
||||
|
||||
* gcc 2.95.2 was used for the build. A glibc 2.1.3 Debian distribution
|
||||
was used to get most of the binary packages.
|
||||
|
||||
* wine-20020411 tarball
|
||||
|
||||
./configure --prefix=/usr/local/qemu-i386/wine
|
||||
|
||||
All exe and libs were stripped. Some compile time tools and the
|
||||
includes were deleted.
|
||||
|
||||
* ldconfig was launched to build the library links:
|
||||
|
||||
./qemu /usr/local/qemu-i386/bin/ldconfig-i386 -C /usr/local/qemu-i386/etc/ld.so.cache
|
28
TODO
28
TODO
@@ -1,10 +1,22 @@
|
||||
- optimize translated cache chaining (DLL PLT-like system)
|
||||
|
||||
- fix iret/lret/fpush not before mem load restarting
|
||||
- fix all remaining thread lock issues (must put TBs in a specific invalid
|
||||
state, find a solution for tb_flush()).
|
||||
- handle fp87 state in signals
|
||||
- add gcc 2.96 test configure (some gcc3 flags are needed)
|
||||
- optimize FPU operations (evaluate x87 stack pointer statically)
|
||||
- add IPC syscalls
|
||||
- submit a patch to fix DOSEMU coopthreads
|
||||
|
||||
lower priority:
|
||||
--------------
|
||||
- handle rare page fault cases (in particular if page fault in heplers or
|
||||
in syscall emulation code).
|
||||
- fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID)
|
||||
- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
|
||||
issues, fix 16 bit uid issues)
|
||||
- finish signal handing (fp87 state, more siginfo conversions)
|
||||
- verify thread support (clone() and various locks)
|
||||
- vm86 syscall support
|
||||
- overrides/16bit for string ops
|
||||
- make it self runnable (use same trick as ld.so : include its own relocator and libc)
|
||||
- improved 16 bit support
|
||||
- fix FPU exceptions (in particular: gen_op_fpush not before mem load)
|
||||
- use page_unprotect_range in every suitable syscall to handle all
|
||||
cases of self modifying code.
|
||||
- use gcc as a backend to generate better code (easy to do by using
|
||||
op-i386.c operations as local inline functions).
|
||||
- add SSE2/MMX operations
|
||||
|
1976
alpha-dis.c
Normal file
1976
alpha-dis.c
Normal file
File diff suppressed because it is too large
Load Diff
128
alpha.ld
Normal file
128
alpha.ld
Normal file
@@ -0,0 +1,128 @@
|
||||
OUTPUT_FORMAT("elf64-alpha", "elf64-alpha",
|
||||
"elf64-alpha")
|
||||
OUTPUT_ARCH(alpha)
|
||||
ENTRY(__start)
|
||||
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
. = 0x60000000 + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.rel.text :
|
||||
{ *(.rel.text) *(.rel.gnu.linkonce.t*) }
|
||||
.rela.text :
|
||||
{ *(.rela.text) *(.rela.gnu.linkonce.t*) }
|
||||
.rel.data :
|
||||
{ *(.rel.data) *(.rel.gnu.linkonce.d*) }
|
||||
.rela.data :
|
||||
{ *(.rela.data) *(.rela.gnu.linkonce.d*) }
|
||||
.rel.rodata :
|
||||
{ *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
|
||||
.rela.rodata :
|
||||
{ *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
|
||||
.rel.got : { *(.rel.got) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rel.ctors : { *(.rel.ctors) }
|
||||
.rela.ctors : { *(.rela.ctors) }
|
||||
.rel.dtors : { *(.rel.dtors) }
|
||||
.rela.dtors : { *(.rela.dtors) }
|
||||
.rel.init : { *(.rel.init) }
|
||||
.rela.init : { *(.rela.init) }
|
||||
.rel.fini : { *(.rel.fini) }
|
||||
.rela.fini : { *(.rela.fini) }
|
||||
.rel.bss : { *(.rel.bss) }
|
||||
.rela.bss : { *(.rela.bss) }
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : { *(.init) } =0x47ff041f
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
} =0x47ff041f
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
.fini : { *(.fini) } =0x47ff041f
|
||||
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.reginfo : { *(.reginfo) }
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN(0x100000) + (. & (0x100000 - 1));
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.ctors :
|
||||
{
|
||||
*(.ctors)
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
*(.dtors)
|
||||
}
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata : { *(.sdata) }
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
__bss_start = .;
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
68
configure
vendored
68
configure
vendored
@@ -19,12 +19,14 @@ TMPH="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.h"
|
||||
|
||||
# default parameters
|
||||
prefix="/usr/local"
|
||||
interp_prefix="/usr/gnemul/qemu-i386"
|
||||
cross_prefix=""
|
||||
cc="gcc"
|
||||
host_cc="gcc"
|
||||
ar="ar"
|
||||
make="make"
|
||||
strip="strip"
|
||||
target_cpu="x86"
|
||||
cpu=`uname -m`
|
||||
case "$cpu" in
|
||||
i386|i486|i586|i686|i86pc|BePC)
|
||||
@@ -36,12 +38,24 @@ case "$cpu" in
|
||||
alpha)
|
||||
cpu="alpha"
|
||||
;;
|
||||
"Power Macintosh"|ppc)
|
||||
"Power Macintosh"|ppc|ppc64)
|
||||
cpu="powerpc"
|
||||
;;
|
||||
mips)
|
||||
cpu="mips"
|
||||
;;
|
||||
s390)
|
||||
cpu="s390"
|
||||
;;
|
||||
sparc)
|
||||
cpu="sparc"
|
||||
;;
|
||||
sparc64)
|
||||
cpu="sparc64"
|
||||
;;
|
||||
ia64)
|
||||
cpu="ia64"
|
||||
;;
|
||||
*)
|
||||
cpu="unknown"
|
||||
;;
|
||||
@@ -52,22 +66,6 @@ bigendian="no"
|
||||
# OS specific
|
||||
targetos=`uname -s`
|
||||
case $targetos in
|
||||
BeOS)
|
||||
prefix="/boot/home/config"
|
||||
# helps building libavcodec
|
||||
CFLAGS="-O2 -DPIC"
|
||||
# no need for libm, but the inet stuff
|
||||
# Check for BONE
|
||||
if (echo $BEINCLUDES|grep 'headers/be/bone' >/dev/null); then
|
||||
extralibs="-lbind -lsocket"
|
||||
else
|
||||
echo "Not sure building for net_server will succeed... good luck."
|
||||
extralibs="-lsocket"
|
||||
fi ;;
|
||||
BSD/OS)
|
||||
extralibs="-lpoll -lgnugetopt -lm"
|
||||
make="gmake"
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
@@ -86,6 +84,8 @@ for opt do
|
||||
case "$opt" in
|
||||
--prefix=*) prefix=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--interp-prefix=*) interp_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--source-path=*) source_path=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
@@ -137,7 +137,7 @@ fi
|
||||
else
|
||||
|
||||
# if cross compiling, cannot launch a program, so make a static guess
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" ; then
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" -o "$cpu" = "sparc" -o "$cpu" = "sparc64"; then
|
||||
bigendian="yes"
|
||||
fi
|
||||
|
||||
@@ -169,7 +169,7 @@ EOF
|
||||
echo "Standard options:"
|
||||
echo " --help print this message"
|
||||
echo " --prefix=PREFIX install in PREFIX [$prefix]"
|
||||
echo " for audio/video/image support"
|
||||
echo " --interp-prefix=PREFIX where to find shared libraries, etc. [$interp_prefix]"
|
||||
echo ""
|
||||
echo "Advanced options (experts only):"
|
||||
echo " --source-path=PATH path of source code [$source_path]"
|
||||
@@ -185,8 +185,9 @@ echo "Install prefix $prefix"
|
||||
echo "Source path $source_path"
|
||||
echo "C compiler $cc"
|
||||
echo "make $make"
|
||||
echo "CPU $cpu"
|
||||
echo "host CPU $cpu"
|
||||
echo "Big Endian $bigendian"
|
||||
echo "target CPU $target_cpu"
|
||||
echo "gprof enabled $gprof"
|
||||
|
||||
echo "Creating config.mak and config.h"
|
||||
@@ -195,7 +196,7 @@ echo "# Automatically generated by configure - do not modify" > config.mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $TMPH
|
||||
|
||||
echo "prefix=$prefix" >> config.mak
|
||||
echo "#define CONFIG_QEMU_PREFIX \"$prefix\"" >> $TMPH
|
||||
echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix\"" >> $TMPH
|
||||
echo "MAKE=$make" >> config.mak
|
||||
echo "CC=$cc" >> config.mak
|
||||
echo "GCC_MAJOR=$gcc_major" >> config.mak
|
||||
@@ -206,12 +207,31 @@ echo "CFLAGS=$CFLAGS" >> config.mak
|
||||
echo "LDFLAGS=$LDFLAGS" >> config.mak
|
||||
if test "$cpu" = "x86" ; then
|
||||
echo "ARCH=i386" >> config.mak
|
||||
echo "#define HOST_I386 1" >> $TMPH
|
||||
elif test "$cpu" = "armv4l" ; then
|
||||
echo "ARCH=arm" >> config.mak
|
||||
echo "#define HOST_ARM 1" >> $TMPH
|
||||
elif test "$cpu" = "powerpc" ; then
|
||||
echo "ARCH=ppc" >> config.mak
|
||||
echo "#define HOST_PPC 1" >> $TMPH
|
||||
elif test "$cpu" = "mips" ; then
|
||||
echo "ARCH=mips" >> config.mak
|
||||
echo "#define HOST_MIPS 1" >> $TMPH
|
||||
elif test "$cpu" = "s390" ; then
|
||||
echo "ARCH=s390" >> config.mak
|
||||
echo "#define HOST_S390 1" >> $TMPH
|
||||
elif test "$cpu" = "alpha" ; then
|
||||
echo "ARCH=alpha" >> config.mak
|
||||
echo "#define HOST_ALPHA 1" >> $TMPH
|
||||
elif test "$cpu" = "sparc" ; then
|
||||
echo "ARCH=sparc" >> config.mak
|
||||
echo "#define HOST_SPARC 1" >> $TMPH
|
||||
elif test "$cpu" = "sparc64" ; then
|
||||
echo "ARCH=sparc64" >> config.mak
|
||||
echo "#define HOST_SPARC64 1" >> $TMPH
|
||||
elif test "$cpu" = "ia64" ; then
|
||||
echo "ARCH=ia64" >> config.mak
|
||||
echo "#define HOST_IA64 1" >> $TMPH
|
||||
else
|
||||
echo "Unsupported CPU"
|
||||
exit 1
|
||||
@@ -230,10 +250,6 @@ echo "" >>config.mak
|
||||
echo -n "#define QEMU_VERSION \"" >> $TMPH
|
||||
head $source_path/VERSION >> $TMPH
|
||||
echo "\"" >> $TMPH
|
||||
if test "$network" = "yes" ; then
|
||||
echo "#define CONFIG_NETWORK 1" >> $TMPH
|
||||
echo "CONFIG_NETWORK=yes" >> config.mak
|
||||
fi
|
||||
|
||||
# build tree in object directory if source path is different from current one
|
||||
if test "$source_path_used" = "yes" ; then
|
||||
@@ -255,4 +271,4 @@ else
|
||||
echo "config.h is unchanged"
|
||||
fi
|
||||
|
||||
rm -f $TMPH
|
||||
rm -f $TMPO $TMPC $TMPE $TMPS $TMPH
|
||||
|
145
cpu-i386.h
145
cpu-i386.h
@@ -48,6 +48,24 @@
|
||||
#define R_FS 4
|
||||
#define R_GS 5
|
||||
|
||||
/* segment descriptor fields */
|
||||
#define DESC_G_MASK (1 << 23)
|
||||
#define DESC_B_MASK (1 << 22)
|
||||
#define DESC_AVL_MASK (1 << 20)
|
||||
#define DESC_P_MASK (1 << 15)
|
||||
#define DESC_DPL_SHIFT 13
|
||||
#define DESC_S_MASK (1 << 12)
|
||||
#define DESC_TYPE_SHIFT 8
|
||||
#define DESC_A_MASK (1 << 8)
|
||||
|
||||
#define DESC_CS_MASK (1 << 11)
|
||||
#define DESC_C_MASK (1 << 10)
|
||||
#define DESC_R_MASK (1 << 9)
|
||||
|
||||
#define DESC_E_MASK (1 << 10)
|
||||
#define DESC_W_MASK (1 << 9)
|
||||
|
||||
/* eflags masks */
|
||||
#define CC_C 0x0001
|
||||
#define CC_P 0x0004
|
||||
#define CC_A 0x0010
|
||||
@@ -55,34 +73,36 @@
|
||||
#define CC_S 0x0080
|
||||
#define CC_O 0x0800
|
||||
|
||||
#define TRAP_FLAG 0x0100
|
||||
#define INTERRUPT_FLAG 0x0200
|
||||
#define DIRECTION_FLAG 0x0400
|
||||
#define IOPL_FLAG_MASK 0x3000
|
||||
#define NESTED_FLAG 0x4000
|
||||
#define BYTE_FL 0x8000 /* Intel reserved! */
|
||||
#define RF_FLAG 0x10000
|
||||
#define VM_FLAG 0x20000
|
||||
/* AC 0x40000 */
|
||||
#define TF_MASK 0x00000100
|
||||
#define IF_MASK 0x00000200
|
||||
#define DF_MASK 0x00000400
|
||||
#define IOPL_MASK 0x00003000
|
||||
#define NT_MASK 0x00004000
|
||||
#define RF_MASK 0x00010000
|
||||
#define VM_MASK 0x00020000
|
||||
#define AC_MASK 0x00040000
|
||||
#define VIF_MASK 0x00080000
|
||||
#define VIP_MASK 0x00100000
|
||||
#define ID_MASK 0x00200000
|
||||
|
||||
#define EXCP00_DIVZ 1
|
||||
#define EXCP01_SSTP 2
|
||||
#define EXCP02_NMI 3
|
||||
#define EXCP03_INT3 4
|
||||
#define EXCP04_INTO 5
|
||||
#define EXCP05_BOUND 6
|
||||
#define EXCP06_ILLOP 7
|
||||
#define EXCP07_PREX 8
|
||||
#define EXCP08_DBLE 9
|
||||
#define EXCP09_XERR 10
|
||||
#define EXCP0A_TSS 11
|
||||
#define EXCP0B_NOSEG 12
|
||||
#define EXCP0C_STACK 13
|
||||
#define EXCP0D_GPF 14
|
||||
#define EXCP0E_PAGE 15
|
||||
#define EXCP10_COPR 17
|
||||
#define EXCP11_ALGN 18
|
||||
#define EXCP12_MCHK 19
|
||||
#define EXCP00_DIVZ 0
|
||||
#define EXCP01_SSTP 1
|
||||
#define EXCP02_NMI 2
|
||||
#define EXCP03_INT3 3
|
||||
#define EXCP04_INTO 4
|
||||
#define EXCP05_BOUND 5
|
||||
#define EXCP06_ILLOP 6
|
||||
#define EXCP07_PREX 7
|
||||
#define EXCP08_DBLE 8
|
||||
#define EXCP09_XERR 9
|
||||
#define EXCP0A_TSS 10
|
||||
#define EXCP0B_NOSEG 11
|
||||
#define EXCP0C_STACK 12
|
||||
#define EXCP0D_GPF 13
|
||||
#define EXCP0E_PAGE 14
|
||||
#define EXCP10_COPR 16
|
||||
#define EXCP11_ALGN 17
|
||||
#define EXCP12_MCHK 18
|
||||
|
||||
#define EXCP_INTERRUPT 256 /* async interruption */
|
||||
|
||||
@@ -158,7 +178,9 @@ typedef struct CPUX86State {
|
||||
/* standard registers */
|
||||
uint32_t regs[8];
|
||||
uint32_t eip;
|
||||
uint32_t eflags;
|
||||
uint32_t eflags; /* eflags register. During CPU emulation, CC
|
||||
flags and DF are set to zero because they are
|
||||
stored elsewhere */
|
||||
|
||||
/* emulator internal eflags handling */
|
||||
uint32_t cc_src;
|
||||
@@ -175,6 +197,12 @@ typedef struct CPUX86State {
|
||||
|
||||
/* emulator internal variables */
|
||||
CPU86_LDouble ft0;
|
||||
union {
|
||||
float f;
|
||||
double d;
|
||||
int i32;
|
||||
int64_t i64;
|
||||
} fp_convert;
|
||||
|
||||
/* segments */
|
||||
uint32_t segs[6]; /* selector values */
|
||||
@@ -183,13 +211,15 @@ typedef struct CPUX86State {
|
||||
SegmentDescriptorTable ldt;
|
||||
SegmentDescriptorTable idt;
|
||||
|
||||
/* various CPU modes */
|
||||
int vm86;
|
||||
|
||||
/* exception/interrupt handling */
|
||||
jmp_buf jmp_env;
|
||||
int exception_index;
|
||||
int error_code;
|
||||
uint32_t cr2;
|
||||
int interrupt_request;
|
||||
|
||||
/* user data */
|
||||
void *opaque;
|
||||
} CPUX86State;
|
||||
|
||||
/* all CPU memory access use these macros */
|
||||
@@ -390,12 +420,12 @@ static inline void stfq(void *ptr, double v)
|
||||
#endif
|
||||
|
||||
#ifndef IN_OP_I386
|
||||
void cpu_x86_outb(int addr, int val);
|
||||
void cpu_x86_outw(int addr, int val);
|
||||
void cpu_x86_outl(int addr, int val);
|
||||
int cpu_x86_inb(int addr);
|
||||
int cpu_x86_inw(int addr);
|
||||
int cpu_x86_inl(int addr);
|
||||
void cpu_x86_outb(CPUX86State *env, int addr, int val);
|
||||
void cpu_x86_outw(CPUX86State *env, int addr, int val);
|
||||
void cpu_x86_outl(CPUX86State *env, int addr, int val);
|
||||
int cpu_x86_inb(CPUX86State *env, int addr);
|
||||
int cpu_x86_inw(CPUX86State *env, int addr);
|
||||
int cpu_x86_inl(CPUX86State *env, int addr);
|
||||
#endif
|
||||
|
||||
CPUX86State *cpu_x86_init(void);
|
||||
@@ -406,23 +436,44 @@ void cpu_x86_close(CPUX86State *s);
|
||||
/* needed to load some predefinied segment registers */
|
||||
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
|
||||
|
||||
/* you can call these signal handler from you SIGBUS and SIGSEGV
|
||||
/* you can call this signal handler from your SIGBUS and SIGSEGV
|
||||
signal handlers to inform the virtual CPU of exceptions. non zero
|
||||
is returned if the signal was handled by the virtual CPU. */
|
||||
struct siginfo;
|
||||
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
||||
void *puc);
|
||||
|
||||
/* internal functions */
|
||||
/* used to debug */
|
||||
#define X86_DUMP_FPU 0x0001 /* dump FPU state too */
|
||||
#define X86_DUMP_CCOP 0x0002 /* dump qemu flag cache */
|
||||
void cpu_x86_dump_state(CPUX86State *env, FILE *f, int flags);
|
||||
|
||||
#define GEN_FLAG_CODE32_SHIFT 0
|
||||
#define GEN_FLAG_ADDSEG_SHIFT 1
|
||||
#define GEN_FLAG_SS32_SHIFT 2
|
||||
#define GEN_FLAG_ST_SHIFT 3
|
||||
/* page related stuff */
|
||||
#define TARGET_PAGE_BITS 12
|
||||
#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
|
||||
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
|
||||
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
|
||||
|
||||
int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
|
||||
int *gen_code_size_ptr,
|
||||
uint8_t *pc_start, uint8_t *cs_base, int flags);
|
||||
void cpu_x86_tblocks_init(void);
|
||||
extern unsigned long real_host_page_size;
|
||||
extern unsigned long host_page_bits;
|
||||
extern unsigned long host_page_size;
|
||||
extern unsigned long host_page_mask;
|
||||
|
||||
#define HOST_PAGE_ALIGN(addr) (((addr) + host_page_size - 1) & host_page_mask)
|
||||
|
||||
/* same as PROT_xxx */
|
||||
#define PAGE_READ 0x0001
|
||||
#define PAGE_WRITE 0x0002
|
||||
#define PAGE_EXEC 0x0004
|
||||
#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
|
||||
#define PAGE_VALID 0x0008
|
||||
/* original state of the write flag (used when tracking self-modifying
|
||||
code */
|
||||
#define PAGE_WRITE_ORG 0x0010
|
||||
|
||||
void page_dump(FILE *f);
|
||||
int page_get_flags(unsigned long address);
|
||||
void page_set_flags(unsigned long start, unsigned long end, int flags);
|
||||
void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||
|
||||
#endif /* CPU_I386_H */
|
||||
|
150
dis-asm.h
150
dis-asm.h
@@ -11,7 +11,152 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "bfd.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#define PARAMS(x) x
|
||||
typedef void *PTR;
|
||||
typedef uint64_t bfd_vma;
|
||||
typedef uint8_t bfd_byte;
|
||||
|
||||
enum bfd_flavour {
|
||||
bfd_target_unknown_flavour,
|
||||
bfd_target_aout_flavour,
|
||||
bfd_target_coff_flavour,
|
||||
bfd_target_ecoff_flavour,
|
||||
bfd_target_elf_flavour,
|
||||
bfd_target_ieee_flavour,
|
||||
bfd_target_nlm_flavour,
|
||||
bfd_target_oasys_flavour,
|
||||
bfd_target_tekhex_flavour,
|
||||
bfd_target_srec_flavour,
|
||||
bfd_target_ihex_flavour,
|
||||
bfd_target_som_flavour,
|
||||
bfd_target_os9k_flavour,
|
||||
bfd_target_versados_flavour,
|
||||
bfd_target_msdos_flavour,
|
||||
bfd_target_evax_flavour
|
||||
};
|
||||
|
||||
enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
|
||||
|
||||
enum bfd_architecture
|
||||
{
|
||||
bfd_arch_unknown, /* File arch not known */
|
||||
bfd_arch_obscure, /* Arch known, not one of these */
|
||||
bfd_arch_m68k, /* Motorola 68xxx */
|
||||
#define bfd_mach_m68000 1
|
||||
#define bfd_mach_m68008 2
|
||||
#define bfd_mach_m68010 3
|
||||
#define bfd_mach_m68020 4
|
||||
#define bfd_mach_m68030 5
|
||||
#define bfd_mach_m68040 6
|
||||
#define bfd_mach_m68060 7
|
||||
bfd_arch_vax, /* DEC Vax */
|
||||
bfd_arch_i960, /* Intel 960 */
|
||||
/* The order of the following is important.
|
||||
lower number indicates a machine type that
|
||||
only accepts a subset of the instructions
|
||||
available to machines with higher numbers.
|
||||
The exception is the "ca", which is
|
||||
incompatible with all other machines except
|
||||
"core". */
|
||||
|
||||
#define bfd_mach_i960_core 1
|
||||
#define bfd_mach_i960_ka_sa 2
|
||||
#define bfd_mach_i960_kb_sb 3
|
||||
#define bfd_mach_i960_mc 4
|
||||
#define bfd_mach_i960_xa 5
|
||||
#define bfd_mach_i960_ca 6
|
||||
#define bfd_mach_i960_jx 7
|
||||
#define bfd_mach_i960_hx 8
|
||||
|
||||
bfd_arch_a29k, /* AMD 29000 */
|
||||
bfd_arch_sparc, /* SPARC */
|
||||
#define bfd_mach_sparc 1
|
||||
/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */
|
||||
#define bfd_mach_sparc_sparclet 2
|
||||
#define bfd_mach_sparc_sparclite 3
|
||||
#define bfd_mach_sparc_v8plus 4
|
||||
#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns */
|
||||
#define bfd_mach_sparc_v9 6
|
||||
#define bfd_mach_sparc_v9a 7 /* with ultrasparc add'ns */
|
||||
/* Nonzero if MACH has the v9 instruction set. */
|
||||
#define bfd_mach_sparc_v9_p(mach) \
|
||||
((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9a)
|
||||
bfd_arch_mips, /* MIPS Rxxxx */
|
||||
#define bfd_mach_mips3000 3000
|
||||
#define bfd_mach_mips3900 3900
|
||||
#define bfd_mach_mips4000 4000
|
||||
#define bfd_mach_mips4010 4010
|
||||
#define bfd_mach_mips4100 4100
|
||||
#define bfd_mach_mips4300 4300
|
||||
#define bfd_mach_mips4400 4400
|
||||
#define bfd_mach_mips4600 4600
|
||||
#define bfd_mach_mips4650 4650
|
||||
#define bfd_mach_mips5000 5000
|
||||
#define bfd_mach_mips6000 6000
|
||||
#define bfd_mach_mips8000 8000
|
||||
#define bfd_mach_mips10000 10000
|
||||
#define bfd_mach_mips16 16
|
||||
bfd_arch_i386, /* Intel 386 */
|
||||
#define bfd_mach_i386_i386 0
|
||||
#define bfd_mach_i386_i8086 1
|
||||
bfd_arch_we32k, /* AT&T WE32xxx */
|
||||
bfd_arch_tahoe, /* CCI/Harris Tahoe */
|
||||
bfd_arch_i860, /* Intel 860 */
|
||||
bfd_arch_romp, /* IBM ROMP PC/RT */
|
||||
bfd_arch_alliant, /* Alliant */
|
||||
bfd_arch_convex, /* Convex */
|
||||
bfd_arch_m88k, /* Motorola 88xxx */
|
||||
bfd_arch_pyramid, /* Pyramid Technology */
|
||||
bfd_arch_h8300, /* Hitachi H8/300 */
|
||||
#define bfd_mach_h8300 1
|
||||
#define bfd_mach_h8300h 2
|
||||
#define bfd_mach_h8300s 3
|
||||
bfd_arch_powerpc, /* PowerPC */
|
||||
bfd_arch_rs6000, /* IBM RS/6000 */
|
||||
bfd_arch_hppa, /* HP PA RISC */
|
||||
bfd_arch_d10v, /* Mitsubishi D10V */
|
||||
bfd_arch_z8k, /* Zilog Z8000 */
|
||||
#define bfd_mach_z8001 1
|
||||
#define bfd_mach_z8002 2
|
||||
bfd_arch_h8500, /* Hitachi H8/500 */
|
||||
bfd_arch_sh, /* Hitachi SH */
|
||||
#define bfd_mach_sh 0
|
||||
#define bfd_mach_sh3 0x30
|
||||
#define bfd_mach_sh3e 0x3e
|
||||
#define bfd_mach_sh4 0x40
|
||||
bfd_arch_alpha, /* Dec Alpha */
|
||||
bfd_arch_arm, /* Advanced Risc Machines ARM */
|
||||
#define bfd_mach_arm_2 1
|
||||
#define bfd_mach_arm_2a 2
|
||||
#define bfd_mach_arm_3 3
|
||||
#define bfd_mach_arm_3M 4
|
||||
#define bfd_mach_arm_4 5
|
||||
#define bfd_mach_arm_4T 6
|
||||
bfd_arch_ns32k, /* National Semiconductors ns32000 */
|
||||
bfd_arch_w65, /* WDC 65816 */
|
||||
bfd_arch_tic30, /* Texas Instruments TMS320C30 */
|
||||
bfd_arch_v850, /* NEC V850 */
|
||||
#define bfd_mach_v850 0
|
||||
bfd_arch_arc, /* Argonaut RISC Core */
|
||||
#define bfd_mach_arc_base 0
|
||||
bfd_arch_m32r, /* Mitsubishi M32R/D */
|
||||
#define bfd_mach_m32r 0 /* backwards compatibility */
|
||||
bfd_arch_mn10200, /* Matsushita MN10200 */
|
||||
bfd_arch_mn10300, /* Matsushita MN10300 */
|
||||
bfd_arch_last
|
||||
};
|
||||
|
||||
typedef struct symbol_cache_entry
|
||||
{
|
||||
const char *name;
|
||||
union
|
||||
{
|
||||
PTR p;
|
||||
bfd_vma i;
|
||||
} udata;
|
||||
} asymbol;
|
||||
|
||||
typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...));
|
||||
|
||||
@@ -175,9 +320,12 @@ extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_d10v PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_v850 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_tic30 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_ppc PARAMS ((bfd_vma, disassemble_info*));
|
||||
|
||||
#if 0
|
||||
/* Fetch the disassembler for a given BFD, if that support is available. */
|
||||
extern disassembler_ftype disassembler PARAMS ((bfd *));
|
||||
#endif
|
||||
|
||||
|
||||
/* This block of definitions is for particular callers who read instructions
|
||||
|
81
disas.c
Normal file
81
disas.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/* General "disassemble this chunk" code. Used for debugging. */
|
||||
#include "dis-asm.h"
|
||||
#include "disas.h"
|
||||
#include "elf.h"
|
||||
|
||||
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
||||
unsigned int disas_num_syms;
|
||||
void *disas_symtab;
|
||||
const char *disas_strtab;
|
||||
|
||||
/* Disassemble this for me please... (debugging). */
|
||||
void disas(FILE *out, void *code, unsigned long size, enum disas_type type)
|
||||
{
|
||||
uint8_t *pc;
|
||||
int count;
|
||||
struct disassemble_info disasm_info;
|
||||
int (*print_insn)(bfd_vma pc, disassemble_info *info);
|
||||
|
||||
INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
|
||||
|
||||
disasm_info.buffer = code;
|
||||
disasm_info.buffer_vma = (unsigned long)code;
|
||||
disasm_info.buffer_length = size;
|
||||
|
||||
if (type == DISAS_TARGET) {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
disasm_info.endian = BFD_ENDIAN_BIG;
|
||||
#else
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#endif
|
||||
#ifdef __i386__
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(__powerpc__)
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(__alpha__)
|
||||
print_insn = print_insn_alpha;
|
||||
#else
|
||||
fprintf(out, "Asm output not supported on this arch\n");
|
||||
return;
|
||||
#endif
|
||||
} else {
|
||||
/* Currently only source supported in x86. */
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
if (type == DISAS_I386_I386)
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
else
|
||||
disasm_info.mach = bfd_mach_i386_i8086;
|
||||
print_insn = print_insn_i386;
|
||||
}
|
||||
|
||||
for (pc = code; pc < (uint8_t *)code + size; pc += count) {
|
||||
fprintf(out, "0x%08lx: ", (long)pc);
|
||||
count = print_insn((unsigned long)pc, &disasm_info);
|
||||
fprintf(out, "\n");
|
||||
if (count < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||
const char *lookup_symbol(void *orig_addr)
|
||||
{
|
||||
unsigned int i;
|
||||
/* Hack, because we know this is x86. */
|
||||
Elf32_Sym *sym = disas_symtab;
|
||||
|
||||
for (i = 0; i < disas_num_syms; i++) {
|
||||
if (sym[i].st_shndx == SHN_UNDEF
|
||||
|| sym[i].st_shndx >= SHN_LORESERVE)
|
||||
continue;
|
||||
|
||||
if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC)
|
||||
continue;
|
||||
|
||||
if ((long)orig_addr >= sym[i].st_value
|
||||
&& (long)orig_addr < sym[i].st_value + sym[i].st_size)
|
||||
return disas_strtab + sym[i].st_name;
|
||||
}
|
||||
return "";
|
||||
}
|
20
disas.h
Normal file
20
disas.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _QEMU_DISAS_H
|
||||
#define _QEMU_DISAS_H
|
||||
|
||||
enum disas_type {
|
||||
DISAS_I386_I386,
|
||||
DISAS_I386_I8086,
|
||||
DISAS_TARGET, /* whatever host is. */
|
||||
};
|
||||
|
||||
/* Disassemble this for me please... (debugging). */
|
||||
void disas(FILE *out, void *code, unsigned long size, enum disas_type type);
|
||||
|
||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||
const char *lookup_symbol(void *orig_addr);
|
||||
|
||||
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
||||
extern unsigned int disas_num_syms;
|
||||
extern void *disas_symtab; /* FIXME: includes are a mess --RR */
|
||||
extern const char *disas_strtab;
|
||||
#endif /* _QEMU_DISAS_H */
|
405
exec-i386.c
405
exec-i386.c
@@ -18,93 +18,38 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "exec-i386.h"
|
||||
#include "disas.h"
|
||||
|
||||
//#define DEBUG_EXEC
|
||||
#define DEBUG_FLUSH
|
||||
//#define DEBUG_SIGNAL
|
||||
|
||||
/* main execution loop */
|
||||
|
||||
/* maximum total translate dcode allocated */
|
||||
#define CODE_GEN_BUFFER_SIZE (2048 * 1024)
|
||||
//#define CODE_GEN_BUFFER_SIZE (128 * 1024)
|
||||
#define CODE_GEN_MAX_SIZE 65536
|
||||
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
||||
|
||||
/* threshold to flush the translated code buffer */
|
||||
#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE)
|
||||
|
||||
#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64)
|
||||
#define CODE_GEN_HASH_BITS 15
|
||||
#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS)
|
||||
|
||||
typedef struct TranslationBlock {
|
||||
unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */
|
||||
unsigned long cs_base; /* CS base for this block */
|
||||
unsigned int flags; /* flags defining in which context the code was generated */
|
||||
uint8_t *tc_ptr; /* pointer to the translated code */
|
||||
struct TranslationBlock *hash_next; /* next matching block */
|
||||
} TranslationBlock;
|
||||
|
||||
TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
|
||||
TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
|
||||
int nb_tbs;
|
||||
|
||||
uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
|
||||
uint8_t *code_gen_ptr;
|
||||
|
||||
/* thread support */
|
||||
|
||||
#ifdef __powerpc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
__asm__ __volatile__ (
|
||||
"0: lwarx %0,0,%1 ;"
|
||||
" xor. %0,%3,%0;"
|
||||
" bne 1f;"
|
||||
" stwcx. %2,0,%1;"
|
||||
" bne- 0b;"
|
||||
"1: "
|
||||
: "=&r" (ret)
|
||||
: "r" (p), "r" (1), "r" (0)
|
||||
: "cr0", "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
char ret;
|
||||
long int readval;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
|
||||
: "=q" (ret), "=m" (*p), "=a" (readval)
|
||||
: "r" (1), "m" (*p), "a" (0)
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int global_cpu_lock = 0;
|
||||
spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
void cpu_lock(void)
|
||||
{
|
||||
while (testandset(&global_cpu_lock));
|
||||
spin_lock(&global_cpu_lock);
|
||||
}
|
||||
|
||||
void cpu_unlock(void)
|
||||
{
|
||||
global_cpu_lock = 0;
|
||||
spin_unlock(&global_cpu_lock);
|
||||
}
|
||||
|
||||
/* exception support */
|
||||
/* NOTE: not static to force relocation generation by GCC */
|
||||
void raise_exception(int exception_index)
|
||||
void cpu_loop_exit(void)
|
||||
{
|
||||
/* NOTE: the register at this point must be saved by hand because
|
||||
longjmp restore them */
|
||||
#ifdef __sparc__
|
||||
/* We have to stay in the same register window as our caller,
|
||||
* thus this trick.
|
||||
*/
|
||||
__asm__ __volatile__("restore\n\t"
|
||||
"mov\t%o0, %i0");
|
||||
#endif
|
||||
#ifdef reg_EAX
|
||||
env->regs[R_EAX] = EAX;
|
||||
#endif
|
||||
@@ -129,133 +74,9 @@ void raise_exception(int exception_index)
|
||||
#ifdef reg_EDI
|
||||
env->regs[R_EDI] = EDI;
|
||||
#endif
|
||||
env->exception_index = exception_index;
|
||||
longjmp(env->jmp_env, 1);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_EXEC)
|
||||
static const char *cc_op_str[] = {
|
||||
"DYNAMIC",
|
||||
"EFLAGS",
|
||||
"MUL",
|
||||
"ADDB",
|
||||
"ADDW",
|
||||
"ADDL",
|
||||
"ADCB",
|
||||
"ADCW",
|
||||
"ADCL",
|
||||
"SUBB",
|
||||
"SUBW",
|
||||
"SUBL",
|
||||
"SBBB",
|
||||
"SBBW",
|
||||
"SBBL",
|
||||
"LOGICB",
|
||||
"LOGICW",
|
||||
"LOGICL",
|
||||
"INCB",
|
||||
"INCW",
|
||||
"INCL",
|
||||
"DECB",
|
||||
"DECW",
|
||||
"DECL",
|
||||
"SHLB",
|
||||
"SHLW",
|
||||
"SHLL",
|
||||
"SARB",
|
||||
"SARW",
|
||||
"SARL",
|
||||
};
|
||||
|
||||
static void cpu_x86_dump_state(FILE *f)
|
||||
{
|
||||
int eflags;
|
||||
eflags = cc_table[CC_OP].compute_all();
|
||||
eflags |= (DF & DIRECTION_FLAG);
|
||||
fprintf(f,
|
||||
"EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n"
|
||||
"ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n"
|
||||
"CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n"
|
||||
"EIP=%08x\n",
|
||||
env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX],
|
||||
env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP],
|
||||
env->cc_src, env->cc_dst, cc_op_str[env->cc_op],
|
||||
eflags & DIRECTION_FLAG ? 'D' : '-',
|
||||
eflags & CC_O ? 'O' : '-',
|
||||
eflags & CC_S ? 'S' : '-',
|
||||
eflags & CC_Z ? 'Z' : '-',
|
||||
eflags & CC_A ? 'A' : '-',
|
||||
eflags & CC_P ? 'P' : '-',
|
||||
eflags & CC_C ? 'C' : '-',
|
||||
env->eip);
|
||||
#if 1
|
||||
fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n",
|
||||
(double)ST0, (double)ST1, (double)ST(2), (double)ST(3));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void cpu_x86_tblocks_init(void)
|
||||
{
|
||||
if (!code_gen_ptr) {
|
||||
code_gen_ptr = code_gen_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* flush all the translation blocks */
|
||||
static void tb_flush(void)
|
||||
{
|
||||
int i;
|
||||
#ifdef DEBUG_FLUSH
|
||||
printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n",
|
||||
code_gen_ptr - code_gen_buffer,
|
||||
nb_tbs,
|
||||
(code_gen_ptr - code_gen_buffer) / nb_tbs);
|
||||
#endif
|
||||
nb_tbs = 0;
|
||||
for(i = 0;i < CODE_GEN_HASH_SIZE; i++)
|
||||
tb_hash[i] = NULL;
|
||||
code_gen_ptr = code_gen_buffer;
|
||||
/* XXX: flush processor icache at this point */
|
||||
}
|
||||
|
||||
/* find a translation block in the translation cache. If not found,
|
||||
return NULL and the pointer to the last element of the list in pptb */
|
||||
static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
|
||||
unsigned long pc,
|
||||
unsigned long cs_base,
|
||||
unsigned int flags)
|
||||
{
|
||||
TranslationBlock **ptb, *tb;
|
||||
unsigned int h;
|
||||
|
||||
h = pc & (CODE_GEN_HASH_SIZE - 1);
|
||||
ptb = &tb_hash[h];
|
||||
for(;;) {
|
||||
tb = *ptb;
|
||||
if (!tb)
|
||||
break;
|
||||
if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags)
|
||||
return tb;
|
||||
ptb = &tb->hash_next;
|
||||
}
|
||||
*pptb = ptb;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate a new translation block. flush the translation buffer if
|
||||
too many translation blocks or too much generated code */
|
||||
static inline TranslationBlock *tb_alloc(void)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
|
||||
(code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
|
||||
tb_flush();
|
||||
tb = &tbs[nb_tbs++];
|
||||
return tb;
|
||||
}
|
||||
|
||||
int cpu_x86_exec(CPUX86State *env1)
|
||||
{
|
||||
int saved_T0, saved_T1, saved_A0;
|
||||
@@ -289,7 +110,7 @@ int cpu_x86_exec(CPUX86State *env1)
|
||||
TranslationBlock *tb, **ptb;
|
||||
uint8_t *tc_ptr, *cs_base, *pc;
|
||||
unsigned int flags;
|
||||
|
||||
|
||||
/* first we save global registers */
|
||||
saved_T0 = T0;
|
||||
saved_T1 = T1;
|
||||
@@ -330,20 +151,35 @@ int cpu_x86_exec(CPUX86State *env1)
|
||||
#endif
|
||||
|
||||
/* put eflags in CPU temporary format */
|
||||
T0 = env->eflags;
|
||||
op_movl_eflags_T0();
|
||||
CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
DF = 1 - (2 * ((env->eflags >> 10) & 1));
|
||||
CC_OP = CC_OP_EFLAGS;
|
||||
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
env->interrupt_request = 0;
|
||||
|
||||
|
||||
/* prepare setjmp context for exception handling */
|
||||
if (setjmp(env->jmp_env) == 0) {
|
||||
T0 = 0; /* force lookup of first TB */
|
||||
for(;;) {
|
||||
if (env->interrupt_request) {
|
||||
raise_exception(EXCP_INTERRUPT);
|
||||
env->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
#ifdef DEBUG_EXEC
|
||||
if (loglevel) {
|
||||
cpu_x86_dump_state(logfile);
|
||||
/* XXX: save all volatile state in cpu state */
|
||||
/* restore flags in standard format */
|
||||
env->regs[R_EAX] = EAX;
|
||||
env->regs[R_EBX] = EBX;
|
||||
env->regs[R_ECX] = ECX;
|
||||
env->regs[R_EDX] = EDX;
|
||||
env->regs[R_ESI] = ESI;
|
||||
env->regs[R_EDI] = EDI;
|
||||
env->regs[R_EBP] = EBP;
|
||||
env->regs[R_ESP] = ESP;
|
||||
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||
cpu_x86_dump_state(env, logfile, 0);
|
||||
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
}
|
||||
#endif
|
||||
/* we compute the CPU state. We assume it will not
|
||||
@@ -354,44 +190,82 @@ int cpu_x86_exec(CPUX86State *env1)
|
||||
(unsigned long)env->seg_cache[R_ES].base |
|
||||
(unsigned long)env->seg_cache[R_SS].base) != 0) <<
|
||||
GEN_FLAG_ADDSEG_SHIFT;
|
||||
if (!(env->eflags & VM_MASK)) {
|
||||
flags |= (env->segs[R_CS] & 3) << GEN_FLAG_CPL_SHIFT;
|
||||
} else {
|
||||
/* NOTE: a dummy CPL is kept */
|
||||
flags |= (1 << GEN_FLAG_VM_SHIFT);
|
||||
flags |= (3 << GEN_FLAG_CPL_SHIFT);
|
||||
}
|
||||
flags |= (env->eflags & (IOPL_MASK | TF_MASK));
|
||||
cs_base = env->seg_cache[R_CS].base;
|
||||
pc = cs_base + env->eip;
|
||||
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
|
||||
flags);
|
||||
if (!tb) {
|
||||
spin_lock(&tb_lock);
|
||||
/* if no translated code available, then translate it now */
|
||||
/* XXX: very inefficient: we lock all the cpus when
|
||||
generating code */
|
||||
cpu_lock();
|
||||
tc_ptr = code_gen_ptr;
|
||||
ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
|
||||
&code_gen_size, pc, cs_base, flags);
|
||||
/* if invalid instruction, signal it */
|
||||
if (ret != 0) {
|
||||
cpu_unlock();
|
||||
raise_exception(EXCP06_ILLOP);
|
||||
tb = tb_alloc((unsigned long)pc);
|
||||
if (!tb) {
|
||||
/* flush must be done */
|
||||
tb_flush();
|
||||
/* cannot fail at this point */
|
||||
tb = tb_alloc((unsigned long)pc);
|
||||
/* don't forget to invalidate previous TB info */
|
||||
ptb = &tb_hash[tb_hash_func((unsigned long)pc)];
|
||||
T0 = 0;
|
||||
}
|
||||
tb = tb_alloc();
|
||||
*ptb = tb;
|
||||
tb->pc = (unsigned long)pc;
|
||||
tc_ptr = code_gen_ptr;
|
||||
tb->tc_ptr = tc_ptr;
|
||||
tb->cs_base = (unsigned long)cs_base;
|
||||
tb->flags = flags;
|
||||
tb->tc_ptr = tc_ptr;
|
||||
ret = cpu_x86_gen_code(tb, CODE_GEN_MAX_SIZE, &code_gen_size);
|
||||
/* if invalid instruction, signal it */
|
||||
if (ret != 0) {
|
||||
/* NOTE: the tb is allocated but not linked, so we
|
||||
can leave it */
|
||||
spin_unlock(&tb_lock);
|
||||
raise_exception(EXCP06_ILLOP);
|
||||
}
|
||||
*ptb = tb;
|
||||
tb->hash_next = NULL;
|
||||
tb_link(tb);
|
||||
code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
|
||||
cpu_unlock();
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
/* execute the generated code */
|
||||
#ifdef DEBUG_EXEC
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Trace 0x%08lx [0x%08lx] %s\n",
|
||||
(long)tb->tc_ptr, (long)tb->pc,
|
||||
lookup_symbol((void *)tb->pc));
|
||||
}
|
||||
#endif
|
||||
/* see if we can patch the calling TB */
|
||||
if (T0 != 0 && !(env->eflags & TF_MASK)) {
|
||||
spin_lock(&tb_lock);
|
||||
tb_add_jump((TranslationBlock *)(T0 & ~3), T0 & 3, tb);
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
|
||||
tc_ptr = tb->tc_ptr;
|
||||
|
||||
/* execute the generated code */
|
||||
gen_func = (void *)tc_ptr;
|
||||
#ifdef __sparc__
|
||||
__asm__ __volatile__("call %0\n\t"
|
||||
" mov %%o7,%%i0"
|
||||
: /* no outputs */
|
||||
: "r" (gen_func)
|
||||
: "i0", "i1", "i2", "i3", "i4", "i5");
|
||||
#else
|
||||
gen_func();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
ret = env->exception_index;
|
||||
|
||||
/* restore flags in standard format */
|
||||
op_movl_T0_eflags();
|
||||
env->eflags = T0;
|
||||
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||
|
||||
/* restore global registers */
|
||||
#ifdef reg_EAX
|
||||
@@ -437,7 +311,19 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
|
||||
|
||||
saved_env = env;
|
||||
env = s;
|
||||
load_seg(seg_reg, selector);
|
||||
if (env->eflags & VM_MASK) {
|
||||
SegmentCache *sc;
|
||||
selector &= 0xffff;
|
||||
sc = &env->seg_cache[seg_reg];
|
||||
/* NOTE: in VM86 mode, limit and seg_32bit are never reloaded,
|
||||
so we must load them here */
|
||||
sc->base = (void *)(selector << 4);
|
||||
sc->limit = 0xffff;
|
||||
sc->seg_32bit = 0;
|
||||
env->segs[seg_reg] = selector;
|
||||
} else {
|
||||
load_seg(seg_reg, selector, 0);
|
||||
}
|
||||
env = saved_env;
|
||||
}
|
||||
|
||||
@@ -453,23 +339,38 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
|
||||
#include <signal.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
static inline int handle_cpu_signal(unsigned long pc,
|
||||
sigset_t *old_set)
|
||||
/* 'pc' is the host PC at which the exception was raised. 'address' is
|
||||
the effective address of the memory exception. 'is_write' is 1 if a
|
||||
write caused the exception and otherwise 0'. 'old_set' is the
|
||||
signal set which should be restored */
|
||||
static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
|
||||
int is_write, sigset_t *old_set)
|
||||
{
|
||||
#ifdef DEBUG_SIGNAL
|
||||
printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n",
|
||||
pc, *(unsigned long *)old_set);
|
||||
TranslationBlock *tb;
|
||||
int ret;
|
||||
uint32_t found_pc;
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
printf("qemu: SIGSEGV pc=0x%08lx address=%08lx wr=%d oldset=0x%08lx\n",
|
||||
pc, address, is_write, *(unsigned long *)old_set);
|
||||
#endif
|
||||
if (pc >= (unsigned long)code_gen_buffer &&
|
||||
pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) {
|
||||
/* XXX: locking issue */
|
||||
if (is_write && page_unprotect(address)) {
|
||||
return 1;
|
||||
}
|
||||
tb = tb_find_pc(pc);
|
||||
if (tb) {
|
||||
/* the PC is inside the translated code. It means that we have
|
||||
a virtual CPU fault */
|
||||
ret = cpu_x86_search_pc(tb, &found_pc, pc);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
env->eip = found_pc - tb->cs_base;
|
||||
env->cr2 = address;
|
||||
/* we restore the process signal mask as the sigreturn should
|
||||
do it */
|
||||
do it (XXX: use sigsetjmp) */
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
/* XXX: need to compute virtual pc position by retranslating
|
||||
code. The rest of the CPU state should be correct. */
|
||||
raise_exception(EXCP0D_GPF);
|
||||
raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1));
|
||||
/* never comes here */
|
||||
return 1;
|
||||
} else {
|
||||
@@ -477,19 +378,53 @@ static inline int handle_cpu_signal(unsigned long pc,
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
||||
void *puc)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
struct ucontext *uc = puc;
|
||||
unsigned long pc;
|
||||
sigset_t *pold_set;
|
||||
|
||||
pc = uc->uc_mcontext.gregs[EIP];
|
||||
pold_set = &uc->uc_sigmask;
|
||||
return handle_cpu_signal(pc, pold_set);
|
||||
#else
|
||||
#warning No CPU specific signal handler: cannot handle target SIGSEGV events
|
||||
return 0;
|
||||
#ifndef REG_EIP
|
||||
/* for glibc 2.1 */
|
||||
#define REG_EIP EIP
|
||||
#define REG_ERR ERR
|
||||
#define REG_TRAPNO TRAPNO
|
||||
#endif
|
||||
pc = uc->uc_mcontext.gregs[REG_EIP];
|
||||
return handle_cpu_signal(pc, (unsigned long)info->si_addr,
|
||||
uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ?
|
||||
(uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0,
|
||||
&uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#elif defined(__powerpc)
|
||||
|
||||
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
||||
void *puc)
|
||||
{
|
||||
struct ucontext *uc = puc;
|
||||
struct pt_regs *regs = uc->uc_mcontext.regs;
|
||||
unsigned long pc;
|
||||
int is_write;
|
||||
|
||||
pc = regs->nip;
|
||||
is_write = 0;
|
||||
#if 0
|
||||
/* ppc 4xx case */
|
||||
if (regs->dsisr & 0x00800000)
|
||||
is_write = 1;
|
||||
#else
|
||||
if (regs->trap != 0x400 && (regs->dsisr & 0x02000000))
|
||||
is_write = 1;
|
||||
#endif
|
||||
return handle_cpu_signal(pc, (unsigned long)info->si_addr,
|
||||
is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error CPU specific signal handler needed
|
||||
|
||||
#endif
|
||||
|
76
exec-i386.h
76
exec-i386.h
@@ -66,6 +66,7 @@ register unsigned int T1 asm("r25");
|
||||
register unsigned int A0 asm("r26");
|
||||
register struct CPUX86State *env asm("r27");
|
||||
#define USE_INT_TO_FLOAT_HELPERS
|
||||
#define BUGGY_GCC_DIV64
|
||||
#define reg_EAX
|
||||
#define reg_ECX
|
||||
#define reg_EDX
|
||||
@@ -88,10 +89,51 @@ register unsigned int A0 asm("s2");
|
||||
register struct CPUX86State *env asm("s3");
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
register unsigned int T0 asm("l0");
|
||||
register unsigned int T1 asm("l1");
|
||||
register unsigned int A0 asm("l2");
|
||||
register struct CPUX86State *env asm("l3");
|
||||
register unsigned int EAX asm("l0");
|
||||
register unsigned int ECX asm("l1");
|
||||
register unsigned int EDX asm("l2");
|
||||
register unsigned int EBX asm("l3");
|
||||
register unsigned int ESP asm("l4");
|
||||
register unsigned int EBP asm("l5");
|
||||
register unsigned int ESI asm("l6");
|
||||
register unsigned int EDI asm("l7");
|
||||
register unsigned int T0 asm("g1");
|
||||
register unsigned int T1 asm("g2");
|
||||
register unsigned int A0 asm("g3");
|
||||
register struct CPUX86State *env asm("g6");
|
||||
#define USE_FP_CONVERT
|
||||
#define reg_EAX
|
||||
#define reg_ECX
|
||||
#define reg_EDX
|
||||
#define reg_EBX
|
||||
#define reg_ESP
|
||||
#define reg_EBP
|
||||
#define reg_ESI
|
||||
#define reg_EDI
|
||||
#endif
|
||||
#ifdef __s390__
|
||||
register unsigned int T0 asm("r7");
|
||||
register unsigned int T1 asm("r8");
|
||||
register unsigned int A0 asm("r9");
|
||||
register struct CPUX86State *env asm("r10");
|
||||
#endif
|
||||
#ifdef __alpha__
|
||||
register unsigned int T0 asm("$9");
|
||||
register unsigned int T1 asm("$10");
|
||||
register unsigned int A0 asm("$11");
|
||||
register unsigned int EAX asm("$12");
|
||||
register unsigned int ESP asm("$13");
|
||||
register unsigned int EBP asm("$14");
|
||||
register struct CPUX86State *env asm("$15");
|
||||
#define reg_EAX
|
||||
#define reg_ESP
|
||||
#define reg_EBP
|
||||
#endif
|
||||
#ifdef __ia64__
|
||||
register unsigned int T0 asm("r24");
|
||||
register unsigned int T1 asm("r25");
|
||||
register unsigned int A0 asm("r26");
|
||||
register struct CPUX86State *env asm("r27");
|
||||
#endif
|
||||
|
||||
/* force GCC to generate only one epilog at the end of the function */
|
||||
@@ -141,12 +183,32 @@ register struct CPUX86State *env asm("l3");
|
||||
#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7])
|
||||
#define ST1 ST(1)
|
||||
|
||||
#ifdef USE_FP_CONVERT
|
||||
#define FP_CONVERT (env->fp_convert)
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
/* Suggested by Richard Henderson. This will result in code like
|
||||
ldah $0,__op_param1($29) !gprelhigh
|
||||
lda $0,__op_param1($0) !gprellow
|
||||
We can then conveniently change $29 to $31 and adapt the offsets to
|
||||
emit the appropriate constant. */
|
||||
extern int __op_param1 __attribute__((visibility("hidden")));
|
||||
extern int __op_param2 __attribute__((visibility("hidden")));
|
||||
extern int __op_param3 __attribute__((visibility("hidden")));
|
||||
#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; })
|
||||
#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; })
|
||||
#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; })
|
||||
#else
|
||||
extern int __op_param1, __op_param2, __op_param3;
|
||||
#define PARAM1 ((long)(&__op_param1))
|
||||
#define PARAM2 ((long)(&__op_param2))
|
||||
#define PARAM3 ((long)(&__op_param3))
|
||||
#endif
|
||||
extern int __op_jmp0, __op_jmp1;
|
||||
|
||||
#include "cpu-i386.h"
|
||||
#include "exec.h"
|
||||
|
||||
typedef struct CCTable {
|
||||
int (*compute_all)(void); /* return all the flags */
|
||||
@@ -155,10 +217,14 @@ typedef struct CCTable {
|
||||
|
||||
extern CCTable cc_table[];
|
||||
|
||||
void load_seg(int seg_reg, int selector);
|
||||
void load_seg(int seg_reg, int selector, unsigned cur_eip);
|
||||
void cpu_lock(void);
|
||||
void cpu_unlock(void);
|
||||
void raise_interrupt(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip);
|
||||
void raise_exception_err(int exception_index, int error_code);
|
||||
void raise_exception(int exception_index);
|
||||
void cpu_loop_exit(void);
|
||||
|
||||
void OPPROTO op_movl_eflags_T0(void);
|
||||
void OPPROTO op_movl_T0_eflags(void);
|
||||
|
564
exec.c
Normal file
564
exec.c
Normal file
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* virtual page mapping and translated block handling
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "cpu-i386.h"
|
||||
#include "exec.h"
|
||||
|
||||
//#define DEBUG_TB_INVALIDATE
|
||||
#define DEBUG_FLUSH
|
||||
|
||||
/* make various TB consistency checks */
|
||||
//#define DEBUG_TB_CHECK
|
||||
|
||||
/* threshold to flush the translated code buffer */
|
||||
#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE)
|
||||
|
||||
#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64)
|
||||
|
||||
TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
|
||||
TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
|
||||
int nb_tbs;
|
||||
/* any access to the tbs or the page table must use this lock */
|
||||
spinlock_t tb_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
|
||||
uint8_t *code_gen_ptr;
|
||||
|
||||
/* XXX: pack the flags in the low bits of the pointer ? */
|
||||
typedef struct PageDesc {
|
||||
unsigned long flags;
|
||||
TranslationBlock *first_tb;
|
||||
} PageDesc;
|
||||
|
||||
#define L2_BITS 10
|
||||
#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS)
|
||||
|
||||
#define L1_SIZE (1 << L1_BITS)
|
||||
#define L2_SIZE (1 << L2_BITS)
|
||||
|
||||
static void tb_invalidate_page(unsigned long address);
|
||||
|
||||
unsigned long real_host_page_size;
|
||||
unsigned long host_page_bits;
|
||||
unsigned long host_page_size;
|
||||
unsigned long host_page_mask;
|
||||
|
||||
static PageDesc *l1_map[L1_SIZE];
|
||||
|
||||
void page_init(void)
|
||||
{
|
||||
/* NOTE: we can always suppose that host_page_size >=
|
||||
TARGET_PAGE_SIZE */
|
||||
real_host_page_size = getpagesize();
|
||||
if (host_page_size == 0)
|
||||
host_page_size = real_host_page_size;
|
||||
if (host_page_size < TARGET_PAGE_SIZE)
|
||||
host_page_size = TARGET_PAGE_SIZE;
|
||||
host_page_bits = 0;
|
||||
while ((1 << host_page_bits) < host_page_size)
|
||||
host_page_bits++;
|
||||
host_page_mask = ~(host_page_size - 1);
|
||||
}
|
||||
|
||||
/* dump memory mappings */
|
||||
void page_dump(FILE *f)
|
||||
{
|
||||
unsigned long start, end;
|
||||
int i, j, prot, prot1;
|
||||
PageDesc *p;
|
||||
|
||||
fprintf(f, "%-8s %-8s %-8s %s\n",
|
||||
"start", "end", "size", "prot");
|
||||
start = -1;
|
||||
end = -1;
|
||||
prot = 0;
|
||||
for(i = 0; i <= L1_SIZE; i++) {
|
||||
if (i < L1_SIZE)
|
||||
p = l1_map[i];
|
||||
else
|
||||
p = NULL;
|
||||
for(j = 0;j < L2_SIZE; j++) {
|
||||
if (!p)
|
||||
prot1 = 0;
|
||||
else
|
||||
prot1 = p[j].flags;
|
||||
if (prot1 != prot) {
|
||||
end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS);
|
||||
if (start != -1) {
|
||||
fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
|
||||
start, end, end - start,
|
||||
prot & PAGE_READ ? 'r' : '-',
|
||||
prot & PAGE_WRITE ? 'w' : '-',
|
||||
prot & PAGE_EXEC ? 'x' : '-');
|
||||
}
|
||||
if (prot1 != 0)
|
||||
start = end;
|
||||
else
|
||||
start = -1;
|
||||
prot = prot1;
|
||||
}
|
||||
if (!p)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline PageDesc *page_find_alloc(unsigned int index)
|
||||
{
|
||||
PageDesc **lp, *p;
|
||||
|
||||
lp = &l1_map[index >> L2_BITS];
|
||||
p = *lp;
|
||||
if (!p) {
|
||||
/* allocate if not found */
|
||||
p = malloc(sizeof(PageDesc) * L2_SIZE);
|
||||
memset(p, 0, sizeof(PageDesc) * L2_SIZE);
|
||||
*lp = p;
|
||||
}
|
||||
return p + (index & (L2_SIZE - 1));
|
||||
}
|
||||
|
||||
static inline PageDesc *page_find(unsigned int index)
|
||||
{
|
||||
PageDesc *p;
|
||||
|
||||
p = l1_map[index >> L2_BITS];
|
||||
if (!p)
|
||||
return 0;
|
||||
return p + (index & (L2_SIZE - 1));
|
||||
}
|
||||
|
||||
int page_get_flags(unsigned long address)
|
||||
{
|
||||
PageDesc *p;
|
||||
|
||||
p = page_find(address >> TARGET_PAGE_BITS);
|
||||
if (!p)
|
||||
return 0;
|
||||
return p->flags;
|
||||
}
|
||||
|
||||
/* modify the flags of a page and invalidate the code if
|
||||
necessary. The flag PAGE_WRITE_ORG is positionned automatically
|
||||
depending on PAGE_WRITE */
|
||||
void page_set_flags(unsigned long start, unsigned long end, int flags)
|
||||
{
|
||||
PageDesc *p;
|
||||
unsigned long addr;
|
||||
|
||||
start = start & TARGET_PAGE_MASK;
|
||||
end = TARGET_PAGE_ALIGN(end);
|
||||
if (flags & PAGE_WRITE)
|
||||
flags |= PAGE_WRITE_ORG;
|
||||
spin_lock(&tb_lock);
|
||||
for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
|
||||
p = page_find_alloc(addr >> TARGET_PAGE_BITS);
|
||||
/* if the write protection is set, then we invalidate the code
|
||||
inside */
|
||||
if (!(p->flags & PAGE_WRITE) &&
|
||||
(flags & PAGE_WRITE) &&
|
||||
p->first_tb) {
|
||||
tb_invalidate_page(addr);
|
||||
}
|
||||
p->flags = flags;
|
||||
}
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
|
||||
void cpu_x86_tblocks_init(void)
|
||||
{
|
||||
if (!code_gen_ptr) {
|
||||
code_gen_ptr = code_gen_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* set to NULL all the 'first_tb' fields in all PageDescs */
|
||||
static void page_flush_tb(void)
|
||||
{
|
||||
int i, j;
|
||||
PageDesc *p;
|
||||
|
||||
for(i = 0; i < L1_SIZE; i++) {
|
||||
p = l1_map[i];
|
||||
if (p) {
|
||||
for(j = 0; j < L2_SIZE; j++)
|
||||
p[j].first_tb = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* flush all the translation blocks */
|
||||
/* XXX: tb_flush is currently not thread safe */
|
||||
void tb_flush(void)
|
||||
{
|
||||
int i;
|
||||
#ifdef DEBUG_FLUSH
|
||||
printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n",
|
||||
code_gen_ptr - code_gen_buffer,
|
||||
nb_tbs,
|
||||
(code_gen_ptr - code_gen_buffer) / nb_tbs);
|
||||
#endif
|
||||
nb_tbs = 0;
|
||||
for(i = 0;i < CODE_GEN_HASH_SIZE; i++)
|
||||
tb_hash[i] = NULL;
|
||||
page_flush_tb();
|
||||
code_gen_ptr = code_gen_buffer;
|
||||
/* XXX: flush processor icache at this point if cache flush is
|
||||
expensive */
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TB_CHECK
|
||||
|
||||
static void tb_invalidate_check(unsigned long address)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
int i;
|
||||
address &= TARGET_PAGE_MASK;
|
||||
for(i = 0;i < CODE_GEN_HASH_SIZE; i++) {
|
||||
for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) {
|
||||
if (!(address + TARGET_PAGE_SIZE <= tb->pc ||
|
||||
address >= tb->pc + tb->size)) {
|
||||
printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n",
|
||||
address, tb->pc, tb->size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* verify that all the pages have correct rights for code */
|
||||
static void tb_page_check(void)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
int i, flags1, flags2;
|
||||
|
||||
for(i = 0;i < CODE_GEN_HASH_SIZE; i++) {
|
||||
for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) {
|
||||
flags1 = page_get_flags(tb->pc);
|
||||
flags2 = page_get_flags(tb->pc + tb->size - 1);
|
||||
if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) {
|
||||
printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n",
|
||||
tb->pc, tb->size, flags1, flags2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tb_jmp_check(TranslationBlock *tb)
|
||||
{
|
||||
TranslationBlock *tb1;
|
||||
unsigned int n1;
|
||||
|
||||
/* suppress any remaining jumps to this TB */
|
||||
tb1 = tb->jmp_first;
|
||||
for(;;) {
|
||||
n1 = (long)tb1 & 3;
|
||||
tb1 = (TranslationBlock *)((long)tb1 & ~3);
|
||||
if (n1 == 2)
|
||||
break;
|
||||
tb1 = tb1->jmp_next[n1];
|
||||
}
|
||||
/* check end of list */
|
||||
if (tb1 != tb) {
|
||||
printf("ERROR: jmp_list from 0x%08lx\n", (long)tb);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* invalidate one TB */
|
||||
static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb,
|
||||
int next_offset)
|
||||
{
|
||||
TranslationBlock *tb1;
|
||||
for(;;) {
|
||||
tb1 = *ptb;
|
||||
if (tb1 == tb) {
|
||||
*ptb = *(TranslationBlock **)((char *)tb1 + next_offset);
|
||||
break;
|
||||
}
|
||||
ptb = (TranslationBlock **)((char *)tb1 + next_offset);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tb_jmp_remove(TranslationBlock *tb, int n)
|
||||
{
|
||||
TranslationBlock *tb1, **ptb;
|
||||
unsigned int n1;
|
||||
|
||||
ptb = &tb->jmp_next[n];
|
||||
tb1 = *ptb;
|
||||
if (tb1) {
|
||||
/* find tb(n) in circular list */
|
||||
for(;;) {
|
||||
tb1 = *ptb;
|
||||
n1 = (long)tb1 & 3;
|
||||
tb1 = (TranslationBlock *)((long)tb1 & ~3);
|
||||
if (n1 == n && tb1 == tb)
|
||||
break;
|
||||
if (n1 == 2) {
|
||||
ptb = &tb1->jmp_first;
|
||||
} else {
|
||||
ptb = &tb1->jmp_next[n1];
|
||||
}
|
||||
}
|
||||
/* now we can suppress tb(n) from the list */
|
||||
*ptb = tb->jmp_next[n];
|
||||
|
||||
tb->jmp_next[n] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset the jump entry 'n' of a TB so that it is not chained to
|
||||
another TB */
|
||||
static inline void tb_reset_jump(TranslationBlock *tb, int n)
|
||||
{
|
||||
tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n]));
|
||||
}
|
||||
|
||||
static inline void tb_invalidate(TranslationBlock *tb, int parity)
|
||||
{
|
||||
PageDesc *p;
|
||||
unsigned int page_index1, page_index2;
|
||||
unsigned int h, n1;
|
||||
TranslationBlock *tb1, *tb2;
|
||||
|
||||
/* remove the TB from the hash list */
|
||||
h = tb_hash_func(tb->pc);
|
||||
tb_remove(&tb_hash[h], tb,
|
||||
offsetof(TranslationBlock, hash_next));
|
||||
/* remove the TB from the page list */
|
||||
page_index1 = tb->pc >> TARGET_PAGE_BITS;
|
||||
if ((page_index1 & 1) == parity) {
|
||||
p = page_find(page_index1);
|
||||
tb_remove(&p->first_tb, tb,
|
||||
offsetof(TranslationBlock, page_next[page_index1 & 1]));
|
||||
}
|
||||
page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS;
|
||||
if ((page_index2 & 1) == parity) {
|
||||
p = page_find(page_index2);
|
||||
tb_remove(&p->first_tb, tb,
|
||||
offsetof(TranslationBlock, page_next[page_index2 & 1]));
|
||||
}
|
||||
|
||||
/* suppress this TB from the two jump lists */
|
||||
tb_jmp_remove(tb, 0);
|
||||
tb_jmp_remove(tb, 1);
|
||||
|
||||
/* suppress any remaining jumps to this TB */
|
||||
tb1 = tb->jmp_first;
|
||||
for(;;) {
|
||||
n1 = (long)tb1 & 3;
|
||||
if (n1 == 2)
|
||||
break;
|
||||
tb1 = (TranslationBlock *)((long)tb1 & ~3);
|
||||
tb2 = tb1->jmp_next[n1];
|
||||
tb_reset_jump(tb1, n1);
|
||||
tb1->jmp_next[n1] = NULL;
|
||||
tb1 = tb2;
|
||||
}
|
||||
tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */
|
||||
}
|
||||
|
||||
/* invalidate all TBs which intersect with the target page starting at addr */
|
||||
static void tb_invalidate_page(unsigned long address)
|
||||
{
|
||||
TranslationBlock *tb_next, *tb;
|
||||
unsigned int page_index;
|
||||
int parity1, parity2;
|
||||
PageDesc *p;
|
||||
#ifdef DEBUG_TB_INVALIDATE
|
||||
printf("tb_invalidate_page: %lx\n", address);
|
||||
#endif
|
||||
|
||||
page_index = address >> TARGET_PAGE_BITS;
|
||||
p = page_find(page_index);
|
||||
if (!p)
|
||||
return;
|
||||
tb = p->first_tb;
|
||||
parity1 = page_index & 1;
|
||||
parity2 = parity1 ^ 1;
|
||||
while (tb != NULL) {
|
||||
tb_next = tb->page_next[parity1];
|
||||
tb_invalidate(tb, parity2);
|
||||
tb = tb_next;
|
||||
}
|
||||
p->first_tb = NULL;
|
||||
}
|
||||
|
||||
/* add the tb in the target page and protect it if necessary */
|
||||
static inline void tb_alloc_page(TranslationBlock *tb, unsigned int page_index)
|
||||
{
|
||||
PageDesc *p;
|
||||
unsigned long host_start, host_end, addr, page_addr;
|
||||
int prot;
|
||||
|
||||
p = page_find_alloc(page_index);
|
||||
tb->page_next[page_index & 1] = p->first_tb;
|
||||
p->first_tb = tb;
|
||||
if (p->flags & PAGE_WRITE) {
|
||||
/* force the host page as non writable (writes will have a
|
||||
page fault + mprotect overhead) */
|
||||
page_addr = (page_index << TARGET_PAGE_BITS);
|
||||
host_start = page_addr & host_page_mask;
|
||||
host_end = host_start + host_page_size;
|
||||
prot = 0;
|
||||
for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE)
|
||||
prot |= page_get_flags(addr);
|
||||
mprotect((void *)host_start, host_page_size,
|
||||
(prot & PAGE_BITS) & ~PAGE_WRITE);
|
||||
#ifdef DEBUG_TB_INVALIDATE
|
||||
printf("protecting code page: 0x%08lx\n",
|
||||
host_start);
|
||||
#endif
|
||||
p->flags &= ~PAGE_WRITE;
|
||||
#ifdef DEBUG_TB_CHECK
|
||||
tb_page_check();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate a new translation block. Flush the translation buffer if
|
||||
too many translation blocks or too much generated code. */
|
||||
TranslationBlock *tb_alloc(unsigned long pc)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
|
||||
if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
|
||||
(code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
|
||||
return NULL;
|
||||
tb = &tbs[nb_tbs++];
|
||||
tb->pc = pc;
|
||||
return tb;
|
||||
}
|
||||
|
||||
/* link the tb with the other TBs */
|
||||
void tb_link(TranslationBlock *tb)
|
||||
{
|
||||
unsigned int page_index1, page_index2;
|
||||
|
||||
/* add in the page list */
|
||||
page_index1 = tb->pc >> TARGET_PAGE_BITS;
|
||||
tb_alloc_page(tb, page_index1);
|
||||
page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS;
|
||||
if (page_index2 != page_index1) {
|
||||
tb_alloc_page(tb, page_index2);
|
||||
}
|
||||
tb->jmp_first = (TranslationBlock *)((long)tb | 2);
|
||||
tb->jmp_next[0] = NULL;
|
||||
tb->jmp_next[1] = NULL;
|
||||
|
||||
/* init original jump addresses */
|
||||
if (tb->tb_next_offset[0] != 0xffff)
|
||||
tb_reset_jump(tb, 0);
|
||||
if (tb->tb_next_offset[1] != 0xffff)
|
||||
tb_reset_jump(tb, 1);
|
||||
}
|
||||
|
||||
/* called from signal handler: invalidate the code and unprotect the
|
||||
page. Return TRUE if the fault was succesfully handled. */
|
||||
int page_unprotect(unsigned long address)
|
||||
{
|
||||
unsigned int page_index, prot, pindex;
|
||||
PageDesc *p, *p1;
|
||||
unsigned long host_start, host_end, addr;
|
||||
|
||||
host_start = address & host_page_mask;
|
||||
page_index = host_start >> TARGET_PAGE_BITS;
|
||||
p1 = page_find(page_index);
|
||||
if (!p1)
|
||||
return 0;
|
||||
host_end = host_start + host_page_size;
|
||||
p = p1;
|
||||
prot = 0;
|
||||
for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= p->flags;
|
||||
p++;
|
||||
}
|
||||
/* if the page was really writable, then we change its
|
||||
protection back to writable */
|
||||
if (prot & PAGE_WRITE_ORG) {
|
||||
mprotect((void *)host_start, host_page_size,
|
||||
(prot & PAGE_BITS) | PAGE_WRITE);
|
||||
pindex = (address - host_start) >> TARGET_PAGE_BITS;
|
||||
p1[pindex].flags |= PAGE_WRITE;
|
||||
/* and since the content will be modified, we must invalidate
|
||||
the corresponding translated code. */
|
||||
tb_invalidate_page(address);
|
||||
#ifdef DEBUG_TB_CHECK
|
||||
tb_invalidate_check(address);
|
||||
#endif
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* call this function when system calls directly modify a memory area */
|
||||
void page_unprotect_range(uint8_t *data, unsigned long data_size)
|
||||
{
|
||||
unsigned long start, end, addr;
|
||||
|
||||
start = (unsigned long)data;
|
||||
end = start + data_size;
|
||||
start &= TARGET_PAGE_MASK;
|
||||
end = TARGET_PAGE_ALIGN(end);
|
||||
for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
|
||||
page_unprotect(addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr <
|
||||
tb[1].tc_ptr. Return NULL if not found */
|
||||
TranslationBlock *tb_find_pc(unsigned long tc_ptr)
|
||||
{
|
||||
int m_min, m_max, m;
|
||||
unsigned long v;
|
||||
TranslationBlock *tb;
|
||||
|
||||
if (nb_tbs <= 0)
|
||||
return NULL;
|
||||
if (tc_ptr < (unsigned long)code_gen_buffer ||
|
||||
tc_ptr >= (unsigned long)code_gen_ptr)
|
||||
return NULL;
|
||||
/* binary search (cf Knuth) */
|
||||
m_min = 0;
|
||||
m_max = nb_tbs - 1;
|
||||
while (m_min <= m_max) {
|
||||
m = (m_min + m_max) >> 1;
|
||||
tb = &tbs[m];
|
||||
v = (unsigned long)tb->tc_ptr;
|
||||
if (v == tc_ptr)
|
||||
return tb;
|
||||
else if (tc_ptr < v) {
|
||||
m_max = m - 1;
|
||||
} else {
|
||||
m_min = m + 1;
|
||||
}
|
||||
}
|
||||
return &tbs[m_max];
|
||||
}
|
269
exec.h
Normal file
269
exec.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* internal execution defines for qemu
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define GEN_FLAG_CODE32_SHIFT 0
|
||||
#define GEN_FLAG_ADDSEG_SHIFT 1
|
||||
#define GEN_FLAG_SS32_SHIFT 2
|
||||
#define GEN_FLAG_VM_SHIFT 3
|
||||
#define GEN_FLAG_ST_SHIFT 4
|
||||
#define GEN_FLAG_TF_SHIFT 8 /* same position as eflags */
|
||||
#define GEN_FLAG_CPL_SHIFT 9
|
||||
#define GEN_FLAG_IOPL_SHIFT 12 /* same position as eflags */
|
||||
|
||||
struct TranslationBlock;
|
||||
int cpu_x86_gen_code(struct TranslationBlock *tb,
|
||||
int max_code_size, int *gen_code_size_ptr);
|
||||
int cpu_x86_search_pc(struct TranslationBlock *tb,
|
||||
uint32_t *found_pc, unsigned long searched_pc);
|
||||
void cpu_x86_tblocks_init(void);
|
||||
void page_init(void);
|
||||
int page_unprotect(unsigned long address);
|
||||
|
||||
#define CODE_GEN_MAX_SIZE 65536
|
||||
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
||||
|
||||
#define CODE_GEN_HASH_BITS 15
|
||||
#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS)
|
||||
|
||||
/* maximum total translate dcode allocated */
|
||||
#define CODE_GEN_BUFFER_SIZE (2048 * 1024)
|
||||
//#define CODE_GEN_BUFFER_SIZE (128 * 1024)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
#define USE_DIRECT_JUMP
|
||||
#endif
|
||||
|
||||
typedef struct TranslationBlock {
|
||||
unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */
|
||||
unsigned long cs_base; /* CS base for this block */
|
||||
unsigned int flags; /* flags defining in which context the code was generated */
|
||||
uint16_t size; /* size of target code for this block (1 <=
|
||||
size <= TARGET_PAGE_SIZE) */
|
||||
uint8_t *tc_ptr; /* pointer to the translated code */
|
||||
struct TranslationBlock *hash_next; /* next matching block */
|
||||
struct TranslationBlock *page_next[2]; /* next blocks in even/odd page */
|
||||
/* the following data are used to directly call another TB from
|
||||
the code of this one. */
|
||||
uint16_t tb_next_offset[2]; /* offset of original jump target */
|
||||
#ifdef USE_DIRECT_JUMP
|
||||
uint16_t tb_jmp_offset[2]; /* offset of jump instruction */
|
||||
#else
|
||||
uint8_t *tb_next[2]; /* address of jump generated code */
|
||||
#endif
|
||||
/* list of TBs jumping to this one. This is a circular list using
|
||||
the two least significant bits of the pointers to tell what is
|
||||
the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 =
|
||||
jmp_first */
|
||||
struct TranslationBlock *jmp_next[2];
|
||||
struct TranslationBlock *jmp_first;
|
||||
} TranslationBlock;
|
||||
|
||||
static inline unsigned int tb_hash_func(unsigned long pc)
|
||||
{
|
||||
return pc & (CODE_GEN_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
TranslationBlock *tb_alloc(unsigned long pc);
|
||||
void tb_flush(void);
|
||||
void tb_link(TranslationBlock *tb);
|
||||
|
||||
extern TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
|
||||
|
||||
extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
|
||||
extern uint8_t *code_gen_ptr;
|
||||
|
||||
/* find a translation block in the translation cache. If not found,
|
||||
return NULL and the pointer to the last element of the list in pptb */
|
||||
static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
|
||||
unsigned long pc,
|
||||
unsigned long cs_base,
|
||||
unsigned int flags)
|
||||
{
|
||||
TranslationBlock **ptb, *tb;
|
||||
unsigned int h;
|
||||
|
||||
h = tb_hash_func(pc);
|
||||
ptb = &tb_hash[h];
|
||||
for(;;) {
|
||||
tb = *ptb;
|
||||
if (!tb)
|
||||
break;
|
||||
if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags)
|
||||
return tb;
|
||||
ptb = &tb->hash_next;
|
||||
}
|
||||
*pptb = ptb;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
static inline void tb_set_jmp_target(TranslationBlock *tb,
|
||||
int n, unsigned long addr)
|
||||
{
|
||||
uint32_t val, *ptr;
|
||||
unsigned long offset;
|
||||
|
||||
offset = (unsigned long)(tb->tc_ptr + tb->tb_jmp_offset[n]);
|
||||
|
||||
/* patch the branch destination */
|
||||
ptr = (uint32_t *)offset;
|
||||
val = *ptr;
|
||||
val = (val & ~0x03fffffc) | ((addr - offset) & 0x03fffffc);
|
||||
*ptr = val;
|
||||
/* flush icache */
|
||||
asm volatile ("dcbst 0,%0" : : "r"(ptr) : "memory");
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("icbi 0,%0" : : "r"(ptr) : "memory");
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("isync" : : : "memory");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* set the jump target */
|
||||
static inline void tb_set_jmp_target(TranslationBlock *tb,
|
||||
int n, unsigned long addr)
|
||||
{
|
||||
tb->tb_next[n] = (void *)addr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
TranslationBlock *tb_next)
|
||||
{
|
||||
/* NOTE: this test is only needed for thread safety */
|
||||
if (!tb->jmp_next[n]) {
|
||||
/* patch the native jump address */
|
||||
tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr);
|
||||
|
||||
/* add in TB jmp circular list */
|
||||
tb->jmp_next[n] = tb_next->jmp_first;
|
||||
tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
|
||||
}
|
||||
}
|
||||
|
||||
TranslationBlock *tb_find_pc(unsigned long pc_ptr);
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
__asm__ __volatile__ (
|
||||
"0: lwarx %0,0,%1 ;"
|
||||
" xor. %0,%3,%0;"
|
||||
" bne 1f;"
|
||||
" stwcx. %2,0,%1;"
|
||||
" bne- 0b;"
|
||||
"1: "
|
||||
: "=&r" (ret)
|
||||
: "r" (p), "r" (1), "r" (0)
|
||||
: "cr0", "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
char ret;
|
||||
long int readval;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
|
||||
: "=q" (ret), "=m" (*p), "=a" (readval)
|
||||
: "r" (1), "m" (*p), "a" (0)
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __s390__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__ ("0: cs %0,%1,0(%2)\n"
|
||||
" jl 0b"
|
||||
: "=&d" (ret)
|
||||
: "r" (1), "a" (p), "0" (*p)
|
||||
: "cc", "memory" );
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
unsigned long one;
|
||||
|
||||
__asm__ __volatile__ ("0: mov 1,%2\n"
|
||||
" ldl_l %0,%1\n"
|
||||
" stl_c %2,%1\n"
|
||||
" beq %2,1f\n"
|
||||
".subsection 2\n"
|
||||
"1: br 0b\n"
|
||||
".previous"
|
||||
: "=r" (ret), "=m" (*p), "=r" (one)
|
||||
: "m" (*p));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __sparc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__("ldstub [%1], %0"
|
||||
: "=r" (ret)
|
||||
: "r" (p)
|
||||
: "memory");
|
||||
|
||||
return (ret ? 1 : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef int spinlock_t;
|
||||
|
||||
#define SPIN_LOCK_UNLOCKED 0
|
||||
|
||||
static inline void spin_lock(spinlock_t *lock)
|
||||
{
|
||||
while (testandset(lock));
|
||||
}
|
||||
|
||||
static inline void spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline int spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
return !testandset(lock);
|
||||
}
|
||||
|
||||
extern spinlock_t tb_lock;
|
||||
|
@@ -33,12 +33,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
* the Intel manual for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "dis-asm.h"
|
||||
|
||||
#define MAXLEN 20
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
static int fetch_data PARAMS ((struct disassemble_info *, bfd_byte *));
|
||||
|
||||
struct dis_private
|
||||
|
32
ia64-syscall.S
Normal file
32
ia64-syscall.S
Normal file
@@ -0,0 +1,32 @@
|
||||
/* derived from glibc sysdeps/unix/sysv/linux/ia64/sysdep.S */
|
||||
|
||||
#define __ASSEMBLY__
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
ENTRY(__syscall_error)
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(0)
|
||||
alloc r33=ar.pfs, 0, 4, 0, 0
|
||||
mov r32=rp
|
||||
.body
|
||||
mov r35=r8
|
||||
mov r34=r1
|
||||
;;
|
||||
br.call.sptk.many b0 = __errno_location
|
||||
.Lret0: /* force new bundle */
|
||||
st4 [r8]=r35
|
||||
mov r1=r34
|
||||
mov rp=r32
|
||||
mov r8=-1
|
||||
mov ar.pfs=r33
|
||||
br.ret.sptk.few b0
|
||||
END(__syscall_error)
|
||||
|
||||
GLOBAL_ENTRY(__ia64_syscall)
|
||||
mov r15=r37 /* syscall number */
|
||||
break __BREAK_SYSCALL
|
||||
cmp.eq p6,p0=-1,r10 /* r10 = -1 on error */
|
||||
(p6) br.cond.spnt.few __syscall_error
|
||||
br.ret.sptk.few b0
|
||||
.endp __ia64_syscall
|
@@ -11,29 +11,121 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "qemu.h"
|
||||
#include "disas.h"
|
||||
|
||||
#ifdef TARGET_I386
|
||||
|
||||
#define ELF_START_MMAP 0x80000000
|
||||
|
||||
typedef uint32_t elf_greg_t;
|
||||
|
||||
#define ELF_NGREG (sizeof (struct target_pt_regs) / sizeof(elf_greg_t))
|
||||
typedef elf_greg_t elf_gregset_t[ELF_NGREG];
|
||||
|
||||
typedef struct user_i387_struct elf_fpregset_t;
|
||||
|
||||
/*
|
||||
* This is used to ensure we don't load something for the wrong architecture.
|
||||
*/
|
||||
#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) )
|
||||
|
||||
/*
|
||||
* These are used to set parameters in the core dumps.
|
||||
*/
|
||||
#define ELF_CLASS ELFCLASS32
|
||||
#define ELF_DATA ELFDATA2LSB
|
||||
#define ELF_ARCH EM_386
|
||||
|
||||
/* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program
|
||||
starts %edx contains a pointer to a function which might be
|
||||
registered using `atexit'. This provides a mean for the
|
||||
dynamic linker to call DT_FINI functions for shared libraries
|
||||
that have been loaded before the code runs.
|
||||
|
||||
A value of 0 tells we have no such handler. */
|
||||
#define ELF_PLAT_INIT(_r) _r->edx = 0
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
#endif
|
||||
|
||||
#include "linux_bin.h"
|
||||
#include "elf.h"
|
||||
#include "segment.h"
|
||||
|
||||
/*
|
||||
* MAX_ARG_PAGES defines the number of pages allocated for arguments
|
||||
* and envelope for the new program. 32 should suffice, this gives
|
||||
* a maximum env+arg of 128kB w/4KB pages!
|
||||
*/
|
||||
#define MAX_ARG_PAGES 32
|
||||
|
||||
/*
|
||||
* This structure is used to hold the arguments that are
|
||||
* used when loading binaries.
|
||||
*/
|
||||
struct linux_binprm {
|
||||
char buf[128];
|
||||
unsigned long page[MAX_ARG_PAGES];
|
||||
unsigned long p;
|
||||
int sh_bang;
|
||||
int fd;
|
||||
int e_uid, e_gid;
|
||||
int argc, envc;
|
||||
char * filename; /* Name of binary */
|
||||
unsigned long loader, exec;
|
||||
int dont_iput; /* binfmt handler has put inode */
|
||||
};
|
||||
|
||||
struct exec
|
||||
{
|
||||
unsigned int a_info; /* Use macros N_MAGIC, etc for access */
|
||||
unsigned int a_text; /* length of text, in bytes */
|
||||
unsigned int a_data; /* length of data, in bytes */
|
||||
unsigned int a_bss; /* length of uninitialized data area, in bytes */
|
||||
unsigned int a_syms; /* length of symbol table data in file, in bytes */
|
||||
unsigned int a_entry; /* start address */
|
||||
unsigned int a_trsize; /* length of relocation info for text, in bytes */
|
||||
unsigned int a_drsize; /* length of relocation info for data, in bytes */
|
||||
};
|
||||
|
||||
|
||||
#define N_MAGIC(exec) ((exec).a_info & 0xffff)
|
||||
#define OMAGIC 0407
|
||||
#define NMAGIC 0410
|
||||
#define ZMAGIC 0413
|
||||
#define QMAGIC 0314
|
||||
|
||||
/* max code+data+bss space allocated to elf interpreter */
|
||||
#define INTERP_MAP_SIZE (32 * 1024 * 1024)
|
||||
|
||||
/* max code+data+bss+brk space allocated to ET_DYN executables */
|
||||
#define ET_DYN_MAP_SIZE (128 * 1024 * 1024)
|
||||
|
||||
/* from personality.h */
|
||||
|
||||
/* Flags for bug emulation. These occupy the top three bytes. */
|
||||
#define STICKY_TIMEOUTS 0x4000000
|
||||
#define WHOLE_SECONDS 0x2000000
|
||||
|
||||
/* Personality types. These go in the low byte. Avoid using the top bit,
|
||||
* it will conflict with error returns.
|
||||
*/
|
||||
#define PER_MASK (0x00ff)
|
||||
#define PER_LINUX (0x0000)
|
||||
#define PER_SVR4 (0x0001 | STICKY_TIMEOUTS)
|
||||
#define PER_SVR3 (0x0002 | STICKY_TIMEOUTS)
|
||||
#define PER_SCOSVR3 (0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS)
|
||||
#define PER_WYSEV386 (0x0004 | STICKY_TIMEOUTS)
|
||||
#define PER_ISCR4 (0x0005 | STICKY_TIMEOUTS)
|
||||
#define PER_BSD (0x0006)
|
||||
#define PER_XENIX (0x0007 | STICKY_TIMEOUTS)
|
||||
|
||||
/* Necessary parameters */
|
||||
#define ALPHA_PAGE_SIZE 4096
|
||||
#define X86_PAGE_SIZE 4096
|
||||
|
||||
#define ALPHA_PAGE_MASK (~(ALPHA_PAGE_SIZE-1))
|
||||
#define X86_PAGE_MASK (~(X86_PAGE_SIZE-1))
|
||||
|
||||
#define ALPHA_PAGE_ALIGN(addr) ((((addr)+ALPHA_PAGE_SIZE)-1)&ALPHA_PAGE_MASK)
|
||||
#define X86_PAGE_ALIGN(addr) ((((addr)+X86_PAGE_SIZE)-1)&X86_PAGE_MASK)
|
||||
|
||||
#define NGROUPS 32
|
||||
|
||||
#define X86_ELF_EXEC_PAGESIZE X86_PAGE_SIZE
|
||||
#define X86_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(X86_ELF_EXEC_PAGESIZE-1))
|
||||
#define X86_ELF_PAGEOFFSET(_v) ((_v) & (X86_ELF_EXEC_PAGESIZE-1))
|
||||
|
||||
#define ALPHA_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ALPHA_PAGE_SIZE-1))
|
||||
#define ALPHA_ELF_PAGEOFFSET(_v) ((_v) & (ALPHA_PAGE_SIZE-1))
|
||||
#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
|
||||
#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
|
||||
#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
|
||||
|
||||
#define INTERPRETER_NONE 0
|
||||
#define INTERPRETER_AOUT 1
|
||||
@@ -41,12 +133,18 @@
|
||||
|
||||
#define DLINFO_ITEMS 12
|
||||
|
||||
/* Where we find X86 libraries... */
|
||||
//#define X86_DEFAULT_LIB_DIR "/usr/x86/"
|
||||
#define X86_DEFAULT_LIB_DIR "/"
|
||||
#define put_user(x,ptr) (void)(*(ptr) = (typeof(*ptr))(x))
|
||||
#define get_user(ptr) (typeof(*ptr))(*(ptr))
|
||||
|
||||
//extern void * mmap4k();
|
||||
#define mmap4k(a, b, c, d, e, f) mmap((void *)(a), b, c, d, e, f)
|
||||
static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
|
||||
{
|
||||
memcpy(to, from, n);
|
||||
}
|
||||
|
||||
static inline void memcpy_tofs(void * to, const void * from, unsigned long n)
|
||||
{
|
||||
memcpy(to, from, n);
|
||||
}
|
||||
|
||||
extern unsigned long x86_stack_size;
|
||||
|
||||
@@ -81,6 +179,28 @@ static void bswap_phdr(Elf32_Phdr *phdr)
|
||||
bswap32s(&phdr->p_flags); /* Segment flags */
|
||||
bswap32s(&phdr->p_align); /* Segment alignment */
|
||||
}
|
||||
|
||||
static void bswap_shdr(Elf32_Shdr *shdr)
|
||||
{
|
||||
bswap32s(&shdr->sh_name);
|
||||
bswap32s(&shdr->sh_type);
|
||||
bswap32s(&shdr->sh_flags);
|
||||
bswap32s(&shdr->sh_addr);
|
||||
bswap32s(&shdr->sh_offset);
|
||||
bswap32s(&shdr->sh_size);
|
||||
bswap32s(&shdr->sh_link);
|
||||
bswap32s(&shdr->sh_info);
|
||||
bswap32s(&shdr->sh_addralign);
|
||||
bswap32s(&shdr->sh_entsize);
|
||||
}
|
||||
|
||||
static void bswap_sym(Elf32_Sym *sym)
|
||||
{
|
||||
bswap32s(&sym->st_name);
|
||||
bswap32s(&sym->st_value);
|
||||
bswap32s(&sym->st_size);
|
||||
bswap16s(&sym->st_shndx);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void * get_free_page(void)
|
||||
@@ -90,8 +210,8 @@ static void * get_free_page(void)
|
||||
/* User-space version of kernel get_free_page. Returns a page-aligned
|
||||
* page-sized chunk of memory.
|
||||
*/
|
||||
retval = mmap4k(0, ALPHA_PAGE_SIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
retval = (void *)target_mmap(0, host_page_size, PROT_READ|PROT_WRITE,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
if((long)retval == -1) {
|
||||
perror("get_free_page");
|
||||
@@ -104,7 +224,7 @@ static void * get_free_page(void)
|
||||
|
||||
static void free_page(void * pageaddr)
|
||||
{
|
||||
(void)munmap(pageaddr, ALPHA_PAGE_SIZE);
|
||||
target_munmap((unsigned long)pageaddr, host_page_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -135,9 +255,9 @@ static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
|
||||
while (len) {
|
||||
--p; --tmp; --len;
|
||||
if (--offset < 0) {
|
||||
offset = p % X86_PAGE_SIZE;
|
||||
if (!(pag = (char *) page[p/X86_PAGE_SIZE]) &&
|
||||
!(pag = (char *) page[p/X86_PAGE_SIZE] =
|
||||
offset = p % TARGET_PAGE_SIZE;
|
||||
if (!(pag = (char *) page[p/TARGET_PAGE_SIZE]) &&
|
||||
!(pag = (char *) page[p/TARGET_PAGE_SIZE] =
|
||||
(unsigned long *) get_free_page())) {
|
||||
return 0;
|
||||
}
|
||||
@@ -246,51 +366,43 @@ static int prepare_binprm(struct linux_binprm *bprm)
|
||||
unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm,
|
||||
struct image_info * info)
|
||||
{
|
||||
unsigned long stack_base;
|
||||
unsigned long stack_base, size, error;
|
||||
int i;
|
||||
extern unsigned long stktop;
|
||||
|
||||
stack_base = X86_STACK_TOP - MAX_ARG_PAGES*X86_PAGE_SIZE;
|
||||
/* Create enough stack to hold everything. If we don't use
|
||||
* it for args, we'll use it for something else...
|
||||
*/
|
||||
size = x86_stack_size;
|
||||
if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE)
|
||||
size = MAX_ARG_PAGES*TARGET_PAGE_SIZE;
|
||||
error = target_mmap(0,
|
||||
size + host_page_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
if (error == -1) {
|
||||
perror("stk mmap");
|
||||
exit(-1);
|
||||
}
|
||||
/* we reserve one extra page at the top of the stack as guard */
|
||||
target_mprotect(error + size, host_page_size, PROT_NONE);
|
||||
|
||||
stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
|
||||
p += stack_base;
|
||||
|
||||
if (bprm->loader) {
|
||||
bprm->loader += stack_base;
|
||||
}
|
||||
bprm->exec += stack_base;
|
||||
|
||||
/* Create enough stack to hold everything. If we don't use
|
||||
* it for args, we'll use it for something else...
|
||||
*/
|
||||
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
|
||||
we allocate a bigger stack. Need a better solution, for example
|
||||
by remapping the process stack directly at the right place */
|
||||
if(x86_stack_size > MAX_ARG_PAGES*X86_PAGE_SIZE) {
|
||||
if((long)mmap4k((void *)(X86_STACK_TOP-x86_stack_size), x86_stack_size + X86_PAGE_SIZE,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_GROWSDOWN | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) {
|
||||
perror("stk mmap");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if((long)mmap4k((void *)stack_base, (MAX_ARG_PAGES+1)*X86_PAGE_SIZE,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_GROWSDOWN | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) {
|
||||
perror("stk mmap");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
stktop = stack_base;
|
||||
|
||||
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
|
||||
if (bprm->page[i]) {
|
||||
info->rss++;
|
||||
|
||||
memcpy((void *)stack_base, (void *)bprm->page[i], X86_PAGE_SIZE);
|
||||
memcpy((void *)stack_base, (void *)bprm->page[i], TARGET_PAGE_SIZE);
|
||||
free_page((void *)bprm->page[i]);
|
||||
}
|
||||
stack_base += X86_PAGE_SIZE;
|
||||
stack_base += TARGET_PAGE_SIZE;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -298,13 +410,13 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm,
|
||||
static void set_brk(unsigned long start, unsigned long end)
|
||||
{
|
||||
/* page-align the start and end addresses... */
|
||||
start = ALPHA_PAGE_ALIGN(start);
|
||||
end = ALPHA_PAGE_ALIGN(end);
|
||||
start = HOST_PAGE_ALIGN(start);
|
||||
end = HOST_PAGE_ALIGN(end);
|
||||
if (end <= start)
|
||||
return;
|
||||
if((long)mmap4k(start, end - start,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) {
|
||||
if(target_mmap(start, end - start,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) {
|
||||
perror("cannot mmap brk");
|
||||
exit(-1);
|
||||
}
|
||||
@@ -322,9 +434,9 @@ static void padzero(unsigned long elf_bss)
|
||||
unsigned long nbyte;
|
||||
char * fpnt;
|
||||
|
||||
nbyte = elf_bss & (ALPHA_PAGE_SIZE-1); /* was X86_PAGE_SIZE - JRP */
|
||||
nbyte = elf_bss & (host_page_size-1); /* was TARGET_PAGE_SIZE - JRP */
|
||||
if (nbyte) {
|
||||
nbyte = ALPHA_PAGE_SIZE - nbyte;
|
||||
nbyte = host_page_size - nbyte;
|
||||
fpnt = (char *) elf_bss;
|
||||
do {
|
||||
*fpnt++ = 0;
|
||||
@@ -333,10 +445,11 @@ static void padzero(unsigned long elf_bss)
|
||||
}
|
||||
|
||||
static unsigned int * create_elf_tables(char *p, int argc, int envc,
|
||||
struct elfhdr * exec,
|
||||
unsigned long load_addr,
|
||||
unsigned long interp_load_addr, int ibcs,
|
||||
struct image_info *info)
|
||||
struct elfhdr * exec,
|
||||
unsigned long load_addr,
|
||||
unsigned long load_bias,
|
||||
unsigned long interp_load_addr, int ibcs,
|
||||
struct image_info *info)
|
||||
{
|
||||
target_ulong *argv, *envp, *dlinfo;
|
||||
target_ulong *sp;
|
||||
@@ -361,20 +474,17 @@ static unsigned int * create_elf_tables(char *p, int argc, int envc,
|
||||
put_user (tswapl(val), dlinfo++)
|
||||
|
||||
if (exec) { /* Put this here for an ELF program interpreter */
|
||||
struct elf_phdr * eppnt;
|
||||
eppnt = (struct elf_phdr *)((unsigned long)exec->e_phoff);
|
||||
|
||||
NEW_AUX_ENT (AT_PHDR, (unsigned int)(load_addr + exec->e_phoff));
|
||||
NEW_AUX_ENT (AT_PHENT, (unsigned int)(sizeof (struct elf_phdr)));
|
||||
NEW_AUX_ENT (AT_PHNUM, (unsigned int)(exec->e_phnum));
|
||||
NEW_AUX_ENT (AT_PAGESZ, (unsigned int)(ALPHA_PAGE_SIZE));
|
||||
NEW_AUX_ENT (AT_BASE, (unsigned int)(interp_load_addr));
|
||||
NEW_AUX_ENT (AT_FLAGS, (unsigned int)0);
|
||||
NEW_AUX_ENT (AT_ENTRY, (unsigned int) exec->e_entry);
|
||||
NEW_AUX_ENT (AT_UID, (unsigned int) getuid());
|
||||
NEW_AUX_ENT (AT_EUID, (unsigned int) geteuid());
|
||||
NEW_AUX_ENT (AT_GID, (unsigned int) getgid());
|
||||
NEW_AUX_ENT (AT_EGID, (unsigned int) getegid());
|
||||
NEW_AUX_ENT (AT_PHDR, (target_ulong)(load_addr + exec->e_phoff));
|
||||
NEW_AUX_ENT (AT_PHENT, (target_ulong)(sizeof (struct elf_phdr)));
|
||||
NEW_AUX_ENT (AT_PHNUM, (target_ulong)(exec->e_phnum));
|
||||
NEW_AUX_ENT (AT_PAGESZ, (target_ulong)(TARGET_PAGE_SIZE));
|
||||
NEW_AUX_ENT (AT_BASE, (target_ulong)(interp_load_addr));
|
||||
NEW_AUX_ENT (AT_FLAGS, (target_ulong)0);
|
||||
NEW_AUX_ENT (AT_ENTRY, load_bias + exec->e_entry);
|
||||
NEW_AUX_ENT (AT_UID, (target_ulong) getuid());
|
||||
NEW_AUX_ENT (AT_EUID, (target_ulong) geteuid());
|
||||
NEW_AUX_ENT (AT_GID, (target_ulong) getgid());
|
||||
NEW_AUX_ENT (AT_EGID, (target_ulong) getegid());
|
||||
}
|
||||
NEW_AUX_ENT (AT_NULL, 0);
|
||||
#undef NEW_AUX_ENT
|
||||
@@ -403,7 +513,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
{
|
||||
struct elf_phdr *elf_phdata = NULL;
|
||||
struct elf_phdr *eppnt;
|
||||
unsigned long load_addr;
|
||||
unsigned long load_addr = 0;
|
||||
int load_addr_set = 0;
|
||||
int retval;
|
||||
unsigned long last_bss, elf_bss;
|
||||
@@ -414,21 +524,20 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
last_bss = 0;
|
||||
error = 0;
|
||||
|
||||
/* We put this here so that mmap will search for the *first*
|
||||
* available memory...
|
||||
*/
|
||||
load_addr = INTERP_LOADADDR;
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
bswap_ehdr(interp_elf_ex);
|
||||
#endif
|
||||
/* First of all, some simple consistency checks */
|
||||
if ((interp_elf_ex->e_type != ET_EXEC &&
|
||||
interp_elf_ex->e_type != ET_DYN) ||
|
||||
interp_elf_ex->e_type != ET_DYN) ||
|
||||
!elf_check_arch(interp_elf_ex->e_machine)) {
|
||||
return ~0UL;
|
||||
}
|
||||
|
||||
|
||||
/* Now read in all of the header information */
|
||||
|
||||
if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > X86_PAGE_SIZE)
|
||||
if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE)
|
||||
return ~0UL;
|
||||
|
||||
elf_phdata = (struct elf_phdr *)
|
||||
@@ -441,11 +550,10 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
* If the size of this structure has changed, then punt, since
|
||||
* we will be doing the wrong thing.
|
||||
*/
|
||||
if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
|
||||
{
|
||||
if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) {
|
||||
free(elf_phdata);
|
||||
return ~0UL;
|
||||
}
|
||||
}
|
||||
|
||||
retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET);
|
||||
if(retval >= 0) {
|
||||
@@ -453,7 +561,6 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
(char *) elf_phdata,
|
||||
sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
|
||||
}
|
||||
|
||||
if (retval < 0) {
|
||||
perror("load_elf_interp");
|
||||
exit(-1);
|
||||
@@ -466,6 +573,21 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
bswap_phdr(eppnt);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (interp_elf_ex->e_type == ET_DYN) {
|
||||
/* in order to avoid harcoding the interpreter load
|
||||
address in qemu, we allocate a big enough memory zone */
|
||||
error = target_mmap(0, INTERP_MAP_SIZE,
|
||||
PROT_NONE, MAP_PRIVATE | MAP_ANON,
|
||||
-1, 0);
|
||||
if (error == -1) {
|
||||
perror("mmap");
|
||||
exit(-1);
|
||||
}
|
||||
load_addr = error;
|
||||
load_addr_set = 1;
|
||||
}
|
||||
|
||||
eppnt = elf_phdata;
|
||||
for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
|
||||
if (eppnt->p_type == PT_LOAD) {
|
||||
@@ -481,12 +603,12 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
elf_type |= MAP_FIXED;
|
||||
vaddr = eppnt->p_vaddr;
|
||||
}
|
||||
error = (unsigned long)mmap4k(load_addr+X86_ELF_PAGESTART(vaddr),
|
||||
eppnt->p_filesz + X86_ELF_PAGEOFFSET(eppnt->p_vaddr),
|
||||
error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr),
|
||||
eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr),
|
||||
elf_prot,
|
||||
elf_type,
|
||||
interpreter_fd,
|
||||
eppnt->p_offset - X86_ELF_PAGEOFFSET(eppnt->p_vaddr));
|
||||
eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr));
|
||||
|
||||
if (error > -1024UL) {
|
||||
/* Real error */
|
||||
@@ -526,13 +648,13 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
* bss page.
|
||||
*/
|
||||
padzero(elf_bss);
|
||||
elf_bss = X86_ELF_PAGESTART(elf_bss + ALPHA_PAGE_SIZE - 1); /* What we have mapped so far */
|
||||
elf_bss = TARGET_ELF_PAGESTART(elf_bss + host_page_size - 1); /* What we have mapped so far */
|
||||
|
||||
/* Map the last of the bss segment */
|
||||
if (last_bss > elf_bss) {
|
||||
mmap4k(elf_bss, last_bss-elf_bss,
|
||||
PROT_READ|PROT_WRITE|PROT_EXEC,
|
||||
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
target_mmap(elf_bss, last_bss-elf_bss,
|
||||
PROT_READ|PROT_WRITE|PROT_EXEC,
|
||||
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
free(elf_phdata);
|
||||
|
||||
@@ -540,7 +662,56 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||
return ((unsigned long) interp_elf_ex->e_entry) + load_addr;
|
||||
}
|
||||
|
||||
/* Best attempt to load symbols from this ELF object. */
|
||||
static void load_symbols(struct elfhdr *hdr, int fd)
|
||||
{
|
||||
unsigned int i;
|
||||
struct elf_shdr sechdr, symtab, strtab;
|
||||
char *strings;
|
||||
|
||||
lseek(fd, hdr->e_shoff, SEEK_SET);
|
||||
for (i = 0; i < hdr->e_shnum; i++) {
|
||||
if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
|
||||
return;
|
||||
#ifdef BSWAP_NEEDED
|
||||
bswap_shdr(&sechdr);
|
||||
#endif
|
||||
if (sechdr.sh_type == SHT_SYMTAB) {
|
||||
symtab = sechdr;
|
||||
lseek(fd, hdr->e_shoff
|
||||
+ sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
|
||||
if (read(fd, &strtab, sizeof(strtab))
|
||||
!= sizeof(strtab))
|
||||
return;
|
||||
#ifdef BSWAP_NEEDED
|
||||
bswap_shdr(&strtab);
|
||||
#endif
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
return; /* Shouldn't happen... */
|
||||
|
||||
found:
|
||||
/* Now know where the strtab and symtab are. Snarf them. */
|
||||
disas_symtab = malloc(symtab.sh_size);
|
||||
disas_strtab = strings = malloc(strtab.sh_size);
|
||||
if (!disas_symtab || !disas_strtab)
|
||||
return;
|
||||
|
||||
lseek(fd, symtab.sh_offset, SEEK_SET);
|
||||
if (read(fd, disas_symtab, symtab.sh_size) != symtab.sh_size)
|
||||
return;
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
for (i = 0; i < symtab.sh_size / sizeof(struct elf_sym); i++)
|
||||
bswap_sym(disas_symtab + sizeof(struct elf_sym)*i);
|
||||
#endif
|
||||
|
||||
lseek(fd, strtab.sh_offset, SEEK_SET);
|
||||
if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
|
||||
return;
|
||||
disas_num_syms = symtab.sh_size / sizeof(struct elf_sym);
|
||||
}
|
||||
|
||||
static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
struct image_info * info)
|
||||
@@ -549,12 +720,12 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
struct elfhdr interp_elf_ex;
|
||||
struct exec interp_ex;
|
||||
int interpreter_fd = -1; /* avoid warning */
|
||||
unsigned long load_addr;
|
||||
unsigned long load_addr, load_bias;
|
||||
int load_addr_set = 0;
|
||||
unsigned int interpreter_type = INTERPRETER_NONE;
|
||||
unsigned char ibcs2_interpreter;
|
||||
int i;
|
||||
void * mapped_addr;
|
||||
unsigned long mapped_addr;
|
||||
struct elf_phdr * elf_ppnt;
|
||||
struct elf_phdr *elf_phdata;
|
||||
unsigned long elf_bss, k, elf_brk;
|
||||
@@ -569,6 +740,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
ibcs2_interpreter = 0;
|
||||
status = 0;
|
||||
load_addr = 0;
|
||||
load_bias = 0;
|
||||
elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
|
||||
#ifdef BSWAP_NEEDED
|
||||
bswap_ehdr(&elf_ex);
|
||||
@@ -638,7 +810,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
* is an a.out format binary
|
||||
*/
|
||||
|
||||
elf_interpreter = (char *)malloc(elf_ppnt->p_filesz+strlen(X86_DEFAULT_LIB_DIR));
|
||||
elf_interpreter = (char *)malloc(elf_ppnt->p_filesz);
|
||||
|
||||
if (elf_interpreter == NULL) {
|
||||
free (elf_phdata);
|
||||
@@ -646,12 +818,9 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strcpy(elf_interpreter, X86_DEFAULT_LIB_DIR);
|
||||
retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET);
|
||||
if(retval >= 0) {
|
||||
retval = read(bprm->fd,
|
||||
elf_interpreter+strlen(X86_DEFAULT_LIB_DIR),
|
||||
elf_ppnt->p_filesz);
|
||||
retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz);
|
||||
}
|
||||
if(retval < 0) {
|
||||
perror("load_elf_binary2");
|
||||
@@ -673,7 +842,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
printf("Using ELF interpreter %s\n", elf_interpreter);
|
||||
#endif
|
||||
if (retval >= 0) {
|
||||
retval = open(elf_interpreter, O_RDONLY);
|
||||
retval = open(path(elf_interpreter), O_RDONLY);
|
||||
if(retval >= 0) {
|
||||
interpreter_fd = retval;
|
||||
}
|
||||
@@ -773,56 +942,85 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
* address.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
|
||||
if (elf_ppnt->p_type == PT_LOAD) {
|
||||
int elf_prot = 0;
|
||||
if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
|
||||
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
|
||||
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
|
||||
|
||||
mapped_addr = mmap4k(X86_ELF_PAGESTART(elf_ppnt->p_vaddr),
|
||||
(elf_ppnt->p_filesz +
|
||||
X86_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
|
||||
elf_prot,
|
||||
(MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
|
||||
bprm->fd,
|
||||
(elf_ppnt->p_offset -
|
||||
X86_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
|
||||
|
||||
if((unsigned long)mapped_addr == 0xffffffffffffffff) {
|
||||
perror("mmap");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
int elf_prot = 0;
|
||||
int elf_flags = 0;
|
||||
unsigned long error;
|
||||
|
||||
if (elf_ppnt->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
|
||||
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
|
||||
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
|
||||
elf_flags = MAP_PRIVATE | MAP_DENYWRITE;
|
||||
if (elf_ex.e_type == ET_EXEC || load_addr_set) {
|
||||
elf_flags |= MAP_FIXED;
|
||||
} else if (elf_ex.e_type == ET_DYN) {
|
||||
/* Try and get dynamic programs out of the way of the default mmap
|
||||
base, as well as whatever program they might try to exec. This
|
||||
is because the brk will follow the loader, and is not movable. */
|
||||
/* NOTE: for qemu, we do a big mmap to get enough space
|
||||
without harcoding any address */
|
||||
error = target_mmap(0, ET_DYN_MAP_SIZE,
|
||||
PROT_NONE, MAP_PRIVATE | MAP_ANON,
|
||||
-1, 0);
|
||||
if (error == -1) {
|
||||
perror("mmap");
|
||||
exit(-1);
|
||||
}
|
||||
load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr);
|
||||
}
|
||||
|
||||
error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr),
|
||||
(elf_ppnt->p_filesz +
|
||||
TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
|
||||
elf_prot,
|
||||
(MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
|
||||
bprm->fd,
|
||||
(elf_ppnt->p_offset -
|
||||
TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
|
||||
if (error == -1) {
|
||||
perror("mmap");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#ifdef LOW_ELF_STACK
|
||||
if (X86_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
|
||||
elf_stack = X86_ELF_PAGESTART(elf_ppnt->p_vaddr);
|
||||
if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
|
||||
elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr);
|
||||
#endif
|
||||
|
||||
if (!load_addr_set) {
|
||||
load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
|
||||
load_addr_set = 1;
|
||||
}
|
||||
k = elf_ppnt->p_vaddr;
|
||||
if (k < start_code) start_code = k;
|
||||
k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
|
||||
if (k > elf_bss) elf_bss = k;
|
||||
#if 1
|
||||
if ((elf_ppnt->p_flags & PF_X) && end_code < k)
|
||||
#else
|
||||
if ( !(elf_ppnt->p_flags & PF_W) && end_code < k)
|
||||
#endif
|
||||
end_code = k;
|
||||
if (end_data < k) end_data = k;
|
||||
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
|
||||
if (k > elf_brk) elf_brk = k;
|
||||
}
|
||||
|
||||
if (!load_addr_set) {
|
||||
load_addr_set = 1;
|
||||
load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
|
||||
if (elf_ex.e_type == ET_DYN) {
|
||||
load_bias += error -
|
||||
TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr);
|
||||
load_addr += load_bias;
|
||||
}
|
||||
}
|
||||
k = elf_ppnt->p_vaddr;
|
||||
if (k < start_code)
|
||||
start_code = k;
|
||||
k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
|
||||
if (k > elf_bss)
|
||||
elf_bss = k;
|
||||
if ((elf_ppnt->p_flags & PF_X) && end_code < k)
|
||||
end_code = k;
|
||||
if (end_data < k)
|
||||
end_data = k;
|
||||
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
|
||||
if (k > elf_brk) elf_brk = k;
|
||||
}
|
||||
|
||||
elf_entry += load_bias;
|
||||
elf_bss += load_bias;
|
||||
elf_brk += load_bias;
|
||||
start_code += load_bias;
|
||||
end_code += load_bias;
|
||||
// start_data += load_bias;
|
||||
end_data += load_bias;
|
||||
|
||||
if (elf_interpreter) {
|
||||
if (interpreter_type & 1) {
|
||||
elf_entry = load_aout_interp(&interp_ex, interpreter_fd);
|
||||
@@ -845,6 +1043,9 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
|
||||
free(elf_phdata);
|
||||
|
||||
if (loglevel)
|
||||
load_symbols(&elf_ex, bprm->fd);
|
||||
|
||||
if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd);
|
||||
info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
|
||||
|
||||
@@ -856,7 +1057,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
bprm->argc,
|
||||
bprm->envc,
|
||||
(interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
|
||||
load_addr,
|
||||
load_addr, load_bias,
|
||||
interp_load_addr,
|
||||
(interpreter_type == INTERPRETER_AOUT ? 0 : 1),
|
||||
info);
|
||||
@@ -889,8 +1090,8 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
||||
and some applications "depend" upon this behavior.
|
||||
Since we do not have the power to recompile these, we
|
||||
emulate the SVr4 behavior. Sigh. */
|
||||
mapped_addr = mmap4k(NULL, ALPHA_PAGE_SIZE, PROT_READ | PROT_EXEC,
|
||||
MAP_FIXED | MAP_PRIVATE, -1, 0);
|
||||
mapped_addr = target_mmap(0, host_page_size, PROT_READ | PROT_EXEC,
|
||||
MAP_FIXED | MAP_PRIVATE, -1, 0);
|
||||
}
|
||||
|
||||
#ifdef ELF_PLAT_INIT
|
||||
@@ -918,7 +1119,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp,
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
bprm.p = X86_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
|
||||
bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
|
||||
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
|
||||
bprm.page[i] = 0;
|
||||
retval = open(filename, O_RDONLY);
|
||||
|
@@ -37,6 +37,8 @@
|
||||
IOCTL(TIOCNOTTY, 0, TYPE_NULL)
|
||||
IOCTL(TIOCGETD, IOC_R, MK_PTR(TYPE_INT))
|
||||
IOCTL(TIOCSETD, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(TIOCGPTN, IOC_R, MK_PTR(TYPE_INT))
|
||||
IOCTL(TIOCSPTLCK, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(FIOCLEX, 0, TYPE_NULL)
|
||||
IOCTL(FIONCLEX, 0, TYPE_NULL)
|
||||
IOCTL(FIOASYNC, IOC_W, MK_PTR(TYPE_INT))
|
||||
@@ -49,6 +51,12 @@
|
||||
IOCTL(TIOCMIWAIT, 0, TYPE_INT)
|
||||
IOCTL(TIOCGICOUNT, IOC_R, MK_PTR(MK_STRUCT(STRUCT_serial_icounter_struct)))
|
||||
|
||||
IOCTL(KIOCSOUND, 0, TYPE_INT)
|
||||
IOCTL(KDMKTONE, 0, TYPE_INT)
|
||||
IOCTL(KDGKBTYPE, IOC_R, MK_PTR(TYPE_CHAR))
|
||||
IOCTL(KDGKBENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbentry)))
|
||||
IOCTL(KDGKBSENT, IOC_RW, TYPE_PTRVOID)
|
||||
|
||||
IOCTL(BLKROSET, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT))
|
||||
IOCTL(BLKRRPART, 0, TYPE_NULL)
|
||||
@@ -66,6 +74,7 @@
|
||||
IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG))
|
||||
#endif
|
||||
|
||||
IOCTL(SIOCATMARK, 0, TYPE_NULL)
|
||||
IOCTL(SIOCADDRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry)))
|
||||
IOCTL(SIOCDELRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry)))
|
||||
IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT))
|
||||
|
@@ -32,12 +32,28 @@
|
||||
|
||||
FILE *logfile = NULL;
|
||||
int loglevel;
|
||||
static const char *interp_prefix = CONFIG_QEMU_PREFIX;
|
||||
|
||||
#ifdef __i386__
|
||||
/* Force usage of an ELF interpreter even if it is an ELF shared
|
||||
object ! */
|
||||
const char interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
|
||||
|
||||
/* for recent libc, we add these dummies symbol which are not declared
|
||||
when generating a linked object (bug in ld ?) */
|
||||
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)
|
||||
long __init_array_start[0];
|
||||
long __init_array_end[0];
|
||||
long __fini_array_start[0];
|
||||
long __fini_array_end[0];
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
|
||||
we allocate a bigger stack. Need a better solution, for example
|
||||
by remapping the process stack directly at the right place */
|
||||
unsigned long x86_stack_size = 512 * 1024;
|
||||
unsigned long stktop;
|
||||
|
||||
void gemu_log(const char *fmt, ...)
|
||||
{
|
||||
@@ -51,104 +67,158 @@ void gemu_log(const char *fmt, ...)
|
||||
/***********************************************************/
|
||||
/* CPUX86 core interface */
|
||||
|
||||
void cpu_x86_outb(int addr, int val)
|
||||
void cpu_x86_outb(CPUX86State *env, int addr, int val)
|
||||
{
|
||||
fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val);
|
||||
}
|
||||
|
||||
void cpu_x86_outw(int addr, int val)
|
||||
void cpu_x86_outw(CPUX86State *env, int addr, int val)
|
||||
{
|
||||
fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val);
|
||||
}
|
||||
|
||||
void cpu_x86_outl(int addr, int val)
|
||||
void cpu_x86_outl(CPUX86State *env, int addr, int val)
|
||||
{
|
||||
fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val);
|
||||
}
|
||||
|
||||
int cpu_x86_inb(int addr)
|
||||
int cpu_x86_inb(CPUX86State *env, int addr)
|
||||
{
|
||||
fprintf(stderr, "inb: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_x86_inw(int addr)
|
||||
int cpu_x86_inw(CPUX86State *env, int addr)
|
||||
{
|
||||
fprintf(stderr, "inw: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_x86_inl(int addr)
|
||||
int cpu_x86_inl(CPUX86State *env, int addr)
|
||||
{
|
||||
fprintf(stderr, "inl: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
int seg32_bit)
|
||||
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
int flags)
|
||||
{
|
||||
unsigned int e1, e2, limit_in_pages;
|
||||
limit_in_pages = 0;
|
||||
if (limit > 0xffff) {
|
||||
limit = limit >> 12;
|
||||
limit_in_pages = 1;
|
||||
}
|
||||
unsigned int e1, e2;
|
||||
e1 = (addr << 16) | (limit & 0xffff);
|
||||
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
|
||||
e2 |= limit_in_pages << 23; /* byte granularity */
|
||||
e2 |= seg32_bit << 22; /* 32 bit segment */
|
||||
e2 |= flags;
|
||||
stl((uint8_t *)ptr, e1);
|
||||
stl((uint8_t *)ptr + 4, e2);
|
||||
}
|
||||
|
||||
static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
|
||||
unsigned long addr, unsigned int sel)
|
||||
{
|
||||
unsigned int e1, e2;
|
||||
e1 = (addr & 0xffff) | (sel << 16);
|
||||
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
|
||||
stl((uint8_t *)ptr, e1);
|
||||
stl((uint8_t *)ptr + 4, e2);
|
||||
}
|
||||
|
||||
uint64_t gdt_table[6];
|
||||
uint64_t idt_table[256];
|
||||
|
||||
void cpu_loop(struct CPUX86State *env)
|
||||
/* only dpl matters as we do only user space emulation */
|
||||
static void set_idt(int n, unsigned int dpl)
|
||||
{
|
||||
int err;
|
||||
set_gate(idt_table + n, 0, dpl, 0, 0);
|
||||
}
|
||||
|
||||
void cpu_loop(CPUX86State *env)
|
||||
{
|
||||
int trapnr;
|
||||
uint8_t *pc;
|
||||
target_siginfo_t info;
|
||||
|
||||
|
||||
for(;;) {
|
||||
err = cpu_x86_exec(env);
|
||||
pc = env->seg_cache[R_CS].base + env->eip;
|
||||
switch(err) {
|
||||
trapnr = cpu_x86_exec(env);
|
||||
switch(trapnr) {
|
||||
case 0x80:
|
||||
/* linux syscall */
|
||||
env->regs[R_EAX] = do_syscall(env,
|
||||
env->regs[R_EAX],
|
||||
env->regs[R_EBX],
|
||||
env->regs[R_ECX],
|
||||
env->regs[R_EDX],
|
||||
env->regs[R_ESI],
|
||||
env->regs[R_EDI],
|
||||
env->regs[R_EBP]);
|
||||
break;
|
||||
case EXCP0B_NOSEG:
|
||||
case EXCP0C_STACK:
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP0D_GPF:
|
||||
if (pc[0] == 0xcd && pc[1] == 0x80) {
|
||||
/* syscall */
|
||||
env->eip += 2;
|
||||
env->regs[R_EAX] = do_syscall(env,
|
||||
env->regs[R_EAX],
|
||||
env->regs[R_EBX],
|
||||
env->regs[R_ECX],
|
||||
env->regs[R_EDX],
|
||||
env->regs[R_ESI],
|
||||
env->regs[R_EDI],
|
||||
env->regs[R_EBP]);
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_fault(env);
|
||||
} else {
|
||||
/* XXX: more precise info */
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = 0;
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
queue_signal(info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP00_DIVZ:
|
||||
/* division by zero */
|
||||
info.si_signo = SIGFPE;
|
||||
case EXCP0E_PAGE:
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_FPE_INTDIV;
|
||||
info._sifields._sigfault._addr = env->eip;
|
||||
if (!(env->error_code & 1))
|
||||
info.si_code = TARGET_SEGV_MAPERR;
|
||||
else
|
||||
info.si_code = TARGET_SEGV_ACCERR;
|
||||
info._sifields._sigfault._addr = env->cr2;
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP00_DIVZ:
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_trap(env, trapnr);
|
||||
} else {
|
||||
/* division by zero */
|
||||
info.si_signo = SIGFPE;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_FPE_INTDIV;
|
||||
info._sifields._sigfault._addr = env->eip;
|
||||
queue_signal(info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP01_SSTP:
|
||||
case EXCP03_INT3:
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_trap(env, trapnr);
|
||||
} else {
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
if (trapnr == EXCP01_SSTP) {
|
||||
info.si_code = TARGET_TRAP_BRKPT;
|
||||
info._sifields._sigfault._addr = env->eip;
|
||||
} else {
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
}
|
||||
queue_signal(info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP04_INTO:
|
||||
case EXCP05_BOUND:
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = 0;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
queue_signal(info.si_signo, &info);
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_trap(env, trapnr);
|
||||
} else {
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
queue_signal(info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP06_ILLOP:
|
||||
info.si_signo = SIGILL;
|
||||
@@ -161,8 +231,9 @@ void cpu_loop(struct CPUX86State *env)
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "0x%08lx: Unknown exception CPU %d, aborting\n",
|
||||
(long)pc, err);
|
||||
pc = env->seg_cache[R_CS].base + env->eip;
|
||||
fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
|
||||
(long)pc, trapnr);
|
||||
abort();
|
||||
}
|
||||
process_pending_signals(env);
|
||||
@@ -172,31 +243,78 @@ void cpu_loop(struct CPUX86State *env)
|
||||
void usage(void)
|
||||
{
|
||||
printf("qemu version " QEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
|
||||
"usage: qemu [-d] program [arguments...]\n"
|
||||
"usage: qemu [-h] [-d] [-L path] [-s size] program [arguments...]\n"
|
||||
"Linux x86 emulator\n"
|
||||
);
|
||||
exit(1);
|
||||
"\n"
|
||||
"-h print this help\n"
|
||||
"-L path set the x86 elf interpreter prefix (default=%s)\n"
|
||||
"-s size set the x86 stack size in bytes (default=%ld)\n"
|
||||
"\n"
|
||||
"debug options:\n"
|
||||
"-d activate log (logfile=%s)\n"
|
||||
"-p pagesize set the host page size to 'pagesize'\n",
|
||||
interp_prefix,
|
||||
x86_stack_size,
|
||||
DEBUG_LOGFILE);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* XXX: currently only used for async signals (see signal.c) */
|
||||
CPUX86State *global_env;
|
||||
/* used to free thread contexts */
|
||||
TaskState *first_task_state;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *filename;
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
struct image_info info1, *info = &info1;
|
||||
TaskState ts1, *ts = &ts1;
|
||||
CPUX86State *env;
|
||||
int optind;
|
||||
|
||||
const char *r;
|
||||
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
|
||||
loglevel = 0;
|
||||
optind = 1;
|
||||
if (argv[optind] && !strcmp(argv[optind], "-d")) {
|
||||
loglevel = 1;
|
||||
for(;;) {
|
||||
if (optind >= argc)
|
||||
break;
|
||||
r = argv[optind];
|
||||
if (r[0] != '-')
|
||||
break;
|
||||
optind++;
|
||||
r++;
|
||||
if (!strcmp(r, "-")) {
|
||||
break;
|
||||
} else if (!strcmp(r, "d")) {
|
||||
loglevel = 1;
|
||||
} else if (!strcmp(r, "s")) {
|
||||
r = argv[optind++];
|
||||
x86_stack_size = strtol(r, (char **)&r, 0);
|
||||
if (x86_stack_size <= 0)
|
||||
usage();
|
||||
if (*r == 'M')
|
||||
x86_stack_size *= 1024 * 1024;
|
||||
else if (*r == 'k' || *r == 'K')
|
||||
x86_stack_size *= 1024;
|
||||
} else if (!strcmp(r, "L")) {
|
||||
interp_prefix = argv[optind++];
|
||||
} else if (!strcmp(r, "p")) {
|
||||
host_page_size = atoi(argv[optind++]);
|
||||
if (host_page_size == 0 ||
|
||||
(host_page_size & (host_page_size - 1)) != 0) {
|
||||
fprintf(stderr, "page size must be a power of two\n");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
if (optind >= argc)
|
||||
usage();
|
||||
filename = argv[optind];
|
||||
|
||||
/* init debug */
|
||||
@@ -204,7 +322,7 @@ int main(int argc, char **argv)
|
||||
logfile = fopen(DEBUG_LOGFILE, "w");
|
||||
if (!logfile) {
|
||||
perror(DEBUG_LOGFILE);
|
||||
exit(1);
|
||||
_exit(1);
|
||||
}
|
||||
setvbuf(logfile, NULL, _IOLBF, 0);
|
||||
}
|
||||
@@ -215,12 +333,21 @@ int main(int argc, char **argv)
|
||||
/* Zero out image_info */
|
||||
memset(info, 0, sizeof(struct image_info));
|
||||
|
||||
if(elf_exec(filename, argv+optind, environ, regs, info) != 0) {
|
||||
/* Scan interp_prefix dir for replacement files. */
|
||||
init_paths(interp_prefix);
|
||||
|
||||
/* NOTE: we need to init the CPU at this stage to get the
|
||||
host_page_size */
|
||||
env = cpu_x86_init();
|
||||
|
||||
if (elf_exec(filename, argv+optind, environ, regs, info) != 0) {
|
||||
printf("Error loading %s\n", filename);
|
||||
exit(1);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if (loglevel) {
|
||||
page_dump(logfile);
|
||||
|
||||
fprintf(logfile, "start_brk 0x%08lx\n" , info->start_brk);
|
||||
fprintf(logfile, "end_code 0x%08lx\n" , info->end_code);
|
||||
fprintf(logfile, "start_code 0x%08lx\n" , info->start_code);
|
||||
@@ -235,9 +362,13 @@ int main(int argc, char **argv)
|
||||
syscall_init();
|
||||
signal_init();
|
||||
|
||||
env = cpu_x86_init();
|
||||
global_env = env;
|
||||
|
||||
/* build Task State */
|
||||
memset(ts, 0, sizeof(TaskState));
|
||||
env->opaque = ts;
|
||||
ts->used = 1;
|
||||
|
||||
/* linux register setup */
|
||||
env->regs[R_EAX] = regs->eax;
|
||||
env->regs[R_EBX] = regs->ebx;
|
||||
@@ -249,11 +380,40 @@ int main(int argc, char **argv)
|
||||
env->regs[R_ESP] = regs->esp;
|
||||
env->eip = regs->eip;
|
||||
|
||||
/* linux interrupt setup */
|
||||
env->idt.base = (void *)idt_table;
|
||||
env->idt.limit = sizeof(idt_table) - 1;
|
||||
set_idt(0, 0);
|
||||
set_idt(1, 0);
|
||||
set_idt(2, 0);
|
||||
set_idt(3, 3);
|
||||
set_idt(4, 3);
|
||||
set_idt(5, 3);
|
||||
set_idt(6, 0);
|
||||
set_idt(7, 0);
|
||||
set_idt(8, 0);
|
||||
set_idt(9, 0);
|
||||
set_idt(10, 0);
|
||||
set_idt(11, 0);
|
||||
set_idt(12, 0);
|
||||
set_idt(13, 0);
|
||||
set_idt(14, 0);
|
||||
set_idt(15, 0);
|
||||
set_idt(16, 0);
|
||||
set_idt(17, 0);
|
||||
set_idt(18, 0);
|
||||
set_idt(19, 0);
|
||||
set_idt(0x80, 3);
|
||||
|
||||
/* linux segment setup */
|
||||
env->gdt.base = (void *)gdt_table;
|
||||
env->gdt.limit = sizeof(gdt_table) - 1;
|
||||
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xffffffff, 1);
|
||||
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xffffffff, 1);
|
||||
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
|
||||
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
|
||||
cpu_x86_load_seg(env, R_CS, __USER_CS);
|
||||
cpu_x86_load_seg(env, R_DS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_ES, __USER_DS);
|
||||
|
381
linux-user/mmap.c
Normal file
381
linux-user/mmap.c
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* mmap support for qemu
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
//#define DEBUG_MMAP
|
||||
|
||||
/* NOTE: all the constants are the HOST ones */
|
||||
int target_mprotect(unsigned long start, unsigned long len, int prot)
|
||||
{
|
||||
unsigned long end, host_start, host_end, addr;
|
||||
int prot1, ret;
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("mprotect: start=0x%lx len=0x%lx prot=%c%c%c\n", start, len,
|
||||
prot & PROT_READ ? 'r' : '-',
|
||||
prot & PROT_WRITE ? 'w' : '-',
|
||||
prot & PROT_EXEC ? 'x' : '-');
|
||||
#endif
|
||||
|
||||
if ((start & ~TARGET_PAGE_MASK) != 0)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
end = start + len;
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
|
||||
return -EINVAL;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
host_start = start & host_page_mask;
|
||||
host_end = HOST_PAGE_ALIGN(end);
|
||||
if (start > host_start) {
|
||||
/* handle host page containing start */
|
||||
prot1 = prot;
|
||||
for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
if (host_end == host_start + host_page_size) {
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
end = host_end;
|
||||
}
|
||||
ret = mprotect((void *)host_start, host_page_size, prot1 & PAGE_BITS);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
host_start += host_page_size;
|
||||
}
|
||||
if (end < host_end) {
|
||||
prot1 = prot;
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
ret = mprotect((void *)(host_end - host_page_size), host_page_size,
|
||||
prot1 & PAGE_BITS);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
host_end -= host_page_size;
|
||||
}
|
||||
|
||||
/* handle the pages in the middle */
|
||||
if (host_start < host_end) {
|
||||
ret = mprotect((void *)host_start, host_end - host_start, prot);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
page_set_flags(start, start + len, prot | PAGE_VALID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* map an incomplete host page */
|
||||
int mmap_frag(unsigned long host_start,
|
||||
unsigned long start, unsigned long end,
|
||||
int prot, int flags, int fd, unsigned long offset)
|
||||
{
|
||||
unsigned long host_end, ret, addr;
|
||||
int prot1, prot_new;
|
||||
|
||||
host_end = host_start + host_page_size;
|
||||
|
||||
/* get the protection of the target pages outside the mapping */
|
||||
prot1 = 0;
|
||||
for(addr = host_start; addr < host_end; addr++) {
|
||||
if (addr < start || addr >= end)
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
|
||||
if (prot1 == 0) {
|
||||
/* no page was there, so we allocate one */
|
||||
ret = (long)mmap((void *)host_start, host_page_size, prot,
|
||||
flags | MAP_ANONYMOUS, -1, 0);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
}
|
||||
prot1 &= PAGE_BITS;
|
||||
|
||||
prot_new = prot | prot1;
|
||||
if (!(flags & MAP_ANONYMOUS)) {
|
||||
/* msync() won't work here, so we return an error if write is
|
||||
possible while it is a shared mapping */
|
||||
if ((flags & MAP_TYPE) == MAP_SHARED &&
|
||||
(prot & PROT_WRITE))
|
||||
return -EINVAL;
|
||||
|
||||
/* adjust protection to be able to read */
|
||||
if (!(prot1 & PROT_WRITE))
|
||||
mprotect((void *)host_start, host_page_size, prot1 | PROT_WRITE);
|
||||
|
||||
/* read the corresponding file data */
|
||||
pread(fd, (void *)start, end - start, offset);
|
||||
|
||||
/* put final protection */
|
||||
if (prot_new != (prot1 | PROT_WRITE))
|
||||
mprotect((void *)host_start, host_page_size, prot_new);
|
||||
} else {
|
||||
/* just update the protection */
|
||||
if (prot_new != prot1) {
|
||||
mprotect((void *)host_start, host_page_size, prot_new);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: all the constants are the HOST ones */
|
||||
long target_mmap(unsigned long start, unsigned long len, int prot,
|
||||
int flags, int fd, unsigned long offset)
|
||||
{
|
||||
unsigned long ret, end, host_start, host_end, retaddr, host_offset, host_len;
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
{
|
||||
printf("mmap: start=0x%lx len=0x%lx prot=%c%c%c flags=",
|
||||
start, len,
|
||||
prot & PROT_READ ? 'r' : '-',
|
||||
prot & PROT_WRITE ? 'w' : '-',
|
||||
prot & PROT_EXEC ? 'x' : '-');
|
||||
if (flags & MAP_FIXED)
|
||||
printf("MAP_FIXED ");
|
||||
if (flags & MAP_ANONYMOUS)
|
||||
printf("MAP_ANON ");
|
||||
switch(flags & MAP_TYPE) {
|
||||
case MAP_PRIVATE:
|
||||
printf("MAP_PRIVATE ");
|
||||
break;
|
||||
case MAP_SHARED:
|
||||
printf("MAP_SHARED ");
|
||||
break;
|
||||
default:
|
||||
printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
|
||||
break;
|
||||
}
|
||||
printf("fd=%d offset=%lx\n", fd, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (offset & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
if (len == 0)
|
||||
return start;
|
||||
host_start = start & host_page_mask;
|
||||
|
||||
if (!(flags & MAP_FIXED)) {
|
||||
if (host_page_size != real_host_page_size) {
|
||||
/* NOTE: this code is only for debugging with '-p' option */
|
||||
/* reserve a memory area */
|
||||
host_len = HOST_PAGE_ALIGN(len) + host_page_size - TARGET_PAGE_SIZE;
|
||||
host_start = (long)mmap((void *)host_start, host_len, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (host_start == -1)
|
||||
return host_start;
|
||||
host_end = host_start + host_len;
|
||||
start = HOST_PAGE_ALIGN(host_start);
|
||||
end = start + HOST_PAGE_ALIGN(len);
|
||||
if (start > host_start)
|
||||
munmap((void *)host_start, start - host_start);
|
||||
if (end < host_end)
|
||||
munmap((void *)end, host_end - end);
|
||||
/* use it as a fixed mapping */
|
||||
flags |= MAP_FIXED;
|
||||
} else {
|
||||
/* if not fixed, no need to do anything */
|
||||
host_offset = offset & host_page_mask;
|
||||
host_len = len + offset - host_offset;
|
||||
start = (long)mmap((void *)host_start, host_len,
|
||||
prot, flags, fd, host_offset);
|
||||
if (start == -1)
|
||||
return start;
|
||||
/* update start so that it points to the file position at 'offset' */
|
||||
if (!(flags & MAP_ANONYMOUS))
|
||||
start += offset - host_offset;
|
||||
goto the_end1;
|
||||
}
|
||||
}
|
||||
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
end = start + len;
|
||||
host_end = HOST_PAGE_ALIGN(end);
|
||||
|
||||
/* worst case: we cannot map the file because the offset is not
|
||||
aligned, so we read it */
|
||||
if (!(flags & MAP_ANONYMOUS) &&
|
||||
(offset & ~host_page_mask) != (start & ~host_page_mask)) {
|
||||
/* msync() won't work here, so we return an error if write is
|
||||
possible while it is a shared mapping */
|
||||
if ((flags & MAP_TYPE) == MAP_SHARED &&
|
||||
(prot & PROT_WRITE))
|
||||
return -EINVAL;
|
||||
retaddr = target_mmap(start, len, prot | PROT_WRITE,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
if (retaddr == -1)
|
||||
return retaddr;
|
||||
pread(fd, (void *)start, len, offset);
|
||||
if (!(prot & PROT_WRITE)) {
|
||||
ret = target_mprotect(start, len, prot);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
/* handle the start of the mapping */
|
||||
if (start > host_start) {
|
||||
if (host_end == host_start + host_page_size) {
|
||||
/* one single host page */
|
||||
ret = mmap_frag(host_start, start, end,
|
||||
prot, flags, fd, offset);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
goto the_end1;
|
||||
}
|
||||
ret = mmap_frag(host_start, start, host_start + host_page_size,
|
||||
prot, flags, fd, offset);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
host_start += host_page_size;
|
||||
}
|
||||
/* handle the end of the mapping */
|
||||
if (end < host_end) {
|
||||
ret = mmap_frag(host_end - host_page_size,
|
||||
host_end - host_page_size, host_end,
|
||||
prot, flags, fd,
|
||||
offset + host_end - host_page_size - start);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
host_end -= host_page_size;
|
||||
}
|
||||
|
||||
/* map the middle (easier) */
|
||||
if (host_start < host_end) {
|
||||
ret = (long)mmap((void *)host_start, host_end - host_start,
|
||||
prot, flags, fd, offset + host_start - start);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
}
|
||||
the_end1:
|
||||
page_set_flags(start, start + len, prot | PAGE_VALID);
|
||||
the_end:
|
||||
#ifdef DEBUG_MMAP
|
||||
page_dump(stdout);
|
||||
printf("\n");
|
||||
#endif
|
||||
return start;
|
||||
}
|
||||
|
||||
int target_munmap(unsigned long start, unsigned long len)
|
||||
{
|
||||
unsigned long end, host_start, host_end, addr;
|
||||
int prot, ret;
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("munmap: start=0x%lx len=0x%lx\n", start, len);
|
||||
#endif
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
end = start + len;
|
||||
host_start = start & host_page_mask;
|
||||
host_end = HOST_PAGE_ALIGN(end);
|
||||
|
||||
if (start > host_start) {
|
||||
/* handle host page containing start */
|
||||
prot = 0;
|
||||
for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
if (host_end == host_start + host_page_size) {
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
end = host_end;
|
||||
}
|
||||
if (prot != 0)
|
||||
host_start += host_page_size;
|
||||
}
|
||||
if (end < host_end) {
|
||||
prot = 0;
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
if (prot != 0)
|
||||
host_end -= host_page_size;
|
||||
}
|
||||
|
||||
/* unmap what we can */
|
||||
if (host_start < host_end) {
|
||||
ret = munmap((void *)host_start, host_end - host_start);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
page_set_flags(start, start + len, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
|
||||
blocks which have been allocated starting on a host page */
|
||||
long target_mremap(unsigned long old_addr, unsigned long old_size,
|
||||
unsigned long new_size, unsigned long flags,
|
||||
unsigned long new_addr)
|
||||
{
|
||||
int prot;
|
||||
|
||||
/* XXX: use 5 args syscall */
|
||||
new_addr = (long)mremap((void *)old_addr, old_size, new_size, flags);
|
||||
if (new_addr == -1)
|
||||
return new_addr;
|
||||
prot = page_get_flags(old_addr);
|
||||
page_set_flags(old_addr, old_addr + old_size, 0);
|
||||
page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
|
||||
return new_addr;
|
||||
}
|
||||
|
||||
int target_msync(unsigned long start, unsigned long len, int flags)
|
||||
{
|
||||
unsigned long end;
|
||||
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
end = start + len;
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
if (end == start)
|
||||
return 0;
|
||||
|
||||
start &= host_page_mask;
|
||||
return msync((void *)start, end - start, flags);
|
||||
}
|
||||
|
142
linux-user/path.c
Normal file
142
linux-user/path.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Code to mangle pathnames into those matching a given prefix.
|
||||
eg. open("/lib/foo.so") => open("/usr/gnemul/i386-linux/lib/foo.so");
|
||||
|
||||
The assumption is that this area does not change.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "qemu.h"
|
||||
|
||||
struct pathelem
|
||||
{
|
||||
/* Name of this, eg. lib */
|
||||
char *name;
|
||||
/* Full path name, eg. /usr/gnemul/x86-linux/lib. */
|
||||
char *pathname;
|
||||
struct pathelem *parent;
|
||||
/* Children */
|
||||
unsigned int num_entries;
|
||||
struct pathelem *entries[0];
|
||||
};
|
||||
|
||||
static struct pathelem *base;
|
||||
|
||||
/* First N chars of S1 match S2, and S2 is N chars long. */
|
||||
static int strneq(const char *s1, unsigned int n, const char *s2)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
if (s1[i] != s2[i])
|
||||
return 0;
|
||||
return s2[i] == 0;
|
||||
}
|
||||
|
||||
static struct pathelem *add_entry(struct pathelem *root, const char *name);
|
||||
|
||||
static struct pathelem *new_entry(const char *root,
|
||||
struct pathelem *parent,
|
||||
const char *name)
|
||||
{
|
||||
struct pathelem *new = malloc(sizeof(*new));
|
||||
new->name = strdup(name);
|
||||
asprintf(&new->pathname, "%s/%s", root, name);
|
||||
new->num_entries = 0;
|
||||
return new;
|
||||
}
|
||||
|
||||
#define streq(a,b) (strcmp((a), (b)) == 0)
|
||||
|
||||
static struct pathelem *add_dir_maybe(struct pathelem *path)
|
||||
{
|
||||
DIR *dir;
|
||||
|
||||
if ((dir = opendir(path->pathname)) != NULL) {
|
||||
struct dirent *dirent;
|
||||
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){
|
||||
path = add_entry(path, dirent->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
static struct pathelem *add_entry(struct pathelem *root, const char *name)
|
||||
{
|
||||
root->num_entries++;
|
||||
|
||||
root = realloc(root, sizeof(*root)
|
||||
+ sizeof(root->entries[0])*root->num_entries);
|
||||
|
||||
root->entries[root->num_entries-1] = new_entry(root->pathname, root, name);
|
||||
root->entries[root->num_entries-1]
|
||||
= add_dir_maybe(root->entries[root->num_entries-1]);
|
||||
return root;
|
||||
}
|
||||
|
||||
/* This needs to be done after tree is stabalized (ie. no more reallocs!). */
|
||||
static void set_parents(struct pathelem *child, struct pathelem *parent)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
child->parent = parent;
|
||||
for (i = 0; i < child->num_entries; i++)
|
||||
set_parents(child->entries[i], child);
|
||||
}
|
||||
|
||||
void init_paths(const char *prefix)
|
||||
{
|
||||
if (prefix[0] != '/' ||
|
||||
prefix[0] == '\0' ||
|
||||
!strcmp(prefix, "/"))
|
||||
return;
|
||||
|
||||
base = new_entry("", NULL, prefix+1);
|
||||
base = add_dir_maybe(base);
|
||||
set_parents(base, base);
|
||||
}
|
||||
|
||||
/* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */
|
||||
static const char *
|
||||
follow_path(const struct pathelem *cursor, const char *name)
|
||||
{
|
||||
unsigned int i, namelen;
|
||||
|
||||
name += strspn(name, "/");
|
||||
namelen = strcspn(name, "/");
|
||||
|
||||
if (namelen == 0)
|
||||
return cursor->pathname;
|
||||
|
||||
if (strneq(name, namelen, ".."))
|
||||
return follow_path(cursor->parent, name + namelen);
|
||||
|
||||
if (strneq(name, namelen, "."))
|
||||
return follow_path(cursor, name + namelen);
|
||||
|
||||
for (i = 0; i < cursor->num_entries; i++)
|
||||
if (strneq(name, namelen, cursor->entries[i]->name))
|
||||
return follow_path(cursor->entries[i], name + namelen);
|
||||
|
||||
/* Not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Look for path in emulation dir, otherwise return name. */
|
||||
const char *path(const char *name)
|
||||
{
|
||||
/* Only do absolute paths: quick and dirty, but should mostly be OK.
|
||||
Could do relative by tracking cwd. */
|
||||
if (!base || name[0] != '/')
|
||||
return name;
|
||||
|
||||
return follow_path(base, name) ?: name;
|
||||
}
|
@@ -33,6 +33,36 @@ struct image_info {
|
||||
int personality;
|
||||
};
|
||||
|
||||
/* Information about the current linux thread */
|
||||
struct vm86_saved_state {
|
||||
uint32_t eax; /* return code */
|
||||
uint32_t ebx;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint32_t ebp;
|
||||
uint32_t esp;
|
||||
uint32_t eflags;
|
||||
uint32_t eip;
|
||||
uint16_t cs, ss, ds, es, fs, gs;
|
||||
};
|
||||
|
||||
/* NOTE: we force a big alignment so that the stack stored after is
|
||||
aligned too */
|
||||
typedef struct TaskState {
|
||||
struct TaskState *next;
|
||||
struct target_vm86plus_struct *target_v86;
|
||||
struct vm86_saved_state vm86_saved_regs;
|
||||
struct target_vm86plus_struct vm86plus;
|
||||
uint32_t v86flags;
|
||||
uint32_t v86mask;
|
||||
int used; /* non zero if used */
|
||||
uint8_t stack[0];
|
||||
} __attribute__((aligned(16))) TaskState;
|
||||
|
||||
extern TaskState *first_task_state;
|
||||
|
||||
int elf_exec(const char * filename, char ** argv, char ** envp,
|
||||
struct target_pt_regs * regs, struct image_info *infop);
|
||||
|
||||
@@ -46,5 +76,27 @@ void cpu_loop(CPUX86State *env);
|
||||
void process_pending_signals(void *cpu_env);
|
||||
void signal_init(void);
|
||||
int queue_signal(int sig, target_siginfo_t *info);
|
||||
void init_paths(const char *prefix);
|
||||
const char *path(const char *pathname);
|
||||
|
||||
extern int loglevel;
|
||||
extern FILE *logfile;
|
||||
|
||||
/* vm86.c */
|
||||
void save_v86_state(CPUX86State *env);
|
||||
void handle_vm86_trap(CPUX86State *env, int trapno);
|
||||
void handle_vm86_fault(CPUX86State *env);
|
||||
int do_vm86(CPUX86State *env, long subfunction,
|
||||
struct target_vm86plus_struct * target_v86);
|
||||
|
||||
/* mmap.c */
|
||||
int target_mprotect(unsigned long start, unsigned long len, int prot);
|
||||
long target_mmap(unsigned long start, unsigned long len, int prot,
|
||||
int flags, int fd, unsigned long offset);
|
||||
int target_munmap(unsigned long start, unsigned long len);
|
||||
long target_mremap(unsigned long old_addr, unsigned long old_size,
|
||||
unsigned long new_size, unsigned long flags,
|
||||
unsigned long new_addr);
|
||||
int target_msync(unsigned long start, unsigned long len, int flags);
|
||||
|
||||
#endif
|
||||
|
@@ -21,10 +21,18 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#ifdef __ia64__
|
||||
#undef uc_mcontext
|
||||
#undef uc_sigmask
|
||||
#undef uc_stack
|
||||
#undef uc_link
|
||||
#endif
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
//#define DEBUG_SIGNAL
|
||||
@@ -102,7 +110,8 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
|
||||
tinfo->si_signo = sig;
|
||||
tinfo->si_errno = 0;
|
||||
tinfo->si_code = 0;
|
||||
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) {
|
||||
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV ||
|
||||
sig == SIGBUS || sig == SIGTRAP) {
|
||||
/* should never come here, but who knows. The information for
|
||||
the target is irrelevant */
|
||||
tinfo->_sifields._sigfault._addr = 0;
|
||||
@@ -123,7 +132,8 @@ static void tswap_siginfo(target_siginfo_t *tinfo,
|
||||
tinfo->si_signo = tswap32(sig);
|
||||
tinfo->si_errno = tswap32(info->si_errno);
|
||||
tinfo->si_code = tswap32(info->si_code);
|
||||
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) {
|
||||
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV ||
|
||||
sig == SIGBUS || sig == SIGTRAP) {
|
||||
tinfo->_sifields._sigfault._addr =
|
||||
tswapl(info->_sifields._sigfault._addr);
|
||||
} else if (sig >= TARGET_SIGRTMIN) {
|
||||
@@ -165,7 +175,7 @@ void signal_init(void)
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_sigaction = host_signal_handler;
|
||||
for(i = 1; i < NSIG; i++) {
|
||||
sigaction(i, &act, NULL);
|
||||
sigaction(i, &act, NULL);
|
||||
}
|
||||
|
||||
memset(sigact_table, 0, sizeof(sigact_table));
|
||||
@@ -198,7 +208,7 @@ void __attribute((noreturn)) force_sig(int sig)
|
||||
{
|
||||
int host_sig;
|
||||
host_sig = target_to_host_signal(sig);
|
||||
fprintf(stderr, "gemu: uncaught target signal %d (%s) - exiting\n",
|
||||
fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
|
||||
sig, strsignal(host_sig));
|
||||
#if 1
|
||||
_exit(-host_sig);
|
||||
@@ -223,7 +233,7 @@ int queue_signal(int sig, target_siginfo_t *info)
|
||||
target_ulong handler;
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "queue_sigal: sig=%d\n",
|
||||
fprintf(stderr, "queue_signal: sig=%d\n",
|
||||
sig);
|
||||
#endif
|
||||
k = &sigact_table[sig - 1];
|
||||
@@ -317,7 +327,7 @@ static void host_signal_handler(int host_signum, siginfo_t *info,
|
||||
if (sig < 1 || sig > TARGET_NSIG)
|
||||
return;
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "gemu: got signal %d\n", sig);
|
||||
fprintf(stderr, "qemu: got signal %d\n", sig);
|
||||
dump_regs(puc);
|
||||
#endif
|
||||
host_to_target_siginfo_noswap(&tinfo, info);
|
||||
@@ -519,8 +529,8 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
|
||||
err |= __put_user(env->regs[R_EDX], &sc->edx);
|
||||
err |= __put_user(env->regs[R_ECX], &sc->ecx);
|
||||
err |= __put_user(env->regs[R_EAX], &sc->eax);
|
||||
err |= __put_user(/*current->thread.trap_no*/ 0, &sc->trapno);
|
||||
err |= __put_user(/*current->thread.error_code*/ 0, &sc->err);
|
||||
err |= __put_user(env->exception_index, &sc->trapno);
|
||||
err |= __put_user(env->error_code, &sc->err);
|
||||
err |= __put_user(env->eip, &sc->eip);
|
||||
err |= __put_user(env->segs[R_CS], (unsigned int *)&sc->cs);
|
||||
err |= __put_user(env->eflags, &sc->eflags);
|
||||
@@ -537,8 +547,7 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
|
||||
#endif
|
||||
/* non-iBCS2 extensions.. */
|
||||
err |= __put_user(mask, &sc->oldmask);
|
||||
err |= __put_user(/*current->thread.cr2*/ 0, &sc->cr2);
|
||||
|
||||
err |= __put_user(env->cr2, &sc->cr2);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -570,8 +579,6 @@ get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size)
|
||||
return (void *)((esp - frame_size) & -8ul);
|
||||
}
|
||||
|
||||
#define TF_MASK TRAP_FLAG
|
||||
|
||||
static void setup_frame(int sig, struct emulated_sigaction *ka,
|
||||
target_sigset_t *set, CPUX86State *env)
|
||||
{
|
||||
@@ -783,6 +790,9 @@ long do_sigreturn(CPUX86State *env)
|
||||
sigset_t set;
|
||||
int eax, i;
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "do_sigreturn\n");
|
||||
#endif
|
||||
/* set blocked signals */
|
||||
target_set.sig[0] = frame->sc.oldmask;
|
||||
for(i = 1; i < TARGET_NSIG_WORDS; i++)
|
||||
@@ -861,7 +871,7 @@ void process_pending_signals(void *cpu_env)
|
||||
|
||||
handle_signal:
|
||||
#ifdef DEBUG_SIGNAL
|
||||
fprintf(stderr, "gemu: process signal %d\n", sig);
|
||||
fprintf(stderr, "qemu: process signal %d\n", sig);
|
||||
#endif
|
||||
/* dequeue signal */
|
||||
q = k->first;
|
||||
@@ -895,6 +905,14 @@ void process_pending_signals(void *cpu_env)
|
||||
end of the signal execution (see do_sigreturn) */
|
||||
host_to_target_sigset(&target_old_set, &old_set);
|
||||
|
||||
/* if the CPU is in VM86 mode, we restore the 32 bit values */
|
||||
#ifdef TARGET_I386
|
||||
{
|
||||
CPUX86State *env = cpu_env;
|
||||
if (env->eflags & VM_MASK)
|
||||
save_v86_state(env);
|
||||
}
|
||||
#endif
|
||||
/* prepare the stack frame of the virtual CPU */
|
||||
if (k->sa.sa_flags & TARGET_SA_SIGINFO)
|
||||
setup_rt_frame(sig, k, &q->info, &target_old_set, cpu_env);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,11 @@
|
||||
#define SOCKOP_sendmsg 16
|
||||
#define SOCKOP_recvmsg 17
|
||||
|
||||
struct target_sockaddr {
|
||||
uint16_t sa_family;
|
||||
uint8_t sa_data[14];
|
||||
};
|
||||
|
||||
struct target_timeval {
|
||||
target_long tv_sec;
|
||||
target_long tv_usec;
|
||||
@@ -34,6 +39,15 @@ struct target_itimerval {
|
||||
struct target_timeval it_value;
|
||||
};
|
||||
|
||||
typedef target_long target_clock_t;
|
||||
|
||||
struct target_tms {
|
||||
target_clock_t tms_utime;
|
||||
target_clock_t tms_stime;
|
||||
target_clock_t tms_cutime;
|
||||
target_clock_t tms_cstime;
|
||||
};
|
||||
|
||||
struct target_iovec {
|
||||
target_long iov_base; /* Starting address */
|
||||
target_long iov_len; /* Number of bytes */
|
||||
@@ -49,6 +63,43 @@ struct target_msghdr {
|
||||
unsigned int msg_flags;
|
||||
};
|
||||
|
||||
struct target_cmsghdr {
|
||||
target_long cmsg_len;
|
||||
int cmsg_level;
|
||||
int cmsg_type;
|
||||
};
|
||||
|
||||
#define TARGET_CMSG_DATA(cmsg) ((unsigned char *) ((struct target_cmsghdr *) (cmsg) + 1))
|
||||
#define TARGET_CMSG_NXTHDR(mhdr, cmsg) __target_cmsg_nxthdr (mhdr, cmsg)
|
||||
#define TARGET_CMSG_FIRSTHDR(mhdr) \
|
||||
((size_t) tswapl((mhdr)->msg_controllen) >= sizeof (struct target_cmsghdr) \
|
||||
? (struct target_cmsghdr *) tswapl((mhdr)->msg_control) : (struct target_cmsghdr *) NULL)
|
||||
#define TARGET_CMSG_ALIGN(len) (((len) + sizeof (target_long) - 1) \
|
||||
& (size_t) ~(sizeof (target_long) - 1))
|
||||
#define TARGET_CMSG_SPACE(len) (TARGET_CMSG_ALIGN (len) \
|
||||
+ TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)))
|
||||
#define TARGET_CMSG_LEN(len) (TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)) + (len))
|
||||
|
||||
static __inline__ struct target_cmsghdr *
|
||||
__target_cmsg_nxthdr (struct target_msghdr *__mhdr, struct target_cmsghdr *__cmsg)
|
||||
{
|
||||
if (tswapl(__cmsg->cmsg_len) < sizeof (struct target_cmsghdr))
|
||||
/* The kernel header does this so there may be a reason. */
|
||||
return 0;
|
||||
|
||||
__cmsg = (struct target_cmsghdr *) ((unsigned char *) __cmsg
|
||||
+ TARGET_CMSG_ALIGN (tswapl(__cmsg->cmsg_len)));
|
||||
if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) tswapl(__mhdr->msg_control)
|
||||
+ tswapl(__mhdr->msg_controllen))
|
||||
|| ((unsigned char *) __cmsg + TARGET_CMSG_ALIGN (tswapl(__cmsg->cmsg_len))
|
||||
> ((unsigned char *) tswapl(__mhdr->msg_control)
|
||||
+ tswapl(__mhdr->msg_controllen))))
|
||||
/* No more entries. */
|
||||
return 0;
|
||||
return __cmsg;
|
||||
}
|
||||
|
||||
|
||||
struct target_rusage {
|
||||
struct target_timeval ru_utime; /* user time used */
|
||||
struct target_timeval ru_stime; /* system time used */
|
||||
@@ -161,6 +212,13 @@ struct target_pollfd {
|
||||
short revents; /* returned events */
|
||||
};
|
||||
|
||||
/* virtual terminal ioctls */
|
||||
#define TARGET_KIOCSOUND 0x4B2F /* start sound generation (0 for off) */
|
||||
#define TARGET_KDMKTONE 0x4B30 /* generate tone */
|
||||
#define TARGET_KDGKBTYPE 0x4b33
|
||||
#define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */
|
||||
#define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */
|
||||
|
||||
/* Networking ioctls */
|
||||
#define TARGET_SIOCADDRT 0x890B /* add routing table entry */
|
||||
#define TARGET_SIOCDELRT 0x890C /* delete routing table entry */
|
||||
|
@@ -64,3 +64,6 @@ STRUCT(hd_geometry,
|
||||
|
||||
STRUCT(dirent,
|
||||
TYPE_LONG, TYPE_LONG, TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 256))
|
||||
|
||||
STRUCT(kbentry,
|
||||
TYPE_CHAR, TYPE_CHAR, TYPE_SHORT)
|
||||
|
475
linux-user/vm86.c
Normal file
475
linux-user/vm86.c
Normal file
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* vm86 linux syscall support
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
//#define DEBUG_VM86
|
||||
|
||||
#define set_flags(X,new,mask) \
|
||||
((X) = ((X) & ~(mask)) | ((new) & (mask)))
|
||||
|
||||
#define SAFE_MASK (0xDD5)
|
||||
#define RETURN_MASK (0xDFF)
|
||||
|
||||
static inline int is_revectored(int nr, struct target_revectored_struct *bitmap)
|
||||
{
|
||||
return (((uint8_t *)bitmap)[nr >> 3] >> (nr & 7)) & 1;
|
||||
}
|
||||
|
||||
static inline void vm_putw(uint8_t *segptr, unsigned int reg16, unsigned int val)
|
||||
{
|
||||
*(uint16_t *)(segptr + (reg16 & 0xffff)) = tswap16(val);
|
||||
}
|
||||
|
||||
static inline void vm_putl(uint8_t *segptr, unsigned int reg16, unsigned int val)
|
||||
{
|
||||
*(uint32_t *)(segptr + (reg16 & 0xffff)) = tswap32(val);
|
||||
}
|
||||
|
||||
static inline unsigned int vm_getw(uint8_t *segptr, unsigned int reg16)
|
||||
{
|
||||
return tswap16(*(uint16_t *)(segptr + (reg16 & 0xffff)));
|
||||
}
|
||||
|
||||
static inline unsigned int vm_getl(uint8_t *segptr, unsigned int reg16)
|
||||
{
|
||||
return tswap32(*(uint32_t *)(segptr + (reg16 & 0xffff)));
|
||||
}
|
||||
|
||||
void save_v86_state(CPUX86State *env)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
|
||||
/* put the VM86 registers in the userspace register structure */
|
||||
ts->target_v86->regs.eax = tswap32(env->regs[R_EAX]);
|
||||
ts->target_v86->regs.ebx = tswap32(env->regs[R_EBX]);
|
||||
ts->target_v86->regs.ecx = tswap32(env->regs[R_ECX]);
|
||||
ts->target_v86->regs.edx = tswap32(env->regs[R_EDX]);
|
||||
ts->target_v86->regs.esi = tswap32(env->regs[R_ESI]);
|
||||
ts->target_v86->regs.edi = tswap32(env->regs[R_EDI]);
|
||||
ts->target_v86->regs.ebp = tswap32(env->regs[R_EBP]);
|
||||
ts->target_v86->regs.esp = tswap32(env->regs[R_ESP]);
|
||||
ts->target_v86->regs.eip = tswap32(env->eip);
|
||||
ts->target_v86->regs.cs = tswap16(env->segs[R_CS]);
|
||||
ts->target_v86->regs.ss = tswap16(env->segs[R_SS]);
|
||||
ts->target_v86->regs.ds = tswap16(env->segs[R_DS]);
|
||||
ts->target_v86->regs.es = tswap16(env->segs[R_ES]);
|
||||
ts->target_v86->regs.fs = tswap16(env->segs[R_FS]);
|
||||
ts->target_v86->regs.gs = tswap16(env->segs[R_GS]);
|
||||
set_flags(env->eflags, ts->v86flags, VIF_MASK | ts->v86mask);
|
||||
ts->target_v86->regs.eflags = tswap32(env->eflags);
|
||||
#ifdef DEBUG_VM86
|
||||
fprintf(logfile, "save_v86_state: eflags=%08x cs:ip=%04x:%04x\n",
|
||||
env->eflags, env->segs[R_CS], env->eip);
|
||||
#endif
|
||||
|
||||
/* restore 32 bit registers */
|
||||
env->regs[R_EAX] = ts->vm86_saved_regs.eax;
|
||||
env->regs[R_EBX] = ts->vm86_saved_regs.ebx;
|
||||
env->regs[R_ECX] = ts->vm86_saved_regs.ecx;
|
||||
env->regs[R_EDX] = ts->vm86_saved_regs.edx;
|
||||
env->regs[R_ESI] = ts->vm86_saved_regs.esi;
|
||||
env->regs[R_EDI] = ts->vm86_saved_regs.edi;
|
||||
env->regs[R_EBP] = ts->vm86_saved_regs.ebp;
|
||||
env->regs[R_ESP] = ts->vm86_saved_regs.esp;
|
||||
env->eflags = ts->vm86_saved_regs.eflags;
|
||||
env->eip = ts->vm86_saved_regs.eip;
|
||||
|
||||
cpu_x86_load_seg(env, R_CS, ts->vm86_saved_regs.cs);
|
||||
cpu_x86_load_seg(env, R_SS, ts->vm86_saved_regs.ss);
|
||||
cpu_x86_load_seg(env, R_DS, ts->vm86_saved_regs.ds);
|
||||
cpu_x86_load_seg(env, R_ES, ts->vm86_saved_regs.es);
|
||||
cpu_x86_load_seg(env, R_FS, ts->vm86_saved_regs.fs);
|
||||
cpu_x86_load_seg(env, R_GS, ts->vm86_saved_regs.gs);
|
||||
}
|
||||
|
||||
/* return from vm86 mode to 32 bit. The vm86() syscall will return
|
||||
'retval' */
|
||||
static inline void return_to_32bit(CPUX86State *env, int retval)
|
||||
{
|
||||
#ifdef DEBUG_VM86
|
||||
fprintf(logfile, "return_to_32bit: ret=0x%x\n", retval);
|
||||
#endif
|
||||
save_v86_state(env);
|
||||
env->regs[R_EAX] = retval;
|
||||
}
|
||||
|
||||
static inline int set_IF(CPUX86State *env)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
|
||||
ts->v86flags |= VIF_MASK;
|
||||
if (ts->v86flags & VIP_MASK) {
|
||||
return_to_32bit(env, TARGET_VM86_STI);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void clear_IF(CPUX86State *env)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
|
||||
ts->v86flags &= ~VIF_MASK;
|
||||
}
|
||||
|
||||
static inline void clear_TF(CPUX86State *env)
|
||||
{
|
||||
env->eflags &= ~TF_MASK;
|
||||
}
|
||||
|
||||
static inline void clear_AC(CPUX86State *env)
|
||||
{
|
||||
env->eflags &= ~AC_MASK;
|
||||
}
|
||||
|
||||
static inline int set_vflags_long(unsigned long eflags, CPUX86State *env)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
|
||||
set_flags(ts->v86flags, eflags, ts->v86mask);
|
||||
set_flags(env->eflags, eflags, SAFE_MASK);
|
||||
if (eflags & IF_MASK)
|
||||
return set_IF(env);
|
||||
else
|
||||
clear_IF(env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int set_vflags_short(unsigned short flags, CPUX86State *env)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
|
||||
set_flags(ts->v86flags, flags, ts->v86mask & 0xffff);
|
||||
set_flags(env->eflags, flags, SAFE_MASK);
|
||||
if (flags & IF_MASK)
|
||||
return set_IF(env);
|
||||
else
|
||||
clear_IF(env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int get_vflags(CPUX86State *env)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
unsigned int flags;
|
||||
|
||||
flags = env->eflags & RETURN_MASK;
|
||||
if (ts->v86flags & VIF_MASK)
|
||||
flags |= IF_MASK;
|
||||
return flags | (ts->v86flags & ts->v86mask);
|
||||
}
|
||||
|
||||
#define ADD16(reg, val) reg = (reg & ~0xffff) | ((reg + (val)) & 0xffff)
|
||||
|
||||
/* handle VM86 interrupt (NOTE: the CPU core currently does not
|
||||
support TSS interrupt revectoring, so this code is always executed) */
|
||||
static void do_int(CPUX86State *env, int intno)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
uint32_t *int_ptr, segoffs;
|
||||
uint8_t *ssp;
|
||||
unsigned int sp;
|
||||
|
||||
if (env->segs[R_CS] == TARGET_BIOSSEG)
|
||||
goto cannot_handle;
|
||||
if (is_revectored(intno, &ts->vm86plus.int_revectored))
|
||||
goto cannot_handle;
|
||||
if (intno == 0x21 && is_revectored((env->regs[R_EAX] >> 8) & 0xff,
|
||||
&ts->vm86plus.int21_revectored))
|
||||
goto cannot_handle;
|
||||
int_ptr = (uint32_t *)(intno << 2);
|
||||
segoffs = tswap32(*int_ptr);
|
||||
if ((segoffs >> 16) == TARGET_BIOSSEG)
|
||||
goto cannot_handle;
|
||||
#if defined(DEBUG_VM86)
|
||||
fprintf(logfile, "VM86: emulating int 0x%x. CS:IP=%04x:%04x\n",
|
||||
intno, segoffs >> 16, segoffs & 0xffff);
|
||||
#endif
|
||||
/* save old state */
|
||||
ssp = (uint8_t *)(env->segs[R_SS] << 4);
|
||||
sp = env->regs[R_ESP] & 0xffff;
|
||||
vm_putw(ssp, sp - 2, get_vflags(env));
|
||||
vm_putw(ssp, sp - 4, env->segs[R_CS]);
|
||||
vm_putw(ssp, sp - 6, env->eip);
|
||||
ADD16(env->regs[R_ESP], -6);
|
||||
/* goto interrupt handler */
|
||||
env->eip = segoffs & 0xffff;
|
||||
cpu_x86_load_seg(env, R_CS, segoffs >> 16);
|
||||
clear_TF(env);
|
||||
clear_IF(env);
|
||||
clear_AC(env);
|
||||
return;
|
||||
cannot_handle:
|
||||
#if defined(DEBUG_VM86)
|
||||
fprintf(logfile, "VM86: return to 32 bits int 0x%x\n", intno);
|
||||
#endif
|
||||
return_to_32bit(env, TARGET_VM86_INTx | (intno << 8));
|
||||
}
|
||||
|
||||
void handle_vm86_trap(CPUX86State *env, int trapno)
|
||||
{
|
||||
if (trapno == 1 || trapno == 3) {
|
||||
return_to_32bit(env, TARGET_VM86_TRAP + (trapno << 8));
|
||||
} else {
|
||||
do_int(env, trapno);
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_IF_IN_TRAP() \
|
||||
if ((ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) && \
|
||||
(ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_TFpendig)) \
|
||||
newflags |= TF_MASK
|
||||
|
||||
#define VM86_FAULT_RETURN \
|
||||
if ((ts->vm86plus.vm86plus.flags & TARGET_force_return_for_pic) && \
|
||||
(ts->v86flags & (IF_MASK | VIF_MASK))) \
|
||||
return_to_32bit(env, TARGET_VM86_PICRETURN); \
|
||||
return
|
||||
|
||||
void handle_vm86_fault(CPUX86State *env)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
uint8_t *csp, *pc, *ssp;
|
||||
unsigned int ip, sp, newflags, newip, newcs, opcode, intno;
|
||||
int data32, pref_done;
|
||||
|
||||
csp = (uint8_t *)(env->segs[R_CS] << 4);
|
||||
ip = env->eip & 0xffff;
|
||||
pc = csp + ip;
|
||||
|
||||
ssp = (uint8_t *)(env->segs[R_SS] << 4);
|
||||
sp = env->regs[R_ESP] & 0xffff;
|
||||
|
||||
#if defined(DEBUG_VM86)
|
||||
fprintf(logfile, "VM86 exception %04x:%08x %02x %02x\n",
|
||||
env->segs[R_CS], env->eip, pc[0], pc[1]);
|
||||
#endif
|
||||
|
||||
data32 = 0;
|
||||
pref_done = 0;
|
||||
do {
|
||||
opcode = csp[ip];
|
||||
ADD16(ip, 1);
|
||||
switch (opcode) {
|
||||
case 0x66: /* 32-bit data */ data32=1; break;
|
||||
case 0x67: /* 32-bit address */ break;
|
||||
case 0x2e: /* CS */ break;
|
||||
case 0x3e: /* DS */ break;
|
||||
case 0x26: /* ES */ break;
|
||||
case 0x36: /* SS */ break;
|
||||
case 0x65: /* GS */ break;
|
||||
case 0x64: /* FS */ break;
|
||||
case 0xf2: /* repnz */ break;
|
||||
case 0xf3: /* rep */ break;
|
||||
default: pref_done = 1;
|
||||
}
|
||||
} while (!pref_done);
|
||||
|
||||
/* VM86 mode */
|
||||
switch(opcode) {
|
||||
case 0x9c: /* pushf */
|
||||
if (data32) {
|
||||
vm_putl(ssp, sp - 4, get_vflags(env));
|
||||
ADD16(env->regs[R_ESP], -4);
|
||||
} else {
|
||||
vm_putw(ssp, sp - 2, get_vflags(env));
|
||||
ADD16(env->regs[R_ESP], -2);
|
||||
}
|
||||
env->eip = ip;
|
||||
VM86_FAULT_RETURN;
|
||||
|
||||
case 0x9d: /* popf */
|
||||
if (data32) {
|
||||
newflags = vm_getl(ssp, sp);
|
||||
ADD16(env->regs[R_ESP], 4);
|
||||
} else {
|
||||
newflags = vm_getw(ssp, sp);
|
||||
ADD16(env->regs[R_ESP], 2);
|
||||
}
|
||||
env->eip = ip;
|
||||
CHECK_IF_IN_TRAP();
|
||||
if (data32) {
|
||||
if (set_vflags_long(newflags, env))
|
||||
return;
|
||||
} else {
|
||||
if (set_vflags_short(newflags, env))
|
||||
return;
|
||||
}
|
||||
VM86_FAULT_RETURN;
|
||||
|
||||
case 0xcd: /* int */
|
||||
intno = csp[ip];
|
||||
ADD16(ip, 1);
|
||||
env->eip = ip;
|
||||
if (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) {
|
||||
if ( (ts->vm86plus.vm86plus.vm86dbg_intxxtab[intno >> 3] >>
|
||||
(intno &7)) & 1) {
|
||||
return_to_32bit(env, TARGET_VM86_INTx + (intno << 8));
|
||||
return;
|
||||
}
|
||||
}
|
||||
do_int(env, intno);
|
||||
break;
|
||||
|
||||
case 0xcf: /* iret */
|
||||
if (data32) {
|
||||
newip = vm_getl(ssp, sp) & 0xffff;
|
||||
newcs = vm_getl(ssp, sp + 4) & 0xffff;
|
||||
newflags = vm_getl(ssp, sp + 8);
|
||||
ADD16(env->regs[R_ESP], 12);
|
||||
} else {
|
||||
newip = vm_getw(ssp, sp);
|
||||
newcs = vm_getw(ssp, sp + 2);
|
||||
newflags = vm_getw(ssp, sp + 4);
|
||||
ADD16(env->regs[R_ESP], 6);
|
||||
}
|
||||
env->eip = newip;
|
||||
cpu_x86_load_seg(env, R_CS, newcs);
|
||||
CHECK_IF_IN_TRAP();
|
||||
if (data32) {
|
||||
if (set_vflags_long(newflags, env))
|
||||
return;
|
||||
} else {
|
||||
if (set_vflags_short(newflags, env))
|
||||
return;
|
||||
}
|
||||
VM86_FAULT_RETURN;
|
||||
|
||||
case 0xfa: /* cli */
|
||||
env->eip = ip;
|
||||
clear_IF(env);
|
||||
VM86_FAULT_RETURN;
|
||||
|
||||
case 0xfb: /* sti */
|
||||
env->eip = ip;
|
||||
if (set_IF(env))
|
||||
return;
|
||||
VM86_FAULT_RETURN;
|
||||
|
||||
default:
|
||||
/* real VM86 GPF exception */
|
||||
return_to_32bit(env, TARGET_VM86_UNKNOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int do_vm86(CPUX86State *env, long subfunction,
|
||||
struct target_vm86plus_struct * target_v86)
|
||||
{
|
||||
TaskState *ts = env->opaque;
|
||||
int ret;
|
||||
|
||||
switch (subfunction) {
|
||||
case TARGET_VM86_REQUEST_IRQ:
|
||||
case TARGET_VM86_FREE_IRQ:
|
||||
case TARGET_VM86_GET_IRQ_BITS:
|
||||
case TARGET_VM86_GET_AND_RESET_IRQ:
|
||||
gemu_log("qemu: unsupported vm86 subfunction (%ld)\n", subfunction);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
case TARGET_VM86_PLUS_INSTALL_CHECK:
|
||||
/* NOTE: on old vm86 stuff this will return the error
|
||||
from verify_area(), because the subfunction is
|
||||
interpreted as (invalid) address to vm86_struct.
|
||||
So the installation check works.
|
||||
*/
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ts->target_v86 = target_v86;
|
||||
/* save current CPU regs */
|
||||
ts->vm86_saved_regs.eax = 0; /* default vm86 syscall return code */
|
||||
ts->vm86_saved_regs.ebx = env->regs[R_EBX];
|
||||
ts->vm86_saved_regs.ecx = env->regs[R_ECX];
|
||||
ts->vm86_saved_regs.edx = env->regs[R_EDX];
|
||||
ts->vm86_saved_regs.esi = env->regs[R_ESI];
|
||||
ts->vm86_saved_regs.edi = env->regs[R_EDI];
|
||||
ts->vm86_saved_regs.ebp = env->regs[R_EBP];
|
||||
ts->vm86_saved_regs.esp = env->regs[R_ESP];
|
||||
ts->vm86_saved_regs.eflags = env->eflags;
|
||||
ts->vm86_saved_regs.eip = env->eip;
|
||||
ts->vm86_saved_regs.cs = env->segs[R_CS];
|
||||
ts->vm86_saved_regs.ss = env->segs[R_SS];
|
||||
ts->vm86_saved_regs.ds = env->segs[R_DS];
|
||||
ts->vm86_saved_regs.es = env->segs[R_ES];
|
||||
ts->vm86_saved_regs.fs = env->segs[R_FS];
|
||||
ts->vm86_saved_regs.gs = env->segs[R_GS];
|
||||
|
||||
/* build vm86 CPU state */
|
||||
ts->v86flags = tswap32(target_v86->regs.eflags);
|
||||
env->eflags = (env->eflags & ~SAFE_MASK) |
|
||||
(tswap32(target_v86->regs.eflags) & SAFE_MASK) | VM_MASK;
|
||||
|
||||
ts->vm86plus.cpu_type = tswapl(target_v86->cpu_type);
|
||||
switch (ts->vm86plus.cpu_type) {
|
||||
case TARGET_CPU_286:
|
||||
ts->v86mask = 0;
|
||||
break;
|
||||
case TARGET_CPU_386:
|
||||
ts->v86mask = NT_MASK | IOPL_MASK;
|
||||
break;
|
||||
case TARGET_CPU_486:
|
||||
ts->v86mask = AC_MASK | NT_MASK | IOPL_MASK;
|
||||
break;
|
||||
default:
|
||||
ts->v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK;
|
||||
break;
|
||||
}
|
||||
|
||||
env->regs[R_EBX] = tswap32(target_v86->regs.ebx);
|
||||
env->regs[R_ECX] = tswap32(target_v86->regs.ecx);
|
||||
env->regs[R_EDX] = tswap32(target_v86->regs.edx);
|
||||
env->regs[R_ESI] = tswap32(target_v86->regs.esi);
|
||||
env->regs[R_EDI] = tswap32(target_v86->regs.edi);
|
||||
env->regs[R_EBP] = tswap32(target_v86->regs.ebp);
|
||||
env->regs[R_ESP] = tswap32(target_v86->regs.esp);
|
||||
env->eip = tswap32(target_v86->regs.eip);
|
||||
cpu_x86_load_seg(env, R_CS, tswap16(target_v86->regs.cs));
|
||||
cpu_x86_load_seg(env, R_SS, tswap16(target_v86->regs.ss));
|
||||
cpu_x86_load_seg(env, R_DS, tswap16(target_v86->regs.ds));
|
||||
cpu_x86_load_seg(env, R_ES, tswap16(target_v86->regs.es));
|
||||
cpu_x86_load_seg(env, R_FS, tswap16(target_v86->regs.fs));
|
||||
cpu_x86_load_seg(env, R_GS, tswap16(target_v86->regs.gs));
|
||||
ret = tswap32(target_v86->regs.eax); /* eax will be restored at
|
||||
the end of the syscall */
|
||||
memcpy(&ts->vm86plus.int_revectored,
|
||||
&target_v86->int_revectored, 32);
|
||||
memcpy(&ts->vm86plus.int21_revectored,
|
||||
&target_v86->int21_revectored, 32);
|
||||
ts->vm86plus.vm86plus.flags = tswapl(target_v86->vm86plus.flags);
|
||||
memcpy(&ts->vm86plus.vm86plus.vm86dbg_intxxtab,
|
||||
target_v86->vm86plus.vm86dbg_intxxtab, 32);
|
||||
|
||||
#ifdef DEBUG_VM86
|
||||
fprintf(logfile, "do_vm86: cs:ip=%04x:%04x\n", env->segs[R_CS], env->eip);
|
||||
#endif
|
||||
/* now the virtual CPU is ready for vm86 execution ! */
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
245
op_string.h
Normal file
245
op_string.h
Normal file
@@ -0,0 +1,245 @@
|
||||
|
||||
void OPPROTO glue(glue(op_movs, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, inc;
|
||||
inc = (DF << SHIFT);
|
||||
v = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
glue(st, SUFFIX)(DI_ADDR, v);
|
||||
inc = (DF << SHIFT);
|
||||
INC_SI();
|
||||
INC_DI();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_rep_movs, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, inc;
|
||||
inc = (DF << SHIFT);
|
||||
while (CX != 0) {
|
||||
v = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
glue(st, SUFFIX)(DI_ADDR, v);
|
||||
INC_SI();
|
||||
INC_DI();
|
||||
DEC_CX();
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_stos, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int inc;
|
||||
glue(st, SUFFIX)(DI_ADDR, EAX);
|
||||
inc = (DF << SHIFT);
|
||||
INC_DI();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_rep_stos, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int inc;
|
||||
inc = (DF << SHIFT);
|
||||
while (CX != 0) {
|
||||
glue(st, SUFFIX)(DI_ADDR, EAX);
|
||||
INC_DI();
|
||||
DEC_CX();
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_lods, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, inc;
|
||||
v = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
#if SHIFT == 0
|
||||
EAX = (EAX & ~0xff) | v;
|
||||
#elif SHIFT == 1
|
||||
EAX = (EAX & ~0xffff) | v;
|
||||
#else
|
||||
EAX = v;
|
||||
#endif
|
||||
inc = (DF << SHIFT);
|
||||
INC_SI();
|
||||
}
|
||||
|
||||
/* don't know if it is used */
|
||||
void OPPROTO glue(glue(op_rep_lods, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, inc;
|
||||
inc = (DF << SHIFT);
|
||||
while (CX != 0) {
|
||||
v = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
#if SHIFT == 0
|
||||
EAX = (EAX & ~0xff) | v;
|
||||
#elif SHIFT == 1
|
||||
EAX = (EAX & ~0xffff) | v;
|
||||
#else
|
||||
EAX = v;
|
||||
#endif
|
||||
INC_SI();
|
||||
DEC_CX();
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_scas, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, inc;
|
||||
|
||||
v = glue(ldu, SUFFIX)(DI_ADDR);
|
||||
inc = (DF << SHIFT);
|
||||
INC_DI();
|
||||
CC_SRC = EAX;
|
||||
CC_DST = EAX - v;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_repz_scas, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
|
||||
if (CX != 0) {
|
||||
/* NOTE: the flags are not modified if CX == 0 */
|
||||
v1 = EAX & DATA_MASK;
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v2 = glue(ldu, SUFFIX)(DI_ADDR);
|
||||
INC_DI();
|
||||
DEC_CX();
|
||||
if (v1 != v2)
|
||||
break;
|
||||
} while (CX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_repnz_scas, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
|
||||
if (CX != 0) {
|
||||
/* NOTE: the flags are not modified if CX == 0 */
|
||||
v1 = EAX & DATA_MASK;
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v2 = glue(ldu, SUFFIX)(DI_ADDR);
|
||||
INC_DI();
|
||||
DEC_CX();
|
||||
if (v1 == v2)
|
||||
break;
|
||||
} while (CX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_cmps, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
v1 = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
v2 = glue(ldu, SUFFIX)(DI_ADDR);
|
||||
inc = (DF << SHIFT);
|
||||
INC_SI();
|
||||
INC_DI();
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_repz_cmps, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
if (CX != 0) {
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v1 = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
v2 = glue(ldu, SUFFIX)(DI_ADDR);
|
||||
INC_SI();
|
||||
INC_DI();
|
||||
DEC_CX();
|
||||
if (v1 != v2)
|
||||
break;
|
||||
} while (CX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_repnz_cmps, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
if (CX != 0) {
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v1 = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
v2 = glue(ldu, SUFFIX)(DI_ADDR);
|
||||
INC_SI();
|
||||
INC_DI();
|
||||
DEC_CX();
|
||||
if (v1 == v2)
|
||||
break;
|
||||
} while (CX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_outs, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, dx, inc;
|
||||
dx = EDX & 0xffff;
|
||||
v = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
glue(cpu_x86_out, SUFFIX)(env, dx, v);
|
||||
inc = (DF << SHIFT);
|
||||
INC_SI();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_rep_outs, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, dx, inc;
|
||||
inc = (DF << SHIFT);
|
||||
dx = EDX & 0xffff;
|
||||
while (CX != 0) {
|
||||
v = glue(ldu, SUFFIX)(SI_ADDR);
|
||||
glue(cpu_x86_out, SUFFIX)(env, dx, v);
|
||||
INC_SI();
|
||||
DEC_CX();
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_ins, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, dx, inc;
|
||||
dx = EDX & 0xffff;
|
||||
v = glue(cpu_x86_in, SUFFIX)(env, dx);
|
||||
glue(st, SUFFIX)(DI_ADDR, v);
|
||||
inc = (DF << SHIFT);
|
||||
INC_DI();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_rep_ins, SUFFIX), STRING_SUFFIX)(void)
|
||||
{
|
||||
int v, dx, inc;
|
||||
inc = (DF << SHIFT);
|
||||
dx = EDX & 0xffff;
|
||||
while (CX != 0) {
|
||||
v = glue(cpu_x86_in, SUFFIX)(env, dx);
|
||||
glue(st, SUFFIX)(DI_ADDR, v);
|
||||
INC_DI();
|
||||
DEC_CX();
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
#undef STRING_SUFFIX
|
||||
#undef SI_ADDR
|
||||
#undef DI_ADDR
|
||||
#undef INC_SI
|
||||
#undef INC_DI
|
||||
#undef CX
|
||||
#undef DEC_CX
|
534
opc-i386.h
534
opc-i386.h
@@ -1,534 +0,0 @@
|
||||
DEF(end)
|
||||
DEF(movl_A0_EAX)
|
||||
DEF(addl_A0_EAX)
|
||||
DEF(addl_A0_EAX_s1)
|
||||
DEF(addl_A0_EAX_s2)
|
||||
DEF(addl_A0_EAX_s3)
|
||||
DEF(movl_T0_EAX)
|
||||
DEF(movl_T1_EAX)
|
||||
DEF(movh_T0_EAX)
|
||||
DEF(movh_T1_EAX)
|
||||
DEF(movl_EAX_T0)
|
||||
DEF(movl_EAX_T1)
|
||||
DEF(movl_EAX_A0)
|
||||
DEF(cmovw_EAX_T1_T0)
|
||||
DEF(cmovl_EAX_T1_T0)
|
||||
DEF(movw_EAX_T0)
|
||||
DEF(movw_EAX_T1)
|
||||
DEF(movw_EAX_A0)
|
||||
DEF(movb_EAX_T0)
|
||||
DEF(movh_EAX_T0)
|
||||
DEF(movb_EAX_T1)
|
||||
DEF(movh_EAX_T1)
|
||||
DEF(movl_A0_ECX)
|
||||
DEF(addl_A0_ECX)
|
||||
DEF(addl_A0_ECX_s1)
|
||||
DEF(addl_A0_ECX_s2)
|
||||
DEF(addl_A0_ECX_s3)
|
||||
DEF(movl_T0_ECX)
|
||||
DEF(movl_T1_ECX)
|
||||
DEF(movh_T0_ECX)
|
||||
DEF(movh_T1_ECX)
|
||||
DEF(movl_ECX_T0)
|
||||
DEF(movl_ECX_T1)
|
||||
DEF(movl_ECX_A0)
|
||||
DEF(cmovw_ECX_T1_T0)
|
||||
DEF(cmovl_ECX_T1_T0)
|
||||
DEF(movw_ECX_T0)
|
||||
DEF(movw_ECX_T1)
|
||||
DEF(movw_ECX_A0)
|
||||
DEF(movb_ECX_T0)
|
||||
DEF(movh_ECX_T0)
|
||||
DEF(movb_ECX_T1)
|
||||
DEF(movh_ECX_T1)
|
||||
DEF(movl_A0_EDX)
|
||||
DEF(addl_A0_EDX)
|
||||
DEF(addl_A0_EDX_s1)
|
||||
DEF(addl_A0_EDX_s2)
|
||||
DEF(addl_A0_EDX_s3)
|
||||
DEF(movl_T0_EDX)
|
||||
DEF(movl_T1_EDX)
|
||||
DEF(movh_T0_EDX)
|
||||
DEF(movh_T1_EDX)
|
||||
DEF(movl_EDX_T0)
|
||||
DEF(movl_EDX_T1)
|
||||
DEF(movl_EDX_A0)
|
||||
DEF(cmovw_EDX_T1_T0)
|
||||
DEF(cmovl_EDX_T1_T0)
|
||||
DEF(movw_EDX_T0)
|
||||
DEF(movw_EDX_T1)
|
||||
DEF(movw_EDX_A0)
|
||||
DEF(movb_EDX_T0)
|
||||
DEF(movh_EDX_T0)
|
||||
DEF(movb_EDX_T1)
|
||||
DEF(movh_EDX_T1)
|
||||
DEF(movl_A0_EBX)
|
||||
DEF(addl_A0_EBX)
|
||||
DEF(addl_A0_EBX_s1)
|
||||
DEF(addl_A0_EBX_s2)
|
||||
DEF(addl_A0_EBX_s3)
|
||||
DEF(movl_T0_EBX)
|
||||
DEF(movl_T1_EBX)
|
||||
DEF(movh_T0_EBX)
|
||||
DEF(movh_T1_EBX)
|
||||
DEF(movl_EBX_T0)
|
||||
DEF(movl_EBX_T1)
|
||||
DEF(movl_EBX_A0)
|
||||
DEF(cmovw_EBX_T1_T0)
|
||||
DEF(cmovl_EBX_T1_T0)
|
||||
DEF(movw_EBX_T0)
|
||||
DEF(movw_EBX_T1)
|
||||
DEF(movw_EBX_A0)
|
||||
DEF(movb_EBX_T0)
|
||||
DEF(movh_EBX_T0)
|
||||
DEF(movb_EBX_T1)
|
||||
DEF(movh_EBX_T1)
|
||||
DEF(movl_A0_ESP)
|
||||
DEF(addl_A0_ESP)
|
||||
DEF(addl_A0_ESP_s1)
|
||||
DEF(addl_A0_ESP_s2)
|
||||
DEF(addl_A0_ESP_s3)
|
||||
DEF(movl_T0_ESP)
|
||||
DEF(movl_T1_ESP)
|
||||
DEF(movh_T0_ESP)
|
||||
DEF(movh_T1_ESP)
|
||||
DEF(movl_ESP_T0)
|
||||
DEF(movl_ESP_T1)
|
||||
DEF(movl_ESP_A0)
|
||||
DEF(cmovw_ESP_T1_T0)
|
||||
DEF(cmovl_ESP_T1_T0)
|
||||
DEF(movw_ESP_T0)
|
||||
DEF(movw_ESP_T1)
|
||||
DEF(movw_ESP_A0)
|
||||
DEF(movb_ESP_T0)
|
||||
DEF(movh_ESP_T0)
|
||||
DEF(movb_ESP_T1)
|
||||
DEF(movh_ESP_T1)
|
||||
DEF(movl_A0_EBP)
|
||||
DEF(addl_A0_EBP)
|
||||
DEF(addl_A0_EBP_s1)
|
||||
DEF(addl_A0_EBP_s2)
|
||||
DEF(addl_A0_EBP_s3)
|
||||
DEF(movl_T0_EBP)
|
||||
DEF(movl_T1_EBP)
|
||||
DEF(movh_T0_EBP)
|
||||
DEF(movh_T1_EBP)
|
||||
DEF(movl_EBP_T0)
|
||||
DEF(movl_EBP_T1)
|
||||
DEF(movl_EBP_A0)
|
||||
DEF(cmovw_EBP_T1_T0)
|
||||
DEF(cmovl_EBP_T1_T0)
|
||||
DEF(movw_EBP_T0)
|
||||
DEF(movw_EBP_T1)
|
||||
DEF(movw_EBP_A0)
|
||||
DEF(movb_EBP_T0)
|
||||
DEF(movh_EBP_T0)
|
||||
DEF(movb_EBP_T1)
|
||||
DEF(movh_EBP_T1)
|
||||
DEF(movl_A0_ESI)
|
||||
DEF(addl_A0_ESI)
|
||||
DEF(addl_A0_ESI_s1)
|
||||
DEF(addl_A0_ESI_s2)
|
||||
DEF(addl_A0_ESI_s3)
|
||||
DEF(movl_T0_ESI)
|
||||
DEF(movl_T1_ESI)
|
||||
DEF(movh_T0_ESI)
|
||||
DEF(movh_T1_ESI)
|
||||
DEF(movl_ESI_T0)
|
||||
DEF(movl_ESI_T1)
|
||||
DEF(movl_ESI_A0)
|
||||
DEF(cmovw_ESI_T1_T0)
|
||||
DEF(cmovl_ESI_T1_T0)
|
||||
DEF(movw_ESI_T0)
|
||||
DEF(movw_ESI_T1)
|
||||
DEF(movw_ESI_A0)
|
||||
DEF(movb_ESI_T0)
|
||||
DEF(movh_ESI_T0)
|
||||
DEF(movb_ESI_T1)
|
||||
DEF(movh_ESI_T1)
|
||||
DEF(movl_A0_EDI)
|
||||
DEF(addl_A0_EDI)
|
||||
DEF(addl_A0_EDI_s1)
|
||||
DEF(addl_A0_EDI_s2)
|
||||
DEF(addl_A0_EDI_s3)
|
||||
DEF(movl_T0_EDI)
|
||||
DEF(movl_T1_EDI)
|
||||
DEF(movh_T0_EDI)
|
||||
DEF(movh_T1_EDI)
|
||||
DEF(movl_EDI_T0)
|
||||
DEF(movl_EDI_T1)
|
||||
DEF(movl_EDI_A0)
|
||||
DEF(cmovw_EDI_T1_T0)
|
||||
DEF(cmovl_EDI_T1_T0)
|
||||
DEF(movw_EDI_T0)
|
||||
DEF(movw_EDI_T1)
|
||||
DEF(movw_EDI_A0)
|
||||
DEF(movb_EDI_T0)
|
||||
DEF(movh_EDI_T0)
|
||||
DEF(movb_EDI_T1)
|
||||
DEF(movh_EDI_T1)
|
||||
DEF(addl_T0_T1_cc)
|
||||
DEF(orl_T0_T1_cc)
|
||||
DEF(andl_T0_T1_cc)
|
||||
DEF(subl_T0_T1_cc)
|
||||
DEF(xorl_T0_T1_cc)
|
||||
DEF(cmpl_T0_T1_cc)
|
||||
DEF(negl_T0_cc)
|
||||
DEF(incl_T0_cc)
|
||||
DEF(decl_T0_cc)
|
||||
DEF(testl_T0_T1_cc)
|
||||
DEF(addl_T0_T1)
|
||||
DEF(orl_T0_T1)
|
||||
DEF(andl_T0_T1)
|
||||
DEF(subl_T0_T1)
|
||||
DEF(xorl_T0_T1)
|
||||
DEF(negl_T0)
|
||||
DEF(incl_T0)
|
||||
DEF(decl_T0)
|
||||
DEF(notl_T0)
|
||||
DEF(bswapl_T0)
|
||||
DEF(mulb_AL_T0)
|
||||
DEF(imulb_AL_T0)
|
||||
DEF(mulw_AX_T0)
|
||||
DEF(imulw_AX_T0)
|
||||
DEF(mull_EAX_T0)
|
||||
DEF(imull_EAX_T0)
|
||||
DEF(imulw_T0_T1)
|
||||
DEF(imull_T0_T1)
|
||||
DEF(divb_AL_T0)
|
||||
DEF(idivb_AL_T0)
|
||||
DEF(divw_AX_T0)
|
||||
DEF(idivw_AX_T0)
|
||||
DEF(divl_EAX_T0)
|
||||
DEF(idivl_EAX_T0)
|
||||
DEF(movl_T0_im)
|
||||
DEF(addl_T0_im)
|
||||
DEF(andl_T0_ffff)
|
||||
DEF(movl_T0_T1)
|
||||
DEF(movl_T1_im)
|
||||
DEF(addl_T1_im)
|
||||
DEF(movl_T1_A0)
|
||||
DEF(movl_A0_im)
|
||||
DEF(addl_A0_im)
|
||||
DEF(andl_A0_ffff)
|
||||
DEF(ldub_T0_A0)
|
||||
DEF(ldsb_T0_A0)
|
||||
DEF(lduw_T0_A0)
|
||||
DEF(ldsw_T0_A0)
|
||||
DEF(ldl_T0_A0)
|
||||
DEF(ldub_T1_A0)
|
||||
DEF(ldsb_T1_A0)
|
||||
DEF(lduw_T1_A0)
|
||||
DEF(ldsw_T1_A0)
|
||||
DEF(ldl_T1_A0)
|
||||
DEF(stb_T0_A0)
|
||||
DEF(stw_T0_A0)
|
||||
DEF(stl_T0_A0)
|
||||
DEF(add_bitw_A0_T1)
|
||||
DEF(add_bitl_A0_T1)
|
||||
DEF(jmp_T0)
|
||||
DEF(jmp_im)
|
||||
DEF(int_im)
|
||||
DEF(int3)
|
||||
DEF(into)
|
||||
DEF(jb_subb)
|
||||
DEF(jz_subb)
|
||||
DEF(jbe_subb)
|
||||
DEF(js_subb)
|
||||
DEF(jl_subb)
|
||||
DEF(jle_subb)
|
||||
DEF(setb_T0_subb)
|
||||
DEF(setz_T0_subb)
|
||||
DEF(setbe_T0_subb)
|
||||
DEF(sets_T0_subb)
|
||||
DEF(setl_T0_subb)
|
||||
DEF(setle_T0_subb)
|
||||
DEF(rolb_T0_T1_cc)
|
||||
DEF(rolb_T0_T1)
|
||||
DEF(rorb_T0_T1_cc)
|
||||
DEF(rorb_T0_T1)
|
||||
DEF(rclb_T0_T1_cc)
|
||||
DEF(rcrb_T0_T1_cc)
|
||||
DEF(shlb_T0_T1_cc)
|
||||
DEF(shlb_T0_T1)
|
||||
DEF(shrb_T0_T1_cc)
|
||||
DEF(shrb_T0_T1)
|
||||
DEF(sarb_T0_T1_cc)
|
||||
DEF(sarb_T0_T1)
|
||||
DEF(adcb_T0_T1_cc)
|
||||
DEF(sbbb_T0_T1_cc)
|
||||
DEF(cmpxchgb_T0_T1_EAX_cc)
|
||||
DEF(movsb)
|
||||
DEF(rep_movsb)
|
||||
DEF(stosb)
|
||||
DEF(rep_stosb)
|
||||
DEF(lodsb)
|
||||
DEF(rep_lodsb)
|
||||
DEF(scasb)
|
||||
DEF(repz_scasb)
|
||||
DEF(repnz_scasb)
|
||||
DEF(cmpsb)
|
||||
DEF(repz_cmpsb)
|
||||
DEF(repnz_cmpsb)
|
||||
DEF(outsb)
|
||||
DEF(rep_outsb)
|
||||
DEF(insb)
|
||||
DEF(rep_insb)
|
||||
DEF(outb_T0_T1)
|
||||
DEF(inb_T0_T1)
|
||||
DEF(jb_subw)
|
||||
DEF(jz_subw)
|
||||
DEF(jbe_subw)
|
||||
DEF(js_subw)
|
||||
DEF(jl_subw)
|
||||
DEF(jle_subw)
|
||||
DEF(loopnzw)
|
||||
DEF(loopzw)
|
||||
DEF(loopw)
|
||||
DEF(jecxzw)
|
||||
DEF(setb_T0_subw)
|
||||
DEF(setz_T0_subw)
|
||||
DEF(setbe_T0_subw)
|
||||
DEF(sets_T0_subw)
|
||||
DEF(setl_T0_subw)
|
||||
DEF(setle_T0_subw)
|
||||
DEF(rolw_T0_T1_cc)
|
||||
DEF(rolw_T0_T1)
|
||||
DEF(rorw_T0_T1_cc)
|
||||
DEF(rorw_T0_T1)
|
||||
DEF(rclw_T0_T1_cc)
|
||||
DEF(rcrw_T0_T1_cc)
|
||||
DEF(shlw_T0_T1_cc)
|
||||
DEF(shlw_T0_T1)
|
||||
DEF(shrw_T0_T1_cc)
|
||||
DEF(shrw_T0_T1)
|
||||
DEF(sarw_T0_T1_cc)
|
||||
DEF(sarw_T0_T1)
|
||||
DEF(shldw_T0_T1_im_cc)
|
||||
DEF(shldw_T0_T1_ECX_cc)
|
||||
DEF(shrdw_T0_T1_im_cc)
|
||||
DEF(shrdw_T0_T1_ECX_cc)
|
||||
DEF(adcw_T0_T1_cc)
|
||||
DEF(sbbw_T0_T1_cc)
|
||||
DEF(cmpxchgw_T0_T1_EAX_cc)
|
||||
DEF(btw_T0_T1_cc)
|
||||
DEF(btsw_T0_T1_cc)
|
||||
DEF(btrw_T0_T1_cc)
|
||||
DEF(btcw_T0_T1_cc)
|
||||
DEF(bsfw_T0_cc)
|
||||
DEF(bsrw_T0_cc)
|
||||
DEF(movsw)
|
||||
DEF(rep_movsw)
|
||||
DEF(stosw)
|
||||
DEF(rep_stosw)
|
||||
DEF(lodsw)
|
||||
DEF(rep_lodsw)
|
||||
DEF(scasw)
|
||||
DEF(repz_scasw)
|
||||
DEF(repnz_scasw)
|
||||
DEF(cmpsw)
|
||||
DEF(repz_cmpsw)
|
||||
DEF(repnz_cmpsw)
|
||||
DEF(outsw)
|
||||
DEF(rep_outsw)
|
||||
DEF(insw)
|
||||
DEF(rep_insw)
|
||||
DEF(outw_T0_T1)
|
||||
DEF(inw_T0_T1)
|
||||
DEF(jb_subl)
|
||||
DEF(jz_subl)
|
||||
DEF(jbe_subl)
|
||||
DEF(js_subl)
|
||||
DEF(jl_subl)
|
||||
DEF(jle_subl)
|
||||
DEF(loopnzl)
|
||||
DEF(loopzl)
|
||||
DEF(loopl)
|
||||
DEF(jecxzl)
|
||||
DEF(setb_T0_subl)
|
||||
DEF(setz_T0_subl)
|
||||
DEF(setbe_T0_subl)
|
||||
DEF(sets_T0_subl)
|
||||
DEF(setl_T0_subl)
|
||||
DEF(setle_T0_subl)
|
||||
DEF(roll_T0_T1_cc)
|
||||
DEF(roll_T0_T1)
|
||||
DEF(rorl_T0_T1_cc)
|
||||
DEF(rorl_T0_T1)
|
||||
DEF(rcll_T0_T1_cc)
|
||||
DEF(rcrl_T0_T1_cc)
|
||||
DEF(shll_T0_T1_cc)
|
||||
DEF(shll_T0_T1)
|
||||
DEF(shrl_T0_T1_cc)
|
||||
DEF(shrl_T0_T1)
|
||||
DEF(sarl_T0_T1_cc)
|
||||
DEF(sarl_T0_T1)
|
||||
DEF(shldl_T0_T1_im_cc)
|
||||
DEF(shldl_T0_T1_ECX_cc)
|
||||
DEF(shrdl_T0_T1_im_cc)
|
||||
DEF(shrdl_T0_T1_ECX_cc)
|
||||
DEF(adcl_T0_T1_cc)
|
||||
DEF(sbbl_T0_T1_cc)
|
||||
DEF(cmpxchgl_T0_T1_EAX_cc)
|
||||
DEF(btl_T0_T1_cc)
|
||||
DEF(btsl_T0_T1_cc)
|
||||
DEF(btrl_T0_T1_cc)
|
||||
DEF(btcl_T0_T1_cc)
|
||||
DEF(bsfl_T0_cc)
|
||||
DEF(bsrl_T0_cc)
|
||||
DEF(movsl)
|
||||
DEF(rep_movsl)
|
||||
DEF(stosl)
|
||||
DEF(rep_stosl)
|
||||
DEF(lodsl)
|
||||
DEF(rep_lodsl)
|
||||
DEF(scasl)
|
||||
DEF(repz_scasl)
|
||||
DEF(repnz_scasl)
|
||||
DEF(cmpsl)
|
||||
DEF(repz_cmpsl)
|
||||
DEF(repnz_cmpsl)
|
||||
DEF(outsl)
|
||||
DEF(rep_outsl)
|
||||
DEF(insl)
|
||||
DEF(rep_insl)
|
||||
DEF(outl_T0_T1)
|
||||
DEF(inl_T0_T1)
|
||||
DEF(movsbl_T0_T0)
|
||||
DEF(movzbl_T0_T0)
|
||||
DEF(movswl_T0_T0)
|
||||
DEF(movzwl_T0_T0)
|
||||
DEF(movswl_EAX_AX)
|
||||
DEF(movsbw_AX_AL)
|
||||
DEF(movslq_EDX_EAX)
|
||||
DEF(movswl_DX_AX)
|
||||
DEF(pushl_T0)
|
||||
DEF(pushw_T0)
|
||||
DEF(pushl_ss32_T0)
|
||||
DEF(pushw_ss32_T0)
|
||||
DEF(pushl_ss16_T0)
|
||||
DEF(pushw_ss16_T0)
|
||||
DEF(popl_T0)
|
||||
DEF(popw_T0)
|
||||
DEF(popl_ss32_T0)
|
||||
DEF(popw_ss32_T0)
|
||||
DEF(popl_ss16_T0)
|
||||
DEF(popw_ss16_T0)
|
||||
DEF(addl_ESP_4)
|
||||
DEF(addl_ESP_2)
|
||||
DEF(addw_ESP_4)
|
||||
DEF(addw_ESP_2)
|
||||
DEF(addl_ESP_im)
|
||||
DEF(addw_ESP_im)
|
||||
DEF(rdtsc)
|
||||
DEF(aam)
|
||||
DEF(aad)
|
||||
DEF(aaa)
|
||||
DEF(aas)
|
||||
DEF(daa)
|
||||
DEF(das)
|
||||
DEF(movl_seg_T0)
|
||||
DEF(movl_T0_seg)
|
||||
DEF(addl_A0_seg)
|
||||
DEF(jo_cc)
|
||||
DEF(jb_cc)
|
||||
DEF(jz_cc)
|
||||
DEF(jbe_cc)
|
||||
DEF(js_cc)
|
||||
DEF(jp_cc)
|
||||
DEF(jl_cc)
|
||||
DEF(jle_cc)
|
||||
DEF(seto_T0_cc)
|
||||
DEF(setb_T0_cc)
|
||||
DEF(setz_T0_cc)
|
||||
DEF(setbe_T0_cc)
|
||||
DEF(sets_T0_cc)
|
||||
DEF(setp_T0_cc)
|
||||
DEF(setl_T0_cc)
|
||||
DEF(setle_T0_cc)
|
||||
DEF(xor_T0_1)
|
||||
DEF(set_cc_op)
|
||||
DEF(movl_eflags_T0)
|
||||
DEF(movb_eflags_T0)
|
||||
DEF(movl_T0_eflags)
|
||||
DEF(cld)
|
||||
DEF(std)
|
||||
DEF(clc)
|
||||
DEF(stc)
|
||||
DEF(cmc)
|
||||
DEF(salc)
|
||||
DEF(flds_FT0_A0)
|
||||
DEF(fldl_FT0_A0)
|
||||
DEF(fild_FT0_A0)
|
||||
DEF(fildl_FT0_A0)
|
||||
DEF(fildll_FT0_A0)
|
||||
DEF(flds_ST0_A0)
|
||||
DEF(fldl_ST0_A0)
|
||||
DEF(fldt_ST0_A0)
|
||||
DEF(fild_ST0_A0)
|
||||
DEF(fildl_ST0_A0)
|
||||
DEF(fildll_ST0_A0)
|
||||
DEF(fsts_ST0_A0)
|
||||
DEF(fstl_ST0_A0)
|
||||
DEF(fstt_ST0_A0)
|
||||
DEF(fist_ST0_A0)
|
||||
DEF(fistl_ST0_A0)
|
||||
DEF(fistll_ST0_A0)
|
||||
DEF(fbld_ST0_A0)
|
||||
DEF(fbst_ST0_A0)
|
||||
DEF(fpush)
|
||||
DEF(fpop)
|
||||
DEF(fdecstp)
|
||||
DEF(fincstp)
|
||||
DEF(fmov_ST0_FT0)
|
||||
DEF(fmov_FT0_STN)
|
||||
DEF(fmov_ST0_STN)
|
||||
DEF(fmov_STN_ST0)
|
||||
DEF(fxchg_ST0_STN)
|
||||
DEF(fcom_ST0_FT0)
|
||||
DEF(fucom_ST0_FT0)
|
||||
DEF(fadd_ST0_FT0)
|
||||
DEF(fmul_ST0_FT0)
|
||||
DEF(fsub_ST0_FT0)
|
||||
DEF(fsubr_ST0_FT0)
|
||||
DEF(fdiv_ST0_FT0)
|
||||
DEF(fdivr_ST0_FT0)
|
||||
DEF(fadd_STN_ST0)
|
||||
DEF(fmul_STN_ST0)
|
||||
DEF(fsub_STN_ST0)
|
||||
DEF(fsubr_STN_ST0)
|
||||
DEF(fdiv_STN_ST0)
|
||||
DEF(fdivr_STN_ST0)
|
||||
DEF(fchs_ST0)
|
||||
DEF(fabs_ST0)
|
||||
DEF(fxam_ST0)
|
||||
DEF(fld1_ST0)
|
||||
DEF(fldl2t_ST0)
|
||||
DEF(fldl2e_ST0)
|
||||
DEF(fldpi_ST0)
|
||||
DEF(fldlg2_ST0)
|
||||
DEF(fldln2_ST0)
|
||||
DEF(fldz_ST0)
|
||||
DEF(fldz_FT0)
|
||||
DEF(f2xm1)
|
||||
DEF(fyl2x)
|
||||
DEF(fptan)
|
||||
DEF(fpatan)
|
||||
DEF(fxtract)
|
||||
DEF(fprem1)
|
||||
DEF(fprem)
|
||||
DEF(fyl2xp1)
|
||||
DEF(fsqrt)
|
||||
DEF(fsincos)
|
||||
DEF(frndint)
|
||||
DEF(fscale)
|
||||
DEF(fsin)
|
||||
DEF(fcos)
|
||||
DEF(fnstsw_A0)
|
||||
DEF(fnstsw_EAX)
|
||||
DEF(fnstcw_A0)
|
||||
DEF(fldcw_A0)
|
||||
DEF(fclex)
|
||||
DEF(fninit)
|
||||
DEF(lock)
|
||||
DEF(unlock)
|
@@ -1,5 +1,23 @@
|
||||
/* templates for various register related operations */
|
||||
|
||||
/*
|
||||
* i386 micro operations (templates for various register related
|
||||
* operations)
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
void OPPROTO glue(op_movl_A0,REGNAME)(void)
|
||||
{
|
||||
A0 = REG;
|
||||
|
313
ops_template.h
313
ops_template.h
@@ -4,21 +4,20 @@
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define DATA_BITS (1 << (3 + SHIFT))
|
||||
#define SHIFT_MASK (DATA_BITS - 1)
|
||||
#define SIGN_MASK (1 << (DATA_BITS - 1))
|
||||
@@ -205,8 +204,13 @@ static int glue(compute_all_shl, SUFFIX)(void)
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
#if DATA_BITS == 32
|
||||
static int glue(compute_c_shl, SUFFIX)(void)
|
||||
{
|
||||
return (CC_SRC >> (DATA_BITS - 1)) & CC_C;
|
||||
}
|
||||
|
||||
#if DATA_BITS == 32
|
||||
static int glue(compute_c_sar, SUFFIX)(void)
|
||||
{
|
||||
return CC_SRC & 1;
|
||||
}
|
||||
@@ -234,18 +238,18 @@ void OPPROTO glue(op_jb_sub, SUFFIX)(void)
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_TYPE)src1 < (DATA_TYPE)src2)
|
||||
EIP = PARAM1;
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
EIP = PARAM2;
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_jz_sub, SUFFIX)(void)
|
||||
{
|
||||
if ((DATA_TYPE)CC_DST == 0)
|
||||
EIP = PARAM1;
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
EIP = PARAM2;
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
@@ -256,18 +260,18 @@ void OPPROTO glue(op_jbe_sub, SUFFIX)(void)
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_TYPE)src1 <= (DATA_TYPE)src2)
|
||||
EIP = PARAM1;
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
EIP = PARAM2;
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_js_sub, SUFFIX)(void)
|
||||
{
|
||||
if (CC_DST & SIGN_MASK)
|
||||
EIP = PARAM1;
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
EIP = PARAM2;
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
@@ -278,9 +282,9 @@ void OPPROTO glue(op_jl_sub, SUFFIX)(void)
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_STYPE)src1 < (DATA_STYPE)src2)
|
||||
EIP = PARAM1;
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
EIP = PARAM2;
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
@@ -291,9 +295,9 @@ void OPPROTO glue(op_jle_sub, SUFFIX)(void)
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_STYPE)src1 <= (DATA_STYPE)src2)
|
||||
EIP = PARAM1;
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
EIP = PARAM2;
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
@@ -610,6 +614,7 @@ void OPPROTO glue(glue(op_shld, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_im_cc)(void)
|
||||
@@ -644,6 +649,7 @@ void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -671,6 +677,7 @@ void OPPROTO glue(glue(op_shld, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SHLB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_im_cc)(void)
|
||||
@@ -697,6 +704,7 @@ void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -807,246 +815,45 @@ void OPPROTO glue(glue(op_bsr, SUFFIX), _T0_cc)(void)
|
||||
#endif
|
||||
|
||||
/* string operations */
|
||||
/* XXX: maybe use lower level instructions to ease exception handling */
|
||||
/* XXX: maybe use lower level instructions to ease 16 bit / segment handling */
|
||||
|
||||
void OPPROTO glue(op_movs, SUFFIX)(void)
|
||||
{
|
||||
int v;
|
||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
||||
glue(st, SUFFIX)((void *)EDI, v);
|
||||
ESI += (DF << SHIFT);
|
||||
EDI += (DF << SHIFT);
|
||||
}
|
||||
#define STRING_SUFFIX _fast
|
||||
#define SI_ADDR (void *)ESI
|
||||
#define DI_ADDR (void *)EDI
|
||||
#define INC_SI() ESI += inc
|
||||
#define INC_DI() EDI += inc
|
||||
#define CX ECX
|
||||
#define DEC_CX() ECX--
|
||||
#include "op_string.h"
|
||||
|
||||
void OPPROTO glue(op_rep_movs, SUFFIX)(void)
|
||||
{
|
||||
int v, inc;
|
||||
inc = (DF << SHIFT);
|
||||
while (ECX != 0) {
|
||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
||||
glue(st, SUFFIX)((void *)EDI, v);
|
||||
ESI += inc;
|
||||
EDI += inc;
|
||||
ECX--;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
#define STRING_SUFFIX _a32
|
||||
#define SI_ADDR (uint8_t *)A0 + ESI
|
||||
#define DI_ADDR env->seg_cache[R_ES].base + EDI
|
||||
#define INC_SI() ESI += inc
|
||||
#define INC_DI() EDI += inc
|
||||
#define CX ECX
|
||||
#define DEC_CX() ECX--
|
||||
#include "op_string.h"
|
||||
|
||||
void OPPROTO glue(op_stos, SUFFIX)(void)
|
||||
{
|
||||
glue(st, SUFFIX)((void *)EDI, EAX);
|
||||
EDI += (DF << SHIFT);
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_rep_stos, SUFFIX)(void)
|
||||
{
|
||||
int inc;
|
||||
inc = (DF << SHIFT);
|
||||
while (ECX != 0) {
|
||||
glue(st, SUFFIX)((void *)EDI, EAX);
|
||||
EDI += inc;
|
||||
ECX--;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_lods, SUFFIX)(void)
|
||||
{
|
||||
int v;
|
||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
||||
#if SHIFT == 0
|
||||
EAX = (EAX & ~0xff) | v;
|
||||
#elif SHIFT == 1
|
||||
EAX = (EAX & ~0xffff) | v;
|
||||
#else
|
||||
EAX = v;
|
||||
#endif
|
||||
ESI += (DF << SHIFT);
|
||||
}
|
||||
|
||||
/* don't know if it is used */
|
||||
void OPPROTO glue(op_rep_lods, SUFFIX)(void)
|
||||
{
|
||||
int v, inc;
|
||||
inc = (DF << SHIFT);
|
||||
while (ECX != 0) {
|
||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
||||
#if SHIFT == 0
|
||||
EAX = (EAX & ~0xff) | v;
|
||||
#elif SHIFT == 1
|
||||
EAX = (EAX & ~0xffff) | v;
|
||||
#else
|
||||
EAX = v;
|
||||
#endif
|
||||
ESI += inc;
|
||||
ECX--;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_scas, SUFFIX)(void)
|
||||
{
|
||||
int v;
|
||||
|
||||
v = glue(ldu, SUFFIX)((void *)EDI);
|
||||
EDI += (DF << SHIFT);
|
||||
CC_SRC = EAX;
|
||||
CC_DST = EAX - v;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_repz_scas, SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
|
||||
if (ECX != 0) {
|
||||
/* NOTE: the flags are not modified if ECX == 0 */
|
||||
v1 = EAX & DATA_MASK;
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v2 = glue(ldu, SUFFIX)((void *)EDI);
|
||||
EDI += inc;
|
||||
ECX--;
|
||||
if (v1 != v2)
|
||||
break;
|
||||
} while (ECX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_repnz_scas, SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
|
||||
if (ECX != 0) {
|
||||
/* NOTE: the flags are not modified if ECX == 0 */
|
||||
v1 = EAX & DATA_MASK;
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v2 = glue(ldu, SUFFIX)((void *)EDI);
|
||||
EDI += inc;
|
||||
ECX--;
|
||||
if (v1 == v2)
|
||||
break;
|
||||
} while (ECX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_cmps, SUFFIX)(void)
|
||||
{
|
||||
int v1, v2;
|
||||
v1 = glue(ldu, SUFFIX)((void *)ESI);
|
||||
v2 = glue(ldu, SUFFIX)((void *)EDI);
|
||||
ESI += (DF << SHIFT);
|
||||
EDI += (DF << SHIFT);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_repz_cmps, SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
if (ECX != 0) {
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v1 = glue(ldu, SUFFIX)((void *)ESI);
|
||||
v2 = glue(ldu, SUFFIX)((void *)EDI);
|
||||
ESI += inc;
|
||||
EDI += inc;
|
||||
ECX--;
|
||||
if (v1 != v2)
|
||||
break;
|
||||
} while (ECX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_repnz_cmps, SUFFIX)(void)
|
||||
{
|
||||
int v1, v2, inc;
|
||||
if (ECX != 0) {
|
||||
inc = (DF << SHIFT);
|
||||
do {
|
||||
v1 = glue(ldu, SUFFIX)((void *)ESI);
|
||||
v2 = glue(ldu, SUFFIX)((void *)EDI);
|
||||
ESI += inc;
|
||||
EDI += inc;
|
||||
ECX--;
|
||||
if (v1 == v2)
|
||||
break;
|
||||
} while (ECX != 0);
|
||||
CC_SRC = v1;
|
||||
CC_DST = v1 - v2;
|
||||
CC_OP = CC_OP_SUBB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
#define STRING_SUFFIX _a16
|
||||
#define SI_ADDR (uint8_t *)A0 + (ESI & 0xffff)
|
||||
#define DI_ADDR env->seg_cache[R_ES].base + (EDI & 0xffff)
|
||||
#define INC_SI() ESI = (ESI & ~0xffff) | ((ESI + inc) & 0xffff)
|
||||
#define INC_DI() EDI = (EDI & ~0xffff) | ((EDI + inc) & 0xffff)
|
||||
#define CX (ECX & 0xffff)
|
||||
#define DEC_CX() ECX = (ECX & ~0xffff) | ((ECX - 1) & 0xffff)
|
||||
#include "op_string.h"
|
||||
|
||||
/* port I/O */
|
||||
|
||||
void OPPROTO glue(op_outs, SUFFIX)(void)
|
||||
{
|
||||
int v, dx;
|
||||
dx = EDX & 0xffff;
|
||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
||||
glue(cpu_x86_out, SUFFIX)(dx, v);
|
||||
ESI += (DF << SHIFT);
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_rep_outs, SUFFIX)(void)
|
||||
{
|
||||
int v, dx, inc;
|
||||
inc = (DF << SHIFT);
|
||||
dx = EDX & 0xffff;
|
||||
while (ECX != 0) {
|
||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
||||
glue(cpu_x86_out, SUFFIX)(dx, v);
|
||||
ESI += inc;
|
||||
ECX--;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_ins, SUFFIX)(void)
|
||||
{
|
||||
int v, dx;
|
||||
dx = EDX & 0xffff;
|
||||
v = glue(cpu_x86_in, SUFFIX)(dx);
|
||||
glue(st, SUFFIX)((void *)EDI, v);
|
||||
EDI += (DF << SHIFT);
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_rep_ins, SUFFIX)(void)
|
||||
{
|
||||
int v, dx, inc;
|
||||
inc = (DF << SHIFT);
|
||||
dx = EDX & 0xffff;
|
||||
while (ECX != 0) {
|
||||
v = glue(cpu_x86_in, SUFFIX)(dx);
|
||||
glue(st, SUFFIX)((void *)EDI, v);
|
||||
EDI += (DF << SHIFT);
|
||||
ECX--;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_out, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
glue(cpu_x86_out, SUFFIX)(T0 & 0xffff, T1 & DATA_MASK);
|
||||
glue(cpu_x86_out, SUFFIX)(env, T0 & 0xffff, T1 & DATA_MASK);
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_in, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
T1 = glue(cpu_x86_in, SUFFIX)(T0 & 0xffff);
|
||||
T1 = glue(cpu_x86_in, SUFFIX)(env, T0 & 0xffff);
|
||||
}
|
||||
|
||||
#undef DATA_BITS
|
||||
|
447
qemu-doc.texi
Normal file
447
qemu-doc.texi
Normal file
@@ -0,0 +1,447 @@
|
||||
\input texinfo @c -*- texinfo -*-
|
||||
|
||||
@settitle QEMU x86 Emulator Reference Documentation
|
||||
@titlepage
|
||||
@sp 7
|
||||
@center @titlefont{QEMU x86 Emulator Reference Documentation}
|
||||
@sp 3
|
||||
@end titlepage
|
||||
|
||||
@chapter Introduction
|
||||
|
||||
QEMU is an x86 processor emulator. Its purpose is to run x86 Linux
|
||||
processes on non-x86 Linux architectures such as PowerPC. By using
|
||||
dynamic translation it achieves a reasonnable speed while being easy to
|
||||
port on new host CPUs. Its main goal is to be able to launch the
|
||||
@code{Wine} Windows API emulator (@url{http://www.winehq.org}) or
|
||||
@code{DOSEMU} (@url{http://www.dosemu.org}) on non-x86 CPUs.
|
||||
|
||||
QEMU features:
|
||||
|
||||
@itemize
|
||||
|
||||
@item User space only x86 emulator.
|
||||
|
||||
@item Currently ported on i386, PowerPC. Work in progress for S390, Alpha and Sparc.
|
||||
|
||||
@item Using dynamic translation to native code for reasonnable speed.
|
||||
|
||||
@item The virtual x86 CPU supports 16 bit and 32 bit addressing with segmentation.
|
||||
User space LDT and GDT are emulated. VM86 mode is also supported.
|
||||
|
||||
@item Generic Linux system call converter, including most ioctls.
|
||||
|
||||
@item clone() emulation using native CPU clone() to use Linux scheduler for threads.
|
||||
|
||||
@item Accurate signal handling by remapping host signals to virtual x86 signals.
|
||||
|
||||
@item Precise user space x86 exceptions.
|
||||
|
||||
@item Self-modifying code support.
|
||||
|
||||
@item Support of host page sizes bigger than 4KB.
|
||||
|
||||
@item QEMU can emulate itself on x86.
|
||||
|
||||
@item The virtual x86 CPU is a library (@code{libqemu}) which can be used
|
||||
in other projects.
|
||||
|
||||
@item An extensive Linux x86 CPU test program is included @file{tests/test-i386}.
|
||||
It can be used to test other x86 virtual CPUs.
|
||||
|
||||
@end itemize
|
||||
|
||||
Current QEMU limitations:
|
||||
|
||||
@itemize
|
||||
|
||||
@item No SSE/MMX support (yet).
|
||||
|
||||
@item No x86-64 support.
|
||||
|
||||
@item IPC syscalls are missing.
|
||||
|
||||
@item The x86 segment limits and access rights are not tested at every
|
||||
memory access (and will never be to have good performances).
|
||||
|
||||
@item On non x86 host CPUs, @code{double}s are used instead of the non standard
|
||||
10 byte @code{long double}s of x86 for floating point emulation to get
|
||||
maximum performances.
|
||||
|
||||
@end itemize
|
||||
|
||||
@chapter Invocation
|
||||
|
||||
@section Quick Start
|
||||
|
||||
In order to launch a Linux process, QEMU needs the process executable
|
||||
itself and all the target (x86) dynamic libraries used by it.
|
||||
|
||||
@itemize
|
||||
|
||||
@item On x86, you can just try to launch any process by using the native
|
||||
libraries:
|
||||
|
||||
@example
|
||||
qemu -L / /bin/ls
|
||||
@end example
|
||||
|
||||
@code{-L /} tells that the x86 dynamic linker must be searched with a
|
||||
@file{/} prefix.
|
||||
|
||||
@item Since QEMU is also a linux process, you can launch qemu with qemu:
|
||||
|
||||
@example
|
||||
qemu -L / qemu -L / /bin/ls
|
||||
@end example
|
||||
|
||||
@item On non x86 CPUs, you need first to download at least an x86 glibc
|
||||
(@file{qemu-XXX-i386-glibc21.tar.gz} on the QEMU web page). Ensure that
|
||||
@code{LD_LIBRARY_PATH} is not set:
|
||||
|
||||
@example
|
||||
unset LD_LIBRARY_PATH
|
||||
@end example
|
||||
|
||||
Then you can launch the precompiled @file{ls} x86 executable:
|
||||
|
||||
@example
|
||||
qemu /usr/local/qemu-i386/bin/ls-i386
|
||||
@end example
|
||||
You can look at @file{/usr/local/qemu-i386/bin/qemu-conf.sh} so that
|
||||
QEMU is automatically launched by the Linux kernel when you try to
|
||||
launch x86 executables. It requires the @code{binfmt_misc} module in the
|
||||
Linux kernel.
|
||||
|
||||
@item The x86 version of QEMU is also included. You can try weird things such as:
|
||||
@example
|
||||
qemu /usr/local/qemu-i386/bin/qemu-i386 /usr/local/qemu-i386/bin/ls-i386
|
||||
@end example
|
||||
|
||||
@end itemize
|
||||
|
||||
@section Wine launch
|
||||
|
||||
@itemize
|
||||
|
||||
@item Ensure that you have a working QEMU with the x86 glibc
|
||||
distribution (see previous section). In order to verify it, you must be
|
||||
able to do:
|
||||
|
||||
@example
|
||||
qemu /usr/local/qemu-i386/bin/ls-i386
|
||||
@end example
|
||||
|
||||
@item Download the binary x86 Wine install
|
||||
(@file{qemu-XXX-i386-wine.tar.gz} on the QEMU web page).
|
||||
|
||||
@item Configure Wine on your account. Look at the provided script
|
||||
@file{/usr/local/qemu-i386/bin/wine-conf.sh}. Your previous
|
||||
@code{$@{HOME@}/.wine} directory is saved to @code{$@{HOME@}/.wine.org}.
|
||||
|
||||
@item Then you can try the example @file{putty.exe}:
|
||||
|
||||
@example
|
||||
qemu /usr/local/qemu-i386/wine/bin/wine /usr/local/qemu-i386/wine/c/Program\ Files/putty.exe
|
||||
@end example
|
||||
|
||||
@end itemize
|
||||
|
||||
@section Command line options
|
||||
|
||||
@example
|
||||
usage: qemu [-h] [-d] [-L path] [-s size] program [arguments...]
|
||||
@end example
|
||||
|
||||
@table @option
|
||||
@item -h
|
||||
Print the help
|
||||
@item -L path
|
||||
Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386)
|
||||
@item -s size
|
||||
Set the x86 stack size in bytes (default=524288)
|
||||
@end table
|
||||
|
||||
Debug options:
|
||||
|
||||
@table @option
|
||||
@item -d
|
||||
Activate log (logfile=/tmp/qemu.log)
|
||||
@item -p pagesize
|
||||
Act as if the host page size was 'pagesize' bytes
|
||||
@end table
|
||||
|
||||
@chapter QEMU Internals
|
||||
|
||||
@section QEMU compared to other emulators
|
||||
|
||||
Unlike bochs [3], QEMU emulates only a user space x86 CPU. It means that
|
||||
you cannot launch an operating system with it. The benefit is that it is
|
||||
simpler and faster due to the fact that some of the low level CPU state
|
||||
can be ignored (in particular, no virtual memory needs to be emulated).
|
||||
|
||||
Like Valgrind [2], QEMU does user space emulation and dynamic
|
||||
translation. Valgrind is mainly a memory debugger while QEMU has no
|
||||
support for it (QEMU could be used to detect out of bound memory accesses
|
||||
as Valgrind, but it has no support to track uninitialised data as
|
||||
Valgrind does). Valgrind dynamic translator generates better code than
|
||||
QEMU (in particular it does register allocation) but it is closely tied
|
||||
to an x86 host.
|
||||
|
||||
EM86 [4] is the closest project to QEMU (and QEMU still uses some of its
|
||||
code, in particular the ELF file loader). EM86 was limited to an alpha
|
||||
host and used a proprietary and slow interpreter (the interpreter part
|
||||
of the FX!32 Digital Win32 code translator [5]).
|
||||
|
||||
TWIN [6] is a Windows API emulator like Wine. It is less accurate than
|
||||
Wine but includes a protected mode x86 interpreter to launch x86 Windows
|
||||
executables. Such an approach as greater potential because most of the
|
||||
Windows API is executed natively but it is far more difficult to develop
|
||||
because all the data structures and function parameters exchanged
|
||||
between the API and the x86 code must be converted.
|
||||
|
||||
@section Portable dynamic translation
|
||||
|
||||
QEMU is a dynamic translator. When it first encounters a piece of code,
|
||||
it converts it to the host instruction set. Usually dynamic translators
|
||||
are very complicated and highly CPU dependant. QEMU uses some tricks
|
||||
which make it relatively easily portable and simple while achieving good
|
||||
performances.
|
||||
|
||||
The basic idea is to split every x86 instruction into fewer simpler
|
||||
instructions. Each simple instruction is implemented by a piece of C
|
||||
code (see @file{op-i386.c}). Then a compile time tool (@file{dyngen})
|
||||
takes the corresponding object file (@file{op-i386.o}) to generate a
|
||||
dynamic code generator which concatenates the simple instructions to
|
||||
build a function (see @file{op-i386.h:dyngen_code()}).
|
||||
|
||||
In essence, the process is similar to [1], but more work is done at
|
||||
compile time.
|
||||
|
||||
A key idea to get optimal performances is that constant parameters can
|
||||
be passed to the simple operations. For that purpose, dummy ELF
|
||||
relocations are generated with gcc for each constant parameter. Then,
|
||||
the tool (@file{dyngen}) can locate the relocations and generate the
|
||||
appriopriate C code to resolve them when building the dynamic code.
|
||||
|
||||
That way, QEMU is no more difficult to port than a dynamic linker.
|
||||
|
||||
To go even faster, GCC static register variables are used to keep the
|
||||
state of the virtual CPU.
|
||||
|
||||
@section Register allocation
|
||||
|
||||
Since QEMU uses fixed simple instructions, no efficient register
|
||||
allocation can be done. However, because RISC CPUs have a lot of
|
||||
register, most of the virtual CPU state can be put in registers without
|
||||
doing complicated register allocation.
|
||||
|
||||
@section Condition code optimisations
|
||||
|
||||
Good CPU condition codes emulation (@code{EFLAGS} register on x86) is a
|
||||
critical point to get good performances. QEMU uses lazy condition code
|
||||
evaluation: instead of computing the condition codes after each x86
|
||||
instruction, it just stores one operand (called @code{CC_SRC}), the
|
||||
result (called @code{CC_DST}) and the type of operation (called
|
||||
@code{CC_OP}).
|
||||
|
||||
@code{CC_OP} is almost never explicitely set in the generated code
|
||||
because it is known at translation time.
|
||||
|
||||
In order to increase performances, a backward pass is performed on the
|
||||
generated simple instructions (see
|
||||
@code{translate-i386.c:optimize_flags()}). When it can be proved that
|
||||
the condition codes are not needed by the next instructions, no
|
||||
condition codes are computed at all.
|
||||
|
||||
@section CPU state optimisations
|
||||
|
||||
The x86 CPU has many internal states which change the way it evaluates
|
||||
instructions. In order to achieve a good speed, the translation phase
|
||||
considers that some state information of the virtual x86 CPU cannot
|
||||
change in it. For example, if the SS, DS and ES segments have a zero
|
||||
base, then the translator does not even generate an addition for the
|
||||
segment base.
|
||||
|
||||
[The FPU stack pointer register is not handled that way yet].
|
||||
|
||||
@section Translation cache
|
||||
|
||||
A 2MByte cache holds the most recently used translations. For
|
||||
simplicity, it is completely flushed when it is full. A translation unit
|
||||
contains just a single basic block (a block of x86 instructions
|
||||
terminated by a jump or by a virtual CPU state change which the
|
||||
translator cannot deduce statically).
|
||||
|
||||
@section Direct block chaining
|
||||
|
||||
After each translated basic block is executed, QEMU uses the simulated
|
||||
Program Counter (PC) and other cpu state informations (such as the CS
|
||||
segment base value) to find the next basic block.
|
||||
|
||||
In order to accelerate the most common cases where the new simulated PC
|
||||
is known, QEMU can patch a basic block so that it jumps directly to the
|
||||
next one.
|
||||
|
||||
The most portable code uses an indirect jump. An indirect jump makes it
|
||||
easier to make the jump target modification atomic. On some
|
||||
architectures (such as PowerPC), the @code{JUMP} opcode is directly
|
||||
patched so that the block chaining has no overhead.
|
||||
|
||||
@section Self-modifying code and translated code invalidation
|
||||
|
||||
Self-modifying code is a special challenge in x86 emulation because no
|
||||
instruction cache invalidation is signaled by the application when code
|
||||
is modified.
|
||||
|
||||
When translated code is generated for a basic block, the corresponding
|
||||
host page is write protected if it is not already read-only (with the
|
||||
system call @code{mprotect()}). Then, if a write access is done to the
|
||||
page, Linux raises a SEGV signal. QEMU then invalidates all the
|
||||
translated code in the page and enables write accesses to the page.
|
||||
|
||||
Correct translated code invalidation is done efficiently by maintaining
|
||||
a linked list of every translated block contained in a given page. Other
|
||||
linked lists are also maintained to undo direct block chaining.
|
||||
|
||||
Althought the overhead of doing @code{mprotect()} calls is important,
|
||||
most MSDOS programs can be emulated at reasonnable speed with QEMU and
|
||||
DOSEMU.
|
||||
|
||||
Note that QEMU also invalidates pages of translated code when it detects
|
||||
that memory mappings are modified with @code{mmap()} or @code{munmap()}.
|
||||
|
||||
@section Exception support
|
||||
|
||||
longjmp() is used when an exception such as division by zero is
|
||||
encountered.
|
||||
|
||||
The host SIGSEGV and SIGBUS signal handlers are used to get invalid
|
||||
memory accesses. The exact CPU state can be retrieved because all the
|
||||
x86 registers are stored in fixed host registers. The simulated program
|
||||
counter is found by retranslating the corresponding basic block and by
|
||||
looking where the host program counter was at the exception point.
|
||||
|
||||
The virtual CPU cannot retrieve the exact @code{EFLAGS} register because
|
||||
in some cases it is not computed because of condition code
|
||||
optimisations. It is not a big concern because the emulated code can
|
||||
still be restarted in any cases.
|
||||
|
||||
@section Linux system call translation
|
||||
|
||||
QEMU includes a generic system call translator for Linux. It means that
|
||||
the parameters of the system calls can be converted to fix the
|
||||
endianness and 32/64 bit issues. The IOCTLs are converted with a generic
|
||||
type description system (see @file{ioctls.h} and @file{thunk.c}).
|
||||
|
||||
QEMU supports host CPUs which have pages bigger than 4KB. It records all
|
||||
the mappings the process does and try to emulated the @code{mmap()}
|
||||
system calls in cases where the host @code{mmap()} call would fail
|
||||
because of bad page alignment.
|
||||
|
||||
@section Linux signals
|
||||
|
||||
Normal and real-time signals are queued along with their information
|
||||
(@code{siginfo_t}) as it is done in the Linux kernel. Then an interrupt
|
||||
request is done to the virtual CPU. When it is interrupted, one queued
|
||||
signal is handled by generating a stack frame in the virtual CPU as the
|
||||
Linux kernel does. The @code{sigreturn()} system call is emulated to return
|
||||
from the virtual signal handler.
|
||||
|
||||
Some signals (such as SIGALRM) directly come from the host. Other
|
||||
signals are synthetized from the virtual CPU exceptions such as SIGFPE
|
||||
when a division by zero is done (see @code{main.c:cpu_loop()}).
|
||||
|
||||
The blocked signal mask is still handled by the host Linux kernel so
|
||||
that most signal system calls can be redirected directly to the host
|
||||
Linux kernel. Only the @code{sigaction()} and @code{sigreturn()} system
|
||||
calls need to be fully emulated (see @file{signal.c}).
|
||||
|
||||
@section clone() system call and threads
|
||||
|
||||
The Linux clone() system call is usually used to create a thread. QEMU
|
||||
uses the host clone() system call so that real host threads are created
|
||||
for each emulated thread. One virtual CPU instance is created for each
|
||||
thread.
|
||||
|
||||
The virtual x86 CPU atomic operations are emulated with a global lock so
|
||||
that their semantic is preserved.
|
||||
|
||||
Note that currently there are still some locking issues in QEMU. In
|
||||
particular, the translated cache flush is not protected yet against
|
||||
reentrancy.
|
||||
|
||||
@section Self-virtualization
|
||||
|
||||
QEMU was conceived so that ultimately it can emulate itself. Althought
|
||||
it is not very useful, it is an important test to show the power of the
|
||||
emulator.
|
||||
|
||||
Achieving self-virtualization is not easy because there may be address
|
||||
space conflicts. QEMU solves this problem by being an executable ELF
|
||||
shared object as the ld-linux.so ELF interpreter. That way, it can be
|
||||
relocated at load time.
|
||||
|
||||
@section Bibliography
|
||||
|
||||
@table @asis
|
||||
|
||||
@item [1]
|
||||
@url{http://citeseer.nj.nec.com/piumarta98optimizing.html}, Optimizing
|
||||
direct threaded code by selective inlining (1998) by Ian Piumarta, Fabio
|
||||
Riccardi.
|
||||
|
||||
@item [2]
|
||||
@url{http://developer.kde.org/~sewardj/}, Valgrind, an open-source
|
||||
memory debugger for x86-GNU/Linux, by Julian Seward.
|
||||
|
||||
@item [3]
|
||||
@url{http://bochs.sourceforge.net/}, the Bochs IA-32 Emulator Project,
|
||||
by Kevin Lawton et al.
|
||||
|
||||
@item [4]
|
||||
@url{http://www.cs.rose-hulman.edu/~donaldlf/em86/index.html}, the EM86
|
||||
x86 emulator on Alpha-Linux.
|
||||
|
||||
@item [5]
|
||||
@url{http://www.usenix.org/publications/library/proceedings/usenix-nt97/full_papers/chernoff/chernoff.pdf},
|
||||
DIGITAL FX!32: Running 32-Bit x86 Applications on Alpha NT, by Anton
|
||||
Chernoff and Ray Hookway.
|
||||
|
||||
@item [6]
|
||||
@url{http://www.willows.com/}, Windows API library emulation from
|
||||
Willows Software.
|
||||
|
||||
@end table
|
||||
|
||||
@chapter Regression Tests
|
||||
|
||||
In the directory @file{tests/}, various interesting x86 testing programs
|
||||
are available. There are used for regression testing.
|
||||
|
||||
@section @file{hello}
|
||||
|
||||
Very simple statically linked x86 program, just to test QEMU during a
|
||||
port to a new host CPU.
|
||||
|
||||
@section @file{test-i386}
|
||||
|
||||
This program executes most of the 16 bit and 32 bit x86 instructions and
|
||||
generates a text output. It can be compared with the output obtained with
|
||||
a real CPU or another emulator. The target @code{make test} runs this
|
||||
program and a @code{diff} on the generated output.
|
||||
|
||||
The Linux system call @code{modify_ldt()} is used to create x86 selectors
|
||||
to test some 16 bit addressing and 32 bit with segmentation cases.
|
||||
|
||||
The Linux system call @code{vm86()} is used to test vm86 emulation.
|
||||
|
||||
Various exceptions are raised to test most of the x86 user space
|
||||
exception reporting.
|
||||
|
||||
@section @file{sha1}
|
||||
|
||||
It is a simple benchmark. Care must be taken to interpret the results
|
||||
because it mostly tests the ability of the virtual CPU to optimize the
|
||||
@code{rol} x86 instruction and the condition code computations.
|
||||
|
204
s390.ld
Normal file
204
s390.ld
Normal file
@@ -0,0 +1,204 @@
|
||||
OUTPUT_FORMAT("elf32-s390", "elf32-s390",
|
||||
"elf32-s390")
|
||||
OUTPUT_ARCH(s390:31-bit)
|
||||
ENTRY(_start)
|
||||
SEARCH_DIR("/usr/s390-redhat-linux/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib");
|
||||
/* Do we need any of these for elf?
|
||||
__DYNAMIC = 0; */
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
. = 0x60000000 + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.rel.dyn :
|
||||
{
|
||||
*(.rel.init)
|
||||
*(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
|
||||
*(.rel.fini)
|
||||
*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
|
||||
*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
|
||||
*(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
|
||||
*(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
|
||||
*(.rel.ctors)
|
||||
*(.rel.dtors)
|
||||
*(.rel.got)
|
||||
*(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
|
||||
*(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
|
||||
*(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
|
||||
*(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
|
||||
*(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
|
||||
}
|
||||
.rela.dyn :
|
||||
{
|
||||
*(.rela.init)
|
||||
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
|
||||
*(.rela.fini)
|
||||
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
|
||||
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
|
||||
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
|
||||
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
|
||||
*(.rela.ctors)
|
||||
*(.rela.dtors)
|
||||
*(.rela.got)
|
||||
*(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
|
||||
*(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
|
||||
*(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
|
||||
*(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
|
||||
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
|
||||
}
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init :
|
||||
{
|
||||
KEEP (*(.init))
|
||||
} =0x07070707
|
||||
.plt : { *(.plt) }
|
||||
.text :
|
||||
{
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
} =0x07070707
|
||||
.fini :
|
||||
{
|
||||
KEEP (*(.fini))
|
||||
} =0x07070707
|
||||
PROVIDE (__etext = .);
|
||||
PROVIDE (_etext = .);
|
||||
PROVIDE (etext = .);
|
||||
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
|
||||
.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
|
||||
.eh_frame_hdr : { *(.eh_frame_hdr) }
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN(0x1000) + (. & (0x1000 - 1));
|
||||
/* Ensure the __preinit_array_start label is properly aligned. We
|
||||
could instead move the label definition inside the section, but
|
||||
the linker would then create the section even if it turns out to
|
||||
be empty, which isn't pretty. */
|
||||
. = ALIGN(32 / 8);
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
.preinit_array : { *(.preinit_array) }
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
PROVIDE (__init_array_start = .);
|
||||
.init_array : { *(.init_array) }
|
||||
PROVIDE (__init_array_end = .);
|
||||
PROVIDE (__fini_array_start = .);
|
||||
.fini_array : { *(.fini_array) }
|
||||
PROVIDE (__fini_array_end = .);
|
||||
.data :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
|
||||
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
|
||||
.eh_frame : { KEEP (*(.eh_frame)) }
|
||||
.gcc_except_table : { *(.gcc_except_table) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
.ctors :
|
||||
{
|
||||
/* gcc uses crtbegin.o to find the start of
|
||||
the constructors, so we make sure it is
|
||||
first. Because this is a wildcard, it
|
||||
doesn't matter if the user does not
|
||||
actually link against crtbegin.o; the
|
||||
linker won't look for a file to match a
|
||||
wildcard. The wildcard also means that it
|
||||
doesn't matter which directory crtbegin.o
|
||||
is in. */
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
/* We don't want to include the .ctor section from
|
||||
from the crtend.o file until after the sorted ctors.
|
||||
The .ctor section from the crtend file contains the
|
||||
end of ctors marker and it must be last */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
}
|
||||
.jcr : { KEEP (*(.jcr)) }
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata :
|
||||
{
|
||||
*(.sdata .sdata.* .gnu.linkonce.s.*)
|
||||
}
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
__bss_start = .;
|
||||
.sbss :
|
||||
{
|
||||
PROVIDE (__sbss_start = .);
|
||||
PROVIDE (___sbss_start = .);
|
||||
*(.dynsbss)
|
||||
*(.sbss .sbss.* .gnu.linkonce.sb.*)
|
||||
*(.scommon)
|
||||
PROVIDE (__sbss_end = .);
|
||||
PROVIDE (___sbss_end = .);
|
||||
}
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
/* Align here to ensure that the .bss section occupies space up to
|
||||
_end. Align after .bss to ensure correct alignment even if the
|
||||
.bss section disappears because there are no input sections. */
|
||||
. = ALIGN(32 / 8);
|
||||
}
|
||||
. = ALIGN(32 / 8);
|
||||
_end = .;
|
||||
PROVIDE (end = .);
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
}
|
||||
|
306
syscall-i386.h
306
syscall-i386.h
@@ -237,6 +237,36 @@
|
||||
#define TARGET_NR_removexattr 235
|
||||
#define TARGET_NR_lremovexattr 236
|
||||
#define TARGET_NR_fremovexattr 237
|
||||
#define TARGET_NR_tkill 238
|
||||
#define TARGET_NR_sendfile64 239
|
||||
#define TARGET_NR_futex 240
|
||||
#define TARGET_NR_sched_setaffinity 241
|
||||
#define TARGET_NR_sched_getaffinity 242
|
||||
#define TARGET_NR_set_thread_area 243
|
||||
#define TARGET_NR_get_thread_area 244
|
||||
#define TARGET_NR_io_setup 245
|
||||
#define TARGET_NR_io_destroy 246
|
||||
#define TARGET_NR_io_getevents 247
|
||||
#define TARGET_NR_io_submit 248
|
||||
#define TARGET_NR_io_cancel 249
|
||||
#define TARGET_NR_fadvise64 250
|
||||
|
||||
#define TARGET_NR_exit_group 252
|
||||
#define TARGET_NR_lookup_dcookie 253
|
||||
#define TARGET_NR_epoll_create 254
|
||||
#define TARGET_NR_epoll_ctl 255
|
||||
#define TARGET_NR_epoll_wait 256
|
||||
#define TARGET_NR_remap_file_pages 257
|
||||
#define TARGET_NR_set_tid_address 258
|
||||
#define TARGET_NR_timer_create 259
|
||||
#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1)
|
||||
#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2)
|
||||
#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3)
|
||||
#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4)
|
||||
#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5)
|
||||
#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6)
|
||||
#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7)
|
||||
#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8)
|
||||
|
||||
#define TARGET_SIG_BLOCK 0 /* for blocking signals */
|
||||
#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */
|
||||
@@ -255,11 +285,11 @@ struct target_stat {
|
||||
target_ulong st_size;
|
||||
target_ulong st_blksize;
|
||||
target_ulong st_blocks;
|
||||
target_ulong st_atime;
|
||||
target_ulong target_st_atime;
|
||||
target_ulong __unused1;
|
||||
target_ulong st_mtime;
|
||||
target_ulong target_st_mtime;
|
||||
target_ulong __unused2;
|
||||
target_ulong st_ctime;
|
||||
target_ulong target_st_ctime;
|
||||
target_ulong __unused3;
|
||||
target_ulong __unused4;
|
||||
target_ulong __unused5;
|
||||
@@ -272,7 +302,7 @@ struct target_stat64 {
|
||||
unsigned short st_dev;
|
||||
unsigned char __pad0[10];
|
||||
|
||||
#define STAT64_HAS_BROKEN_ST_INO 1
|
||||
#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
|
||||
target_ulong __st_ino;
|
||||
|
||||
unsigned int st_mode;
|
||||
@@ -290,17 +320,17 @@ struct target_stat64 {
|
||||
target_ulong st_blocks; /* Number 512-byte blocks allocated. */
|
||||
target_ulong __pad4; /* future possible st_blocks high bits */
|
||||
|
||||
target_ulong st_atime;
|
||||
target_ulong target_st_atime;
|
||||
target_ulong __pad5;
|
||||
|
||||
target_ulong st_mtime;
|
||||
target_ulong target_st_mtime;
|
||||
target_ulong __pad6;
|
||||
|
||||
target_ulong st_ctime;
|
||||
target_ulong target_st_ctime;
|
||||
target_ulong __pad7; /* will be high 32 bits of ctime someday */
|
||||
|
||||
unsigned long long st_ino;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
#define TARGET_SA_NOCLDSTOP 0x00000001
|
||||
#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */
|
||||
@@ -397,8 +427,8 @@ typedef struct target_siginfo {
|
||||
pid_t _pid; /* which child */
|
||||
uid_t _uid; /* sender's uid */
|
||||
int _status; /* exit code */
|
||||
clock_t _utime;
|
||||
clock_t _stime;
|
||||
target_clock_t _utime;
|
||||
target_clock_t _stime;
|
||||
} _sigchld;
|
||||
|
||||
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
|
||||
@@ -414,6 +444,18 @@ typedef struct target_siginfo {
|
||||
} _sifields;
|
||||
} target_siginfo_t;
|
||||
|
||||
/*
|
||||
* si_code values
|
||||
* Digital reserves positive values for kernel-generated signals.
|
||||
*/
|
||||
#define TARGET_SI_USER 0 /* sent by kill, sigsend, raise */
|
||||
#define TARGET_SI_KERNEL 0x80 /* sent by the kernel from somewhere */
|
||||
#define TARGET_SI_QUEUE -1 /* sent by sigqueue */
|
||||
#define TARGET_SI_TIMER -2 /* sent by timer expiration */
|
||||
#define TARGET_SI_MESGQ -3 /* sent by real time mesq state change */
|
||||
#define TARGET_SI_ASYNCIO -4 /* sent by AIO completion */
|
||||
#define TARGET_SI_SIGIO -5 /* sent by queued SIGIO */
|
||||
|
||||
/*
|
||||
* SIGILL si_codes
|
||||
*/
|
||||
@@ -432,6 +474,18 @@ typedef struct target_siginfo {
|
||||
#define TARGET_FPE_FLTSUB (8) /* subscript out of range */
|
||||
#define TARGET_NSIGFPE 8
|
||||
|
||||
/*
|
||||
* SIGSEGV si_codes
|
||||
*/
|
||||
#define TARGET_SEGV_MAPERR (1) /* address not mapped to object */
|
||||
#define TARGET_SEGV_ACCERR (2) /* invalid permissions for mapped object */
|
||||
|
||||
/*
|
||||
* SIGTRAP si_codes
|
||||
*/
|
||||
#define TARGET_TRAP_BRKPT (1) /* process breakpoint */
|
||||
#define TARGET_TRAP_TRACE (2) /* process trace trap */
|
||||
|
||||
/* default linux values for the selectors */
|
||||
#define __USER_CS (0x23)
|
||||
#define __USER_DS (0x2B)
|
||||
@@ -542,8 +596,8 @@ struct target_pt_regs {
|
||||
#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
|
||||
#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
|
||||
#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
|
||||
#define TARGET_TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
|
||||
#define TARGET_TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
|
||||
#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
|
||||
#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
|
||||
|
||||
#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */
|
||||
#define TARGET_FIOCLEX 0x5451
|
||||
@@ -714,6 +768,10 @@ struct target_termios {
|
||||
#define TARGET_LDT_ENTRIES 8192
|
||||
#define TARGET_LDT_ENTRY_SIZE 8
|
||||
|
||||
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
|
||||
#define TARGET_GDT_ENTRY_TLS_MIN 6
|
||||
#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
|
||||
|
||||
struct target_modify_ldt_ldt_s {
|
||||
unsigned int entry_number;
|
||||
target_ulong base_addr;
|
||||
@@ -721,6 +779,228 @@ struct target_modify_ldt_ldt_s {
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
|
||||
/* vm86 defines */
|
||||
|
||||
#define TARGET_BIOSSEG 0x0f000
|
||||
|
||||
#define TARGET_CPU_086 0
|
||||
#define TARGET_CPU_186 1
|
||||
#define TARGET_CPU_286 2
|
||||
#define TARGET_CPU_386 3
|
||||
#define TARGET_CPU_486 4
|
||||
#define TARGET_CPU_586 5
|
||||
|
||||
#define TARGET_VM86_SIGNAL 0 /* return due to signal */
|
||||
#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
|
||||
#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */
|
||||
#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */
|
||||
|
||||
/*
|
||||
* Additional return values when invoking new vm86()
|
||||
*/
|
||||
#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */
|
||||
#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */
|
||||
|
||||
/*
|
||||
* function codes when invoking new vm86()
|
||||
*/
|
||||
#define TARGET_VM86_PLUS_INSTALL_CHECK 0
|
||||
#define TARGET_VM86_ENTER 1
|
||||
#define TARGET_VM86_ENTER_NO_BYPASS 2
|
||||
#define TARGET_VM86_REQUEST_IRQ 3
|
||||
#define TARGET_VM86_FREE_IRQ 4
|
||||
#define TARGET_VM86_GET_IRQ_BITS 5
|
||||
#define TARGET_VM86_GET_AND_RESET_IRQ 6
|
||||
|
||||
/*
|
||||
* This is the stack-layout seen by the user space program when we have
|
||||
* done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout
|
||||
* is 'kernel_vm86_regs' (see below).
|
||||
*/
|
||||
|
||||
struct target_vm86_regs {
|
||||
/*
|
||||
* normal regs, with special meaning for the segment descriptors..
|
||||
*/
|
||||
target_long ebx;
|
||||
target_long ecx;
|
||||
target_long edx;
|
||||
target_long esi;
|
||||
target_long edi;
|
||||
target_long ebp;
|
||||
target_long eax;
|
||||
target_long __null_ds;
|
||||
target_long __null_es;
|
||||
target_long __null_fs;
|
||||
target_long __null_gs;
|
||||
target_long orig_eax;
|
||||
target_long eip;
|
||||
unsigned short cs, __csh;
|
||||
target_long eflags;
|
||||
target_long esp;
|
||||
unsigned short ss, __ssh;
|
||||
/*
|
||||
* these are specific to v86 mode:
|
||||
*/
|
||||
unsigned short es, __esh;
|
||||
unsigned short ds, __dsh;
|
||||
unsigned short fs, __fsh;
|
||||
unsigned short gs, __gsh;
|
||||
};
|
||||
|
||||
struct target_revectored_struct {
|
||||
target_ulong __map[8]; /* 256 bits */
|
||||
};
|
||||
|
||||
struct target_vm86_struct {
|
||||
struct target_vm86_regs regs;
|
||||
target_ulong flags;
|
||||
target_ulong screen_bitmap;
|
||||
target_ulong cpu_type;
|
||||
struct target_revectored_struct int_revectored;
|
||||
struct target_revectored_struct int21_revectored;
|
||||
};
|
||||
|
||||
/*
|
||||
* flags masks
|
||||
*/
|
||||
#define TARGET_VM86_SCREEN_BITMAP 0x0001
|
||||
|
||||
struct target_vm86plus_info_struct {
|
||||
target_ulong flags;
|
||||
#define TARGET_force_return_for_pic (1 << 0)
|
||||
#define TARGET_vm86dbg_active (1 << 1) /* for debugger */
|
||||
#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */
|
||||
#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */
|
||||
unsigned char vm86dbg_intxxtab[32]; /* for debugger */
|
||||
};
|
||||
|
||||
struct target_vm86plus_struct {
|
||||
struct target_vm86_regs regs;
|
||||
target_ulong flags;
|
||||
target_ulong screen_bitmap;
|
||||
target_ulong cpu_type;
|
||||
struct target_revectored_struct int_revectored;
|
||||
struct target_revectored_struct int21_revectored;
|
||||
struct target_vm86plus_info_struct vm86plus;
|
||||
};
|
||||
|
||||
/* ipcs */
|
||||
|
||||
#define TARGET_SEMOP 1
|
||||
#define TARGET_SEMGET 2
|
||||
#define TARGET_SEMCTL 3
|
||||
#define TARGET_MSGSND 11
|
||||
#define TARGET_MSGRCV 12
|
||||
#define TARGET_MSGGET 13
|
||||
#define TARGET_MSGCTL 14
|
||||
#define TARGET_SHMAT 21
|
||||
#define TARGET_SHMDT 22
|
||||
#define TARGET_SHMGET 23
|
||||
#define TARGET_SHMCTL 24
|
||||
|
||||
struct target_msgbuf {
|
||||
int mtype;
|
||||
char mtext[1];
|
||||
};
|
||||
|
||||
struct target_ipc_kludge {
|
||||
unsigned int msgp; /* Really (struct msgbuf *) */
|
||||
int msgtyp;
|
||||
};
|
||||
|
||||
struct alpha_msgbuf {
|
||||
long mtype;
|
||||
char mtext[4096];
|
||||
};
|
||||
|
||||
struct target_ipc_perm {
|
||||
int key;
|
||||
unsigned short uid;
|
||||
unsigned short gid;
|
||||
unsigned short cuid;
|
||||
unsigned short cgid;
|
||||
unsigned short mode;
|
||||
unsigned short seq;
|
||||
};
|
||||
|
||||
struct target_msqid_ds {
|
||||
struct target_ipc_perm msg_perm;
|
||||
unsigned int msg_first; /* really struct target_msg* */
|
||||
unsigned int msg_last; /* really struct target_msg* */
|
||||
unsigned int msg_stime; /* really target_time_t */
|
||||
unsigned int msg_rtime; /* really target_time_t */
|
||||
unsigned int msg_ctime; /* really target_time_t */
|
||||
unsigned int wwait; /* really struct wait_queue* */
|
||||
unsigned int rwait; /* really struct wait_queue* */
|
||||
unsigned short msg_cbytes;
|
||||
unsigned short msg_qnum;
|
||||
unsigned short msg_qbytes;
|
||||
unsigned short msg_lspid;
|
||||
unsigned short msg_lrpid;
|
||||
};
|
||||
|
||||
struct target_shmid_ds {
|
||||
struct target_ipc_perm shm_perm;
|
||||
int shm_segsz;
|
||||
unsigned int shm_atime; /* really target_time_t */
|
||||
unsigned int shm_dtime; /* really target_time_t */
|
||||
unsigned int shm_ctime; /* really target_time_t */
|
||||
unsigned short shm_cpid;
|
||||
unsigned short shm_lpid;
|
||||
short shm_nattch;
|
||||
unsigned short shm_npages;
|
||||
unsigned long *shm_pages;
|
||||
void *attaches; /* really struct shm_desc * */
|
||||
};
|
||||
|
||||
#define TARGET_IPC_RMID 0
|
||||
#define TARGET_IPC_SET 1
|
||||
#define TARGET_IPC_STAT 2
|
||||
|
||||
union target_semun {
|
||||
int val;
|
||||
unsigned int buf; /* really struct semid_ds * */
|
||||
unsigned int array; /* really unsigned short * */
|
||||
unsigned int __buf; /* really struct seminfo * */
|
||||
unsigned int __pad; /* really void* */
|
||||
};
|
||||
|
||||
#define TARGET_F_DUPFD 0 /* dup */
|
||||
#define TARGET_F_GETFD 1 /* get close_on_exec */
|
||||
#define TARGET_F_SETFD 2 /* set/clear close_on_exec */
|
||||
#define TARGET_F_GETFL 3 /* get file->f_flags */
|
||||
#define TARGET_F_SETFL 4 /* set file->f_flags */
|
||||
#define TARGET_F_GETLK 5
|
||||
#define TARGET_F_SETLK 6
|
||||
#define TARGET_F_SETLKW 7
|
||||
|
||||
#define TARGET_F_SETOWN 8 /* for sockets. */
|
||||
#define TARGET_F_GETOWN 9 /* for sockets. */
|
||||
#define TARGET_F_SETSIG 10 /* for sockets. */
|
||||
#define TARGET_F_GETSIG 11 /* for sockets. */
|
||||
|
||||
#define TARGET_F_GETLK64 12 /* using 'struct flock64' */
|
||||
#define TARGET_F_SETLK64 13
|
||||
#define TARGET_F_SETLKW64 14
|
||||
|
||||
struct target_flock {
|
||||
short l_type;
|
||||
short l_whence;
|
||||
target_ulong l_start;
|
||||
target_ulong l_len;
|
||||
int l_pid;
|
||||
};
|
||||
|
||||
struct target_flock64 {
|
||||
short l_type;
|
||||
short l_whence;
|
||||
unsigned long long l_start;
|
||||
unsigned long long l_len;
|
||||
int l_pid;
|
||||
};
|
||||
|
||||
/* soundcard defines (XXX: move them to generic file syscall_defs.h) */
|
||||
|
||||
#define TARGET_SNDCTL_COPR_HALT 0xc0144307
|
||||
@@ -850,3 +1130,5 @@ struct target_modify_ldt_ldt_s {
|
||||
|
||||
#define TARGET_VFAT_IOCTL_READDIR_BOTH 0x82187201
|
||||
#define TARGET_VFAT_IOCTL_READDIR_SHORT 0x82187202
|
||||
|
||||
#define TARGET_SIOCATMARK 0x8905
|
||||
|
22
tests/.cvsignore
Normal file
22
tests/.cvsignore
Normal file
@@ -0,0 +1,22 @@
|
||||
gmon.out
|
||||
testsig
|
||||
hello
|
||||
sha1.test.c
|
||||
sha1.c
|
||||
test-i386
|
||||
sha1
|
||||
testclone
|
||||
.gdb_history
|
||||
testthread
|
||||
test-i386.s
|
||||
test-i386.ref
|
||||
sha1-i386
|
||||
runcom
|
||||
debug.com
|
||||
test-i386.out
|
||||
speed.txt
|
||||
test-i386.ref.P3
|
||||
pi_10.com
|
||||
test-i386.ref.P4
|
||||
ldso.c
|
||||
test_path
|
@@ -4,9 +4,9 @@ CFLAGS=-Wall -O2 -g
|
||||
LDFLAGS=
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
TESTS=testclone testsig testthread sha1-i386 test-i386
|
||||
TESTS=testclone testsig testthread sha1-i386 test-i386 runcom
|
||||
endif
|
||||
TESTS+=sha1
|
||||
TESTS+=sha1 test_path
|
||||
|
||||
QEMU=../qemu
|
||||
|
||||
@@ -25,10 +25,15 @@ testsig: testsig.c
|
||||
testthread: testthread.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
|
||||
|
||||
test_path: test_path.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
./$@ || { rm $@; exit 1; }
|
||||
|
||||
# i386 emulation test (test various opcodes) */
|
||||
test-i386: test-i386.c test-i386-code16.S \
|
||||
test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \
|
||||
test-i386.h test-i386-shift.h test-i386-muldiv.h
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ test-i386.c test-i386-code16.S -lm
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ test-i386.c \
|
||||
test-i386-code16.S test-i386-vm86.S -lm
|
||||
|
||||
test: test-i386
|
||||
ifeq ($(ARCH),i386)
|
||||
@@ -48,5 +53,9 @@ speed: sha1 sha1-i386
|
||||
time ./sha1
|
||||
time $(QEMU) ./sha1-i386
|
||||
|
||||
# vm86 test
|
||||
runcom: runcom.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *~ *.o $(TESTS)
|
||||
|
@@ -19,7 +19,7 @@ extern inline int write(int fd, const char * buf, int len)
|
||||
: "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len)));
|
||||
}
|
||||
|
||||
void _startup(void)
|
||||
void _start(void)
|
||||
{
|
||||
write(1, "Hello World\n", 12);
|
||||
exit(0);
|
||||
|
195
tests/runcom.c
Normal file
195
tests/runcom.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Simple example of use of vm86: launch a basic .com DOS executable
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <linux/unistd.h>
|
||||
#include <asm/vm86.h>
|
||||
|
||||
//#define SIGTEST
|
||||
|
||||
#undef __syscall_return
|
||||
#define __syscall_return(type, res) \
|
||||
do { \
|
||||
return (type) (res); \
|
||||
} while (0)
|
||||
|
||||
_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86)
|
||||
|
||||
#define COM_BASE_ADDR 0x10100
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n"
|
||||
"usage: runcom file.com\n"
|
||||
"VM86 Run simple .com DOS executables (linux vm86 test mode)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static inline void set_bit(uint8_t *a, unsigned int bit)
|
||||
{
|
||||
a[bit / 8] |= (1 << (bit % 8));
|
||||
}
|
||||
|
||||
static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
|
||||
{
|
||||
return (uint8_t *)((seg << 4) + (reg & 0xffff));
|
||||
}
|
||||
|
||||
static inline void pushw(struct vm86_regs *r, int val)
|
||||
{
|
||||
r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
|
||||
*(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
|
||||
}
|
||||
|
||||
void dump_regs(struct vm86_regs *r)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
|
||||
"ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n"
|
||||
"EIP=%08lx EFL=%08lx\n"
|
||||
"CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n",
|
||||
r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp,
|
||||
r->eip, r->eflags,
|
||||
r->cs, r->ds, r->es, r->ss, r->fs, r->gs);
|
||||
}
|
||||
|
||||
#ifdef SIGTEST
|
||||
void alarm_handler(int sig)
|
||||
{
|
||||
fprintf(stderr, "alarm signal=%d\n", sig);
|
||||
alarm(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
uint8_t *vm86_mem;
|
||||
const char *filename;
|
||||
int fd, ret, seg;
|
||||
struct vm86plus_struct ctx;
|
||||
struct vm86_regs *r;
|
||||
|
||||
if (argc != 2)
|
||||
usage();
|
||||
filename = argv[1];
|
||||
|
||||
vm86_mem = mmap((void *)0x00000000, 0x110000,
|
||||
PROT_WRITE | PROT_READ | PROT_EXEC,
|
||||
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (vm86_mem == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
exit(1);
|
||||
}
|
||||
#ifdef SIGTEST
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
act.sa_handler = alarm_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGALRM, &act, NULL);
|
||||
alarm(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* load the MSDOS .com executable */
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror(filename);
|
||||
exit(1);
|
||||
}
|
||||
ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256);
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
/* init basic registers */
|
||||
r = &ctx.regs;
|
||||
r->eip = 0x100;
|
||||
r->esp = 0xfffe;
|
||||
seg = (COM_BASE_ADDR - 0x100) >> 4;
|
||||
r->cs = seg;
|
||||
r->ss = seg;
|
||||
r->ds = seg;
|
||||
r->es = seg;
|
||||
r->fs = seg;
|
||||
r->gs = seg;
|
||||
r->eflags = VIF_MASK;
|
||||
|
||||
/* put return code */
|
||||
set_bit((uint8_t *)&ctx.int_revectored, 0x21);
|
||||
*seg_to_linear(r->cs, 0) = 0xb4; /* mov ah, $0 */
|
||||
*seg_to_linear(r->cs, 1) = 0x00;
|
||||
*seg_to_linear(r->cs, 2) = 0xcd; /* int $0x21 */
|
||||
*seg_to_linear(r->cs, 3) = 0x21;
|
||||
pushw(&ctx.regs, 0x0000);
|
||||
|
||||
/* the value of these registers seem to be assumed by pi_10.com */
|
||||
r->esi = 0x100;
|
||||
r->ecx = 0xff;
|
||||
r->ebp = 0x0900;
|
||||
r->edi = 0xfffe;
|
||||
|
||||
for(;;) {
|
||||
ret = vm86(VM86_ENTER, &ctx);
|
||||
switch(VM86_TYPE(ret)) {
|
||||
case VM86_INTx:
|
||||
{
|
||||
int int_num, ah;
|
||||
|
||||
int_num = VM86_ARG(ret);
|
||||
if (int_num != 0x21)
|
||||
goto unknown_int;
|
||||
ah = (r->eax >> 8) & 0xff;
|
||||
switch(ah) {
|
||||
case 0x00: /* exit */
|
||||
exit(0);
|
||||
case 0x02: /* write char */
|
||||
{
|
||||
uint8_t c = r->edx;
|
||||
write(1, &c, 1);
|
||||
}
|
||||
break;
|
||||
case 0x09: /* write string */
|
||||
{
|
||||
uint8_t c;
|
||||
for(;;) {
|
||||
c = *seg_to_linear(r->ds, r->edx);
|
||||
if (c == '$')
|
||||
break;
|
||||
write(1, &c, 1);
|
||||
}
|
||||
r->eax = (r->eax & ~0xff) | '$';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unknown_int:
|
||||
fprintf(stderr, "unsupported int 0x%02x\n", int_num);
|
||||
dump_regs(&ctx.regs);
|
||||
// exit(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VM86_SIGNAL:
|
||||
/* a signal came, we just ignore that */
|
||||
break;
|
||||
case VM86_STI:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret);
|
||||
dump_regs(&ctx.regs);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
@@ -108,7 +108,12 @@ void exec_opb(int s0, int s1, int iflags)
|
||||
void exec_op(int s2, int s0, int s1)
|
||||
{
|
||||
exec_opl(s2, s0, s1, 0);
|
||||
#ifdef OP_SHIFTD
|
||||
if (s1 <= 15)
|
||||
exec_opw(s2, s0, s1, 0);
|
||||
#else
|
||||
exec_opw(s2, s0, s1, 0);
|
||||
#endif
|
||||
#ifndef OP_NOBYTE
|
||||
exec_opb(s0, s1, 0);
|
||||
#endif
|
||||
|
104
tests/test-i386-vm86.S
Normal file
104
tests/test-i386-vm86.S
Normal file
@@ -0,0 +1,104 @@
|
||||
.code16
|
||||
.globl vm86_code_start
|
||||
.globl vm86_code_end
|
||||
|
||||
#define GET_OFFSET(x) ((x) - vm86_code_start + 0x100)
|
||||
|
||||
vm86_code_start:
|
||||
movw $GET_OFFSET(hello_world), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
|
||||
/* prepare int 0x90 vector */
|
||||
xorw %ax, %ax
|
||||
movw %ax, %es
|
||||
es movw $GET_OFFSET(int90_test), 0x90 * 4
|
||||
es movw %cs, 0x90 * 4 + 2
|
||||
|
||||
/* launch int 0x90 */
|
||||
|
||||
int $0x90
|
||||
|
||||
/* test IF support */
|
||||
movw $GET_OFFSET(IF_msg), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
|
||||
pushf
|
||||
popw %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
cli
|
||||
pushf
|
||||
popw %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
sti
|
||||
pushfl
|
||||
popl %edx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
#if 0
|
||||
movw $GET_OFFSET(IF_msg1), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
|
||||
pushf
|
||||
movw %sp, %bx
|
||||
andw $~0x200, (%bx)
|
||||
popf
|
||||
#else
|
||||
cli
|
||||
#endif
|
||||
|
||||
pushf
|
||||
popw %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
pushfl
|
||||
movw %sp, %bx
|
||||
orw $0x200, (%bx)
|
||||
popfl
|
||||
|
||||
pushfl
|
||||
popl %edx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
movb $0x00, %ah
|
||||
int $0x21
|
||||
|
||||
int90_test:
|
||||
pushf
|
||||
pop %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
movw %sp, %bx
|
||||
movw 4(%bx), %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
movw $GET_OFFSET(int90_msg), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
iret
|
||||
|
||||
int90_msg:
|
||||
.string "INT90 started\n$"
|
||||
|
||||
hello_world:
|
||||
.string "Hello VM86 world\n$"
|
||||
|
||||
IF_msg:
|
||||
.string "VM86 IF test\n$"
|
||||
|
||||
IF_msg1:
|
||||
.string "If you see a diff here, your Linux kernel is buggy, please update to 2.4.20 kernel\n$"
|
||||
|
||||
vm86_code_end:
|
||||
|
@@ -1,7 +1,13 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/mman.h>
|
||||
#include <asm/vm86.h>
|
||||
|
||||
#define TEST_CMOV 0
|
||||
|
||||
@@ -707,6 +713,19 @@ uint8_t seg_data2[4096];
|
||||
|
||||
#define MK_SEL(n) (((n) << 3) | 7)
|
||||
|
||||
#define TEST_LR(op, size, seg, mask)\
|
||||
{\
|
||||
int res, res2;\
|
||||
res = 0x12345678;\
|
||||
asm (op " %" size "2, %" size "0\n" \
|
||||
"movl $0, %1\n"\
|
||||
"jnz 1f\n"\
|
||||
"movl $1, %1\n"\
|
||||
"1:\n"\
|
||||
: "=r" (res), "=r" (res2) : "m" (seg), "0" (res));\
|
||||
printf(op ": Z=%d %08x\n", res2, res & ~(mask));\
|
||||
}
|
||||
|
||||
/* NOTE: we use Linux modify_ldt syscall */
|
||||
void test_segs(void)
|
||||
{
|
||||
@@ -714,6 +733,10 @@ void test_segs(void)
|
||||
long long ldt_table[3];
|
||||
int res, res2;
|
||||
char tmp;
|
||||
struct {
|
||||
uint32_t offset;
|
||||
uint16_t seg;
|
||||
} __attribute__((packed)) segoff;
|
||||
|
||||
ldt.entry_number = 1;
|
||||
ldt.base_addr = (unsigned long)&seg_data1;
|
||||
@@ -772,6 +795,24 @@ void test_segs(void)
|
||||
: "r" (MK_SEL(1)), "r" (&tmp));
|
||||
printf("DS[1] = %02x\n", res);
|
||||
printf("SS[tmp] = %02x\n", res2);
|
||||
|
||||
segoff.seg = MK_SEL(2);
|
||||
segoff.offset = 0xabcdef12;
|
||||
asm volatile("lfs %2, %0\n\t"
|
||||
"movl %%fs, %1\n\t"
|
||||
: "=r" (res), "=g" (res2)
|
||||
: "m" (segoff));
|
||||
printf("FS:reg = %04x:%08x\n", res2, res);
|
||||
|
||||
TEST_LR("larw", "w", MK_SEL(2), 0x0100);
|
||||
TEST_LR("larl", "", MK_SEL(2), 0x0100);
|
||||
TEST_LR("lslw", "w", MK_SEL(2), 0);
|
||||
TEST_LR("lsll", "", MK_SEL(2), 0);
|
||||
|
||||
TEST_LR("larw", "w", 0xfff8, 0);
|
||||
TEST_LR("larl", "", 0xfff8, 0);
|
||||
TEST_LR("lslw", "w", 0xfff8, 0);
|
||||
TEST_LR("lsll", "", 0xfff8, 0);
|
||||
}
|
||||
|
||||
/* 16 bit code test */
|
||||
@@ -812,7 +853,414 @@ void test_code16(void)
|
||||
printf("func3() = 0x%08x\n", res);
|
||||
}
|
||||
|
||||
void test_misc(void)
|
||||
{
|
||||
char table[256];
|
||||
int res, i;
|
||||
|
||||
for(i=0;i<256;i++) table[i] = 256 - i;
|
||||
res = 0x12345678;
|
||||
asm ("xlat" : "=a" (res) : "b" (table), "0" (res));
|
||||
printf("xlat: EAX=%08x\n", res);
|
||||
}
|
||||
|
||||
uint8_t str_buffer[4096];
|
||||
|
||||
#define TEST_STRING1(OP, size, DF, REP)\
|
||||
{\
|
||||
int esi, edi, eax, ecx, eflags;\
|
||||
\
|
||||
esi = (long)(str_buffer + sizeof(str_buffer) / 2);\
|
||||
edi = (long)(str_buffer + sizeof(str_buffer) / 2) + 16;\
|
||||
eax = 0x12345678;\
|
||||
ecx = 17;\
|
||||
\
|
||||
asm volatile ("pushl $0\n\t"\
|
||||
"popf\n\t"\
|
||||
DF "\n\t"\
|
||||
REP #OP size "\n\t"\
|
||||
"cld\n\t"\
|
||||
"pushf\n\t"\
|
||||
"popl %4\n\t"\
|
||||
: "=S" (esi), "=D" (edi), "=a" (eax), "=c" (ecx), "=g" (eflags)\
|
||||
: "0" (esi), "1" (edi), "2" (eax), "3" (ecx));\
|
||||
printf("%-10s ESI=%08x EDI=%08x EAX=%08x ECX=%08x EFL=%04x\n",\
|
||||
REP #OP size, esi, edi, eax, ecx,\
|
||||
eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\
|
||||
}
|
||||
|
||||
#define TEST_STRING(OP, REP)\
|
||||
TEST_STRING1(OP, "b", "", REP);\
|
||||
TEST_STRING1(OP, "w", "", REP);\
|
||||
TEST_STRING1(OP, "l", "", REP);\
|
||||
TEST_STRING1(OP, "b", "std", REP);\
|
||||
TEST_STRING1(OP, "w", "std", REP);\
|
||||
TEST_STRING1(OP, "l", "std", REP)
|
||||
|
||||
void test_string(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0;i < sizeof(str_buffer); i++)
|
||||
str_buffer[i] = i + 0x56;
|
||||
TEST_STRING(stos, "");
|
||||
TEST_STRING(stos, "rep ");
|
||||
TEST_STRING(lods, ""); /* to verify stos */
|
||||
TEST_STRING(lods, "rep ");
|
||||
TEST_STRING(movs, "");
|
||||
TEST_STRING(movs, "rep ");
|
||||
TEST_STRING(lods, ""); /* to verify stos */
|
||||
|
||||
/* XXX: better tests */
|
||||
TEST_STRING(scas, "");
|
||||
TEST_STRING(scas, "repz ");
|
||||
TEST_STRING(scas, "repnz ");
|
||||
TEST_STRING(cmps, "");
|
||||
TEST_STRING(cmps, "repz ");
|
||||
TEST_STRING(cmps, "repnz ");
|
||||
}
|
||||
|
||||
/* VM86 test */
|
||||
|
||||
static inline void set_bit(uint8_t *a, unsigned int bit)
|
||||
{
|
||||
a[bit / 8] |= (1 << (bit % 8));
|
||||
}
|
||||
|
||||
static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
|
||||
{
|
||||
return (uint8_t *)((seg << 4) + (reg & 0xffff));
|
||||
}
|
||||
|
||||
static inline void pushw(struct vm86_regs *r, int val)
|
||||
{
|
||||
r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
|
||||
*(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
|
||||
}
|
||||
|
||||
#undef __syscall_return
|
||||
#define __syscall_return(type, res) \
|
||||
do { \
|
||||
return (type) (res); \
|
||||
} while (0)
|
||||
|
||||
_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86)
|
||||
|
||||
extern char vm86_code_start;
|
||||
extern char vm86_code_end;
|
||||
|
||||
#define VM86_CODE_CS 0x100
|
||||
#define VM86_CODE_IP 0x100
|
||||
|
||||
void test_vm86(void)
|
||||
{
|
||||
struct vm86plus_struct ctx;
|
||||
struct vm86_regs *r;
|
||||
uint8_t *vm86_mem;
|
||||
int seg, ret;
|
||||
|
||||
vm86_mem = mmap((void *)0x00000000, 0x110000,
|
||||
PROT_WRITE | PROT_READ | PROT_EXEC,
|
||||
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (vm86_mem == MAP_FAILED) {
|
||||
printf("ERROR: could not map vm86 memory");
|
||||
return;
|
||||
}
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
|
||||
/* init basic registers */
|
||||
r = &ctx.regs;
|
||||
r->eip = VM86_CODE_IP;
|
||||
r->esp = 0xfffe;
|
||||
seg = VM86_CODE_CS;
|
||||
r->cs = seg;
|
||||
r->ss = seg;
|
||||
r->ds = seg;
|
||||
r->es = seg;
|
||||
r->fs = seg;
|
||||
r->gs = seg;
|
||||
r->eflags = VIF_MASK;
|
||||
|
||||
/* move code to proper address. We use the same layout as a .com
|
||||
dos program. */
|
||||
memcpy(vm86_mem + (VM86_CODE_CS << 4) + VM86_CODE_IP,
|
||||
&vm86_code_start, &vm86_code_end - &vm86_code_start);
|
||||
|
||||
/* mark int 0x21 as being emulated */
|
||||
set_bit((uint8_t *)&ctx.int_revectored, 0x21);
|
||||
|
||||
for(;;) {
|
||||
ret = vm86(VM86_ENTER, &ctx);
|
||||
switch(VM86_TYPE(ret)) {
|
||||
case VM86_INTx:
|
||||
{
|
||||
int int_num, ah;
|
||||
|
||||
int_num = VM86_ARG(ret);
|
||||
if (int_num != 0x21)
|
||||
goto unknown_int;
|
||||
ah = (r->eax >> 8) & 0xff;
|
||||
switch(ah) {
|
||||
case 0x00: /* exit */
|
||||
goto the_end;
|
||||
case 0x02: /* write char */
|
||||
{
|
||||
uint8_t c = r->edx;
|
||||
putchar(c);
|
||||
}
|
||||
break;
|
||||
case 0x09: /* write string */
|
||||
{
|
||||
uint8_t c, *ptr;
|
||||
ptr = seg_to_linear(r->ds, r->edx);
|
||||
for(;;) {
|
||||
c = *ptr++;
|
||||
if (c == '$')
|
||||
break;
|
||||
putchar(c);
|
||||
}
|
||||
r->eax = (r->eax & ~0xff) | '$';
|
||||
}
|
||||
break;
|
||||
case 0xff: /* extension: write hex number in edx */
|
||||
printf("%08x\n", (int)r->edx);
|
||||
break;
|
||||
default:
|
||||
unknown_int:
|
||||
printf("unsupported int 0x%02x\n", int_num);
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VM86_SIGNAL:
|
||||
/* a signal came, we just ignore that */
|
||||
break;
|
||||
case VM86_STI:
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unhandled vm86 return code (0x%x)\n", ret);
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
the_end:
|
||||
printf("VM86 end\n");
|
||||
munmap(vm86_mem, 0x110000);
|
||||
}
|
||||
|
||||
/* exception tests */
|
||||
#ifndef REG_EAX
|
||||
#define REG_EAX EAX
|
||||
#define REG_EBX EBX
|
||||
#define REG_ECX ECX
|
||||
#define REG_EDX EDX
|
||||
#define REG_ESI ESI
|
||||
#define REG_EDI EDI
|
||||
#define REG_EBP EBP
|
||||
#define REG_ESP ESP
|
||||
#define REG_EIP EIP
|
||||
#define REG_EFL EFL
|
||||
#define REG_TRAPNO TRAPNO
|
||||
#define REG_ERR ERR
|
||||
#endif
|
||||
|
||||
jmp_buf jmp_env;
|
||||
int v1;
|
||||
int tab[2];
|
||||
|
||||
void sig_handler(int sig, siginfo_t *info, void *puc)
|
||||
{
|
||||
struct ucontext *uc = puc;
|
||||
|
||||
printf("si_signo=%d si_errno=%d si_code=%d",
|
||||
info->si_signo, info->si_errno, info->si_code);
|
||||
printf(" si_addr=0x%08lx",
|
||||
(unsigned long)info->si_addr);
|
||||
printf("\n");
|
||||
|
||||
printf("trapno=0x%02x err=0x%08x",
|
||||
uc->uc_mcontext.gregs[REG_TRAPNO],
|
||||
uc->uc_mcontext.gregs[REG_ERR]);
|
||||
printf(" EIP=0x%08x", uc->uc_mcontext.gregs[REG_EIP]);
|
||||
printf("\n");
|
||||
longjmp(jmp_env, 1);
|
||||
}
|
||||
|
||||
void test_exceptions(void)
|
||||
{
|
||||
struct modify_ldt_ldt_s ldt;
|
||||
struct sigaction act;
|
||||
volatile int val;
|
||||
|
||||
act.sa_sigaction = sig_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGFPE, &act, NULL);
|
||||
sigaction(SIGILL, &act, NULL);
|
||||
sigaction(SIGSEGV, &act, NULL);
|
||||
sigaction(SIGBUS, &act, NULL);
|
||||
sigaction(SIGTRAP, &act, NULL);
|
||||
|
||||
/* test division by zero reporting */
|
||||
printf("DIVZ exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now divide by zero */
|
||||
v1 = 0;
|
||||
v1 = 2 / v1;
|
||||
}
|
||||
|
||||
printf("BOUND exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* bound exception */
|
||||
tab[0] = 1;
|
||||
tab[1] = 10;
|
||||
asm volatile ("bound %0, %1" : : "r" (11), "m" (tab));
|
||||
}
|
||||
|
||||
printf("segment exceptions:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* load an invalid segment */
|
||||
asm volatile ("movl %0, %%fs" : : "r" ((0x1234 << 3) | 1));
|
||||
}
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* null data segment is valid */
|
||||
asm volatile ("movl %0, %%fs" : : "r" (3));
|
||||
/* null stack segment */
|
||||
asm volatile ("movl %0, %%ss" : : "r" (3));
|
||||
}
|
||||
|
||||
ldt.entry_number = 1;
|
||||
ldt.base_addr = (unsigned long)&seg_data1;
|
||||
ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;
|
||||
ldt.seg_32bit = 1;
|
||||
ldt.contents = MODIFY_LDT_CONTENTS_DATA;
|
||||
ldt.read_exec_only = 0;
|
||||
ldt.limit_in_pages = 1;
|
||||
ldt.seg_not_present = 1;
|
||||
ldt.useable = 1;
|
||||
modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
|
||||
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* segment not present */
|
||||
asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1)));
|
||||
}
|
||||
|
||||
/* test SEGV reporting */
|
||||
printf("PF exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
val = 1;
|
||||
/* now store in an invalid address */
|
||||
*(char *)0x1234 = 1;
|
||||
}
|
||||
|
||||
/* test SEGV reporting */
|
||||
printf("PF exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
val = 1;
|
||||
/* read from an invalid address */
|
||||
v1 = *(char *)0x1234;
|
||||
}
|
||||
|
||||
/* test illegal instruction reporting */
|
||||
printf("UD2 exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now execute an invalid instruction */
|
||||
asm volatile("ud2");
|
||||
}
|
||||
|
||||
printf("INT exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int $0xfd");
|
||||
}
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int $0x01");
|
||||
}
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile (".byte 0xcd, 0x03");
|
||||
}
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int $0x04");
|
||||
}
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int $0x05");
|
||||
}
|
||||
|
||||
printf("INT3 exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int3");
|
||||
}
|
||||
|
||||
printf("CLI exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
printf("STI exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
printf("INTO exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* overflow exception */
|
||||
asm volatile ("addl $1, %0 ; into" : : "r" (0x7fffffff));
|
||||
}
|
||||
|
||||
printf("OUTB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("outb %%al, %%dx" : : "d" (0x4321), "a" (0));
|
||||
}
|
||||
|
||||
printf("INB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("inb %%dx, %%al" : "=a" (val) : "d" (0x4321));
|
||||
}
|
||||
|
||||
printf("REP OUTSB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("rep outsb" : : "d" (0x4321), "S" (tab), "c" (1));
|
||||
}
|
||||
|
||||
printf("REP INSB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("rep insb" : : "d" (0x4321), "D" (tab), "c" (1));
|
||||
}
|
||||
|
||||
printf("HLT exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
|
||||
printf("single step exception:\n");
|
||||
val = 0;
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("pushf\n"
|
||||
"orl $0x00100, (%%esp)\n"
|
||||
"popf\n"
|
||||
"movl $0xabcd, %0\n"
|
||||
"movl $0x0, %0\n" : "=m" (val) : : "cc", "memory");
|
||||
}
|
||||
printf("val=0x%x\n", val);
|
||||
}
|
||||
|
||||
/* self modifying code test */
|
||||
uint8_t code[] = {
|
||||
0xb8, 0x1, 0x00, 0x00, 0x00, /* movl $1, %eax */
|
||||
0xc3, /* ret */
|
||||
};
|
||||
|
||||
typedef int FuncType(void);
|
||||
|
||||
void test_self_modifying_code(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("self modifying code:\n");
|
||||
printf("func1 = 0x%x\n", ((FuncType *)code)());
|
||||
for(i = 2; i <= 4; i++) {
|
||||
code[1] = i;
|
||||
printf("func%d = 0x%x\n", i, ((FuncType *)code)());
|
||||
}
|
||||
}
|
||||
|
||||
static void *call_end __init_call = NULL;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -831,8 +1279,13 @@ int main(int argc, char **argv)
|
||||
test_floats();
|
||||
test_bcd();
|
||||
test_xchg();
|
||||
test_string();
|
||||
test_misc();
|
||||
test_lea();
|
||||
test_segs();
|
||||
test_code16();
|
||||
test_vm86();
|
||||
test_exceptions();
|
||||
test_self_modifying_code();
|
||||
return 0;
|
||||
}
|
||||
|
152
tests/test_path.c
Normal file
152
tests/test_path.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/* Test path override code */
|
||||
#define _GNU_SOURCE
|
||||
#include "../path.c"
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/* Any log message kills the test. */
|
||||
void gemu_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "FATAL: ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define NO_CHANGE(_path) \
|
||||
do { \
|
||||
if (strcmp(path(_path), _path) != 0) return __LINE__; \
|
||||
} while(0)
|
||||
|
||||
#define CHANGE_TO(_path, _newpath) \
|
||||
do { \
|
||||
if (strcmp(path(_path), _newpath) != 0) return __LINE__; \
|
||||
} while(0)
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE2");
|
||||
unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE3");
|
||||
unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE4");
|
||||
unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE5");
|
||||
rmdir("/tmp/qemu-test_path/DIR1/DIR2");
|
||||
rmdir("/tmp/qemu-test_path/DIR1/DIR3");
|
||||
rmdir("/tmp/qemu-test_path/DIR1");
|
||||
rmdir("/tmp/qemu-test_path");
|
||||
}
|
||||
|
||||
static unsigned int do_test(void)
|
||||
{
|
||||
if (mkdir("/tmp/qemu-test_path", 0700) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (mkdir("/tmp/qemu-test_path/DIR1", 0700) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (mkdir("/tmp/qemu-test_path/DIR1/DIR2", 0700) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (mkdir("/tmp/qemu-test_path/DIR1/DIR3", 0700) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE", 0600)) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE2", 0600)) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE3", 0600)) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE4", 0600)) != 0)
|
||||
return __LINE__;
|
||||
|
||||
if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE5", 0600)) != 0)
|
||||
return __LINE__;
|
||||
|
||||
init_paths("/tmp/qemu-test_path");
|
||||
|
||||
NO_CHANGE("/tmp");
|
||||
NO_CHANGE("/tmp/");
|
||||
NO_CHANGE("/tmp/qemu-test_path");
|
||||
NO_CHANGE("/tmp/qemu-test_path/");
|
||||
NO_CHANGE("/tmp/qemu-test_path/D");
|
||||
NO_CHANGE("/tmp/qemu-test_path/DI");
|
||||
NO_CHANGE("/tmp/qemu-test_path/DIR");
|
||||
NO_CHANGE("/tmp/qemu-test_path/DIR1");
|
||||
NO_CHANGE("/tmp/qemu-test_path/DIR1/");
|
||||
|
||||
NO_CHANGE("/D");
|
||||
NO_CHANGE("/DI");
|
||||
NO_CHANGE("/DIR");
|
||||
NO_CHANGE("/DIR2");
|
||||
NO_CHANGE("/DIR1.");
|
||||
|
||||
CHANGE_TO("/DIR1", "/tmp/qemu-test_path/DIR1");
|
||||
CHANGE_TO("/DIR1/", "/tmp/qemu-test_path/DIR1");
|
||||
|
||||
NO_CHANGE("/DIR1/D");
|
||||
NO_CHANGE("/DIR1/DI");
|
||||
NO_CHANGE("/DIR1/DIR");
|
||||
NO_CHANGE("/DIR1/DIR1");
|
||||
|
||||
CHANGE_TO("/DIR1/DIR2", "/tmp/qemu-test_path/DIR1/DIR2");
|
||||
CHANGE_TO("/DIR1/DIR2/", "/tmp/qemu-test_path/DIR1/DIR2");
|
||||
|
||||
CHANGE_TO("/DIR1/DIR3", "/tmp/qemu-test_path/DIR1/DIR3");
|
||||
CHANGE_TO("/DIR1/DIR3/", "/tmp/qemu-test_path/DIR1/DIR3");
|
||||
|
||||
NO_CHANGE("/DIR1/DIR2/F");
|
||||
NO_CHANGE("/DIR1/DIR2/FI");
|
||||
NO_CHANGE("/DIR1/DIR2/FIL");
|
||||
NO_CHANGE("/DIR1/DIR2/FIL.");
|
||||
|
||||
CHANGE_TO("/DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/DIR1/DIR2/FILE2", "/tmp/qemu-test_path/DIR1/DIR2/FILE2");
|
||||
CHANGE_TO("/DIR1/DIR2/FILE3", "/tmp/qemu-test_path/DIR1/DIR2/FILE3");
|
||||
CHANGE_TO("/DIR1/DIR2/FILE4", "/tmp/qemu-test_path/DIR1/DIR2/FILE4");
|
||||
CHANGE_TO("/DIR1/DIR2/FILE5", "/tmp/qemu-test_path/DIR1/DIR2/FILE5");
|
||||
|
||||
NO_CHANGE("/DIR1/DIR2/FILE6");
|
||||
NO_CHANGE("/DIR1/DIR2/FILE/X");
|
||||
|
||||
CHANGE_TO("/DIR1/../DIR1", "/tmp/qemu-test_path/DIR1");
|
||||
CHANGE_TO("/DIR1/../DIR1/", "/tmp/qemu-test_path/DIR1");
|
||||
CHANGE_TO("/../DIR1", "/tmp/qemu-test_path/DIR1");
|
||||
CHANGE_TO("/../DIR1/", "/tmp/qemu-test_path/DIR1");
|
||||
CHANGE_TO("/DIR1/DIR2/../DIR2", "/tmp/qemu-test_path/DIR1/DIR2");
|
||||
CHANGE_TO("/DIR1/DIR2/../DIR2/../../DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/DIR1/DIR2/../DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
|
||||
NO_CHANGE("/DIR1/DIR2/../DIR1");
|
||||
NO_CHANGE("/DIR1/DIR2/../FILE");
|
||||
|
||||
CHANGE_TO("/./DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/././DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/DIR1/./DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/DIR1/././DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/DIR1/DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/DIR1/DIR2/././FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
CHANGE_TO("/./DIR1/./DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = do_test();
|
||||
cleanup();
|
||||
if (ret) {
|
||||
fprintf(stderr, "test_path: failed on line %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
167
tests/testsig.c
167
tests/testsig.c
@@ -15,21 +15,38 @@ void alarm_handler(int sig)
|
||||
alarm(1);
|
||||
}
|
||||
|
||||
#ifndef REG_EAX
|
||||
#define REG_EAX EAX
|
||||
#define REG_EBX EBX
|
||||
#define REG_ECX ECX
|
||||
#define REG_EDX EDX
|
||||
#define REG_ESI ESI
|
||||
#define REG_EDI EDI
|
||||
#define REG_EBP EBP
|
||||
#define REG_ESP ESP
|
||||
#define REG_EIP EIP
|
||||
#define REG_EFL EFL
|
||||
#define REG_TRAPNO TRAPNO
|
||||
#define REG_ERR ERR
|
||||
#endif
|
||||
|
||||
void dump_regs(struct ucontext *uc)
|
||||
{
|
||||
printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
|
||||
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
|
||||
"EFL=%08x EIP=%08x\n",
|
||||
uc->uc_mcontext.gregs[EAX],
|
||||
uc->uc_mcontext.gregs[EBX],
|
||||
uc->uc_mcontext.gregs[ECX],
|
||||
uc->uc_mcontext.gregs[EDX],
|
||||
uc->uc_mcontext.gregs[ESI],
|
||||
uc->uc_mcontext.gregs[EDI],
|
||||
uc->uc_mcontext.gregs[EBP],
|
||||
uc->uc_mcontext.gregs[ESP],
|
||||
uc->uc_mcontext.gregs[EFL],
|
||||
uc->uc_mcontext.gregs[EIP]);
|
||||
"EFL=%08x EIP=%08x trapno=%02x err=%08x\n",
|
||||
uc->uc_mcontext.gregs[REG_EAX],
|
||||
uc->uc_mcontext.gregs[REG_EBX],
|
||||
uc->uc_mcontext.gregs[REG_ECX],
|
||||
uc->uc_mcontext.gregs[REG_EDX],
|
||||
uc->uc_mcontext.gregs[REG_ESI],
|
||||
uc->uc_mcontext.gregs[REG_EDI],
|
||||
uc->uc_mcontext.gregs[REG_EBP],
|
||||
uc->uc_mcontext.gregs[REG_ESP],
|
||||
uc->uc_mcontext.gregs[REG_EFL],
|
||||
uc->uc_mcontext.gregs[REG_EIP],
|
||||
uc->uc_mcontext.gregs[REG_TRAPNO],
|
||||
uc->uc_mcontext.gregs[REG_ERR]);
|
||||
}
|
||||
|
||||
void sig_handler(int sig, siginfo_t *info, void *puc)
|
||||
@@ -45,19 +62,23 @@ void sig_handler(int sig, siginfo_t *info, void *puc)
|
||||
}
|
||||
|
||||
int v1;
|
||||
int tab[2];
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction act;
|
||||
int i;
|
||||
volatile int val;
|
||||
|
||||
act.sa_sigaction = sig_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGFPE, &act, NULL);
|
||||
sigaction(SIGILL, &act, NULL);
|
||||
sigaction(SIGSEGV, &act, NULL);
|
||||
sigaction(SIGTRAP, &act, NULL);
|
||||
|
||||
/* test division by zero reporting */
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
act.sa_sigaction = sig_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO | SA_ONESHOT;
|
||||
sigaction(SIGFPE, &act, NULL);
|
||||
|
||||
/* now divide by zero */
|
||||
v1 = 0;
|
||||
v1 = 2 / v1;
|
||||
@@ -65,33 +86,109 @@ int main(int argc, char **argv)
|
||||
|
||||
/* test illegal instruction reporting */
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
act.sa_sigaction = sig_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO | SA_ONESHOT;
|
||||
sigaction(SIGILL, &act, NULL);
|
||||
|
||||
/* now execute an invalid instruction */
|
||||
asm volatile("ud2");
|
||||
}
|
||||
|
||||
/* test SEGV reporting */
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
act.sa_sigaction = sig_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO | SA_ONESHOT;
|
||||
sigaction(SIGSEGV, &act, NULL);
|
||||
|
||||
/* now store in an invalid address */
|
||||
*(char *)0x1234 = 1;
|
||||
}
|
||||
|
||||
act.sa_handler = alarm_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGALRM, &act, NULL);
|
||||
alarm(1);
|
||||
for(i = 0;i < 2; i++) {
|
||||
sleep(1);
|
||||
|
||||
/* test SEGV reporting */
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* read from an invalid address */
|
||||
v1 = *(char *)0x1234;
|
||||
}
|
||||
|
||||
printf("segment GPF exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* load an invalid segment */
|
||||
asm volatile ("movl %0, %%fs" : : "r" ((0x1234 << 3) | 0));
|
||||
}
|
||||
|
||||
printf("INT exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int $0xfd");
|
||||
}
|
||||
|
||||
printf("INT3 exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int3");
|
||||
}
|
||||
|
||||
printf("CLI exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
printf("STI exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
printf("INTO exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* overflow exception */
|
||||
asm volatile ("addl $1, %0 ; into" : : "r" (0x7fffffff));
|
||||
}
|
||||
|
||||
printf("BOUND exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* bound exception */
|
||||
tab[0] = 1;
|
||||
tab[1] = 10;
|
||||
asm volatile ("bound %0, %1" : : "r" (11), "m" (tab));
|
||||
}
|
||||
|
||||
printf("OUTB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("outb %%al, %%dx" : : "d" (0x4321), "a" (0));
|
||||
}
|
||||
|
||||
printf("INB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("inb %%dx, %%al" : "=a" (val) : "d" (0x4321));
|
||||
}
|
||||
|
||||
printf("REP OUTSB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("rep outsb" : : "d" (0x4321), "S" (tab), "c" (1));
|
||||
}
|
||||
|
||||
printf("REP INSB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("rep insb" : : "d" (0x4321), "D" (tab), "c" (1));
|
||||
}
|
||||
|
||||
printf("HLT exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
|
||||
printf("single step exception:\n");
|
||||
val = 0;
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("pushf\n"
|
||||
"orl $0x00100, (%%esp)\n"
|
||||
"popf\n"
|
||||
"movl $0xabcd, %0\n" : "=m" (val) : : "cc", "memory");
|
||||
}
|
||||
printf("val=0x%x\n", val);
|
||||
|
||||
#if 1
|
||||
{
|
||||
int i;
|
||||
act.sa_handler = alarm_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGALRM, &act, NULL);
|
||||
alarm(1);
|
||||
for(i = 0;i < 2; i++) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
2
thunk.c
2
thunk.c
@@ -218,7 +218,7 @@ const argtype *thunk_convert(void *dst, const void *src,
|
||||
case TYPE_LONG:
|
||||
case TYPE_ULONG:
|
||||
case TYPE_PTRVOID:
|
||||
if (target_to_host) {
|
||||
if (to_host) {
|
||||
*(uint64_t *)dst = tswap32(*(uint32_t *)src);
|
||||
} else {
|
||||
*(uint32_t *)dst = tswap32(*(uint64_t *)src & 0xffffffff);
|
||||
|
20
thunk.h
20
thunk.h
@@ -70,7 +70,7 @@
|
||||
#define TARGET_LONG_BITS 32
|
||||
|
||||
|
||||
#if defined(__alpha__)
|
||||
#if defined(__alpha__) || defined (__ia64__)
|
||||
#define HOST_LONG_BITS 64
|
||||
#else
|
||||
#define HOST_LONG_BITS 32
|
||||
@@ -94,17 +94,17 @@ static inline uint64_t bswap64(uint64_t x)
|
||||
return bswap_64(x);
|
||||
}
|
||||
|
||||
static void inline bswap16s(uint16_t *s)
|
||||
static inline void bswap16s(uint16_t *s)
|
||||
{
|
||||
*s = bswap16(*s);
|
||||
}
|
||||
|
||||
static void inline bswap32s(uint32_t *s)
|
||||
static inline void bswap32s(uint32_t *s)
|
||||
{
|
||||
*s = bswap32(*s);
|
||||
}
|
||||
|
||||
static void inline bswap64s(uint64_t *s)
|
||||
static inline void bswap64s(uint64_t *s)
|
||||
{
|
||||
*s = bswap64(*s);
|
||||
}
|
||||
@@ -126,17 +126,17 @@ static inline uint64_t tswap64(uint64_t s)
|
||||
return bswap64(s);
|
||||
}
|
||||
|
||||
static void inline tswap16s(uint16_t *s)
|
||||
static inline void tswap16s(uint16_t *s)
|
||||
{
|
||||
*s = bswap16(*s);
|
||||
}
|
||||
|
||||
static void inline tswap32s(uint32_t *s)
|
||||
static inline void tswap32s(uint32_t *s)
|
||||
{
|
||||
*s = bswap32(*s);
|
||||
}
|
||||
|
||||
static void inline tswap64s(uint64_t *s)
|
||||
static inline void tswap64s(uint64_t *s)
|
||||
{
|
||||
*s = bswap64(*s);
|
||||
}
|
||||
@@ -158,15 +158,15 @@ static inline uint64_t tswap64(uint64_t s)
|
||||
return s;
|
||||
}
|
||||
|
||||
static void inline tswap16s(uint16_t *s)
|
||||
static inline void tswap16s(uint16_t *s)
|
||||
{
|
||||
}
|
||||
|
||||
static void inline tswap32s(uint32_t *s)
|
||||
static inline void tswap32s(uint32_t *s)
|
||||
{
|
||||
}
|
||||
|
||||
static void inline tswap64s(uint64_t *s)
|
||||
static inline void tswap64s(uint64_t *s)
|
||||
{
|
||||
}
|
||||
|
||||
|
1122
translate-i386.c
1122
translate-i386.c
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user