@@ -8,10 +8,12 @@ import (
8
8
"bufio"
9
9
"bytes"
10
10
"debug/elf"
11
+ "encoding/binary"
11
12
"errors"
12
13
"flag"
13
14
"fmt"
14
15
"go/build"
16
+ "io"
15
17
"io/ioutil"
16
18
"log"
17
19
"math/rand"
@@ -176,6 +178,89 @@ func TestShlibnameFiles(t *testing.T) {
176
178
}
177
179
}
178
180
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
+
179
264
func dynStrings (path string , flag elf.DynTag ) []string {
180
265
f , err := elf .Open (path )
181
266
defer f .Close ()
@@ -233,6 +318,97 @@ func TestGOPathShlib(t *testing.T) {
233
318
run (t , "executable linked to GOPATH library" , "./bin/exe" )
234
319
}
235
320
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
+
236
412
// Testing rebuilding of shared libraries when they are stale is a bit more
237
413
// complicated that it seems like it should be. First, we make everything "old": but
238
414
// only a few seconds old, or it might be older than 6g (or the runtime source) and
0 commit comments