From 72ec930fa70c20ce69b21bf32a7916c04c2e9c2f Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 2 Nov 2009 12:02:16 -0800 Subject: [PATCH] Fix cgo for GCC 4.4 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 | 12 +- src/pkg/debug/elf/file.go | 130 ++++++++++++++++++ src/pkg/debug/elf/file_test.go | 51 +++++++ .../go-relocation-test-gcc424-x86-64.o | Bin 0 -> 3088 bytes .../go-relocation-test-gcc441-x86-64.o | Bin 0 -> 2936 bytes .../testdata/go-relocation-test-gcc441-x86.o | Bin 0 -> 1884 bytes src/pkg/encoding/binary/binary.go | 49 +++++++ 7 files changed, 236 insertions(+), 6 deletions(-) create mode 100644 src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o create mode 100644 src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o create mode 100644 src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index f573b98cb5..b9354cdd6a 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -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)); diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index 7b1d784548..0945eb506f 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -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); } diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 9b756aea12..04c924d124 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -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 index 0000000000000000000000000000000000000000..a7c6d6e562c1c70342d9d1cf41194b7ef3fdedf6 GIT binary patch literal 3088 zcmbVO&1(};5TCc3wy9}r+WG}mj8L%^vueqyN*gHJYD*EJAR^S=xJir6rX-tMMX?8w z3JRXYiy)pn>eZ9rMg0prdhrkN?4i!=zG=3TNBtPs_vZcPG_4+RIcERIfdNFg4YbfX1vOuZAPoYD5y#;U-wsPB{y8I z1^Li#g!xLTR2VBbB{&^cSEF2Vuv8h8dL&`Jwo@I;K?rae*A|T7sMN2%UL2#MU?~*r zMjNH+j!!|svzCRifRzE*LQ$aBvprqu=enrFQ9x_Jl0=dsdtdgkm7+rTc2Ycvx_6Z_ zC|l>C_`{I}Ac~cN+QyTn#k1W>v!2pM#ImgOJ}g+vaxBlZm-vKFQ7h{Q;M6uwx7iZT z)k3s3JvB9+8^Nm{{liUz)3{p=T@UB59?wgxh5mX7&Mh}|!SNamRDG{mS#&+G?yrG^ z4dQBOIVjh504vLZzXK@O-6|vg(xP6*39-+1_^vj+0KJhgLE+Zjph8%wR;~KjNKQ}- zeFu-2Z-j2?c3TPl6&S(*x;I(VqD~}nzE5q2W2jJk;`NP8GwdK{8Gi!pbi4l>Pd&nY zm!x9;LJ~CiP8u)@xlaQMb!1W`*EkyGnaKa+o;d1lJi&gPJMf{ik{pUBxPr6HNhW@o z=CvPB(svdOCjO;N9Kk%q--VClcM+30De{^Ii?m1x$1`*T6%+r4=0BqyiN6~kj^}+f z5F@JXFC6M5FaMH1KwlI8JHz=2lKc-4GoAmN2ItV86zTg6jS?gOVSOMzC+Cp9n`kiY z|FdELedsIq{{}Icleo@ney%@ZB+~a4VkZ9YhWn!rx8#3s$cYfFX#SJ5NQmVBfHsMd z`wJbP2#r+f+mERHc90{-e~qGAgN&!Oi7H3Uin5^t4xqjlA9;RqGx5LH@BdM>Cq?on zZAqQ{Qt)#tiLq`QD@OY;orG}H4hc8iJmK`EY8N~Df|zh!EMc-iH&GkjE0B0;19f=2 z*g&1vE;bCdgNWEL(1ssDWFS$u?ccTC{(O~Nzl(I8$*Nq{46=W&3!Hg8jZ+@_4zsLk zMdn!-IOo~WIOWkT79sRIMk4d@e>w8y-X*^r4Jgt-7ols1n+fjLZE2(}X}5^PM7@}J zV~E0$5Itg>Q=1x3iiNYtq}l}$P>=vuTnoUqxc){4KpjKHuEii zt66q~TCneE2Yw6?%cZ9X#}0fJ74I8+QJ-1 zQ$Fg7NbTJNwVPGbBSWcu-0msFXc2{-T+L)ludLCyE2sUm9=IMj?VuI$O_2hBHvs36 z8@S+j?KXH3d7{1j@9#7wpy`Oug%cK;>$pq`n_&h8 zinj$|zN+b>>hWjMKy&l2@icu@Uni+w&FLY{FP&3ck_-$gX)dm{hr(J1O` z#KJipL|LAZDaP+2*K?BT6Wdpq3)6k+MPjNC=_o?#&~q|`ORxU`F+C?mPBOSgN`iFX zEwt$vxqA#seMzDFR?%SUf6CB59jV^`OT_e?RBMmHc~TOj`_es|>d$k1iKp`$!KK&V zM~rfcABrKp<3eWyU@rBAzd_s-|Ct?T{}TU}<25(K~E; zN&1X5^Q?(2$*JrBeEXTiM7E6+OMRGjLb$0z!c8YnIK655$qwHDCY(2`nQY*fSRYPL zLO6}2f67DeVWCSbh`-?UT_O@(^2qmq^2jWcpFF4HPZ*KJk-x}) R!sVv)JUI$Nq6tk-=`Vm&no0lw literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..0d59fe303b6dad25698a1e7c0644d285460797a9 GIT binary patch literal 1884 zcma)7O;1x%5S{y8(Q2VkP@|$rNgzNX578zjfS}edjB#OPLt;o<>{420?)s0Sa@64Q;duHxOU*Fuif7=)%Dn@+ic_NYLA_c2v zXGYD)5fLwt)j>bKm|4oKjS;*C(4Uk{W(9dzvx?y; zQ?`tWf{F}Ua*=^pd!{PJEd7Q0sexJ}5(zBDSvPh6Y2SIUo-gJLK_OU>`KZ1Nq*z?c z&#ad_ji}=SbA^ue7G~o_5tgHv7Z1g}Oq=n+RO&r?Jq?T{DYne@Z2HDKZ^+s~d#@gq z$`Z7rmebp6Md9;Ef+wY@Btf~|h8dPSTbreFxfMQ>pcU@KNUc$ACjFgSBh>7jW+fTg z?UWm(dI+-K@=~kR*dpZkQC@;dvtAEzLqVe%g#iv^t{s&sPu;R`d$ZaCi`&wF&Ev@8 zwc!a_;TudIxn9HsaOUwkDa%fEXU&@i!h8N#-UZ~4PlbG}t)j6N0BSRaI~P706^$(d zIHn-<4{YczH1D4KVSnuF0fw%DDPJ)z(TGK3_uwb_3P;^~^12uBsc4LMm#n*-sCyhS-N!4~x`rIHaPl-_(byjRB;QT&jR505_(@Q{kFdGs zA_ip*PR^NjW{l%gtTk@~;H1rPljePn^EC9DfrO~L4~%yF36!^R3W+@SNBM%0+{dHD zxv)>&2W@?OFl8Tn-)O|5b(1w2I^ZJ#coJ$DgM2(E)=csp;9c?lRg_PC)k!=hKYuGu z-T(Ecue*0yeqTk#^%OMDW_#lIb;SJSNzv!a>51R{GjNzM*>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"; } -- 2.50.0