Skip to content

Commit

Permalink
Fix cgo for GCC 4.4
Browse files Browse the repository at this point in the history
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
  • Loading branch information
agl committed Nov 2, 2009
1 parent 6358cac commit 72ec930
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 6 deletions.
12 changes: 6 additions & 6 deletions src/cmd/cgo/gcc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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));
Expand Down
130 changes: 130 additions & 0 deletions src/pkg/debug/elf/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package elf

import (
"bytes";
"debug/dwarf";
"encoding/binary";
"fmt";
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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);
}
51 changes: 51 additions & 0 deletions src/pkg/debug/elf/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package elf

import (
"debug/dwarf";
"encoding/binary";
"reflect";
"testing";
Expand Down Expand Up @@ -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;
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
49 changes: 49 additions & 0 deletions src/pkg/encoding/binary/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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";
}
Expand All @@ -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";
}
Expand Down

0 comments on commit 72ec930

Please sign in to comment.