@@ -8,10 +8,12 @@ import (
88 "bufio"
99 "bytes"
1010 "debug/elf"
11+ "encoding/binary"
1112 "errors"
1213 "flag"
1314 "fmt"
1415 "go/build"
16+ "io"
1517 "io/ioutil"
1618 "log"
1719 "math/rand"
@@ -176,6 +178,89 @@ func TestShlibnameFiles(t *testing.T) {
176178 }
177179}
178180
181+ // Is a given offset into the file contained in a loaded segment?
182+ func isOffsetLoaded (f * elf.File , offset uint64 ) bool {
183+ for _ , prog := range f .Progs {
184+ if prog .Type == elf .PT_LOAD {
185+ if prog .Off <= offset && offset < prog .Off + prog .Filesz {
186+ return true
187+ }
188+ }
189+ }
190+ return false
191+ }
192+
193+ func rnd (v int32 , r int32 ) int32 {
194+ if r <= 0 {
195+ return v
196+ }
197+ v += r - 1
198+ c := v % r
199+ if c < 0 {
200+ c += r
201+ }
202+ v -= c
203+ return v
204+ }
205+
206+ func readwithpad (r io.Reader , sz int32 ) ([]byte , error ) {
207+ data := make ([]byte , rnd (sz , 4 ))
208+ _ , err := io .ReadFull (r , data )
209+ if err != nil {
210+ return nil , err
211+ }
212+ data = data [:sz ]
213+ return data , nil
214+ }
215+
216+ type note struct {
217+ name string
218+ tag int32
219+ desc string
220+ section * elf.Section
221+ }
222+
223+ // Read all notes from f. As ELF section names are not supposed to be special, one
224+ // looks for a particular note by scanning all SHT_NOTE sections looking for a note
225+ // with a particular "name" and "tag".
226+ func readNotes (f * elf.File ) ([]* note , error ) {
227+ var notes []* note
228+ for _ , sect := range f .Sections {
229+ if sect .Type != elf .SHT_NOTE {
230+ continue
231+ }
232+ r := sect .Open ()
233+ for {
234+ var namesize , descsize , tag int32
235+ err := binary .Read (r , f .ByteOrder , & namesize )
236+ if err != nil {
237+ if err == io .EOF {
238+ break
239+ }
240+ return nil , fmt .Errorf ("read namesize failed:" , err )
241+ }
242+ err = binary .Read (r , f .ByteOrder , & descsize )
243+ if err != nil {
244+ return nil , fmt .Errorf ("read descsize failed:" , err )
245+ }
246+ err = binary .Read (r , f .ByteOrder , & tag )
247+ if err != nil {
248+ return nil , fmt .Errorf ("read type failed:" , err )
249+ }
250+ name , err := readwithpad (r , namesize )
251+ if err != nil {
252+ return nil , fmt .Errorf ("read name failed:" , err )
253+ }
254+ desc , err := readwithpad (r , descsize )
255+ if err != nil {
256+ return nil , fmt .Errorf ("read desc failed:" , err )
257+ }
258+ notes = append (notes , & note {name : string (name ), tag : tag , desc : string (desc ), section : sect })
259+ }
260+ }
261+ return notes , nil
262+ }
263+
179264func dynStrings (path string , flag elf.DynTag ) []string {
180265 f , err := elf .Open (path )
181266 defer f .Close ()
@@ -233,6 +318,97 @@ func TestGOPathShlib(t *testing.T) {
233318 run (t , "executable linked to GOPATH library" , "./bin/exe" )
234319}
235320
321+ // The shared library contains a note listing the packages it contains in a section
322+ // that is not mapped into memory.
323+ func testPkgListNote (t * testing.T , f * elf.File , note * note ) {
324+ if note .section .Flags != 0 {
325+ t .Errorf ("package list section has flags %v" , note .section .Flags )
326+ }
327+ if isOffsetLoaded (f , note .section .Offset ) {
328+ t .Errorf ("package list section contained in PT_LOAD segment" )
329+ }
330+ if note .desc != "dep\n " {
331+ t .Errorf ("incorrect package list %q" , note .desc )
332+ }
333+ }
334+
335+ // The shared library contains a note containing the ABI hash that is mapped into
336+ // memory and there is a local symbol called go.link.abihashbytes that points 16
337+ // bytes into it.
338+ func testABIHashNote (t * testing.T , f * elf.File , note * note ) {
339+ if note .section .Flags != elf .SHF_ALLOC {
340+ t .Errorf ("abi hash section has flags %v" , note .section .Flags )
341+ }
342+ if ! isOffsetLoaded (f , note .section .Offset ) {
343+ t .Errorf ("abihash section not contained in PT_LOAD segment" )
344+ }
345+ var hashbytes elf.Symbol
346+ symbols , err := f .Symbols ()
347+ if err != nil {
348+ t .Errorf ("error reading symbols %v" , err )
349+ return
350+ }
351+ for _ , sym := range symbols {
352+ if sym .Name == "go.link.abihashbytes" {
353+ hashbytes = sym
354+ }
355+ }
356+ if hashbytes .Name == "" {
357+ t .Errorf ("no symbol called go.link.abihashbytes" )
358+ return
359+ }
360+ if elf .ST_BIND (hashbytes .Info ) != elf .STB_LOCAL {
361+ t .Errorf ("%s has incorrect binding %v" , hashbytes .Name , elf .ST_BIND (hashbytes .Info ))
362+ }
363+ if f .Sections [hashbytes .Section ] != note .section {
364+ t .Errorf ("%s has incorrect section %v" , hashbytes .Name , f .Sections [hashbytes .Section ].Name )
365+ }
366+ if hashbytes .Value - note .section .Addr != 16 {
367+ t .Errorf ("%s has incorrect offset into section %d" , hashbytes .Name , hashbytes .Value - note .section .Addr )
368+ }
369+ }
370+
371+ // The shared library contains notes with defined contents; see above.
372+ func TestNotes (t * testing.T ) {
373+ goCmd (t , "install" , "-buildmode=shared" , "-linkshared" , "dep" )
374+ f , err := elf .Open (filepath .Join (gopathInstallDir , "libdep.so" ))
375+ if err != nil {
376+ t .Fatal (err )
377+ }
378+ defer f .Close ()
379+ notes , err := readNotes (f )
380+ if err != nil {
381+ t .Fatal (err )
382+ }
383+ pkgListNoteFound := false
384+ abiHashNoteFound := false
385+ for _ , note := range notes {
386+ if note .name != "GO\x00 \x00 " {
387+ continue
388+ }
389+ switch note .tag {
390+ case 1 : // ELF_NOTE_GOPKGLIST_TAG
391+ if pkgListNoteFound {
392+ t .Error ("multiple package list notes" )
393+ }
394+ testPkgListNote (t , f , note )
395+ pkgListNoteFound = true
396+ case 2 : // ELF_NOTE_GOABIHASH_TAG
397+ if abiHashNoteFound {
398+ t .Error ("multiple abi hash notes" )
399+ }
400+ testABIHashNote (t , f , note )
401+ abiHashNoteFound = true
402+ }
403+ }
404+ if ! pkgListNoteFound {
405+ t .Error ("package list note not found" )
406+ }
407+ if ! abiHashNoteFound {
408+ t .Error ("abi hash note not found" )
409+ }
410+ }
411+
236412// Testing rebuilding of shared libraries when they are stale is a bit more
237413// complicated that it seems like it should be. First, we make everything "old": but
238414// only a few seconds old, or it might be older than 6g (or the runtime source) and
0 commit comments