blob: 8a52ba8e2ad9dfccfb496d8ebf47e8483f205610 [file] [log] [blame]
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package ssl
import (
"bytes"
"encoding/binary"
"fmt"
"regexp"
"github.com/apache/skywalking-rover/pkg/tools/elf"
"github.com/apache/skywalking-rover/pkg/tools/host"
"github.com/apache/skywalking-rover/pkg/tools/profiling"
"github.com/apache/skywalking-rover/pkg/tools/version"
"github.com/cilium/ebpf"
)
var (
goVersionRegex = regexp.MustCompile(`^go(?P<Major>\d)\.(?P<Minor>\d+)`)
goTLSWriteSymbol = "crypto/tls.(*Conn).Write"
goTLSReadSymbol = "crypto/tls.(*Conn).Read"
goTLSGIDStatusSymbol = "runtime.casgstatus"
goTLSPollFDSymbol = "internal/poll.FD"
goTLSConnSymbol = "crypto/tls.Conn"
goTLSRuntimeG = "runtime.g"
)
type GoTLSArgsLocationType uint32
const (
GoTLSArgsLocationTypeStack GoTLSArgsLocationType = 1
GoTLSArgsLocationTypeRegister GoTLSArgsLocationType = 2
)
type GoSymbolLocation struct {
Type GoTLSArgsLocationType
Offset uint32
}
type GoTLSSymbolAddress struct {
// net.Conn addresses
FDSysFDOffset uint64
TLSConnOffset uint64
GIDOffset uint64
TCPConnOffset uint64
IsClientOffset uint64
// write function relate locations
WriteConnectionLoc GoSymbolLocation
WriteBufferLoc GoSymbolLocation
WriteRet0Loc GoSymbolLocation
WriteRet1Loc GoSymbolLocation
// write function relate locations
ReadConnectionLoc GoSymbolLocation
ReadBufferLoc GoSymbolLocation
ReadRet0Loc GoSymbolLocation
ReadRet1Loc GoSymbolLocation
}
func (r *Register) GoTLS(symbolAddrMap *ebpf.Map, write, writeRet, read, readRet *ebpf.Program) {
r.addHandler("goTLS", func() (bool, error) {
buildVersionSymbol := r.searchSymbolInModules(r.modules, func(a, b string) bool {
return a == b
}, "runtime.buildVersion")
if buildVersionSymbol == nil {
return false, nil
}
pidExeFile := host.GetFileInHost(fmt.Sprintf("/proc/%d/exe", r.pid))
elfFile, err := elf.NewFile(pidExeFile)
if err != nil {
return false, fmt.Errorf("read executable file error: %v", err)
}
defer elfFile.Close()
v, err := r.getGoVersion(elfFile, buildVersionSymbol)
if err != nil {
return false, err
}
offsets, err := r.generateGOTLSSymbolOffsets(r, elfFile, v)
if err != nil {
return false, err
}
if offsets == nil {
return false, nil
}
// setting the locations
if err := symbolAddrMap.Put(uint32(r.pid), offsets); err != nil {
return false, fmt.Errorf("setting the Go TLS argument location failure, pid: %d, error: %v", r.pid, err)
}
exeFile := r.linker.OpenUProbeExeFile(pidExeFile)
exeFile.AddGoLink(goTLSWriteSymbol, write, writeRet, elfFile)
exeFile.AddGoLink(goTLSReadSymbol, read, readRet, elfFile)
if e := r.linker.HasError(); e != nil {
return false, e
}
return true, nil
})
}
func (r *Register) getGoVersion(elfFile *elf.File, versionSymbol *profiling.Symbol) (*version.Version, error) {
buffer, err := elfFile.ReadSymbolData(".data", versionSymbol.Location, versionSymbol.Size)
if err != nil {
return nil, fmt.Errorf("reading go version struct info failure: %v", err)
}
var t = goStringInC{}
buf := bytes.NewReader(buffer)
err = binary.Read(buf, binary.LittleEndian, &t)
if err != nil {
return nil, fmt.Errorf("read the go structure failure: %v", err)
}
buffer, err = elfFile.ReadSymbolData(".data", t.Ptr, t.Size)
if err != nil {
return nil, fmt.Errorf("read the go version failure: %v", err)
}
// parse versions
submatch := goVersionRegex.FindStringSubmatch(string(buffer))
if len(submatch) != 3 {
return nil, fmt.Errorf("the go version is failure to identify, version: %s", string(buffer))
}
return version.Read(submatch[1], submatch[2], "")
}
type goStringInC struct {
Ptr uint64
Size uint64
}
func (r *Register) generateGOTLSSymbolOffsets(register *Register, elfFile *elf.File, v *version.Version) (*GoTLSSymbolAddress, error) {
reader, err := elfFile.NewDwarfReader(
goTLSReadSymbol, goTLSWriteSymbol, goTLSGIDStatusSymbol,
goTLSPollFDSymbol, goTLSConnSymbol, goTLSRuntimeG)
if err != nil {
return nil, err
}
symbolAddresses := &GoTLSSymbolAddress{}
sym := register.SearchSymbol(func(a, b string) bool {
return a == b
}, "go.itab.*net.TCPConn,net.Conn")
if sym == nil {
log.Warnf("could not found the tcp connection symbol: go.itab.*net.TCPConn,net.Conn")
return nil, nil
}
symbolAddresses.TCPConnOffset = sym.Location
readFunction := reader.GetFunction(goTLSReadSymbol)
if readFunction == nil {
log.Warnf("could not found the go tls read symbol: %s", goTLSReadSymbol)
return nil, nil
}
writeFunction := reader.GetFunction(goTLSWriteSymbol)
if writeFunction == nil {
log.Warnf("could not found the go tls write symbol: %s", goTLSWriteSymbol)
return nil, nil
}
gidStatusFunction := reader.GetFunction(goTLSGIDStatusSymbol)
if gidStatusFunction == nil {
log.Warnf("could not found the goid status change symbol: %s", goTLSGIDStatusSymbol)
return nil, nil
}
var retValArg0, retValArg1 = "~r1", "~r2"
if v.Minor >= 18 {
retValArg0, retValArg1 = "~r0", "~r1"
}
// build the symbols
var assignError error
// offset
assignError = r.assignGoTLSStructureOffset(assignError, reader, goTLSPollFDSymbol, "Sysfd", &symbolAddresses.FDSysFDOffset)
assignError = r.assignGoTLSStructureOffset(assignError, reader, goTLSConnSymbol, "conn", &symbolAddresses.TLSConnOffset)
assignError = r.assignGoTLSStructureOffset(assignError, reader, goTLSRuntimeG, "goid", &symbolAddresses.GIDOffset)
assignError = r.assignGoTLSStructureOffset(assignError, reader, goTLSConnSymbol, "isClient", &symbolAddresses.IsClientOffset)
// write
assignError = r.assignGoTLSArgsLocation(assignError, writeFunction, "c", &symbolAddresses.WriteConnectionLoc)
assignError = r.assignGoTLSArgsLocation(assignError, writeFunction, "b", &symbolAddresses.WriteBufferLoc)
assignError = r.assignGoTLSArgsLocation(assignError, writeFunction, retValArg0, &symbolAddresses.WriteRet0Loc)
assignError = r.assignGoTLSArgsLocation(assignError, writeFunction, retValArg1, &symbolAddresses.WriteRet1Loc)
// read
assignError = r.assignGoTLSArgsLocation(assignError, readFunction, "c", &symbolAddresses.ReadConnectionLoc)
assignError = r.assignGoTLSArgsLocation(assignError, readFunction, "b", &symbolAddresses.ReadBufferLoc)
assignError = r.assignGoTLSArgsLocation(assignError, readFunction, retValArg0, &symbolAddresses.ReadRet0Loc)
assignError = r.assignGoTLSArgsLocation(assignError, readFunction, retValArg1, &symbolAddresses.ReadRet1Loc)
return symbolAddresses, assignError
}
func (r *Register) assignGoTLSStructureOffset(err error, reader *elf.DwarfReader, structName, fieldName string, dest *uint64) error {
if err != nil {
return err
}
structure := reader.GetStructure(structName)
if structure == nil {
return fmt.Errorf("the structure is not found, name: %s", structName)
}
field := structure.GetField(fieldName)
if field == nil {
return fmt.Errorf("the field is not found in structure, structure name: %s, field name: %s", structName, fieldName)
}
*dest = uint64(field.Offset)
return nil
}
func (r *Register) assignGoTLSArgsLocation(err error, function *elf.FunctionInfo, argName string, dest *GoSymbolLocation) error {
if err != nil {
return err
}
var kSPOffset uint32 = 8
args := function.Args(argName)
if args == nil {
return fmt.Errorf("the args is not found, function: %s, args name: %s", function.Name(), argName)
}
if args.Location.Type == elf.ArgLocationTypeStack {
dest.Type = GoTLSArgsLocationTypeStack
dest.Offset = uint32(args.Location.Offset) + kSPOffset
} else if args.Location.Type == elf.ArgLocationTypeRegister {
dest.Type = GoTLSArgsLocationTypeRegister
dest.Offset = uint32(args.Location.Offset)
} else {
return fmt.Errorf("the location type is not support, function: %s, args name: %s, type: %d",
function.Name(), argName, args.Location.Type)
}
return nil
}