Commit 4295ed9b authored by Clément Chigot's avatar Clément Chigot Committed by Ian Lance Taylor

cmd: fix symbols addressing for aix/ppc64

This commit changes the code generated for addressing symbols on AIX
operating system.

On AIX, every symbol accesses must be done via another symbol near the TOC,
named TOC anchor or TOC entry. This TOC anchor is a pointer to the symbol
address.
During Progedit function, when a symbol access is detected, its instructions
are modified to create a load on its TOC anchor and retrieve the symbol.

Change-Id: I00cf8f49c13004bc99fa8af13d549a709320f797
Reviewed-on: https://go-review.googlesource.com/c/151039
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent b41cdc4a
...@@ -214,6 +214,8 @@ const ( ...@@ -214,6 +214,8 @@ const (
// Indicates auto that was optimized away, but whose type // Indicates auto that was optimized away, but whose type
// we want to preserve in the DWARF debug info. // we want to preserve in the DWARF debug info.
NAME_DELETED_AUTO NAME_DELETED_AUTO
// Indicates that this is a reference to a TOC anchor.
NAME_TOCREF
) )
//go:generate stringer -type AddrType //go:generate stringer -type AddrType
......
...@@ -98,6 +98,17 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) { ...@@ -98,6 +98,17 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) {
w.writeRefs(s) w.writeRefs(s)
w.addLengths(s) w.addLengths(s)
} }
if ctxt.Headtype == objabi.Haix {
// Data must be sorted to keep a constant order in TOC symbols.
// As they are created during Progedit, two symbols can be switched between
// two different compilations. Therefore, BuildID will be different.
// TODO: find a better place and optimize to only sort TOC symbols
SortSlice(ctxt.Data, func(i, j int) bool {
return ctxt.Data[i].Name < ctxt.Data[j].Name
})
}
for _, s := range ctxt.Data { for _, s := range ctxt.Data {
w.writeRefs(s) w.writeRefs(s)
w.addLengths(s) w.addLengths(s)
......
...@@ -391,6 +391,7 @@ const ( ...@@ -391,6 +391,7 @@ const (
C_GOK C_GOK
C_ADDR C_ADDR
C_GOTADDR C_GOTADDR
C_TOCADDR
C_TLS_LE C_TLS_LE
C_TLS_IE C_TLS_IE
C_TEXTSIZE C_TEXTSIZE
......
...@@ -26,6 +26,7 @@ var cnames9 = []string{ ...@@ -26,6 +26,7 @@ var cnames9 = []string{
"DACON", "DACON",
"SBRA", "SBRA",
"LBRA", "LBRA",
"LBRAPIC",
"SAUTO", "SAUTO",
"LAUTO", "LAUTO",
"SEXT", "SEXT",
...@@ -42,6 +43,7 @@ var cnames9 = []string{ ...@@ -42,6 +43,7 @@ var cnames9 = []string{
"GOK", "GOK",
"ADDR", "ADDR",
"GOTADDR", "GOTADDR",
"TOCADDR",
"TLS_LE", "TLS_LE",
"TLS_IE", "TLS_IE",
"TEXTSIZE", "TEXTSIZE",
......
...@@ -287,6 +287,7 @@ var optab = []Optab{ ...@@ -287,6 +287,7 @@ var optab = []Optab{
{AMOVD, C_TLS_IE, C_NONE, C_NONE, C_REG, 80, 8, 0}, {AMOVD, C_TLS_IE, C_NONE, C_NONE, C_REG, 80, 8, 0},
{AMOVD, C_GOTADDR, C_NONE, C_NONE, C_REG, 81, 8, 0}, {AMOVD, C_GOTADDR, C_NONE, C_NONE, C_REG, 81, 8, 0},
{AMOVD, C_TOCADDR, C_NONE, C_NONE, C_REG, 95, 8, 0},
/* load constant */ /* load constant */
{AMOVD, C_SECON, C_NONE, C_NONE, C_REG, 3, 4, REGSB}, {AMOVD, C_SECON, C_NONE, C_NONE, C_REG, 3, 4, REGSB},
...@@ -846,6 +847,9 @@ func (c *ctxt9) aclass(a *obj.Addr) int { ...@@ -846,6 +847,9 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
case obj.NAME_GOTREF: case obj.NAME_GOTREF:
return C_GOTADDR return C_GOTADDR
case obj.NAME_TOCREF:
return C_TOCADDR
case obj.NAME_AUTO: case obj.NAME_AUTO:
c.instoffset = int64(c.autosize) + a.Offset c.instoffset = int64(c.autosize) + a.Offset
if c.instoffset >= -BIG && c.instoffset < BIG { if c.instoffset >= -BIG && c.instoffset < BIG {
...@@ -1019,7 +1023,7 @@ func (c *ctxt9) oplook(p *obj.Prog) *Optab { ...@@ -1019,7 +1023,7 @@ func (c *ctxt9) oplook(p *obj.Prog) *Optab {
} }
} }
//print("oplook %v %d %d %d %d\n", p, a1, a2, a3, a4); // c.ctxt.Logf("oplook %v %d %d %d %d\n", p, a1, a2, a3, a4)
ops := oprange[p.As&obj.AMask] ops := oprange[p.As&obj.AMask]
c1 := &xcmp[a1] c1 := &xcmp[a1]
c3 := &xcmp[a3] c3 := &xcmp[a3]
...@@ -2207,6 +2211,10 @@ func (c *ctxt9) opform(insn uint32) int { ...@@ -2207,6 +2211,10 @@ func (c *ctxt9) opform(insn uint32) int {
// Encode instructions and create relocation for accessing s+d according to the // Encode instructions and create relocation for accessing s+d according to the
// instruction op with source or destination (as appropriate) register reg. // instruction op with source or destination (as appropriate) register reg.
func (c *ctxt9) symbolAccess(s *obj.LSym, d int64, reg int16, op uint32) (o1, o2 uint32) { func (c *ctxt9) symbolAccess(s *obj.LSym, d int64, reg int16, op uint32) (o1, o2 uint32) {
if c.ctxt.Headtype == objabi.Haix {
// Every symbol accesses must be made via a TOC anchor.
c.ctxt.Diag("symbolAccess called for %s", s.Name)
}
var base uint32 var base uint32
form := c.opform(op) form := c.opform(op)
if c.ctxt.Flag_shared { if c.ctxt.Flag_shared {
...@@ -3647,6 +3655,24 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { ...@@ -3647,6 +3655,24 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) {
/* operand order: RA, RB, CY, RT */ /* operand order: RA, RB, CY, RT */
cy := int(c.regoff(p.GetFrom3())) cy := int(c.regoff(p.GetFrom3()))
o1 = AOP_Z23I(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg), uint32(cy)) o1 = AOP_Z23I(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg), uint32(cy))
case 95: /* Retrieve TOC symbol */
v := c.vregoff(&p.To)
if v != 0 {
c.ctxt.Diag("invalid offset against TOC slot %v", p)
}
if c.opform(c.opload(p.As)) != DS_FORM {
c.ctxt.Diag("invalid form for a TOC access in %v", p)
}
o1 = AOP_IRR(OP_ADDIS, uint32(p.To.Reg), REG_R2, 0)
o2 = AOP_IRR(c.opload(AMOVD), uint32(p.To.Reg), uint32(p.To.Reg), 0)
rel := obj.Addrel(c.cursym)
rel.Off = int32(c.pc)
rel.Siz = 8
rel.Sym = p.From.Sym
rel.Type = objabi.R_ADDRPOWER_TOCREL_DS
} }
out[0] = o1 out[0] = o1
......
...@@ -108,9 +108,122 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { ...@@ -108,9 +108,122 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
} }
if c.ctxt.Flag_dynlink { if c.ctxt.Flag_dynlink {
c.rewriteToUseGot(p) c.rewriteToUseGot(p)
} else if c.ctxt.Headtype == objabi.Haix {
c.rewriteToUseTOC(p)
} }
} }
// Rewrite p, if necessary, to access a symbol using its TOC anchor.
func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) {
if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
return
}
var source *obj.Addr
if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC {
if p.From.Type == obj.TYPE_ADDR {
if p.As == ADWORD {
// ADWORD $sym doesn't need TOC anchor
return
}
if p.As != AMOVD {
c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p)
return
}
if p.To.Type != obj.TYPE_REG {
c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p)
return
}
} else if p.From.Type != obj.TYPE_MEM {
c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
return
}
source = &p.From
} else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC {
if p.To.Type != obj.TYPE_MEM {
c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
return
}
if source != nil {
c.ctxt.Diag("cannot handle symbols on both sides in %v", p)
return
}
source = &p.To
} else {
return
}
if source.Sym == nil {
c.ctxt.Diag("do not know how to handle nil symbol in %v", p)
return
}
if source.Sym.Type == objabi.STLSBSS {
return
}
// Retrieve or create the TOC anchor.
symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) {
s.Type = objabi.SDATA
s.Set(obj.AttrDuplicateOK, true)
c.ctxt.Data = append(c.ctxt.Data, s)
s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0)
})
if source.Type == obj.TYPE_ADDR {
// MOVD $sym, Rx becomes MOVD symtoc, Rx
// MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx
p.From.Type = obj.TYPE_MEM
p.From.Sym = symtoc
p.From.Name = obj.NAME_TOCREF
if p.From.Offset != 0 {
q := obj.Appendp(p, c.newprog)
q.As = AADD
q.From.Type = obj.TYPE_CONST
q.From.Offset = p.From.Offset
p.From.Offset = 0
q.To = p.To
}
return
}
// MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry
// MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP)
// An addition may be inserted between the two MOVs if there is an offset.
q := obj.Appendp(p, c.newprog)
q.As = AMOVD
q.From.Type = obj.TYPE_MEM
q.From.Sym = symtoc
q.From.Name = obj.NAME_TOCREF
q.To.Type = obj.TYPE_REG
q.To.Reg = REGTMP
q = obj.Appendp(q, c.newprog)
q.As = p.As
q.From = p.From
q.To = p.To
if p.From.Name != obj.NAME_NONE {
q.From.Type = obj.TYPE_MEM
q.From.Reg = REGTMP
q.From.Name = obj.NAME_NONE
q.From.Sym = nil
} else if p.To.Name != obj.NAME_NONE {
q.To.Type = obj.TYPE_MEM
q.To.Reg = REGTMP
q.To.Name = obj.NAME_NONE
q.To.Sym = nil
} else {
c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p)
}
obj.Nopout(p)
}
// Rewrite p, if necessary, to access global data via the global offset table. // Rewrite p, if necessary, to access global data via the global offset table.
func (c *ctxt9) rewriteToUseGot(p *obj.Prog) { func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
......
...@@ -372,6 +372,17 @@ func Mconv(a *Addr) string { ...@@ -372,6 +372,17 @@ func Mconv(a *Addr) string {
} else { } else {
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg) str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
} }
case NAME_TOCREF:
reg := "SB"
if a.Reg != REG_NONE {
reg = Rconv(int(a.Reg))
}
if a.Sym != nil {
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
} else {
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
}
} }
return str return str
} }
......
...@@ -314,14 +314,18 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -314,14 +314,18 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
break break
} }
// On AIX, if a relocated symbol is in .data, a second relocation
// must be done by the loader, as the section .data will be moved. // On AIX, a second relocation must be done by the loader,
// as section addresses can change once loaded.
// The "default" symbol address is still needed by the loader so // The "default" symbol address is still needed by the loader so
// the current relocation can't be skipped. // the current relocation can't be skipped.
if ctxt.HeadType == objabi.Haix && r.Sym.Type != sym.SDYNIMPORT && r.Sym.Sect.Seg == &Segdata { if ctxt.HeadType == objabi.Haix && r.Sym.Type != sym.SDYNIMPORT {
// It's not possible to make a loader relocation to a DWARF section. // It's not possible to make a loader relocation in a
// FIXME // symbol which is not inside .data section.
if s.Sect.Seg != &Segdwarf { // FIXME: It should be forbidden to have R_ADDR from a
// symbol which isn't in .data. However, as .text has the
// same address once loaded, this is possible.
if s.Sect.Seg == &Segdata {
Xcoffadddynrel(ctxt, s, r) Xcoffadddynrel(ctxt, s, r)
} }
} }
......
...@@ -913,8 +913,8 @@ func (f *xcoffFile) adddynimpsym(ctxt *Link, s *sym.Symbol) { ...@@ -913,8 +913,8 @@ func (f *xcoffFile) adddynimpsym(ctxt *Link, s *sym.Symbol) {
// Xcoffadddynrel adds a dynamic relocation in a XCOFF file. // Xcoffadddynrel adds a dynamic relocation in a XCOFF file.
// This relocation will be made by the loader. // This relocation will be made by the loader.
func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
if s.Type <= sym.SPCLNTAB && r.Sym.Type >= sym.SELFSECT && r.Sym.Type <= sym.SXREF { if s.Type <= sym.SPCLNTAB {
Errorf(s, "cannot have a relocation in a text section with a data symbol: %s ", r.Sym.Name) Errorf(s, "cannot have a relocation to %s in a text section symbol", r.Sym.Name)
return false return false
} }
...@@ -936,9 +936,22 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { ...@@ -936,9 +936,22 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
break break
} }
} }
} else if s.Type == sym.SDATA && r.Sym.Type >= sym.SELFSECT && r.Sym.Type <= sym.SXREF { } else if s.Type == sym.SDATA {
// .data to .data relocation switch r.Sym.Sect.Seg {
ldr.symndx = 1 // .data default:
Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name)
case &Segtext:
case &Segrodata:
ldr.symndx = 0 // .text
case &Segdata:
if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS {
ldr.symndx = 2 // .bss
} else {
ldr.symndx = 1 // .data
}
}
} else { } else {
Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", r.Sym.Name, s.Type, r.Sym.Type) Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", r.Sym.Name, s.Type, r.Sym.Type)
return false return false
...@@ -1009,6 +1022,12 @@ func (ctxt *Link) doxcoff() { ...@@ -1009,6 +1022,12 @@ func (ctxt *Link) doxcoff() {
}) })
xfile.genDynSym(ctxt) xfile.genDynSym(ctxt)
for _, s := range ctxt.Syms.Allsym {
if strings.HasPrefix(s.Name, "TOC.") {
s.Type = sym.SXCOFFTOC
}
}
} }
// Loader section // Loader section
......
...@@ -489,7 +489,40 @@ func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 { ...@@ -489,7 +489,40 @@ func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 {
return toc.Value return toc.Value
} }
func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
var o1, o2 uint32
o1 = uint32(val >> 32)
o2 = uint32(val)
t := ld.Symaddr(r.Sym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value // sym addr
if t != int64(int32(t)) {
ld.Errorf(s, "TOC relocation for %s is too big to relocate %s: 0x%x", s.Name, r.Sym, t)
}
if t&0x8000 != 0 {
t += 0x10000
}
o1 |= uint32((t >> 16) & 0xFFFF)
switch r.Type {
case objabi.R_ADDRPOWER_TOCREL_DS:
if t&3 != 0 {
ld.Errorf(s, "bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym))
}
o2 |= uint32(t) & 0xFFFC
default:
return -1
}
return int64(o1)<<32 | int64(o2)
}
func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 { func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
if ctxt.HeadType == objabi.Haix {
ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name)
}
var o1, o2 uint32 var o1, o2 uint32
if ctxt.Arch.ByteOrder == binary.BigEndian { if ctxt.Arch.ByteOrder == binary.BigEndian {
o1 = uint32(val >> 32) o1 = uint32(val >> 32)
...@@ -508,7 +541,7 @@ func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 ...@@ -508,7 +541,7 @@ func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64
t := ld.Symaddr(r.Sym) + r.Add t := ld.Symaddr(r.Sym) + r.Add
if t < 0 || t >= 1<<31 { if t < 0 || t >= 1<<31 {
ld.Errorf(s, "relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym)) ld.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", s.Name, ld.Symaddr(r.Sym))
} }
if t&0x8000 != 0 { if t&0x8000 != 0 {
t += 0x10000 t += 0x10000
...@@ -682,6 +715,8 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo ...@@ -682,6 +715,8 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
return r.Add, true return r.Add, true
case objabi.R_GOTOFF: case objabi.R_GOTOFF:
return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS:
return archreloctoc(ctxt, r, s, val), true
case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS: case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS:
return archrelocaddr(ctxt, r, s, val), true return archrelocaddr(ctxt, r, s, val), true
case objabi.R_CALLPOWER: case objabi.R_CALLPOWER:
......
...@@ -89,10 +89,10 @@ const ( ...@@ -89,10 +89,10 @@ const (
SNOPTRDATA SNOPTRDATA
SINITARR SINITARR
SDATA SDATA
SXCOFFTOC
SBSS SBSS
SNOPTRBSS SNOPTRBSS
STLSBSS STLSBSS
SXCOFFTOC
SXREF SXREF
SMACHOSYMSTR SMACHOSYMSTR
SMACHOSYMTAB SMACHOSYMTAB
......
...@@ -4,9 +4,9 @@ package sym ...@@ -4,9 +4,9 @@ package sym
import "strconv" import "strconv"
const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXCOFFTOCSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFMISCSABIALIAS" const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFMISCSABIALIAS"
var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 296, 301, 313, 325, 342, 359, 368, 374, 384, 392, 402, 412, 423, 432, 442, 451} var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 276, 280, 289, 296, 301, 313, 325, 342, 359, 368, 374, 384, 392, 402, 412, 423, 432, 442, 451}
func (i SymKind) String() string { func (i SymKind) String() string {
if i >= SymKind(len(_SymKind_index)-1) { if i >= SymKind(len(_SymKind_index)-1) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment