]> Cypherpunks repositories - gostls13.git/commitdiff
Fix cgo for GCC 4.4
authorAdam Langley <agl@golang.org>
Mon, 2 Nov 2009 20:02:16 +0000 (12:02 -0800)
committerAdam Langley <agl@golang.org>
Mon, 2 Nov 2009 20:02:16 +0000 (12:02 -0800)
Firstly, with -Werror, GCC switched to printing warnings starting
with "error:". Widening the string matches solves this as the messages
are otherwise unchanged.

Secondly, GCC 4.4 outputs DWARF sections with with NUL bytes in all
the offsets and requires the relocation section for .debug_info to be
processed in order to result in valid DWARF data. Thus we add minimal
handling for relocation sections, which is sufficient for our needs.

BUG=1
Fixes #1.

R=rsc, iant
CC=go-dev
http://go/go-review/1017003

src/cmd/cgo/gcc.go
src/pkg/debug/elf/file.go
src/pkg/debug/elf/file_test.go
src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o [new file with mode: 0644]
src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o [new file with mode: 0644]
src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o [new file with mode: 0644]
src/pkg/encoding/binary/binary.go

index f573b98cb5c6f42ff8a64c64525dbfb959a76c90..b9354cdd6a21685ad0a09ba4645216f12b381316 100644 (file)
@@ -78,9 +78,9 @@ func (p *Prog) loadDebugInfo() {
                switch {
                default:
                        continue;
-               case strings.Index(line, "warning: useless type name in empty declaration") >= 0:
+               case strings.Index(line, ": useless type name in empty declaration") >= 0:
                        what = "type";
-               case strings.Index(line, "warning: statement with no effect") >= 0:
+               case strings.Index(line, ": statement with no effect") >= 0:
                        what = "value";
                case strings.Index(line, "undeclared") >= 0:
                        what = "error";
@@ -114,7 +114,7 @@ func (p *Prog) loadDebugInfo() {
                fatal("gcc failed:\n%s\non input:\n%s", stderr, b.Bytes());
        }
 
-       // Scan DWARF info for  top-level TagVariable entries with AttrName __cgo__i.
+       // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
        types := make([]dwarf.Type, len(names));
        r := d.Reader();
        for {
@@ -198,10 +198,10 @@ func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) {
                machine,
                "-Wall",        // many warnings
                "-Werror",      // warnings are errors
-               "-o"+tmp,       // write object to tmp
-               "-gdwarf-2",    // generate DWARF v2 debugging symbols
+               "-o"+tmp,       // write object to tmp
+               "-gdwarf-2",    // generate DWARF v2 debugging symbols
                "-c",   // do not link
-               "-xc",  // input language is C
+               "-xc",  // input language is C
                "-",    // read input from standard input
        };
        _, stderr, ok := run(stdin, concat(base, p.GccOptions));
index 7b1d784548bed7aae8d42b738f13e1673147f50e..0945eb506fdaef2eef0110a1a4dc1d696c890ae4 100644 (file)
@@ -6,6 +6,7 @@
 package elf
 
 import (
+       "bytes";
        "debug/dwarf";
        "encoding/binary";
        "fmt";
@@ -109,6 +110,13 @@ func (p *Prog) Open() io.ReadSeeker {
        return io.NewSectionReader(p.sr, 0, 1<<63 - 1);
 }
 
+// A Symbol represents an entry in an ELF symbol table section.
+type Symbol struct {
+       Name            uint32;
+       Info, Other     byte;
+       Section         uint32;
+       Value, Size     uint64;
+}
 
 /*
  * ELF reader
@@ -305,6 +313,60 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
        return f, nil;
 }
 
+func (f *File) getSymbols() ([]Symbol, os.Error) {
+       switch f.Class {
+       case ELFCLASS64:
+               return f.getSymbols64();
+       }
+
+       return nil, os.ErrorString("not implemented");
+}
+
+// GetSymbols returns a slice of Symbols from parsing the symbol table.
+func (f *File) getSymbols64() ([]Symbol, os.Error) {
+       var symtabSection *Section;
+       for _, section := range f.Sections {
+               if section.Type == SHT_SYMTAB {
+                       symtabSection = section;
+                       break;
+               }
+       }
+
+       if symtabSection == nil {
+               return nil, os.ErrorString("no symbol section");
+       }
+
+       data, err := symtabSection.Data();
+       if err != nil {
+               return nil, os.ErrorString("cannot load symbol section");
+       }
+       symtab := bytes.NewBuffer(data);
+       if symtab.Len() % Sym64Size != 0 {
+               return nil, os.ErrorString("length of symbol section is not a multiple of Sym64Size");
+       }
+
+       // The first entry is all zeros.
+       var skip [Sym64Size]byte;
+       symtab.Read(skip[0:len(skip)]);
+
+       symbols := make([]Symbol, symtab.Len() / Sym64Size);
+
+       i := 0;
+       var sym Sym64;
+       for symtab.Len() > 0 {
+               binary.Read(symtab, f.ByteOrder, &sym);
+               symbols[i].Name = sym.Name;
+               symbols[i].Info = sym.Info;
+               symbols[i].Other = sym.Other;
+               symbols[i].Section = uint32(sym.Shndx);
+               symbols[i].Value = sym.Value;
+               symbols[i].Size = sym.Size;
+               i++;
+       }
+
+       return symbols, nil;
+}
+
 // getString extracts a string from an ELF string table.
 func getString(section []byte, start int) (string, bool) {
        if start < 0 || start >= len(section) {
@@ -330,6 +392,60 @@ func (f *File) Section(name string) *Section {
        return nil;
 }
 
+// applyRelocations applies relocations to dst. rels is a relocations section
+// in RELA format.
+func (f *File) applyRelocations(dst []byte, rels []byte) os.Error {
+       if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 {
+               return f.applyRelocationsAMD64(dst, rels);
+       }
+
+       return os.ErrorString("not implemented");
+}
+
+func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error {
+       if len(rels) % Sym64Size != 0 {
+               return os.ErrorString("length of relocation section is not a multiple of Sym64Size");
+       }
+
+       symbols, err := f.getSymbols();
+       if err != nil {
+               return err;
+       }
+
+       b := bytes.NewBuffer(rels);
+       var rela Rela64;
+
+       for b.Len() > 0 {
+               binary.Read(b, f.ByteOrder, &rela);
+               symNo := rela.Info >> 32;
+               t := R_X86_64(rela.Info & 0xffff);
+
+               if symNo >= uint64(len(symbols)) {
+                       continue;
+               }
+               sym := &symbols[symNo];
+               if SymType(sym.Info & 0xf) != STT_SECTION {
+                       // We don't handle non-section relocations for now.
+                       continue;
+               }
+
+               switch t {
+               case R_X86_64_64:
+                       if rela.Off + 8 >= uint64(len(dst)) || rela.Addend < 0 {
+                               continue;
+                       }
+                       f.ByteOrder.PutUint64(dst[rela.Off : rela.Off + 8], uint64(rela.Addend));
+               case R_X86_64_32:
+                       if rela.Off + 4 >= uint64(len(dst)) || rela.Addend < 0 {
+                               continue;
+                       }
+                       f.ByteOrder.PutUint32(dst[rela.Off : rela.Off + 4], uint32(rela.Addend));
+               }
+       }
+
+       return nil;
+}
+
 func (f *File) DWARF() (*dwarf.Data, os.Error) {
        // There are many other DWARF sections, but these
        // are the required ones, and the debug/dwarf package
@@ -349,6 +465,20 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) {
                dat[i] = b;
        }
 
+       // If there's a relocation table for .debug_info, we have to process it
+       // now otherwise the data in .debug_info is invalid for x86-64 objects.
+       rela := f.Section(".rela.debug_info");
+       if rela != nil && rela.Type == SHT_RELA && f.Machine == EM_X86_64 {
+               data, err := rela.Data();
+               if err != nil {
+                       return nil, err;
+               }
+               err = f.applyRelocations(dat[1], data);
+               if err != nil {
+                       return nil, err;
+               }
+       }
+
        abbrev, info, str := dat[0], dat[1], dat[2];
        return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str);
 }
index 9b756aea122c0f68f88bfcb827ea54e5d3fe9dff..04c924d12406ea9c3681129b569620ed38c2776c 100644 (file)
@@ -5,6 +5,7 @@
 package elf
 
 import (
+       "debug/dwarf";
        "encoding/binary";
        "reflect";
        "testing";
@@ -127,3 +128,53 @@ func TestOpen(t *testing.T) {
                }
        }
 }
+
+type relocationTest struct {
+       file            string;
+       firstEntry      *dwarf.Entry;
+}
+
+var relocationTests = []relocationTest{
+       relocationTest{
+               "testdata/go-relocation-test-gcc441-x86-64.o",
+               &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}},
+       },
+       relocationTest{
+               "testdata/go-relocation-test-gcc441-x86.o",
+               &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "t.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}},
+       },
+       relocationTest{
+               "testdata/go-relocation-test-gcc424-x86-64.o",
+               &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}},
+       },
+}
+
+func TestDWARFRelocations(t *testing.T) {
+       for i, test := range relocationTests {
+               f, err := Open(test.file);
+               if err != nil {
+                       t.Error(err);
+                       continue;
+               }
+               dwarf, err := f.DWARF();
+               if err != nil {
+                       t.Error(err);
+                       continue;
+               }
+               reader := dwarf.Reader();
+               // Checking only the first entry is sufficient since it has
+               // many different strings. If the relocation had failed, all
+               // the string offsets would be zero and all the strings would
+               // end up being the same.
+               firstEntry, err := reader.Next();
+               if err != nil {
+                       t.Error(err);
+                       continue;
+               }
+
+               if !reflect.DeepEqual(test.firstEntry, firstEntry) {
+                       t.Errorf("#%d: mismatch: got:%#v want:%#v", i, firstEntry, test.firstEntry);
+                       continue;
+               }
+       }
+}
diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o
new file mode 100644 (file)
index 0000000..a7c6d6e
Binary files /dev/null and b/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o differ
diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o
new file mode 100644 (file)
index 0000000..2d37ab6
Binary files /dev/null and b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o differ
diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o
new file mode 100644 (file)
index 0000000..0d59fe3
Binary files /dev/null and b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o differ
index 836a43df0939d1f819a9f53e2079d02f8fd62bc8..c49879c664b85e398ce0ded9783565a6a084fa66 100644 (file)
@@ -19,6 +19,9 @@ type ByteOrder interface {
        Uint16(b []byte) uint16;
        Uint32(b []byte) uint32;
        Uint64(b []byte) uint64;
+       PutUint16([]byte, uint16);
+       PutUint32([]byte, uint32);
+       PutUint64([]byte, uint64);
        String() string;
 }
 
@@ -35,15 +38,38 @@ func (littleEndian) Uint16(b []byte) uint16 {
        return uint16(b[0]) | uint16(b[1])<<8;
 }
 
+func (littleEndian) PutUint16(b []byte, v uint16) {
+       b[0] = byte(v);
+       b[1] = byte(v>>8);
+}
+
 func (littleEndian) Uint32(b []byte) uint32 {
        return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24;
 }
 
+func (littleEndian) PutUint32(b []byte, v uint32) {
+       b[0] = byte(v);
+       b[1] = byte(v>>8);
+       b[2] = byte(v>>16);
+       b[3] = byte(v>>24);
+}
+
 func (littleEndian) Uint64(b []byte) uint64 {
        return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
                uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56;
 }
 
+func (littleEndian) PutUint64(b []byte, v uint64) {
+       b[0] = byte(v);
+       b[1] = byte(v>>8);
+       b[2] = byte(v>>16);
+       b[3] = byte(v>>24);
+       b[4] = byte(v>>32);
+       b[5] = byte(v>>40);
+       b[6] = byte(v>>48);
+       b[7] = byte(v>>56);
+}
+
 func (littleEndian) String() string {
        return "LittleEndian";
 }
@@ -58,15 +84,38 @@ func (bigEndian) Uint16(b []byte) uint16 {
        return uint16(b[1]) | uint16(b[0])<<8;
 }
 
+func (bigEndian) PutUint16(b []byte, v uint16) {
+       b[0] = byte(v>>8);
+       b[1] = byte(v);
+}
+
 func (bigEndian) Uint32(b []byte) uint32 {
        return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24;
 }
 
+func (bigEndian) PutUint32(b []byte, v uint32) {
+       b[0] = byte(v>>24);
+       b[1] = byte(v>>16);
+       b[2] = byte(v>>8);
+       b[3] = byte(v);
+}
+
 func (bigEndian) Uint64(b []byte) uint64 {
        return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
                uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56;
 }
 
+func (bigEndian) PutUint64(b []byte, v uint64) {
+       b[0] = byte(v>>56);
+       b[1] = byte(v>>48);
+       b[2] = byte(v>>40);
+       b[3] = byte(v>>32);
+       b[4] = byte(v>>24);
+       b[5] = byte(v>>16);
+       b[6] = byte(v>>8);
+       b[7] = byte(v);
+}
+
 func (bigEndian) String() string {
        return "BigEndian";
 }