Skip to content

Commit 2870259

Browse files
committed
cmd/compile,reflect: allow longer type names
Encode the length of type names and tags in a varint encoding instead of a fixed 2-byte encoding. This allows lengths longer than 65535 (which can happen for large unnamed structs). Removed the alignment check for #14962, it isn't relevant any more since we're no longer reading pointers directly out of this data (it is encoded as an offset which is copied out bytewise). Fixes #44155 Update #14962 Change-Id: I6084f6027e5955dc16777c87b0dd5ea2baa49629 Reviewed-on: https://go-review.googlesource.com/c/go/+/318249 Trust: Keith Randall <khr@golang.org> Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent c14ecac commit 2870259

File tree

7 files changed

+143
-133
lines changed

7 files changed

+143
-133
lines changed

src/cmd/compile/internal/reflectdata/reflect.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package reflectdata
66

77
import (
8+
"encoding/binary"
89
"fmt"
910
"internal/buildcfg"
1011
"os"
@@ -473,36 +474,38 @@ func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int {
473474

474475
// dnameData writes the contents of a reflect.name into s at offset ot.
475476
func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int {
476-
if len(name) > 1<<16-1 {
477-
base.Fatalf("name too long: %s", name)
477+
if len(name) >= 1<<29 {
478+
base.Fatalf("name too long: %d %s...", len(name), name[:1024])
478479
}
479-
if len(tag) > 1<<16-1 {
480-
base.Fatalf("tag too long: %s", tag)
480+
if len(tag) >= 1<<29 {
481+
base.Fatalf("tag too long: %d %s...", len(tag), tag[:1024])
481482
}
483+
var nameLen [binary.MaxVarintLen64]byte
484+
nameLenLen := binary.PutUvarint(nameLen[:], uint64(len(name)))
485+
var tagLen [binary.MaxVarintLen64]byte
486+
tagLenLen := binary.PutUvarint(tagLen[:], uint64(len(tag)))
482487

483488
// Encode name and tag. See reflect/type.go for details.
484489
var bits byte
485-
l := 1 + 2 + len(name)
490+
l := 1 + nameLenLen + len(name)
486491
if exported {
487492
bits |= 1 << 0
488493
}
489494
if len(tag) > 0 {
490-
l += 2 + len(tag)
495+
l += tagLenLen + len(tag)
491496
bits |= 1 << 1
492497
}
493498
if pkg != nil {
494499
bits |= 1 << 2
495500
}
496501
b := make([]byte, l)
497502
b[0] = bits
498-
b[1] = uint8(len(name) >> 8)
499-
b[2] = uint8(len(name))
500-
copy(b[3:], name)
503+
copy(b[1:], nameLen[:nameLenLen])
504+
copy(b[1+nameLenLen:], name)
501505
if len(tag) > 0 {
502-
tb := b[3+len(name):]
503-
tb[0] = uint8(len(tag) >> 8)
504-
tb[1] = uint8(len(tag))
505-
copy(tb[2:], tag)
506+
tb := b[1+nameLenLen+len(name):]
507+
copy(tb, tagLen[:tagLenLen])
508+
copy(tb[tagLenLen:], tag)
506509
}
507510

508511
ot = int(s.WriteBytes(base.Ctxt, int64(ot), b))

src/cmd/link/internal/ld/decodesym.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"cmd/link/internal/loader"
1111
"cmd/link/internal/sym"
1212
"debug/elf"
13+
"encoding/binary"
1314
"log"
1415
)
1516

@@ -126,8 +127,8 @@ func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs
126127
}
127128

128129
data := ldr.Data(r)
129-
namelen := int(uint16(data[1])<<8 | uint16(data[2]))
130-
return string(data[3 : 3+namelen])
130+
nameLen, nameLenLen := binary.Uvarint(data[1:])
131+
return string(data[1+nameLenLen : 1+nameLenLen+int(nameLen)])
131132
}
132133

133134
func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {

src/internal/reflectlite/all_test.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -982,19 +982,6 @@ func TestNames(t *testing.T) {
982982
}
983983
}
984984

985-
type embed struct {
986-
EmbedWithUnexpMeth
987-
}
988-
989-
func TestNameBytesAreAligned(t *testing.T) {
990-
typ := TypeOf(embed{})
991-
b := FirstMethodNameBytes(typ)
992-
v := uintptr(unsafe.Pointer(b))
993-
if v%unsafe.Alignof((*byte)(nil)) != 0 {
994-
t.Errorf("reflect.name.bytes pointer is not aligned: %x", v)
995-
}
996-
}
997-
998985
// TestUnaddressableField tests that the reflect package will not allow
999986
// a type from another package to be used as a named type with an
1000987
// unexported field.

src/internal/reflectlite/type.go

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -321,49 +321,55 @@ func (n name) isExported() bool {
321321
return (*n.bytes)&(1<<0) != 0
322322
}
323323

324-
func (n name) nameLen() int {
325-
return int(uint16(*n.data(1, "name len field"))<<8 | uint16(*n.data(2, "name len field")))
326-
}
327-
328-
func (n name) tagLen() int {
329-
if *n.data(0, "name flag field")&(1<<1) == 0 {
330-
return 0
324+
func (n name) hasTag() bool {
325+
return (*n.bytes)&(1<<1) != 0
326+
}
327+
328+
// readVarint parses a varint as encoded by encoding/binary.
329+
// It returns the number of encoded bytes and the encoded value.
330+
func (n name) readVarint(off int) (int, int) {
331+
v := 0
332+
for i := 0; ; i++ {
333+
x := *n.data(off+i, "read varint")
334+
v += int(x&0x7f) << (7 * i)
335+
if x&0x80 == 0 {
336+
return i + 1, v
337+
}
331338
}
332-
off := 3 + n.nameLen()
333-
return int(uint16(*n.data(off, "name taglen field"))<<8 | uint16(*n.data(off+1, "name taglen field")))
334339
}
335340

336341
func (n name) name() (s string) {
337342
if n.bytes == nil {
338343
return
339344
}
340-
b := (*[4]byte)(unsafe.Pointer(n.bytes))
341-
345+
i, l := n.readVarint(1)
342346
hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
343-
hdr.Data = unsafe.Pointer(&b[3])
344-
hdr.Len = int(b[1])<<8 | int(b[2])
345-
return s
347+
hdr.Data = unsafe.Pointer(n.data(1+i, "non-empty string"))
348+
hdr.Len = l
349+
return
346350
}
347351

348352
func (n name) tag() (s string) {
349-
tl := n.tagLen()
350-
if tl == 0 {
353+
if !n.hasTag() {
351354
return ""
352355
}
353-
nl := n.nameLen()
356+
i, l := n.readVarint(1)
357+
i2, l2 := n.readVarint(1 + i + l)
354358
hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
355-
hdr.Data = unsafe.Pointer(n.data(3+nl+2, "non-empty string"))
356-
hdr.Len = tl
357-
return s
359+
hdr.Data = unsafe.Pointer(n.data(1+i+l+i2, "non-empty string"))
360+
hdr.Len = l2
361+
return
358362
}
359363

360364
func (n name) pkgPath() string {
361365
if n.bytes == nil || *n.data(0, "name flag field")&(1<<2) == 0 {
362366
return ""
363367
}
364-
off := 3 + n.nameLen()
365-
if tl := n.tagLen(); tl > 0 {
366-
off += 2 + tl
368+
i, l := n.readVarint(1)
369+
off := 1 + i + l
370+
if n.hasTag() {
371+
i2, l2 := n.readVarint(off)
372+
off += i2 + l2
367373
}
368374
var nameOff int32
369375
// Note that this field may not be aligned in memory,

src/reflect/all_test.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6942,19 +6942,6 @@ func TestExported(t *testing.T) {
69426942
}
69436943
}
69446944

6945-
type embed struct {
6946-
EmbedWithUnexpMeth
6947-
}
6948-
6949-
func TestNameBytesAreAligned(t *testing.T) {
6950-
typ := TypeOf(embed{})
6951-
b := FirstMethodNameBytes(typ)
6952-
v := uintptr(unsafe.Pointer(b))
6953-
if v%unsafe.Alignof((*byte)(nil)) != 0 {
6954-
t.Errorf("reflect.name.bytes pointer is not aligned: %x", v)
6955-
}
6956-
}
6957-
69586945
func TestTypeStrings(t *testing.T) {
69596946
type stringTest struct {
69606947
typ Type

src/reflect/type.go

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -450,21 +450,25 @@ type structType struct {
450450
// 1<<1 tag data follows the name
451451
// 1<<2 pkgPath nameOff follows the name and tag
452452
//
453-
// The next two bytes are the data length:
453+
// Following that, there is a varint-encoded length of the name,
454+
// followed by the name itself.
454455
//
455-
// l := uint16(data[1])<<8 | uint16(data[2])
456-
//
457-
// Bytes [3:3+l] are the string data.
458-
//
459-
// If tag data follows then bytes 3+l and 3+l+1 are the tag length,
460-
// with the data following.
456+
// If tag data is present, it also has a varint-encoded length
457+
// followed by the tag itself.
461458
//
462459
// If the import path follows, then 4 bytes at the end of
463460
// the data form a nameOff. The import path is only set for concrete
464461
// methods that are defined in a different package than their type.
465462
//
466463
// If a name starts with "*", then the exported bit represents
467464
// whether the pointed to type is exported.
465+
//
466+
// Note: this encoding must match here and in:
467+
// cmd/compile/internal/reflectdata/reflect.go
468+
// runtime/type.go
469+
// internal/reflectlite/type.go
470+
// cmd/link/internal/ld/decodesym.go
471+
468472
type name struct {
469473
bytes *byte
470474
}
@@ -477,49 +481,70 @@ func (n name) isExported() bool {
477481
return (*n.bytes)&(1<<0) != 0
478482
}
479483

480-
func (n name) nameLen() int {
481-
return int(uint16(*n.data(1, "name len field"))<<8 | uint16(*n.data(2, "name len field")))
484+
func (n name) hasTag() bool {
485+
return (*n.bytes)&(1<<1) != 0
486+
}
487+
488+
// readVarint parses a varint as encoded by encoding/binary.
489+
// It returns the number of encoded bytes and the encoded value.
490+
func (n name) readVarint(off int) (int, int) {
491+
v := 0
492+
for i := 0; ; i++ {
493+
x := *n.data(off+i, "read varint")
494+
v += int(x&0x7f) << (7 * i)
495+
if x&0x80 == 0 {
496+
return i + 1, v
497+
}
498+
}
482499
}
483500

484-
func (n name) tagLen() int {
485-
if *n.data(0, "name flag field")&(1<<1) == 0 {
486-
return 0
501+
// writeVarint writes n to buf in varint form. Returns the
502+
// number of bytes written. n must be nonnegative.
503+
// Writes at most 10 bytes.
504+
func writeVarint(buf []byte, n int) int {
505+
for i := 0; ; i++ {
506+
b := byte(n & 0x7f)
507+
n >>= 7
508+
if n == 0 {
509+
buf[i] = b
510+
return i + 1
511+
}
512+
buf[i] = b | 0x80
487513
}
488-
off := 3 + n.nameLen()
489-
return int(uint16(*n.data(off, "name taglen field"))<<8 | uint16(*n.data(off+1, "name taglen field")))
490514
}
491515

492516
func (n name) name() (s string) {
493517
if n.bytes == nil {
494518
return
495519
}
496-
b := (*[4]byte)(unsafe.Pointer(n.bytes))
497-
520+
i, l := n.readVarint(1)
498521
hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
499-
hdr.Data = unsafe.Pointer(&b[3])
500-
hdr.Len = int(b[1])<<8 | int(b[2])
501-
return s
522+
hdr.Data = unsafe.Pointer(n.data(1+i, "non-empty string"))
523+
hdr.Len = l
524+
return
502525
}
503526

504527
func (n name) tag() (s string) {
505-
tl := n.tagLen()
506-
if tl == 0 {
528+
if !n.hasTag() {
507529
return ""
508530
}
509-
nl := n.nameLen()
531+
i, l := n.readVarint(1)
532+
i2, l2 := n.readVarint(1 + i + l)
510533
hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
511-
hdr.Data = unsafe.Pointer(n.data(3+nl+2, "non-empty string"))
512-
hdr.Len = tl
513-
return s
534+
hdr.Data = unsafe.Pointer(n.data(1+i+l+i2, "non-empty string"))
535+
hdr.Len = l2
536+
return
514537
}
515538

516539
func (n name) pkgPath() string {
517540
if n.bytes == nil || *n.data(0, "name flag field")&(1<<2) == 0 {
518541
return ""
519542
}
520-
off := 3 + n.nameLen()
521-
if tl := n.tagLen(); tl > 0 {
522-
off += 2 + tl
543+
i, l := n.readVarint(1)
544+
off := 1 + i + l
545+
if n.hasTag() {
546+
i2, l2 := n.readVarint(off)
547+
off += i2 + l2
523548
}
524549
var nameOff int32
525550
// Note that this field may not be aligned in memory,
@@ -530,33 +555,35 @@ func (n name) pkgPath() string {
530555
}
531556

532557
func newName(n, tag string, exported bool) name {
533-
if len(n) > 1<<16-1 {
534-
panic("reflect.nameFrom: name too long: " + n)
558+
if len(n) >= 1<<29 {
559+
panic("reflect.nameFrom: name too long: " + n[:1024] + "...")
535560
}
536-
if len(tag) > 1<<16-1 {
537-
panic("reflect.nameFrom: tag too long: " + tag)
561+
if len(tag) >= 1<<29 {
562+
panic("reflect.nameFrom: tag too long: " + tag[:1024] + "...")
538563
}
564+
var nameLen [10]byte
565+
var tagLen [10]byte
566+
nameLenLen := writeVarint(nameLen[:], len(n))
567+
tagLenLen := writeVarint(tagLen[:], len(tag))
539568

540569
var bits byte
541-
l := 1 + 2 + len(n)
570+
l := 1 + nameLenLen + len(n)
542571
if exported {
543572
bits |= 1 << 0
544573
}
545574
if len(tag) > 0 {
546-
l += 2 + len(tag)
575+
l += tagLenLen + len(tag)
547576
bits |= 1 << 1
548577
}
549578

550579
b := make([]byte, l)
551580
b[0] = bits
552-
b[1] = uint8(len(n) >> 8)
553-
b[2] = uint8(len(n))
554-
copy(b[3:], n)
581+
copy(b[1:], nameLen[:nameLenLen])
582+
copy(b[1+nameLenLen:], n)
555583
if len(tag) > 0 {
556-
tb := b[3+len(n):]
557-
tb[0] = uint8(len(tag) >> 8)
558-
tb[1] = uint8(len(tag))
559-
copy(tb[2:], tag)
584+
tb := b[1+nameLenLen+len(n):]
585+
copy(tb, tagLen[:tagLenLen])
586+
copy(tb[tagLenLen:], tag)
560587
}
561588

562589
return name{bytes: &b[0]}
@@ -2570,7 +2597,7 @@ func StructOf(fields []StructField) Type {
25702597
hash = fnv1(hash, byte(ft.hash>>24), byte(ft.hash>>16), byte(ft.hash>>8), byte(ft.hash))
25712598

25722599
repr = append(repr, (" " + ft.String())...)
2573-
if f.name.tagLen() > 0 {
2600+
if f.name.hasTag() {
25742601
hash = fnv1(hash, []byte(f.name.tag())...)
25752602
repr = append(repr, (" " + strconv.Quote(f.name.tag()))...)
25762603
}

0 commit comments

Comments
 (0)