-
Notifications
You must be signed in to change notification settings - Fork 18k
cmd/go: TestNoteReading fails on Solaris with linkmode=external #12178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
CL https://golang.org/cl/13679 mentions this issue. |
Update #11184 (linux/ppc64). Filed #12178 (solaris) for Go 1.6. Change-Id: I9e3a456aaccb49590ad4e14b53ddfefca5b0801c Reviewed-on: https://go-review.googlesource.com/13679 Reviewed-by: Russ Cox <[email protected]>
Debugging this now; short version is .note section is likely in first ~1MB when using external linking on Solaris instead of first 16K due to how Go is linking things in external linking mode. I'll submit a CL once I feel like I have the right fix. The naive fix at the moment is to read the first ~1MB of the file instead of the first ~16K in ReadBuildIDFromBinary, but that's obviously undesirable. |
So the root cause of this is a subtlety in the System V ABI; the ABI specification defines .note sections as "special" sections that are not allocable (SHF_ALLOC):
http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/specialsections.html However, Go relies on the fact that it manually marked the section as allocable, and GNU ld will apply any specified flags to the sections. As a result, on Linux or other platforms where the allocable flag is honoured, the .note.go.buildid section will be placed roughly in the first 16K or so of the file. Solaris ld(1), however, follows the System V ABI specification strictly and ignores any section attributes that the developer specified by "cleaning" the section flags by default to match System V although this can be "overridden" via a mapfile. As such, it seems like there are two possible solutions:
Comments? |
On Thu, Aug 20, 2015 at 3:18 PM, Shawn [email protected] wrote:
Although it's true that the ABI says that sections named ".note" Also, the ABI defines a PT_NOTE segment. Both the GNU linker and gold
This seems like the simplest solution if there is no way to create a Ian |
I think this is another one of those "matter of interpretation due to vague or non-explicit wording in standard" cases. For example, taking other statements in the standard into consideration:
...one could interpret those statements to imply that sections generated by the application, if marked with SHT_NOTE, such as a .note.go.buildid are system sections since they start with a '.' prefix and while the name is not exactly ".note", I could understand why someone would interpret that to mean that any section with type SHT_NOTE should have the attributes listed in the ABI specification. Regardless, our tentative plan for future versions of Solaris is to mimic the GNU linker's behaviour that you describe below and that I also found in an older blog post: http://www.airs.com/blog/archives/45 ...in that we'll change it to honour SHF_ALLOC for those sections.
The Solaris linker will create a PT_NOTE segment regardless of whether SHF_ALLOC is set, it just ignores the SHF_ALLOC flags per the table in the ABI; from the Solaris documentation:
The GNU linker appears to only create a PT_NOTE section though if SHF_ALLOC is set on a SHT_NOTE section which seems potentially at odds again with some readings of the standard. As an example of what happens today with Go's -linkmode=external:
Note the SH_OFFSET of the If we apply the mapfile I showed originally with -linkmode=external:
...but we don't get a PT_NOTE segment because of our override. In talking with the local linker aliens, there doesn't seem to be any way to get the desired result (SHT_NOTE section with SHF_ALLOC set and a corresponding PT_NOTE section). The Solaris linker makes the explicit assumption that note sections were never meant to be loaded into memory; only used for on-disk purposes. |
For the record, the test does not actually pass with mapfile example I showed earlier; I failed to remove the "skip" for LinkExternal on Solaris from note_test.go after doing a "git sync". While the mapfile gets us halfway there, the previous mapfile doesn't ensure that a PT_NOTE segment is created. Anyway, short of changing Go to use something other than a SHT_NOTE section to record the buildid, the only other option I can see is to use a mapfile whenever linking the executables to leave space for a PT_NOTE header, ensure that the .note.go.buildid section stays within the first 16K of the binary, and ensure that the .note.go.buildid section is allocable:
Then, to leverage the above, Go would have to either transform the PT_NULL header into a PT_NOTE header itself or use an 'elfedit' script to do the same. |
So it isn't pretty, but if Go wants to continue using the SHT_NOTE section for .note.go.buildid, the following appears to work:
It accomplishes this by a combination of what I outlined earlier:
The test now passes as expected. However, I feel like this solution is pretty ugly given the amount of additional platform-specific code I had to add to the linking step. |
Huh. It seems very weird to me to have a segment that refers to non-allocated sections. The whole point of segments is so that the dynamic linker can find things in memory at run time. That fails if the segment only points back to the file. Does the Solaris linker set the p_vaddr, p_paddr, and p_memsz fields to zero? |
I think that on Solaris we just shouldn't use a SHT_NOTE section. We really don't care what the type of section is. On GNU/Linux SHT_NOTE is appropriate--it's convenient for readelf and objdump, for example--but if Solaris needs something else, we should use something else. |
The specification seems to strongly imply that non-allocable sections are expected in the segments by calling some segments "loadable" and others not. For example, in the "Program Header" section of the specification (5-1), it states: "Some entries describe process segments; others give supplementary information and do not contribute to the process image." That seems to pretty clearly imply that some process segments found in the program header will not be loaded into memory. Again, another one of the dark corners of the standard I think.
Yes, as I showed earlier:
Which is logical; the specification states that "[p_memsz] gives the number of bytes in the memory image of the segment; it may be zero." That implies that |
That's fine by me; as I said, future versions of Solaris will attempt to more closely mimic the GNU linker's behaviour, but for the foreseeable future, we'll have to pursue another solution for Go. I'll see what I can come up with using a different section type instead of SHT_NOTE. Thanks for your patience. |
OK, fair enough. On GNU/Linux systems, the sense has always been that you can have a PT_LOAD segment, you can have a segment like PT_INTERP or PT_TLS that refers to in-process memory, you can have a segment ilke PT_GNU_STACK that conveys information but does not refer to any contents of the file, but you will never have a program header that refers to file contents not loaded into memory, because such a segment is not useful. The kernel loads the PT_LOAD segments into memory, and passes them to the dynamic linker. We never want the dynamic linker to look at anything other than what the kernel already loaded into memory, so a segment that refers to unloaded memory is useless. But you seem to be right that the ELF ABI permits it. |
CL https://golang.org/cl/14210 mentions this issue. |
CL https://golang.org/cl/17142 mentions this issue. |
TestNoteReading fails on Solaris with linkmode=external due to some assumptions made about how ELF .note sections are written by some linkers. On current versions of Solaris and older derivatives, SHF_ALLOC is intentionally ignored for .note sections unless the .note section is assigned to the text segment via a mapfile. Also, if .note sections are assigned to the text segment, no PT_NOTE program header will be created thwarting Go's attempts at attempting to quickly find the .note. Furthermore, Go assumes that the relevant note segment will be placed early in the file while the Solaris linker currently places the note segment last in the file, additionally thwarting Go's optimisation attempts that read only the first 16KB of the file to find the buildid. The fix is to detect when the note section is outside of the first 16KB of the file and then fallback to additionally reading that section of the file. This way, in future versions of Solaris when this linking behaviour is changed, the fast path will always succeed and we'll only be slower if it fails; likewise, any other linker that does this will also just work. Fixes #12178 Change-Id: I61c1dc3f744ae3ad63938386d2ace8a432c0efe1 Reviewed-on: https://go-review.googlesource.com/14210 Run-TryBot: Aram Hăvărneanu <[email protected]> Reviewed-by: Aram Hăvărneanu <[email protected]> Reviewed-on: https://go-review.googlesource.com/17142 Run-TryBot: Russ Cox <[email protected]> Reviewed-by: Russ Cox <[email protected]>
See http://build.golang.org/log/c7559674bb66a323958ebc8f8b51aaa0908c46f9
Disabling test for Go 1.5.
The text was updated successfully, but these errors were encountered: