cmd/cgo: support indirect macro expansion for string

Current code cannot handle string #define macros if those macros are
defined via other macros. This CL solve the issue.

Updates #18720

Change-Id: Ibed0773d10db3d545bb246b97e81c0d19e3af3d5
Reviewed-on: https://go-review.googlesource.com/41312
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Hiroshi Ioka 2017-04-21 19:18:36 +09:00 committed by Ian Lance Taylor
parent 1b53f15ebb
commit dd61aa55e8
3 changed files with 174 additions and 5 deletions

View file

@ -77,5 +77,6 @@ func TestCheckConst(t *testing.T) { testCheckConst(t) }
func Test17537(t *testing.T) { test17537(t) }
func Test18126(t *testing.T) { test18126(t) }
func Test20369(t *testing.T) { test20369(t) }
func Test18720(t *testing.T) { test18720(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }

View file

@ -0,0 +1,19 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
/*
#define HELLO "hello"
#define WORLD "world"
#define HELLO_WORLD HELLO "\000" WORLD
*/
import "C"
import "testing"
func test18720(t *testing.T) {
if C.HELLO_WORLD != "hello\000world" {
t.Fatalf(`expected "hello\000world", but got %q`, C.HELLO_WORLD)
}
}

View file

@ -507,7 +507,15 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
fmt.Fprintf(&b, "\t1\n")
fmt.Fprintf(&b, "};\n")
d, ints, floats := p.gccDebug(b.Bytes())
// do the same work for strings.
for i, n := range names {
if n.Kind == "sconst" {
fmt.Fprintf(&b, "const char __cgodebug_str__%d[] = %s;\n", i, n.C)
fmt.Fprintf(&b, "const unsigned long long __cgodebug_strlen__%d = sizeof(%s)-1;\n", i, n.C)
}
}
d, ints, floats, strs := p.gccDebug(b.Bytes(), len(names))
// Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
types := make([]dwarf.Type, len(names))
@ -592,6 +600,10 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
if i < len(floats) {
n.Const = fmt.Sprintf("%f", floats[i])
}
case "sconst":
if i < len(strs) {
n.Const = fmt.Sprintf("%q", strs[i])
}
}
}
conv.FinishType(pos)
@ -1282,7 +1294,7 @@ func (p *Package) gccCmd() []string {
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and, if present, debug data block.
func (p *Package) gccDebug(stdin []byte) (d *dwarf.Data, ints []int64, floats []float64) {
func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int64, floats []float64, strs []string) {
runGcc(stdin, p.gccCmd())
isDebugInts := func(s string) bool {
@ -1293,6 +1305,45 @@ func (p *Package) gccDebug(stdin []byte) (d *dwarf.Data, ints []int64, floats []
// Some systems use leading _ to denote non-assembly symbols.
return s == "__cgodebug_floats" || s == "___cgodebug_floats"
}
indexOfDebugStr := func(s string) int {
// Some systems use leading _ to denote non-assembly symbols.
if strings.HasPrefix(s, "___") {
s = s[1:]
}
if strings.HasPrefix(s, "__cgodebug_str__") {
if n, err := strconv.Atoi(s[len("__cgodebug_str__"):]); err == nil {
return n
}
}
return -1
}
indexOfDebugStrlen := func(s string) int {
// Some systems use leading _ to denote non-assembly symbols.
if strings.HasPrefix(s, "___") {
s = s[1:]
}
if strings.HasPrefix(s, "__cgodebug_strlen__") {
if n, err := strconv.Atoi(s[len("__cgodebug_strlen__"):]); err == nil {
return n
}
}
return -1
}
strs = make([]string, nnames)
strdata := make(map[int]string, nnames)
strlens := make(map[int]int, nnames)
buildStrings := func() {
for n, strlen := range strlens {
data := strdata[n]
if len(data) <= strlen {
fatalf("invalid string literal")
}
strs[n] = string(data[:strlen])
}
}
if f, err := macho.Open(gccTmp()); err == nil {
defer f.Close()
@ -1333,10 +1384,43 @@ func (p *Package) gccDebug(stdin []byte) (d *dwarf.Data, ints []int64, floats []
}
}
}
default:
if n := indexOfDebugStr(s.Name); n != -1 {
// Found it. Now find data section.
if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value-sect.Addr:]
strdata[n] = string(data)
}
}
}
break
}
if n := indexOfDebugStrlen(s.Name); n != -1 {
// Found it. Now find data section.
if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value-sect.Addr:]
strlen := bo.Uint64(data[:8])
if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
fatalf("string literal too big")
}
strlens[n] = int(strlen)
}
}
}
break
}
}
}
buildStrings()
}
return d, ints, floats
return d, ints, floats, strs
}
if f, err := elf.Open(gccTmp()); err == nil {
@ -1379,10 +1463,43 @@ func (p *Package) gccDebug(stdin []byte) (d *dwarf.Data, ints []int64, floats []
}
}
}
default:
if n := indexOfDebugStr(s.Name); n != -1 {
// Found it. Now find data section.
if i := int(s.Section); 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value-sect.Addr:]
strdata[n] = string(data)
}
}
}
break
}
if n := indexOfDebugStrlen(s.Name); n != -1 {
// Found it. Now find data section.
if i := int(s.Section); 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value-sect.Addr:]
strlen := bo.Uint64(data[:8])
if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
fatalf("string literal too big")
}
strlens[n] = int(strlen)
}
}
}
break
}
}
}
buildStrings()
}
return d, ints, floats
return d, ints, floats, strs
}
if f, err := pe.Open(gccTmp()); err == nil {
@ -1420,9 +1537,41 @@ func (p *Package) gccDebug(stdin []byte) (d *dwarf.Data, ints []int64, floats []
}
}
}
default:
if n := indexOfDebugStr(s.Name); n != -1 {
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value:]
strdata[n] = string(data)
}
}
}
break
}
if n := indexOfDebugStrlen(s.Name); n != -1 {
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value:]
strlen := bo.Uint64(data[:8])
if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
fatalf("string literal too big")
}
strlens[n] = int(strlen)
}
}
}
break
}
}
}
return d, ints, floats
buildStrings()
return d, ints, floats, strs
}
fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())