@@ -10,12 +10,14 @@ import (
1010 "debug/elf"
1111 "flag"
1212 "fmt"
13+ "io"
1314 "log"
1415 "os"
1516 "os/exec"
1617 "path/filepath"
1718 "regexp"
1819 "runtime"
20+ "strconv"
1921 "strings"
2022 "syscall"
2123 "testing"
@@ -263,6 +265,173 @@ func checkLineComments(t *testing.T, hdrname string) {
263265 }
264266}
265267
268+ // checkArchive verifies that the created library looks OK.
269+ // We just check a couple of things now, we can add more checks as needed.
270+ func checkArchive (t * testing.T , arname string ) {
271+ t .Helper ()
272+
273+ switch GOOS {
274+ case "aix" , "darwin" , "ios" , "windows" :
275+ // We don't have any checks for non-ELF libraries yet.
276+ if _ , err := os .Stat (arname ); err != nil {
277+ t .Errorf ("archive %s does not exist: %v" , arname , err )
278+ }
279+ default :
280+ checkELFArchive (t , arname )
281+ }
282+ }
283+
284+ // checkELFArchive checks an ELF archive.
285+ func checkELFArchive (t * testing.T , arname string ) {
286+ t .Helper ()
287+
288+ f , err := os .Open (arname )
289+ if err != nil {
290+ t .Errorf ("archive %s does not exist: %v" , arname , err )
291+ return
292+ }
293+ defer f .Close ()
294+
295+ // TODO(iant): put these in a shared package? But where?
296+ const (
297+ magic = "!<arch>\n "
298+ fmag = "`\n "
299+
300+ namelen = 16
301+ datelen = 12
302+ uidlen = 6
303+ gidlen = 6
304+ modelen = 8
305+ sizelen = 10
306+ fmaglen = 2
307+ hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen
308+ )
309+
310+ type arhdr struct {
311+ name string
312+ date string
313+ uid string
314+ gid string
315+ mode string
316+ size string
317+ fmag string
318+ }
319+
320+ var magbuf [len (magic )]byte
321+ if _ , err := io .ReadFull (f , magbuf [:]); err != nil {
322+ t .Errorf ("%s: archive too short" , arname )
323+ return
324+ }
325+ if string (magbuf [:]) != magic {
326+ t .Errorf ("%s: incorrect archive magic string %q" , arname , magbuf )
327+ }
328+
329+ off := int64 (len (magic ))
330+ for {
331+ if off & 1 != 0 {
332+ var b [1 ]byte
333+ if _ , err := f .Read (b [:]); err != nil {
334+ if err == io .EOF {
335+ break
336+ }
337+ t .Errorf ("%s: error skipping alignment byte at %d: %v" , arname , off , err )
338+ }
339+ off ++
340+ }
341+
342+ var hdrbuf [hdrlen ]byte
343+ if _ , err := io .ReadFull (f , hdrbuf [:]); err != nil {
344+ if err == io .EOF {
345+ break
346+ }
347+ t .Errorf ("%s: error reading archive header at %d: %v" , arname , off , err )
348+ return
349+ }
350+
351+ var hdr arhdr
352+ hdrslice := hdrbuf [:]
353+ set := func (len int , ps * string ) {
354+ * ps = string (bytes .TrimSpace (hdrslice [:len ]))
355+ hdrslice = hdrslice [len :]
356+ }
357+ set (namelen , & hdr .name )
358+ set (datelen , & hdr .date )
359+ set (uidlen , & hdr .uid )
360+ set (gidlen , & hdr .gid )
361+ set (modelen , & hdr .mode )
362+ set (sizelen , & hdr .size )
363+ hdr .fmag = string (hdrslice [:fmaglen ])
364+ hdrslice = hdrslice [fmaglen :]
365+ if len (hdrslice ) != 0 {
366+ t .Fatalf ("internal error: len(hdrslice) == %d" , len (hdrslice ))
367+ }
368+
369+ if hdr .fmag != fmag {
370+ t .Errorf ("%s: invalid fmagic value %q at %d" , arname , hdr .fmag , off )
371+ return
372+ }
373+
374+ size , err := strconv .ParseInt (hdr .size , 10 , 64 )
375+ if err != nil {
376+ t .Errorf ("%s: error parsing size %q at %d: %v" , arname , hdr .size , off , err )
377+ return
378+ }
379+
380+ off += hdrlen
381+
382+ switch hdr .name {
383+ case "__.SYMDEF" , "/" , "/SYM64/" :
384+ // The archive symbol map.
385+ case "//" , "ARFILENAMES/" :
386+ // The extended name table.
387+ default :
388+ // This should be an ELF object.
389+ checkELFArchiveObject (t , arname , off , io .NewSectionReader (f , off , size ))
390+ }
391+
392+ off += size
393+ if _ , err := f .Seek (off , os .SEEK_SET ); err != nil {
394+ t .Errorf ("%s: failed to seek to %d: %v" , arname , off , err )
395+ }
396+ }
397+ }
398+
399+ // checkELFArchiveObject checks an object in an ELF archive.
400+ func checkELFArchiveObject (t * testing.T , arname string , off int64 , obj io.ReaderAt ) {
401+ t .Helper ()
402+
403+ ef , err := elf .NewFile (obj )
404+ if err != nil {
405+ t .Errorf ("%s: failed to open ELF file at %d: %v" , arname , off , err )
406+ return
407+ }
408+ defer ef .Close ()
409+
410+ // Verify section types.
411+ for _ , sec := range ef .Sections {
412+ want := elf .SHT_NULL
413+ switch sec .Name {
414+ case ".text" , ".data" :
415+ want = elf .SHT_PROGBITS
416+ case ".bss" :
417+ want = elf .SHT_NOBITS
418+ case ".symtab" :
419+ want = elf .SHT_SYMTAB
420+ case ".strtab" :
421+ want = elf .SHT_STRTAB
422+ case ".init_array" :
423+ want = elf .SHT_INIT_ARRAY
424+ case ".fini_array" :
425+ want = elf .SHT_FINI_ARRAY
426+ case ".preinit_array" :
427+ want = elf .SHT_PREINIT_ARRAY
428+ }
429+ if want != elf .SHT_NULL && sec .Type != want {
430+ t .Errorf ("%s: incorrect section type in elf file at %d for section %q: got %v want %v" , arname , off , sec .Name , sec .Type , want )
431+ }
432+ }
433+ }
434+
266435func TestInstall (t * testing.T ) {
267436 if ! testWork {
268437 defer os .RemoveAll (filepath .Join (GOPATH , "pkg" ))
@@ -321,6 +490,7 @@ func TestEarlySignalHandler(t *testing.T) {
321490 t .Fatal (err )
322491 }
323492 checkLineComments (t , "libgo2.h" )
493+ checkArchive (t , "libgo2.a" )
324494
325495 ccArgs := append (cc , "-o" , "testp" + exeSuffix , "main2.c" , "libgo2.a" )
326496 if runtime .Compiler == "gccgo" {
@@ -361,6 +531,7 @@ func TestSignalForwarding(t *testing.T) {
361531 t .Fatal (err )
362532 }
363533 checkLineComments (t , "libgo2.h" )
534+ checkArchive (t , "libgo2.a" )
364535
365536 ccArgs := append (cc , "-o" , "testp" + exeSuffix , "main5.c" , "libgo2.a" )
366537 if runtime .Compiler == "gccgo" {
@@ -411,6 +582,7 @@ func TestSignalForwardingExternal(t *testing.T) {
411582 t .Fatal (err )
412583 }
413584 checkLineComments (t , "libgo2.h" )
585+ checkArchive (t , "libgo2.a" )
414586
415587 ccArgs := append (cc , "-o" , "testp" + exeSuffix , "main5.c" , "libgo2.a" )
416588 if runtime .Compiler == "gccgo" {
@@ -528,6 +700,7 @@ func TestOsSignal(t *testing.T) {
528700 t .Fatal (err )
529701 }
530702 checkLineComments (t , "libgo3.h" )
703+ checkArchive (t , "libgo3.a" )
531704
532705 ccArgs := append (cc , "-o" , "testp" + exeSuffix , "main3.c" , "libgo3.a" )
533706 if runtime .Compiler == "gccgo" {
@@ -565,6 +738,7 @@ func TestSigaltstack(t *testing.T) {
565738 t .Fatal (err )
566739 }
567740 checkLineComments (t , "libgo4.h" )
741+ checkArchive (t , "libgo4.a" )
568742
569743 ccArgs := append (cc , "-o" , "testp" + exeSuffix , "main4.c" , "libgo4.a" )
570744 if runtime .Compiler == "gccgo" {
@@ -752,6 +926,7 @@ func TestSIGPROF(t *testing.T) {
752926 t .Fatal (err )
753927 }
754928 checkLineComments (t , "libgo6.h" )
929+ checkArchive (t , "libgo6.a" )
755930
756931 ccArgs := append (cc , "-o" , "testp6" + exeSuffix , "main6.c" , "libgo6.a" )
757932 if runtime .Compiler == "gccgo" {
@@ -795,6 +970,7 @@ func TestCompileWithoutShared(t *testing.T) {
795970 t .Fatal (err )
796971 }
797972 checkLineComments (t , "libgo2.h" )
973+ checkArchive (t , "libgo2.a" )
798974
799975 exe := "./testnoshared" + exeSuffix
800976
@@ -899,6 +1075,7 @@ func TestManyCalls(t *testing.T) {
8991075 t .Fatal (err )
9001076 }
9011077 checkLineComments (t , "libgo7.h" )
1078+ checkArchive (t , "libgo7.a" )
9021079
9031080 ccArgs := append (cc , "-o" , "testp7" + exeSuffix , "main7.c" , "libgo7.a" )
9041081 if runtime .Compiler == "gccgo" {
0 commit comments