Compare 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 | ||
|
|
3ef693a032 | ||
|
|
b03c60f351 | ||
|
|
9de5e440b9 | ||
|
|
66fb9763af | ||
|
|
1b6b029e40 | ||
|
|
612384d771 | ||
|
|
dab2ed991a | ||
|
|
e591824733 | ||
|
|
04369ff2f5 | ||
|
|
68decc7c7f | ||
|
|
dc99065b5f | ||
|
|
ca735206e0 | ||
|
|
5dd9488c09 | ||
|
|
60cd49d5d7 | ||
|
|
a300e69170 | ||
|
|
1a9353d258 | ||
|
|
6dbad63eef | ||
|
|
27362c82e9 | ||
|
|
55480af80e | ||
|
|
7d13299d07 | ||
|
|
1017ebe9cb | ||
|
|
77f8dd5add | ||
|
|
c5e9815da4 | ||
|
|
9d8e9c0993 | ||
|
|
d57c4e0120 | ||
|
|
4b74fe1f00 | ||
|
|
586314f2aa | ||
|
|
0ecfa9930c | ||
|
|
ba1c6e37fc | ||
|
|
927f621e79 | ||
|
|
367e86e847 | ||
|
|
7bfdb6d18c | ||
|
|
379ca80d34 | ||
|
|
4d1135e486 | ||
|
|
5147f5aac0 | ||
|
|
766a487abf | ||
|
|
72f0390014 | ||
|
|
b17780d521 | ||
|
|
3431395696 | ||
|
|
01ffc75bc9 | ||
|
|
31e31b8a24 | ||
|
|
e63c3dc74b |
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
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.
|
||||
|
||||
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 software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. 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 components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program 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
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
+111
-111
@@ -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!
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +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.
|
||||
@@ -1,36 +1,190 @@
|
||||
include config.mak
|
||||
|
||||
CFLAGS=-Wall -O2 -g
|
||||
LDFLAGS=-g
|
||||
DEFINES=-D_GNU_SOURCE -DGEMU -DDOSEMU #-DNO_TRACE_MSGS
|
||||
LIBS=
|
||||
DEFINES=-DHAVE_BYTESWAP_H
|
||||
|
||||
OBJS= i386/fp87.o i386/interp_main.o i386/interp_modrm.o i386/interp_16_32.o \
|
||||
i386/interp_32_16.o i386/interp_32_32.o i386/emu-utils.o \
|
||||
i386/dis8086.o i386/emu-ldt.o
|
||||
OBJS+= elfload.o main.o thunk.o syscall.o
|
||||
ifeq ($(ARCH),i386)
|
||||
CFLAGS+=-fomit-frame-pointer
|
||||
OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
|
||||
ifeq ($(GCC_MAJOR),3)
|
||||
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
|
||||
|
||||
SRCS = $(OBJS:.o=.c)
|
||||
ifeq ($(ARCH),ppc)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,ppc.ld
|
||||
endif
|
||||
|
||||
all: gemu
|
||||
ifeq ($(ARCH),s390)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,s390.ld
|
||||
endif
|
||||
|
||||
gemu: $(OBJS)
|
||||
$(CC) -Wl,-T,i386.ld $(LDFLAGS) -o $@ $(OBJS)
|
||||
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)
|
||||
# very important to generate a return at the end of every operation
|
||||
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
||||
endif
|
||||
|
||||
#########################################################
|
||||
|
||||
DEFINES+=-D_GNU_SOURCE
|
||||
LIBS+=-lm
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-p
|
||||
main.o: CFLAGS+=-p
|
||||
endif
|
||||
|
||||
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 exec.o
|
||||
|
||||
# NOTE: the disassembler code is only needed for debugging
|
||||
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) $(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
|
||||
|
||||
# libqemu
|
||||
|
||||
libqemu.a: $(LIBOBJS)
|
||||
rm -f $@
|
||||
$(AR) rcs $@ $(LIBOBJS)
|
||||
|
||||
dyngen: dyngen.c
|
||||
$(HOST_CC) -O2 -Wall -g $< -o $@
|
||||
|
||||
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 $@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ i386/*.o i386/*~ gemu hello test1 test2 TAGS
|
||||
$(MAKE) -C tests clean
|
||||
rm -f *.o *.a *~ qemu dyngen TAGS
|
||||
|
||||
hello: hello.c
|
||||
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
distclean: clean
|
||||
rm -f config.mak config.h
|
||||
|
||||
test1: test1.c
|
||||
$(CC) $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
install: qemu
|
||||
install -m 755 -s qemu $(prefix)/bin
|
||||
|
||||
test2: test2.c
|
||||
$(CC) $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
# various test targets
|
||||
test speed: qemu
|
||||
make -C tests $@
|
||||
|
||||
TAGS:
|
||||
etags *.[ch] tests/*.[ch]
|
||||
|
||||
# documentation
|
||||
qemu-doc.html: qemu-doc.texi
|
||||
texi2html -monolithic -number $<
|
||||
|
||||
FILES= \
|
||||
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)
|
||||
|
||||
tar:
|
||||
rm -rf /tmp/$(FILE)
|
||||
mkdir -p /tmp/$(FILE)
|
||||
cp -P $(FILES) /tmp/$(FILE)
|
||||
( 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
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
The QEMU x86 emulator
|
||||
---------------------
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
Type
|
||||
|
||||
./configure --interp-prefix=/usr/local/qemu-i386
|
||||
make
|
||||
|
||||
to build qemu and libqemu.a.
|
||||
|
||||
Type
|
||||
|
||||
make install
|
||||
|
||||
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
|
||||
-------------
|
||||
|
||||
Read the documentation in qemu-doc.html.
|
||||
|
||||
|
||||
Fabrice Bellard.
|
||||
@@ -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
|
||||
@@ -1,2 +1,22 @@
|
||||
- swap all elf paramters
|
||||
- fix printf for doubles (fp87.c bug ?)
|
||||
|
||||
- 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)
|
||||
- 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
File diff suppressed because it is too large
Load Diff
@@ -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 . */
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# qemu configure script (c) 2003 Fabrice Bellard
|
||||
#
|
||||
# set temporary file name
|
||||
if test ! -z "$TMPDIR" ; then
|
||||
TMPDIR1="${TMPDIR}"
|
||||
elif test ! -z "$TEMPDIR" ; then
|
||||
TMPDIR1="${TEMPDIR}"
|
||||
else
|
||||
TMPDIR1="/tmp"
|
||||
fi
|
||||
|
||||
TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
|
||||
TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
|
||||
TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}"
|
||||
TMPS="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.S"
|
||||
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)
|
||||
cpu="x86"
|
||||
;;
|
||||
armv4l)
|
||||
cpu="armv4l"
|
||||
;;
|
||||
alpha)
|
||||
cpu="alpha"
|
||||
;;
|
||||
"Power Macintosh"|ppc|ppc64)
|
||||
cpu="powerpc"
|
||||
;;
|
||||
mips)
|
||||
cpu="mips"
|
||||
;;
|
||||
s390)
|
||||
cpu="s390"
|
||||
;;
|
||||
sparc)
|
||||
cpu="sparc"
|
||||
;;
|
||||
sparc64)
|
||||
cpu="sparc64"
|
||||
;;
|
||||
ia64)
|
||||
cpu="ia64"
|
||||
;;
|
||||
*)
|
||||
cpu="unknown"
|
||||
;;
|
||||
esac
|
||||
gprof="no"
|
||||
bigendian="no"
|
||||
|
||||
# OS specific
|
||||
targetos=`uname -s`
|
||||
case $targetos in
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# find source path
|
||||
# XXX: we assume an absolute path is given when launching configure,
|
||||
# except in './configure' case.
|
||||
source_path=${0%configure}
|
||||
source_path=${source_path%/}
|
||||
source_path_used="yes"
|
||||
if test -z "$source_path" -o "$source_path" = "." ; then
|
||||
source_path=`pwd`
|
||||
source_path_used="no"
|
||||
fi
|
||||
|
||||
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`
|
||||
;;
|
||||
--cc=*) cc=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--make=*) make=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--extra-cflags=*) CFLAGS="${opt#--extra-cflags=}"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="${opt#--extra-ldflags=}"
|
||||
;;
|
||||
--extra-libs=*) extralibs=${opt#--extra-libs=}
|
||||
;;
|
||||
--cpu=*) cpu=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--enable-gprof) gprof="yes"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Checking for CFLAGS
|
||||
if test -z "$CFLAGS"; then
|
||||
CFLAGS="-O2"
|
||||
fi
|
||||
|
||||
cc="${cross_prefix}${cc}"
|
||||
ar="${cross_prefix}${ar}"
|
||||
strip="${cross_prefix}${strip}"
|
||||
|
||||
if test -z "$cross_prefix" ; then
|
||||
|
||||
# ---
|
||||
# big/little endian test
|
||||
cat > $TMPC << EOF
|
||||
#include <inttypes.h>
|
||||
int main(int argc, char ** argv){
|
||||
volatile uint32_t i=0x01234567;
|
||||
return (*((uint8_t*)(&i))) == 0x67;
|
||||
}
|
||||
EOF
|
||||
|
||||
if $cc -o $TMPE $TMPC 2>/dev/null ; then
|
||||
$TMPE && bigendian="yes"
|
||||
else
|
||||
echo big/little test failed
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
# if cross compiling, cannot launch a program, so make a static guess
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" -o "$cpu" = "sparc" -o "$cpu" = "sparc64"; then
|
||||
bigendian="yes"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# check gcc version
|
||||
cat > $TMPC <<EOF
|
||||
int main(void) {
|
||||
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
|
||||
return 0;
|
||||
#else
|
||||
#error gcc < 3.2
|
||||
#endif
|
||||
}
|
||||
EOF
|
||||
|
||||
gcc_major="2"
|
||||
if $cc -o $TMPO $TMPC 2> /dev/null ; then
|
||||
gcc_major="3"
|
||||
fi
|
||||
|
||||
if test x"$1" = x"-h" -o x"$1" = x"--help" ; then
|
||||
cat << EOF
|
||||
|
||||
Usage: configure [options]
|
||||
Options: [defaults in brackets after descriptions]
|
||||
|
||||
EOF
|
||||
echo "Standard options:"
|
||||
echo " --help print this message"
|
||||
echo " --prefix=PREFIX install in PREFIX [$prefix]"
|
||||
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]"
|
||||
echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]"
|
||||
echo " --cc=CC use C compiler CC [$cc]"
|
||||
echo " --make=MAKE use specified make [$make]"
|
||||
echo ""
|
||||
echo "NOTE: The object files are build at the place where configure is launched"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Install prefix $prefix"
|
||||
echo "Source path $source_path"
|
||||
echo "C compiler $cc"
|
||||
echo "make $make"
|
||||
echo "host CPU $cpu"
|
||||
echo "Big Endian $bigendian"
|
||||
echo "target CPU $target_cpu"
|
||||
echo "gprof enabled $gprof"
|
||||
|
||||
echo "Creating config.mak and config.h"
|
||||
|
||||
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 \"$interp_prefix\"" >> $TMPH
|
||||
echo "MAKE=$make" >> config.mak
|
||||
echo "CC=$cc" >> config.mak
|
||||
echo "GCC_MAJOR=$gcc_major" >> config.mak
|
||||
echo "HOST_CC=$host_cc" >> config.mak
|
||||
echo "AR=$ar" >> config.mak
|
||||
echo "STRIP=$strip -s -R .comment -R .note" >> config.mak
|
||||
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
|
||||
fi
|
||||
if test "$bigendian" = "yes" ; then
|
||||
echo "WORDS_BIGENDIAN=yes" >> config.mak
|
||||
echo "#define WORDS_BIGENDIAN 1" >> $TMPH
|
||||
fi
|
||||
if test "$gprof" = "yes" ; then
|
||||
echo "TARGET_GPROF=yes" >> config.mak
|
||||
echo "#define HAVE_GPROF 1" >> $TMPH
|
||||
fi
|
||||
echo -n "VERSION=" >>config.mak
|
||||
head $source_path/VERSION >>config.mak
|
||||
echo "" >>config.mak
|
||||
echo -n "#define QEMU_VERSION \"" >> $TMPH
|
||||
head $source_path/VERSION >> $TMPH
|
||||
echo "\"" >> $TMPH
|
||||
|
||||
# build tree in object directory if source path is different from current one
|
||||
if test "$source_path_used" = "yes" ; then
|
||||
DIRS="tests"
|
||||
FILES="Makefile tests/Makefile"
|
||||
for dir in $DIRS ; do
|
||||
mkdir -p $dir
|
||||
done
|
||||
for f in $FILES ; do
|
||||
ln -sf $source_path/$f $f
|
||||
done
|
||||
fi
|
||||
echo "SRC_PATH=$source_path" >> config.mak
|
||||
|
||||
diff $TMPH config.h >/dev/null 2>&1
|
||||
if test $? -ne 0 ; then
|
||||
mv -f $TMPH config.h
|
||||
else
|
||||
echo "config.h is unchanged"
|
||||
fi
|
||||
|
||||
rm -f $TMPO $TMPC $TMPE $TMPS $TMPH
|
||||
+479
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* i386 virtual CPU header
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#ifndef CPU_I386_H
|
||||
#define CPU_I386_H
|
||||
|
||||
#include "config.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
#define R_EAX 0
|
||||
#define R_ECX 1
|
||||
#define R_EDX 2
|
||||
#define R_EBX 3
|
||||
#define R_ESP 4
|
||||
#define R_EBP 5
|
||||
#define R_ESI 6
|
||||
#define R_EDI 7
|
||||
|
||||
#define R_AL 0
|
||||
#define R_CL 1
|
||||
#define R_DL 2
|
||||
#define R_BL 3
|
||||
#define R_AH 4
|
||||
#define R_CH 5
|
||||
#define R_DH 6
|
||||
#define R_BH 7
|
||||
|
||||
#define R_ES 0
|
||||
#define R_CS 1
|
||||
#define R_SS 2
|
||||
#define R_DS 3
|
||||
#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
|
||||
#define CC_Z 0x0040
|
||||
#define CC_S 0x0080
|
||||
#define CC_O 0x0800
|
||||
|
||||
#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 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 */
|
||||
|
||||
enum {
|
||||
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
|
||||
CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */
|
||||
CC_OP_MUL, /* modify all flags, C, O = (CC_SRC != 0) */
|
||||
|
||||
CC_OP_ADDB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_ADDW,
|
||||
CC_OP_ADDL,
|
||||
|
||||
CC_OP_ADCB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_ADCW,
|
||||
CC_OP_ADCL,
|
||||
|
||||
CC_OP_SUBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_SUBW,
|
||||
CC_OP_SUBL,
|
||||
|
||||
CC_OP_SBBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_SBBW,
|
||||
CC_OP_SBBL,
|
||||
|
||||
CC_OP_LOGICB, /* modify all flags, CC_DST = res */
|
||||
CC_OP_LOGICW,
|
||||
CC_OP_LOGICL,
|
||||
|
||||
CC_OP_INCB, /* modify all flags except, CC_DST = res, CC_SRC = C */
|
||||
CC_OP_INCW,
|
||||
CC_OP_INCL,
|
||||
|
||||
CC_OP_DECB, /* modify all flags except, CC_DST = res, CC_SRC = C */
|
||||
CC_OP_DECW,
|
||||
CC_OP_DECL,
|
||||
|
||||
CC_OP_SHLB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */
|
||||
CC_OP_SHLW,
|
||||
CC_OP_SHLL,
|
||||
|
||||
CC_OP_SARB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */
|
||||
CC_OP_SARW,
|
||||
CC_OP_SARL,
|
||||
|
||||
CC_OP_NB,
|
||||
};
|
||||
|
||||
#ifdef __i386__
|
||||
#define USE_X86LDOUBLE
|
||||
#endif
|
||||
|
||||
#ifdef USE_X86LDOUBLE
|
||||
typedef long double CPU86_LDouble;
|
||||
#else
|
||||
typedef double CPU86_LDouble;
|
||||
#endif
|
||||
|
||||
typedef struct SegmentCache {
|
||||
uint8_t *base;
|
||||
unsigned long limit;
|
||||
uint8_t seg_32bit;
|
||||
} SegmentCache;
|
||||
|
||||
typedef struct SegmentDescriptorTable {
|
||||
uint8_t *base;
|
||||
unsigned long limit;
|
||||
/* this is the returned base when reading the register, just to
|
||||
avoid that the emulated program modifies it */
|
||||
unsigned long emu_base;
|
||||
} SegmentDescriptorTable;
|
||||
|
||||
typedef struct CPUX86State {
|
||||
/* standard registers */
|
||||
uint32_t regs[8];
|
||||
uint32_t eip;
|
||||
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;
|
||||
uint32_t cc_dst;
|
||||
uint32_t cc_op;
|
||||
int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */
|
||||
|
||||
/* FPU state */
|
||||
unsigned int fpstt; /* top of stack index */
|
||||
unsigned int fpus;
|
||||
unsigned int fpuc;
|
||||
uint8_t fptags[8]; /* 0 = valid, 1 = empty */
|
||||
CPU86_LDouble fpregs[8];
|
||||
|
||||
/* 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 */
|
||||
SegmentCache seg_cache[6]; /* info taken from LDT/GDT */
|
||||
SegmentDescriptorTable gdt;
|
||||
SegmentDescriptorTable ldt;
|
||||
SegmentDescriptorTable idt;
|
||||
|
||||
/* 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 */
|
||||
static inline int ldub(void *ptr)
|
||||
{
|
||||
return *(uint8_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsb(void *ptr)
|
||||
{
|
||||
return *(int8_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stb(void *ptr, int v)
|
||||
{
|
||||
*(uint8_t *)ptr = v;
|
||||
}
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
|
||||
/* conservative code for little endian unaligned accesses */
|
||||
static inline int lduw(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return p[0] | (p[1] << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldsw(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return (int16_t)val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return (int16_t)(p[0] | (p[1] << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldl(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
__asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
|
||||
return val;
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t ldq(void *ptr)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
uint32_t v1, v2;
|
||||
v1 = ldl(p);
|
||||
v2 = ldl(p + 4);
|
||||
return v1 | ((uint64_t)v2 << 32);
|
||||
}
|
||||
|
||||
static inline void stw(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
p[0] = v;
|
||||
p[1] = v >> 8;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stl(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
|
||||
#else
|
||||
uint8_t *p = ptr;
|
||||
p[0] = v;
|
||||
p[1] = v >> 8;
|
||||
p[2] = v >> 16;
|
||||
p[3] = v >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stq(void *ptr, uint64_t v)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
stl(p, (uint32_t)v);
|
||||
stl(p + 4, v >> 32);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float ldfl(void *ptr)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline double ldfq(void *ptr)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} u;
|
||||
u.i = ldq(ptr);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfl(void *ptr, float v)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline void stfq(void *ptr, double v)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} u;
|
||||
u.d = v;
|
||||
stq(ptr, u.i);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lduw(void *ptr)
|
||||
{
|
||||
return *(uint16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsw(void *ptr)
|
||||
{
|
||||
return *(int16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldl(void *ptr)
|
||||
{
|
||||
return *(uint32_t *)ptr;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq(void *ptr)
|
||||
{
|
||||
return *(uint64_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stw(void *ptr, int v)
|
||||
{
|
||||
*(uint16_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stl(void *ptr, int v)
|
||||
{
|
||||
*(uint32_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stq(void *ptr, uint64_t v)
|
||||
{
|
||||
*(uint64_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float ldfl(void *ptr)
|
||||
{
|
||||
return *(float *)ptr;
|
||||
}
|
||||
|
||||
static inline double ldfq(void *ptr)
|
||||
{
|
||||
return *(double *)ptr;
|
||||
}
|
||||
|
||||
static inline void stfl(void *ptr, float v)
|
||||
{
|
||||
*(float *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stfq(void *ptr, double v)
|
||||
{
|
||||
*(double *)ptr = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef IN_OP_I386
|
||||
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);
|
||||
int cpu_x86_exec(CPUX86State *s);
|
||||
void cpu_x86_interrupt(CPUX86State *s);
|
||||
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 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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)
|
||||
|
||||
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 */
|
||||
@@ -0,0 +1,386 @@
|
||||
/* Interface between the opcode library and its callers.
|
||||
Written by Cygnus Support, 1993.
|
||||
|
||||
The opcode library (libopcodes.a) provides instruction decoders for
|
||||
a large variety of instruction sets, callable with an identical
|
||||
interface, for making instruction-processing programs more independent
|
||||
of the instruction set being processed. */
|
||||
|
||||
#ifndef DIS_ASM_H
|
||||
#define DIS_ASM_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.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*, ...));
|
||||
|
||||
enum dis_insn_type {
|
||||
dis_noninsn, /* Not a valid instruction */
|
||||
dis_nonbranch, /* Not a branch instruction */
|
||||
dis_branch, /* Unconditional branch */
|
||||
dis_condbranch, /* Conditional branch */
|
||||
dis_jsr, /* Jump to subroutine */
|
||||
dis_condjsr, /* Conditional jump to subroutine */
|
||||
dis_dref, /* Data reference instruction */
|
||||
dis_dref2 /* Two data references in instruction */
|
||||
};
|
||||
|
||||
/* This struct is passed into the instruction decoding routine,
|
||||
and is passed back out into each callback. The various fields are used
|
||||
for conveying information from your main routine into your callbacks,
|
||||
for passing information into the instruction decoders (such as the
|
||||
addresses of the callback functions), or for passing information
|
||||
back from the instruction decoders to their callers.
|
||||
|
||||
It must be initialized before it is first passed; this can be done
|
||||
by hand, or using one of the initialization macros below. */
|
||||
|
||||
typedef struct disassemble_info {
|
||||
fprintf_ftype fprintf_func;
|
||||
FILE *stream;
|
||||
PTR application_data;
|
||||
|
||||
/* Target description. We could replace this with a pointer to the bfd,
|
||||
but that would require one. There currently isn't any such requirement
|
||||
so to avoid introducing one we record these explicitly. */
|
||||
/* The bfd_flavour. This can be bfd_target_unknown_flavour. */
|
||||
enum bfd_flavour flavour;
|
||||
/* The bfd_arch value. */
|
||||
enum bfd_architecture arch;
|
||||
/* The bfd_mach value. */
|
||||
unsigned long mach;
|
||||
/* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */
|
||||
enum bfd_endian endian;
|
||||
|
||||
/* An array of pointers to symbols either at the location being disassembled
|
||||
or at the start of the function being disassembled. The array is sorted
|
||||
so that the first symbol is intended to be the one used. The others are
|
||||
present for any misc. purposes. This is not set reliably, but if it is
|
||||
not NULL, it is correct. */
|
||||
asymbol **symbols;
|
||||
/* Number of symbols in array. */
|
||||
int num_symbols;
|
||||
|
||||
/* For use by the disassembler.
|
||||
The top 16 bits are reserved for public use (and are documented here).
|
||||
The bottom 16 bits are for the internal use of the disassembler. */
|
||||
unsigned long flags;
|
||||
#define INSN_HAS_RELOC 0x80000000
|
||||
PTR private_data;
|
||||
|
||||
/* Function used to get bytes to disassemble. MEMADDR is the
|
||||
address of the stuff to be disassembled, MYADDR is the address to
|
||||
put the bytes in, and LENGTH is the number of bytes to read.
|
||||
INFO is a pointer to this struct.
|
||||
Returns an errno value or 0 for success. */
|
||||
int (*read_memory_func)
|
||||
PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int length,
|
||||
struct disassemble_info *info));
|
||||
|
||||
/* Function which should be called if we get an error that we can't
|
||||
recover from. STATUS is the errno value from read_memory_func and
|
||||
MEMADDR is the address that we were trying to read. INFO is a
|
||||
pointer to this struct. */
|
||||
void (*memory_error_func)
|
||||
PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info));
|
||||
|
||||
/* Function called to print ADDR. */
|
||||
void (*print_address_func)
|
||||
PARAMS ((bfd_vma addr, struct disassemble_info *info));
|
||||
|
||||
/* Function called to determine if there is a symbol at the given ADDR.
|
||||
If there is, the function returns 1, otherwise it returns 0.
|
||||
This is used by ports which support an overlay manager where
|
||||
the overlay number is held in the top part of an address. In
|
||||
some circumstances we want to include the overlay number in the
|
||||
address, (normally because there is a symbol associated with
|
||||
that address), but sometimes we want to mask out the overlay bits. */
|
||||
int (* symbol_at_address_func)
|
||||
PARAMS ((bfd_vma addr, struct disassemble_info * info));
|
||||
|
||||
/* These are for buffer_read_memory. */
|
||||
bfd_byte *buffer;
|
||||
bfd_vma buffer_vma;
|
||||
int buffer_length;
|
||||
|
||||
/* This variable may be set by the instruction decoder. It suggests
|
||||
the number of bytes objdump should display on a single line. If
|
||||
the instruction decoder sets this, it should always set it to
|
||||
the same value in order to get reasonable looking output. */
|
||||
int bytes_per_line;
|
||||
|
||||
/* the next two variables control the way objdump displays the raw data */
|
||||
/* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */
|
||||
/* output will look like this:
|
||||
00: 00000000 00000000
|
||||
with the chunks displayed according to "display_endian". */
|
||||
int bytes_per_chunk;
|
||||
enum bfd_endian display_endian;
|
||||
|
||||
/* Results from instruction decoders. Not all decoders yet support
|
||||
this information. This info is set each time an instruction is
|
||||
decoded, and is only valid for the last such instruction.
|
||||
|
||||
To determine whether this decoder supports this information, set
|
||||
insn_info_valid to 0, decode an instruction, then check it. */
|
||||
|
||||
char insn_info_valid; /* Branch info has been set. */
|
||||
char branch_delay_insns; /* How many sequential insn's will run before
|
||||
a branch takes effect. (0 = normal) */
|
||||
char data_size; /* Size of data reference in insn, in bytes */
|
||||
enum dis_insn_type insn_type; /* Type of instruction */
|
||||
bfd_vma target; /* Target address of branch or dref, if known;
|
||||
zero if unknown. */
|
||||
bfd_vma target2; /* Second target address for dref2 */
|
||||
|
||||
} disassemble_info;
|
||||
|
||||
|
||||
/* Standard disassemblers. Disassemble one instruction at the given
|
||||
target address. Return number of bytes processed. */
|
||||
typedef int (*disassembler_ftype)
|
||||
PARAMS((bfd_vma, disassemble_info *));
|
||||
|
||||
extern int print_insn_big_mips PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_mips PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_i386 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m68k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_z8001 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_z8002 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300h PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300s PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern disassembler_ftype arc_get_disassembler PARAMS ((int, int));
|
||||
extern int print_insn_big_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_i960 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sh PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_shl PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_hppa PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m32r PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m88k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_mn10200 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_mn10300 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_ns32k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_powerpc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_powerpc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_rs6000 PARAMS ((bfd_vma, disassemble_info*));
|
||||
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
|
||||
into a buffer before calling the instruction decoder. */
|
||||
|
||||
/* Here is a function which callers may wish to use for read_memory_func.
|
||||
It gets bytes from a buffer. */
|
||||
extern int buffer_read_memory
|
||||
PARAMS ((bfd_vma, bfd_byte *, int, struct disassemble_info *));
|
||||
|
||||
/* This function goes with buffer_read_memory.
|
||||
It prints a message using info->fprintf_func and info->stream. */
|
||||
extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *));
|
||||
|
||||
|
||||
/* Just print the address in hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
extern void generic_print_address
|
||||
PARAMS ((bfd_vma, struct disassemble_info *));
|
||||
|
||||
/* Always true. */
|
||||
extern int generic_symbol_at_address
|
||||
PARAMS ((bfd_vma, struct disassemble_info *));
|
||||
|
||||
/* Macro to initialize a disassemble_info struct. This should be called
|
||||
by all applications creating such a struct. */
|
||||
#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \
|
||||
(INFO).flavour = bfd_target_unknown_flavour, \
|
||||
(INFO).arch = bfd_arch_unknown, \
|
||||
(INFO).mach = 0, \
|
||||
(INFO).endian = BFD_ENDIAN_UNKNOWN, \
|
||||
INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC)
|
||||
|
||||
/* Call this macro to initialize only the internal variables for the
|
||||
disassembler. Architecture dependent things such as byte order, or machine
|
||||
variant are not touched by this macro. This makes things much easier for
|
||||
GDB which must initialize these things seperatly. */
|
||||
|
||||
#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \
|
||||
(INFO).fprintf_func = (FPRINTF_FUNC), \
|
||||
(INFO).stream = (STREAM), \
|
||||
(INFO).symbols = NULL, \
|
||||
(INFO).num_symbols = 0, \
|
||||
(INFO).buffer = NULL, \
|
||||
(INFO).buffer_vma = 0, \
|
||||
(INFO).buffer_length = 0, \
|
||||
(INFO).read_memory_func = buffer_read_memory, \
|
||||
(INFO).memory_error_func = perror_memory, \
|
||||
(INFO).print_address_func = generic_print_address, \
|
||||
(INFO).symbol_at_address_func = generic_symbol_at_address, \
|
||||
(INFO).flags = 0, \
|
||||
(INFO).bytes_per_line = 0, \
|
||||
(INFO).bytes_per_chunk = 0, \
|
||||
(INFO).display_endian = BFD_ENDIAN_UNKNOWN, \
|
||||
(INFO).insn_info_valid = 0
|
||||
|
||||
#endif /* ! defined (DIS_ASM_H) */
|
||||
@@ -0,0 +1,79 @@
|
||||
/* Disassemble from a buffer, for GNU.
|
||||
Copyright (C) 1993, 1994 Free Software Foundation, Inc.
|
||||
|
||||
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "dis-asm.h"
|
||||
#include <errno.h>
|
||||
|
||||
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
||||
Transfer them to myaddr. */
|
||||
int
|
||||
buffer_read_memory (memaddr, myaddr, length, info)
|
||||
bfd_vma memaddr;
|
||||
bfd_byte *myaddr;
|
||||
int length;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (memaddr < info->buffer_vma
|
||||
|| memaddr + length > info->buffer_vma + info->buffer_length)
|
||||
/* Out of bounds. Use EIO because GDB uses it. */
|
||||
return EIO;
|
||||
memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print an error message. We can assume that this is in response to
|
||||
an error return from buffer_read_memory. */
|
||||
void
|
||||
perror_memory (status, memaddr, info)
|
||||
int status;
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (status != EIO)
|
||||
/* Can't happen. */
|
||||
(*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
|
||||
else
|
||||
/* Actually, address between memaddr and memaddr + len was
|
||||
out of bounds. */
|
||||
(*info->fprintf_func) (info->stream,
|
||||
"Address 0x%x is out of bounds.\n", memaddr);
|
||||
}
|
||||
|
||||
/* This could be in a separate file, to save miniscule amounts of space
|
||||
in statically linked executables. */
|
||||
|
||||
/* Just print the address is hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
|
||||
void
|
||||
generic_print_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "0x%x", addr);
|
||||
}
|
||||
|
||||
/* Just return the given address. */
|
||||
|
||||
int
|
||||
generic_symbol_at_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info * info;
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -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 "";
|
||||
}
|
||||
@@ -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 */
|
||||
+430
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* i386 emulator main execution loop
|
||||
*
|
||||
* 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 "exec-i386.h"
|
||||
#include "disas.h"
|
||||
|
||||
//#define DEBUG_EXEC
|
||||
//#define DEBUG_SIGNAL
|
||||
|
||||
/* main execution loop */
|
||||
|
||||
/* thread support */
|
||||
|
||||
spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
void cpu_lock(void)
|
||||
{
|
||||
spin_lock(&global_cpu_lock);
|
||||
}
|
||||
|
||||
void cpu_unlock(void)
|
||||
{
|
||||
spin_unlock(&global_cpu_lock);
|
||||
}
|
||||
|
||||
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
|
||||
#ifdef reg_ECX
|
||||
env->regs[R_ECX] = ECX;
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
env->regs[R_EDX] = EDX;
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
env->regs[R_EBX] = EBX;
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
env->regs[R_ESP] = ESP;
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
env->regs[R_EBP] = EBP;
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
env->regs[R_ESI] = ESI;
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
env->regs[R_EDI] = EDI;
|
||||
#endif
|
||||
longjmp(env->jmp_env, 1);
|
||||
}
|
||||
|
||||
int cpu_x86_exec(CPUX86State *env1)
|
||||
{
|
||||
int saved_T0, saved_T1, saved_A0;
|
||||
CPUX86State *saved_env;
|
||||
#ifdef reg_EAX
|
||||
int saved_EAX;
|
||||
#endif
|
||||
#ifdef reg_ECX
|
||||
int saved_ECX;
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
int saved_EDX;
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
int saved_EBX;
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
int saved_ESP;
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
int saved_EBP;
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
int saved_ESI;
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
int saved_EDI;
|
||||
#endif
|
||||
int code_gen_size, ret;
|
||||
void (*gen_func)(void);
|
||||
TranslationBlock *tb, **ptb;
|
||||
uint8_t *tc_ptr, *cs_base, *pc;
|
||||
unsigned int flags;
|
||||
|
||||
/* first we save global registers */
|
||||
saved_T0 = T0;
|
||||
saved_T1 = T1;
|
||||
saved_A0 = A0;
|
||||
saved_env = env;
|
||||
env = env1;
|
||||
#ifdef reg_EAX
|
||||
saved_EAX = EAX;
|
||||
EAX = env->regs[R_EAX];
|
||||
#endif
|
||||
#ifdef reg_ECX
|
||||
saved_ECX = ECX;
|
||||
ECX = env->regs[R_ECX];
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
saved_EDX = EDX;
|
||||
EDX = env->regs[R_EDX];
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
saved_EBX = EBX;
|
||||
EBX = env->regs[R_EBX];
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
saved_ESP = ESP;
|
||||
ESP = env->regs[R_ESP];
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
saved_EBP = EBP;
|
||||
EBP = env->regs[R_EBP];
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
saved_ESI = ESI;
|
||||
ESI = env->regs[R_ESI];
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
saved_EDI = EDI;
|
||||
EDI = env->regs[R_EDI];
|
||||
#endif
|
||||
|
||||
/* put eflags in CPU temporary format */
|
||||
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) {
|
||||
env->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
#ifdef DEBUG_EXEC
|
||||
if (loglevel) {
|
||||
/* 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
|
||||
change during the whole generated block. */
|
||||
flags = env->seg_cache[R_CS].seg_32bit << GEN_FLAG_CODE32_SHIFT;
|
||||
flags |= env->seg_cache[R_SS].seg_32bit << GEN_FLAG_SS32_SHIFT;
|
||||
flags |= (((unsigned long)env->seg_cache[R_DS].base |
|
||||
(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 */
|
||||
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;
|
||||
}
|
||||
tc_ptr = code_gen_ptr;
|
||||
tb->tc_ptr = tc_ptr;
|
||||
tb->cs_base = (unsigned long)cs_base;
|
||||
tb->flags = flags;
|
||||
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));
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
#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 */
|
||||
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||
|
||||
/* restore global registers */
|
||||
#ifdef reg_EAX
|
||||
EAX = saved_EAX;
|
||||
#endif
|
||||
#ifdef reg_ECX
|
||||
ECX = saved_ECX;
|
||||
#endif
|
||||
#ifdef reg_EDX
|
||||
EDX = saved_EDX;
|
||||
#endif
|
||||
#ifdef reg_EBX
|
||||
EBX = saved_EBX;
|
||||
#endif
|
||||
#ifdef reg_ESP
|
||||
ESP = saved_ESP;
|
||||
#endif
|
||||
#ifdef reg_EBP
|
||||
EBP = saved_EBP;
|
||||
#endif
|
||||
#ifdef reg_ESI
|
||||
ESI = saved_ESI;
|
||||
#endif
|
||||
#ifdef reg_EDI
|
||||
EDI = saved_EDI;
|
||||
#endif
|
||||
T0 = saved_T0;
|
||||
T1 = saved_T1;
|
||||
A0 = saved_A0;
|
||||
env = saved_env;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cpu_x86_interrupt(CPUX86State *s)
|
||||
{
|
||||
s->interrupt_request = 1;
|
||||
}
|
||||
|
||||
|
||||
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
|
||||
{
|
||||
CPUX86State *saved_env;
|
||||
|
||||
saved_env = env;
|
||||
env = s;
|
||||
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;
|
||||
}
|
||||
|
||||
#undef EAX
|
||||
#undef ECX
|
||||
#undef EDX
|
||||
#undef EBX
|
||||
#undef ESP
|
||||
#undef EBP
|
||||
#undef ESI
|
||||
#undef EDI
|
||||
#undef EIP
|
||||
#include <signal.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
/* '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)
|
||||
{
|
||||
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
|
||||
/* 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 (XXX: use sigsetjmp) */
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1));
|
||||
/* never comes here */
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
||||
void *puc)
|
||||
{
|
||||
struct ucontext *uc = puc;
|
||||
unsigned long pc;
|
||||
|
||||
#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
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* i386 execution defines
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef signed long long int64_t;
|
||||
|
||||
#define bswap32(x) \
|
||||
({ \
|
||||
uint32_t __x = (x); \
|
||||
((uint32_t)( \
|
||||
(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
|
||||
})
|
||||
|
||||
#define NULL 0
|
||||
#include <fenv.h>
|
||||
|
||||
typedef struct FILE FILE;
|
||||
extern FILE *logfile;
|
||||
extern int loglevel;
|
||||
extern int fprintf(FILE *, const char *, ...);
|
||||
extern int printf(const char *, ...);
|
||||
|
||||
#ifdef __i386__
|
||||
register unsigned int T0 asm("ebx");
|
||||
register unsigned int T1 asm("esi");
|
||||
register unsigned int A0 asm("edi");
|
||||
register struct CPUX86State *env asm("ebp");
|
||||
#endif
|
||||
#ifdef __powerpc__
|
||||
register unsigned int EAX asm("r16");
|
||||
register unsigned int ECX asm("r17");
|
||||
register unsigned int EDX asm("r18");
|
||||
register unsigned int EBX asm("r19");
|
||||
register unsigned int ESP asm("r20");
|
||||
register unsigned int EBP asm("r21");
|
||||
register unsigned int ESI asm("r22");
|
||||
register unsigned int EDI asm("r23");
|
||||
register unsigned int T0 asm("r24");
|
||||
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
|
||||
#define reg_EBX
|
||||
#define reg_ESP
|
||||
#define reg_EBP
|
||||
#define reg_ESI
|
||||
#define reg_EDI
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
register unsigned int T0 asm("r4");
|
||||
register unsigned int T1 asm("r5");
|
||||
register unsigned int A0 asm("r6");
|
||||
register struct CPUX86State *env asm("r7");
|
||||
#endif
|
||||
#ifdef __mips__
|
||||
register unsigned int T0 asm("s0");
|
||||
register unsigned int T1 asm("s1");
|
||||
register unsigned int A0 asm("s2");
|
||||
register struct CPUX86State *env asm("s3");
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
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 */
|
||||
#define FORCE_RET() asm volatile ("");
|
||||
|
||||
#ifndef OPPROTO
|
||||
#define OPPROTO
|
||||
#endif
|
||||
|
||||
#define xglue(x, y) x ## y
|
||||
#define glue(x, y) xglue(x, y)
|
||||
|
||||
#ifndef reg_EAX
|
||||
#define EAX (env->regs[R_EAX])
|
||||
#endif
|
||||
#ifndef reg_ECX
|
||||
#define ECX (env->regs[R_ECX])
|
||||
#endif
|
||||
#ifndef reg_EDX
|
||||
#define EDX (env->regs[R_EDX])
|
||||
#endif
|
||||
#ifndef reg_EBX
|
||||
#define EBX (env->regs[R_EBX])
|
||||
#endif
|
||||
#ifndef reg_ESP
|
||||
#define ESP (env->regs[R_ESP])
|
||||
#endif
|
||||
#ifndef reg_EBP
|
||||
#define EBP (env->regs[R_EBP])
|
||||
#endif
|
||||
#ifndef reg_ESI
|
||||
#define ESI (env->regs[R_ESI])
|
||||
#endif
|
||||
#ifndef reg_EDI
|
||||
#define EDI (env->regs[R_EDI])
|
||||
#endif
|
||||
#define EIP (env->eip)
|
||||
#define DF (env->df)
|
||||
|
||||
#define CC_SRC (env->cc_src)
|
||||
#define CC_DST (env->cc_dst)
|
||||
#define CC_OP (env->cc_op)
|
||||
|
||||
/* float macros */
|
||||
#define FT0 (env->ft0)
|
||||
#define ST0 (env->fpregs[env->fpstt])
|
||||
#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 */
|
||||
int (*compute_c)(void); /* return the C flag */
|
||||
} CCTable;
|
||||
|
||||
extern CCTable cc_table[];
|
||||
|
||||
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);
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
+2300
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
+378
-174
@@ -10,30 +10,122 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gemu.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,48 +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...
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@@ -295,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);
|
||||
}
|
||||
@@ -319,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;
|
||||
@@ -330,14 +445,14 @@ 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)
|
||||
{
|
||||
unsigned int *argv, *envp, *dlinfo;
|
||||
unsigned int *sp;
|
||||
char **alpha_envp;
|
||||
target_ulong *argv, *envp, *dlinfo;
|
||||
target_ulong *sp;
|
||||
|
||||
/*
|
||||
* Force 16 byte alignment here for generality.
|
||||
@@ -350,49 +465,42 @@ static unsigned int * create_elf_tables(char *p, int argc, int envc,
|
||||
sp -= argc+1;
|
||||
argv = sp;
|
||||
if (!ibcs) {
|
||||
put_user(envp,--sp);
|
||||
put_user(argv,--sp);
|
||||
put_user(tswapl((target_ulong)envp),--sp);
|
||||
put_user(tswapl((target_ulong)argv),--sp);
|
||||
}
|
||||
alpha_envp = (char **)malloc((envc+1) * sizeof(char *));
|
||||
|
||||
#define NEW_AUX_ENT(id, val) \
|
||||
put_user ((id), dlinfo++); \
|
||||
put_user ((val), dlinfo++)
|
||||
put_user (tswapl(id), dlinfo++); \
|
||||
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
|
||||
put_user((unsigned int)argc,--sp);
|
||||
put_user(tswapl(argc),--sp);
|
||||
info->arg_start = (unsigned int)((unsigned long)p & 0xffffffff);
|
||||
while (argc-->0) {
|
||||
put_user(p,argv++);
|
||||
put_user(tswapl((target_ulong)p),argv++);
|
||||
while (get_user(p++)) /* nothing */ ;
|
||||
}
|
||||
put_user(0,argv);
|
||||
info->arg_end = info->env_start = (unsigned int)((unsigned long)p & 0xffffffff);
|
||||
__environ = alpha_envp;
|
||||
while (envc-->0) {
|
||||
*alpha_envp++ = (char *)p;
|
||||
put_user(p,envp++);
|
||||
put_user(tswapl((target_ulong)p),envp++);
|
||||
while (get_user(p++)) /* nothing */ ;
|
||||
}
|
||||
put_user(0,envp);
|
||||
*alpha_envp = 0;
|
||||
info->env_end = (unsigned int)((unsigned long)p & 0xffffffff);
|
||||
return sp;
|
||||
}
|
||||
@@ -405,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;
|
||||
@@ -416,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 *)
|
||||
@@ -443,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) {
|
||||
@@ -455,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);
|
||||
@@ -468,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) {
|
||||
@@ -483,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 */
|
||||
@@ -528,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);
|
||||
|
||||
@@ -542,21 +662,70 @@ 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... */
|
||||
|
||||
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
struct image_info * info)
|
||||
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)
|
||||
{
|
||||
struct elfhdr elf_ex;
|
||||
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;
|
||||
@@ -571,6 +740,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
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);
|
||||
@@ -581,7 +751,6 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
|
||||
/* First of all, some simple consistency checks */
|
||||
if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
|
||||
(! elf_check_arch(elf_ex.e_machine))) {
|
||||
@@ -608,6 +777,12 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
return -errno;
|
||||
}
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
elf_ppnt = elf_phdata;
|
||||
for (i=0; i<elf_ex.e_phnum; i++, elf_ppnt++) {
|
||||
bswap_phdr(elf_ppnt);
|
||||
}
|
||||
#endif
|
||||
elf_ppnt = elf_phdata;
|
||||
|
||||
elf_bss = 0;
|
||||
@@ -635,7 +810,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
* 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);
|
||||
@@ -643,12 +818,9 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
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");
|
||||
@@ -670,7 +842,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
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;
|
||||
}
|
||||
@@ -770,56 +942,85 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
* 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);
|
||||
@@ -842,6 +1043,9 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
|
||||
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);
|
||||
|
||||
@@ -853,7 +1057,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
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);
|
||||
@@ -886,8 +1090,8 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
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
|
||||
@@ -909,13 +1113,13 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs,
|
||||
|
||||
|
||||
int elf_exec(const char * filename, char ** argv, char ** envp,
|
||||
struct pt_regs * regs, struct image_info *infop)
|
||||
struct target_pt_regs * regs, struct image_info *infop)
|
||||
{
|
||||
struct linux_binprm bprm;
|
||||
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);
|
||||
|
||||
+17
-1
@@ -1,7 +1,7 @@
|
||||
/* emulated ioctl list */
|
||||
|
||||
IOCTL(TCGETS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
|
||||
IOCTL(TCGETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
|
||||
IOCTL(TCSETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
|
||||
IOCTL(TCSETSF, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
|
||||
IOCTL(TCSETSW, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
|
||||
IOCTL(TIOCGWINSZ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_winsize)))
|
||||
@@ -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))
|
||||
@@ -199,8 +208,12 @@
|
||||
IOCTL(SNDCTL_TMR_METRONOME, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(SNDCTL_TMR_SELECT, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(SNDCTL_TMR_SOURCE, IOC_RW, MK_PTR(TYPE_INT))
|
||||
#if 0
|
||||
/* we invalidate these defines because they have a same number as
|
||||
termios ioctls */
|
||||
IOCTL(SNDCTL_TMR_START, 0, TYPE_NULL)
|
||||
IOCTL(SNDCTL_TMR_STOP, 0, TYPE_NULL)
|
||||
#endif
|
||||
IOCTL(SNDCTL_TMR_TEMPO, IOC_RW, MK_PTR(TYPE_INT))
|
||||
IOCTL(SNDCTL_TMR_TIMEBASE, IOC_RW, MK_PTR(TYPE_INT))
|
||||
|
||||
@@ -280,3 +293,6 @@
|
||||
IOCTL(HDIO_SET_DMA, 0, TYPE_INT)
|
||||
IOCTL(HDIO_SET_32BIT, 0, TYPE_INT)
|
||||
IOCTL(HDIO_SET_PIO_MODE, 0, TYPE_INT)
|
||||
|
||||
IOCTL(VFAT_IOCTL_READDIR_BOTH, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2)))
|
||||
IOCTL(VFAT_IOCTL_READDIR_SHORT, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2)))
|
||||
|
||||
+336
-219
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* emu main
|
||||
* qemu main
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
@@ -20,16 +20,40 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <elf.h>
|
||||
#include <endian.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "gemu.h"
|
||||
#include "qemu.h"
|
||||
|
||||
#include "i386/hsw_interp.h"
|
||||
#include "cpu-i386.h"
|
||||
|
||||
unsigned long x86_stack_size;
|
||||
unsigned long stktop;
|
||||
#define DEBUG_LOGFILE "/tmp/qemu.log"
|
||||
|
||||
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;
|
||||
|
||||
void gemu_log(const char *fmt, ...)
|
||||
{
|
||||
@@ -40,271 +64,364 @@ void gemu_log(const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* virtual x86 CPU stuff */
|
||||
/***********************************************************/
|
||||
/* CPUX86 core interface */
|
||||
|
||||
extern int invoke_code16(Interp_ENV *, int, int);
|
||||
extern int invoke_code32(Interp_ENV *, int);
|
||||
extern char *e_print_cpuemu_regs(ENVPARAMS, int is32);
|
||||
extern char *e_emu_disasm(ENVPARAMS, unsigned char *org, int is32);
|
||||
extern void init_npu(void);
|
||||
|
||||
Interp_ENV env_global;
|
||||
Interp_ENV *envp_global;
|
||||
|
||||
QWORD EMUtime = 0;
|
||||
|
||||
int CEmuStat = 0;
|
||||
|
||||
long instr_count;
|
||||
|
||||
/* who will initialize this? */
|
||||
unsigned long io_bitmap[IO_BITMAP_SIZE+1];
|
||||
|
||||
/* debug flag, 0=disable 1..9=level */
|
||||
int d_emu = 0;
|
||||
|
||||
unsigned long CRs[5] =
|
||||
void cpu_x86_outb(CPUX86State *env, int addr, int val)
|
||||
{
|
||||
0x00000013, /* valid bits: 0xe005003f */
|
||||
0x00000000, /* invalid */
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000
|
||||
};
|
||||
|
||||
/*
|
||||
* DR0-3 = linear address of breakpoint 0-3
|
||||
* DR4=5 = reserved
|
||||
* DR6 b0-b3 = BP active
|
||||
* b13 = BD
|
||||
* b14 = BS
|
||||
* b15 = BT
|
||||
* DR7 b0-b1 = G:L bp#0
|
||||
* b2-b3 = G:L bp#1
|
||||
* b4-b5 = G:L bp#2
|
||||
* b6-b7 = G:L bp#3
|
||||
* b8-b9 = GE:LE
|
||||
* b13 = GD
|
||||
* b16-19= LLRW bp#0 LL=00(1),01(2),11(4)
|
||||
* b20-23= LLRW bp#1 RW=00(x),01(w),11(rw)
|
||||
* b24-27= LLRW bp#2
|
||||
* b28-31= LLRW bp#3
|
||||
*/
|
||||
unsigned long DRs[8] =
|
||||
{
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0xffff1ff0,
|
||||
0x00000400,
|
||||
0xffff1ff0,
|
||||
0x00000400
|
||||
};
|
||||
|
||||
unsigned long TRs[2] =
|
||||
{
|
||||
0x00000000,
|
||||
0x00000000
|
||||
};
|
||||
|
||||
void FatalAppExit(UINT wAction, LPCSTR lpText)
|
||||
{
|
||||
fprintf(stderr, "Fatal error '%s' in CPU\n", lpText);
|
||||
exit(1);
|
||||
fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val);
|
||||
}
|
||||
|
||||
int e_debug_check(unsigned char *PC)
|
||||
void cpu_x86_outw(CPUX86State *env, int addr, int val)
|
||||
{
|
||||
register unsigned long d7 = DRs[7];
|
||||
fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val);
|
||||
}
|
||||
|
||||
if (d7&0x03) {
|
||||
if (d7&0x30000) return 0; /* only execute(00) bkp */
|
||||
if ((long)PC==DRs[0]) {
|
||||
e_printf("DBRK: DR0 hit at %p\n",PC);
|
||||
DRs[6] |= 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (d7&0x0c) {
|
||||
if (d7&0x300000) return 0;
|
||||
if ((long)PC==DRs[1]) {
|
||||
e_printf("DBRK: DR1 hit at %p\n",PC);
|
||||
DRs[6] |= 2;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (d7&0x30) {
|
||||
if (d7&0x3000000) return 0;
|
||||
if ((long)PC==DRs[2]) {
|
||||
e_printf("DBRK: DR2 hit at %p\n",PC);
|
||||
DRs[6] |= 4;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (d7&0xc0) {
|
||||
if (d7&0x30000000) return 0;
|
||||
if ((long)PC==DRs[3]) {
|
||||
e_printf("DBRK: DR3 hit at %p\n",PC);
|
||||
DRs[6] |= 8;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
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(CPUX86State *env, int addr)
|
||||
{
|
||||
fprintf(stderr, "inb: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Debug stuff */
|
||||
void logstr(unsigned long mask, const char *fmt,...)
|
||||
int cpu_x86_inw(CPUX86State *env, int addr)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "inw: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unconditional message into debug log and stderr */
|
||||
#undef error
|
||||
void error(const char *fmt, ...)
|
||||
int cpu_x86_inl(CPUX86State *env, int addr)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
fprintf(stderr, "inl: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PortIO(DWORD port, DWORD value, UINT size, BOOL is_write)
|
||||
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
int flags)
|
||||
{
|
||||
fprintf(stderr, "IO: %s port=0x%lx value=0x%lx size=%d",
|
||||
is_write ? "write" : "read", port, value, size);
|
||||
return value;
|
||||
unsigned int e1, e2;
|
||||
e1 = (addr << 16) | (limit & 0xffff);
|
||||
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
|
||||
e2 |= flags;
|
||||
stl((uint8_t *)ptr, e1);
|
||||
stl((uint8_t *)ptr + 4, e2);
|
||||
}
|
||||
|
||||
void LogProcName(WORD wSel, WORD wOff, WORD wAction)
|
||||
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);
|
||||
}
|
||||
|
||||
void INT_handler(int num, void *env)
|
||||
uint64_t gdt_table[6];
|
||||
uint64_t idt_table[256];
|
||||
|
||||
/* only dpl matters as we do only user space emulation */
|
||||
static void set_idt(int n, unsigned int dpl)
|
||||
{
|
||||
fprintf(stderr, "EM86: int %d\n", num);
|
||||
set_gate(idt_table + n, 0, dpl, 0, 0);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
void cpu_loop(CPUX86State *env)
|
||||
{
|
||||
int trapnr;
|
||||
uint8_t *pc;
|
||||
target_siginfo_t info;
|
||||
|
||||
/* XXX: currently we use LDT entries */
|
||||
#define __USER_CS (0x23|4)
|
||||
#define __USER_DS (0x2B|4)
|
||||
for(;;) {
|
||||
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 (env->eflags & VM_MASK) {
|
||||
handle_vm86_fault(env);
|
||||
} 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 EXCP0E_PAGE:
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
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:
|
||||
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;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_ILL_ILLOPN;
|
||||
info._sifields._sigfault._addr = env->eip;
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP_INTERRUPT:
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
default:
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("gemu version 0.1, Copyright (c) 2003 Fabrice Bellard\n"
|
||||
"usage: gemu program [arguments...]\n"
|
||||
printf("qemu version " QEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\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 pt_regs regs1, *regs = ®s1;
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
struct image_info info1, *info = &info1;
|
||||
Interp_ENV *env;
|
||||
|
||||
TaskState ts1, *ts = &ts1;
|
||||
CPUX86State *env;
|
||||
int optind;
|
||||
const char *r;
|
||||
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
|
||||
filename = argv[1];
|
||||
|
||||
loglevel = 0;
|
||||
optind = 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 */
|
||||
if (loglevel) {
|
||||
logfile = fopen(DEBUG_LOGFILE, "w");
|
||||
if (!logfile) {
|
||||
perror(DEBUG_LOGFILE);
|
||||
_exit(1);
|
||||
}
|
||||
setvbuf(logfile, NULL, _IOLBF, 0);
|
||||
}
|
||||
|
||||
/* Zero out regs */
|
||||
memset(regs, 0, sizeof(struct pt_regs));
|
||||
memset(regs, 0, sizeof(struct target_pt_regs));
|
||||
|
||||
/* Zero out image_info */
|
||||
memset(info, 0, sizeof(struct image_info));
|
||||
|
||||
if(elf_exec(filename, argv+1, __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 0
|
||||
printf("start_brk 0x%08lx\n" , info->start_brk);
|
||||
printf("end_code 0x%08lx\n" , info->end_code);
|
||||
printf("start_code 0x%08lx\n" , info->start_code);
|
||||
printf("end_data 0x%08lx\n" , info->end_data);
|
||||
printf("start_stack 0x%08lx\n" , info->start_stack);
|
||||
printf("brk 0x%08lx\n" , info->brk);
|
||||
printf("esp 0x%08lx\n" , regs->esp);
|
||||
printf("eip 0x%08lx\n" , regs->eip);
|
||||
#endif
|
||||
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);
|
||||
fprintf(logfile, "end_data 0x%08lx\n" , info->end_data);
|
||||
fprintf(logfile, "start_stack 0x%08lx\n" , info->start_stack);
|
||||
fprintf(logfile, "brk 0x%08lx\n" , info->brk);
|
||||
fprintf(logfile, "esp 0x%08lx\n" , regs->esp);
|
||||
fprintf(logfile, "eip 0x%08lx\n" , regs->eip);
|
||||
}
|
||||
|
||||
target_set_brk((char *)info->brk);
|
||||
syscall_init();
|
||||
signal_init();
|
||||
|
||||
env = &env_global;
|
||||
envp_global = env;
|
||||
memset(env, 0, sizeof(Interp_ENV));
|
||||
global_env = env;
|
||||
|
||||
env->rax.e = regs->eax;
|
||||
env->rbx.e = regs->ebx;
|
||||
env->rcx.e = regs->ecx;
|
||||
env->rdx.e = regs->edx;
|
||||
env->rsi.esi = regs->esi;
|
||||
env->rdi.edi = regs->edi;
|
||||
env->rbp.ebp = regs->ebp;
|
||||
env->rsp.esp = regs->esp;
|
||||
env->cs.cs = __USER_CS;
|
||||
env->ds.ds = __USER_DS;
|
||||
env->es.es = __USER_DS;
|
||||
env->ss.ss = __USER_DS;
|
||||
env->fs.fs = __USER_DS;
|
||||
env->gs.gs = __USER_DS;
|
||||
env->trans_addr = regs->eip;
|
||||
/* 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;
|
||||
env->regs[R_ECX] = regs->ecx;
|
||||
env->regs[R_EDX] = regs->edx;
|
||||
env->regs[R_ESI] = regs->esi;
|
||||
env->regs[R_EDI] = regs->edi;
|
||||
env->regs[R_EBP] = regs->ebp;
|
||||
env->regs[R_ESP] = regs->esp;
|
||||
env->eip = regs->eip;
|
||||
|
||||
LDT[__USER_CS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32;
|
||||
LDT[__USER_CS >> 3].dwSelLimit = 0xfffff;
|
||||
LDT[__USER_CS >> 3].lpSelBase = NULL;
|
||||
/* 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);
|
||||
|
||||
LDT[__USER_DS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32;
|
||||
LDT[__USER_DS >> 3].dwSelLimit = 0xfffff;
|
||||
LDT[__USER_DS >> 3].lpSelBase = NULL;
|
||||
init_npu();
|
||||
/* linux segment setup */
|
||||
env->gdt.base = (void *)gdt_table;
|
||||
env->gdt.limit = sizeof(gdt_table) - 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);
|
||||
cpu_x86_load_seg(env, R_SS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_FS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_GS, __USER_DS);
|
||||
|
||||
for(;;) {
|
||||
int err;
|
||||
uint8_t *pc;
|
||||
|
||||
err = invoke_code32(env, -1);
|
||||
env->trans_addr = env->return_addr;
|
||||
pc = env->seg_regs[0] + env->trans_addr;
|
||||
switch(err) {
|
||||
case EXCP0D_GPF:
|
||||
if (pc[0] == 0xcd && pc[1] == 0x80) {
|
||||
/* syscall */
|
||||
env->trans_addr += 2;
|
||||
env->rax.e = do_syscall(env->rax.e,
|
||||
env->rbx.e,
|
||||
env->rcx.e,
|
||||
env->rdx.e,
|
||||
env->rsi.esi,
|
||||
env->rdi.edi,
|
||||
env->rbp.ebp);
|
||||
} else {
|
||||
goto trap_error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
trap_error:
|
||||
fprintf(stderr, "GEMU: Unknown error %d, aborting\n", err);
|
||||
d_emu = 9;
|
||||
fprintf(stderr, "%s\n%s\n",
|
||||
e_print_cpuemu_regs(env, 1),
|
||||
e_emu_disasm(env,pc,1));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
cpu_loop(env);
|
||||
/* never exits */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+64
-19
@@ -3,23 +3,13 @@
|
||||
|
||||
#include "thunk.h"
|
||||
|
||||
struct pt_regs {
|
||||
long ebx;
|
||||
long ecx;
|
||||
long edx;
|
||||
long esi;
|
||||
long edi;
|
||||
long ebp;
|
||||
long eax;
|
||||
int xds;
|
||||
int xes;
|
||||
long orig_eax;
|
||||
long eip;
|
||||
int xcs;
|
||||
long eflags;
|
||||
long esp;
|
||||
int xss;
|
||||
};
|
||||
#include <signal.h>
|
||||
#include "syscall_defs.h"
|
||||
|
||||
#ifdef TARGET_I386
|
||||
#include "cpu-i386.h"
|
||||
#include "syscall-i386.h"
|
||||
#endif
|
||||
|
||||
/* This struct is used to hold certain information about the image.
|
||||
* Basically, it replicates in user space what would be certain
|
||||
@@ -43,15 +33,70 @@ 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 pt_regs * regs, struct image_info *infop);
|
||||
struct target_pt_regs * regs, struct image_info *infop);
|
||||
|
||||
void target_set_brk(char *new_brk);
|
||||
void syscall_init(void);
|
||||
long do_syscall(int num, long arg1, long arg2, long arg3,
|
||||
long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
||||
long arg4, long arg5, long arg6);
|
||||
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
|
||||
extern CPUX86State *global_env;
|
||||
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
|
||||
|
||||
+854
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Emulation of Linux signal handling
|
||||
* Emulation of Linux signals
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
@@ -19,22 +19,48 @@
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
/* Algorithm strongly inspired from em86 : we queue the signals so
|
||||
that we can handle them at precise points in the emulated code. */
|
||||
#ifdef __ia64__
|
||||
#undef uc_mcontext
|
||||
#undef uc_sigmask
|
||||
#undef uc_stack
|
||||
#undef uc_link
|
||||
#endif
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
//#define DEBUG_SIGNAL
|
||||
|
||||
#define MAX_SIGQUEUE_SIZE 1024
|
||||
|
||||
struct sigqueue {
|
||||
struct sigqueue *next;
|
||||
target_siginfo_t info;
|
||||
};
|
||||
|
||||
struct emulated_sigaction {
|
||||
struct target_sigaction sa;
|
||||
int nb_pending;
|
||||
struct target_siginfo info;
|
||||
int pending; /* true if signal is pending */
|
||||
struct sigqueue *first;
|
||||
struct sigqueue info; /* in order to always have memory for the
|
||||
first signal, we put it here */
|
||||
};
|
||||
|
||||
struct emulated_sigaction sigact_table[NSIG];
|
||||
int signal_pending;
|
||||
static struct emulated_sigaction sigact_table[TARGET_NSIG];
|
||||
static struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
|
||||
static struct sigqueue *first_free; /* first free siginfo queue entry */
|
||||
static int signal_pending; /* non zero if a signal may be pending */
|
||||
|
||||
static void host_signal_handler(int host_signum, siginfo_t *info,
|
||||
void *puc);
|
||||
|
||||
/* XXX: do it properly */
|
||||
static inline int host_to_target_signal(int sig)
|
||||
{
|
||||
return sig;
|
||||
@@ -45,61 +71,858 @@ static inline int target_to_host_signal(int sig)
|
||||
return sig;
|
||||
}
|
||||
|
||||
void host_to_target_sigset(target_sigset_t *d, sigset_t *s)
|
||||
{
|
||||
int i;
|
||||
for(i = 0;i < TARGET_NSIG_WORDS; i++) {
|
||||
d->sig[i] = tswapl(((unsigned long *)s)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void target_to_host_sigset(sigset_t *d, target_sigset_t *s)
|
||||
{
|
||||
int i;
|
||||
for(i = 0;i < TARGET_NSIG_WORDS; i++) {
|
||||
((unsigned long *)d)[i] = tswapl(s->sig[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void host_to_target_old_sigset(target_ulong *old_sigset,
|
||||
const sigset_t *sigset)
|
||||
{
|
||||
*old_sigset = tswap32(*(unsigned long *)sigset & 0xffffffff);
|
||||
}
|
||||
|
||||
void target_to_host_old_sigset(sigset_t *sigset,
|
||||
const target_ulong *old_sigset)
|
||||
{
|
||||
sigemptyset(sigset);
|
||||
*(unsigned long *)sigset = tswapl(*old_sigset);
|
||||
}
|
||||
|
||||
/* siginfo conversion */
|
||||
|
||||
static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
|
||||
const siginfo_t *info)
|
||||
{
|
||||
int sig;
|
||||
sig = host_to_target_signal(info->si_signo);
|
||||
tinfo->si_signo = sig;
|
||||
tinfo->si_errno = 0;
|
||||
tinfo->si_code = 0;
|
||||
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;
|
||||
} else if (sig >= TARGET_SIGRTMIN) {
|
||||
tinfo->_sifields._rt._pid = info->si_pid;
|
||||
tinfo->_sifields._rt._uid = info->si_uid;
|
||||
/* XXX: potential problem if 64 bit */
|
||||
tinfo->_sifields._rt._sigval.sival_ptr =
|
||||
(target_ulong)info->si_value.sival_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void tswap_siginfo(target_siginfo_t *tinfo,
|
||||
const target_siginfo_t *info)
|
||||
{
|
||||
int sig;
|
||||
sig = info->si_signo;
|
||||
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 || sig == SIGTRAP) {
|
||||
tinfo->_sifields._sigfault._addr =
|
||||
tswapl(info->_sifields._sigfault._addr);
|
||||
} else if (sig >= TARGET_SIGRTMIN) {
|
||||
tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid);
|
||||
tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid);
|
||||
tinfo->_sifields._rt._sigval.sival_ptr =
|
||||
tswapl(info->_sifields._rt._sigval.sival_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
|
||||
{
|
||||
host_to_target_siginfo_noswap(tinfo, info);
|
||||
tswap_siginfo(tinfo, tinfo);
|
||||
}
|
||||
|
||||
/* XXX: we support only POSIX RT signals are used. */
|
||||
/* XXX: find a solution for 64 bit (additionnal malloced data is needed) */
|
||||
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
|
||||
{
|
||||
info->si_signo = tswap32(tinfo->si_signo);
|
||||
info->si_errno = tswap32(tinfo->si_errno);
|
||||
info->si_code = tswap32(tinfo->si_code);
|
||||
info->si_pid = tswap32(tinfo->_sifields._rt._pid);
|
||||
info->si_uid = tswap32(tinfo->_sifields._rt._uid);
|
||||
info->si_value.sival_ptr =
|
||||
(void *)tswapl(tinfo->_sifields._rt._sigval.sival_ptr);
|
||||
}
|
||||
|
||||
void signal_init(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
int i;
|
||||
|
||||
/* set all host signal handlers */
|
||||
sigemptyset(&act.sa_mask);
|
||||
/* set all host signal handlers. ALL signals are blocked during
|
||||
the handlers to serialize them. */
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_sigaction = host_signal_handler;
|
||||
for(i = 1; i < NSIG; i++) {
|
||||
sigaction(i, &sa, NULL);
|
||||
sigaction(i, &act, NULL);
|
||||
}
|
||||
|
||||
memset(sigact_table, 0, sizeof(sigact_table));
|
||||
|
||||
first_free = &sigqueue_table[0];
|
||||
for(i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++)
|
||||
sigqueue_table[i].next = &sigqueue_table[i + 1];
|
||||
sigqueue_table[MAX_SIGQUEUE_SIZE - 1].next = NULL;
|
||||
}
|
||||
|
||||
/* signal queue handling */
|
||||
|
||||
static inline struct sigqueue *alloc_sigqueue(void)
|
||||
{
|
||||
struct sigqueue *q = first_free;
|
||||
if (!q)
|
||||
return NULL;
|
||||
first_free = q->next;
|
||||
return q;
|
||||
}
|
||||
|
||||
static inline void free_sigqueue(struct sigqueue *q)
|
||||
{
|
||||
q->next = first_free;
|
||||
first_free = q;
|
||||
}
|
||||
|
||||
/* abort execution with signal */
|
||||
void __attribute((noreturn)) force_sig(int sig)
|
||||
{
|
||||
int host_sig;
|
||||
host_sig = target_to_host_signal(sig);
|
||||
fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
|
||||
sig, strsignal(host_sig));
|
||||
#if 1
|
||||
_exit(-host_sig);
|
||||
#else
|
||||
{
|
||||
struct sigaction act;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_sigaction = SIG_DFL;
|
||||
sigaction(SIGABRT, &act, NULL);
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* queue a signal so that it will be send to the virtual CPU as soon
|
||||
as possible */
|
||||
int queue_signal(int sig, target_siginfo_t *info)
|
||||
{
|
||||
struct emulated_sigaction *k;
|
||||
struct sigqueue *q, **pq;
|
||||
target_ulong handler;
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "queue_signal: sig=%d\n",
|
||||
sig);
|
||||
#endif
|
||||
k = &sigact_table[sig - 1];
|
||||
handler = k->sa._sa_handler;
|
||||
if (handler == TARGET_SIG_DFL) {
|
||||
/* default handler : ignore some signal. The other are fatal */
|
||||
if (sig != TARGET_SIGCHLD &&
|
||||
sig != TARGET_SIGURG &&
|
||||
sig != TARGET_SIGWINCH) {
|
||||
force_sig(sig);
|
||||
} else {
|
||||
return 0; /* indicate ignored */
|
||||
}
|
||||
} else if (handler == TARGET_SIG_IGN) {
|
||||
/* ignore signal */
|
||||
return 0;
|
||||
} else if (handler == TARGET_SIG_ERR) {
|
||||
force_sig(sig);
|
||||
} else {
|
||||
pq = &k->first;
|
||||
if (sig < TARGET_SIGRTMIN) {
|
||||
/* if non real time signal, we queue exactly one signal */
|
||||
if (!k->pending)
|
||||
q = &k->info;
|
||||
else
|
||||
return 0;
|
||||
} else {
|
||||
if (!k->pending) {
|
||||
/* first signal */
|
||||
q = &k->info;
|
||||
} else {
|
||||
q = alloc_sigqueue();
|
||||
if (!q)
|
||||
return -EAGAIN;
|
||||
while (*pq != NULL)
|
||||
pq = &(*pq)->next;
|
||||
}
|
||||
}
|
||||
*pq = q;
|
||||
q->info = *info;
|
||||
q->next = NULL;
|
||||
k->pending = 1;
|
||||
/* signal that a new signal is pending */
|
||||
signal_pending = 1;
|
||||
return 1; /* indicates that the signal was queued */
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
#ifdef __i386__
|
||||
static void dump_regs(struct ucontext *uc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"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]);
|
||||
}
|
||||
#else
|
||||
static void dump_regs(struct ucontext *uc)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static void host_signal_handler(int host_signum, siginfo_t *info,
|
||||
void *puc)
|
||||
{
|
||||
struct ucontext *uc = puc;
|
||||
int signum;
|
||||
int sig;
|
||||
target_siginfo_t tinfo;
|
||||
|
||||
/* the CPU emulator uses some host signals to detect exceptions,
|
||||
we we forward to it some signals */
|
||||
if (host_signum == SIGSEGV || host_signum == SIGBUS) {
|
||||
if (cpu_x86_signal_handler(host_signum, info, puc))
|
||||
return;
|
||||
}
|
||||
|
||||
/* get target signal number */
|
||||
signum = host_to_target(host_signum);
|
||||
if (signum >= TARGET_NSIG)
|
||||
sig = host_to_target_signal(host_signum);
|
||||
if (sig < 1 || sig > TARGET_NSIG)
|
||||
return;
|
||||
/* we save the old mask */
|
||||
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "qemu: got signal %d\n", sig);
|
||||
dump_regs(puc);
|
||||
#endif
|
||||
host_to_target_siginfo_noswap(&tinfo, info);
|
||||
if (queue_signal(sig, &tinfo) == 1) {
|
||||
/* interrupt the virtual CPU as soon as possible */
|
||||
cpu_x86_interrupt(global_env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void process_pending_signals(void)
|
||||
int do_sigaction(int sig, const struct target_sigaction *act,
|
||||
struct target_sigaction *oact)
|
||||
{
|
||||
int signum;
|
||||
target_ulong _sa_handler;
|
||||
struct emulated_sigaction *k;
|
||||
|
||||
struct emulated_sigaction *esig;
|
||||
if (sig < 1 || sig > TARGET_NSIG)
|
||||
return -EINVAL;
|
||||
k = &sigact_table[sig - 1];
|
||||
#if defined(DEBUG_SIGNAL) && 0
|
||||
fprintf(stderr, "sigaction sig=%d act=0x%08x, oact=0x%08x\n",
|
||||
sig, (int)act, (int)oact);
|
||||
#endif
|
||||
if (oact) {
|
||||
oact->_sa_handler = tswapl(k->sa._sa_handler);
|
||||
oact->sa_flags = tswapl(k->sa.sa_flags);
|
||||
oact->sa_restorer = tswapl(k->sa.sa_restorer);
|
||||
oact->sa_mask = k->sa.sa_mask;
|
||||
}
|
||||
if (act) {
|
||||
k->sa._sa_handler = tswapl(act->_sa_handler);
|
||||
k->sa.sa_flags = tswapl(act->sa_flags);
|
||||
k->sa.sa_restorer = tswapl(act->sa_restorer);
|
||||
k->sa.sa_mask = act->sa_mask;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TARGET_I386
|
||||
|
||||
/* from the Linux kernel */
|
||||
|
||||
struct target_fpreg {
|
||||
uint16_t significand[4];
|
||||
uint16_t exponent;
|
||||
};
|
||||
|
||||
struct target_fpxreg {
|
||||
uint16_t significand[4];
|
||||
uint16_t exponent;
|
||||
uint16_t padding[3];
|
||||
};
|
||||
|
||||
struct target_xmmreg {
|
||||
target_ulong element[4];
|
||||
};
|
||||
|
||||
struct target_fpstate {
|
||||
/* Regular FPU environment */
|
||||
target_ulong cw;
|
||||
target_ulong sw;
|
||||
target_ulong tag;
|
||||
target_ulong ipoff;
|
||||
target_ulong cssel;
|
||||
target_ulong dataoff;
|
||||
target_ulong datasel;
|
||||
struct target_fpreg _st[8];
|
||||
uint16_t status;
|
||||
uint16_t magic; /* 0xffff = regular FPU data only */
|
||||
|
||||
/* FXSR FPU environment */
|
||||
target_ulong _fxsr_env[6]; /* FXSR FPU env is ignored */
|
||||
target_ulong mxcsr;
|
||||
target_ulong reserved;
|
||||
struct target_fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */
|
||||
struct target_xmmreg _xmm[8];
|
||||
target_ulong padding[56];
|
||||
};
|
||||
|
||||
#define X86_FXSR_MAGIC 0x0000
|
||||
|
||||
struct target_sigcontext {
|
||||
uint16_t gs, __gsh;
|
||||
uint16_t fs, __fsh;
|
||||
uint16_t es, __esh;
|
||||
uint16_t ds, __dsh;
|
||||
target_ulong edi;
|
||||
target_ulong esi;
|
||||
target_ulong ebp;
|
||||
target_ulong esp;
|
||||
target_ulong ebx;
|
||||
target_ulong edx;
|
||||
target_ulong ecx;
|
||||
target_ulong eax;
|
||||
target_ulong trapno;
|
||||
target_ulong err;
|
||||
target_ulong eip;
|
||||
uint16_t cs, __csh;
|
||||
target_ulong eflags;
|
||||
target_ulong esp_at_signal;
|
||||
uint16_t ss, __ssh;
|
||||
target_ulong fpstate; /* pointer */
|
||||
target_ulong oldmask;
|
||||
target_ulong cr2;
|
||||
};
|
||||
|
||||
typedef struct target_sigaltstack {
|
||||
target_ulong ss_sp;
|
||||
int ss_flags;
|
||||
target_ulong ss_size;
|
||||
} target_stack_t;
|
||||
|
||||
struct target_ucontext {
|
||||
target_ulong uc_flags;
|
||||
target_ulong uc_link;
|
||||
target_stack_t uc_stack;
|
||||
struct target_sigcontext uc_mcontext;
|
||||
target_sigset_t uc_sigmask; /* mask last for extensibility */
|
||||
};
|
||||
|
||||
struct sigframe
|
||||
{
|
||||
target_ulong pretcode;
|
||||
int sig;
|
||||
struct target_sigcontext sc;
|
||||
struct target_fpstate fpstate;
|
||||
target_ulong extramask[TARGET_NSIG_WORDS-1];
|
||||
char retcode[8];
|
||||
};
|
||||
|
||||
struct rt_sigframe
|
||||
{
|
||||
target_ulong pretcode;
|
||||
int sig;
|
||||
target_ulong pinfo;
|
||||
target_ulong puc;
|
||||
struct target_siginfo info;
|
||||
struct target_ucontext uc;
|
||||
struct target_fpstate fpstate;
|
||||
char retcode[8];
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up a signal frame.
|
||||
*/
|
||||
|
||||
#define __put_user(x,ptr)\
|
||||
({\
|
||||
int size = sizeof(*ptr);\
|
||||
switch(size) {\
|
||||
case 1:\
|
||||
stb(ptr, (typeof(*ptr))(x));\
|
||||
break;\
|
||||
case 2:\
|
||||
stw(ptr, (typeof(*ptr))(x));\
|
||||
break;\
|
||||
case 4:\
|
||||
stl(ptr, (typeof(*ptr))(x));\
|
||||
break;\
|
||||
case 8:\
|
||||
stq(ptr, (typeof(*ptr))(x));\
|
||||
break;\
|
||||
default:\
|
||||
abort();\
|
||||
}\
|
||||
0;\
|
||||
})
|
||||
|
||||
#define get_user(val, ptr) (typeof(*ptr))(*(ptr))
|
||||
|
||||
|
||||
#define __copy_to_user(dst, src, size)\
|
||||
({\
|
||||
memcpy(dst, src, size);\
|
||||
0;\
|
||||
})
|
||||
|
||||
static inline int copy_siginfo_to_user(target_siginfo_t *tinfo,
|
||||
const target_siginfo_t *info)
|
||||
{
|
||||
tswap_siginfo(tinfo, info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: save x87 state */
|
||||
static int
|
||||
setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
|
||||
CPUX86State *env, unsigned long mask)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err |= __put_user(env->segs[R_GS], (unsigned int *)&sc->gs);
|
||||
err |= __put_user(env->segs[R_FS], (unsigned int *)&sc->fs);
|
||||
err |= __put_user(env->segs[R_ES], (unsigned int *)&sc->es);
|
||||
err |= __put_user(env->segs[R_DS], (unsigned int *)&sc->ds);
|
||||
err |= __put_user(env->regs[R_EDI], &sc->edi);
|
||||
err |= __put_user(env->regs[R_ESI], &sc->esi);
|
||||
err |= __put_user(env->regs[R_EBP], &sc->ebp);
|
||||
err |= __put_user(env->regs[R_ESP], &sc->esp);
|
||||
err |= __put_user(env->regs[R_EBX], &sc->ebx);
|
||||
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(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);
|
||||
err |= __put_user(env->regs[R_ESP], &sc->esp_at_signal);
|
||||
err |= __put_user(env->segs[R_SS], (unsigned int *)&sc->ss);
|
||||
#if 0
|
||||
tmp = save_i387(fpstate);
|
||||
if (tmp < 0)
|
||||
err = 1;
|
||||
else
|
||||
err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate);
|
||||
#else
|
||||
err |= __put_user(0, &sc->fpstate);
|
||||
#endif
|
||||
/* non-iBCS2 extensions.. */
|
||||
err |= __put_user(mask, &sc->oldmask);
|
||||
err |= __put_user(env->cr2, &sc->cr2);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine which stack to use..
|
||||
*/
|
||||
|
||||
static inline void *
|
||||
get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size)
|
||||
{
|
||||
unsigned long esp;
|
||||
|
||||
/* Default to using normal stack */
|
||||
esp = env->regs[R_ESP];
|
||||
#if 0
|
||||
/* This is the X/Open sanctioned signal stack switching. */
|
||||
if (ka->sa.sa_flags & SA_ONSTACK) {
|
||||
if (sas_ss_flags(esp) == 0)
|
||||
esp = current->sas_ss_sp + current->sas_ss_size;
|
||||
}
|
||||
|
||||
/* This is the legacy signal stack switching. */
|
||||
else if ((regs->xss & 0xffff) != __USER_DS &&
|
||||
!(ka->sa.sa_flags & SA_RESTORER) &&
|
||||
ka->sa.sa_restorer) {
|
||||
esp = (unsigned long) ka->sa.sa_restorer;
|
||||
}
|
||||
#endif
|
||||
return (void *)((esp - frame_size) & -8ul);
|
||||
}
|
||||
|
||||
static void setup_frame(int sig, struct emulated_sigaction *ka,
|
||||
target_sigset_t *set, CPUX86State *env)
|
||||
{
|
||||
struct sigframe *frame;
|
||||
int err = 0;
|
||||
|
||||
frame = get_sigframe(ka, env, sizeof(*frame));
|
||||
|
||||
#if 0
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
||||
goto give_sigsegv;
|
||||
#endif
|
||||
err |= __put_user((/*current->exec_domain
|
||||
&& current->exec_domain->signal_invmap
|
||||
&& sig < 32
|
||||
? current->exec_domain->signal_invmap[sig]
|
||||
: */ sig),
|
||||
&frame->sig);
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0]);
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
if (TARGET_NSIG_WORDS > 1) {
|
||||
err |= __copy_to_user(frame->extramask, &set->sig[1],
|
||||
sizeof(frame->extramask));
|
||||
}
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
/* Set up to return from userspace. If provided, use a stub
|
||||
already in userspace. */
|
||||
if (ka->sa.sa_flags & TARGET_SA_RESTORER) {
|
||||
err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
|
||||
} else {
|
||||
err |= __put_user(frame->retcode, &frame->pretcode);
|
||||
/* This is popl %eax ; movl $,%eax ; int $0x80 */
|
||||
err |= __put_user(0xb858, (short *)(frame->retcode+0));
|
||||
err |= __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2));
|
||||
err |= __put_user(0x80cd, (short *)(frame->retcode+6));
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
/* Set up registers for signal handler */
|
||||
env->regs[R_ESP] = (unsigned long) frame;
|
||||
env->eip = (unsigned long) ka->sa._sa_handler;
|
||||
|
||||
cpu_x86_load_seg(env, R_DS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_ES, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_SS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_CS, __USER_CS);
|
||||
env->eflags &= ~TF_MASK;
|
||||
|
||||
return;
|
||||
|
||||
give_sigsegv:
|
||||
if (sig == TARGET_SIGSEGV)
|
||||
ka->sa._sa_handler = TARGET_SIG_DFL;
|
||||
force_sig(TARGET_SIGSEGV /* , current */);
|
||||
}
|
||||
|
||||
static void setup_rt_frame(int sig, struct emulated_sigaction *ka,
|
||||
target_siginfo_t *info,
|
||||
target_sigset_t *set, CPUX86State *env)
|
||||
{
|
||||
struct rt_sigframe *frame;
|
||||
int err = 0;
|
||||
|
||||
frame = get_sigframe(ka, env, sizeof(*frame));
|
||||
|
||||
#if 0
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
||||
goto give_sigsegv;
|
||||
#endif
|
||||
|
||||
err |= __put_user((/*current->exec_domain
|
||||
&& current->exec_domain->signal_invmap
|
||||
&& sig < 32
|
||||
? current->exec_domain->signal_invmap[sig]
|
||||
: */sig),
|
||||
&frame->sig);
|
||||
err |= __put_user((target_ulong)&frame->info, &frame->pinfo);
|
||||
err |= __put_user((target_ulong)&frame->uc, &frame->puc);
|
||||
err |= copy_siginfo_to_user(&frame->info, info);
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
/* Create the ucontext. */
|
||||
err |= __put_user(0, &frame->uc.uc_flags);
|
||||
err |= __put_user(0, &frame->uc.uc_link);
|
||||
err |= __put_user(/*current->sas_ss_sp*/ 0, &frame->uc.uc_stack.ss_sp);
|
||||
err |= __put_user(/* sas_ss_flags(regs->esp) */ 0,
|
||||
&frame->uc.uc_stack.ss_flags);
|
||||
err |= __put_user(/* current->sas_ss_size */ 0, &frame->uc.uc_stack.ss_size);
|
||||
err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
|
||||
env, set->sig[0]);
|
||||
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
/* Set up to return from userspace. If provided, use a stub
|
||||
already in userspace. */
|
||||
if (ka->sa.sa_flags & TARGET_SA_RESTORER) {
|
||||
err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
|
||||
} else {
|
||||
err |= __put_user(frame->retcode, &frame->pretcode);
|
||||
/* This is movl $,%eax ; int $0x80 */
|
||||
err |= __put_user(0xb8, (char *)(frame->retcode+0));
|
||||
err |= __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1));
|
||||
err |= __put_user(0x80cd, (short *)(frame->retcode+5));
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
/* Set up registers for signal handler */
|
||||
env->regs[R_ESP] = (unsigned long) frame;
|
||||
env->eip = (unsigned long) ka->sa._sa_handler;
|
||||
|
||||
cpu_x86_load_seg(env, R_DS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_ES, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_SS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_CS, __USER_CS);
|
||||
env->eflags &= ~TF_MASK;
|
||||
|
||||
return;
|
||||
|
||||
give_sigsegv:
|
||||
if (sig == TARGET_SIGSEGV)
|
||||
ka->sa._sa_handler = TARGET_SIG_DFL;
|
||||
force_sig(TARGET_SIGSEGV /* , current */);
|
||||
}
|
||||
|
||||
static int
|
||||
restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax)
|
||||
{
|
||||
unsigned int err = 0;
|
||||
|
||||
|
||||
|
||||
#define COPY(x) err |= __get_user(regs->x, &sc->x)
|
||||
|
||||
#define COPY_SEG(seg) \
|
||||
{ unsigned short tmp; \
|
||||
err |= __get_user(tmp, &sc->seg); \
|
||||
regs->x##seg = tmp; }
|
||||
|
||||
#define COPY_SEG_STRICT(seg) \
|
||||
{ unsigned short tmp; \
|
||||
err |= __get_user(tmp, &sc->seg); \
|
||||
regs->x##seg = tmp|3; }
|
||||
|
||||
#define GET_SEG(seg) \
|
||||
{ unsigned short tmp; \
|
||||
err |= __get_user(tmp, &sc->seg); \
|
||||
loadsegment(seg,tmp); }
|
||||
|
||||
cpu_x86_load_seg(env, R_GS, lduw(&sc->gs));
|
||||
cpu_x86_load_seg(env, R_FS, lduw(&sc->fs));
|
||||
cpu_x86_load_seg(env, R_ES, lduw(&sc->es));
|
||||
cpu_x86_load_seg(env, R_DS, lduw(&sc->ds));
|
||||
|
||||
env->regs[R_EDI] = ldl(&sc->edi);
|
||||
env->regs[R_ESI] = ldl(&sc->esi);
|
||||
env->regs[R_EBP] = ldl(&sc->ebp);
|
||||
env->regs[R_ESP] = ldl(&sc->esp);
|
||||
env->regs[R_EBX] = ldl(&sc->ebx);
|
||||
env->regs[R_EDX] = ldl(&sc->edx);
|
||||
env->regs[R_ECX] = ldl(&sc->ecx);
|
||||
env->eip = ldl(&sc->eip);
|
||||
|
||||
cpu_x86_load_seg(env, R_CS, lduw(&sc->cs) | 3);
|
||||
cpu_x86_load_seg(env, R_SS, lduw(&sc->ss) | 3);
|
||||
|
||||
{
|
||||
unsigned int tmpflags;
|
||||
tmpflags = ldl(&sc->eflags);
|
||||
env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5);
|
||||
// regs->orig_eax = -1; /* disable syscall checks */
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
struct _fpstate * buf;
|
||||
err |= __get_user(buf, &sc->fpstate);
|
||||
if (buf) {
|
||||
if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
|
||||
goto badframe;
|
||||
err |= restore_i387(buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
*peax = ldl(&sc->eax);
|
||||
return err;
|
||||
#if 0
|
||||
badframe:
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
long do_sigreturn(CPUX86State *env)
|
||||
{
|
||||
struct sigframe *frame = (struct sigframe *)(env->regs[R_ESP] - 8);
|
||||
target_sigset_t target_set;
|
||||
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++)
|
||||
target_set.sig[i] = frame->extramask[i - 1];
|
||||
|
||||
target_to_host_sigset(&set, &target_set);
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
|
||||
/* restore registers */
|
||||
if (restore_sigcontext(env, &frame->sc, &eax))
|
||||
goto badframe;
|
||||
return eax;
|
||||
|
||||
badframe:
|
||||
force_sig(TARGET_SIGSEGV);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long do_rt_sigreturn(CPUX86State *env)
|
||||
{
|
||||
struct rt_sigframe *frame = (struct rt_sigframe *)(env->regs[R_ESP] - 4);
|
||||
target_sigset_t target_set;
|
||||
sigset_t set;
|
||||
// stack_t st;
|
||||
int eax;
|
||||
|
||||
#if 0
|
||||
if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
|
||||
goto badframe;
|
||||
#endif
|
||||
memcpy(&target_set, &frame->uc.uc_sigmask, sizeof(target_sigset_t));
|
||||
|
||||
target_to_host_sigset(&set, &target_set);
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
|
||||
if (restore_sigcontext(env, &frame->uc.uc_mcontext, &eax))
|
||||
goto badframe;
|
||||
|
||||
#if 0
|
||||
if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
|
||||
goto badframe;
|
||||
/* It is more difficult to avoid calling this function than to
|
||||
call it and ignore errors. */
|
||||
do_sigaltstack(&st, NULL, regs->esp);
|
||||
#endif
|
||||
return eax;
|
||||
|
||||
badframe:
|
||||
force_sig(TARGET_SIGSEGV);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void process_pending_signals(void *cpu_env)
|
||||
{
|
||||
int sig;
|
||||
target_ulong handler;
|
||||
sigset_t set, old_set;
|
||||
target_sigset_t target_old_set;
|
||||
struct emulated_sigaction *k;
|
||||
struct sigqueue *q;
|
||||
|
||||
if (!signal_pending)
|
||||
return;
|
||||
|
||||
esig = sigact_table;
|
||||
for(signum = 1; signum < TARGET_NSIG; signum++) {
|
||||
if (esig->nb_pending != 0)
|
||||
k = sigact_table;
|
||||
for(sig = 1; sig <= TARGET_NSIG; sig++) {
|
||||
if (k->pending)
|
||||
goto handle_signal;
|
||||
esig++;
|
||||
k++;
|
||||
}
|
||||
/* if no signal is pending, just return */
|
||||
signal_pending = 0;
|
||||
return;
|
||||
|
||||
handle_signal:
|
||||
_sa_handler = esig->sa._sa_handler;
|
||||
if (_sa_handler == TARGET_SIG_DFL) {
|
||||
/* default handling
|
||||
#ifdef DEBUG_SIGNAL
|
||||
fprintf(stderr, "qemu: process signal %d\n", sig);
|
||||
#endif
|
||||
/* dequeue signal */
|
||||
q = k->first;
|
||||
k->first = q->next;
|
||||
if (!k->first)
|
||||
k->pending = 0;
|
||||
|
||||
handler = k->sa._sa_handler;
|
||||
if (handler == TARGET_SIG_DFL) {
|
||||
/* default handler : ignore some signal. The other are fatal */
|
||||
if (sig != TARGET_SIGCHLD &&
|
||||
sig != TARGET_SIGURG &&
|
||||
sig != TARGET_SIGWINCH) {
|
||||
force_sig(sig);
|
||||
}
|
||||
} else if (handler == TARGET_SIG_IGN) {
|
||||
/* ignore sig */
|
||||
} else if (handler == TARGET_SIG_ERR) {
|
||||
force_sig(sig);
|
||||
} else {
|
||||
/* compute the blocked signals during the handler execution */
|
||||
target_to_host_sigset(&set, &k->sa.sa_mask);
|
||||
/* SA_NODEFER indicates that the current signal should not be
|
||||
blocked during the handler */
|
||||
if (!(k->sa.sa_flags & TARGET_SA_NODEFER))
|
||||
sigaddset(&set, target_to_host_signal(sig));
|
||||
|
||||
/* block signals in the handler using Linux */
|
||||
sigprocmask(SIG_BLOCK, &set, &old_set);
|
||||
/* save the previous blocked signal state to restore it at the
|
||||
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);
|
||||
else
|
||||
setup_frame(sig, k, &target_old_set, cpu_env);
|
||||
if (k->sa.sa_flags & TARGET_SA_RESETHAND)
|
||||
k->sa._sa_handler = TARGET_SIG_DFL;
|
||||
}
|
||||
|
||||
|
||||
if (q != &k->info)
|
||||
free_sigqueue(q);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1330
-240
File diff suppressed because it is too large
Load Diff
+138
-1
@@ -19,16 +19,87 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
struct target_timespec {
|
||||
target_long tv_sec;
|
||||
target_long tv_nsec;
|
||||
};
|
||||
|
||||
struct target_itimerval {
|
||||
struct target_timeval it_interval;
|
||||
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 */
|
||||
};
|
||||
|
||||
struct target_msghdr {
|
||||
target_long msg_name; /* Socket name */
|
||||
int msg_namelen; /* Length of name */
|
||||
target_long msg_iov; /* Data blocks */
|
||||
target_long msg_iovlen; /* Number of blocks */
|
||||
target_long msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
|
||||
target_long msg_controllen; /* Length of cmsg list */
|
||||
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 */
|
||||
@@ -52,7 +123,7 @@ typedef struct {
|
||||
int val[2];
|
||||
} kernel_fsid_t;
|
||||
|
||||
struct statfs {
|
||||
struct kernel_statfs {
|
||||
int f_type;
|
||||
int f_bsize;
|
||||
int f_blocks;
|
||||
@@ -65,6 +136,22 @@ struct statfs {
|
||||
int f_spare[6];
|
||||
};
|
||||
|
||||
struct target_dirent {
|
||||
target_long d_ino;
|
||||
target_long d_off;
|
||||
unsigned short d_reclen;
|
||||
char d_name[256]; /* We must not include limits.h! */
|
||||
};
|
||||
|
||||
struct target_dirent64 {
|
||||
uint64_t d_ino;
|
||||
int64_t d_off;
|
||||
unsigned short d_reclen;
|
||||
unsigned char d_type;
|
||||
char d_name[256];
|
||||
};
|
||||
|
||||
|
||||
/* mostly generic signal stuff */
|
||||
#define TARGET_SIG_DFL ((target_long)0) /* default signal handling */
|
||||
#define TARGET_SIG_IGN ((target_long)1) /* ignore signal */
|
||||
@@ -82,6 +169,56 @@ typedef struct {
|
||||
target_ulong sig[TARGET_NSIG_WORDS];
|
||||
} target_sigset_t;
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s)
|
||||
{
|
||||
int i;
|
||||
for(i = 0;i < TARGET_NSIG_WORDS; i++)
|
||||
d->sig[i] = tswapl(s->sig[i]);
|
||||
}
|
||||
#else
|
||||
static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s)
|
||||
{
|
||||
*d = *s;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void target_siginitset(target_sigset_t *d, target_ulong set)
|
||||
{
|
||||
int i;
|
||||
d->sig[0] = set;
|
||||
for(i = 1;i < TARGET_NSIG_WORDS; i++)
|
||||
d->sig[i] = 0;
|
||||
}
|
||||
|
||||
void host_to_target_sigset(target_sigset_t *d, sigset_t *s);
|
||||
void target_to_host_sigset(sigset_t *d, target_sigset_t *s);
|
||||
void host_to_target_old_sigset(target_ulong *old_sigset,
|
||||
const sigset_t *sigset);
|
||||
void target_to_host_old_sigset(sigset_t *sigset,
|
||||
const target_ulong *old_sigset);
|
||||
struct target_sigaction;
|
||||
int do_sigaction(int sig, const struct target_sigaction *act,
|
||||
struct target_sigaction *oact);
|
||||
|
||||
struct target_rlimit {
|
||||
target_ulong rlim_cur;
|
||||
target_ulong rlim_max;
|
||||
};
|
||||
|
||||
struct target_pollfd {
|
||||
int fd; /* file descriptor */
|
||||
short events; /* requested events */
|
||||
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 */
|
||||
|
||||
@@ -62,3 +62,8 @@ STRUCT(cdrom_read_audio,
|
||||
STRUCT(hd_geometry,
|
||||
TYPE_CHAR, TYPE_CHAR, TYPE_SHORT, TYPE_ULONG)
|
||||
|
||||
STRUCT(dirent,
|
||||
TYPE_LONG, TYPE_LONG, TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 256))
|
||||
|
||||
STRUCT(kbentry,
|
||||
TYPE_CHAR, TYPE_CHAR, TYPE_SHORT)
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_addl_A0,REGNAME)(void)
|
||||
{
|
||||
A0 += REG;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_addl_A0,REGNAME),_s1)(void)
|
||||
{
|
||||
A0 += REG << 1;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_addl_A0,REGNAME),_s2)(void)
|
||||
{
|
||||
A0 += REG << 2;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_addl_A0,REGNAME),_s3)(void)
|
||||
{
|
||||
A0 += REG << 3;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_movl_T0,REGNAME)(void)
|
||||
{
|
||||
T0 = REG;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_movl_T1,REGNAME)(void)
|
||||
{
|
||||
T1 = REG;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_movh_T0,REGNAME)(void)
|
||||
{
|
||||
T0 = REG >> 8;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_movh_T1,REGNAME)(void)
|
||||
{
|
||||
T1 = REG >> 8;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_movl,REGNAME),_T0)(void)
|
||||
{
|
||||
REG = T0;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_movl,REGNAME),_T1)(void)
|
||||
{
|
||||
REG = T1;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_movl,REGNAME),_A0)(void)
|
||||
{
|
||||
REG = A0;
|
||||
}
|
||||
|
||||
/* mov T1 to REG if T0 is true */
|
||||
void OPPROTO glue(glue(op_cmovw,REGNAME),_T1_T0)(void)
|
||||
{
|
||||
if (T0)
|
||||
REG = (REG & 0xffff0000) | (T1 & 0xffff);
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_cmovl,REGNAME),_T1_T0)(void)
|
||||
{
|
||||
if (T0)
|
||||
REG = T1;
|
||||
}
|
||||
|
||||
/* NOTE: T0 high order bits are ignored */
|
||||
void OPPROTO glue(glue(op_movw,REGNAME),_T0)(void)
|
||||
{
|
||||
REG = (REG & 0xffff0000) | (T0 & 0xffff);
|
||||
}
|
||||
|
||||
/* NOTE: T0 high order bits are ignored */
|
||||
void OPPROTO glue(glue(op_movw,REGNAME),_T1)(void)
|
||||
{
|
||||
REG = (REG & 0xffff0000) | (T1 & 0xffff);
|
||||
}
|
||||
|
||||
/* NOTE: A0 high order bits are ignored */
|
||||
void OPPROTO glue(glue(op_movw,REGNAME),_A0)(void)
|
||||
{
|
||||
REG = (REG & 0xffff0000) | (A0 & 0xffff);
|
||||
}
|
||||
|
||||
/* NOTE: T0 high order bits are ignored */
|
||||
void OPPROTO glue(glue(op_movb,REGNAME),_T0)(void)
|
||||
{
|
||||
REG = (REG & 0xffffff00) | (T0 & 0xff);
|
||||
}
|
||||
|
||||
/* NOTE: T0 high order bits are ignored */
|
||||
void OPPROTO glue(glue(op_movh,REGNAME),_T0)(void)
|
||||
{
|
||||
REG = (REG & 0xffff00ff) | ((T0 & 0xff) << 8);
|
||||
}
|
||||
|
||||
/* NOTE: T1 high order bits are ignored */
|
||||
void OPPROTO glue(glue(op_movb,REGNAME),_T1)(void)
|
||||
{
|
||||
REG = (REG & 0xffffff00) | (T1 & 0xff);
|
||||
}
|
||||
|
||||
/* NOTE: T1 high order bits are ignored */
|
||||
void OPPROTO glue(glue(op_movh,REGNAME),_T1)(void)
|
||||
{
|
||||
REG = (REG & 0xffff00ff) | ((T1 & 0xff) << 8);
|
||||
}
|
||||
+865
@@ -0,0 +1,865 @@
|
||||
/*
|
||||
* i386 micro operations (included several times to generate
|
||||
* different operand sizes)
|
||||
*
|
||||
* 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 DATA_BITS (1 << (3 + SHIFT))
|
||||
#define SHIFT_MASK (DATA_BITS - 1)
|
||||
#define SIGN_MASK (1 << (DATA_BITS - 1))
|
||||
|
||||
#if DATA_BITS == 8
|
||||
#define SUFFIX b
|
||||
#define DATA_TYPE uint8_t
|
||||
#define DATA_STYPE int8_t
|
||||
#define DATA_MASK 0xff
|
||||
#elif DATA_BITS == 16
|
||||
#define SUFFIX w
|
||||
#define DATA_TYPE uint16_t
|
||||
#define DATA_STYPE int16_t
|
||||
#define DATA_MASK 0xffff
|
||||
#elif DATA_BITS == 32
|
||||
#define SUFFIX l
|
||||
#define DATA_TYPE uint32_t
|
||||
#define DATA_STYPE int32_t
|
||||
#define DATA_MASK 0xffffffff
|
||||
#else
|
||||
#error unhandled operand size
|
||||
#endif
|
||||
|
||||
/* dynamic flags computation */
|
||||
|
||||
static int glue(compute_all_add, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_DST - CC_SRC;
|
||||
cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = (CC_DST ^ src1 ^ src2) & 0x10;
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
static int glue(compute_c_add, SUFFIX)(void)
|
||||
{
|
||||
int src1, cf;
|
||||
src1 = CC_SRC;
|
||||
cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1;
|
||||
return cf;
|
||||
}
|
||||
|
||||
static int glue(compute_all_adc, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_DST - CC_SRC - 1;
|
||||
cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = (CC_DST ^ src1 ^ src2) & 0x10;
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
static int glue(compute_c_adc, SUFFIX)(void)
|
||||
{
|
||||
int src1, cf;
|
||||
src1 = CC_SRC;
|
||||
cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1;
|
||||
return cf;
|
||||
}
|
||||
|
||||
static int glue(compute_all_sub, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
cf = (DATA_TYPE)src1 < (DATA_TYPE)src2;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = (CC_DST ^ src1 ^ src2) & 0x10;
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
static int glue(compute_c_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2, cf;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
cf = (DATA_TYPE)src1 < (DATA_TYPE)src2;
|
||||
return cf;
|
||||
}
|
||||
|
||||
static int glue(compute_all_sbb, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST - 1;
|
||||
cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = (CC_DST ^ src1 ^ src2) & 0x10;
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
static int glue(compute_c_sbb, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2, cf;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST - 1;
|
||||
cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2;
|
||||
return cf;
|
||||
}
|
||||
|
||||
static int glue(compute_all_logic, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
cf = 0;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = 0;
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
of = 0;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
static int glue(compute_c_logic, SUFFIX)(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glue(compute_all_inc, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
int src1, src2;
|
||||
src1 = CC_DST - 1;
|
||||
src2 = 1;
|
||||
cf = CC_SRC;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = (CC_DST ^ src1 ^ src2) & 0x10;
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
of = ((CC_DST & DATA_MASK) == SIGN_MASK) << 11;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
#if DATA_BITS == 32
|
||||
static int glue(compute_c_inc, SUFFIX)(void)
|
||||
{
|
||||
return CC_SRC;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int glue(compute_all_dec, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
int src1, src2;
|
||||
src1 = CC_DST + 1;
|
||||
src2 = 1;
|
||||
cf = CC_SRC;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = (CC_DST ^ src1 ^ src2) & 0x10;
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
of = ((CC_DST & DATA_MASK) == ((uint32_t)SIGN_MASK - 1)) << 11;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
static int glue(compute_all_shl, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
cf = (CC_SRC >> (DATA_BITS - 1)) & CC_C;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = 0; /* undefined */
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
/* of is defined if shift count == 1 */
|
||||
of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int glue(compute_all_sar, SUFFIX)(void)
|
||||
{
|
||||
int cf, pf, af, zf, sf, of;
|
||||
cf = CC_SRC & 1;
|
||||
pf = parity_table[(uint8_t)CC_DST];
|
||||
af = 0; /* undefined */
|
||||
zf = ((DATA_TYPE)CC_DST == 0) << 6;
|
||||
sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
|
||||
/* of is defined if shift count == 1 */
|
||||
of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O;
|
||||
return cf | pf | af | zf | sf | of;
|
||||
}
|
||||
|
||||
/* various optimized jumps cases */
|
||||
|
||||
void OPPROTO glue(op_jb_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_TYPE)src1 < (DATA_TYPE)src2)
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_jz_sub, SUFFIX)(void)
|
||||
{
|
||||
if ((DATA_TYPE)CC_DST == 0)
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_jbe_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_TYPE)src1 <= (DATA_TYPE)src2)
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_js_sub, SUFFIX)(void)
|
||||
{
|
||||
if (CC_DST & SIGN_MASK)
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_jl_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_STYPE)src1 < (DATA_STYPE)src2)
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_jle_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
if ((DATA_STYPE)src1 <= (DATA_STYPE)src2)
|
||||
JUMP_TB(PARAM1, 0, PARAM2);
|
||||
else
|
||||
JUMP_TB(PARAM1, 1, PARAM3);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* oldies */
|
||||
|
||||
#if DATA_BITS >= 16
|
||||
|
||||
void OPPROTO glue(op_loopnz, SUFFIX)(void)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int eflags;
|
||||
eflags = cc_table[CC_OP].compute_all();
|
||||
tmp = (ECX - 1) & DATA_MASK;
|
||||
ECX = (ECX & ~DATA_MASK) | tmp;
|
||||
if (tmp != 0 && !(eflags & CC_Z))
|
||||
EIP = PARAM1;
|
||||
else
|
||||
EIP = PARAM2;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_loopz, SUFFIX)(void)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int eflags;
|
||||
eflags = cc_table[CC_OP].compute_all();
|
||||
tmp = (ECX - 1) & DATA_MASK;
|
||||
ECX = (ECX & ~DATA_MASK) | tmp;
|
||||
if (tmp != 0 && (eflags & CC_Z))
|
||||
EIP = PARAM1;
|
||||
else
|
||||
EIP = PARAM2;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_loop, SUFFIX)(void)
|
||||
{
|
||||
unsigned int tmp;
|
||||
tmp = (ECX - 1) & DATA_MASK;
|
||||
ECX = (ECX & ~DATA_MASK) | tmp;
|
||||
if (tmp != 0)
|
||||
EIP = PARAM1;
|
||||
else
|
||||
EIP = PARAM2;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_jecxz, SUFFIX)(void)
|
||||
{
|
||||
if ((DATA_TYPE)ECX == 0)
|
||||
EIP = PARAM1;
|
||||
else
|
||||
EIP = PARAM2;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* various optimized set cases */
|
||||
|
||||
void OPPROTO glue(op_setb_T0_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
T0 = ((DATA_TYPE)src1 < (DATA_TYPE)src2);
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_setz_T0_sub, SUFFIX)(void)
|
||||
{
|
||||
T0 = ((DATA_TYPE)CC_DST == 0);
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_setbe_T0_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
T0 = ((DATA_TYPE)src1 <= (DATA_TYPE)src2);
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_sets_T0_sub, SUFFIX)(void)
|
||||
{
|
||||
T0 = lshift(CC_DST, -(DATA_BITS - 1)) & 1;
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_setl_T0_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
T0 = ((DATA_STYPE)src1 < (DATA_STYPE)src2);
|
||||
}
|
||||
|
||||
void OPPROTO glue(op_setle_T0_sub, SUFFIX)(void)
|
||||
{
|
||||
int src1, src2;
|
||||
src1 = CC_SRC;
|
||||
src2 = CC_SRC - CC_DST;
|
||||
|
||||
T0 = ((DATA_STYPE)src1 <= (DATA_STYPE)src2);
|
||||
}
|
||||
|
||||
/* shifts */
|
||||
|
||||
void OPPROTO glue(glue(op_rol, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count, src;
|
||||
count = T1 & SHIFT_MASK;
|
||||
if (count) {
|
||||
CC_SRC = cc_table[CC_OP].compute_all() & ~(CC_O | CC_C);
|
||||
src = T0;
|
||||
T0 &= DATA_MASK;
|
||||
T0 = (T0 << count) | (T0 >> (DATA_BITS - count));
|
||||
CC_SRC |= (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
|
||||
(T0 & CC_C);
|
||||
CC_OP = CC_OP_EFLAGS;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_rol, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & SHIFT_MASK;
|
||||
if (count) {
|
||||
T0 &= DATA_MASK;
|
||||
T0 = (T0 << count) | (T0 >> (DATA_BITS - count));
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_ror, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count, src;
|
||||
count = T1 & SHIFT_MASK;
|
||||
if (count) {
|
||||
CC_SRC = cc_table[CC_OP].compute_all() & ~(CC_O | CC_C);
|
||||
src = T0;
|
||||
T0 &= DATA_MASK;
|
||||
T0 = (T0 >> count) | (T0 << (DATA_BITS - count));
|
||||
CC_SRC |= (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
|
||||
((T0 >> (DATA_BITS - 1)) & CC_C);
|
||||
CC_OP = CC_OP_EFLAGS;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_ror, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & SHIFT_MASK;
|
||||
if (count) {
|
||||
T0 &= DATA_MASK;
|
||||
T0 = (T0 >> count) | (T0 << (DATA_BITS - count));
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_rcl, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count, res, eflags;
|
||||
unsigned int src;
|
||||
|
||||
count = T1 & 0x1f;
|
||||
#if DATA_BITS == 16
|
||||
count = rclw_table[count];
|
||||
#elif DATA_BITS == 8
|
||||
count = rclb_table[count];
|
||||
#endif
|
||||
if (count) {
|
||||
eflags = cc_table[CC_OP].compute_all();
|
||||
T0 &= DATA_MASK;
|
||||
src = T0;
|
||||
res = (T0 << count) | ((eflags & CC_C) << (count - 1));
|
||||
if (count > 1)
|
||||
res |= T0 >> (DATA_BITS + 1 - count);
|
||||
T0 = res;
|
||||
CC_SRC = (eflags & ~(CC_C | CC_O)) |
|
||||
(lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
|
||||
((src >> (DATA_BITS - count)) & CC_C);
|
||||
CC_OP = CC_OP_EFLAGS;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_rcr, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count, res, eflags;
|
||||
unsigned int src;
|
||||
|
||||
count = T1 & 0x1f;
|
||||
#if DATA_BITS == 16
|
||||
count = rclw_table[count];
|
||||
#elif DATA_BITS == 8
|
||||
count = rclb_table[count];
|
||||
#endif
|
||||
if (count) {
|
||||
eflags = cc_table[CC_OP].compute_all();
|
||||
T0 &= DATA_MASK;
|
||||
src = T0;
|
||||
res = (T0 >> count) | ((eflags & CC_C) << (DATA_BITS - count));
|
||||
if (count > 1)
|
||||
res |= T0 << (DATA_BITS + 1 - count);
|
||||
T0 = res;
|
||||
CC_SRC = (eflags & ~(CC_C | CC_O)) |
|
||||
(lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
|
||||
((src >> (count - 1)) & CC_C);
|
||||
CC_OP = CC_OP_EFLAGS;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shl, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & 0x1f;
|
||||
if (count) {
|
||||
CC_SRC = (DATA_TYPE)T0 << (count - 1);
|
||||
T0 = T0 << count;
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SHLB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shl, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & 0x1f;
|
||||
T0 = T0 << count;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shr, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & 0x1f;
|
||||
if (count) {
|
||||
T0 &= DATA_MASK;
|
||||
CC_SRC = T0 >> (count - 1);
|
||||
T0 = T0 >> count;
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shr, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & 0x1f;
|
||||
T0 &= DATA_MASK;
|
||||
T0 = T0 >> count;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_sar, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count, src;
|
||||
count = T1 & 0x1f;
|
||||
if (count) {
|
||||
src = (DATA_STYPE)T0;
|
||||
CC_SRC = src >> (count - 1);
|
||||
T0 = src >> count;
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_sar, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
int count, src;
|
||||
count = T1 & 0x1f;
|
||||
src = (DATA_STYPE)T0;
|
||||
T0 = src >> count;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
#if DATA_BITS == 16
|
||||
/* XXX: overflow flag might be incorrect in some cases in shldw */
|
||||
void OPPROTO glue(glue(op_shld, SUFFIX), _T0_T1_im_cc)(void)
|
||||
{
|
||||
int count;
|
||||
unsigned int res;
|
||||
count = PARAM1;
|
||||
T1 &= 0xffff;
|
||||
res = T1 | (T0 << 16);
|
||||
CC_SRC = res >> (32 - count);
|
||||
res <<= count;
|
||||
if (count > 16)
|
||||
res |= T1 << (count - 16);
|
||||
T0 = res >> 16;
|
||||
CC_DST = T0;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shld, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
{
|
||||
int count;
|
||||
unsigned int res;
|
||||
count = ECX & 0x1f;
|
||||
if (count) {
|
||||
T1 &= 0xffff;
|
||||
res = T1 | (T0 << 16);
|
||||
CC_SRC = res >> (32 - count);
|
||||
res <<= count;
|
||||
if (count > 16)
|
||||
res |= T1 << (count - 16);
|
||||
T0 = res >> 16;
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_im_cc)(void)
|
||||
{
|
||||
int count;
|
||||
unsigned int res;
|
||||
|
||||
count = PARAM1;
|
||||
res = (T0 & 0xffff) | (T1 << 16);
|
||||
CC_SRC = res >> (count - 1);
|
||||
res >>= count;
|
||||
if (count > 16)
|
||||
res |= T1 << (32 - count);
|
||||
T0 = res;
|
||||
CC_DST = T0;
|
||||
}
|
||||
|
||||
|
||||
void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
{
|
||||
int count;
|
||||
unsigned int res;
|
||||
|
||||
count = ECX & 0x1f;
|
||||
if (count) {
|
||||
res = (T0 & 0xffff) | (T1 << 16);
|
||||
CC_SRC = res >> (count - 1);
|
||||
res >>= count;
|
||||
if (count > 16)
|
||||
res |= T1 << (32 - count);
|
||||
T0 = res;
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DATA_BITS == 32
|
||||
void OPPROTO glue(glue(op_shld, SUFFIX), _T0_T1_im_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = PARAM1;
|
||||
T0 &= DATA_MASK;
|
||||
T1 &= DATA_MASK;
|
||||
CC_SRC = T0 << (count - 1);
|
||||
T0 = (T0 << count) | (T1 >> (DATA_BITS - count));
|
||||
CC_DST = T0;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shld, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = ECX & 0x1f;
|
||||
if (count) {
|
||||
T0 &= DATA_MASK;
|
||||
T1 &= DATA_MASK;
|
||||
CC_SRC = T0 << (count - 1);
|
||||
T0 = (T0 << count) | (T1 >> (DATA_BITS - count));
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SHLB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_im_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = PARAM1;
|
||||
T0 &= DATA_MASK;
|
||||
T1 &= DATA_MASK;
|
||||
CC_SRC = T0 >> (count - 1);
|
||||
T0 = (T0 >> count) | (T1 << (DATA_BITS - count));
|
||||
CC_DST = T0;
|
||||
}
|
||||
|
||||
|
||||
void OPPROTO glue(glue(op_shrd, SUFFIX), _T0_T1_ECX_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = ECX & 0x1f;
|
||||
if (count) {
|
||||
T0 &= DATA_MASK;
|
||||
T1 &= DATA_MASK;
|
||||
CC_SRC = T0 >> (count - 1);
|
||||
T0 = (T0 >> count) | (T1 << (DATA_BITS - count));
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SARB + SHIFT;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* carry add/sub (we only need to set CC_OP differently) */
|
||||
|
||||
void OPPROTO glue(glue(op_adc, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int cf;
|
||||
cf = cc_table[CC_OP].compute_c();
|
||||
CC_SRC = T0;
|
||||
T0 = T0 + T1 + cf;
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_ADDB + SHIFT + cf * 3;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_sbb, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int cf;
|
||||
cf = cc_table[CC_OP].compute_c();
|
||||
CC_SRC = T0;
|
||||
T0 = T0 - T1 - cf;
|
||||
CC_DST = T0;
|
||||
CC_OP = CC_OP_SUBB + SHIFT + cf * 3;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_cmpxchg, SUFFIX), _T0_T1_EAX_cc)(void)
|
||||
{
|
||||
CC_SRC = EAX;
|
||||
CC_DST = EAX - T0;
|
||||
if ((DATA_TYPE)CC_DST == 0) {
|
||||
T0 = T1;
|
||||
} else {
|
||||
EAX = (EAX & ~DATA_MASK) | (T0 & DATA_MASK);
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* bit operations */
|
||||
#if DATA_BITS >= 16
|
||||
|
||||
void OPPROTO glue(glue(op_bt, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & SHIFT_MASK;
|
||||
CC_SRC = T0 >> count;
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_bts, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & SHIFT_MASK;
|
||||
CC_SRC = T0 >> count;
|
||||
T0 |= (1 << count);
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_btr, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & SHIFT_MASK;
|
||||
CC_SRC = T0 >> count;
|
||||
T0 &= ~(1 << count);
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_btc, SUFFIX), _T0_T1_cc)(void)
|
||||
{
|
||||
int count;
|
||||
count = T1 & SHIFT_MASK;
|
||||
CC_SRC = T0 >> count;
|
||||
T0 ^= (1 << count);
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_bsf, SUFFIX), _T0_cc)(void)
|
||||
{
|
||||
int res, count;
|
||||
res = T0 & DATA_MASK;
|
||||
if (res != 0) {
|
||||
count = 0;
|
||||
while ((res & 1) == 0) {
|
||||
count++;
|
||||
res >>= 1;
|
||||
}
|
||||
T0 = count;
|
||||
CC_DST = 1; /* ZF = 1 */
|
||||
} else {
|
||||
CC_DST = 0; /* ZF = 1 */
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_bsr, SUFFIX), _T0_cc)(void)
|
||||
{
|
||||
int res, count;
|
||||
res = T0 & DATA_MASK;
|
||||
if (res != 0) {
|
||||
count = DATA_BITS - 1;
|
||||
while ((res & SIGN_MASK) == 0) {
|
||||
count--;
|
||||
res <<= 1;
|
||||
}
|
||||
T0 = count;
|
||||
CC_DST = 1; /* ZF = 1 */
|
||||
} else {
|
||||
CC_DST = 0; /* ZF = 1 */
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* string operations */
|
||||
/* XXX: maybe use lower level instructions to ease 16 bit / segment handling */
|
||||
|
||||
#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"
|
||||
|
||||
#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"
|
||||
|
||||
#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(glue(op_out, SUFFIX), _T0_T1)(void)
|
||||
{
|
||||
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)(env, T0 & 0xffff);
|
||||
}
|
||||
|
||||
#undef DATA_BITS
|
||||
#undef SHIFT_MASK
|
||||
#undef SIGN_MASK
|
||||
#undef DATA_TYPE
|
||||
#undef DATA_STYPE
|
||||
#undef DATA_MASK
|
||||
#undef SUFFIX
|
||||
@@ -0,0 +1,130 @@
|
||||
/* ld script to make i386 Linux kernel
|
||||
* Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
|
||||
*/
|
||||
OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
|
||||
OUTPUT_ARCH(powerpc)
|
||||
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
|
||||
ENTRY(_start)
|
||||
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 . */
|
||||
}
|
||||
+447
@@ -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.
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
+410
-36
@@ -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 */
|
||||
@@ -245,24 +275,24 @@
|
||||
struct target_stat {
|
||||
unsigned short st_dev;
|
||||
unsigned short __pad1;
|
||||
unsigned long st_ino;
|
||||
target_ulong st_ino;
|
||||
unsigned short st_mode;
|
||||
unsigned short st_nlink;
|
||||
unsigned short st_uid;
|
||||
unsigned short st_gid;
|
||||
unsigned short st_rdev;
|
||||
unsigned short __pad2;
|
||||
unsigned long st_size;
|
||||
unsigned long st_blksize;
|
||||
unsigned long st_blocks;
|
||||
unsigned long st_atime;
|
||||
unsigned long __unused1;
|
||||
unsigned long st_mtime;
|
||||
unsigned long __unused2;
|
||||
unsigned long st_ctime;
|
||||
unsigned long __unused3;
|
||||
unsigned long __unused4;
|
||||
unsigned long __unused5;
|
||||
target_ulong st_size;
|
||||
target_ulong st_blksize;
|
||||
target_ulong st_blocks;
|
||||
target_ulong target_st_atime;
|
||||
target_ulong __unused1;
|
||||
target_ulong target_st_mtime;
|
||||
target_ulong __unused2;
|
||||
target_ulong target_st_ctime;
|
||||
target_ulong __unused3;
|
||||
target_ulong __unused4;
|
||||
target_ulong __unused5;
|
||||
};
|
||||
|
||||
/* This matches struct stat64 in glibc2.1, hence the absolutely
|
||||
@@ -272,55 +302,94 @@ struct target_stat64 {
|
||||
unsigned short st_dev;
|
||||
unsigned char __pad0[10];
|
||||
|
||||
#define STAT64_HAS_BROKEN_ST_INO 1
|
||||
unsigned long __st_ino;
|
||||
#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
|
||||
target_ulong __st_ino;
|
||||
|
||||
unsigned int st_mode;
|
||||
unsigned int st_nlink;
|
||||
|
||||
unsigned long st_uid;
|
||||
unsigned long st_gid;
|
||||
target_ulong st_uid;
|
||||
target_ulong st_gid;
|
||||
|
||||
unsigned short st_rdev;
|
||||
unsigned char __pad3[10];
|
||||
|
||||
long long st_size;
|
||||
unsigned long st_blksize;
|
||||
target_ulong st_blksize;
|
||||
|
||||
unsigned long st_blocks; /* Number 512-byte blocks allocated. */
|
||||
unsigned long __pad4; /* future possible st_blocks high bits */
|
||||
target_ulong st_blocks; /* Number 512-byte blocks allocated. */
|
||||
target_ulong __pad4; /* future possible st_blocks high bits */
|
||||
|
||||
unsigned long st_atime;
|
||||
unsigned long __pad5;
|
||||
target_ulong target_st_atime;
|
||||
target_ulong __pad5;
|
||||
|
||||
unsigned long st_mtime;
|
||||
unsigned long __pad6;
|
||||
target_ulong target_st_mtime;
|
||||
target_ulong __pad6;
|
||||
|
||||
unsigned long st_ctime;
|
||||
unsigned long __pad7; /* will be high 32 bits of ctime someday */
|
||||
target_ulong target_st_ctime;
|
||||
target_ulong __pad7; /* will be high 32 bits of ctime someday */
|
||||
|
||||
unsigned long long st_ino;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef unsigned long old_sigset_t; /* at least 32 bits */
|
||||
#define TARGET_SA_NOCLDSTOP 0x00000001
|
||||
#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */
|
||||
#define TARGET_SA_SIGINFO 0x00000004
|
||||
#define TARGET_SA_ONSTACK 0x08000000
|
||||
#define TARGET_SA_RESTART 0x10000000
|
||||
#define TARGET_SA_NODEFER 0x40000000
|
||||
#define TARGET_SA_RESETHAND 0x80000000
|
||||
#define TARGET_SA_RESTORER 0x04000000
|
||||
|
||||
#define TARGET_SIGHUP 1
|
||||
#define TARGET_SIGINT 2
|
||||
#define TARGET_SIGQUIT 3
|
||||
#define TARGET_SIGILL 4
|
||||
#define TARGET_SIGTRAP 5
|
||||
#define TARGET_SIGABRT 6
|
||||
#define TARGET_SIGIOT 6
|
||||
#define TARGET_SIGBUS 7
|
||||
#define TARGET_SIGFPE 8
|
||||
#define TARGET_SIGKILL 9
|
||||
#define TARGET_SIGUSR1 10
|
||||
#define TARGET_SIGSEGV 11
|
||||
#define TARGET_SIGUSR2 12
|
||||
#define TARGET_SIGPIPE 13
|
||||
#define TARGET_SIGALRM 14
|
||||
#define TARGET_SIGTERM 15
|
||||
#define TARGET_SIGSTKFLT 16
|
||||
#define TARGET_SIGCHLD 17
|
||||
#define TARGET_SIGCONT 18
|
||||
#define TARGET_SIGSTOP 19
|
||||
#define TARGET_SIGTSTP 20
|
||||
#define TARGET_SIGTTIN 21
|
||||
#define TARGET_SIGTTOU 22
|
||||
#define TARGET_SIGURG 23
|
||||
#define TARGET_SIGXCPU 24
|
||||
#define TARGET_SIGXFSZ 25
|
||||
#define TARGET_SIGVTALRM 26
|
||||
#define TARGET_SIGPROF 27
|
||||
#define TARGET_SIGWINCH 28
|
||||
#define TARGET_SIGIO 29
|
||||
#define TARGET_SIGRTMIN 32
|
||||
|
||||
struct target_old_sigaction {
|
||||
target_ulong _sa_handler;
|
||||
target_ulong sa_mask;
|
||||
target_ulong sa_flags;
|
||||
void (*sa_restorer)(void);
|
||||
target_ulong sa_restorer;
|
||||
};
|
||||
|
||||
struct target_sigaction {
|
||||
target_ulong _sa_handler;
|
||||
target_sigset_t sa_mask;
|
||||
target_ulong sa_flags;
|
||||
target_ulong sa_restorer;
|
||||
target_sigset_t sa_mask;
|
||||
};
|
||||
|
||||
typedef union target_sigval {
|
||||
int sival_int;
|
||||
void *sival_ptr;
|
||||
target_ulong sival_ptr;
|
||||
} target_sigval_t;
|
||||
|
||||
#define TARGET_SI_MAX_SIZE 128
|
||||
@@ -350,7 +419,7 @@ typedef struct target_siginfo {
|
||||
struct {
|
||||
pid_t _pid; /* sender's pid */
|
||||
uid_t _uid; /* sender's uid */
|
||||
sigval_t _sigval;
|
||||
target_sigval_t _sigval;
|
||||
} _rt;
|
||||
|
||||
/* SIGCHLD */
|
||||
@@ -358,13 +427,13 @@ 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 */
|
||||
struct {
|
||||
void *_addr; /* faulting insn/memory ref. */
|
||||
target_ulong _addr; /* faulting insn/memory ref. */
|
||||
} _sigfault;
|
||||
|
||||
/* SIGPOLL */
|
||||
@@ -375,6 +444,70 @@ 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
|
||||
*/
|
||||
#define TARGET_ILL_ILLOPN (2) /* illegal operand */
|
||||
|
||||
/*
|
||||
* SIGFPE si_codes
|
||||
*/
|
||||
#define TARGET_FPE_INTDIV (1) /* integer divide by zero */
|
||||
#define TARGET_FPE_INTOVF (2) /* integer overflow */
|
||||
#define TARGET_FPE_FLTDIV (3) /* floating point divide by zero */
|
||||
#define TARGET_FPE_FLTOVF (4) /* floating point overflow */
|
||||
#define TARGET_FPE_FLTUND (5) /* floating point underflow */
|
||||
#define TARGET_FPE_FLTRES (6) /* floating point inexact result */
|
||||
#define TARGET_FPE_FLTINV (7) /* floating point invalid operation */
|
||||
#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)
|
||||
|
||||
struct target_pt_regs {
|
||||
long ebx;
|
||||
long ecx;
|
||||
long edx;
|
||||
long esi;
|
||||
long edi;
|
||||
long ebp;
|
||||
long eax;
|
||||
int xds;
|
||||
int xes;
|
||||
long orig_eax;
|
||||
long eip;
|
||||
int xcs;
|
||||
long eflags;
|
||||
long esp;
|
||||
int xss;
|
||||
};
|
||||
|
||||
/* ioctls */
|
||||
|
||||
/*
|
||||
@@ -463,8 +596,8 @@ typedef struct target_siginfo {
|
||||
#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
|
||||
@@ -632,6 +765,242 @@ struct target_termios {
|
||||
#define TARGET_VLNEXT 15
|
||||
#define TARGET_VEOL2 16
|
||||
|
||||
#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;
|
||||
unsigned int limit;
|
||||
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
|
||||
@@ -758,3 +1127,8 @@ struct target_termios {
|
||||
#define TARGET_SOUND_MIXER_WRITE_ENHANCE 0xc0044d1f
|
||||
#define TARGET_SOUND_MIXER_WRITE_LOUD 0xc0044d1f
|
||||
#define TARGET_SOUND_MIXER_WRITE_RECSRC 0xc0044dff
|
||||
|
||||
#define TARGET_VFAT_IOCTL_READDIR_BOTH 0x82187201
|
||||
#define TARGET_VFAT_IOCTL_READDIR_SHORT 0x82187202
|
||||
|
||||
#define TARGET_SIOCATMARK 0x8905
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,61 @@
|
||||
include ../config.mak
|
||||
|
||||
CFLAGS=-Wall -O2 -g
|
||||
LDFLAGS=
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
TESTS=testclone testsig testthread sha1-i386 test-i386 runcom
|
||||
endif
|
||||
TESTS+=sha1 test_path
|
||||
|
||||
QEMU=../qemu
|
||||
|
||||
all: $(TESTS)
|
||||
|
||||
hello: hello.c
|
||||
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
strip hello
|
||||
|
||||
testclone: testclone.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
testsig: testsig.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
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-vm86.S \
|
||||
test-i386.h test-i386-shift.h test-i386-muldiv.h
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ test-i386.c \
|
||||
test-i386-code16.S test-i386-vm86.S -lm
|
||||
|
||||
test: test-i386
|
||||
ifeq ($(ARCH),i386)
|
||||
./test-i386 > test-i386.ref
|
||||
endif
|
||||
$(QEMU) test-i386 > test-i386.out
|
||||
@if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi
|
||||
|
||||
# speed test
|
||||
sha1-i386: sha1.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
sha1: sha1.c
|
||||
$(HOST_CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
speed: sha1 sha1-i386
|
||||
time ./sha1
|
||||
time $(QEMU) ./sha1-i386
|
||||
|
||||
# vm86 test
|
||||
runcom: runcom.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *~ *.o $(TESTS)
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <asm/unistd.h>
|
||||
|
||||
extern inline volatile void exit(int status)
|
||||
{
|
||||
int __res;
|
||||
__asm__ volatile ("movl %%ecx,%%ebx\n"\
|
||||
"int $0x80" \
|
||||
: "=a" (__res) : "0" (__NR_exit),"c" ((long)(status)));
|
||||
}
|
||||
|
||||
extern inline int write(int fd, const char * buf, int len)
|
||||
{
|
||||
int status;
|
||||
__asm__ volatile ("pushl %%ebx\n"\
|
||||
"movl %%esi,%%ebx\n"\
|
||||
"int $0x80\n" \
|
||||
"popl %%ebx\n"\
|
||||
: "=a" (status) \
|
||||
: "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len)));
|
||||
}
|
||||
|
||||
void _start(void)
|
||||
{
|
||||
write(1, "Hello World\n", 12);
|
||||
exit(0);
|
||||
}
|
||||
+195
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
.code16
|
||||
.globl code16_start
|
||||
.globl code16_end
|
||||
|
||||
CS_SEG = 0xf
|
||||
|
||||
code16_start:
|
||||
|
||||
.globl code16_func1
|
||||
|
||||
/* basic test */
|
||||
code16_func1 = . - code16_start
|
||||
mov $1, %eax
|
||||
data32 lret
|
||||
|
||||
/* test push/pop in 16 bit mode */
|
||||
.globl code16_func2
|
||||
code16_func2 = . - code16_start
|
||||
xor %eax, %eax
|
||||
mov $0x12345678, %ebx
|
||||
movl %esp, %ecx
|
||||
push %bx
|
||||
subl %esp, %ecx
|
||||
pop %ax
|
||||
data32 lret
|
||||
|
||||
/* test various jmp opcodes */
|
||||
.globl code16_func3
|
||||
code16_func3 = . - code16_start
|
||||
jmp 1f
|
||||
nop
|
||||
1:
|
||||
mov $4, %eax
|
||||
mov $0x12345678, %ebx
|
||||
xor %bx, %bx
|
||||
jz 2f
|
||||
add $2, %ax
|
||||
2:
|
||||
|
||||
call myfunc
|
||||
|
||||
lcall $CS_SEG, $(myfunc2 - code16_start)
|
||||
|
||||
ljmp $CS_SEG, $(myjmp1 - code16_start)
|
||||
myjmp1_next:
|
||||
|
||||
cs lcall myfunc2_addr - code16_start
|
||||
|
||||
cs ljmp myjmp2_addr - code16_start
|
||||
myjmp2_next:
|
||||
|
||||
data32 lret
|
||||
|
||||
myfunc2_addr:
|
||||
.short myfunc2 - code16_start
|
||||
.short CS_SEG
|
||||
|
||||
myjmp2_addr:
|
||||
.short myjmp2 - code16_start
|
||||
.short CS_SEG
|
||||
|
||||
myjmp1:
|
||||
add $8, %ax
|
||||
jmp myjmp1_next
|
||||
|
||||
myjmp2:
|
||||
add $16, %ax
|
||||
jmp myjmp2_next
|
||||
|
||||
myfunc:
|
||||
add $1, %ax
|
||||
ret
|
||||
|
||||
myfunc2:
|
||||
add $4, %ax
|
||||
lret
|
||||
|
||||
|
||||
code16_end:
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
void glue(glue(test_, OP), b)(int op0, int op1)
|
||||
{
|
||||
int res, s1, s0, flags;
|
||||
s0 = op0;
|
||||
s1 = op1;
|
||||
res = s0;
|
||||
flags = 0;
|
||||
asm ("push %4\n\t"
|
||||
"popf\n\t"
|
||||
stringify(OP)"b %b2\n\t"
|
||||
"pushf\n\t"
|
||||
"popl %1\n\t"
|
||||
: "=a" (res), "=g" (flags)
|
||||
: "q" (s1), "0" (res), "1" (flags));
|
||||
printf("%-10s A=%08x B=%08x R=%08x CC=%04x\n",
|
||||
stringify(OP) "b", s0, s1, res, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void glue(glue(test_, OP), w)(int op0h, int op0, int op1)
|
||||
{
|
||||
int res, s1, flags, resh;
|
||||
s1 = op1;
|
||||
resh = op0h;
|
||||
res = op0;
|
||||
flags = 0;
|
||||
asm ("push %5\n\t"
|
||||
"popf\n\t"
|
||||
stringify(OP) "w %w3\n\t"
|
||||
"pushf\n\t"
|
||||
"popl %1\n\t"
|
||||
: "=a" (res), "=g" (flags), "=d" (resh)
|
||||
: "q" (s1), "0" (res), "1" (flags), "2" (resh));
|
||||
printf("%-10s AH=%08x AL=%08x B=%08x RH=%08x RL=%08x CC=%04x\n",
|
||||
stringify(OP) "w", op0h, op0, s1, resh, res, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void glue(glue(test_, OP), l)(int op0h, int op0, int op1)
|
||||
{
|
||||
int res, s1, flags, resh;
|
||||
s1 = op1;
|
||||
resh = op0h;
|
||||
res = op0;
|
||||
flags = 0;
|
||||
asm ("push %5\n\t"
|
||||
"popf\n\t"
|
||||
stringify(OP) "l %3\n\t"
|
||||
"pushf\n\t"
|
||||
"popl %1\n\t"
|
||||
: "=a" (res), "=g" (flags), "=d" (resh)
|
||||
: "q" (s1), "0" (res), "1" (flags), "2" (resh));
|
||||
printf("%-10s AH=%08x AL=%08x B=%08x RH=%08x RL=%08x CC=%04x\n",
|
||||
stringify(OP) "l", op0h, op0, s1, resh, res, flags & CC_MASK);
|
||||
}
|
||||
|
||||
#undef OP
|
||||
@@ -0,0 +1,143 @@
|
||||
|
||||
#define exec_op glue(exec_, OP)
|
||||
#define exec_opl glue(glue(exec_, OP), l)
|
||||
#define exec_opw glue(glue(exec_, OP), w)
|
||||
#define exec_opb glue(glue(exec_, OP), b)
|
||||
|
||||
#ifndef OP_SHIFTD
|
||||
|
||||
#ifdef OP_NOBYTE
|
||||
#define EXECSHIFT(size, res, s1, s2, flags) \
|
||||
asm ("push %4\n\t"\
|
||||
"popf\n\t"\
|
||||
stringify(OP) size " %" size "2, %" size "0\n\t" \
|
||||
"pushf\n\t"\
|
||||
"popl %1\n\t"\
|
||||
: "=g" (res), "=g" (flags)\
|
||||
: "r" (s1), "0" (res), "1" (flags));
|
||||
#else
|
||||
#define EXECSHIFT(size, res, s1, s2, flags) \
|
||||
asm ("push %4\n\t"\
|
||||
"popf\n\t"\
|
||||
stringify(OP) size " %%cl, %" size "0\n\t" \
|
||||
"pushf\n\t"\
|
||||
"popl %1\n\t"\
|
||||
: "=q" (res), "=g" (flags)\
|
||||
: "c" (s1), "0" (res), "1" (flags));
|
||||
#endif
|
||||
|
||||
void exec_opl(int s2, int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECSHIFT("", res, s1, s2, flags);
|
||||
/* overflow is undefined if count != 1 */
|
||||
if (s1 != 1)
|
||||
flags &= ~CC_O;
|
||||
printf("%-10s A=%08x B=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "l", s0, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void exec_opw(int s2, int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECSHIFT("w", res, s1, s2, flags);
|
||||
/* overflow is undefined if count != 1 */
|
||||
if (s1 != 1)
|
||||
flags &= ~CC_O;
|
||||
printf("%-10s A=%08x B=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "w", s0, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
#else
|
||||
#define EXECSHIFT(size, res, s1, s2, flags) \
|
||||
asm ("push %4\n\t"\
|
||||
"popf\n\t"\
|
||||
stringify(OP) size " %%cl, %" size "5, %" size "0\n\t" \
|
||||
"pushf\n\t"\
|
||||
"popl %1\n\t"\
|
||||
: "=g" (res), "=g" (flags)\
|
||||
: "c" (s1), "0" (res), "1" (flags), "r" (s2));
|
||||
|
||||
void exec_opl(int s2, int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECSHIFT("", res, s1, s2, flags);
|
||||
/* overflow is undefined if count != 1 */
|
||||
if (s1 != 1)
|
||||
flags &= ~CC_O;
|
||||
printf("%-10s A=%08x B=%08x C=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "l", s0, s2, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void exec_opw(int s2, int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECSHIFT("w", res, s1, s2, flags);
|
||||
/* overflow is undefined if count != 1 */
|
||||
if (s1 != 1)
|
||||
flags &= ~CC_O;
|
||||
printf("%-10s A=%08x B=%08x C=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "w", s0, s2, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef OP_NOBYTE
|
||||
void exec_opb(int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECSHIFT("b", res, s1, 0, flags);
|
||||
/* overflow is undefined if count != 1 */
|
||||
if (s1 != 1)
|
||||
flags &= ~CC_O;
|
||||
printf("%-10s A=%08x B=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "b", s0, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
#ifdef OP_CC
|
||||
exec_opl(s2, s0, s1, CC_C);
|
||||
exec_opw(s2, s0, s1, CC_C);
|
||||
exec_opb(s0, s1, CC_C);
|
||||
#endif
|
||||
}
|
||||
|
||||
void glue(test_, OP)(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < 32; i++)
|
||||
exec_op(0x21ad3d34, 0x12345678, i);
|
||||
for(i = 0; i < 32; i++)
|
||||
exec_op(0x813f3421, 0x82345678, i);
|
||||
}
|
||||
|
||||
void *glue(_test_, OP) __init_call = glue(test_, OP);
|
||||
|
||||
#undef OP
|
||||
#undef OP_CC
|
||||
#undef OP_SHIFTD
|
||||
#undef OP_NOBYTE
|
||||
#undef EXECSHIFT
|
||||
|
||||
@@ -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:
|
||||
|
||||
+1291
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
|
||||
#define exec_op glue(exec_, OP)
|
||||
#define exec_opl glue(glue(exec_, OP), l)
|
||||
#define exec_opw glue(glue(exec_, OP), w)
|
||||
#define exec_opb glue(glue(exec_, OP), b)
|
||||
|
||||
#define EXECOP2(size, res, s1, flags) \
|
||||
asm ("push %4\n\t"\
|
||||
"popf\n\t"\
|
||||
stringify(OP) size " %" size "2, %" size "0\n\t" \
|
||||
"pushf\n\t"\
|
||||
"popl %1\n\t"\
|
||||
: "=q" (res), "=g" (flags)\
|
||||
: "q" (s1), "0" (res), "1" (flags));
|
||||
|
||||
#define EXECOP1(size, res, flags) \
|
||||
asm ("push %3\n\t"\
|
||||
"popf\n\t"\
|
||||
stringify(OP) size " %" size "0\n\t" \
|
||||
"pushf\n\t"\
|
||||
"popl %1\n\t"\
|
||||
: "=q" (res), "=g" (flags)\
|
||||
: "0" (res), "1" (flags));
|
||||
|
||||
#ifdef OP1
|
||||
void exec_opl(int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECOP1("", res, flags);
|
||||
printf("%-10s A=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "l", s0, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void exec_opw(int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECOP1("w", res, flags);
|
||||
printf("%-10s A=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "w", s0, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void exec_opb(int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECOP1("b", res, flags);
|
||||
printf("%-10s A=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "b", s0, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
#else
|
||||
void exec_opl(int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECOP2("", res, s1, flags);
|
||||
printf("%-10s A=%08x B=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "l", s0, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void exec_opw(int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECOP2("w", res, s1, flags);
|
||||
printf("%-10s A=%08x B=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "w", s0, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
|
||||
void exec_opb(int s0, int s1, int iflags)
|
||||
{
|
||||
int res, flags;
|
||||
res = s0;
|
||||
flags = iflags;
|
||||
EXECOP2("b", res, s1, flags);
|
||||
printf("%-10s A=%08x B=%08x R=%08x CCIN=%04x CC=%04x\n",
|
||||
stringify(OP) "b", s0, s1, res, iflags, flags & CC_MASK);
|
||||
}
|
||||
#endif
|
||||
|
||||
void exec_op(int s0, int s1)
|
||||
{
|
||||
exec_opl(s0, s1, 0);
|
||||
exec_opw(s0, s1, 0);
|
||||
exec_opb(s0, s1, 0);
|
||||
#ifdef OP_CC
|
||||
exec_opl(s0, s1, CC_C);
|
||||
exec_opw(s0, s1, CC_C);
|
||||
exec_opb(s0, s1, CC_C);
|
||||
#endif
|
||||
}
|
||||
|
||||
void glue(test_, OP)(void)
|
||||
{
|
||||
exec_op(0x12345678, 0x812FADA);
|
||||
exec_op(0x12341, 0x12341);
|
||||
exec_op(0x12341, -0x12341);
|
||||
exec_op(0xffffffff, 0);
|
||||
exec_op(0xffffffff, -1);
|
||||
exec_op(0xffffffff, 1);
|
||||
exec_op(0xffffffff, 2);
|
||||
exec_op(0x7fffffff, 0);
|
||||
exec_op(0x7fffffff, 1);
|
||||
exec_op(0x7fffffff, -1);
|
||||
exec_op(0x80000000, -1);
|
||||
exec_op(0x80000000, 1);
|
||||
exec_op(0x80000000, -2);
|
||||
exec_op(0x12347fff, 0);
|
||||
exec_op(0x12347fff, 1);
|
||||
exec_op(0x12347fff, -1);
|
||||
exec_op(0x12348000, -1);
|
||||
exec_op(0x12348000, 1);
|
||||
exec_op(0x12348000, -2);
|
||||
exec_op(0x12347f7f, 0);
|
||||
exec_op(0x12347f7f, 1);
|
||||
exec_op(0x12347f7f, -1);
|
||||
exec_op(0x12348080, -1);
|
||||
exec_op(0x12348080, 1);
|
||||
exec_op(0x12348080, -2);
|
||||
}
|
||||
|
||||
void *glue(_test_, OP) __init_call = glue(test_, OP);
|
||||
|
||||
#undef OP
|
||||
#undef OP_CC
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sched.h>
|
||||
|
||||
int thread1_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
|
||||
for(i=0;i<10;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thread2_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
for(i=0;i<20;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(120 * 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STACK_SIZE 16384
|
||||
|
||||
void test_clone(void)
|
||||
{
|
||||
uint8_t *stack1, *stack2;
|
||||
int pid1, pid2, status1, status2;
|
||||
|
||||
stack1 = malloc(STACK_SIZE);
|
||||
pid1 = clone(thread1_func, stack1 + STACK_SIZE,
|
||||
CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1");
|
||||
|
||||
stack2 = malloc(STACK_SIZE);
|
||||
pid2 = clone(thread2_func, stack2 + STACK_SIZE,
|
||||
CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2");
|
||||
|
||||
while (waitpid(pid1, &status1, 0) != pid1);
|
||||
while (waitpid(pid2, &status2, 0) != pid2);
|
||||
printf("status1=0x%x\n", status1);
|
||||
printf("status2=0x%x\n", status2);
|
||||
printf("End of clone test.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_clone();
|
||||
return 0;
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <setjmp.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
jmp_buf jmp_env;
|
||||
|
||||
void alarm_handler(int sig)
|
||||
{
|
||||
printf("alarm signal=%d\n", 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 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)
|
||||
{
|
||||
struct ucontext *uc = puc;
|
||||
|
||||
printf("%s: si_signo=%d si_errno=%d si_code=%d si_addr=0x%08lx\n",
|
||||
strsignal(info->si_signo),
|
||||
info->si_signo, info->si_errno, info->si_code,
|
||||
(unsigned long)info->si_addr);
|
||||
dump_regs(uc);
|
||||
longjmp(jmp_env, 1);
|
||||
}
|
||||
|
||||
int v1;
|
||||
int tab[2];
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
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(SIGTRAP, &act, NULL);
|
||||
|
||||
/* test division by zero reporting */
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now divide by zero */
|
||||
v1 = 0;
|
||||
v1 = 2 / v1;
|
||||
}
|
||||
|
||||
/* test illegal instruction reporting */
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now execute an invalid instruction */
|
||||
asm volatile("ud2");
|
||||
}
|
||||
|
||||
/* test SEGV reporting */
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now store in an invalid address */
|
||||
*(char *)0x1234 = 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;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sched.h>
|
||||
|
||||
void *thread1_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
|
||||
for(i=0;i<10;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *thread2_func(void *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
for(i=0;i<20;i++) {
|
||||
snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
|
||||
write(1, buf, strlen(buf));
|
||||
usleep(150 * 1000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void test_pthread(void)
|
||||
{
|
||||
pthread_t tid1, tid2;
|
||||
|
||||
pthread_create(&tid1, NULL, thread1_func, "hello1");
|
||||
pthread_create(&tid2, NULL, thread2_func, "hello2");
|
||||
pthread_join(tid1, NULL);
|
||||
pthread_join(tid2, NULL);
|
||||
printf("End of pthread test.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_pthread();
|
||||
return 0;
|
||||
}
|
||||
@@ -3,25 +3,25 @@
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "gemu.h"
|
||||
#include "qemu.h"
|
||||
#include "thunk.h"
|
||||
|
||||
//#define DEBUG
|
||||
@@ -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);
|
||||
|
||||
@@ -1,24 +1,76 @@
|
||||
/*
|
||||
* Generic thunking code to convert data between host and target CPU
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#ifndef THUNK_H
|
||||
#define THUNK_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
#include <byteswap.h>
|
||||
#else
|
||||
|
||||
#define bswap_16(x) \
|
||||
({ \
|
||||
uint16_t __x = (x); \
|
||||
((uint16_t)( \
|
||||
(((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \
|
||||
(((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \
|
||||
})
|
||||
|
||||
#define bswap_32(x) \
|
||||
({ \
|
||||
uint32_t __x = (x); \
|
||||
((uint32_t)( \
|
||||
(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
|
||||
})
|
||||
|
||||
#define bswap_64(x) \
|
||||
({ \
|
||||
uint64_t __x = (x); \
|
||||
((uint64_t)( \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
|
||||
})
|
||||
|
||||
#undef WORDS_BIGENDIAN
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define WORDS_BIGENDIAN
|
||||
#endif
|
||||
|
||||
#ifdef WORD_BIGENDIAN
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define BSWAP_NEEDED
|
||||
#endif
|
||||
|
||||
/* XXX: auto autoconf */
|
||||
/* XXX: autoconf */
|
||||
#define TARGET_I386
|
||||
#define TARGET_LONG_BITS 32
|
||||
|
||||
|
||||
#if defined(__alpha__)
|
||||
#if defined(__alpha__) || defined (__ia64__)
|
||||
#define HOST_LONG_BITS 64
|
||||
#else
|
||||
#define HOST_LONG_BITS 32
|
||||
@@ -42,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);
|
||||
}
|
||||
@@ -74,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);
|
||||
}
|
||||
@@ -106,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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+4048
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user