mirror of
https://github.com/golang/go.git
synced 2026-01-29 15:12:08 +03:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40cbf58f96 | ||
|
|
3cca924e64 | ||
|
|
377a8a806b | ||
|
|
8ad1dc42ad | ||
|
|
6b26788c55 | ||
|
|
9f9b95bb59 | ||
|
|
3facf7eceb | ||
|
|
bebbc62402 | ||
|
|
2876020ee7 | ||
|
|
2246923267 | ||
|
|
61e1caee5b | ||
|
|
497e4a8093 | ||
|
|
edc3452f4a | ||
|
|
a28f3ef1c2 | ||
|
|
55c62d6e32 | ||
|
|
aca4fa5cf5 | ||
|
|
0c64370422 | ||
|
|
29a22abfc8 | ||
|
|
08ea82529a | ||
|
|
7ab4cba9ad | ||
|
|
2a6c7739b5 | ||
|
|
a10b27094c | ||
|
|
e49330911f | ||
|
|
20a053611b | ||
|
|
a8e839bb55 | ||
|
|
ecb9ce6a78 | ||
|
|
5dda3bc2fd | ||
|
|
c0c730da28 | ||
|
|
8b3f554d86 | ||
|
|
244294f821 | ||
|
|
36fe6f2d5d | ||
|
|
6a18122614 | ||
|
|
a89768461f | ||
|
|
2cfde41520 | ||
|
|
095710b39e | ||
|
|
0b5982f08e | ||
|
|
d3a413569b | ||
|
|
fc0f36b2ce | ||
|
|
9f59bc85a2 | ||
|
|
71a7647662 | ||
|
|
c257dfb178 | ||
|
|
6f21d7cc26 | ||
|
|
3c5af9a846 | ||
|
|
f2e4c8b5fb | ||
|
|
e7915b2727 | ||
|
|
23c646c226 | ||
|
|
71387ff53f | ||
|
|
5e1648d5fe | ||
|
|
7765a5b7d0 | ||
|
|
878aef8bf9 | ||
|
|
f265044a48 | ||
|
|
c0dd201113 | ||
|
|
a7e30ce931 | ||
|
|
1d11801f40 | ||
|
|
0507802396 | ||
|
|
23ef1e1933 | ||
|
|
f7d7403776 | ||
|
|
e4e59921f1 | ||
|
|
13d03fae63 | ||
|
|
97ec0a816b | ||
|
|
2dfb0eb6c2 | ||
|
|
d4f1309372 | ||
|
|
c20b8e145a | ||
|
|
5aa3ba8673 | ||
|
|
a1350a1f7b | ||
|
|
00dbc5a548 | ||
|
|
bb03defe93 | ||
|
|
a30edb8071 | ||
|
|
0d20a61e68 | ||
|
|
e7f6a244a2 |
1
AUTHORS
1
AUTHORS
@@ -411,6 +411,7 @@ Oliver Hookins <ohookins@gmail.com>
|
||||
Olivier Antoine <olivier.antoine@gmail.com>
|
||||
Olivier Duperray <duperray.olivier@gmail.com>
|
||||
Olivier Saingre <osaingre@gmail.com>
|
||||
Oracle
|
||||
Padraig Kitterick <padraigkitterick@gmail.com>
|
||||
Palm Stone Games
|
||||
Paolo Giarrusso <p.giarrusso@gmail.com>
|
||||
|
||||
273
doc/conduct.html
Normal file
273
doc/conduct.html
Normal file
@@ -0,0 +1,273 @@
|
||||
<!--{
|
||||
"Title": "Go Community Code of Conduct",
|
||||
"Path": "/conduct",
|
||||
"Template": true
|
||||
}-->
|
||||
|
||||
<style>
|
||||
ul {
|
||||
max-width: 800px;
|
||||
}
|
||||
ul ul {
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h2>About the Code of Conduct</h2>
|
||||
|
||||
<h3>Why have a Code of Conduct?</h3>
|
||||
|
||||
<p>
|
||||
Online communities include people from many different backgrounds.
|
||||
The Go contributors are committed to providing a friendly, safe and welcoming
|
||||
environment for all, regardless of age, disability, gender, nationality, race,
|
||||
religion, sexuality, or similar personal characteristic.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The first goal of the Code of Conduct is to specify a baseline standard
|
||||
of behavior so that people with different social values and communication
|
||||
styles can talk about Go effectively, productively, and respectfully.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The second goal is to provide a mechanism for resolving conflicts in the
|
||||
community when they arise.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The third goal of the Code of Conduct is to make our community welcoming to
|
||||
people from different backgrounds.
|
||||
Diversity is critical to the project; for Go to be successful, it needs
|
||||
contributors and users from all backgrounds.
|
||||
(See <a href="https://blog.golang.org/open-source">Go, Open Source, Community</a>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
With that said, a healthy community must allow for disagreement and debate.
|
||||
The Code of Conduct is not a mechanism for people to silence others with whom
|
||||
they disagree.
|
||||
</p>
|
||||
|
||||
<h3>Where does the Code of Conduct apply?</h3>
|
||||
|
||||
<p>
|
||||
If you participate in or contribute to the Go ecosystem in any way,
|
||||
you are encouraged to follow the Code of Conduct while doing so.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Explicit enforcement of the Code of Conduct applies to the
|
||||
official forums operated by the Go project (“Go spaces”):
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>The official <a href="https://github.com/golang/">GitHub projects</a>
|
||||
and <a href="https://go-review.googlesource.com/">code reviews</a>.
|
||||
<li>The <a href="https://groups.google.com/group/golang-nuts">golang-nuts</a> and
|
||||
<a href="https://groups.google.com/group/golang-dev">golang-dev</a> mailing lists.
|
||||
<li>The #go-nuts IRC channel on Freenode.
|
||||
<li>The <a href="https://reddit.com/r/golang">/r/golang subreddit</a>.
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Other Go groups (such as conferences, meetups, and other unofficial forums) are
|
||||
encouraged to adopt this Code of Conduct. Those groups must provide their own
|
||||
moderators and/or working group (see below).
|
||||
</p>
|
||||
|
||||
<h2>Gopher values</h2>
|
||||
|
||||
<p>
|
||||
These are the values to which people in the Go community (“Gophers”) should aspire.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Be friendly and welcoming
|
||||
<li>Be patient
|
||||
<ul>
|
||||
<li>Remember that people have varying communication styles and that not
|
||||
everyone is using their native language.
|
||||
(Meaning and tone can be lost in translation.)
|
||||
</ul>
|
||||
<li>Be thoughtful
|
||||
<ul>
|
||||
<li>Productive communication requires effort.
|
||||
Think about how your words will be interpreted.
|
||||
<li>Remember that sometimes it is best to refrain entirely from commenting.
|
||||
</ul>
|
||||
<li>Be respectful
|
||||
<ul>
|
||||
<li>In particular, respect differences of opinion.
|
||||
</ul>
|
||||
<li>Be charitable
|
||||
<ul>
|
||||
<li>Interpret the arguments of others in good faith, do not seek to disagree.
|
||||
<li>When we do disagree, try to understand why.
|
||||
</ul>
|
||||
<li>Avoid destructive behavior:
|
||||
<ul>
|
||||
<li>Derailing: stay on topic; if you want to talk about something else,
|
||||
start a new conversation.
|
||||
<li>Unconstructive criticism: don't merely decry the current state of affairs;
|
||||
offer—or at least solicit—suggestions as to how things may be improved.
|
||||
<li>Snarking (pithy, unproductive, sniping comments)
|
||||
<li>Discussing potentially offensive or sensitive issues;
|
||||
this all too often leads to unnecessary conflict.
|
||||
<li>Microaggressions: brief and commonplace verbal, behavioral and
|
||||
environmental indignities that communicate hostile, derogatory or negative
|
||||
slights and insults to a person or group.
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
People are complicated.
|
||||
You should expect to be misunderstood and to misunderstand others;
|
||||
when this inevitably occurs, resist the urge to be defensive or assign blame.
|
||||
Try not to take offense where no offense was intended.
|
||||
Give people the benefit of the doubt.
|
||||
Even if the intent was to provoke, do not rise to it.
|
||||
It is the responsibility of <i>all parties</i> to de-escalate conflict when it arises.
|
||||
</p>
|
||||
|
||||
<h2>Unwelcome behavior</h2>
|
||||
|
||||
<p>
|
||||
These actions are explicitly forbidden in Go spaces:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Insulting, demeaning, hateful, or threatening remarks.
|
||||
<li>Discrimination based on age, disability, gender, nationality, race,
|
||||
religion, sexuality, or similar personal characteristic.
|
||||
<li>Bullying or systematic harassment.
|
||||
<li>Unwelcome sexual advances.
|
||||
<li>Incitement to any of these.
|
||||
</ul>
|
||||
|
||||
<h2>Moderation</h2>
|
||||
|
||||
<p>
|
||||
The Go spaces are not free speech venues; they are for discussion about Go.
|
||||
These spaces have moderators.
|
||||
The goal of the moderators is to facilitate civil discussion about Go.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When using the official Go spaces you should act in the spirit of the “Gopher
|
||||
values”.
|
||||
If you conduct yourself in a way that is explicitly forbidden by the CoC,
|
||||
you will be warned and asked to stop.
|
||||
If you do not stop, you will be removed from our community spaces temporarily.
|
||||
Repeated, wilful breaches of the CoC will result in a permanent ban.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Moderators are held to a higher standard than other community members.
|
||||
If a moderator creates an inappropriate situation, they should expect less
|
||||
leeway than others, and should expect to be removed from their position if they
|
||||
cannot adhere to the CoC.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Complaints about moderator actions must be handled using the reporting process
|
||||
below.
|
||||
</p>
|
||||
|
||||
<h2>Reporting issues</h2>
|
||||
|
||||
<p>
|
||||
The Code of Conduct Working Group is a group of people that represent the Go
|
||||
community. They are responsible for handling conduct-related issues.
|
||||
Their purpose is to de-escalate conflicts and try to resolve issues to the
|
||||
satisfaction of all parties. They are:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Aditya Mukerjee <dev@chimeracoder.net>
|
||||
<li>Andrew Gerrand <adg@golang.org>
|
||||
<li>Dave Cheney <dave@cheney.net>
|
||||
<li>Jason Buberel <jbuberel@google.com>
|
||||
<li>Peggy Li <peggyli.224@gmail.com>
|
||||
<li>Sarah Adams <sadams.codes@gmail.com>
|
||||
<li>Steve Francia <steve.francia@gmail.com>
|
||||
<li>Verónica López <gveronicalg@gmail.com>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
If you encounter a conduct-related issue, you should report it to the
|
||||
Working Group using the process described below.
|
||||
<b>Do not</b> post about the issue publicly or try to rally sentiment against a
|
||||
particular individual or group.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Mail <a href="mailto:conduct@golang.org">conduct@golang.org</a> or
|
||||
<a href="https://golang.org/s/conduct-report">submit an anonymous report</a>.
|
||||
<ul>
|
||||
<li>Your message will reach the Working Group.
|
||||
<li>Reports are confidential within the Working Group.
|
||||
<li>Should you choose to remain anonymous then the Working Group cannot
|
||||
notify you of the outcome of your report.
|
||||
<li>You may contact a member of the group directly if you do not feel
|
||||
comfortable contacting the group as a whole. That member will then raise
|
||||
the issue with the Working Group as a whole, preserving the privacy of the
|
||||
reporter (if desired).
|
||||
<li>If your report concerns a member of the Working Group they will be recused
|
||||
from Working Group discussions of the report.
|
||||
<li>The Working Group will strive to handle reports with discretion and
|
||||
sensitivity, to protect the privacy of the involved parties,
|
||||
and to avoid conflicts of interest.
|
||||
</ul>
|
||||
<li>You should receive a response within 48 hours (likely sooner).
|
||||
(Should you choose to contact a single Working Group member,
|
||||
it may take longer to receive a response.)
|
||||
<li>The Working Group will meet to review the incident and determine what happened.
|
||||
<ul>
|
||||
<li>With the permission of person reporting the incident, the Working Group
|
||||
may reach out to other community members for more context.
|
||||
</ul>
|
||||
<li>The Working Group will reach a decision as to how to act. These may include:
|
||||
<ul>
|
||||
<li>Nothing.
|
||||
<li>A request for a private or public apology.
|
||||
<li>A private or public warning.
|
||||
<li>An imposed vacation (for instance, asking someone to abstain for a week
|
||||
from a mailing list or IRC).
|
||||
<li>A permanent or temporary ban from some or all Go spaces.
|
||||
</ul>
|
||||
<li>The Working Group will reach out to the original reporter to let them know
|
||||
the decision.
|
||||
<li>Appeals to the decision may be made to the Working Group,
|
||||
or to any of its members directly.
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<b>Note that the goal of the Code of Conduct and the Working Group is to resolve
|
||||
conflicts in the most harmonious way possible.</b>
|
||||
We hope that in most cases issues may be resolved through polite discussion and
|
||||
mutual agreement.
|
||||
Bannings and other forceful measures are to be employed only as a last resort.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Changes to the Code of Conduct (including to the members of the Working Group)
|
||||
should be proposed using the
|
||||
<a href="https://golang.org/s/proposal-process">change proposal process</a>.
|
||||
</p>
|
||||
|
||||
<h2>Summary</h2>
|
||||
|
||||
<ul>
|
||||
<li>Treat everyone with respect and kindness.
|
||||
<li>Be thoughtful in how you communicate.
|
||||
<li>Don’t be destructive or inflammatory.
|
||||
<li>If you encounter an issue, please mail <a href="mailto:conduct@golang.org">conduct@golang.org</a>.
|
||||
</ul>
|
||||
|
||||
<h3>Acknowledgements</h3>
|
||||
|
||||
<p>
|
||||
Parts of this document were derived from the Code of Conduct documents of the
|
||||
Django, FreeBSD, and Rust projects.
|
||||
</p>
|
||||
@@ -93,7 +93,15 @@ We pride ourselves on being meticulous; no issue is too small.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Sensitive security-related issues should be reported to <a href="mailto:security@golang.org">security@golang.org</a>.
|
||||
Security-related issues should be reported to
|
||||
<a href="mailto:security@golang.org">security@golang.org</a>.<br>
|
||||
See the <a href="/security">security policy</a> for more details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Community-related issues should be reported to
|
||||
<a href="mailto:conduct@golang.org">conduct@golang.org</a>.<br>
|
||||
See the <a href="/conduct">Code of Conduct</a> for more details.
|
||||
</p>
|
||||
|
||||
<h3><a href="/doc/contribute.html">Contributing code</a></h3>
|
||||
|
||||
@@ -19,6 +19,24 @@ Go 1.5 is a major release of Go.
|
||||
Read the <a href="/doc/go1.5">Go 1.5 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.5.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.5.2 (released 2015/12/02) includes bug fixes to the compiler, linker, and
|
||||
the <code>mime/multipart</code>, <code>net</code>, and <code>runtime</code>
|
||||
packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.5.2">Go
|
||||
1.5.2 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.5.1 (released 2015/09/08) includes bug fixes to the compiler, assembler, and
|
||||
the <code>fmt</code>, <code>net/textproto</code>, <code>net/http</code>, and
|
||||
<code>runtime</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.5.1">Go
|
||||
1.5.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.4">go1.4 (released 2014/12/10)</h2>
|
||||
|
||||
<p>
|
||||
@@ -38,6 +56,11 @@ go1.4.2 (released 2015/02/17) includes bug fixes to the <code>go</code> command,
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.4.2">Go 1.4.2 milestone on our issue tracker</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.4.3 (released 2015/09/22) includes security fixes to the <code>net/http</code> package and bug fixes to the <code>runtime</code> package.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.4.3">Go 1.4.3 milestone on our issue tracker</a> for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.3">go1.3 (released 2014/06/18)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -504,6 +504,13 @@ SWIG support has been updated such that
|
||||
now require SWIG 3.0.6 or later.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The <code>install</code> subcommand now removes the
|
||||
binary created by the <code>build</code> subcommand
|
||||
in the source directory, if present,
|
||||
to avoid problems having two binaries present in the tree.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The <code>std</code> (standard library) wildcard package name
|
||||
now excludes commands.
|
||||
|
||||
@@ -48,3 +48,9 @@ Each month in places around the world, groups of Go programmers ("gophers")
|
||||
meet to talk about Go. Find a chapter near you.
|
||||
</p>
|
||||
|
||||
<h3 id="conduct"><a href="/conduct">Code of Conduct</a></h3>
|
||||
<p>
|
||||
Guidelines for participating in Go community spaces
|
||||
and a reporting process for handling issues.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ Then clone the repository and check out the latest release tag:</p>
|
||||
<pre>
|
||||
$ git clone https://go.googlesource.com/go
|
||||
$ cd go
|
||||
$ git checkout go1.5
|
||||
$ git checkout go1.5.2
|
||||
</pre>
|
||||
|
||||
<h2 id="head">(Optional) Switch to the master branch</h2>
|
||||
@@ -346,7 +346,7 @@ New releases are announced on the
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list.
|
||||
Each announcement mentions the latest release tag, for instance,
|
||||
<code>go1.5</code>.
|
||||
<code>go1.5.2</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!--{
|
||||
"Path": "/"
|
||||
"Path": "/",
|
||||
"Template": true
|
||||
}-->
|
||||
|
||||
<div class="left">
|
||||
@@ -25,7 +26,9 @@ Hello, 世界
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<a class="run" href="#" title="Run this code [shift-enter]">Run</a>
|
||||
{{if $.Share}}
|
||||
<a class="share" href="#" title="Share this code">Share</a>
|
||||
{{end}}
|
||||
<a class="tour" href="//tour.golang.org/" title="Learn Go from your browser">Tour</a>
|
||||
</div>
|
||||
<div class="toys">
|
||||
|
||||
174
doc/security.html
Normal file
174
doc/security.html
Normal file
@@ -0,0 +1,174 @@
|
||||
<!--{
|
||||
"Title": "Go Security Policy",
|
||||
"Path": "/security",
|
||||
"Template": true
|
||||
}-->
|
||||
|
||||
<h2>Implementation</h2>
|
||||
|
||||
<h3>Reporting a Security Bug</h3>
|
||||
|
||||
<p>
|
||||
Please report to us any issues you find.
|
||||
This document explains how to do that and what to expect in return.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
All security bugs in the Go distribution should be reported by email to
|
||||
<a href="mailto:security@golang.org">security@golang.org</a>.
|
||||
This mail is delivered to a small security team.
|
||||
Your email will be acknowledged within 24 hours, and you'll receive a more
|
||||
detailed response to your email within 72 hours indicating the next steps in
|
||||
handling your report.
|
||||
If you would like, you can encrypt your report using our PGP key (listed below).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Please use a descriptive subject line for your report email.
|
||||
After the initial reply to your report, the security team will endeavor to keep
|
||||
you informed of the progress being made towards a fix and full announcement.
|
||||
These updates will be sent at least every five days.
|
||||
In reality, this is more likely to be every 24-48 hours.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you have not received a reply to your email within 48 hours or you have not
|
||||
heard from the security team for the past five days please contact the Go
|
||||
security team directly:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Primary security coordinator: <a href="mailto:adg@golang.org">Andrew Gerrand</a> (<a href="https://drive.google.com/a/google.com/file/d/0B42ZAZN5yFufRldybEVNandRN2c/view">public key</a>).</li>
|
||||
<li>Secondary coordinator: <a href="mailto:agl@golang.org">Adam Langley</a> (<a href="https://www.imperialviolet.org/key.asc">public key</a>).</li>
|
||||
<li>If you receive no response, mail <a href="mailto:golang-dev@googlegroups.com">golang-dev@googlegroups.com</a> or use the <a href="https://groups.google.com/forum/#!forum/golang-dev">golang-dev web interface</a>.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Please note that golang-dev is a public discussion forum.
|
||||
When escalating on this list, please do not disclose the details of the issue.
|
||||
Simply state that you're trying to reach a member of the security team.
|
||||
</p>
|
||||
|
||||
<h3>Flagging Existing Issues as Security-related</h3>
|
||||
|
||||
<p>
|
||||
If you believe that an <a href="https://golang.org/issue">existing issue</a>
|
||||
is security-related, we ask that you send an email to
|
||||
<a href="mailto:security@golang.org">security@golang.org</a>.
|
||||
The email should include the issue ID and a short description of why it should
|
||||
be handled according to this security policy.
|
||||
</p>
|
||||
|
||||
<h3>Disclosure Process</h3>
|
||||
|
||||
<p>The Go project uses the following disclosure process:</p>
|
||||
|
||||
<ol>
|
||||
<li>Once the security report is received it is assigned a primary handler.
|
||||
This person coordinates the fix and release process.</li>
|
||||
<li>The issue is confirmed and a list of affected software is determined.</li>
|
||||
<li>Code is audited to find any potential similar problems.</li>
|
||||
<li>If it is determined, in consultation with the submitter, that a CVE-ID is
|
||||
required, the primary handler obtains one via email to
|
||||
<a href="http://oss-security.openwall.org/wiki/mailing-lists/distros">oss-distros</a>.</li>
|
||||
<li>Fixes are prepared for the current stable release and the head/master
|
||||
revision. These fixes are not yet committed to the public repository.</li>
|
||||
<li>A notification is sent to the
|
||||
<a href="https://groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list to give users time to prepare their systems for the update.</li>
|
||||
<li>Three working days following this notification, the fixes are applied to
|
||||
the <a href="https://go.googlesource.com/go">public repository</a> and a new
|
||||
Go release is issued.</li>
|
||||
<li>On the date that the fixes are applied, announcements are sent to
|
||||
<a href="https://groups.google.com/group/golang-announce">golang-announce</a>,
|
||||
<a href="https://groups.google.com/group/golang-dev">golang-dev</a>, and
|
||||
<a href="https://groups.google.com/group/golang-nuts">golang-nuts</a>.
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
This process can take some time, especially when coordination is required with
|
||||
maintainers of other projects. Every effort will be made to handle the bug in
|
||||
as timely a manner as possible, however it's important that we follow the
|
||||
process described above to ensure that disclosures are handled consistently.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For security issues that include the assignment of a CVE-ID,
|
||||
the issue is listed publicly under the
|
||||
<a href="https://www.cvedetails.com/vulnerability-list/vendor_id-14185/Golang.html">"Golang" product on the CVEDetails website</a>
|
||||
as well as the
|
||||
<a href="https://web.nvd.nist.gov/view/vuln/search">National Vulnerability Disclosure site</a>.
|
||||
</p>
|
||||
|
||||
<h3>Receiving Security Updates</h3>
|
||||
|
||||
<p>
|
||||
The best way to receive security announcements is to subscribe to the
|
||||
<a href="https://groups.google.com/forum/#!forum/golang-announce">golang-announce</a>
|
||||
mailing list. Any messages pertaining to a security issue will be prefixed
|
||||
with <code>[security]</code>.
|
||||
</p>
|
||||
|
||||
<h3>Comments on This Policy</h3>
|
||||
|
||||
<p>
|
||||
If you have any suggestions to improve this policy, please send an email to
|
||||
<a href="mailto:golang-dev@golang.org">golang-dev@golang.org</a> for discussion.
|
||||
</p>
|
||||
|
||||
<h3>PGP Key for <a href="mailto:security@golang.org">security@golang.org</a></h3>
|
||||
|
||||
<pre>
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Comment: GPGTools - https://gpgtools.org
|
||||
|
||||
mQINBFXI1h0BEADZdm05GDFWvjmQKutUVb0cJKS+VR+6XU3g/YQZGC8tnIL6i7te
|
||||
+fPJHfQc2uIw0xeBgZX4Ni/S8yIqsbIjqYeaToX7QFUufJDQwrmlQRDVAvvT5HBT
|
||||
J80JEs7yHRreFoLzB6dnWehWXzWle4gFKeIy+hvLrYquZVvbeEYTnX7fNzZg0+5L
|
||||
ksvj7lnQlJIy1l3sL/7uPr9qsm45/hzd0WjTQS85Ry6Na3tMwRpqGENDh25Blz75
|
||||
8JgK9JmtTJa00my1zzeCXU04CKKEMRbkMLozzudOH4ZLiLWcFiKRpeCn860wC8l3
|
||||
oJcyyObuTSbr9o05ra3On+epjCEFkknGX1WxPv+TV34i0a23AtuVyTCloKb7RYXc
|
||||
7mUaskZpU2rFBqIkzZ4MQJ7RDtGlm5oBy36j2QL63jAZ1cKoT/yvjJNp2ObmWaVF
|
||||
X3tk/nYw2H0YDjTkTCgGtyAOj3Cfqrtsa5L0jG5K2p4RY8mtVgQ5EOh7QxuS+rmN
|
||||
JiA39SWh7O6uFCwkz/OCXzqeh6/nP10HAb9S9IC34QQxm7Fhd0ZXzEv9IlBTIRzk
|
||||
xddSdACPnLE1gJcFHxBd2LTqS/lmAFShCsf8S252kagKJfHRebQJZHCIs6kT9PfE
|
||||
0muq6KRKeDXv01afAUvoB4QW/3chUrtgL2HryyO8ugMu7leVGmoZhFkIrQARAQAB
|
||||
tCZHbyBTZWN1cml0eSBUZWFtIDxzZWN1cml0eUBnb2xhbmcub3JnPokCPQQTAQoA
|
||||
JwUCVcjWHQIbAwUJB4YfgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRA6RtGR
|
||||
eVpYOLnDD/9YVTd6DTwdJq6irVfM/ICPlPTXB0JLERqCI1Veptcp56eQoJ0XWGQp
|
||||
tkGlgbvmCzFo0B+65Te7YA4R3oyBCXd6JgyWQQPy5p60FHyuuCPVAReclSWyt9f2
|
||||
Yj/u4DjghKhELOvPiI96egcU3g9jrEEcPjm7JYkc9M2gVSNOnnJvcD7wpQJNCzon
|
||||
51eMZ1ZyfA5UCBTa0SaT9eXg5zwNlYQnB6ZF6TjXezkhLqlTsBuHxoNVf+9vCC0o
|
||||
ZKIM2ovptMx9eEguTDKWaQ7tero7Zs/q5fwk/MDzM/LGJ9aXy2RCtqBxv46vDS7G
|
||||
fCNq+aPD/wyFd6hxQkvkua6hgZwYT+cJWHYA2Yv0LO3BYOJdjfc+j2hjv+mC9lF0
|
||||
UpWhCVJv3hHoFaxnz62GdROzf2wXz6aR9Saj1rYSvqT9jC20VInxqMufXNN2sbpo
|
||||
Kyk6MTbAeepphQpfAWQv+ltWgBiEjuFxYdwv/vmw20996JV7O8nqkeCUW84B6su+
|
||||
Y3bbdP9o3DBtOT0j9LTB/FucmdNCNHoO+EnNBKJd6FoYTGLWi3Rq9DLx2V9tdJHo
|
||||
Bn67dymcl+iyp337HJNY+qS+KCgoqAWlxkzXRiXKb/yluhXdIkqhg4kL8JPAJvfS
|
||||
cs7Zn67Mx04ixJnRMYCDmxtD4xPsFMzM7g8m3PQp+nE7WhujM/ImM7kCDQRVyNYd
|
||||
ARAAlw9H/1ybQs4K3XKA1joII16rta9KS7ew76+agXo0jeSRwMEQfItOxYvfhmo8
|
||||
+ydn5TWsTbifGU8L3+EBTMRRyzWhbaGO0Wizw7BTVJ7n5JW+ndPrcUpp/ilUk6AU
|
||||
VxaO/8/R+9+VJZpoeoLHXYloFGNuX58GLIy1jSBvLsLl/Ki5IOrHvD1GK6TftOl5
|
||||
j8IPC1LSBrwGJO803x7wUdQP/tsKN/QPR8pnBntrEgrQFSI+Q3qrCvVMmXnBlYum
|
||||
jfOBt8pKMgB9/ix+HWN8piQNQiJxD+XjEM6XwUmQqIR7y5GINKWgundCmtYIzVgY
|
||||
9p2Br6UPrTJi12LfKv5s2R6NnxFHv/ad29CpPTeLJRsSqFfqBL969BCpj/isXmQE
|
||||
m4FtziZidARXo12KiGAnPF9otirNHp4+8hwNB3scf7cI53y8nZivO9cwI7BoClY6
|
||||
ZIabjDcJxjK+24emoz3mJ5SHpZpQLSb9o8GbLLfXOq+4uzEX2A30fhrtsQb/x0GM
|
||||
4v3EU1aP2mjuksyYbgldtY64tD35wqAA9mVl5Ux+g1HoUBvLw0h+lzwh370NJw//
|
||||
ITvBQVUtDMB96rfIP4fL5pYl5pmRz+vsuJ0iXzm05qBgKfSqO7To9SWxQPdX89R4
|
||||
u0/XVAlw0Ak9Zceq3W96vseEUTR3aoZCMIPiwfcDaq60rWUAEQEAAYkCJQQYAQoA
|
||||
DwUCVcjWHQIbDAUJB4YfgAAKCRA6RtGReVpYOEg/EADZcIYw4q1jAbDkDy3LQG07
|
||||
AR8QmLp/RDp72RKbCSIYyvyXEnmrhUg98lUG676qTH+Y7dlEX107dLhFuKEYyV8D
|
||||
ZalrFQO/3WpLWdIAmWrj/wq14qii1rgmy96Nh3EqG3CS50HEMGkW1llRx2rgBvGl
|
||||
pgoTcwOfT+h8s0HlZdIS/cv2wXqwPgMWr1PIk3as1fu1OH8n/BjeGQQnNJEaoBV7
|
||||
El2C/hz3oqf2uYQ1QvpU23F1NrstekxukO8o2Y/fqsgMJqAiNJApUCl/dNhK+W57
|
||||
iicjvPirUQk8MUVEHXKhWIzYxon6aEUTx+xyNMBpRJIZlJ61FxtnZhoPiAFtXVPb
|
||||
+95BRJA9npidlVFjqz9QDK/4NSnJ3KaERR9tTDcvq4zqT22Z1Ai5gWQKqogTz5Mk
|
||||
F+nZwVizW0yi33id9qDpAuApp8o6AiyH5Ql1Bo23bvqS2lMrXPIS/QmPPsA76CBs
|
||||
lYjQwwz8abUD1pPdzyYtMKZUMwhicSFOHFDM4oQN16k2KJuntuih8BKVDCzIOq+E
|
||||
KHyeh1BqWplUtFh1ckxZlXW9p9F7TsWjtfcKaY8hkX0Cr4uVjwAFIjLcAxk67ROe
|
||||
huEb3Gt+lwJz6aNnZUU87ukMAxRVR2LL0btdxgc6z8spl66GXro/LUkXmAdyOEMV
|
||||
UDrmjf9pr7o00hC7lCHFzw==
|
||||
=WE0r
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
</pre>
|
||||
@@ -65,5 +65,6 @@ func Test9026(t *testing.T) { test9026(t) }
|
||||
func Test9557(t *testing.T) { test9557(t) }
|
||||
func Test10303(t *testing.T) { test10303(t, 10) }
|
||||
func Test11925(t *testing.T) { test11925(t) }
|
||||
func Test12030(t *testing.T) { test12030(t) }
|
||||
|
||||
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
|
||||
|
||||
35
misc/cgo/test/issue12030.go
Normal file
35
misc/cgo/test/issue12030.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Issue 12030. sprintf is defined in both ntdll and msvcrt,
|
||||
// Normally we want the one in the msvcrt.
|
||||
|
||||
package cgotest
|
||||
|
||||
/*
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
void issue12030conv(char *buf, double x) {
|
||||
sprintf(buf, "d=%g", x);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func test12030(t *testing.T) {
|
||||
buf := (*C.char)(C.malloc(256))
|
||||
defer C.free(unsafe.Pointer(buf))
|
||||
for _, f := range []float64{1.0, 2.0, 3.14} {
|
||||
C.issue12030conv(buf, C.double(f))
|
||||
got := C.GoString(buf)
|
||||
if want := fmt.Sprintf("d=%g", f); got != want {
|
||||
t.Fatalf("C.sprintf failed for %g: %q != %q", f, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,11 @@ void scatter() {
|
||||
printf("scatter = %p\n", p);
|
||||
}
|
||||
|
||||
// Adding this explicit extern declaration makes this a test for
|
||||
// https://gcc.gnu.org/PR68072 aka https://golang.org/issue/13344 .
|
||||
// It used to cause a cgo error when building with GCC 6.
|
||||
extern int hola;
|
||||
|
||||
// this example is in issue 3253
|
||||
int hola = 0;
|
||||
int testHola() { return hola; }
|
||||
|
||||
@@ -14,11 +14,14 @@ package cgotest
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func testSetgid(t *testing.T) {
|
||||
func runTestSetgid() bool {
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
C.setgid(0)
|
||||
@@ -26,7 +29,21 @@ func testSetgid(t *testing.T) {
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
return true
|
||||
case <-time.After(5 * time.Second):
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testSetgid(t *testing.T) {
|
||||
if !runTestSetgid() {
|
||||
t.Error("setgid hung")
|
||||
}
|
||||
|
||||
// Now try it again after using signal.Notify.
|
||||
signal.Notify(make(chan os.Signal, 1), syscall.SIGINT)
|
||||
if !runTestSetgid() {
|
||||
t.Error("setgid hung after signal.Notify")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,9 +160,6 @@ func run(bin string, args []string) (err error) {
|
||||
}
|
||||
defer os.Chdir(oldwd)
|
||||
|
||||
type waitPanic struct {
|
||||
err error
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if w, ok := r.(waitPanic); ok {
|
||||
@@ -174,14 +171,96 @@ func run(bin string, args []string) (err error) {
|
||||
}()
|
||||
|
||||
defer exec.Command("killall", "ios-deploy").Run() // cleanup
|
||||
|
||||
exec.Command("killall", "ios-deploy").Run()
|
||||
|
||||
var opts options
|
||||
opts, args = parseArgs(args)
|
||||
|
||||
// ios-deploy invokes lldb to give us a shell session with the app.
|
||||
cmd = exec.Command(
|
||||
s, err := newSession(appdir, args, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
b := s.out.Bytes()
|
||||
if err == nil && !debug {
|
||||
i := bytes.Index(b, []byte("(lldb) process continue"))
|
||||
if i > 0 {
|
||||
b = b[i:]
|
||||
}
|
||||
}
|
||||
os.Stdout.Write(b)
|
||||
}()
|
||||
|
||||
// Script LLDB. Oh dear.
|
||||
s.do(`process handle SIGHUP --stop false --pass true --notify false`)
|
||||
s.do(`process handle SIGPIPE --stop false --pass true --notify false`)
|
||||
s.do(`process handle SIGUSR1 --stop false --pass true --notify false`)
|
||||
s.do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work
|
||||
s.do(`process handle SIGBUS --stop false --pass true --notify false`) // does not work
|
||||
|
||||
if opts.lldb {
|
||||
_, err := io.Copy(s.in, os.Stdin)
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
s.do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go
|
||||
|
||||
s.doCmd("run", "stop reason = breakpoint", 20*time.Second)
|
||||
|
||||
// Move the current working directory into the faux gopath.
|
||||
if pkgpath != "src" {
|
||||
s.do(`breakpoint delete 1`)
|
||||
s.do(`expr char* $mem = (char*)malloc(512)`)
|
||||
s.do(`expr $mem = (char*)getwd($mem, 512)`)
|
||||
s.do(`expr $mem = (char*)strcat($mem, "/` + pkgpath + `")`)
|
||||
s.do(`call (void)chdir($mem)`)
|
||||
}
|
||||
|
||||
startTestsLen := s.out.Len()
|
||||
fmt.Fprintln(s.in, `process continue`)
|
||||
|
||||
passed := func(out *buf) bool {
|
||||
// Just to make things fun, lldb sometimes translates \n into \r\n.
|
||||
return s.out.LastIndex([]byte("\nPASS\n")) > startTestsLen ||
|
||||
s.out.LastIndex([]byte("\nPASS\r")) > startTestsLen ||
|
||||
s.out.LastIndex([]byte("\n(lldb) PASS\n")) > startTestsLen ||
|
||||
s.out.LastIndex([]byte("\n(lldb) PASS\r")) > startTestsLen
|
||||
}
|
||||
err = s.wait("test completion", passed, opts.timeout)
|
||||
if passed(s.out) {
|
||||
// The returned lldb error code is usually non-zero.
|
||||
// We check for test success by scanning for the final
|
||||
// PASS returned by the test harness, assuming the worst
|
||||
// in its absence.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type lldbSession struct {
|
||||
cmd *exec.Cmd
|
||||
in *os.File
|
||||
out *buf
|
||||
timedout chan struct{}
|
||||
exited chan error
|
||||
}
|
||||
|
||||
func newSession(appdir string, args []string, opts options) (*lldbSession, error) {
|
||||
lldbr, in, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &lldbSession{
|
||||
in: in,
|
||||
out: new(buf),
|
||||
exited: make(chan error),
|
||||
}
|
||||
|
||||
s.cmd = exec.Command(
|
||||
// lldb tries to be clever with terminals.
|
||||
// So we wrap it in script(1) and be clever
|
||||
// right back at it.
|
||||
@@ -198,267 +277,120 @@ func run(bin string, args []string) (err error) {
|
||||
"--bundle", appdir,
|
||||
)
|
||||
if debug {
|
||||
log.Println(strings.Join(cmd.Args, " "))
|
||||
log.Println(strings.Join(s.cmd.Args, " "))
|
||||
}
|
||||
|
||||
lldbr, lldb, err := os.Pipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := new(bufWriter)
|
||||
var out io.Writer = s.out
|
||||
if opts.lldb {
|
||||
mw := io.MultiWriter(w, os.Stderr)
|
||||
cmd.Stdout = mw
|
||||
cmd.Stderr = mw
|
||||
} else {
|
||||
cmd.Stdout = w
|
||||
cmd.Stderr = w // everything of interest is on stderr
|
||||
out = io.MultiWriter(out, os.Stderr)
|
||||
}
|
||||
cmd.Stdin = lldbr
|
||||
s.cmd.Stdout = out
|
||||
s.cmd.Stderr = out // everything of interest is on stderr
|
||||
s.cmd.Stdin = lldbr
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("ios-deploy failed to start: %v", err)
|
||||
if err := s.cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("ios-deploy failed to start: %v", err)
|
||||
}
|
||||
|
||||
// Manage the -test.timeout here, outside of the test. There is a lot
|
||||
// of moving parts in an iOS test harness (notably lldb) that can
|
||||
// swallow useful stdio or cause its own ruckus.
|
||||
var timedout chan struct{}
|
||||
if opts.timeout > 1*time.Second {
|
||||
timedout = make(chan struct{})
|
||||
s.timedout = make(chan struct{})
|
||||
time.AfterFunc(opts.timeout-1*time.Second, func() {
|
||||
close(timedout)
|
||||
close(s.timedout)
|
||||
})
|
||||
}
|
||||
|
||||
exited := make(chan error)
|
||||
go func() {
|
||||
exited <- cmd.Wait()
|
||||
s.exited <- s.cmd.Wait()
|
||||
}()
|
||||
|
||||
waitFor := func(stage, str string, timeout time.Duration) error {
|
||||
cond := func(out *buf) bool {
|
||||
i0 := s.out.LastIndex([]byte("(lldb)"))
|
||||
i1 := s.out.LastIndex([]byte("fruitstrap"))
|
||||
i2 := s.out.LastIndex([]byte(" connect"))
|
||||
return i0 > 0 && i1 > 0 && i2 > 0
|
||||
}
|
||||
if err := s.wait("lldb start", cond, 5*time.Second); err != nil {
|
||||
fmt.Printf("lldb start error: %v\n", err)
|
||||
return nil, errRetry
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *lldbSession) do(cmd string) { s.doCmd(cmd, "(lldb)", 0) }
|
||||
|
||||
func (s *lldbSession) doCmd(cmd string, waitFor string, extraTimeout time.Duration) {
|
||||
startLen := s.out.Len()
|
||||
fmt.Fprintln(s.in, cmd)
|
||||
cond := func(out *buf) bool {
|
||||
i := s.out.LastIndex([]byte(waitFor))
|
||||
return i > startLen
|
||||
}
|
||||
if err := s.wait(fmt.Sprintf("running cmd %q", cmd), cond, extraTimeout); err != nil {
|
||||
panic(waitPanic{err})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *lldbSession) wait(reason string, cond func(out *buf) bool, extraTimeout time.Duration) error {
|
||||
doTimeout := 1*time.Second + extraTimeout
|
||||
doTimedout := time.After(doTimeout)
|
||||
for {
|
||||
select {
|
||||
case <-timedout:
|
||||
w.printBuf()
|
||||
if p := cmd.Process; p != nil {
|
||||
case <-s.timedout:
|
||||
if p := s.cmd.Process; p != nil {
|
||||
p.Kill()
|
||||
}
|
||||
return fmt.Errorf("timeout (stage %s)", stage)
|
||||
case err := <-exited:
|
||||
w.printBuf()
|
||||
return fmt.Errorf("failed (stage %s): %v", stage, err)
|
||||
case i := <-w.find(str, timeout):
|
||||
if i < 0 {
|
||||
log.Printf("timed out on stage %q, retrying", stage)
|
||||
return errRetry
|
||||
return fmt.Errorf("test timeout (%s)", reason)
|
||||
case <-doTimedout:
|
||||
return fmt.Errorf("command timeout (%s for %v)", reason, doTimeout)
|
||||
case err := <-s.exited:
|
||||
return fmt.Errorf("exited (%s: %v)", reason, err)
|
||||
default:
|
||||
if cond(s.out) {
|
||||
return nil
|
||||
}
|
||||
w.clearTo(i + len(str))
|
||||
return nil
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
do := func(cmd string) {
|
||||
fmt.Fprintln(lldb, cmd)
|
||||
if err := waitFor(fmt.Sprintf("prompt after %q", cmd), "(lldb)", 0); err != nil {
|
||||
panic(waitPanic{err})
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for installation and connection.
|
||||
if err := waitFor("ios-deploy before run", "(lldb)", 0); err != nil {
|
||||
// Retry if we see a rare and longstanding ios-deploy bug.
|
||||
// https://github.com/phonegap/ios-deploy/issues/11
|
||||
// Assertion failed: (AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL) == 0)
|
||||
log.Printf("%v, retrying", err)
|
||||
return errRetry
|
||||
}
|
||||
|
||||
// Script LLDB. Oh dear.
|
||||
do(`process handle SIGHUP --stop false --pass true --notify false`)
|
||||
do(`process handle SIGPIPE --stop false --pass true --notify false`)
|
||||
do(`process handle SIGUSR1 --stop false --pass true --notify false`)
|
||||
do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work
|
||||
do(`process handle SIGBUS --stop false --pass true --notify false`) // does not work
|
||||
|
||||
if opts.lldb {
|
||||
_, err := io.Copy(lldb, os.Stdin)
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go
|
||||
|
||||
fmt.Fprintln(lldb, `run`)
|
||||
if err := waitFor("br getwd", "stop reason = breakpoint", 20*time.Second); err != nil {
|
||||
// At this point we see several flaky errors from the iOS
|
||||
// build infrastructure. The most common is never reaching
|
||||
// the breakpoint, which we catch with a timeout. Very
|
||||
// occasionally lldb can produce errors like:
|
||||
//
|
||||
// Breakpoint 1: no locations (pending).
|
||||
// WARNING: Unable to resolve breakpoint to any actual locations.
|
||||
//
|
||||
// As no actual test code has been executed by this point,
|
||||
// we treat all errors as recoverable.
|
||||
if err != errRetry {
|
||||
log.Printf("%v, retrying", err)
|
||||
err = errRetry
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := waitFor("br getwd prompt", "(lldb)", 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Move the current working directory into the faux gopath.
|
||||
if pkgpath != "src" {
|
||||
do(`breakpoint delete 1`)
|
||||
do(`expr char* $mem = (char*)malloc(512)`)
|
||||
do(`expr $mem = (char*)getwd($mem, 512)`)
|
||||
do(`expr $mem = (char*)strcat($mem, "/` + pkgpath + `")`)
|
||||
do(`call (void)chdir($mem)`)
|
||||
}
|
||||
|
||||
// Run the tests.
|
||||
w.trimSuffix("(lldb) ")
|
||||
fmt.Fprintln(lldb, `process continue`)
|
||||
|
||||
// Wait for the test to complete.
|
||||
select {
|
||||
case <-timedout:
|
||||
w.printBuf()
|
||||
if p := cmd.Process; p != nil {
|
||||
p.Kill()
|
||||
}
|
||||
return errors.New("timeout running tests")
|
||||
case <-w.find("\nPASS", 0):
|
||||
passed := w.isPass()
|
||||
w.printBuf()
|
||||
if passed {
|
||||
return nil
|
||||
}
|
||||
return errors.New("test failure")
|
||||
case err := <-exited:
|
||||
// The returned lldb error code is usually non-zero.
|
||||
// We check for test success by scanning for the final
|
||||
// PASS returned by the test harness, assuming the worst
|
||||
// in its absence.
|
||||
if w.isPass() {
|
||||
err = nil
|
||||
} else if err == nil {
|
||||
err = errors.New("test failure")
|
||||
}
|
||||
w.printBuf()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
type bufWriter struct {
|
||||
mu sync.Mutex
|
||||
buf []byte
|
||||
suffix []byte // remove from each Write
|
||||
|
||||
findTxt []byte // search buffer on each Write
|
||||
findCh chan int // report find position
|
||||
findAfter *time.Timer
|
||||
type buf struct {
|
||||
mu sync.Mutex
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (w *bufWriter) Write(in []byte) (n int, err error) {
|
||||
func (w *buf) Write(in []byte) (n int, err error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
n = len(in)
|
||||
in = bytes.TrimSuffix(in, w.suffix)
|
||||
|
||||
if debug {
|
||||
inTxt := strings.Replace(string(in), "\n", "\\n", -1)
|
||||
findTxt := strings.Replace(string(w.findTxt), "\n", "\\n", -1)
|
||||
fmt.Printf("debug --> %s <-- debug (findTxt='%s')\n", inTxt, findTxt)
|
||||
}
|
||||
|
||||
w.buf = append(w.buf, in...)
|
||||
|
||||
if len(w.findTxt) > 0 {
|
||||
if i := bytes.Index(w.buf, w.findTxt); i >= 0 {
|
||||
w.findCh <- i
|
||||
close(w.findCh)
|
||||
w.findTxt = nil
|
||||
w.findCh = nil
|
||||
if w.findAfter != nil {
|
||||
w.findAfter.Stop()
|
||||
w.findAfter = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
return len(in), nil
|
||||
}
|
||||
|
||||
func (w *bufWriter) trimSuffix(p string) {
|
||||
func (w *buf) LastIndex(sep []byte) int {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.suffix = []byte(p)
|
||||
return bytes.LastIndex(w.buf, sep)
|
||||
}
|
||||
|
||||
func (w *bufWriter) printBuf() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
fmt.Fprintf(os.Stderr, "%s", w.buf)
|
||||
w.buf = nil
|
||||
}
|
||||
|
||||
func (w *bufWriter) clearTo(i int) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.buf = w.buf[i:]
|
||||
}
|
||||
|
||||
// find returns a channel that will have exactly one byte index sent
|
||||
// to it when the text str appears in the buffer. If the text does not
|
||||
// appear before timeout, -1 is sent.
|
||||
//
|
||||
// A timeout of zero means no timeout.
|
||||
func (w *bufWriter) find(str string, timeout time.Duration) <-chan int {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if len(w.findTxt) > 0 {
|
||||
panic(fmt.Sprintf("find(%s): already trying to find %s", str, w.findTxt))
|
||||
}
|
||||
txt := []byte(str)
|
||||
ch := make(chan int, 1)
|
||||
if i := bytes.Index(w.buf, txt); i >= 0 {
|
||||
ch <- i
|
||||
close(ch)
|
||||
} else {
|
||||
w.findTxt = txt
|
||||
w.findCh = ch
|
||||
if timeout > 0 {
|
||||
w.findAfter = time.AfterFunc(timeout, func() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if w.findCh == ch {
|
||||
w.findTxt = nil
|
||||
w.findCh = nil
|
||||
w.findAfter = nil
|
||||
ch <- -1
|
||||
close(ch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
func (w *bufWriter) isPass() bool {
|
||||
func (w *buf) Bytes() []byte {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
// The final stdio of lldb is non-deterministic, so we
|
||||
// scan the whole buffer.
|
||||
//
|
||||
// Just to make things fun, lldb sometimes translates \n
|
||||
// into \r\n.
|
||||
return bytes.Contains(w.buf, []byte("\nPASS\n")) || bytes.Contains(w.buf, []byte("\nPASS\r"))
|
||||
b := make([]byte, len(w.buf))
|
||||
copy(b, w.buf)
|
||||
return b
|
||||
}
|
||||
|
||||
func (w *buf) Len() int {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return len(w.buf)
|
||||
}
|
||||
|
||||
type waitPanic struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type options struct {
|
||||
|
||||
@@ -41,9 +41,6 @@ go src=..
|
||||
gofmt_test.go
|
||||
testdata
|
||||
+
|
||||
newlink
|
||||
testdata
|
||||
+
|
||||
archive
|
||||
tar
|
||||
testdata
|
||||
|
||||
@@ -35,8 +35,10 @@ cp -R "$src" "$targ"
|
||||
cd "$targ"
|
||||
echo
|
||||
echo "#### Cleaning $targ"
|
||||
rm .gitignore
|
||||
git clean -f -d
|
||||
rm -f .gitignore
|
||||
if [ -e .git ]; then
|
||||
git clean -f -d
|
||||
fi
|
||||
echo
|
||||
echo "#### Building $targ"
|
||||
echo
|
||||
|
||||
@@ -121,6 +121,15 @@ func IsARMMRC(op int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsARMFloatCmp reports whether the op is a floating comparison instruction.
|
||||
func IsARMFloatCmp(op int) bool {
|
||||
switch op {
|
||||
case arm.ACMPF, arm.ACMPD:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions.
|
||||
// The difference between MRC and MCR is represented by a bit high in the word, not
|
||||
// in the usual way by the opcode itself. Asm must use AMRC for both instructions, so
|
||||
|
||||
@@ -463,6 +463,11 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
|
||||
}
|
||||
p.errorf("unrecognized addressing for %s", obj.Aconv(op))
|
||||
}
|
||||
if arch.IsARMFloatCmp(op) {
|
||||
prog.From = a[0]
|
||||
prog.Reg = p.getRegister(prog, op, &a[1])
|
||||
break
|
||||
}
|
||||
} else if p.arch.Thechar == '7' && arch.IsARM64CMP(op) {
|
||||
prog.From = a[0]
|
||||
prog.Reg = p.getRegister(prog, op, &a[1])
|
||||
|
||||
@@ -181,7 +181,7 @@ var amd64OperandTests = []operandTest{
|
||||
{"x·y+8(SB)", "x.y+8(SB)"},
|
||||
{"x·y+8(SP)", "x.y+8(SP)"},
|
||||
{"y+56(FP)", "y+56(FP)"},
|
||||
{"·AddUint32(SB", "\"\".AddUint32(SB)"},
|
||||
{"·AddUint32(SB)", "\"\".AddUint32(SB)"},
|
||||
{"·callReflect(SB)", "\"\".callReflect(SB)"},
|
||||
}
|
||||
|
||||
@@ -288,6 +288,7 @@ var armOperandTests = []operandTest{
|
||||
{"runtime·_sfloat2(SB)", "runtime._sfloat2(SB)"},
|
||||
{"·AddUint32(SB)", "\"\".AddUint32(SB)"},
|
||||
{"(R1, R3)", "(R1, R3)"},
|
||||
{"[R0,R1,g,R15", ""}, // Issue 11764 - previously asm just hung parsing ']' missing register lists
|
||||
}
|
||||
|
||||
var ppc64OperandTests = []operandTest{
|
||||
|
||||
@@ -698,10 +698,15 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
|
||||
func (p *Parser) registerList(a *obj.Addr) {
|
||||
// One range per loop.
|
||||
var bits uint16
|
||||
ListLoop:
|
||||
for {
|
||||
tok := p.next()
|
||||
if tok.ScanToken == ']' {
|
||||
break
|
||||
switch tok.ScanToken {
|
||||
case ']':
|
||||
break ListLoop
|
||||
case scanner.EOF:
|
||||
p.errorf("missing ']' in register list")
|
||||
return
|
||||
}
|
||||
lo := p.registerNumber(tok.String())
|
||||
hi := lo
|
||||
|
||||
4
src/cmd/asm/internal/asm/testdata/arm.out
vendored
4
src/cmd/asm/internal/asm/testdata/arm.out
vendored
@@ -56,4 +56,6 @@
|
||||
281 00056 (testdata/arm.s:281) CALL foo(SB)
|
||||
282 00057 (testdata/arm.s:282) JMP foo(SB)
|
||||
283 00058 (testdata/arm.s:283) CALL foo(SB)
|
||||
292 00059 (testdata/arm.s:292) END
|
||||
286 00059 (testdata/arm.s:286) CMPF F1, F2
|
||||
287 00060 (testdata/arm.s:287) CMPD F1, F2
|
||||
296 00061 (testdata/arm.s:296) END
|
||||
|
||||
4
src/cmd/asm/internal/asm/testdata/arm.s
vendored
4
src/cmd/asm/internal/asm/testdata/arm.s
vendored
@@ -282,6 +282,10 @@ TEXT foo(SB), 0, $0
|
||||
JMP foo(SB)
|
||||
CALL foo(SB)
|
||||
|
||||
// CMPF and CMPD are special.
|
||||
CMPF F1, F2
|
||||
CMPD F1, F2
|
||||
|
||||
//
|
||||
// END
|
||||
//
|
||||
|
||||
@@ -490,6 +490,11 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
|
||||
name, _ := e.Val(dwarf.AttrName).(string)
|
||||
typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset)
|
||||
if name == "" || typOff == 0 {
|
||||
if e.Val(dwarf.AttrSpecification) != nil {
|
||||
// Since we are reading all the DWARF,
|
||||
// assume we will see the variable elsewhere.
|
||||
break
|
||||
}
|
||||
fatalf("malformed DWARF TagVariable entry")
|
||||
}
|
||||
if !strings.HasPrefix(name, "__cgo__") {
|
||||
|
||||
@@ -2018,11 +2018,11 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
|
||||
Regalloc(&n2, nr.Type, nil)
|
||||
Cgen(nr, &n2)
|
||||
nr = &n2
|
||||
Regfree(&n2)
|
||||
|
||||
Regalloc(&n1, nl.Type, nil)
|
||||
Cgen(&tmp, &n1)
|
||||
Regfree(&n1)
|
||||
Regfree(&n2)
|
||||
} else {
|
||||
var n1 Node
|
||||
if !nl.Addable && Ctxt.Arch.Thechar == '8' {
|
||||
|
||||
@@ -1279,20 +1279,28 @@ func defaultlit(np **Node, t *Type) {
|
||||
return
|
||||
|
||||
num:
|
||||
// Note: n.Val().Ctype() can be CTxxx (not a constant) here
|
||||
// in the case of an untyped non-constant value, like 1<<i.
|
||||
v1 := n.Val()
|
||||
if t != nil {
|
||||
if Isint[t.Etype] {
|
||||
t1 = t
|
||||
n.SetVal(toint(n.Val()))
|
||||
v1 = toint(n.Val())
|
||||
} else if Isfloat[t.Etype] {
|
||||
t1 = t
|
||||
n.SetVal(toflt(n.Val()))
|
||||
v1 = toflt(n.Val())
|
||||
} else if Iscomplex[t.Etype] {
|
||||
t1 = t
|
||||
n.SetVal(tocplx(n.Val()))
|
||||
v1 = tocplx(n.Val())
|
||||
}
|
||||
if n.Val().Ctype() != CTxxx {
|
||||
n.SetVal(v1)
|
||||
}
|
||||
}
|
||||
|
||||
overflow(n.Val(), t1)
|
||||
if n.Val().Ctype() != CTxxx {
|
||||
overflow(n.Val(), t1)
|
||||
}
|
||||
Convlit(np, t1)
|
||||
lineno = int32(lno)
|
||||
return
|
||||
|
||||
@@ -1091,7 +1091,10 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
|
||||
OMAKESLICE,
|
||||
ONEW,
|
||||
OREAL,
|
||||
ORECOVER:
|
||||
ORECOVER,
|
||||
OSTRARRAYBYTE,
|
||||
OSTRARRAYBYTETMP,
|
||||
OSTRARRAYRUNE:
|
||||
ordercall(n, order)
|
||||
if lhs == nil || lhs.Op != ONAME || flag_race != 0 {
|
||||
n = ordercopyexpr(n, n.Type, order, 0)
|
||||
|
||||
@@ -2874,12 +2874,17 @@ func keydup(n *Node, hash map[uint32][]*Node) {
|
||||
if Eqtype(a.Left.Type, n.Type) {
|
||||
cmp.Right = a.Left
|
||||
evconst(&cmp)
|
||||
b = uint32(obj.Bool2int(cmp.Val().U.(bool)))
|
||||
if cmp.Op == OLITERAL {
|
||||
// Sometimes evconst fails. See issue 12536.
|
||||
b = uint32(obj.Bool2int(cmp.Val().U.(bool)))
|
||||
}
|
||||
}
|
||||
} else if Eqtype(a.Type, n.Type) {
|
||||
cmp.Right = a
|
||||
evconst(&cmp)
|
||||
b = uint32(obj.Bool2int(cmp.Val().U.(bool)))
|
||||
if cmp.Op == OLITERAL {
|
||||
b = uint32(obj.Bool2int(cmp.Val().U.(bool)))
|
||||
}
|
||||
}
|
||||
|
||||
if b != 0 {
|
||||
|
||||
@@ -3219,6 +3219,11 @@ func walkcompare(np **Node, init **NodeList) {
|
||||
|
||||
if l != nil {
|
||||
x := temp(r.Type)
|
||||
if haspointers(r.Type) {
|
||||
a := Nod(OAS, x, nil)
|
||||
typecheck(&a, Etop)
|
||||
*init = list(*init, a)
|
||||
}
|
||||
ok := temp(Types[TBOOL])
|
||||
|
||||
// l.(type(r))
|
||||
|
||||
@@ -111,7 +111,7 @@ func regnames(n *int) []string {
|
||||
|
||||
func excludedregs() uint64 {
|
||||
// Exclude registers with fixed functions
|
||||
regbits := uint64(1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS))
|
||||
regbits := uint64(1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS) | RtoB(ppc64.REGTMP))
|
||||
|
||||
// Also exclude floating point registers with fixed constants
|
||||
regbits |= RtoB(ppc64.REG_F27) | RtoB(ppc64.REG_F28) | RtoB(ppc64.REG_F29) | RtoB(ppc64.REG_F30) | RtoB(ppc64.REG_F31)
|
||||
|
||||
1
src/cmd/dist/build.go
vendored
1
src/cmd/dist/build.go
vendored
@@ -894,6 +894,7 @@ var buildorder = []string{
|
||||
"crypto/sha1",
|
||||
"debug/dwarf",
|
||||
"debug/elf",
|
||||
"debug/macho",
|
||||
"cmd/go",
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,6 @@ and test commands:
|
||||
|
||||
-a
|
||||
force rebuilding of packages that are already up-to-date.
|
||||
In Go releases, does not apply to the standard library.
|
||||
-n
|
||||
print the commands but do not run them.
|
||||
-p n
|
||||
|
||||
@@ -60,7 +60,6 @@ and test commands:
|
||||
|
||||
-a
|
||||
force rebuilding of packages that are already up-to-date.
|
||||
In Go releases, does not apply to the standard library.
|
||||
-n
|
||||
print the commands but do not run them.
|
||||
-p n
|
||||
|
||||
@@ -7,6 +7,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -69,11 +70,11 @@ func readELFNote(filename, name string, typ int32) ([]byte, error) {
|
||||
|
||||
var elfGoNote = []byte("Go\x00\x00")
|
||||
|
||||
// readELFGoBuildID the Go build ID string from an ELF binary.
|
||||
// The Go build ID is stored in a note described by an ELF PT_NOTE prog header.
|
||||
// The caller has already opened filename, to get f, and read the first 4 kB out, in data.
|
||||
// The Go build ID is stored in a note described by an ELF PT_NOTE prog
|
||||
// header. The caller has already opened filename, to get f, and read
|
||||
// at least 4 kB out, in data.
|
||||
func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) {
|
||||
// Assume the note content is in the first 4 kB, already read.
|
||||
// Assume the note content is in the data, already read.
|
||||
// Rewrite the ELF header to set shnum to 0, so that we can pass
|
||||
// the data to elf.NewFile and it will decode the Prog list but not
|
||||
// try to read the section headers and the string table from disk.
|
||||
@@ -95,11 +96,31 @@ func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string,
|
||||
return "", &os.PathError{Path: filename, Op: "parse", Err: err}
|
||||
}
|
||||
for _, p := range ef.Progs {
|
||||
if p.Type != elf.PT_NOTE || p.Off >= uint64(len(data)) || p.Off+p.Filesz >= uint64(len(data)) || p.Filesz < 16 {
|
||||
if p.Type != elf.PT_NOTE || p.Filesz < 16 {
|
||||
continue
|
||||
}
|
||||
|
||||
note := data[p.Off : p.Off+p.Filesz]
|
||||
var note []byte
|
||||
if p.Off+p.Filesz < uint64(len(data)) {
|
||||
note = data[p.Off : p.Off+p.Filesz]
|
||||
} else {
|
||||
// For some linkers, such as the Solaris linker,
|
||||
// the buildid may not be found in data (which
|
||||
// likely contains the first 16kB of the file)
|
||||
// or even the first few megabytes of the file
|
||||
// due to differences in note segment placement;
|
||||
// in that case, extract the note data manually.
|
||||
_, err = f.Seek(int64(p.Off), 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
note = make([]byte, p.Filesz)
|
||||
_, err = io.ReadFull(f, note)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
nameSize := ef.ByteOrder.Uint32(note)
|
||||
valSize := ef.ByteOrder.Uint32(note[4:])
|
||||
tag := ef.ByteOrder.Uint32(note[8:])
|
||||
@@ -114,3 +135,42 @@ func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string,
|
||||
// No note. Treat as successful but build ID empty.
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// The Go build ID is stored at the beginning of the Mach-O __text segment.
|
||||
// The caller has already opened filename, to get f, and read a few kB out, in data.
|
||||
// Sadly, that's not guaranteed to hold the note, because there is an arbitrary amount
|
||||
// of other junk placed in the file ahead of the main text.
|
||||
func readMachoGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) {
|
||||
// If the data we want has already been read, don't worry about Mach-O parsing.
|
||||
// This is both an optimization and a hedge against the Mach-O parsing failing
|
||||
// in the future due to, for example, the name of the __text section changing.
|
||||
if b, err := readRawGoBuildID(filename, data); b != "" && err == nil {
|
||||
return b, err
|
||||
}
|
||||
|
||||
mf, err := macho.NewFile(f)
|
||||
if err != nil {
|
||||
return "", &os.PathError{Path: filename, Op: "parse", Err: err}
|
||||
}
|
||||
|
||||
sect := mf.Section("__text")
|
||||
if sect == nil {
|
||||
// Every binary has a __text section. Something is wrong.
|
||||
return "", &os.PathError{Path: filename, Op: "parse", Err: fmt.Errorf("cannot find __text section")}
|
||||
}
|
||||
|
||||
// It should be in the first few bytes, but read a lot just in case,
|
||||
// especially given our past problems on OS X with the build ID moving.
|
||||
// There shouldn't be much difference between reading 4kB and 32kB:
|
||||
// the hard part is getting to the data, not transferring it.
|
||||
n := sect.Size
|
||||
if n > uint64(BuildIDReadSize) {
|
||||
n = uint64(BuildIDReadSize)
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
if _, err := f.ReadAt(buf, int64(sect.Offset)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return readRawGoBuildID(filename, buf)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,26 @@ import (
|
||||
)
|
||||
|
||||
func TestNoteReading(t *testing.T) {
|
||||
testNoteReading(t)
|
||||
}
|
||||
|
||||
func TestNoteReading2K(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skipf("2kB is not enough on %s", runtime.GOOS)
|
||||
}
|
||||
// Set BuildIDReadSize to 2kB to exercise Mach-O parsing more strictly.
|
||||
defer func(old int) {
|
||||
main.BuildIDReadSize = old
|
||||
}(main.BuildIDReadSize)
|
||||
main.BuildIDReadSize = 2 * 1024
|
||||
|
||||
testNoteReading(t)
|
||||
}
|
||||
|
||||
func testNoteReading(t *testing.T) {
|
||||
if runtime.GOOS == "dragonfly" {
|
||||
t.Skipf("TestNoteReading is broken on dragonfly - golang.org/issue/13364", runtime.GOOS)
|
||||
}
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
|
||||
@@ -33,9 +53,6 @@ func TestNoteReading(t *testing.T) {
|
||||
// no external linking
|
||||
t.Logf("no external linking - skipping linkmode=external test")
|
||||
|
||||
case "solaris":
|
||||
t.Logf("skipping - golang.org/issue/12178")
|
||||
|
||||
default:
|
||||
tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello.exe"), tg.path("hello.go"))
|
||||
id, err := main.ReadBuildIDFromBinary(tg.path("hello.exe"))
|
||||
|
||||
@@ -368,7 +368,8 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo
|
||||
if gobin != "" {
|
||||
bp.BinDir = gobin
|
||||
}
|
||||
if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path && (!go15VendorExperiment || !strings.Contains(path, "/vendor/")) {
|
||||
if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
|
||||
(!go15VendorExperiment || (!strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/"))) {
|
||||
err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
|
||||
}
|
||||
p.load(stk, bp, err)
|
||||
@@ -1780,8 +1781,17 @@ var (
|
||||
goBuildEnd = []byte("\"\n \xff")
|
||||
|
||||
elfPrefix = []byte("\x7fELF")
|
||||
|
||||
machoPrefixes = [][]byte{
|
||||
{0xfe, 0xed, 0xfa, 0xce},
|
||||
{0xfe, 0xed, 0xfa, 0xcf},
|
||||
{0xce, 0xfa, 0xed, 0xfe},
|
||||
{0xcf, 0xfa, 0xed, 0xfe},
|
||||
}
|
||||
)
|
||||
|
||||
var BuildIDReadSize = 32 * 1024 // changed for testing
|
||||
|
||||
// ReadBuildIDFromBinary reads the build ID from a binary.
|
||||
//
|
||||
// ELF binaries store the build ID in a proper PT_NOTE section.
|
||||
@@ -1796,10 +1806,11 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
|
||||
return "", &os.PathError{Op: "parse", Path: filename, Err: errBuildIDUnknown}
|
||||
}
|
||||
|
||||
// Read the first 16 kB of the binary file.
|
||||
// Read the first 32 kB of the binary file.
|
||||
// That should be enough to find the build ID.
|
||||
// In ELF files, the build ID is in the leading headers,
|
||||
// which are typically less than 4 kB, not to mention 16 kB.
|
||||
// which are typically less than 4 kB, not to mention 32 kB.
|
||||
// In Mach-O files, there's no limit, so we have to parse the file.
|
||||
// On other systems, we're trying to read enough that
|
||||
// we get the beginning of the text segment in the read.
|
||||
// The offset where the text segment begins in a hello
|
||||
@@ -1807,7 +1818,6 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
|
||||
//
|
||||
// Plan 9: 0x20
|
||||
// Windows: 0x600
|
||||
// Mach-O: 0x2000
|
||||
//
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
@@ -1815,7 +1825,7 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data := make([]byte, 16*1024)
|
||||
data := make([]byte, BuildIDReadSize)
|
||||
_, err = io.ReadFull(f, data)
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
err = nil
|
||||
@@ -1827,7 +1837,17 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
|
||||
if bytes.HasPrefix(data, elfPrefix) {
|
||||
return readELFGoBuildID(filename, f, data)
|
||||
}
|
||||
for _, m := range machoPrefixes {
|
||||
if bytes.HasPrefix(data, m) {
|
||||
return readMachoGoBuildID(filename, f, data)
|
||||
}
|
||||
}
|
||||
|
||||
return readRawGoBuildID(filename, data)
|
||||
}
|
||||
|
||||
// readRawGoBuildID finds the raw build ID stored in text segment data.
|
||||
func readRawGoBuildID(filename string, data []byte) (id string, err error) {
|
||||
i := bytes.Index(data, goBuildPrefix)
|
||||
if i < 0 {
|
||||
// Missing. Treat as successful but build ID empty.
|
||||
|
||||
@@ -2173,7 +2173,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
|
||||
r = int(p.To.Reg)
|
||||
}
|
||||
o1 = AOP_RRR(uint32(opirr(ctxt, int(p.As))), uint32(r), uint32(p.To.Reg), uint32(v)&31)
|
||||
if p.As == ASRAD && (v&0x20 != 0) {
|
||||
if (p.As == ASRAD || p.As == ASRADCC) && (v&0x20 != 0) {
|
||||
o1 |= 1 << 1 /* mb[5] */
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
|
||||
|
||||
return
|
||||
|
||||
case 256 + ld.R_X86_64_GOTPCREL:
|
||||
case 256 + ld.R_X86_64_GOTPCREL, 256 + ld.R_X86_64_GOTPCRELX, 256 + ld.R_X86_64_REX_GOTPCRELX:
|
||||
if targ.Type != obj.SDYNIMPORT {
|
||||
// have symbol
|
||||
if r.Off >= 2 && s.P[r.Off-2] == 0x8b {
|
||||
|
||||
@@ -348,7 +348,23 @@ const (
|
||||
R_X86_64_DTPOFF32 = 21
|
||||
R_X86_64_GOTTPOFF = 22
|
||||
R_X86_64_TPOFF32 = 23
|
||||
R_X86_64_COUNT = 24
|
||||
R_X86_64_PC64 = 24
|
||||
R_X86_64_GOTOFF64 = 25
|
||||
R_X86_64_GOTPC32 = 26
|
||||
R_X86_64_GOT64 = 27
|
||||
R_X86_64_GOTPCREL64 = 28
|
||||
R_X86_64_GOTPC64 = 29
|
||||
R_X86_64_GOTPLT64 = 30
|
||||
R_X86_64_PLTOFF64 = 31
|
||||
R_X86_64_SIZE32 = 32
|
||||
R_X86_64_SIZE64 = 33
|
||||
R_X86_64_GOTPC32_TLSDEC = 34
|
||||
R_X86_64_TLSDESC_CALL = 35
|
||||
R_X86_64_TLSDESC = 36
|
||||
R_X86_64_IRELATIVE = 37
|
||||
R_X86_64_PC32_BND = 40
|
||||
R_X86_64_GOTPCRELX = 41
|
||||
R_X86_64_REX_GOTPCRELX = 42
|
||||
R_AARCH64_ABS64 = 257
|
||||
R_AARCH64_ABS32 = 258
|
||||
R_AARCH64_CALL26 = 283
|
||||
@@ -382,7 +398,6 @@ const (
|
||||
R_ALPHA_GLOB_DAT = 25
|
||||
R_ALPHA_JMP_SLOT = 26
|
||||
R_ALPHA_RELATIVE = 27
|
||||
R_ALPHA_COUNT = 28
|
||||
R_ARM_NONE = 0
|
||||
R_ARM_PC24 = 1
|
||||
R_ARM_ABS32 = 2
|
||||
@@ -422,7 +437,6 @@ const (
|
||||
R_ARM_RABS32 = 253
|
||||
R_ARM_RPC24 = 254
|
||||
R_ARM_RBASE = 255
|
||||
R_ARM_COUNT = 38
|
||||
R_386_NONE = 0
|
||||
R_386_32 = 1
|
||||
R_386_PC32 = 2
|
||||
@@ -454,7 +468,11 @@ const (
|
||||
R_386_TLS_DTPMOD32 = 35
|
||||
R_386_TLS_DTPOFF32 = 36
|
||||
R_386_TLS_TPOFF32 = 37
|
||||
R_386_COUNT = 38
|
||||
R_386_TLS_GOTDESC = 39
|
||||
R_386_TLS_DESC_CALL = 40
|
||||
R_386_TLS_DESC = 41
|
||||
R_386_IRELATIVE = 42
|
||||
R_386_GOT32X = 43
|
||||
R_PPC_NONE = 0
|
||||
R_PPC_ADDR32 = 1
|
||||
R_PPC_ADDR24 = 2
|
||||
@@ -492,7 +510,6 @@ const (
|
||||
R_PPC_SECTOFF_LO = 34
|
||||
R_PPC_SECTOFF_HI = 35
|
||||
R_PPC_SECTOFF_HA = 36
|
||||
R_PPC_COUNT = 37
|
||||
R_PPC_TLS = 67
|
||||
R_PPC_DTPMOD32 = 68
|
||||
R_PPC_TPREL16 = 69
|
||||
@@ -533,7 +550,6 @@ const (
|
||||
R_PPC_EMB_RELST_HA = 114
|
||||
R_PPC_EMB_BIT_FLD = 115
|
||||
R_PPC_EMB_RELSDA = 116
|
||||
R_PPC_EMB_COUNT = R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1
|
||||
R_PPC64_REL24 = R_PPC_REL24
|
||||
R_PPC64_JMP_SLOT = R_PPC_JMP_SLOT
|
||||
R_PPC64_ADDR64 = 38
|
||||
@@ -1723,10 +1739,6 @@ func doelf() {
|
||||
Addstring(shstrtab, ".note.go.pkg-list")
|
||||
Addstring(shstrtab, ".note.go.deps")
|
||||
}
|
||||
|
||||
if buildid != "" {
|
||||
Addstring(shstrtab, ".note.go.buildid")
|
||||
}
|
||||
}
|
||||
|
||||
hasinitarr := Linkshared
|
||||
|
||||
@@ -1001,12 +1001,15 @@ func reltype(pn string, elftype int, siz *uint8) int {
|
||||
'6' | R_X86_64_PC32<<24,
|
||||
'6' | R_X86_64_PLT32<<24,
|
||||
'6' | R_X86_64_GOTPCREL<<24,
|
||||
'6' | R_X86_64_GOTPCRELX<<24,
|
||||
'6' | R_X86_64_REX_GOTPCRELX<<24,
|
||||
'8' | R_386_32<<24,
|
||||
'8' | R_386_PC32<<24,
|
||||
'8' | R_386_GOT32<<24,
|
||||
'8' | R_386_PLT32<<24,
|
||||
'8' | R_386_GOTOFF<<24,
|
||||
'8' | R_386_GOTPC<<24,
|
||||
'8' | R_386_GOT32X<<24,
|
||||
'9' | R_PPC64_REL24<<24:
|
||||
*siz = 4
|
||||
|
||||
|
||||
@@ -886,8 +886,8 @@ func archive() {
|
||||
|
||||
mayberemoveoutfile()
|
||||
argv := []string{"ar", "-q", "-c", "-s", outfile}
|
||||
argv = append(argv, hostobjCopy()...)
|
||||
argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir))
|
||||
argv = append(argv, hostobjCopy()...)
|
||||
|
||||
if Debug['v'] != 0 {
|
||||
fmt.Fprintf(&Bso, "archive: %s\n", strings.Join(argv, " "))
|
||||
@@ -1012,8 +1012,8 @@ func hostlink() {
|
||||
argv = append(argv, "-Qunused-arguments")
|
||||
}
|
||||
|
||||
argv = append(argv, hostobjCopy()...)
|
||||
argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir))
|
||||
argv = append(argv, hostobjCopy()...)
|
||||
|
||||
if Linkshared {
|
||||
seenDirs := make(map[string]bool)
|
||||
|
||||
@@ -1107,6 +1107,11 @@ func Asmbpe() {
|
||||
|
||||
t := addpesection(".text", int(Segtext.Length), int(Segtext.Length))
|
||||
t.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
|
||||
if Linkmode == LinkExternal {
|
||||
// some data symbols (e.g. masks) end up in the .text section, and they normally
|
||||
// expect larger alignment requirement than the default text section alignment.
|
||||
t.Characteristics |= IMAGE_SCN_ALIGN_32BYTES
|
||||
}
|
||||
chksectseg(t, &Segtext)
|
||||
textsect = pensect
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
|
||||
|
||||
return
|
||||
|
||||
case 256 + ld.R_386_GOT32:
|
||||
case 256 + ld.R_386_GOT32, 256 + ld.R_386_GOT32X:
|
||||
if targ.Type != obj.SDYNIMPORT {
|
||||
// have symbol
|
||||
if r.Off >= 2 && s.P[r.Off-2] == 0x8b {
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Automatic symbol generation.
|
||||
|
||||
// TODO(rsc): Handle go.typelink, go.track symbols.
|
||||
// TODO(rsc): Do not handle $f64. and $f32. symbols. Instead, generate those
|
||||
// from the compiler and assemblers as dupok data, and then remove autoData below.
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// linkerDefined lists the symbols supplied by other parts of the linker
|
||||
// (runtime.go and layout.go).
|
||||
var linkerDefined = map[string]bool{
|
||||
"runtime.bss": true,
|
||||
"runtime.data": true,
|
||||
"runtime.ebss": true,
|
||||
"runtime.edata": true,
|
||||
"runtime.efunctab": true,
|
||||
"runtime.end": true,
|
||||
"runtime.enoptrbss": true,
|
||||
"runtime.enoptrdata": true,
|
||||
"runtime.erodata": true,
|
||||
"runtime.etext": true,
|
||||
"runtime.etypelink": true,
|
||||
"runtime.functab": true,
|
||||
"runtime.gcbss": true,
|
||||
"runtime.gcdata": true,
|
||||
"runtime.noptrbss": true,
|
||||
"runtime.noptrdata": true,
|
||||
"runtime.pclntab": true,
|
||||
"runtime.rodata": true,
|
||||
"runtime.text": true,
|
||||
"runtime.typelink": true,
|
||||
}
|
||||
|
||||
// isAuto reports whether sym is an automatically-generated data or constant symbol.
|
||||
func (p *Prog) isAuto(sym goobj.SymID) bool {
|
||||
return strings.HasPrefix(sym.Name, "go.weak.") ||
|
||||
strings.HasPrefix(sym.Name, "$f64.") ||
|
||||
strings.HasPrefix(sym.Name, "$f32.") ||
|
||||
linkerDefined[sym.Name]
|
||||
}
|
||||
|
||||
// autoData defines the automatically generated data symbols needed by p.
|
||||
func (p *Prog) autoData() {
|
||||
for sym := range p.Missing {
|
||||
switch {
|
||||
// Floating-point constants that need to be loaded from memory are
|
||||
// written as $f64.{16 hex digits} or $f32.{8 hex digits}; the hex digits
|
||||
// give the IEEE bit pattern of the constant. As far as the layout into
|
||||
// memory is concerned, we interpret these as uint64 or uint32 constants.
|
||||
case strings.HasPrefix(sym.Name, "$f64."), strings.HasPrefix(sym.Name, "$f32."):
|
||||
size := 64
|
||||
if sym.Name[2:4] == "32" {
|
||||
size = 32
|
||||
}
|
||||
delete(p.Missing, sym)
|
||||
fbits, err := strconv.ParseUint(sym.Name[len("$f64."):], 16, size)
|
||||
if err != nil {
|
||||
p.errorf("unexpected floating point symbol %s", sym)
|
||||
continue
|
||||
}
|
||||
data := make([]byte, size/8)
|
||||
if size == 64 {
|
||||
p.byteorder.PutUint64(data, fbits)
|
||||
} else {
|
||||
p.byteorder.PutUint32(data, uint32(fbits))
|
||||
}
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: sym,
|
||||
Kind: goobj.SRODATA,
|
||||
Size: size / 8,
|
||||
},
|
||||
Bytes: data,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// autoConst defines the automatically generated constant symbols needed by p.
|
||||
func (p *Prog) autoConst() {
|
||||
for sym := range p.Missing {
|
||||
switch {
|
||||
case strings.HasPrefix(sym.Name, "go.weak."):
|
||||
// weak symbol resolves to actual symbol if present, or else nil.
|
||||
delete(p.Missing, sym)
|
||||
targ := sym
|
||||
targ.Name = sym.Name[len("go.weak."):]
|
||||
var addr Addr
|
||||
if s := p.Syms[targ]; s != nil {
|
||||
addr = s.Addr
|
||||
}
|
||||
p.defineConst(sym.Name, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defineConst defines a new symbol with the given name and constant address.
|
||||
func (p *Prog) defineConst(name string, addr Addr) {
|
||||
sym := goobj.SymID{Name: name}
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: sym,
|
||||
Kind: goobj.SCONST,
|
||||
},
|
||||
Package: nil,
|
||||
Addr: addr,
|
||||
})
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test for auto-generated symbols.
|
||||
|
||||
// There is no test for $f64. and $f32. symbols, because those are
|
||||
// not possible to write in the assembler syntax. Instead of changing
|
||||
// the assembler to allow that, we plan to change the compilers
|
||||
// not to generate such symbols (plain dupok data is sufficient).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/goobj"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Each test case is an object file, generated from a corresponding .s file.
|
||||
// The image of the autotab symbol should be a sequence of pairs of
|
||||
// identical 8-byte sequences.
|
||||
var autoTests = []string{
|
||||
"testdata/autosection.6",
|
||||
"testdata/autoweak.6",
|
||||
}
|
||||
|
||||
func TestAuto(t *testing.T) {
|
||||
for _, obj := range autoTests {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, obj)
|
||||
if p.NumError > 0 {
|
||||
continue // already reported
|
||||
}
|
||||
|
||||
const name = "autotab"
|
||||
sym := p.Syms[goobj.SymID{Name: name}]
|
||||
if sym == nil {
|
||||
t.Errorf("%s is missing %s symbol", obj, name)
|
||||
return
|
||||
}
|
||||
if sym.Size == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
if len(data)%16 != 0 {
|
||||
t.Errorf("%s: %s.Size = %d, want multiple of 16", obj, name, len(data))
|
||||
return
|
||||
}
|
||||
Data:
|
||||
for i := 0; i < len(data); i += 16 {
|
||||
have := p.byteorder.Uint64(data[i : i+8])
|
||||
want := p.byteorder.Uint64(data[i+8 : i+16])
|
||||
if have != want {
|
||||
// Look for relocation so we can explain what went wrong.
|
||||
for _, r := range sym.Reloc {
|
||||
if r.Offset == i {
|
||||
t.Errorf("%s: %s+%#x: %s: have %#x want %#x", obj, name, i, r.Sym, have, want)
|
||||
continue Data
|
||||
}
|
||||
}
|
||||
t.Errorf("%s: %s+%#x: have %#x want %#x", obj, name, i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Removal of dead code and data.
|
||||
|
||||
package main
|
||||
|
||||
import "cmd/internal/goobj"
|
||||
|
||||
// dead removes unreachable code and data from the program.
|
||||
// It is basically a mark-sweep garbage collection: traverse all the
|
||||
// symbols reachable from the entry (startSymID) and then delete
|
||||
// the rest.
|
||||
func (p *Prog) dead() {
|
||||
p.Dead = make(map[goobj.SymID]bool)
|
||||
reachable := make(map[goobj.SymID]bool)
|
||||
p.walkDead(p.startSym, reachable)
|
||||
|
||||
for sym := range p.Syms {
|
||||
if !reachable[sym] {
|
||||
delete(p.Syms, sym)
|
||||
p.Dead[sym] = true
|
||||
}
|
||||
}
|
||||
|
||||
for sym := range p.Missing {
|
||||
if !reachable[sym] {
|
||||
delete(p.Missing, sym)
|
||||
p.Dead[sym] = true
|
||||
}
|
||||
}
|
||||
|
||||
p.SymOrder = removeDead(p.SymOrder, reachable)
|
||||
|
||||
for _, pkg := range p.Packages {
|
||||
pkg.Syms = removeDead(pkg.Syms, reachable)
|
||||
}
|
||||
}
|
||||
|
||||
// walkDead traverses the symbols reachable from sym, adding them to reachable.
|
||||
// The caller has verified that reachable[sym] = false.
|
||||
func (p *Prog) walkDead(sym goobj.SymID, reachable map[goobj.SymID]bool) {
|
||||
reachable[sym] = true
|
||||
s := p.Syms[sym]
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
for i := range s.Reloc {
|
||||
r := &s.Reloc[i]
|
||||
if !reachable[r.Sym] {
|
||||
p.walkDead(r.Sym, reachable)
|
||||
}
|
||||
}
|
||||
if s.Func != nil {
|
||||
for _, fdata := range s.Func.FuncData {
|
||||
if fdata.Sym.Name != "" && !reachable[fdata.Sym] {
|
||||
p.walkDead(fdata.Sym, reachable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeDead removes unreachable (dead) symbols from syms,
|
||||
// returning a shortened slice using the same underlying array.
|
||||
func removeDead(syms []*Sym, reachable map[goobj.SymID]bool) []*Sym {
|
||||
keep := syms[:0]
|
||||
for _, sym := range syms {
|
||||
if reachable[sym.SymID] {
|
||||
keep = append(keep, sym)
|
||||
}
|
||||
}
|
||||
return keep
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Each test case is an object file, generated from a corresponding .s file.
|
||||
// The symbols in the object file with a dead_ prefix are the ones that
|
||||
// should be removed from the program.
|
||||
var deadTests = []string{
|
||||
"testdata/dead.6",
|
||||
}
|
||||
|
||||
func TestDead(t *testing.T) {
|
||||
for _, obj := range deadTests {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
p.init()
|
||||
p.scan(obj)
|
||||
if p.NumError > 0 {
|
||||
continue // already reported
|
||||
}
|
||||
origSyms := copyMap(p.Syms)
|
||||
origMissing := copyMap(p.Missing)
|
||||
origSymOrder := copySlice(p.SymOrder)
|
||||
origPkgSyms := copySlice(p.Packages["main"].Syms)
|
||||
p.dead()
|
||||
checkDeadMap(t, obj, "p.Syms", origSyms, p.Syms)
|
||||
checkDeadMap(t, obj, "p.Missing", origMissing, p.Missing)
|
||||
checkDeadSlice(t, obj, "p.SymOrder", origSymOrder, p.SymOrder)
|
||||
checkDeadSlice(t, obj, `p.Packages["main"].Syms`, origPkgSyms, p.Packages["main"].Syms)
|
||||
}
|
||||
}
|
||||
|
||||
func copyMap(m interface{}) interface{} {
|
||||
v := reflect.ValueOf(m)
|
||||
out := reflect.MakeMap(v.Type())
|
||||
for _, key := range v.MapKeys() {
|
||||
out.SetMapIndex(key, v.MapIndex(key))
|
||||
}
|
||||
return out.Interface()
|
||||
}
|
||||
|
||||
func checkDeadMap(t *testing.T, obj, name string, old, new interface{}) {
|
||||
vold := reflect.ValueOf(old)
|
||||
vnew := reflect.ValueOf(new)
|
||||
for _, vid := range vold.MapKeys() {
|
||||
id := vid.Interface().(goobj.SymID)
|
||||
if strings.HasPrefix(id.Name, "dead_") {
|
||||
if vnew.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s contains unnecessary symbol %s", obj, name, id)
|
||||
}
|
||||
} else {
|
||||
if !vnew.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s is missing symbol %s", obj, name, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, vid := range vnew.MapKeys() {
|
||||
id := vid.Interface().(goobj.SymID)
|
||||
if !vold.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s contains unexpected symbol %s", obj, name, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copySlice(x []*Sym) (out []*Sym) {
|
||||
return append(out, x...)
|
||||
}
|
||||
|
||||
func checkDeadSlice(t *testing.T, obj, name string, old, new []*Sym) {
|
||||
for i, s := range old {
|
||||
if strings.HasPrefix(s.Name, "dead_") {
|
||||
continue
|
||||
}
|
||||
if len(new) == 0 {
|
||||
t.Errorf("%s: %s is missing symbol %s\nhave%v\nwant%v", obj, name, s, new, old[i:])
|
||||
return
|
||||
}
|
||||
if new[0].SymID != s.SymID {
|
||||
t.Errorf("%s: %s is incorrect: have %s, want %s\nhave%v\nwant%v", obj, name, new[0].SymID, s.SymID, new, old[i:])
|
||||
return
|
||||
}
|
||||
new = new[1:]
|
||||
}
|
||||
if len(new) > 0 {
|
||||
t.Errorf("%s: %s has unexpected symbols: %v", obj, name, new)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of debug data structures (in the executable but not mapped at run time).
|
||||
// See also runtime.go.
|
||||
|
||||
package main
|
||||
|
||||
func (p *Prog) debug() {
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mustParseHexdumpFile returns a block of data generated by
|
||||
// parsing the hex dump in the named file.
|
||||
// If the file cannot be read or does not contain a valid hex dump,
|
||||
// mustParseHexdumpFile calls t.Fatal.
|
||||
func mustParseHexdumpFile(t *testing.T, file string) []byte {
|
||||
hex, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := parseHexdump(string(hex))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// parseHexdump parses the hex dump in text, which should be the
|
||||
// output of "hexdump -C" or Plan 9's "xd -b",
|
||||
// and returns the original data used to produce the dump.
|
||||
// It is meant to enable storing golden binary files as text, so that
|
||||
// changes to the golden files can be seen during code reviews.
|
||||
func parseHexdump(text string) ([]byte, error) {
|
||||
var out []byte
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
if i := strings.Index(line, "|"); i >= 0 { // remove text dump
|
||||
line = line[:i]
|
||||
}
|
||||
f := strings.Fields(line)
|
||||
if len(f) > 1+16 {
|
||||
return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line)
|
||||
}
|
||||
if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted
|
||||
continue
|
||||
}
|
||||
addr64, err := strconv.ParseUint(f[0], 16, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0])
|
||||
}
|
||||
addr := int(addr64)
|
||||
if len(out) < addr {
|
||||
out = append(out, make([]byte, addr-len(out))...)
|
||||
}
|
||||
for _, x := range f[1:] {
|
||||
val, err := strconv.ParseUint(x, 16, 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x)
|
||||
}
|
||||
out = append(out, byte(val))
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func hexdump(data []byte) string {
|
||||
text := hex.Dump(data) + fmt.Sprintf("%08x\n", len(data))
|
||||
text = regexp.MustCompile(`\n([0-9a-f]+(\s+00){16}.*\n)+`).ReplaceAllString(text, "\n*\n")
|
||||
return text
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Executable image layout - address assignment.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
)
|
||||
|
||||
// A layoutSection describes a single section to add to the
|
||||
// final executable. Go binaries only have a fixed set of possible
|
||||
// sections, and the symbol kind determines the section.
|
||||
type layoutSection struct {
|
||||
Segment string
|
||||
Section string
|
||||
Kind goobj.SymKind
|
||||
Index int
|
||||
}
|
||||
|
||||
// layout defines the layout of the generated Go executable.
|
||||
// The order of entries here is the order in the executable.
|
||||
// Entries with the same Segment name must be contiguous.
|
||||
var layout = []layoutSection{
|
||||
{Segment: "text", Section: "text", Kind: goobj.STEXT},
|
||||
{Segment: "rodata", Section: "rodata", Kind: goobj.SRODATA},
|
||||
{Segment: "rodata", Section: "functab", Kind: goobj.SPCLNTAB},
|
||||
{Segment: "rodata", Section: "typelink", Kind: goobj.STYPELINK},
|
||||
{Segment: "data", Section: "noptrdata", Kind: goobj.SNOPTRDATA},
|
||||
{Segment: "data", Section: "data", Kind: goobj.SDATA},
|
||||
{Segment: "data", Section: "bss", Kind: goobj.SBSS},
|
||||
{Segment: "data", Section: "noptrbss", Kind: goobj.SNOPTRBSS},
|
||||
|
||||
// Later:
|
||||
// {"rodata", "type", goobj.STYPE},
|
||||
// {"rodata", "string", goobj.SSTRING},
|
||||
// {"rodata", "gostring", goobj.SGOSTRING},
|
||||
// {"rodata", "gofunc", goobj.SGOFUNC},
|
||||
}
|
||||
|
||||
// layoutByKind maps from SymKind to an entry in layout.
|
||||
var layoutByKind []*layoutSection
|
||||
|
||||
func init() {
|
||||
// Build index from symbol type to layout entry.
|
||||
max := 0
|
||||
for _, sect := range layout {
|
||||
if max <= int(sect.Kind) {
|
||||
max = int(sect.Kind) + 1
|
||||
}
|
||||
}
|
||||
layoutByKind = make([]*layoutSection, max)
|
||||
for i := range layout {
|
||||
sect := &layout[i]
|
||||
layoutByKind[sect.Kind] = sect
|
||||
sect.Index = i
|
||||
}
|
||||
}
|
||||
|
||||
// layout arranges symbols into sections and sections into segments,
|
||||
// and then it assigns addresses to segments, sections, and symbols.
|
||||
func (p *Prog) layout() {
|
||||
sections := make([]*Section, len(layout))
|
||||
|
||||
// Assign symbols to sections using index, creating sections as needed.
|
||||
// Could keep sections separated by type during input instead.
|
||||
for _, sym := range p.SymOrder {
|
||||
kind := sym.Kind
|
||||
if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil {
|
||||
p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind)
|
||||
continue
|
||||
}
|
||||
lsect := layoutByKind[kind]
|
||||
sect := sections[lsect.Index]
|
||||
if sect == nil {
|
||||
sect = &Section{
|
||||
Name: lsect.Section,
|
||||
Align: 1,
|
||||
}
|
||||
sections[lsect.Index] = sect
|
||||
}
|
||||
if sym.Data.Size > 0 || len(sym.Bytes) > 0 {
|
||||
sect.InFile = true
|
||||
}
|
||||
sym.Section = sect
|
||||
sect.Syms = append(sect.Syms, sym)
|
||||
|
||||
// TODO(rsc): Incorporate alignment information.
|
||||
// First that information needs to be added to the object files.
|
||||
//
|
||||
// if sect.Align < Addr(sym.Align) {
|
||||
// sect.Align = Addr(sym.Align)
|
||||
// }
|
||||
}
|
||||
|
||||
// Assign sections to segments, creating segments as needed.
|
||||
var seg *Segment
|
||||
for i, sect := range sections {
|
||||
if sect == nil {
|
||||
continue
|
||||
}
|
||||
segName := layout[i].Segment
|
||||
|
||||
// Special case: Mach-O does not support "rodata" segment,
|
||||
// so store read-only data in text segment.
|
||||
if p.GOOS == "darwin" && segName == "rodata" {
|
||||
segName = "text"
|
||||
}
|
||||
|
||||
if seg == nil || seg.Name != segName {
|
||||
seg = &Segment{
|
||||
Name: segName,
|
||||
}
|
||||
p.Segments = append(p.Segments, seg)
|
||||
}
|
||||
sect.Segment = seg
|
||||
seg.Sections = append(seg.Sections, sect)
|
||||
}
|
||||
|
||||
// Assign addresses.
|
||||
|
||||
// TODO(rsc): This choice needs to be informed by both
|
||||
// the formatter and the target architecture.
|
||||
// And maybe eventually a command line flag (sigh).
|
||||
const segAlign = 4096
|
||||
|
||||
// TODO(rsc): Use a larger amount on most systems, which will let the
|
||||
// compiler eliminate more nil checks.
|
||||
if p.UnmappedSize == 0 {
|
||||
p.UnmappedSize = segAlign
|
||||
}
|
||||
|
||||
// TODO(rsc): addr := Addr(0) when generating a shared library or PIE.
|
||||
addr := p.UnmappedSize
|
||||
|
||||
// Account for initial file header.
|
||||
hdrVirt, hdrFile := p.formatter.headerSize(p)
|
||||
addr += hdrVirt
|
||||
|
||||
// Assign addresses to segments, sections, symbols.
|
||||
// Assign sizes to segments, sections.
|
||||
startVirt := addr
|
||||
startFile := hdrFile
|
||||
for _, seg := range p.Segments {
|
||||
addr = round(addr, segAlign)
|
||||
seg.VirtAddr = addr
|
||||
seg.FileOffset = startFile + seg.VirtAddr - startVirt
|
||||
for _, sect := range seg.Sections {
|
||||
addr = round(addr, sect.Align)
|
||||
sect.VirtAddr = addr
|
||||
for _, sym := range sect.Syms {
|
||||
// TODO(rsc): Respect alignment once we have that information.
|
||||
sym.Addr = addr
|
||||
addr += Addr(sym.Size)
|
||||
}
|
||||
sect.Size = addr - sect.VirtAddr
|
||||
if sect.InFile {
|
||||
seg.FileSize = addr - seg.VirtAddr
|
||||
}
|
||||
}
|
||||
seg.VirtSize = addr - seg.VirtAddr
|
||||
}
|
||||
|
||||
// Define symbols for section names.
|
||||
var progEnd Addr
|
||||
for i, sect := range sections {
|
||||
name := layout[i].Section
|
||||
var start, end Addr
|
||||
if sect != nil {
|
||||
start = sect.VirtAddr
|
||||
end = sect.VirtAddr + sect.Size
|
||||
}
|
||||
p.defineConst("runtime."+name, start)
|
||||
p.defineConst("runtime.e"+name, end)
|
||||
progEnd = end
|
||||
}
|
||||
p.defineConst("runtime.end", progEnd)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLayout(t *testing.T) {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "text_start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
var buf bytes.Buffer
|
||||
const obj = "testdata/layout.6"
|
||||
p.link(&buf, obj)
|
||||
if p.NumError > 0 {
|
||||
return // already reported
|
||||
}
|
||||
if len(p.Dead) > 0 {
|
||||
t.Errorf("%s: unexpected dead symbols %v", obj, p.Dead)
|
||||
return
|
||||
}
|
||||
|
||||
for _, sym := range p.SymOrder {
|
||||
if p.isAuto(sym.SymID) {
|
||||
continue
|
||||
}
|
||||
if sym.Section == nil {
|
||||
t.Errorf("%s: symbol %s is missing section", obj, sym)
|
||||
continue
|
||||
}
|
||||
i := strings.Index(sym.Name, "_")
|
||||
if i < 0 {
|
||||
t.Errorf("%s: unexpected symbol %s", obj, sym)
|
||||
continue
|
||||
}
|
||||
if sym.Section.Name != sym.Name[:i] {
|
||||
t.Errorf("%s: symbol %s in section %s, want %s", obj, sym, sym.Section.Name, sym.Name[:i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/goobj"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLinkHello(t *testing.T) {
|
||||
p := &Prog{
|
||||
GOOS: "darwin",
|
||||
GOARCH: "amd64",
|
||||
Error: func(s string) { t.Error(s) },
|
||||
StartSym: "_rt0_go",
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, "testdata/hello.6")
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
if p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil {
|
||||
t.Errorf("Syms = %v, want at least [_rt0_go hello<1>]", p.Syms)
|
||||
}
|
||||
|
||||
// uncomment to leave file behind for execution:
|
||||
if false {
|
||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
||||
}
|
||||
checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64")
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Loading of code and data fragments from package files into final image.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/obj"
|
||||
"os"
|
||||
)
|
||||
|
||||
// load allocates segment images, populates them with data
|
||||
// read from package files, and applies relocations to the data.
|
||||
func (p *Prog) load() {
|
||||
// TODO(rsc): mmap the output file and store the data directly.
|
||||
// That will make writing the output file more efficient.
|
||||
for _, seg := range p.Segments {
|
||||
seg.Data = make([]byte, seg.FileSize)
|
||||
}
|
||||
for _, pkg := range p.Packages {
|
||||
p.loadPackage(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// loadPackage loads and relocates data for all the
|
||||
// symbols needed in the given package.
|
||||
func (p *Prog) loadPackage(pkg *Package) {
|
||||
if pkg.File == "" {
|
||||
// This "package" contains internally generated symbols only.
|
||||
// All such symbols have a sym.Bytes field holding the actual data
|
||||
// (if any), plus relocations.
|
||||
for _, sym := range pkg.Syms {
|
||||
if sym.Bytes == nil {
|
||||
continue
|
||||
}
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
copy(data, sym.Bytes)
|
||||
p.relocateSym(sym, data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Package stored in file.
|
||||
f, err := os.Open(pkg.File)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// TODO(rsc): Mmap file into memory.
|
||||
|
||||
for _, sym := range pkg.Syms {
|
||||
if sym.Data.Size == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO(rsc): If not using mmap, at least coalesce nearby reads.
|
||||
if sym.Section == nil {
|
||||
p.errorf("internal error: missing section for %s", sym.Name)
|
||||
}
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
if off >= Addr(len(seg.Data)) || off+Addr(sym.Data.Size) > Addr(len(seg.Data)) {
|
||||
p.errorf("internal error: allocated space for %s too small: %d bytes for %d+%d (%d)", sym, len(seg.Data), off, sym.Data.Size, sym.Size)
|
||||
}
|
||||
data := seg.Data[off : off+Addr(sym.Data.Size)]
|
||||
_, err := f.ReadAt(data, sym.Data.Offset)
|
||||
if err != nil {
|
||||
p.errorf("reading %v: %v", sym.SymID, err)
|
||||
}
|
||||
p.relocateSym(sym, data)
|
||||
}
|
||||
}
|
||||
|
||||
// relocateSym applies relocations to sym's data.
|
||||
func (p *Prog) relocateSym(sym *Sym, data []byte) {
|
||||
for i := range sym.Reloc {
|
||||
r := &sym.Reloc[i]
|
||||
targ := p.Syms[r.Sym]
|
||||
if targ == nil {
|
||||
p.errorf("%v: reference to undefined symbol %v", sym, r.Sym)
|
||||
continue
|
||||
}
|
||||
val := targ.Addr + Addr(r.Add)
|
||||
switch r.Type {
|
||||
default:
|
||||
p.errorf("%v: unknown relocation type %d", sym, r.Type)
|
||||
case obj.R_ADDR, obj.R_CALLIND:
|
||||
// ok
|
||||
case obj.R_PCREL, obj.R_CALL:
|
||||
val -= sym.Addr + Addr(r.Offset+r.Size)
|
||||
}
|
||||
frag := data[r.Offset : r.Offset+r.Size]
|
||||
switch r.Size {
|
||||
default:
|
||||
p.errorf("%v: unknown relocation size %d", sym, r.Size)
|
||||
case 4:
|
||||
// TODO(rsc): Check for overflow?
|
||||
p.byteorder.PutUint32(frag, uint32(val))
|
||||
case 8:
|
||||
p.byteorder.PutUint64(frag, uint64(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Mach-O (Darwin) object file writing.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// machoFormat is the implementation of formatter.
|
||||
type machoFormat struct{}
|
||||
|
||||
// machoHeader and friends are data structures
|
||||
// corresponding to the Mach-O file header
|
||||
// to be written to disk.
|
||||
|
||||
const (
|
||||
macho64Bit = 1 << 24
|
||||
machoSubCPU386 = 3
|
||||
)
|
||||
|
||||
// machoArch describes a Mach-O target architecture.
|
||||
type machoArch struct {
|
||||
CPU uint32
|
||||
SubCPU uint32
|
||||
}
|
||||
|
||||
// machoHeader is the Mach-O file header.
|
||||
type machoHeader struct {
|
||||
machoArch
|
||||
FileType uint32
|
||||
Loads []*machoLoad
|
||||
Segments []*machoSegment
|
||||
p *Prog // for reporting errors
|
||||
}
|
||||
|
||||
// machoLoad is a Mach-O load command.
|
||||
type machoLoad struct {
|
||||
Type uint32
|
||||
Data []uint32
|
||||
}
|
||||
|
||||
// machoSegment is a Mach-O segment.
|
||||
type machoSegment struct {
|
||||
Name string
|
||||
VirtAddr Addr
|
||||
VirtSize Addr
|
||||
FileOffset Addr
|
||||
FileSize Addr
|
||||
Prot1 uint32
|
||||
Prot2 uint32
|
||||
Flags uint32
|
||||
Sections []*machoSection
|
||||
}
|
||||
|
||||
// machoSection is a Mach-O section, inside a segment.
|
||||
type machoSection struct {
|
||||
Name string
|
||||
Segment string
|
||||
Addr Addr
|
||||
Size Addr
|
||||
Offset uint32
|
||||
Align uint32
|
||||
Reloc uint32
|
||||
Nreloc uint32
|
||||
Flags uint32
|
||||
Res1 uint32
|
||||
Res2 uint32
|
||||
}
|
||||
|
||||
// layout positions the segments and sections in p
|
||||
// to make room for the Mach-O file header.
|
||||
// That is, it edits their VirtAddr fields to adjust for the presence
|
||||
// of the Mach-O header at the beginning of the address space.
|
||||
func (machoFormat) headerSize(p *Prog) (virt, file Addr) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
size := Addr(h.size())
|
||||
size = round(size, 4096)
|
||||
p.HeaderSize = size
|
||||
return size, size
|
||||
}
|
||||
|
||||
// write writes p to w as a Mach-O executable.
|
||||
// layout(p) must have already been called,
|
||||
// and the number, sizes, and addresses of the segments
|
||||
// and sections must not have been modified since the call.
|
||||
func (machoFormat) write(w io.Writer, p *Prog) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
off := Addr(0)
|
||||
enc := h.encode()
|
||||
w.Write(enc)
|
||||
off += Addr(len(enc))
|
||||
for _, seg := range p.Segments {
|
||||
if seg.FileOffset < off {
|
||||
h.p.errorf("mach-o error: invalid file offset")
|
||||
}
|
||||
w.Write(make([]byte, int(seg.FileOffset-off)))
|
||||
if seg.FileSize != Addr(len(seg.Data)) {
|
||||
h.p.errorf("mach-o error: invalid file size")
|
||||
}
|
||||
w.Write(seg.Data)
|
||||
off = seg.FileOffset + Addr(len(seg.Data))
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion of Prog to macho data structures.
|
||||
|
||||
// machoArches maps from GOARCH to machoArch.
|
||||
var machoArches = map[string]machoArch{
|
||||
"amd64": {
|
||||
CPU: uint32(macho.CpuAmd64),
|
||||
SubCPU: uint32(machoSubCPU386),
|
||||
},
|
||||
}
|
||||
|
||||
// init initializes the header h to describe p.
|
||||
func (h *machoHeader) init(p *Prog) {
|
||||
h.p = p
|
||||
h.Segments = nil
|
||||
h.Loads = nil
|
||||
var ok bool
|
||||
h.machoArch, ok = machoArches[p.GOARCH]
|
||||
if !ok {
|
||||
p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH)
|
||||
return
|
||||
}
|
||||
h.FileType = uint32(macho.TypeExec)
|
||||
|
||||
mseg := h.addSegment(p, "__PAGEZERO", nil)
|
||||
mseg.VirtSize = p.UnmappedSize
|
||||
|
||||
for _, seg := range p.Segments {
|
||||
h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg)
|
||||
}
|
||||
|
||||
var data []uint32
|
||||
switch h.CPU {
|
||||
default:
|
||||
p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH)
|
||||
case uint32(macho.CpuAmd64):
|
||||
data = make([]uint32, 2+42)
|
||||
data[0] = 4 // thread type
|
||||
data[1] = 42 // word count
|
||||
data[2+32] = uint32(p.Entry) // RIP register, in two parts
|
||||
data[2+32+1] = uint32(p.Entry >> 32)
|
||||
}
|
||||
|
||||
h.Loads = append(h.Loads, &machoLoad{
|
||||
Type: uint32(macho.LoadCmdUnixThread),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// addSegment adds to h a Mach-O segment like seg with the given name.
|
||||
func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment {
|
||||
mseg := &machoSegment{
|
||||
Name: name,
|
||||
}
|
||||
h.Segments = append(h.Segments, mseg)
|
||||
if seg == nil {
|
||||
return mseg
|
||||
}
|
||||
|
||||
mseg.VirtAddr = seg.VirtAddr
|
||||
mseg.VirtSize = seg.VirtSize
|
||||
mseg.FileOffset = round(seg.FileOffset, 4096)
|
||||
mseg.FileSize = seg.FileSize
|
||||
|
||||
if name == "__TEXT" {
|
||||
// Initially RWX, then just RX
|
||||
mseg.Prot1 = 7
|
||||
mseg.Prot2 = 5
|
||||
|
||||
// Text segment maps Mach-O header, needed by dynamic linker.
|
||||
mseg.VirtAddr -= p.HeaderSize
|
||||
mseg.VirtSize += p.HeaderSize
|
||||
mseg.FileOffset -= p.HeaderSize
|
||||
mseg.FileSize += p.HeaderSize
|
||||
} else {
|
||||
// RW
|
||||
mseg.Prot1 = 3
|
||||
mseg.Prot2 = 3
|
||||
}
|
||||
|
||||
for _, sect := range seg.Sections {
|
||||
h.addSection(mseg, seg, sect)
|
||||
}
|
||||
return mseg
|
||||
}
|
||||
|
||||
// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name.
|
||||
func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) {
|
||||
msect := &machoSection{
|
||||
Name: "__" + sect.Name,
|
||||
Segment: mseg.Name,
|
||||
// Reloc: sect.RelocOffset,
|
||||
// NumReloc: sect.RelocLen / 8,
|
||||
Addr: sect.VirtAddr,
|
||||
Size: sect.Size,
|
||||
}
|
||||
mseg.Sections = append(mseg.Sections, msect)
|
||||
|
||||
for 1<<msect.Align < sect.Align {
|
||||
msect.Align++
|
||||
}
|
||||
|
||||
if off := sect.VirtAddr - seg.VirtAddr; off < seg.FileSize {
|
||||
// Data in file.
|
||||
if sect.Size > seg.FileSize-off {
|
||||
h.p.errorf("mach-o error: section crosses file boundary")
|
||||
}
|
||||
msect.Offset = uint32(seg.FileOffset + off)
|
||||
} else {
|
||||
// Zero filled.
|
||||
msect.Flags |= 1
|
||||
}
|
||||
|
||||
if sect.Name == "text" {
|
||||
msect.Flags |= 0x400 // contains executable instructions
|
||||
}
|
||||
}
|
||||
|
||||
// A machoWriter helps write Mach-O headers.
|
||||
// It is basically a buffer with some helper routines for writing integers.
|
||||
type machoWriter struct {
|
||||
dst []byte
|
||||
tmp [8]byte
|
||||
order binary.ByteOrder
|
||||
is64 bool
|
||||
p *Prog
|
||||
}
|
||||
|
||||
// if64 returns x if w is writing a 64-bit object file; otherwise it returns y.
|
||||
func (w *machoWriter) if64(x, y interface{}) interface{} {
|
||||
if w.is64 {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// encode encodes each of the given arguments into the writer.
|
||||
// It encodes uint32, []uint32, uint64, and []uint64 by writing each value
|
||||
// in turn in the correct byte order for the output file.
|
||||
// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32.
|
||||
// It encodes []byte and string by writing the raw bytes (no length prefix).
|
||||
// It skips nil values in the args list.
|
||||
func (w *machoWriter) encode(args ...interface{}) {
|
||||
for _, arg := range args {
|
||||
switch arg := arg.(type) {
|
||||
default:
|
||||
w.p.errorf("mach-o error: cannot encode %T", arg)
|
||||
case nil:
|
||||
// skip
|
||||
case []byte:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case string:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case uint32:
|
||||
w.order.PutUint32(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
case []uint32:
|
||||
for _, x := range arg {
|
||||
w.order.PutUint32(w.tmp[:], x)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
case uint64:
|
||||
w.order.PutUint64(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
case Addr:
|
||||
if w.is64 {
|
||||
w.order.PutUint64(w.tmp[:], uint64(arg))
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
} else {
|
||||
if Addr(uint32(arg)) != arg {
|
||||
w.p.errorf("mach-o error: truncating address %#x to uint32", arg)
|
||||
}
|
||||
w.order.PutUint32(w.tmp[:], uint32(arg))
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// segmentSize returns the size of the encoding of seg in bytes.
|
||||
func (w *machoWriter) segmentSize(seg *machoSegment) int {
|
||||
if w.is64 {
|
||||
return 18*4 + 20*4*len(seg.Sections)
|
||||
}
|
||||
return 14*4 + 22*4*len(seg.Sections)
|
||||
}
|
||||
|
||||
// zeroPad returns the string s truncated or padded with NULs to n bytes.
|
||||
func zeroPad(s string, n int) string {
|
||||
if len(s) >= n {
|
||||
return s[:n]
|
||||
}
|
||||
return s + strings.Repeat("\x00", n-len(s))
|
||||
}
|
||||
|
||||
// size returns the encoded size of the header.
|
||||
func (h *machoHeader) size() int {
|
||||
// Could write separate code, but encoding is cheap; encode and throw it away.
|
||||
return len(h.encode())
|
||||
}
|
||||
|
||||
// encode returns the Mach-O encoding of the header.
|
||||
func (h *machoHeader) encode() []byte {
|
||||
w := &machoWriter{p: h.p}
|
||||
w.is64 = h.CPU&macho64Bit != 0
|
||||
w.order = w.p.byteorder
|
||||
|
||||
loadSize := 0
|
||||
for _, seg := range h.Segments {
|
||||
loadSize += w.segmentSize(seg)
|
||||
}
|
||||
for _, l := range h.Loads {
|
||||
loadSize += 4 * (2 + len(l.Data))
|
||||
}
|
||||
|
||||
w.encode(
|
||||
w.if64(macho.Magic64, macho.Magic32),
|
||||
uint32(h.CPU),
|
||||
uint32(h.SubCPU),
|
||||
uint32(h.FileType),
|
||||
uint32(len(h.Loads)+len(h.Segments)),
|
||||
uint32(loadSize),
|
||||
uint32(1),
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
|
||||
for _, seg := range h.Segments {
|
||||
w.encode(
|
||||
w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)),
|
||||
uint32(w.segmentSize(seg)),
|
||||
zeroPad(seg.Name, 16),
|
||||
seg.VirtAddr,
|
||||
seg.VirtSize,
|
||||
seg.FileOffset,
|
||||
seg.FileSize,
|
||||
seg.Prot1,
|
||||
seg.Prot2,
|
||||
uint32(len(seg.Sections)),
|
||||
seg.Flags,
|
||||
)
|
||||
for _, sect := range seg.Sections {
|
||||
w.encode(
|
||||
zeroPad(sect.Name, 16),
|
||||
zeroPad(seg.Name, 16),
|
||||
sect.Addr,
|
||||
sect.Size,
|
||||
sect.Offset,
|
||||
sect.Align,
|
||||
sect.Reloc,
|
||||
sect.Nreloc,
|
||||
sect.Flags,
|
||||
sect.Res1,
|
||||
sect.Res2,
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, load := range h.Loads {
|
||||
w.encode(
|
||||
load.Type,
|
||||
uint32(4*(2+len(load.Data))),
|
||||
load.Data,
|
||||
)
|
||||
}
|
||||
|
||||
return w.dst
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test macho writing by checking that each generated prog can be written
|
||||
// and then read back using debug/macho to get the same prog.
|
||||
// Also check against golden testdata file.
|
||||
var machoWriteTests = []struct {
|
||||
name string
|
||||
golden bool
|
||||
prog *Prog
|
||||
}{
|
||||
// amd64 exit 9
|
||||
{
|
||||
name: "exit9",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 13,
|
||||
FileOffset: 0,
|
||||
FileSize: 13,
|
||||
Data: []byte{
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 13,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world & exit 9
|
||||
{
|
||||
name: "hello",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 35,
|
||||
FileOffset: 0,
|
||||
FileSize: 35,
|
||||
Data: []byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
VirtSize: 12,
|
||||
FileOffset: 0x1000,
|
||||
FileSize: 12,
|
||||
Data: []byte("hello world\n"),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world from rodata & exit 0
|
||||
{
|
||||
name: "helloro",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 0x100c,
|
||||
FileOffset: 0,
|
||||
FileSize: 0x100c,
|
||||
Data: concat(
|
||||
[]byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x00, 0x00, 0x00, 0x00, // MOVL $0, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
make([]byte, 0x1000-35),
|
||||
[]byte("hello world\n"),
|
||||
),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
{
|
||||
Name: "rodata",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func concat(xs ...[]byte) []byte {
|
||||
var out []byte
|
||||
for _, x := range xs {
|
||||
out = append(out, x...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestMachoWrite(t *testing.T) {
|
||||
for _, tt := range machoWriteTests {
|
||||
name := tt.prog.GOARCH + "." + tt.name
|
||||
prog := cloneProg(tt.prog)
|
||||
prog.init()
|
||||
var f machoFormat
|
||||
vsize, fsize := f.headerSize(prog)
|
||||
shiftProg(prog, vsize, fsize)
|
||||
var buf bytes.Buffer
|
||||
f.write(&buf, prog)
|
||||
if false { // enable to debug
|
||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
||||
}
|
||||
read, err := machoRead(machoArches[tt.prog.GOARCH], buf.Bytes())
|
||||
if err != nil {
|
||||
t.Errorf("%s: reading mach-o output:\n\t%v", name, err)
|
||||
continue
|
||||
}
|
||||
diffs := diffProg(read, prog)
|
||||
if diffs != nil {
|
||||
t.Errorf("%s: mismatched prog:\n\t%s", name, strings.Join(diffs, "\n\t"))
|
||||
continue
|
||||
}
|
||||
if !tt.golden {
|
||||
continue
|
||||
}
|
||||
checkGolden(t, buf.Bytes(), "testdata/macho."+name)
|
||||
}
|
||||
}
|
||||
|
||||
// machoRead reads the mach-o file in data and returns a corresponding prog.
|
||||
func machoRead(arch machoArch, data []byte) (*Prog, error) {
|
||||
f, err := macho.NewFile(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var errors []string
|
||||
errorf := func(format string, args ...interface{}) {
|
||||
errors = append(errors, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
magic := uint32(0xFEEDFACE)
|
||||
if arch.CPU&macho64Bit != 0 {
|
||||
magic |= 1
|
||||
}
|
||||
if f.Magic != magic {
|
||||
errorf("header: Magic = %#x, want %#x", f.Magic, magic)
|
||||
}
|
||||
if f.Cpu != macho.CpuAmd64 {
|
||||
errorf("header: CPU = %#x, want %#x", f.Cpu, macho.CpuAmd64)
|
||||
}
|
||||
if f.SubCpu != 3 {
|
||||
errorf("header: SubCPU = %#x, want %#x", f.SubCpu, 3)
|
||||
}
|
||||
if f.Type != 2 {
|
||||
errorf("header: FileType = %d, want %d", f.Type, 2)
|
||||
}
|
||||
if f.Flags != 1 {
|
||||
errorf("header: Flags = %d, want %d", f.Flags, 1)
|
||||
}
|
||||
|
||||
msects := f.Sections
|
||||
var limit uint64
|
||||
prog := new(Prog)
|
||||
for _, load := range f.Loads {
|
||||
switch load := load.(type) {
|
||||
default:
|
||||
errorf("unexpected macho load %T %x", load, load.Raw())
|
||||
|
||||
case macho.LoadBytes:
|
||||
if len(load) < 8 || len(load)%4 != 0 {
|
||||
errorf("unexpected load length %d", len(load))
|
||||
continue
|
||||
}
|
||||
cmd := f.ByteOrder.Uint32(load)
|
||||
switch macho.LoadCmd(cmd) {
|
||||
default:
|
||||
errorf("unexpected macho load cmd %s", macho.LoadCmd(cmd))
|
||||
case macho.LoadCmdUnixThread:
|
||||
data := make([]uint32, len(load[8:])/4)
|
||||
binary.Read(bytes.NewReader(load[8:]), f.ByteOrder, data)
|
||||
if len(data) != 44 {
|
||||
errorf("macho thread len(data) = %d, want 42", len(data))
|
||||
continue
|
||||
}
|
||||
if data[0] != 4 {
|
||||
errorf("macho thread type = %d, want 4", data[0])
|
||||
}
|
||||
if data[1] != uint32(len(data))-2 {
|
||||
errorf("macho thread desc len = %d, want %d", data[1], uint32(len(data))-2)
|
||||
continue
|
||||
}
|
||||
for i, val := range data[2:] {
|
||||
switch i {
|
||||
default:
|
||||
if val != 0 {
|
||||
errorf("macho thread data[%d] = %#x, want 0", i, val)
|
||||
}
|
||||
case 32:
|
||||
prog.Entry = Addr(val)
|
||||
case 33:
|
||||
prog.Entry |= Addr(val) << 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *macho.Segment:
|
||||
if load.Addr < limit {
|
||||
errorf("segments out of order: %q at %#x after %#x", load.Name, load.Addr, limit)
|
||||
}
|
||||
limit = load.Addr + load.Memsz
|
||||
if load.Name == "__PAGEZERO" || load.Addr == 0 && load.Filesz == 0 {
|
||||
if load.Name != "__PAGEZERO" {
|
||||
errorf("segment with Addr=0, Filesz=0 is named %q, want %q", load.Name, "__PAGEZERO")
|
||||
} else if load.Addr != 0 || load.Filesz != 0 {
|
||||
errorf("segment %q has Addr=%#x, Filesz=%d, want Addr=%#x, Filesz=%d", load.Name, load.Addr, load.Filesz, 0, 0)
|
||||
}
|
||||
prog.UnmappedSize = Addr(load.Memsz)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(load.Name, "__") {
|
||||
errorf("segment name %q does not begin with %q", load.Name, "__")
|
||||
}
|
||||
if strings.ToUpper(load.Name) != load.Name {
|
||||
errorf("segment name %q is not all upper case", load.Name)
|
||||
}
|
||||
|
||||
seg := &Segment{
|
||||
Name: strings.ToLower(strings.TrimPrefix(load.Name, "__")),
|
||||
VirtAddr: Addr(load.Addr),
|
||||
VirtSize: Addr(load.Memsz),
|
||||
FileOffset: Addr(load.Offset),
|
||||
FileSize: Addr(load.Filesz),
|
||||
}
|
||||
prog.Segments = append(prog.Segments, seg)
|
||||
|
||||
data, err := load.Data()
|
||||
if err != nil {
|
||||
errorf("loading data from %q: %v", load.Name, err)
|
||||
}
|
||||
seg.Data = data
|
||||
|
||||
var maxprot, prot uint32
|
||||
if load.Name == "__TEXT" {
|
||||
maxprot, prot = 7, 5
|
||||
} else {
|
||||
maxprot, prot = 3, 3
|
||||
}
|
||||
if load.Maxprot != maxprot || load.Prot != prot {
|
||||
errorf("segment %q protection is %d, %d, want %d, %d",
|
||||
load.Name, load.Maxprot, load.Prot, maxprot, prot)
|
||||
}
|
||||
|
||||
for len(msects) > 0 && msects[0].Addr < load.Addr+load.Memsz {
|
||||
msect := msects[0]
|
||||
msects = msects[1:]
|
||||
|
||||
if msect.Offset > 0 && prog.HeaderSize == 0 {
|
||||
prog.HeaderSize = Addr(msect.Offset)
|
||||
if seg.FileOffset != 0 {
|
||||
errorf("initial segment %q does not map header", load.Name)
|
||||
}
|
||||
seg.VirtAddr += prog.HeaderSize
|
||||
seg.VirtSize -= prog.HeaderSize
|
||||
seg.FileOffset += prog.HeaderSize
|
||||
seg.FileSize -= prog.HeaderSize
|
||||
seg.Data = seg.Data[prog.HeaderSize:]
|
||||
}
|
||||
|
||||
if msect.Addr < load.Addr {
|
||||
errorf("section %q at address %#x is missing segment", msect.Name, msect.Addr)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(msect.Name, "__") {
|
||||
errorf("section name %q does not begin with %q", msect.Name, "__")
|
||||
}
|
||||
if strings.ToLower(msect.Name) != msect.Name {
|
||||
errorf("section name %q is not all lower case", msect.Name)
|
||||
}
|
||||
if msect.Seg != load.Name {
|
||||
errorf("section %q is lists segment name %q, want %q",
|
||||
msect.Name, msect.Seg, load.Name)
|
||||
}
|
||||
if uint64(msect.Offset) != uint64(load.Offset)+msect.Addr-load.Addr {
|
||||
errorf("section %q file offset is %#x, want %#x",
|
||||
msect.Name, msect.Offset, load.Offset+msect.Addr-load.Addr)
|
||||
}
|
||||
if msect.Reloff != 0 || msect.Nreloc != 0 {
|
||||
errorf("section %q has reloff %d,%d, want %d,%d",
|
||||
msect.Name, msect.Reloff, msect.Nreloc, 0, 0)
|
||||
}
|
||||
flags := uint32(0)
|
||||
if msect.Name == "__text" {
|
||||
flags = 0x400
|
||||
}
|
||||
if msect.Offset == 0 {
|
||||
flags = 1
|
||||
}
|
||||
if msect.Flags != flags {
|
||||
errorf("section %q flags = %#x, want %#x", msect.Name, msect.Flags, flags)
|
||||
}
|
||||
sect := &Section{
|
||||
Name: strings.ToLower(strings.TrimPrefix(msect.Name, "__")),
|
||||
VirtAddr: Addr(msect.Addr),
|
||||
Size: Addr(msect.Size),
|
||||
Align: 1 << msect.Align,
|
||||
}
|
||||
seg.Sections = append(seg.Sections, sect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, msect := range msects {
|
||||
errorf("section %q has no segment", msect.Name)
|
||||
}
|
||||
|
||||
limit = 0
|
||||
for _, msect := range f.Sections {
|
||||
if msect.Addr < limit {
|
||||
errorf("sections out of order: %q at %#x after %#x", msect.Name, msect.Addr, limit)
|
||||
}
|
||||
limit = msect.Addr + msect.Size
|
||||
}
|
||||
|
||||
err = nil
|
||||
if errors != nil {
|
||||
err = fmt.Errorf("%s", strings.Join(errors, "\n\t"))
|
||||
}
|
||||
return prog, err
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Placeholder to keep build building.
|
||||
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
@@ -1,480 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of runtime function information (pclntab).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"cmd/internal/obj"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var zerofunc goobj.Func
|
||||
|
||||
// pclntab collects the runtime function data for each function that will
|
||||
// be listed in the binary and builds a single table describing all functions.
|
||||
// This table is used at run time for stack traces and to look up PC-specific
|
||||
// information during garbage collection. The symbol created is named
|
||||
// "pclntab" for historical reasons; the scope of the table has grown to
|
||||
// include more than just PC/line number correspondences.
|
||||
// The table format is documented at https://golang.org/s/go12symtab.
|
||||
func (p *Prog) pclntab() {
|
||||
// Count number of functions going into the binary,
|
||||
// so that we can size the initial index correctly.
|
||||
nfunc := 0
|
||||
for _, sym := range p.SymOrder {
|
||||
if sym.Kind != goobj.STEXT {
|
||||
continue
|
||||
}
|
||||
nfunc++
|
||||
}
|
||||
|
||||
// Table header.
|
||||
buf := new(SymBuffer)
|
||||
buf.Init(p)
|
||||
buf.SetSize(8 + p.ptrsize)
|
||||
off := 0
|
||||
off = buf.Uint32(off, 0xfffffffb)
|
||||
off = buf.Uint8(off, 0)
|
||||
off = buf.Uint8(off, 0)
|
||||
off = buf.Uint8(off, uint8(p.pcquantum))
|
||||
off = buf.Uint8(off, uint8(p.ptrsize))
|
||||
off = buf.Uint(off, uint64(nfunc), p.ptrsize)
|
||||
indexOff := off
|
||||
off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in
|
||||
off += 4 // file table start offset, to be filled in
|
||||
buf.SetSize(off)
|
||||
|
||||
// One-file cache for reading PCData tables from package files.
|
||||
// TODO(rsc): Better I/O strategy.
|
||||
var (
|
||||
file *os.File
|
||||
fname string
|
||||
)
|
||||
|
||||
// Files gives the file numbering for source file names recorded
|
||||
// in the binary.
|
||||
files := make(map[string]int)
|
||||
|
||||
// Build the table, build the index, and build the file name numbering.
|
||||
// The loop here must visit functions in the same order that they will
|
||||
// be stored in the binary, or else binary search over the index will fail.
|
||||
// The runtime checks that the index is sorted properly at program start time.
|
||||
var lastSym *Sym
|
||||
for _, sym := range p.SymOrder {
|
||||
if sym.Kind != goobj.STEXT {
|
||||
continue
|
||||
}
|
||||
lastSym = sym
|
||||
|
||||
// Treat no recorded function information same as all zeros.
|
||||
f := sym.Func
|
||||
if f == nil {
|
||||
f = &zerofunc
|
||||
}
|
||||
|
||||
// Open package file if needed, for reading PC data.
|
||||
if fname != sym.Package.File {
|
||||
if file != nil {
|
||||
file.Close()
|
||||
}
|
||||
var err error
|
||||
file, err = os.Open(sym.Package.File)
|
||||
if err != nil {
|
||||
p.errorf("%v: %v", sym, err)
|
||||
return
|
||||
}
|
||||
fname = sym.Package.File
|
||||
}
|
||||
|
||||
// off is the offset of the table entry where we're going to write
|
||||
// the encoded form of Func.
|
||||
// indexOff is the current position in the table index;
|
||||
// we add an entry in the index pointing at off.
|
||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
||||
indexOff = buf.Addr(indexOff, sym.SymID, 0)
|
||||
indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize)
|
||||
|
||||
// The Func encoding starts with a header giving offsets
|
||||
// to data blobs, and then the data blobs themselves.
|
||||
// end gives the current write position for the data blobs.
|
||||
end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize
|
||||
if len(f.FuncData) > 0 {
|
||||
end += -end & (p.ptrsize - 1)
|
||||
}
|
||||
buf.SetSize(end)
|
||||
|
||||
// entry uintptr
|
||||
// name int32
|
||||
// args int32
|
||||
// frame int32
|
||||
//
|
||||
// The frame recorded in the object file is
|
||||
// the frame size used in an assembly listing, which does
|
||||
// not include the caller PC on the stack.
|
||||
// The frame size we want to list here is the delta from
|
||||
// this function's SP to its caller's SP, which does include
|
||||
// the caller PC. Add p.ptrsize to f.Frame to adjust.
|
||||
// TODO(rsc): Record the same frame size in the object file.
|
||||
off = buf.Addr(off, sym.SymID, 0)
|
||||
off = buf.Uint32(off, uint32(addString(buf, sym.Name)))
|
||||
off = buf.Uint32(off, uint32(f.Args))
|
||||
off = buf.Uint32(off, uint32(f.Frame+p.ptrsize))
|
||||
|
||||
// pcdata
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP)))
|
||||
off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files)))
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine)))
|
||||
off = buf.Uint32(off, uint32(len(f.PCData)))
|
||||
off = buf.Uint32(off, uint32(len(f.FuncData)))
|
||||
for _, pcdata := range f.PCData {
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata)))
|
||||
}
|
||||
|
||||
// funcdata
|
||||
if len(f.FuncData) > 0 {
|
||||
off += -off & (p.ptrsize - 1) // must be pointer-aligned
|
||||
for _, funcdata := range f.FuncData {
|
||||
if funcdata.Sym.Name == "" {
|
||||
off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize)
|
||||
} else {
|
||||
off = buf.Addr(off, funcdata.Sym, funcdata.Offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if off != end {
|
||||
p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end)
|
||||
break
|
||||
}
|
||||
}
|
||||
if file != nil {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// Final entry of index is end PC of last function.
|
||||
indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size))
|
||||
|
||||
// Start file table.
|
||||
// Function index is immediately followed by offset to file table.
|
||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
||||
buf.Uint32(indexOff, uint32(off))
|
||||
|
||||
// File table is an array of uint32s.
|
||||
// The first entry gives 1+n, the size of the array.
|
||||
// The following n entries hold offsets to string data.
|
||||
// File number n uses the string pointed at by entry n.
|
||||
// File number 0 is invalid.
|
||||
buf.SetSize(off + (1+len(files))*4)
|
||||
buf.Uint32(off, uint32(1+len(files)))
|
||||
var filestr []string
|
||||
for file := range files {
|
||||
filestr = append(filestr, file)
|
||||
}
|
||||
sort.Strings(filestr)
|
||||
for _, file := range filestr {
|
||||
id := files[file]
|
||||
buf.Uint32(off+4*id, uint32(addString(buf, file)))
|
||||
}
|
||||
|
||||
pclntab := &Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "runtime.pclntab"},
|
||||
Kind: goobj.SPCLNTAB,
|
||||
Size: buf.Size(),
|
||||
Reloc: buf.Reloc(),
|
||||
},
|
||||
Bytes: buf.Bytes(),
|
||||
}
|
||||
p.addSym(pclntab)
|
||||
}
|
||||
|
||||
// addString appends the string s to the buffer b.
|
||||
// It returns the offset of the beginning of the string in the buffer.
|
||||
func addString(b *SymBuffer, s string) int {
|
||||
off := b.Size()
|
||||
b.SetSize(off + len(s) + 1)
|
||||
copy(b.data[off:], s)
|
||||
return off
|
||||
}
|
||||
|
||||
// addPCTable appends the PC-data table stored in the file f at the location loc
|
||||
// to the symbol buffer b. It returns the offset of the beginning of the table
|
||||
// in the buffer.
|
||||
func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int {
|
||||
if loc.Size == 0 {
|
||||
return 0
|
||||
}
|
||||
off := b.Size()
|
||||
b.SetSize(off + int(loc.Size))
|
||||
_, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
}
|
||||
return off
|
||||
}
|
||||
|
||||
// addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table
|
||||
// to use the global numbering maintained in the files map. It adds new files to the
|
||||
// map as necessary.
|
||||
func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int {
|
||||
if loc.Size == 0 {
|
||||
return 0
|
||||
}
|
||||
off := b.Size()
|
||||
|
||||
src := make([]byte, loc.Size)
|
||||
_, err := f.ReadAt(src, loc.Offset)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return 0
|
||||
}
|
||||
|
||||
filenum := make([]int, len(sym.Func.File))
|
||||
for i, name := range sym.Func.File {
|
||||
num := files[name]
|
||||
if num == 0 {
|
||||
num = len(files) + 1
|
||||
files[name] = num
|
||||
}
|
||||
filenum[i] = num
|
||||
}
|
||||
|
||||
var dst []byte
|
||||
newval := int32(-1)
|
||||
var it PCIter
|
||||
for it.Init(p, src); !it.Done; it.Next() {
|
||||
// value delta
|
||||
oldval := it.Value
|
||||
val := oldval
|
||||
if oldval != -1 {
|
||||
if oldval < 0 || int(oldval) >= len(filenum) {
|
||||
p.errorf("%s: corrupt pc-file table", sym)
|
||||
break
|
||||
}
|
||||
val = int32(filenum[oldval])
|
||||
}
|
||||
dv := val - newval
|
||||
newval = val
|
||||
uv := uint32(dv<<1) ^ uint32(dv>>31)
|
||||
dst = appendVarint(dst, uv)
|
||||
|
||||
// pc delta
|
||||
dst = appendVarint(dst, it.NextPC-it.PC)
|
||||
}
|
||||
if it.Corrupt {
|
||||
p.errorf("%s: corrupt pc-file table", sym)
|
||||
}
|
||||
|
||||
// terminating value delta
|
||||
dst = appendVarint(dst, 0)
|
||||
|
||||
b.SetSize(off + len(dst))
|
||||
copy(b.data[off:], dst)
|
||||
return off
|
||||
}
|
||||
|
||||
// A SymBuffer is a buffer for preparing the data image of a
|
||||
// linker-generated symbol.
|
||||
type SymBuffer struct {
|
||||
data []byte
|
||||
reloc []goobj.Reloc
|
||||
order binary.ByteOrder
|
||||
ptrsize int
|
||||
}
|
||||
|
||||
// Init initializes the buffer for writing.
|
||||
func (b *SymBuffer) Init(p *Prog) {
|
||||
b.data = nil
|
||||
b.reloc = nil
|
||||
b.order = p.byteorder
|
||||
b.ptrsize = p.ptrsize
|
||||
}
|
||||
|
||||
// Bytes returns the buffer data.
|
||||
func (b *SymBuffer) Bytes() []byte {
|
||||
return b.data
|
||||
}
|
||||
|
||||
// SetSize sets the buffer's data size to n bytes.
|
||||
func (b *SymBuffer) SetSize(n int) {
|
||||
for cap(b.data) < n {
|
||||
b.data = append(b.data[:cap(b.data)], 0)
|
||||
}
|
||||
b.data = b.data[:n]
|
||||
}
|
||||
|
||||
// Size returns the buffer's data size.
|
||||
func (b *SymBuffer) Size() int {
|
||||
return len(b.data)
|
||||
}
|
||||
|
||||
// Reloc returns the buffered relocations.
|
||||
func (b *SymBuffer) Reloc() []goobj.Reloc {
|
||||
return b.reloc
|
||||
}
|
||||
|
||||
// Uint8 sets the uint8 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint8(off int, v uint8) int {
|
||||
b.data[off] = v
|
||||
return off + 1
|
||||
}
|
||||
|
||||
// Uint16 sets the uint16 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint16(off int, v uint16) int {
|
||||
b.order.PutUint16(b.data[off:], v)
|
||||
return off + 2
|
||||
}
|
||||
|
||||
// Uint32 sets the uint32 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint32(off int, v uint32) int {
|
||||
b.order.PutUint32(b.data[off:], v)
|
||||
return off + 4
|
||||
}
|
||||
|
||||
// Uint64 sets the uint64 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint64(off int, v uint64) int {
|
||||
b.order.PutUint64(b.data[off:], v)
|
||||
return off + 8
|
||||
}
|
||||
|
||||
// Uint sets the size-byte unsigned integer at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint(off int, v uint64, size int) int {
|
||||
switch size {
|
||||
case 1:
|
||||
return b.Uint8(off, uint8(v))
|
||||
case 2:
|
||||
return b.Uint16(off, uint16(v))
|
||||
case 4:
|
||||
return b.Uint32(off, uint32(v))
|
||||
case 8:
|
||||
return b.Uint64(off, v)
|
||||
}
|
||||
panic("invalid use of SymBuffer.SetUint")
|
||||
}
|
||||
|
||||
// Addr sets the pointer-sized address at offset off to refer
|
||||
// to symoff bytes past the start of sym. It returns the offset
|
||||
// just beyond the address.
|
||||
func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int {
|
||||
b.reloc = append(b.reloc, goobj.Reloc{
|
||||
Offset: off,
|
||||
Size: b.ptrsize,
|
||||
Sym: sym,
|
||||
Add: int(symoff),
|
||||
Type: obj.R_ADDR,
|
||||
})
|
||||
return off + b.ptrsize
|
||||
}
|
||||
|
||||
// A PCIter implements iteration over PC-data tables.
|
||||
//
|
||||
// var it PCIter
|
||||
// for it.Init(p, data); !it.Done; it.Next() {
|
||||
// it.Value holds from it.PC up to (but not including) it.NextPC
|
||||
// }
|
||||
// if it.Corrupt {
|
||||
// data was malformed
|
||||
// }
|
||||
//
|
||||
type PCIter struct {
|
||||
PC uint32
|
||||
NextPC uint32
|
||||
Value int32
|
||||
Done bool
|
||||
Corrupt bool
|
||||
p []byte
|
||||
start bool
|
||||
pcquantum uint32
|
||||
}
|
||||
|
||||
// Init initializes the iteration.
|
||||
// On return, if it.Done is true, the iteration is over.
|
||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
||||
func (it *PCIter) Init(p *Prog, buf []byte) {
|
||||
it.p = buf
|
||||
it.PC = 0
|
||||
it.NextPC = 0
|
||||
it.Value = -1
|
||||
it.start = true
|
||||
it.pcquantum = uint32(p.pcquantum)
|
||||
it.Done = false
|
||||
it.Next()
|
||||
}
|
||||
|
||||
// Next steps forward one entry in the table.
|
||||
// On return, if it.Done is true, the iteration is over.
|
||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
||||
func (it *PCIter) Next() {
|
||||
it.PC = it.NextPC
|
||||
if it.Done {
|
||||
return
|
||||
}
|
||||
if len(it.p) == 0 {
|
||||
it.Done = true
|
||||
return
|
||||
}
|
||||
|
||||
// value delta
|
||||
uv, p, ok := decodeVarint(it.p)
|
||||
if !ok {
|
||||
it.Done = true
|
||||
it.Corrupt = true
|
||||
return
|
||||
}
|
||||
it.p = p
|
||||
if uv == 0 && !it.start {
|
||||
it.Done = true
|
||||
return
|
||||
}
|
||||
it.start = false
|
||||
sv := int32(uv>>1) ^ int32(uv<<31)>>31
|
||||
it.Value += sv
|
||||
|
||||
// pc delta
|
||||
uv, it.p, ok = decodeVarint(it.p)
|
||||
if !ok {
|
||||
it.Done = true
|
||||
it.Corrupt = true
|
||||
return
|
||||
}
|
||||
it.NextPC = it.PC + uv*it.pcquantum
|
||||
}
|
||||
|
||||
// decodeVarint decodes an unsigned varint from p,
|
||||
// reporting the value, the remainder of the data, and
|
||||
// whether the decoding was successful.
|
||||
func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) {
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if len(p) == 0 {
|
||||
return
|
||||
}
|
||||
c := uint32(p[0])
|
||||
p = p[1:]
|
||||
v |= (c & 0x7F) << shift
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return v, p, true
|
||||
}
|
||||
|
||||
// appendVarint appends an unsigned varint encoding of v to p
|
||||
// and returns the resulting slice.
|
||||
func appendVarint(p []byte, v uint32) []byte {
|
||||
for ; v >= 0x80; v >>= 7 {
|
||||
p = append(p, byte(v)|0x80)
|
||||
}
|
||||
p = append(p, byte(v))
|
||||
return p
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/goobj"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test of pcln table encoding.
|
||||
// testdata/genpcln.go generates an assembly file with
|
||||
// pseudorandom values for the data that pclntab stores.
|
||||
// This test recomputes the same pseudorandom stream
|
||||
// and checks that the final linked binary uses those values
|
||||
// as well.
|
||||
func TestPclntab(t *testing.T) {
|
||||
p := &Prog{
|
||||
GOOS: "darwin",
|
||||
GOARCH: "amd64",
|
||||
Error: func(s string) { t.Error(s) },
|
||||
StartSym: "start",
|
||||
omitRuntime: true,
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, "testdata/pclntab.6")
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The algorithm for computing values here must match
|
||||
// the one in testdata/genpcln.go.
|
||||
for f := 0; f < 3; f++ {
|
||||
file := "input"
|
||||
line := 1
|
||||
rnd := rand.New(rand.NewSource(int64(f)))
|
||||
args := rnd.Intn(100) * 8
|
||||
frame := 32 + rnd.Intn(32)/8*8
|
||||
size := 200 + rnd.Intn(100)*8
|
||||
|
||||
name := fmt.Sprintf("func%d", f)
|
||||
r, off, fargs, fframe, ok := findFunc(t, p, name)
|
||||
if !ok {
|
||||
continue // error already printed
|
||||
}
|
||||
if fargs != args {
|
||||
t.Errorf("%s: args=%d, want %d", name, fargs, args)
|
||||
}
|
||||
if fframe != frame+8 {
|
||||
t.Errorf("%s: frame=%d, want %d", name, fframe, frame+8)
|
||||
}
|
||||
|
||||
// Check FUNCDATA 1.
|
||||
fdata, ok := loadFuncdata(t, r, name, off, 1)
|
||||
if ok {
|
||||
fsym := p.Syms[goobj.SymID{Name: fmt.Sprintf("funcdata%d", f)}]
|
||||
if fsym == nil {
|
||||
t.Errorf("funcdata%d is missing in binary", f)
|
||||
} else if fdata != fsym.Addr {
|
||||
t.Errorf("%s: funcdata 1 = %#x, want %#x", name, fdata, fsym.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Walk code checking pcdata values.
|
||||
spadj := 0
|
||||
pcdata1 := -1
|
||||
pcdata2 := -1
|
||||
|
||||
checkPCSP(t, r, name, off, 0, 0)
|
||||
checkPCData(t, r, name, off, 0, 0, -1)
|
||||
checkPCData(t, r, name, off, 0, 1, -1)
|
||||
checkPCData(t, r, name, off, 0, 2, -1)
|
||||
|
||||
firstpc := 4
|
||||
for i := 0; i < size; i++ {
|
||||
pc := firstpc + i // skip SP adjustment to allocate frame
|
||||
if i >= 0x100 && t.Failed() {
|
||||
break
|
||||
}
|
||||
// Possible SP adjustment.
|
||||
checkPCSP(t, r, name, off, pc, frame+spadj)
|
||||
if rnd.Intn(100) == 0 {
|
||||
checkPCFileLine(t, r, name, off, pc, file, line)
|
||||
checkPCData(t, r, name, off, pc, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc, 2, pcdata2)
|
||||
i += 1
|
||||
pc = firstpc + i
|
||||
checkPCFileLine(t, r, name, off, pc-1, file, line)
|
||||
checkPCData(t, r, name, off, pc-1, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc-1, 2, pcdata2)
|
||||
checkPCSP(t, r, name, off, pc-1, frame+spadj)
|
||||
|
||||
if spadj <= -32 || spadj < 32 && rnd.Intn(2) == 0 {
|
||||
spadj += 8
|
||||
} else {
|
||||
spadj -= 8
|
||||
}
|
||||
checkPCSP(t, r, name, off, pc, frame+spadj)
|
||||
}
|
||||
|
||||
// Possible PCFile change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
file = fmt.Sprintf("file%d.s", rnd.Intn(10))
|
||||
line = rnd.Intn(100) + 1
|
||||
}
|
||||
|
||||
// Possible PCLine change.
|
||||
if rnd.Intn(10) == 0 {
|
||||
line = rnd.Intn(1000) + 1
|
||||
}
|
||||
|
||||
// Possible PCData $1 change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
pcdata1 = rnd.Intn(1000)
|
||||
}
|
||||
|
||||
// Possible PCData $2 change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
pcdata2 = rnd.Intn(1000)
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
checkPCFileLine(t, r, name, off, 0, file, line)
|
||||
checkPCFileLine(t, r, name, off, pc-1, file, line)
|
||||
}
|
||||
checkPCFileLine(t, r, name, off, pc, file, line)
|
||||
checkPCData(t, r, name, off, pc, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc, 2, pcdata2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findFunc finds the function information in the pclntab of p
|
||||
// for the function with the given name.
|
||||
// It returns a symbol reader for pclntab, the offset of the function information
|
||||
// within that symbol, and the args and frame values read out of the information.
|
||||
func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) {
|
||||
tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}]
|
||||
if tabsym == nil {
|
||||
t.Errorf("pclntab is missing in binary")
|
||||
return
|
||||
}
|
||||
|
||||
r = new(SymReader)
|
||||
r.Init(p, tabsym)
|
||||
|
||||
// pclntab must with 8-byte header
|
||||
if r.Uint32(0) != 0xfffffffb || r.Uint8(4) != 0 || r.Uint8(5) != 0 || r.Uint8(6) != uint8(p.pcquantum) || r.Uint8(7) != uint8(p.ptrsize) {
|
||||
t.Errorf("pclntab has incorrect header %.8x", r.data[:8])
|
||||
return
|
||||
}
|
||||
|
||||
sym := p.Syms[goobj.SymID{Name: name}]
|
||||
if sym == nil {
|
||||
t.Errorf("%s is missing in the binary", name)
|
||||
return
|
||||
}
|
||||
|
||||
// index is nfunc addr0 off0 addr1 off1 ... addr_nfunc (sentinel)
|
||||
nfunc := int(r.Addr(8))
|
||||
i := sort.Search(nfunc, func(i int) bool {
|
||||
return r.Addr(8+p.ptrsize*(1+2*i)) >= sym.Addr
|
||||
})
|
||||
if entry := r.Addr(8 + p.ptrsize*(1+2*i)); entry != sym.Addr {
|
||||
indexTab := make([]Addr, 2*nfunc+1)
|
||||
for j := range indexTab {
|
||||
indexTab[j] = r.Addr(8 + p.ptrsize*(1+j))
|
||||
}
|
||||
t.Errorf("pclntab is missing entry for %s (%#x): %#x", name, sym.Addr, indexTab)
|
||||
return
|
||||
}
|
||||
|
||||
off = int(r.Addr(8 + p.ptrsize*(1+2*i+1)))
|
||||
|
||||
// func description at off is
|
||||
// entry addr
|
||||
// nameoff uint32
|
||||
// args uint32
|
||||
// frame uint32
|
||||
// pcspoff uint32
|
||||
// pcfileoff uint32
|
||||
// pclineoff uint32
|
||||
// npcdata uint32
|
||||
// nfuncdata uint32
|
||||
// pcdata npcdata*uint32
|
||||
// funcdata nfuncdata*addr
|
||||
//
|
||||
if entry := r.Addr(off); entry != sym.Addr {
|
||||
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has entry=%#x", name, sym.Addr, entry)
|
||||
return
|
||||
}
|
||||
nameoff := int(r.Uint32(off + p.ptrsize))
|
||||
args = int(r.Uint32(off + p.ptrsize + 1*4))
|
||||
frame = int(r.Uint32(off + p.ptrsize + 2*4))
|
||||
|
||||
fname := r.String(nameoff)
|
||||
if fname != name {
|
||||
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has name %q", name, sym.Addr, fname)
|
||||
}
|
||||
|
||||
ok = true // off, args, frame are usable
|
||||
return
|
||||
}
|
||||
|
||||
// loadFuncdata returns the funcdata #fnum value
|
||||
// loaded from the function information for name.
|
||||
func loadFuncdata(t *testing.T, r *SymReader, name string, off int, fnum int) (Addr, bool) {
|
||||
npcdata := int(r.Uint32(off + r.p.ptrsize + 6*4))
|
||||
nfuncdata := int(r.Uint32(off + r.p.ptrsize + 7*4))
|
||||
if fnum >= nfuncdata {
|
||||
t.Errorf("pclntab(%s): no funcdata %d (only < %d)", name, fnum, nfuncdata)
|
||||
return 0, false
|
||||
}
|
||||
fdataoff := off + r.p.ptrsize + (8+npcdata)*4 + fnum*r.p.ptrsize
|
||||
fdataoff += fdataoff & 4
|
||||
return r.Addr(fdataoff), true
|
||||
}
|
||||
|
||||
// checkPCSP checks that the PCSP table in the function information at off
|
||||
// lists spadj as the sp delta for pc.
|
||||
func checkPCSP(t *testing.T, r *SymReader, name string, off, pc, spadj int) {
|
||||
pcoff := r.Uint32(off + r.p.ptrsize + 3*4)
|
||||
pcval, ok := readPCData(t, r, name, "PCSP", pcoff, pc)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pcval != spadj {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, pcsp=%d, want %d", name, pc, pcval, spadj)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPCSP checks that the PCFile and PCLine tables in the function information at off
|
||||
// list file, line as the file name and line number for pc.
|
||||
func checkPCFileLine(t *testing.T, r *SymReader, name string, off, pc int, file string, line int) {
|
||||
pcfileoff := r.Uint32(off + r.p.ptrsize + 4*4)
|
||||
pclineoff := r.Uint32(off + r.p.ptrsize + 5*4)
|
||||
pcfilenum, ok1 := readPCData(t, r, name, "PCFile", pcfileoff, pc)
|
||||
pcline, ok2 := readPCData(t, r, name, "PCLine", pclineoff, pc)
|
||||
if !ok1 || !ok2 {
|
||||
return
|
||||
}
|
||||
nfunc := int(r.Addr(8))
|
||||
filetaboff := r.Uint32(8 + r.p.ptrsize*2*(nfunc+1))
|
||||
nfile := int(r.Uint32(int(filetaboff)))
|
||||
if pcfilenum <= 0 || pcfilenum >= nfile {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, filenum=%d (invalid; nfile=%d)", name, pc, pcfilenum, nfile)
|
||||
}
|
||||
pcfile := r.String(int(r.Uint32(int(filetaboff) + pcfilenum*4)))
|
||||
if !strings.HasSuffix(pcfile, file) {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, file=%q, want %q", name, pc, pcfile, file)
|
||||
}
|
||||
if pcline != line {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, line=%d, want %d", name, pc, pcline, line)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPCData checks that the PCData#pnum table in the function information at off
|
||||
// list val as the value for pc.
|
||||
func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int) {
|
||||
pcoff := r.Uint32(off + r.p.ptrsize + (8+pnum)*4)
|
||||
pcval, ok := readPCData(t, r, name, fmt.Sprintf("PCData#%d", pnum), pcoff, pc)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pcval != val {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, pcdata#%d=%d, want %d", name, pc, pnum, pcval, val)
|
||||
}
|
||||
}
|
||||
|
||||
// readPCData reads the PCData table offset off
|
||||
// to obtain and return the value associated with pc.
|
||||
func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) {
|
||||
// "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero,
|
||||
// that table is considered missing, and all PCs take value -1."
|
||||
if pcoff == 0 {
|
||||
return -1, true
|
||||
}
|
||||
|
||||
var it PCIter
|
||||
for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() {
|
||||
if it.PC <= uint32(pc) && uint32(pc) < it.NextPC {
|
||||
return int(it.Value), true
|
||||
}
|
||||
}
|
||||
if it.Corrupt {
|
||||
t.Errorf("pclntab(%s): %s: corrupt pcdata table", name, pcdataname)
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// A SymReader provides typed access to the data for a symbol.
|
||||
type SymReader struct {
|
||||
p *Prog
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (r *SymReader) Init(p *Prog, sym *Sym) {
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
r.p = p
|
||||
r.data = data
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint8(off int) uint8 {
|
||||
return r.data[off]
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint16(off int) uint16 {
|
||||
return r.p.byteorder.Uint16(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint32(off int) uint32 {
|
||||
return r.p.byteorder.Uint32(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint64(off int) uint64 {
|
||||
return r.p.byteorder.Uint64(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Addr(off int) Addr {
|
||||
if r.p.ptrsize == 4 {
|
||||
return Addr(r.Uint32(off))
|
||||
}
|
||||
return Addr(r.Uint64(off))
|
||||
}
|
||||
|
||||
func (r *SymReader) String(off int) string {
|
||||
end := off
|
||||
for r.data[end] != '\x00' {
|
||||
end++
|
||||
}
|
||||
return string(r.data[off:end])
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// A Prog holds state for constructing an executable (program) image.
|
||||
//
|
||||
// The usual sequence of operations on a Prog is:
|
||||
//
|
||||
// p.init()
|
||||
// p.scan(file)
|
||||
// p.dead()
|
||||
// p.runtime()
|
||||
// p.layout()
|
||||
// p.load()
|
||||
// p.debug()
|
||||
// p.write(w)
|
||||
//
|
||||
// p.init is in this file. The rest of the methods are in files
|
||||
// named for the method. The convenience method p.link runs
|
||||
// this sequence.
|
||||
//
|
||||
type Prog struct {
|
||||
// Context
|
||||
GOOS string // target operating system
|
||||
GOARCH string // target architecture
|
||||
Format string // desired file format ("elf", "macho", ...)
|
||||
Error func(string) // called to report an error (if set)
|
||||
NumError int // number of errors printed
|
||||
StartSym string
|
||||
|
||||
// Derived context
|
||||
arch
|
||||
formatter formatter
|
||||
startSym goobj.SymID
|
||||
pkgdir string
|
||||
omitRuntime bool // do not load runtime package
|
||||
|
||||
// Input
|
||||
Packages map[string]*Package // loaded packages, by import path
|
||||
Syms map[goobj.SymID]*Sym // defined symbols, by symbol ID
|
||||
Missing map[goobj.SymID]bool // missing symbols
|
||||
Dead map[goobj.SymID]bool // symbols removed as dead
|
||||
SymOrder []*Sym // order syms were scanned
|
||||
MaxVersion int // max SymID.Version, for generating fresh symbol IDs
|
||||
|
||||
// Output
|
||||
UnmappedSize Addr // size of unmapped region at address 0
|
||||
HeaderSize Addr // size of object file header
|
||||
Entry Addr // virtual address where execution begins
|
||||
Segments []*Segment // loaded memory segments
|
||||
}
|
||||
|
||||
// An arch describes architecture-dependent settings.
|
||||
type arch struct {
|
||||
byteorder binary.ByteOrder
|
||||
ptrsize int
|
||||
pcquantum int
|
||||
}
|
||||
|
||||
// A formatter takes care of the details of generating a particular
|
||||
// kind of executable file.
|
||||
type formatter interface {
|
||||
// headerSize returns the footprint of the header for p
|
||||
// in both virtual address space and file bytes.
|
||||
// The footprint does not include any bytes stored at the
|
||||
// end of the file.
|
||||
headerSize(p *Prog) (virt, file Addr)
|
||||
|
||||
// write writes the executable file for p to w.
|
||||
write(w io.Writer, p *Prog)
|
||||
}
|
||||
|
||||
// An Addr represents a virtual memory address, a file address, or a size.
|
||||
// It must be a uint64, not a uintptr, so that a 32-bit linker can still generate a 64-bit binary.
|
||||
// It must be unsigned in order to link programs placed at very large start addresses.
|
||||
// Math involving Addrs must be checked carefully not to require negative numbers.
|
||||
type Addr uint64
|
||||
|
||||
// A Package is a Go package loaded from a file.
|
||||
type Package struct {
|
||||
*goobj.Package // table of contents
|
||||
File string // file name for reopening
|
||||
Syms []*Sym // symbols defined by this package
|
||||
}
|
||||
|
||||
// A Sym is a symbol defined in a loaded package.
|
||||
type Sym struct {
|
||||
*goobj.Sym // symbol metadata from package file
|
||||
Package *Package // package defining symbol
|
||||
Section *Section // section where symbol is placed in output program
|
||||
Addr Addr // virtual address of symbol in output program
|
||||
Bytes []byte // symbol data, for internally defined symbols
|
||||
}
|
||||
|
||||
// A Segment is a loaded memory segment.
|
||||
// A Prog is expected to have segments named "text" and optionally "data",
|
||||
// in that order, before any other segments.
|
||||
type Segment struct {
|
||||
Name string // name of segment: "text", "data", ...
|
||||
VirtAddr Addr // virtual memory address of segment base
|
||||
VirtSize Addr // size of segment in memory
|
||||
FileOffset Addr // file offset of segment base
|
||||
FileSize Addr // size of segment in file; can be less than VirtSize
|
||||
Sections []*Section // sections inside segment
|
||||
Data []byte // raw data of segment image
|
||||
}
|
||||
|
||||
// A Section is part of a loaded memory segment.
|
||||
type Section struct {
|
||||
Name string // name of section: "text", "rodata", "noptrbss", and so on
|
||||
VirtAddr Addr // virtual memory address of section base
|
||||
Size Addr // size of section in memory
|
||||
Align Addr // required alignment
|
||||
InFile bool // section has image data in file (like data, unlike bss)
|
||||
Syms []*Sym // symbols stored in section
|
||||
Segment *Segment // segment containing section
|
||||
}
|
||||
|
||||
func (p *Prog) errorf(format string, args ...interface{}) {
|
||||
if p.Error != nil {
|
||||
p.Error(fmt.Sprintf(format, args...))
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
}
|
||||
p.NumError++
|
||||
}
|
||||
|
||||
// link is the one-stop convenience method for running a link.
|
||||
// It writes to w the object file generated from using mainFile as the main package.
|
||||
func (p *Prog) link(w io.Writer, mainFile string) {
|
||||
p.init()
|
||||
p.scan(mainFile)
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.dead()
|
||||
p.runtime()
|
||||
p.autoData()
|
||||
p.layout()
|
||||
p.autoConst()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.load()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.debug()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.write(w)
|
||||
}
|
||||
|
||||
// init initializes p for use by the other methods.
|
||||
func (p *Prog) init() {
|
||||
// Set default context if not overridden.
|
||||
if p.GOOS == "" {
|
||||
p.GOOS = build.Default.GOOS
|
||||
}
|
||||
if p.GOARCH == "" {
|
||||
p.GOARCH = build.Default.GOARCH
|
||||
}
|
||||
if p.Format == "" {
|
||||
p.Format = goosFormat[p.GOOS]
|
||||
if p.Format == "" {
|
||||
p.errorf("no default file format for GOOS %q", p.GOOS)
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.StartSym == "" {
|
||||
p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS)
|
||||
}
|
||||
|
||||
// Derive internal context.
|
||||
p.formatter = formatters[p.Format]
|
||||
if p.formatter == nil {
|
||||
p.errorf("unknown output file format %q", p.Format)
|
||||
return
|
||||
}
|
||||
p.startSym = goobj.SymID{Name: p.StartSym}
|
||||
arch, ok := arches[p.GOARCH]
|
||||
if !ok {
|
||||
p.errorf("unknown GOOS %q", p.GOOS)
|
||||
return
|
||||
}
|
||||
p.arch = arch
|
||||
|
||||
p.pkgdir = fmt.Sprintf("%s/pkg/%s_%s", runtime.GOROOT(), p.GOOS, p.GOARCH)
|
||||
}
|
||||
|
||||
// goosFormat records the default format for each known GOOS value.
|
||||
var goosFormat = map[string]string{
|
||||
"darwin": "darwin",
|
||||
}
|
||||
|
||||
// formatters records the format implementation for each known format value.
|
||||
var formatters = map[string]formatter{
|
||||
"darwin": machoFormat{},
|
||||
}
|
||||
|
||||
var arches = map[string]arch{
|
||||
"amd64": {
|
||||
byteorder: binary.LittleEndian,
|
||||
ptrsize: 8,
|
||||
pcquantum: 1,
|
||||
},
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// shiftProg adjusts the addresses in p.
|
||||
// It adds vdelta to all virtual addresses and fdelta to all file offsets.
|
||||
func shiftProg(p *Prog, vdelta Addr, fdelta Addr) {
|
||||
p.Entry += vdelta
|
||||
for _, seg := range p.Segments {
|
||||
seg.FileOffset += fdelta
|
||||
seg.VirtAddr += vdelta
|
||||
for _, sect := range seg.Sections {
|
||||
sect.VirtAddr += vdelta
|
||||
for _, sym := range sect.Syms {
|
||||
sym.Addr += vdelta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// diffProg returns a list of differences between p and q,
|
||||
// assuming p is being checked and q is the correct answer.
|
||||
func diffProg(p, q *Prog) []string {
|
||||
var errors []string
|
||||
if p.UnmappedSize != q.UnmappedSize {
|
||||
errors = append(errors, fmt.Sprintf("p.UnmappedSize = %#x, want %#x", p.UnmappedSize, q.UnmappedSize))
|
||||
}
|
||||
if p.HeaderSize != q.HeaderSize {
|
||||
errors = append(errors, fmt.Sprintf("p.HeaderSize = %#x, want %#x", p.HeaderSize, q.HeaderSize))
|
||||
}
|
||||
if p.Entry != q.Entry {
|
||||
errors = append(errors, fmt.Sprintf("p.Entry = %#x, want %#x", p.Entry, q.Entry))
|
||||
}
|
||||
for i := 0; i < len(p.Segments) || i < len(q.Segments); i++ {
|
||||
if i >= len(p.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p missing segment %q", q.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
if i >= len(q.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p has extra segment %q", p.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
pseg := p.Segments[i]
|
||||
qseg := q.Segments[i]
|
||||
if pseg.Name != qseg.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %d Name = %q, want %q", i, pseg.Name, qseg.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
if pseg.VirtAddr != qseg.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtAddr = %#x, want %#x", pseg.Name, pseg.VirtAddr, qseg.VirtAddr))
|
||||
}
|
||||
if pseg.VirtSize != qseg.VirtSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtSize = %#x, want %#x", pseg.Name, pseg.VirtSize, qseg.VirtSize))
|
||||
}
|
||||
if pseg.FileOffset != qseg.FileOffset {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileOffset = %#x, want %#x", pseg.Name, pseg.FileOffset, qseg.FileOffset))
|
||||
}
|
||||
if pseg.FileSize != qseg.FileSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileSize = %#x, want %#x", pseg.Name, pseg.FileSize, qseg.FileSize))
|
||||
}
|
||||
if len(pseg.Data) != len(qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q len(Data) = %d, want %d", pseg.Name, len(pseg.Data), len(qseg.Data)))
|
||||
} else if !bytes.Equal(pseg.Data, qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q Data mismatch:\n\thave %x\n\twant %x", pseg.Name, pseg.Data, qseg.Data))
|
||||
}
|
||||
|
||||
for j := 0; j < len(pseg.Sections) || j < len(qseg.Sections); j++ {
|
||||
if j >= len(pseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q missing section %q", pseg.Name, qseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
if j >= len(qseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q has extra section %q", pseg.Name, pseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
psect := pseg.Sections[j]
|
||||
qsect := qseg.Sections[j]
|
||||
if psect.Name != qsect.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %q, section %d Name = %q, want %q", pseg.Name, j, psect.Name, qsect.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
|
||||
if psect.VirtAddr != qsect.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q VirtAddr = %#x, want %#x", pseg.Name, psect.Name, psect.VirtAddr, qsect.VirtAddr))
|
||||
}
|
||||
if psect.Size != qsect.Size {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Size = %#x, want %#x", pseg.Name, psect.Name, psect.Size, qsect.Size))
|
||||
}
|
||||
if psect.Align != qsect.Align {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Align = %#x, want %#x", pseg.Name, psect.Name, psect.Align, qsect.Align))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// cloneProg returns a deep copy of p.
|
||||
func cloneProg(p *Prog) *Prog {
|
||||
q := new(Prog)
|
||||
*q = *p
|
||||
q.Segments = make([]*Segment, len(p.Segments))
|
||||
for i, seg := range p.Segments {
|
||||
q.Segments[i] = cloneSegment(seg)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// cloneSegment returns a deep copy of seg.
|
||||
func cloneSegment(seg *Segment) *Segment {
|
||||
t := new(Segment)
|
||||
*t = *seg
|
||||
t.Sections = make([]*Section, len(seg.Sections))
|
||||
for i, sect := range seg.Sections {
|
||||
t.Sections[i] = cloneSection(sect)
|
||||
}
|
||||
t.Data = make([]byte, len(seg.Data))
|
||||
copy(t.Data, seg.Data)
|
||||
return t
|
||||
}
|
||||
|
||||
// cloneSection returns a deep copy of section.
|
||||
func cloneSection(sect *Section) *Section {
|
||||
// At the moment, there's nothing we need to make a deep copy of.
|
||||
t := new(Section)
|
||||
*t = *sect
|
||||
return t
|
||||
}
|
||||
|
||||
const saveMismatch = true
|
||||
|
||||
// checkGolden checks that data matches the named file.
|
||||
// If not, it reports the error to the test.
|
||||
func checkGolden(t *testing.T, data []byte, name string) {
|
||||
golden := mustParseHexdumpFile(t, name)
|
||||
if !bytes.Equal(data, golden) {
|
||||
if saveMismatch {
|
||||
ioutil.WriteFile(name+".raw", data, 0666)
|
||||
ioutil.WriteFile(name+".hex", []byte(hexdump(data)), 0666)
|
||||
}
|
||||
// TODO(rsc): A better diff would be nice, as needed.
|
||||
i := 0
|
||||
for i < len(data) && i < len(golden) && data[i] == golden[i] {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) {
|
||||
t.Errorf("%s: output file shorter than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else if i >= len(golden) {
|
||||
t.Errorf("%s: output file larger than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else {
|
||||
t.Errorf("%s: output file differs at byte %d: have %#02x, want %#02x", name, i, data[i], golden[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of runtime-accessible data structures.
|
||||
// See also debug.go.
|
||||
|
||||
package main
|
||||
|
||||
import "cmd/internal/goobj"
|
||||
|
||||
func (p *Prog) runtime() {
|
||||
p.pclntab()
|
||||
|
||||
// TODO: Implement garbage collection data.
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "runtime.gcdata"},
|
||||
Kind: goobj.SRODATA,
|
||||
},
|
||||
})
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "runtime.gcbss"},
|
||||
Kind: goobj.SRODATA,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Initial scan of packages making up a program.
|
||||
|
||||
// TODO(rsc): Rename goobj.SymID.Version to StaticID to avoid confusion with the ELF meaning of version.
|
||||
// TODO(rsc): Fix file format so that SBSS/SNOPTRBSS with data is listed as SDATA/SNOPTRDATA.
|
||||
// TODO(rsc): Parallelize scan to overlap file i/o where possible.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// scan scans all packages making up the program, starting with package main defined in mainfile.
|
||||
func (p *Prog) scan(mainfile string) {
|
||||
p.initScan()
|
||||
p.scanFile("main", mainfile)
|
||||
if len(p.Missing) > 0 && !p.omitRuntime {
|
||||
p.scanImport("runtime")
|
||||
}
|
||||
|
||||
var missing []string
|
||||
for sym := range p.Missing {
|
||||
if !p.isAuto(sym) {
|
||||
missing = append(missing, sym.String())
|
||||
}
|
||||
}
|
||||
|
||||
if missing != nil {
|
||||
sort.Strings(missing)
|
||||
for _, sym := range missing {
|
||||
p.errorf("undefined: %s", sym)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rsc): Walk import graph to diagnose cycles.
|
||||
}
|
||||
|
||||
// initScan initializes the Prog fields needed by scan.
|
||||
func (p *Prog) initScan() {
|
||||
p.Packages = make(map[string]*Package)
|
||||
p.Syms = make(map[goobj.SymID]*Sym)
|
||||
p.Missing = make(map[goobj.SymID]bool)
|
||||
p.Missing[p.startSym] = true
|
||||
}
|
||||
|
||||
// scanFile reads file to learn about the package with the given import path.
|
||||
func (p *Prog) scanFile(pkgpath string, file string) {
|
||||
pkg := &Package{
|
||||
File: file,
|
||||
}
|
||||
p.Packages[pkgpath] = pkg
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return
|
||||
}
|
||||
gp, err := goobj.Parse(f, pkgpath)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
p.errorf("reading %s: %v", file, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(rsc): Change cmd/internal/goobj to record package name as gp.Name.
|
||||
// TODO(rsc): If pkgpath == "main", check that gp.Name == "main".
|
||||
|
||||
pkg.Package = gp
|
||||
|
||||
for _, gs := range gp.Syms {
|
||||
// TODO(rsc): Fix file format instead of this workaround.
|
||||
if gs.Data.Size > 0 {
|
||||
switch gs.Kind {
|
||||
case goobj.SBSS:
|
||||
gs.Kind = goobj.SDATA
|
||||
case goobj.SNOPTRBSS:
|
||||
gs.Kind = goobj.SNOPTRDATA
|
||||
}
|
||||
}
|
||||
|
||||
if gs.Version != 0 {
|
||||
gs.Version += p.MaxVersion
|
||||
}
|
||||
for i := range gs.Reloc {
|
||||
r := &gs.Reloc[i]
|
||||
if r.Sym.Version != 0 {
|
||||
r.Sym.Version += p.MaxVersion
|
||||
}
|
||||
if p.Syms[r.Sym] == nil {
|
||||
p.Missing[r.Sym] = true
|
||||
}
|
||||
}
|
||||
if gs.Func != nil {
|
||||
for i := range gs.Func.FuncData {
|
||||
fdata := &gs.Func.FuncData[i]
|
||||
if fdata.Sym.Name != "" {
|
||||
if fdata.Sym.Version != 0 {
|
||||
fdata.Sym.Version += p.MaxVersion
|
||||
}
|
||||
if p.Syms[fdata.Sym] == nil {
|
||||
p.Missing[fdata.Sym] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if old := p.Syms[gs.SymID]; old != nil {
|
||||
// Duplicate definition of symbol. Is it okay?
|
||||
// TODO(rsc): Write test for this code.
|
||||
switch {
|
||||
// If both symbols are BSS (no data), take max of sizes
|
||||
// but otherwise ignore second symbol.
|
||||
case old.Data.Size == 0 && gs.Data.Size == 0:
|
||||
if old.Size < gs.Size {
|
||||
old.Size = gs.Size
|
||||
}
|
||||
continue
|
||||
|
||||
// If one is in BSS and one is not, use the one that is not.
|
||||
case old.Data.Size > 0 && gs.Data.Size == 0:
|
||||
continue
|
||||
case gs.Data.Size > 0 && old.Data.Size == 0:
|
||||
break // install gs as new symbol below
|
||||
|
||||
// If either is marked as DupOK, we can keep either one.
|
||||
// Keep the one that we saw first.
|
||||
case old.DupOK || gs.DupOK:
|
||||
continue
|
||||
|
||||
// Otherwise, there's an actual conflict:
|
||||
default:
|
||||
p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data)
|
||||
continue
|
||||
}
|
||||
}
|
||||
s := &Sym{
|
||||
Sym: gs,
|
||||
Package: pkg,
|
||||
}
|
||||
p.addSym(s)
|
||||
delete(p.Missing, gs.SymID)
|
||||
|
||||
if s.Data.Size > int64(s.Size) {
|
||||
p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size)
|
||||
}
|
||||
}
|
||||
p.MaxVersion += pkg.MaxVersion
|
||||
|
||||
for i, pkgpath := range pkg.Imports {
|
||||
// TODO(rsc): Fix file format to drop .a from recorded import path.
|
||||
pkgpath = strings.TrimSuffix(pkgpath, ".a")
|
||||
pkg.Imports[i] = pkgpath
|
||||
|
||||
p.scanImport(pkgpath)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prog) addSym(s *Sym) {
|
||||
pkg := s.Package
|
||||
if pkg == nil {
|
||||
pkg = p.Packages[""]
|
||||
if pkg == nil {
|
||||
pkg = &Package{}
|
||||
p.Packages[""] = pkg
|
||||
}
|
||||
s.Package = pkg
|
||||
}
|
||||
pkg.Syms = append(pkg.Syms, s)
|
||||
p.Syms[s.SymID] = s
|
||||
p.SymOrder = append(p.SymOrder, s)
|
||||
}
|
||||
|
||||
// scanImport finds the object file for the given import path and then scans it.
|
||||
func (p *Prog) scanImport(pkgpath string) {
|
||||
if p.Packages[pkgpath] != nil {
|
||||
return // already loaded
|
||||
}
|
||||
|
||||
// TODO(rsc): Implement correct search to find file.
|
||||
p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a")
|
||||
}
|
||||
15
src/cmd/newlink/testdata/Makefile
vendored
15
src/cmd/newlink/testdata/Makefile
vendored
@@ -1,15 +0,0 @@
|
||||
ALL=\
|
||||
autosection.6\
|
||||
autoweak.6\
|
||||
dead.6\
|
||||
hello.6\
|
||||
layout.6\
|
||||
pclntab.6\
|
||||
|
||||
all: $(ALL)
|
||||
|
||||
%.6: %.s
|
||||
GOARCH=amd64 GOOS=darwin go tool asm -o $*.6 -I $(shell go env GOROOT)/pkg/include -trimpath=$(shell pwd) $*.s
|
||||
|
||||
pclntab.s: genpcln.go
|
||||
go run genpcln.go >pclntab.s
|
||||
BIN
src/cmd/newlink/testdata/autosection.6
vendored
BIN
src/cmd/newlink/testdata/autosection.6
vendored
Binary file not shown.
60
src/cmd/newlink/testdata/autosection.s
vendored
60
src/cmd/newlink/testdata/autosection.s
vendored
@@ -1,60 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section-named symbols.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT start(SB),7,$0
|
||||
MOVQ $autotab(SB),AX
|
||||
MOVQ $autoptr(SB),AX
|
||||
RET
|
||||
|
||||
GLOBL zero(SB), $8
|
||||
|
||||
GLOBL zeronoptr(SB), NOPTR, $16
|
||||
|
||||
// text
|
||||
DATA autotab+0x00(SB)/8, $runtime·text(SB)
|
||||
DATA autotab+0x08(SB)/8, $start(SB)
|
||||
DATA autotab+0x10(SB)/8, $runtime·etext(SB)
|
||||
DATA autotab+0x18(SB)/8, $start+16(SB)
|
||||
|
||||
// data
|
||||
DATA autotab+0x20(SB)/8, $runtime·data(SB)
|
||||
DATA autotab+0x28(SB)/8, $autotab(SB)
|
||||
DATA autotab+0x30(SB)/8, $runtime·edata(SB)
|
||||
DATA autotab+0x38(SB)/8, $nonzero+4(SB)
|
||||
|
||||
// bss
|
||||
DATA autotab+0x40(SB)/8, $runtime·bss(SB)
|
||||
DATA autotab+0x48(SB)/8, $zero(SB)
|
||||
DATA autotab+0x50(SB)/8, $runtime·ebss(SB)
|
||||
DATA autotab+0x58(SB)/8, $zero+8(SB)
|
||||
|
||||
// noptrdata
|
||||
DATA autotab+0x60(SB)/8, $runtime·noptrdata(SB)
|
||||
DATA autotab+0x68(SB)/8, $nonzeronoptr(SB)
|
||||
DATA autotab+0x70(SB)/8, $runtime·enoptrdata(SB)
|
||||
DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB)
|
||||
|
||||
// noptrbss
|
||||
DATA autotab+0x80(SB)/8, $runtime·noptrbss(SB)
|
||||
DATA autotab+0x88(SB)/8, $zeronoptr(SB)
|
||||
DATA autotab+0x90(SB)/8, $runtime·enoptrbss(SB)
|
||||
DATA autotab+0x98(SB)/8, $zeronoptr+16(SB)
|
||||
|
||||
// end
|
||||
DATA autotab+0xa0(SB)/8, $runtime·end(SB)
|
||||
DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB)
|
||||
|
||||
GLOBL autotab(SB), $0xb0
|
||||
|
||||
DATA nonzero(SB)/4, $1
|
||||
GLOBL nonzero(SB), $4
|
||||
|
||||
DATA nonzeronoptr(SB)/8, $2
|
||||
GLOBL nonzeronoptr(SB), NOPTR, $8
|
||||
|
||||
GLOBL autoptr(SB), $0
|
||||
BIN
src/cmd/newlink/testdata/autoweak.6
vendored
BIN
src/cmd/newlink/testdata/autoweak.6
vendored
Binary file not shown.
30
src/cmd/newlink/testdata/autoweak.s
vendored
30
src/cmd/newlink/testdata/autoweak.s
vendored
@@ -1,30 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of go.weak symbols.
|
||||
|
||||
TEXT start(SB),7,$0
|
||||
MOVQ $autotab(SB),AX
|
||||
MOVQ $autoptr(SB),AX
|
||||
RET
|
||||
|
||||
// go.weak.sym should resolve to sym, because sym is in the binary.
|
||||
DATA autotab+0(SB)/8, $go·weak·sym(SB)
|
||||
DATA autotab+8(SB)/8, $sym(SB)
|
||||
|
||||
// go.weak.missingsym should resolve to 0, because missingsym is not in the binary.
|
||||
DATA autotab+16(SB)/8, $go·weak·missingsym(SB)
|
||||
DATA autotab+24(SB)/8, $0
|
||||
|
||||
// go.weak.deadsym should resolve to 0, because deadsym is discarded during dead code removal
|
||||
DATA autotab+32(SB)/8, $go·weak·deadsym(SB)
|
||||
DATA autotab+40(SB)/8, $0
|
||||
|
||||
GLOBL autotab(SB), $48
|
||||
|
||||
GLOBL sym(SB), $1
|
||||
|
||||
GLOBL deadsym(SB), $1
|
||||
|
||||
GLOBL autoptr(SB), $0
|
||||
BIN
src/cmd/newlink/testdata/dead.6
vendored
BIN
src/cmd/newlink/testdata/dead.6
vendored
Binary file not shown.
48
src/cmd/newlink/testdata/dead.s
vendored
48
src/cmd/newlink/testdata/dead.s
vendored
@@ -1,48 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of dead code removal.
|
||||
// Symbols with names beginning with dead_ should be discarded.
|
||||
// Others should be kept.
|
||||
|
||||
TEXT start(SB),7,$0 // start symbol
|
||||
MOVQ $data1<>(SB), AX
|
||||
CALL text1(SB)
|
||||
MOVQ $text2(SB), BX
|
||||
RET
|
||||
|
||||
TEXT text1(SB),7,$0
|
||||
FUNCDATA $1, funcdata+4(SB)
|
||||
RET
|
||||
|
||||
TEXT text2(SB),7,$0
|
||||
MOVQ $runtime·edata(SB),BX
|
||||
RET
|
||||
|
||||
DATA data1<>+0(SB)/8, $data2(SB)
|
||||
DATA data1<>+8(SB)/8, $data3(SB)
|
||||
GLOBL data1<>(SB), $16
|
||||
GLOBL data2(SB), $1
|
||||
GLOBL data3(SB), $1
|
||||
GLOBL funcdata(SB), $8
|
||||
|
||||
TEXT dead_start(SB),7,$0
|
||||
MOVQ $dead_data1(SB), AX
|
||||
CALL dead_text1(SB)
|
||||
MOVQ $dead_text2(SB), BX
|
||||
RET
|
||||
|
||||
TEXT dead_text1(SB),7,$0
|
||||
FUNCDATA $1, dead_funcdata+4(SB)
|
||||
RET
|
||||
|
||||
TEXT dead_text2(SB),7,$0
|
||||
RET
|
||||
|
||||
DATA dead_data1+0(SB)/8, $dead_data2(SB)
|
||||
DATA dead_data1+8(SB)/8, $dead_data3(SB)
|
||||
GLOBL dead_data1(SB), $16
|
||||
GLOBL dead_data2(SB), $1
|
||||
GLOBL dead_data3(SB), $1
|
||||
GLOBL dead_funcdata(SB), $8
|
||||
112
src/cmd/newlink/testdata/genpcln.go
vendored
112
src/cmd/newlink/testdata/genpcln.go
vendored
@@ -1,112 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This program generates a .s file using a pseudorandom
|
||||
// value stream for the runtime function data.
|
||||
// The pclntab test checks that the linked copy
|
||||
// still has the same pseudorandom value stream.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("// generated by genpcln.go; do not edit\n\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
r := rand.New(rand.NewSource(int64(f)))
|
||||
file := "input"
|
||||
line := 1
|
||||
args := r.Intn(100) * 8
|
||||
frame := 32 + r.Intn(32)/8*8
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("TEXT func%d(SB),7,$%d-%d\n", f, frame, args)
|
||||
fmt.Printf("\tFUNCDATA $1, funcdata%d(SB)\n", f)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
size := 200 + r.Intn(100)*8
|
||||
spadj := 0
|
||||
flushed := 0
|
||||
firstpc := 4
|
||||
flush := func(i int) {
|
||||
for i-flushed >= 10 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVQ $0x123456789, AX\n", firstpc+flushed)
|
||||
flushed += 10
|
||||
}
|
||||
for i-flushed >= 5 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVL $0x1234567, AX\n", firstpc+flushed)
|
||||
flushed += 5
|
||||
}
|
||||
for i-flushed > 0 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tBYTE $0\n", firstpc+flushed)
|
||||
flushed++
|
||||
}
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
// Possible SP adjustment.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
if spadj <= -32 || spadj < 32 && r.Intn(2) == 0 {
|
||||
spadj += 8
|
||||
fmt.Printf("/*%#04x*/\tPUSHQ AX\n", firstpc+i)
|
||||
} else {
|
||||
spadj -= 8
|
||||
fmt.Printf("/*%#04x*/\tPOPQ AX\n", firstpc+i)
|
||||
}
|
||||
i += 1
|
||||
flushed = i
|
||||
}
|
||||
|
||||
// Possible PCFile change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
file = fmt.Sprintf("file%d.s", r.Intn(10))
|
||||
line = r.Intn(100) + 1
|
||||
}
|
||||
|
||||
// Possible PCLine change.
|
||||
if r.Intn(10) == 0 {
|
||||
flush(i)
|
||||
line = r.Intn(1000) + 1
|
||||
}
|
||||
|
||||
// Possible PCData $1 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $1, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
|
||||
// Possible PCData $2 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $2, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
}
|
||||
flush(size)
|
||||
for spadj < 0 {
|
||||
fmt.Printf("\tPUSHQ AX\n")
|
||||
spadj += 8
|
||||
}
|
||||
for spadj > 0 {
|
||||
fmt.Printf("\tPOPQ AX\n")
|
||||
spadj -= 8
|
||||
}
|
||||
fmt.Printf("\tRET\n")
|
||||
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("GLOBL funcdata%d(SB), $16\n", f)
|
||||
}
|
||||
|
||||
fmt.Printf("\nTEXT start(SB),7,$0\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
fmt.Printf("\tCALL func%d(SB)\n", f)
|
||||
}
|
||||
fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n")
|
||||
fmt.Printf("\n\tRET\n")
|
||||
}
|
||||
BIN
src/cmd/newlink/testdata/hello.6
vendored
BIN
src/cmd/newlink/testdata/hello.6
vendored
Binary file not shown.
15
src/cmd/newlink/testdata/hello.s
vendored
15
src/cmd/newlink/testdata/hello.s
vendored
@@ -1,15 +0,0 @@
|
||||
TEXT _rt0_go(SB),7,$0
|
||||
MOVL $1, DI
|
||||
MOVL $hello<>(SB), SI
|
||||
MOVL $12, DX
|
||||
MOVL $0x2000004, AX
|
||||
SYSCALL
|
||||
MOVL $0, DI
|
||||
MOVL $0x2000001, AX
|
||||
SYSCALL
|
||||
RET
|
||||
|
||||
DATA hello<>+0(SB)/4, $"hell"
|
||||
DATA hello<>+4(SB)/4, $"o wo"
|
||||
DATA hello<>+8(SB)/4, $"rld\n"
|
||||
GLOBL hello<>(SB), $12
|
||||
BIN
src/cmd/newlink/testdata/layout.6
vendored
BIN
src/cmd/newlink/testdata/layout.6
vendored
Binary file not shown.
29
src/cmd/newlink/testdata/layout.s
vendored
29
src/cmd/newlink/testdata/layout.s
vendored
@@ -1,29 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section assignment in layout.go.
|
||||
// Each symbol should end up in the section named by the symbol name prefix (up to the underscore).
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT text_start(SB),7,$0
|
||||
MOVQ $rodata_sym(SB), AX
|
||||
MOVQ $noptrdata_sym(SB), AX
|
||||
MOVQ $data_sym(SB), AX
|
||||
MOVQ $bss_sym(SB), AX
|
||||
MOVQ $noptrbss_sym(SB), AX
|
||||
RET
|
||||
|
||||
DATA rodata_sym(SB)/4, $1
|
||||
GLOBL rodata_sym(SB), RODATA, $4
|
||||
|
||||
DATA noptrdata_sym(SB)/4, $1
|
||||
GLOBL noptrdata_sym(SB), NOPTR, $4
|
||||
|
||||
DATA data_sym(SB)/4, $1
|
||||
GLOBL data_sym(SB), $4
|
||||
|
||||
GLOBL bss_sym(SB), $4
|
||||
|
||||
GLOBL noptrbss_sym(SB), NOPTR, $4
|
||||
55
src/cmd/newlink/testdata/link.hello.darwin.amd64
vendored
55
src/cmd/newlink/testdata/link.hello.darwin.amd64
vendored
@@ -1,55 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 04 00 00 00 d0 02 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................|
|
||||
00000090 00 00 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................|
|
||||
000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
|
||||
000000e0 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
|
||||
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000120 30 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0 ..............|
|
||||
00000130 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
|
||||
*
|
||||
00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......|
|
||||
00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000170 30 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 |0 ..............|
|
||||
00000180 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
|
||||
*
|
||||
000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
000001b0 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
000001c0 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
000001d0 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
|
||||
000001e0 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
|
||||
000001f0 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000200 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000210 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
*
|
||||
00000230 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
|
||||
00000240 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
|
||||
*
|
||||
000002c0 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
*
|
||||
00001000 bf 01 00 00 00 8d 35 f5 0f 00 00 ba 0c 00 00 00 |......5.........|
|
||||
00001010 b8 04 00 00 02 0f 05 31 ff b8 01 00 00 02 0f 05 |.......1........|
|
||||
00001020 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 fb ff ff ff 00 00 01 08 01 00 00 00 00 00 00 00 |................|
|
||||
00001040 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
|
||||
00001050 30 20 00 00 00 00 00 00 80 00 00 00 00 00 00 00 |0 ..............|
|
||||
00001060 00 20 00 00 00 00 00 00 58 00 00 00 00 00 00 80 |. ......X.......|
|
||||
00001070 08 00 00 00 60 00 00 00 63 00 00 00 66 00 00 00 |....`...c...f...|
|
||||
00001080 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.|
|
||||
00001090 02 30 00 04 30 00 06 05 02 06 02 05 02 05 02 02 |.0..0...........|
|
||||
000010a0 02 02 02 05 02 02 02 10 00 00 00 00 00 00 00 00 |................|
|
||||
000010b0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
24
src/cmd/newlink/testdata/macho.amd64.exit9
vendored
24
src/cmd/newlink/testdata/macho.amd64.exit9
vendored
@@ -1,24 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 98 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
00000090 00 00 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 |. ..............|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000190 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 01 00 00 02 bf 09 00 00 00 0f 05 f4 |.............|
|
||||
0000100d
|
||||
39
src/cmd/newlink/testdata/macho.amd64.hello
vendored
39
src/cmd/newlink/testdata/macho.amd64.hello
vendored
@@ -1,39 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 04 00 00 00 30 02 00 00 01 00 00 00 00 00 00 00 |....0...........|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
00000090 00 00 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000110 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000120 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000130 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
|
||||
00000140 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
|
||||
00000150 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000160 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000170 0c 00 00 00 00 00 00 00 00 20 00 00 06 00 00 00 |......... ......|
|
||||
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000190 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
|
||||
000001a0 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
|
||||
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000220 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 09 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
34
src/cmd/newlink/testdata/macho.amd64.helloro
vendored
34
src/cmd/newlink/testdata/macho.amd64.helloro
vendored
@@ -1,34 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 e8 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 e8 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
00000090 00 00 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
000000a0 07 00 00 00 05 00 00 00 02 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
|
||||
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000120 00 30 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |.0..............|
|
||||
00000130 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000150 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
000001e0 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 00 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
BIN
src/cmd/newlink/testdata/pclntab.6
vendored
BIN
src/cmd/newlink/testdata/pclntab.6
vendored
Binary file not shown.
1751
src/cmd/newlink/testdata/pclntab.s
vendored
1751
src/cmd/newlink/testdata/pclntab.s
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// round returns size rounded up to the next multiple of align;
|
||||
// align must be a power of two.
|
||||
func round(size, align Addr) Addr {
|
||||
return (size + align - 1) &^ (align - 1)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Writing of executable and (for hostlink mode) object files.
|
||||
|
||||
package main
|
||||
|
||||
import "io"
|
||||
|
||||
func (p *Prog) write(w io.Writer) {
|
||||
p.Entry = p.Syms[p.startSym].Addr
|
||||
p.formatter.write(w, p)
|
||||
}
|
||||
@@ -83,6 +83,8 @@ func Scanln(a ...interface{}) (n int, err error) {
|
||||
// the format. It returns the number of items successfully scanned.
|
||||
// If that is less than the number of arguments, err will report why.
|
||||
// Newlines in the input must match newlines in the format.
|
||||
// The one exception: the verb %c always scans the next rune in the
|
||||
// input, even if it is a space (or tab etc.) or newline.
|
||||
func Scanf(format string, a ...interface{}) (n int, err error) {
|
||||
return Fscanf(os.Stdin, format, a...)
|
||||
}
|
||||
@@ -1164,15 +1166,18 @@ func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err erro
|
||||
if !widPresent {
|
||||
s.maxWid = hugeWid
|
||||
}
|
||||
s.SkipSpace()
|
||||
|
||||
c, w := utf8.DecodeRuneInString(format[i:])
|
||||
i += w
|
||||
|
||||
if c != 'c' {
|
||||
s.SkipSpace()
|
||||
}
|
||||
s.argLimit = s.limit
|
||||
if f := s.count + s.maxWid; f < s.argLimit {
|
||||
s.argLimit = f
|
||||
}
|
||||
|
||||
c, w := utf8.DecodeRuneInString(format[i:])
|
||||
i += w
|
||||
|
||||
if numProcessed >= len(a) { // out of operands
|
||||
s.errorString("too few operands for format %" + format[i-w:])
|
||||
break
|
||||
|
||||
@@ -300,10 +300,13 @@ var scanfTests = []ScanfTest{
|
||||
{"%2s", "sssss", &xVal, Xs("ss")},
|
||||
|
||||
// Fixed bugs
|
||||
{"%d\n", "27\n", &intVal, 27}, // ok
|
||||
{"%d\n", "28 \n", &intVal, 28}, // was: "unexpected newline"
|
||||
{"%v", "0", &intVal, 0}, // was: "EOF"; 0 was taken as base prefix and not counted.
|
||||
{"%v", "0", &uintVal, uint(0)}, // was: "EOF"; 0 was taken as base prefix and not counted.
|
||||
{"%d\n", "27\n", &intVal, 27}, // ok
|
||||
{"%d\n", "28 \n", &intVal, 28}, // was: "unexpected newline"
|
||||
{"%v", "0", &intVal, 0}, // was: "EOF"; 0 was taken as base prefix and not counted.
|
||||
{"%v", "0", &uintVal, uint(0)}, // was: "EOF"; 0 was taken as base prefix and not counted.
|
||||
{"%c", " ", &uintVal, uint(' ')}, // %c must accept a blank.
|
||||
{"%c", "\t", &uintVal, uint('\t')}, // %c must accept any space.
|
||||
{"%c", "\n", &uintVal, uint('\n')}, // %c must accept any space.
|
||||
}
|
||||
|
||||
var overflowTests = []ScanTest{
|
||||
|
||||
@@ -310,7 +310,6 @@ loopItems:
|
||||
break
|
||||
}
|
||||
if err == syscall.ERROR_MORE_DATA {
|
||||
println(len(buf), l)
|
||||
// Double buffer size and try again.
|
||||
l = uint32(2 * len(buf))
|
||||
buf = make([]uint16, l)
|
||||
|
||||
@@ -25,6 +25,11 @@ import (
|
||||
|
||||
var emptyParams = make(map[string]string)
|
||||
|
||||
// This constant needs to be at least 76 for this package to work correctly.
|
||||
// This is because \r\n--separator_of_len_70- would fill the buffer and it
|
||||
// wouldn't be safe to consume a single byte from it.
|
||||
const peekBufferSize = 4096
|
||||
|
||||
// A Part represents a single part in a multipart body.
|
||||
type Part struct {
|
||||
// The headers of the body, if any, with the keys canonicalized
|
||||
@@ -91,7 +96,7 @@ func (p *Part) parseContentDisposition() {
|
||||
func NewReader(r io.Reader, boundary string) *Reader {
|
||||
b := []byte("\r\n--" + boundary + "--")
|
||||
return &Reader{
|
||||
bufReader: bufio.NewReader(r),
|
||||
bufReader: bufio.NewReaderSize(r, peekBufferSize),
|
||||
nl: b[:2],
|
||||
nlDashBoundary: b[:len(b)-2],
|
||||
dashBoundaryDash: b[2:],
|
||||
@@ -148,7 +153,7 @@ func (pr partReader) Read(d []byte) (n int, err error) {
|
||||
// the read request. No need to parse more at the moment.
|
||||
return p.buffer.Read(d)
|
||||
}
|
||||
peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
|
||||
peek, err := p.mr.bufReader.Peek(peekBufferSize) // TODO(bradfitz): add buffer size accessor
|
||||
|
||||
// Look for an immediate empty part without a leading \r\n
|
||||
// before the boundary separator. Some MIME code makes empty
|
||||
@@ -229,6 +234,7 @@ func (r *Reader) NextPart() (*Part, error) {
|
||||
expectNewPart := false
|
||||
for {
|
||||
line, err := r.bufReader.ReadSlice('\n')
|
||||
|
||||
if err == io.EOF && r.isFinalBoundary(line) {
|
||||
// If the buffer ends in "--boundary--" without the
|
||||
// trailing "\r\n", ReadSlice will return an error
|
||||
@@ -343,13 +349,17 @@ func (mr *Reader) peekBufferIsEmptyPart(peek []byte) bool {
|
||||
// peekBufferSeparatorIndex returns the index of mr.nlDashBoundary in
|
||||
// peek and whether it is a real boundary (and not a prefix of an
|
||||
// unrelated separator). To be the end, the peek buffer must contain a
|
||||
// newline after the boundary.
|
||||
// newline after the boundary or contain the ending boundary (--separator--).
|
||||
func (mr *Reader) peekBufferSeparatorIndex(peek []byte) (idx int, isEnd bool) {
|
||||
idx = bytes.Index(peek, mr.nlDashBoundary)
|
||||
if idx == -1 {
|
||||
return
|
||||
}
|
||||
|
||||
peek = peek[idx+len(mr.nlDashBoundary):]
|
||||
if len(peek) == 0 || len(peek) == 1 && peek[0] == '-' {
|
||||
return idx, false
|
||||
}
|
||||
if len(peek) > 1 && peek[0] == '-' && peek[1] == '-' {
|
||||
return idx, true
|
||||
}
|
||||
|
||||
@@ -616,6 +616,54 @@ html things
|
||||
},
|
||||
},
|
||||
},
|
||||
// Issue 12662: Check that we don't consume the leading \r if the peekBuffer
|
||||
// ends in '\r\n--separator-'
|
||||
{
|
||||
name: "peek buffer boundary condition",
|
||||
sep: "00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db",
|
||||
in: strings.Replace(`--00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db
|
||||
Content-Disposition: form-data; name="block"; filename="block"
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
`+strings.Repeat("A", peekBufferSize-65)+"\n--00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db--", "\n", "\r\n", -1),
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Content-Type": {`application/octet-stream`}, "Content-Disposition": {`form-data; name="block"; filename="block"`}},
|
||||
strings.Repeat("A", peekBufferSize-65),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Issue 12662: Same test as above with \r\n at the end
|
||||
{
|
||||
name: "peek buffer boundary condition",
|
||||
sep: "00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db",
|
||||
in: strings.Replace(`--00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db
|
||||
Content-Disposition: form-data; name="block"; filename="block"
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
`+strings.Repeat("A", peekBufferSize-65)+"\n--00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db--\n", "\n", "\r\n", -1),
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Content-Type": {`application/octet-stream`}, "Content-Disposition": {`form-data; name="block"; filename="block"`}},
|
||||
strings.Repeat("A", peekBufferSize-65),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Issue 12662v2: We want to make sure that for short buffers that end with
|
||||
// '\r\n--separator-' we always consume at least one (valid) symbol from the
|
||||
// peekBuffer
|
||||
{
|
||||
name: "peek buffer boundary condition",
|
||||
sep: "aaaaaaaaaa00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db",
|
||||
in: strings.Replace(`--aaaaaaaaaa00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db
|
||||
Content-Disposition: form-data; name="block"; filename="block"
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
`+strings.Repeat("A", peekBufferSize)+"\n--aaaaaaaaaa00ffded004d4dd0fdf945fbdef9d9050cfd6a13a821846299b27fc71b9db--", "\n", "\r\n", -1),
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Content-Type": {`application/octet-stream`}, "Content-Disposition": {`form-data; name="block"; filename="block"`}},
|
||||
strings.Repeat("A", peekBufferSize),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
roundTripParseTest(),
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package net
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lsocket -lnsl
|
||||
#cgo LDFLAGS: -lsocket -lnsl -lsendfile
|
||||
#include <netdb.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -105,7 +105,7 @@ type requestCanceler interface {
|
||||
}
|
||||
|
||||
type runOnFirstRead struct {
|
||||
io.Reader
|
||||
io.Reader // optional; nil means empty body
|
||||
|
||||
fn func() // Run before first Read, then set to nil
|
||||
}
|
||||
@@ -115,6 +115,9 @@ func (c *runOnFirstRead) Read(bs []byte) (int, error) {
|
||||
c.fn()
|
||||
c.fn = nil
|
||||
}
|
||||
if c.Reader == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return c.Reader.Read(bs)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -281,3 +282,41 @@ func TestReverseProxyCancellation(t *testing.T) {
|
||||
t.Fatal("DefaultClient.Do() returned nil error")
|
||||
}
|
||||
}
|
||||
|
||||
func req(t *testing.T, v string) *http.Request {
|
||||
req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(v)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
// Issue 12344
|
||||
func TestNilBody(t *testing.T) {
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("hi"))
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
backURL, _ := url.Parse(backend.URL)
|
||||
rp := NewSingleHostReverseProxy(backURL)
|
||||
r := req(t, "GET / HTTP/1.0\r\n\r\n")
|
||||
r.Body = nil // this accidentally worked in Go 1.4 and below, so keep it working
|
||||
rp.ServeHTTP(w, r)
|
||||
}))
|
||||
defer frontend.Close()
|
||||
|
||||
res, err := http.Get(frontend.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
slurp, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(slurp) != "hi" {
|
||||
t.Errorf("Got %q; want %q", slurp, "hi")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func getInterfaceInfos() ([]syscall.InterfaceInfo, error) {
|
||||
return nil, os.NewSyscallError("wsaioctl", err)
|
||||
}
|
||||
iilen := ret / uint32(unsafe.Sizeof(iia[0]))
|
||||
return iia[:iilen-1], nil
|
||||
return iia[:iilen], nil
|
||||
}
|
||||
|
||||
func bytesEqualIP(a []byte, b []int8) bool {
|
||||
|
||||
@@ -123,6 +123,9 @@ func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err erro
|
||||
|
||||
// LookupPort looks up the port for the given network and service.
|
||||
func LookupPort(network, service string) (port int, err error) {
|
||||
if n, i, ok := dtoi(service, 0); ok && i == len(service) {
|
||||
return n, nil
|
||||
}
|
||||
return lookupPort(network, service)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,13 @@ package net
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -163,3 +166,53 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) {
|
||||
t.Fatalf(`"%s" received from recv, but "abc" expected`, s)
|
||||
}
|
||||
}
|
||||
|
||||
func isWindowsXP(t *testing.T) bool {
|
||||
v, err := syscall.GetVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("GetVersion failed: %v", err)
|
||||
}
|
||||
major := byte(v)
|
||||
return major < 6
|
||||
}
|
||||
|
||||
func listInterfacesWithNetsh() ([]string, error) {
|
||||
out, err := exec.Command("netsh", "interface", "ip", "show", "config").CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("netsh failed: %v: %q", err, string(out))
|
||||
}
|
||||
lines := bytes.Split(out, []byte{'\r', '\n'})
|
||||
names := make([]string, 0)
|
||||
for _, line := range lines {
|
||||
f := bytes.Split(line, []byte{'"'})
|
||||
if len(f) == 3 {
|
||||
names = append(names, string(f[1]))
|
||||
}
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func TestInterfaceList(t *testing.T) {
|
||||
if isWindowsXP(t) {
|
||||
t.Skip("Windows XP netsh command does not provide required functionality")
|
||||
}
|
||||
ift, err := Interfaces()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
have := make([]string, 0)
|
||||
for _, ifi := range ift {
|
||||
have = append(have, ifi.Name)
|
||||
}
|
||||
sort.Strings(have)
|
||||
|
||||
want, err := listInterfacesWithNetsh()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sort.Strings(want)
|
||||
|
||||
if strings.Join(want, "/") != strings.Join(have, "/") {
|
||||
t.Fatalf("unexpected interface list %q, want %q", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ var portTests = []struct {
|
||||
{"tcp", "time", 37, true},
|
||||
{"tcp", "domain", 53, true},
|
||||
{"tcp", "finger", 79, true},
|
||||
{"tcp", "42", 42, true},
|
||||
|
||||
{"udp", "echo", 7, true},
|
||||
{"udp", "tftp", 69, true},
|
||||
@@ -36,6 +37,7 @@ var portTests = []struct {
|
||||
{"udp", "ntp", 123, true},
|
||||
{"udp", "snmp", 161, true},
|
||||
{"udp", "syslog", 514, true},
|
||||
{"udp", "42", 42, true},
|
||||
|
||||
{"--badnet--", "zzz", 0, false},
|
||||
{"tcp", "--badport--", 0, false},
|
||||
|
||||
@@ -661,6 +661,8 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20
|
||||
// come in on the m->g0 stack already.
|
||||
get_tls(CX)
|
||||
MOVQ g(CX), R8
|
||||
CMPQ R8, $0
|
||||
JEQ nosave
|
||||
MOVQ g_m(R8), R8
|
||||
MOVQ m_g0(R8), SI
|
||||
MOVQ g(CX), DI
|
||||
@@ -670,11 +672,11 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20
|
||||
CMPQ SI, DI
|
||||
JEQ nosave
|
||||
|
||||
// Switch to system stack.
|
||||
MOVQ m_g0(R8), SI
|
||||
CALL gosave<>(SB)
|
||||
MOVQ SI, g(CX)
|
||||
MOVQ (g_sched+gobuf_sp)(SI), SP
|
||||
nosave:
|
||||
|
||||
// Now on a scheduling stack (a pthread-created stack).
|
||||
// Make sure we have enough room for 4 stack-backed fast-call
|
||||
@@ -700,6 +702,29 @@ nosave:
|
||||
MOVL AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
nosave:
|
||||
// Running on a system stack, perhaps even without a g.
|
||||
// Having no g can happen during thread creation or thread teardown
|
||||
// (see needm/dropm on Solaris, for example).
|
||||
// This code is like the above sequence but without saving/restoring g
|
||||
// and without worrying about the stack moving out from under us
|
||||
// (because we're on a system stack, not a goroutine stack).
|
||||
// The above code could be used directly if already on a system stack,
|
||||
// but then the only path through this code would be a rare case on Solaris.
|
||||
// Using this code for all "already on system stack" calls exercises it more,
|
||||
// which should help keep it correct.
|
||||
SUBQ $64, SP
|
||||
ANDQ $~15, SP
|
||||
MOVQ $0, 48(SP) // where above code stores g, in case someone looks during debugging
|
||||
MOVQ DX, 40(SP) // save original stack pointer
|
||||
MOVQ BX, DI // DI = first argument in AMD64 ABI
|
||||
MOVQ BX, CX // CX = first argument in Win64
|
||||
CALL AX
|
||||
MOVQ 40(SP), SI // restore original stack pointer
|
||||
MOVQ SI, SP
|
||||
MOVL AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
|
||||
// Turn the fn into a Go func (by taking its address) and call
|
||||
// cgocallback_gofunc.
|
||||
|
||||
@@ -599,6 +599,12 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
|
||||
MOVL 0, AX
|
||||
RET
|
||||
|
||||
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
|
||||
// Not implemented.
|
||||
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$0-12
|
||||
MOVL 0, AX
|
||||
RET
|
||||
|
||||
// void setg(G*); set g. for use by needm.
|
||||
// Not implemented.
|
||||
TEXT runtime·setg(SB), NOSPLIT, $0-4
|
||||
@@ -621,15 +627,18 @@ TEXT runtime·memclr(SB),NOSPLIT,$0-8
|
||||
MOVL ptr+0(FP), DI
|
||||
MOVL n+4(FP), CX
|
||||
MOVQ CX, BX
|
||||
ANDQ $7, BX
|
||||
SHRQ $3, CX
|
||||
ANDQ $3, BX
|
||||
SHRQ $2, CX
|
||||
MOVQ $0, AX
|
||||
CLD
|
||||
REP
|
||||
STOSQ
|
||||
STOSL
|
||||
MOVQ BX, CX
|
||||
REP
|
||||
STOSB
|
||||
// Note: we zero only 4 bytes at a time so that the tail is at most
|
||||
// 3 bytes. That guarantees that we aren't zeroing pointers with STOSB.
|
||||
// See issue 13160.
|
||||
RET
|
||||
|
||||
TEXT runtime·getcallerpc(SB),NOSPLIT,$8-12
|
||||
|
||||
@@ -20,7 +20,9 @@ package cgo
|
||||
#cgo !android,linux LDFLAGS: -lpthread
|
||||
#cgo netbsd LDFLAGS: -lpthread
|
||||
#cgo openbsd LDFLAGS: -lpthread
|
||||
#cgo windows LDFLAGS: -lm -mthreads
|
||||
// we must explicitly link msvcrt, because runtime needs ntdll, and ntdll
|
||||
// exports some incompatible libc functions. See golang.org/issue/12030.
|
||||
#cgo windows LDFLAGS: -lmsvcrt -lm -mthreads
|
||||
|
||||
#cgo CFLAGS: -Wall -Werror
|
||||
|
||||
|
||||
@@ -154,3 +154,4 @@ func BenchSetType(n int, x interface{}) {
|
||||
const PtrSize = ptrSize
|
||||
|
||||
var TestingAssertE2I2GC = &testingAssertE2I2GC
|
||||
var TestingAssertE2T2GC = &testingAssertE2T2GC
|
||||
|
||||
@@ -47,6 +47,9 @@ It is a comma-separated list of name=val pairs setting these named variables:
|
||||
that allow the garbage collector to avoid repeating a stack scan during the
|
||||
mark termination phase.
|
||||
|
||||
gcstackbarrierall: setting gcstackbarrierall=1 installs stack barriers
|
||||
in every stack frame, rather than in exponentially-spaced frames.
|
||||
|
||||
gcstoptheworld: setting gcstoptheworld=1 disables concurrent garbage collection,
|
||||
making every garbage collection a stop-the-world event. Setting gcstoptheworld=2
|
||||
also disables concurrent sweeping after the garbage collection finishes.
|
||||
|
||||
@@ -469,3 +469,20 @@ func testAssertVar(x interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAssertE2T2Liveness(t *testing.T) {
|
||||
*runtime.TestingAssertE2T2GC = true
|
||||
defer func() {
|
||||
*runtime.TestingAssertE2T2GC = false
|
||||
}()
|
||||
|
||||
poisonStack()
|
||||
testIfaceEqual(io.EOF)
|
||||
}
|
||||
|
||||
func testIfaceEqual(x interface{}) {
|
||||
if x == "abc" {
|
||||
// Prevent inlining
|
||||
panic("")
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user