mirror of
https://github.com/beego/bee.git
synced 2025-01-23 12:47:13 +00:00
Merge pull request #670 from beego/develop
merge pull request from develop
This commit is contained in:
commit
3121f64b8f
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,3 +30,4 @@ _testmain.go
|
||||
bee
|
||||
*.exe~
|
||||
.goxc.local.json
|
||||
vendor
|
||||
|
@ -35,7 +35,7 @@ var CmdApiapp = &commands.Command{
|
||||
The command 'api' creates a Beego API application.
|
||||
|
||||
{{"Example:"|bold}}
|
||||
$ bee api [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
|
||||
$ bee api [appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||
|
||||
If 'conn' argument is empty, the command will generate an example API application. Otherwise the command
|
||||
will connect to your database and generate models based on the existing tables.
|
||||
@ -65,6 +65,7 @@ runmode = dev
|
||||
autorender = false
|
||||
copyrequestbody = true
|
||||
EnableDocs = true
|
||||
sqlconn = {{.SQLConnStr}}
|
||||
`
|
||||
var apiMaingo = `package main
|
||||
|
||||
@ -93,11 +94,8 @@ import (
|
||||
{{.DriverPkg}}
|
||||
)
|
||||
|
||||
func init() {
|
||||
orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}")
|
||||
}
|
||||
|
||||
func main() {
|
||||
orm.RegisterDataBase("default", "{{.DriverName}}", beego.AppConfig.String("sqlconn"))
|
||||
if beego.BConfig.RunMode == "dev" {
|
||||
beego.BConfig.WebConfig.DirectoryIndex = true
|
||||
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
|
||||
@ -511,7 +509,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
_, file, _, _ := runtime.Caller(1)
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator))))
|
||||
beego.TestBeegoInit(apppath)
|
||||
}
|
||||
@ -558,6 +556,7 @@ func createAPI(cmd *commands.Command, args []string) int {
|
||||
}
|
||||
|
||||
appPath, packPath, err := utils.CheckEnv(args[0])
|
||||
appName := path.Base(args[0])
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("%s", err)
|
||||
}
|
||||
@ -575,11 +574,13 @@ func createAPI(cmd *commands.Command, args []string) int {
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "controllers"), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "tests"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "tests"), "\x1b[0m")
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "conf", "app.conf"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(appPath, "conf", "app.conf"),
|
||||
strings.Replace(apiconf, "{{.Appname}}", path.Base(args[0]), -1))
|
||||
|
||||
if generate.SQLConn != "" {
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "conf", "app.conf"), "\x1b[0m")
|
||||
confContent := strings.Replace(apiconf, "{{.Appname}}", appName, -1)
|
||||
confContent = strings.Replace(confContent, "{{.SQLConnStr}}", generate.SQLConn.String(), -1)
|
||||
utils.WriteToFile(path.Join(appPath, "conf", "app.conf"), confContent)
|
||||
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "main.go"), "\x1b[0m")
|
||||
mainGoContent := strings.Replace(apiMainconngo, "{{.Appname}}", packPath, -1)
|
||||
mainGoContent = strings.Replace(mainGoContent, "{{.DriverName}}", string(generate.SQLDriver), -1)
|
||||
@ -601,6 +602,11 @@ func createAPI(cmd *commands.Command, args []string) int {
|
||||
beeLogger.Log.Infof("Using '%s' as 'tables'", generate.Tables)
|
||||
generate.GenerateAppcode(string(generate.SQLDriver), string(generate.SQLConn), "3", string(generate.Tables), appPath)
|
||||
} else {
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "conf", "app.conf"), "\x1b[0m")
|
||||
confContent := strings.Replace(apiconf, "{{.Appname}}", appName, -1)
|
||||
confContent = strings.Replace(confContent, "{{.SQLConnStr}}", "", -1)
|
||||
utils.WriteToFile(path.Join(appPath, "conf", "app.conf"), confContent)
|
||||
|
||||
os.Mkdir(path.Join(appPath, "models"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "models"), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "routers"), 0755)
|
||||
|
@ -28,11 +28,12 @@ import (
|
||||
"github.com/beego/bee/cmd/commands/version"
|
||||
beeLogger "github.com/beego/bee/logger"
|
||||
"github.com/beego/bee/utils"
|
||||
"github.com/derekparker/delve/pkg/terminal"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
"github.com/derekparker/delve/service/rpccommon"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/go-delve/delve/pkg/terminal"
|
||||
"github.com/go-delve/delve/service"
|
||||
"github.com/go-delve/delve/service/debugger"
|
||||
"github.com/go-delve/delve/service/rpc2"
|
||||
"github.com/go-delve/delve/service/rpccommon"
|
||||
)
|
||||
|
||||
var cmdDlv = &commands.Command{
|
||||
@ -43,7 +44,7 @@ var cmdDlv = &commands.Command{
|
||||
|
||||
To debug your application using Delve, use: {{"$ bee dlv" | bold}}
|
||||
|
||||
For more information on Delve: https://github.com/derekparker/delve
|
||||
For more information on Delve: https://github.com/go-delve/delve
|
||||
`,
|
||||
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
|
||||
Run: runDlv,
|
||||
@ -148,11 +149,13 @@ func startDelveDebugger(addr string, ch chan int) int {
|
||||
server := rpccommon.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
AcceptMulti: true,
|
||||
AttachPid: 0,
|
||||
APIVersion: 2,
|
||||
WorkingDir: ".",
|
||||
ProcessArgs: []string{abs},
|
||||
Backend: "default",
|
||||
Debugger: debugger.Config{
|
||||
AttachPid: 0,
|
||||
WorkingDir: ".",
|
||||
Backend: "default",
|
||||
},
|
||||
})
|
||||
if err := server.Run(); err != nil {
|
||||
beeLogger.Log.Fatalf("Could not start debugger server: %v", err)
|
||||
@ -228,7 +231,7 @@ func startWatcher(paths []string, ch chan int) {
|
||||
|
||||
// Wait 1s before re-build until there is no file change
|
||||
scheduleTime := time.Now().Add(1 * time.Second)
|
||||
time.Sleep(scheduleTime.Sub(time.Now()))
|
||||
time.Sleep(time.Until(scheduleTime))
|
||||
_, err := buildDebug()
|
||||
if err != nil {
|
||||
utils.Notify("Build Failed: "+err.Error(), "bee")
|
||||
|
@ -24,7 +24,7 @@ var CmdHproseapp = &commands.Command{
|
||||
|
||||
{{"To scaffold out your application, use:"|bold}}
|
||||
|
||||
$ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
|
||||
$ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||
|
||||
If 'conn' is empty, the command will generate a sample application. Otherwise the command
|
||||
will connect to your database and generate models based on the existing tables.
|
||||
|
@ -38,19 +38,19 @@ var CmdMigrate = &commands.Command{
|
||||
|
||||
▶ {{"To run all the migrations:"|bold}}
|
||||
|
||||
$ bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||
$ bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-dir="path/to/migration"]
|
||||
|
||||
▶ {{"To rollback the last migration:"|bold}}
|
||||
|
||||
$ bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||
$ bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-dir="path/to/migration"]
|
||||
|
||||
▶ {{"To do a reset, which will rollback all the migrations:"|bold}}
|
||||
|
||||
$ bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||
$ bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-dir="path/to/migration"]
|
||||
|
||||
▶ {{"To update your schema:"|bold}}
|
||||
|
||||
$ bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||
$ bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-dir="path/to/migration"]
|
||||
`,
|
||||
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
|
||||
Run: RunMigration,
|
||||
@ -58,10 +58,12 @@ var CmdMigrate = &commands.Command{
|
||||
|
||||
var mDriver utils.DocValue
|
||||
var mConn utils.DocValue
|
||||
var mDir utils.DocValue
|
||||
|
||||
func init() {
|
||||
CmdMigrate.Flag.Var(&mDriver, "driver", "Database driver. Either mysql, postgres or sqlite.")
|
||||
CmdMigrate.Flag.Var(&mConn, "conn", "Connection string used by the driver to connect to a database instance.")
|
||||
CmdMigrate.Flag.Var(&mDir, "dir", "The directory where the migration files are stored")
|
||||
commands.AvailableCommands = append(commands.AvailableCommands, CmdMigrate)
|
||||
}
|
||||
|
||||
@ -94,25 +96,41 @@ func RunMigration(cmd *commands.Command, args []string) int {
|
||||
mConn = "root:@tcp(127.0.0.1:3306)/test"
|
||||
}
|
||||
}
|
||||
if mDir == "" {
|
||||
mDir = utils.DocValue(config.Conf.Database.Dir)
|
||||
if mDir == "" {
|
||||
mDir = utils.DocValue(path.Join(currpath, "database", "migrations"))
|
||||
}
|
||||
}
|
||||
|
||||
beeLogger.Log.Infof("Using '%s' as 'driver'", mDriver)
|
||||
beeLogger.Log.Infof("Using '%s' as 'conn'", mConn)
|
||||
driverStr, connStr := string(mDriver), string(mConn)
|
||||
//Log sensitive connection information only when DEBUG is set to true.
|
||||
beeLogger.Log.Debugf("Conn: %s", utils.FILE(), utils.LINE(), mConn)
|
||||
beeLogger.Log.Infof("Using '%s' as 'dir'", mDir)
|
||||
driverStr, connStr, dirStr := string(mDriver), string(mConn), string(mDir)
|
||||
|
||||
dirRune := []rune(dirStr)
|
||||
|
||||
if dirRune[0] != '/' && dirRune[1] != ':' {
|
||||
dirStr = path.Join(currpath, dirStr)
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
// run all outstanding migrations
|
||||
beeLogger.Log.Info("Running all outstanding migrations")
|
||||
MigrateUpdate(currpath, driverStr, connStr)
|
||||
MigrateUpdate(currpath, driverStr, connStr, dirStr)
|
||||
} else {
|
||||
mcmd := args[0]
|
||||
switch mcmd {
|
||||
case "rollback":
|
||||
beeLogger.Log.Info("Rolling back the last migration operation")
|
||||
MigrateRollback(currpath, driverStr, connStr)
|
||||
MigrateRollback(currpath, driverStr, connStr, dirStr)
|
||||
case "reset":
|
||||
beeLogger.Log.Info("Reseting all migrations")
|
||||
MigrateReset(currpath, driverStr, connStr)
|
||||
MigrateReset(currpath, driverStr, connStr, dirStr)
|
||||
case "refresh":
|
||||
beeLogger.Log.Info("Refreshing all migrations")
|
||||
MigrateRefresh(currpath, driverStr, connStr)
|
||||
MigrateRefresh(currpath, driverStr, connStr, dirStr)
|
||||
default:
|
||||
beeLogger.Log.Fatal("Command is missing")
|
||||
}
|
||||
@ -122,8 +140,10 @@ func RunMigration(cmd *commands.Command, args []string) int {
|
||||
}
|
||||
|
||||
// migrate generates source code, build it, and invoke the binary who does the actual migration
|
||||
func migrate(goal, currpath, driver, connStr string) {
|
||||
dir := path.Join(currpath, "database", "migrations")
|
||||
func migrate(goal, currpath, driver, connStr, dir string) {
|
||||
if dir == "" {
|
||||
dir = path.Join(currpath, "database", "migrations")
|
||||
}
|
||||
postfix := ""
|
||||
if runtime.GOOS == "windows" {
|
||||
postfix = ".exe"
|
||||
@ -415,21 +435,21 @@ CREATE TABLE migrations (
|
||||
)
|
||||
|
||||
// MigrateUpdate does the schema update
|
||||
func MigrateUpdate(currpath, driver, connStr string) {
|
||||
migrate("upgrade", currpath, driver, connStr)
|
||||
func MigrateUpdate(currpath, driver, connStr, dir string) {
|
||||
migrate("upgrade", currpath, driver, connStr, dir)
|
||||
}
|
||||
|
||||
// MigrateRollback rolls back the latest migration
|
||||
func MigrateRollback(currpath, driver, connStr string) {
|
||||
migrate("rollback", currpath, driver, connStr)
|
||||
func MigrateRollback(currpath, driver, connStr, dir string) {
|
||||
migrate("rollback", currpath, driver, connStr, dir)
|
||||
}
|
||||
|
||||
// MigrateReset rolls back all migrations
|
||||
func MigrateReset(currpath, driver, connStr string) {
|
||||
migrate("reset", currpath, driver, connStr)
|
||||
func MigrateReset(currpath, driver, connStr, dir string) {
|
||||
migrate("reset", currpath, driver, connStr, dir)
|
||||
}
|
||||
|
||||
// migrationRefresh rolls back all migrations and start over again
|
||||
func MigrateRefresh(currpath, driver, connStr string) {
|
||||
migrate("refresh", currpath, driver, connStr)
|
||||
// MigrateRefresh rolls back all migrations and start over again
|
||||
func MigrateRefresh(currpath, driver, connStr, dir string) {
|
||||
migrate("refresh", currpath, driver, connStr, dir)
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
_, file, _, _ := runtime.Caller(1)
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator))))
|
||||
beego.TestBeegoInit(apppath)
|
||||
}
|
||||
@ -254,13 +254,13 @@ func CreateApp(cmd *commands.Command, args []string) int {
|
||||
beeLogger.Log.Fatal("Argument [appname] is missing")
|
||||
}
|
||||
|
||||
apppath, packpath, err := utils.CheckEnv(args[0])
|
||||
appPath, packPath, err := utils.CheckEnv(args[0])
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
if utils.IsExist(apppath) {
|
||||
beeLogger.Log.Errorf(colors.Bold("Application '%s' already exists"), apppath)
|
||||
if utils.IsExist(appPath) {
|
||||
beeLogger.Log.Errorf(colors.Bold("Application '%s' already exists"), appPath)
|
||||
beeLogger.Log.Warn(colors.Bold("Do you want to overwrite it? [Yes|No] "))
|
||||
if !utils.AskForConfirmation() {
|
||||
os.Exit(2)
|
||||
@ -269,46 +269,46 @@ func CreateApp(cmd *commands.Command, args []string) int {
|
||||
|
||||
beeLogger.Log.Info("Creating application...")
|
||||
|
||||
os.MkdirAll(apppath, 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "conf"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "controllers"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "models"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "routers"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "tests"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "static"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "static", "js"), 0755)
|
||||
utils.WriteToFile(path.Join(apppath, "static", "js", "reload.min.js"), reloadJsClient)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "static", "css"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "static", "img"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "img")+string(path.Separator), "\x1b[0m")
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(apppath, "views"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", path.Base(args[0]), -1))
|
||||
os.MkdirAll(appPath, 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", appPath+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "conf"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "conf")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "controllers"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "controllers")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "models"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "models")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "routers"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "routers")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "tests"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "tests")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "static"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "static")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "static", "js"), 0755)
|
||||
utils.WriteToFile(path.Join(appPath, "static", "js", "reload.min.js"), reloadJsClient)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "static", "js")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "static", "css"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "static", "css")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "static", "img"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "static", "img")+string(path.Separator), "\x1b[0m")
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "views")+string(path.Separator), "\x1b[0m")
|
||||
os.Mkdir(path.Join(appPath, "views"), 0755)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "conf", "app.conf"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(appPath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", path.Base(args[0]), -1))
|
||||
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "default.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(apppath, "controllers", "default.go"), controllers)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "controllers", "default.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(appPath, "controllers", "default.go"), controllers)
|
||||
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views", "index.tpl"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(apppath, "views", "index.tpl"), indextpl)
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "views", "index.tpl"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(appPath, "views", "index.tpl"), indextpl)
|
||||
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(router, "{{.Appname}}", packpath, -1))
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "routers", "router.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(appPath, "routers", "router.go"), strings.Replace(router, "{{.Appname}}", packPath, -1))
|
||||
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(test, "{{.Appname}}", packpath, -1))
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "tests", "default_test.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(appPath, "tests", "default_test.go"), strings.Replace(test, "{{.Appname}}", packPath, -1))
|
||||
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packpath, -1))
|
||||
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "main.go"), "\x1b[0m")
|
||||
utils.WriteToFile(path.Join(appPath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packPath, -1))
|
||||
|
||||
beeLogger.Log.Success("New application successfully created!")
|
||||
return 0
|
||||
|
@ -79,37 +79,44 @@ func init() {
|
||||
commands.AvailableCommands = append(commands.AvailableCommands, CmdRun)
|
||||
}
|
||||
|
||||
// RunApp locates files to watch, and starts the beego application
|
||||
func RunApp(cmd *commands.Command, args []string) int {
|
||||
if len(args) == 0 || args[0] == "watchall" {
|
||||
currpath, _ = os.Getwd()
|
||||
if found, _gopath, _ := utils.SearchGOPATHs(currpath); found {
|
||||
appname = path.Base(currpath)
|
||||
currentGoPath = _gopath
|
||||
} else {
|
||||
beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", currpath)
|
||||
}
|
||||
} else {
|
||||
// Check if passed Bee application path/name exists in the GOPATH(s)
|
||||
if found, _gopath, _path := utils.SearchGOPATHs(args[0]); found {
|
||||
currpath = _path
|
||||
currentGoPath = _gopath
|
||||
appname = path.Base(currpath)
|
||||
} else {
|
||||
beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", args[0])
|
||||
}
|
||||
// The default app path is the current working directory
|
||||
appPath, _ := os.Getwd()
|
||||
|
||||
if strings.HasSuffix(appname, ".go") && utils.IsExist(currpath) {
|
||||
// If an argument is presented, we use it as the app path
|
||||
if len(args) != 0 && args[0] != "watchall" {
|
||||
if path.IsAbs(args[0]) {
|
||||
appPath = args[0]
|
||||
} else {
|
||||
appPath = path.Join(appPath, args[0])
|
||||
}
|
||||
}
|
||||
|
||||
if utils.IsInGOPATH(appPath) {
|
||||
if found, _gopath, _path := utils.SearchGOPATHs(appPath); found {
|
||||
appPath = _path
|
||||
appname = path.Base(appPath)
|
||||
currentGoPath = _gopath
|
||||
} else {
|
||||
beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", appPath)
|
||||
}
|
||||
if strings.HasSuffix(appname, ".go") && utils.IsExist(appPath) {
|
||||
beeLogger.Log.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname)
|
||||
beeLogger.Log.Info("Do you want to overwrite it? [yes|no] ")
|
||||
if !utils.AskForConfirmation() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
beeLogger.Log.Warn("Running application outside of GOPATH")
|
||||
appname = path.Base(appPath)
|
||||
currentGoPath = appPath
|
||||
}
|
||||
|
||||
beeLogger.Log.Infof("Using '%s' as 'appname'", appname)
|
||||
|
||||
beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), currpath)
|
||||
beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), appPath)
|
||||
|
||||
if runmode == "prod" || runmode == "dev" {
|
||||
os.Setenv("BEEGO_RUNMODE", runmode)
|
||||
@ -122,7 +129,7 @@ func RunApp(cmd *commands.Command, args []string) int {
|
||||
}
|
||||
|
||||
var paths []string
|
||||
readAppDirectories(currpath, &paths)
|
||||
readAppDirectories(appPath, &paths)
|
||||
|
||||
// Because monitor files has some issues, we watch current directory
|
||||
// and ignore non-go files.
|
||||
@ -159,7 +166,7 @@ func RunApp(cmd *commands.Command, args []string) int {
|
||||
}
|
||||
}
|
||||
if downdoc == "true" {
|
||||
if _, err := os.Stat(path.Join(currpath, "swagger", "index.html")); err != nil {
|
||||
if _, err := os.Stat(path.Join(appPath, "swagger", "index.html")); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
downloadFromURL(swaggerlink, "swagger.zip")
|
||||
unzipAndDelete("swagger.zip")
|
||||
|
@ -85,7 +85,7 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
|
||||
go func() {
|
||||
// Wait 1s before autobuild until there is no file change.
|
||||
scheduleTime = time.Now().Add(1 * time.Second)
|
||||
time.Sleep(scheduleTime.Sub(time.Now()))
|
||||
time.Sleep(time.Until(scheduleTime))
|
||||
AutoBuild(files, isgenerate)
|
||||
|
||||
if config.Conf.EnableReload {
|
||||
@ -148,7 +148,7 @@ func AutoBuild(files []string, isgenerate bool) {
|
||||
}
|
||||
appName := appname
|
||||
if err == nil {
|
||||
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
appName += ".exe"
|
||||
}
|
||||
@ -183,9 +183,29 @@ func Kill() {
|
||||
}
|
||||
}()
|
||||
if cmd != nil && cmd.Process != nil {
|
||||
err := cmd.Process.Kill()
|
||||
if err != nil {
|
||||
beeLogger.Log.Errorf("Error while killing cmd process: %s", err)
|
||||
// Windows does not support Interrupt
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd.Process.Signal(os.Kill)
|
||||
} else {
|
||||
cmd.Process.Signal(os.Interrupt)
|
||||
}
|
||||
|
||||
ch := make(chan struct{}, 1)
|
||||
go func() {
|
||||
cmd.Wait()
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
return
|
||||
case <-time.After(10 * time.Second):
|
||||
beeLogger.Log.Info("Timeout. Force kill cmd process")
|
||||
err := cmd.Process.Kill()
|
||||
if err != nil {
|
||||
beeLogger.Log.Errorf("Error while killing cmd process: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ type bale struct {
|
||||
type database struct {
|
||||
Driver string
|
||||
Conn string
|
||||
Dir string
|
||||
}
|
||||
|
||||
// LoadConfig loads the bee tool configuration.
|
||||
|
@ -44,7 +44,7 @@ func GenerateScaffold(sname, fields, currpath, driver, conn string) {
|
||||
// Run the migration
|
||||
beeLogger.Log.Infof("Do you want to migrate the database? [Yes|No] ")
|
||||
if utils.AskForConfirmation() {
|
||||
migrate.MigrateUpdate(currpath, driver, conn)
|
||||
migrate.MigrateUpdate(currpath, driver, conn, "")
|
||||
}
|
||||
beeLogger.Log.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname))
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/astaxie/beego/swagger"
|
||||
"github.com/astaxie/beego/utils"
|
||||
@ -48,6 +48,12 @@ const (
|
||||
aform = "multipart/form-data"
|
||||
)
|
||||
|
||||
const (
|
||||
astTypeArray = "array"
|
||||
astTypeObject = "object"
|
||||
astTypeMap = "map"
|
||||
)
|
||||
|
||||
var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value
|
||||
var controllerComments map[string]string
|
||||
var importlist map[string]string
|
||||
@ -78,11 +84,13 @@ var basicTypes = map[string]string{
|
||||
"byte": "string:byte",
|
||||
"rune": "string:byte",
|
||||
// builtin golang objects
|
||||
"time.Time": "string:datetime",
|
||||
"time.Time": "string:datetime",
|
||||
"json.RawMessage": "object:",
|
||||
}
|
||||
|
||||
var stdlibObject = map[string]string{
|
||||
"&{time Time}": "time.Time",
|
||||
"&{time Time}": "time.Time",
|
||||
"&{json RawMessage}": "json.RawMessage",
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -111,12 +119,12 @@ func ParsePackagesFromDir(dirpath string) {
|
||||
// all 'tests' folders and dot folders wihin dirpath
|
||||
d, _ := filepath.Rel(dirpath, fpath)
|
||||
if !(d == "vendor" || strings.HasPrefix(d, "vendor"+string(os.PathSeparator))) &&
|
||||
!strings.Contains(fpath, "tests") &&
|
||||
!strings.Contains(d, "tests") &&
|
||||
!(d[0] == '.') {
|
||||
err = parsePackageFromDir(fpath)
|
||||
if err != nil {
|
||||
// Send the error to through the channel and continue walking
|
||||
c <- fmt.Errorf("Error while parsing directory: %s", err.Error())
|
||||
c <- fmt.Errorf("error while parsing directory: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -533,7 +541,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
||||
//TODO: resultMap := buildParamMap(f.Type.Results)
|
||||
if comments != nil && comments.List != nil {
|
||||
for _, c := range comments.List {
|
||||
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||
t := strings.TrimSpace(strings.TrimPrefix(c.Text, "//"))
|
||||
if strings.HasPrefix(t, "@router") {
|
||||
elements := strings.TrimSpace(t[len("@router"):])
|
||||
e1 := strings.SplitN(elements, " ", 2)
|
||||
@ -586,7 +594,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
||||
}
|
||||
if isArray {
|
||||
rs.Schema = &swagger.Schema{
|
||||
Type: "array",
|
||||
Type: astTypeArray,
|
||||
Items: &schema,
|
||||
}
|
||||
} else {
|
||||
@ -640,7 +648,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
||||
m, mod, realTypes := getModel(p[2])
|
||||
if isArray {
|
||||
para.Schema = &swagger.Schema{
|
||||
Type: "array",
|
||||
Type: astTypeArray,
|
||||
Items: &swagger.Schema{
|
||||
Ref: "#/definitions/" + m,
|
||||
},
|
||||
@ -781,12 +789,23 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
|
||||
isArray = true
|
||||
}
|
||||
if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" ||
|
||||
typ == "array" || typ == "file" {
|
||||
typ == astTypeArray || typ == "file" {
|
||||
paraType = typ
|
||||
if para.In == "body" {
|
||||
para.Schema = &swagger.Schema{
|
||||
Type: paraType,
|
||||
}
|
||||
}
|
||||
} else if sType, ok := basicTypes[typ]; ok {
|
||||
typeFormat := strings.Split(sType, ":")
|
||||
paraType = typeFormat[0]
|
||||
paraFormat = typeFormat[1]
|
||||
if para.In == "body" {
|
||||
para.Schema = &swagger.Schema{
|
||||
Type: paraType,
|
||||
Format: paraFormat,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m, mod, realTypes := getModel(typ)
|
||||
para.Schema = &swagger.Schema{
|
||||
@ -801,14 +820,14 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
|
||||
if isArray {
|
||||
if para.In == "body" {
|
||||
para.Schema = &swagger.Schema{
|
||||
Type: "array",
|
||||
Type: astTypeArray,
|
||||
Items: &swagger.Schema{
|
||||
Type: paraType,
|
||||
Format: paraFormat,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
para.Type = "array"
|
||||
para.Type = astTypeArray
|
||||
para.Items = &swagger.ParameterItems{
|
||||
Type: paraType,
|
||||
Format: paraFormat,
|
||||
@ -901,49 +920,60 @@ func getparams(str string) []string {
|
||||
return r
|
||||
}
|
||||
|
||||
func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) {
|
||||
func getModel(str string) (definitionName string, m swagger.Schema, realTypes []string) {
|
||||
strs := strings.Split(str, ".")
|
||||
objectname = strs[len(strs)-1]
|
||||
packageName := ""
|
||||
m.Type = "object"
|
||||
// strs = [packageName].[objectName]
|
||||
packageName := strs[0]
|
||||
objectname := strs[len(strs)-1]
|
||||
|
||||
// Default all swagger schemas to object, if no other type is found
|
||||
m.Type = astTypeObject
|
||||
|
||||
L:
|
||||
for _, pkg := range astPkgs {
|
||||
if strs[0] == pkg.Name {
|
||||
for _, fl := range pkg.Files {
|
||||
for k, d := range fl.Scope.Objects {
|
||||
if d.Kind == ast.Typ {
|
||||
if k != objectname {
|
||||
// Still searching for the right object
|
||||
continue
|
||||
}
|
||||
packageName = pkg.Name
|
||||
parseObject(d, k, &m, &realTypes, astPkgs, pkg.Name)
|
||||
parseObject(d, k, &m, &realTypes, astPkgs, packageName)
|
||||
|
||||
// When we've found the correct object, we can stop searching
|
||||
break L
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m.Title == "" {
|
||||
beeLogger.Log.Warnf("Cannot find the object: %s", str)
|
||||
// Don't log when error has already been logged
|
||||
if _, found := rootapi.Definitions[str]; !found {
|
||||
beeLogger.Log.Warnf("Cannot find the object: %s", str)
|
||||
}
|
||||
m.Title = objectname
|
||||
// TODO remove when all type have been supported
|
||||
//os.Exit(1)
|
||||
}
|
||||
if len(rootapi.Definitions) == 0 {
|
||||
rootapi.Definitions = make(map[string]swagger.Schema)
|
||||
}
|
||||
objectname = packageName + "." + objectname
|
||||
rootapi.Definitions[objectname] = m
|
||||
return
|
||||
rootapi.Definitions[str] = m
|
||||
return str, m, realTypes
|
||||
}
|
||||
|
||||
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs []*ast.Package, packageName string) {
|
||||
ts, ok := d.Decl.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d)
|
||||
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v", d)
|
||||
}
|
||||
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
|
||||
// TODO support other types, such as `MapType`, `InterfaceType` etc...
|
||||
switch t := ts.Type.(type) {
|
||||
case *ast.ArrayType:
|
||||
m.Title = k
|
||||
m.Type = "array"
|
||||
m.Type = astTypeArray
|
||||
if isBasicType(fmt.Sprint(t.Elt)) {
|
||||
typeFormat := strings.Split(basicTypes[fmt.Sprint(t.Elt)], ":")
|
||||
m.Format = typeFormat[0]
|
||||
@ -970,8 +1000,8 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
|
||||
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
|
||||
basicType = object
|
||||
}
|
||||
if k, ok := basicTypes[basicType]; ok {
|
||||
typeFormat := strings.Split(k, ":")
|
||||
if t, ok := basicTypes[basicType]; ok {
|
||||
typeFormat := strings.Split(t, ":")
|
||||
m.Type = typeFormat[0]
|
||||
m.Format = typeFormat[1]
|
||||
}
|
||||
@ -983,7 +1013,7 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
|
||||
if obj.Kind == ast.Con {
|
||||
vs, ok := obj.Decl.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
beeLogger.Log.Fatalf("Unknown type without ValueSpec: %v\n", vs)
|
||||
beeLogger.Log.Fatalf("Unknown type without ValueSpec: %v", vs)
|
||||
}
|
||||
|
||||
ti, ok := vs.Type.(*ast.Ident)
|
||||
@ -1000,7 +1030,7 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
|
||||
for i, val := range vs.Values {
|
||||
v, ok := val.(*ast.BasicLit)
|
||||
if !ok {
|
||||
beeLogger.Log.Warnf("Unknown type without BasicLit: %v\n", v)
|
||||
beeLogger.Log.Warnf("Unknown type without BasicLit: %v", v)
|
||||
continue
|
||||
}
|
||||
enums[int(val.Pos())] = fmt.Sprintf("%s = %s", vs.Names[i].Name, v.Value)
|
||||
@ -1008,14 +1038,14 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
|
||||
case token.INT:
|
||||
vv, err := strconv.Atoi(v.Value)
|
||||
if err != nil {
|
||||
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v\n", v.Value)
|
||||
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v", v.Value)
|
||||
continue
|
||||
}
|
||||
enumValues[int(val.Pos())] = vv
|
||||
case token.FLOAT:
|
||||
vv, err := strconv.ParseFloat(v.Value, 64)
|
||||
if err != nil {
|
||||
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v\n", v.Value)
|
||||
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v", v.Value)
|
||||
continue
|
||||
}
|
||||
enumValues[int(val.Pos())] = vv
|
||||
@ -1050,7 +1080,7 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
|
||||
m.Properties = make(map[string]swagger.Propertie)
|
||||
for _, field := range st.Fields.List {
|
||||
isSlice, realType, sType := typeAnalyser(field)
|
||||
if (isSlice && isBasicType(realType)) || sType == "object" {
|
||||
if (isSlice && isBasicType(realType)) || sType == astTypeObject {
|
||||
if len(strings.Split(realType, " ")) > 1 {
|
||||
realType = strings.Replace(realType, " ", ".", -1)
|
||||
realType = strings.Replace(realType, "&", "", -1)
|
||||
@ -1064,9 +1094,9 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
|
||||
mp := swagger.Propertie{}
|
||||
isObject := false
|
||||
if isSlice {
|
||||
mp.Type = "array"
|
||||
if sType, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok {
|
||||
typeFormat := strings.Split(sType, ":")
|
||||
mp.Type = astTypeArray
|
||||
if t, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok {
|
||||
typeFormat := strings.Split(t, ":")
|
||||
mp.Items = &swagger.Propertie{
|
||||
Type: typeFormat[0],
|
||||
Format: typeFormat[1],
|
||||
@ -1077,14 +1107,14 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if sType == "object" {
|
||||
if sType == astTypeObject {
|
||||
isObject = true
|
||||
mp.Ref = "#/definitions/" + realType
|
||||
} else if isBasicType(realType) {
|
||||
typeFormat := strings.Split(sType, ":")
|
||||
mp.Type = typeFormat[0]
|
||||
mp.Format = typeFormat[1]
|
||||
} else if realType == "map" {
|
||||
} else if realType == astTypeMap {
|
||||
typeFormat := strings.Split(sType, ":")
|
||||
mp.AdditionalProperties = &swagger.Propertie{
|
||||
Type: typeFormat[0],
|
||||
@ -1120,7 +1150,6 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
|
||||
}
|
||||
|
||||
tag := stag.Get("json")
|
||||
|
||||
if tag != "" {
|
||||
tagValues = strings.Split(tag, ",")
|
||||
}
|
||||
@ -1202,12 +1231,12 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
|
||||
return true, fmt.Sprintf("[]%v", arr.Elt), basicTypes[fmt.Sprint(arr.Elt)]
|
||||
}
|
||||
if mp, ok := arr.Elt.(*ast.MapType); ok {
|
||||
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), "object"
|
||||
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), astTypeObject
|
||||
}
|
||||
if star, ok := arr.Elt.(*ast.StarExpr); ok {
|
||||
return true, fmt.Sprint(star.X), "object"
|
||||
return true, fmt.Sprint(star.X), astTypeObject
|
||||
}
|
||||
return true, fmt.Sprint(arr.Elt), "object"
|
||||
return true, fmt.Sprint(arr.Elt), astTypeObject
|
||||
}
|
||||
switch t := f.Type.(type) {
|
||||
case *ast.StarExpr:
|
||||
@ -1218,13 +1247,13 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
|
||||
if k, ok := basicTypes[basicType]; ok {
|
||||
return false, basicType, k
|
||||
}
|
||||
return false, basicType, "object"
|
||||
return false, basicType, astTypeObject
|
||||
case *ast.MapType:
|
||||
val := fmt.Sprintf("%v", t.Value)
|
||||
if isBasicType(val) {
|
||||
return false, "map", basicTypes[val]
|
||||
return false, astTypeMap, basicTypes[val]
|
||||
}
|
||||
return false, val, "object"
|
||||
return false, val, astTypeObject
|
||||
}
|
||||
basicType := fmt.Sprint(f.Type)
|
||||
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
|
||||
@ -1233,7 +1262,7 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
|
||||
if k, ok := basicTypes[basicType]; ok {
|
||||
return false, basicType, k
|
||||
}
|
||||
return false, basicType, "object"
|
||||
return false, basicType, astTypeObject
|
||||
}
|
||||
|
||||
func isBasicType(Type string) bool {
|
||||
@ -1247,7 +1276,7 @@ func isBasicType(Type string) bool {
|
||||
func appendModels(pkgpath, controllerName string, realTypes []string) {
|
||||
for _, realType := range realTypes {
|
||||
if realType != "" && !isBasicType(strings.TrimLeft(realType, "[]")) &&
|
||||
!strings.HasPrefix(realType, "map") && !strings.HasPrefix(realType, "&") {
|
||||
!strings.HasPrefix(realType, astTypeMap) && !strings.HasPrefix(realType, "&") {
|
||||
if _, ok := modelsList[pkgpath+controllerName][realType]; ok {
|
||||
continue
|
||||
}
|
||||
|
13
go.mod
Normal file
13
go.mod
Normal file
@ -0,0 +1,13 @@
|
||||
module github.com/beego/bee
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/astaxie/beego v1.12.1
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-delve/delve v1.4.1
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/lib/pq v1.7.0
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
)
|
88
go.sum
Normal file
88
go.sum
Normal file
@ -0,0 +1,88 @@
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM=
|
||||
github.com/astaxie/beego v1.12.1 h1:dfpuoxpzLVgclveAXe4PyNKqkzgm5zF4tgF2B3kkM2I=
|
||||
github.com/astaxie/beego v1.12.1/go.mod h1:kPBWpSANNbSdIqOc8SUL9h+1oyBMZhROeYsXQDbidWQ=
|
||||
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
|
||||
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8=
|
||||
github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
|
||||
github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
|
||||
github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
|
||||
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-delve/delve v1.4.1 h1:kZs0umEv+VKnK84kY9/ZXWrakdLTeRTyYjFdgLelZCQ=
|
||||
github.com/go-delve/delve v1.4.1/go.mod h1:vmy6iObn7zg8FQ5KOCIe6TruMNsqpoZO8uMiRea+97k=
|
||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b h1:8uaXtUkxiy+T/zdLWuxa/PG4so0TPZDZfafFNNSaptE=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
|
||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
|
||||
go.starlark.net v0.0.0-20190702223751-32f345186213 h1:lkYv5AKwvvduv5XWP6szk/bvvgO6aDeUujhZQXIFTes=
|
||||
go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI=
|
||||
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
@ -328,7 +328,7 @@ func CheckEnv(appname string) (apppath, packpath string, err error) {
|
||||
apppath = filepath.Join(gosrcpath, appname)
|
||||
|
||||
if _, e := os.Stat(apppath); !os.IsNotExist(e) {
|
||||
err = fmt.Errorf("Cannot create application without removing '%s' first", apppath)
|
||||
err = fmt.Errorf("cannot create application without removing '%s' first", apppath)
|
||||
beeLogger.Log.Errorf("Path '%s' already exists", apppath)
|
||||
return
|
||||
}
|
||||
|
174
vendor/github.com/astaxie/beego/swagger/swagger.go
generated
vendored
174
vendor/github.com/astaxie/beego/swagger/swagger.go
generated
vendored
@ -1,174 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Swagger™ is a project used to describe and document RESTful APIs.
|
||||
//
|
||||
// The Swagger specification defines a set of files required to describe such an API. These files can then be used by the Swagger-UI project to display the API and Swagger-Codegen to generate clients in various languages. Additional utilities can also take advantage of the resulting files, such as testing tools.
|
||||
// Now in version 2.0, Swagger is more enabling than ever. And it's 100% open source software.
|
||||
|
||||
// Package swagger struct definition
|
||||
package swagger
|
||||
|
||||
// Swagger list the resource
|
||||
type Swagger struct {
|
||||
SwaggerVersion string `json:"swagger,omitempty" yaml:"swagger,omitempty"`
|
||||
Infos Information `json:"info" yaml:"info"`
|
||||
Host string `json:"host,omitempty" yaml:"host,omitempty"`
|
||||
BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"`
|
||||
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
|
||||
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
|
||||
Paths map[string]*Item `json:"paths" yaml:"paths"`
|
||||
Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"`
|
||||
SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"`
|
||||
Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
|
||||
Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
}
|
||||
|
||||
// Information Provides metadata about the API. The metadata can be used by the clients if needed.
|
||||
type Information struct {
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty"`
|
||||
TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
|
||||
|
||||
Contact Contact `json:"contact,omitempty" yaml:"contact,omitempty"`
|
||||
License *License `json:"license,omitempty" yaml:"license,omitempty"`
|
||||
}
|
||||
|
||||
// Contact information for the exposed API.
|
||||
type Contact struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
EMail string `json:"email,omitempty" yaml:"email,omitempty"`
|
||||
}
|
||||
|
||||
// License information for the exposed API.
|
||||
type License struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
}
|
||||
|
||||
// Item Describes the operations available on a single path.
|
||||
type Item struct {
|
||||
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
|
||||
Get *Operation `json:"get,omitempty" yaml:"get,omitempty"`
|
||||
Put *Operation `json:"put,omitempty" yaml:"put,omitempty"`
|
||||
Post *Operation `json:"post,omitempty" yaml:"post,omitempty"`
|
||||
Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"`
|
||||
Options *Operation `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
Head *Operation `json:"head,omitempty" yaml:"head,omitempty"`
|
||||
Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||
}
|
||||
|
||||
// Operation Describes a single API operation on a path.
|
||||
type Operation struct {
|
||||
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
|
||||
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
|
||||
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
|
||||
Parameters []Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||
Responses map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"`
|
||||
Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
}
|
||||
|
||||
// Parameter Describes a single operation parameter.
|
||||
type Parameter struct {
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||
Items *ParameterItems `json:"items,omitempty" yaml:"items,omitempty"`
|
||||
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
}
|
||||
|
||||
// ParameterItems A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body".
|
||||
// http://swagger.io/specification/#itemsObject
|
||||
type ParameterItems struct {
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||
Items []*ParameterItems `json:"items,omitempty" yaml:"items,omitempty"` //Required if type is "array". Describes the type of items in the array.
|
||||
CollectionFormat string `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"`
|
||||
Default string `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
}
|
||||
|
||||
// Schema Object allows the definition of input and output data types.
|
||||
type Schema struct {
|
||||
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Items *Schema `json:"items,omitempty" yaml:"items,omitempty"`
|
||||
Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||
Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
|
||||
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
|
||||
}
|
||||
|
||||
// Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification
|
||||
type Propertie struct {
|
||||
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
|
||||
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
|
||||
Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||
Items *Propertie `json:"items,omitempty" yaml:"items,omitempty"`
|
||||
AdditionalProperties *Propertie `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
|
||||
}
|
||||
|
||||
// Response as they are returned from executing this operation.
|
||||
type Response struct {
|
||||
Description string `json:"description" yaml:"description"`
|
||||
Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
|
||||
}
|
||||
|
||||
// Security Allows the definition of a security scheme that can be used by the operations
|
||||
type Security struct {
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"` // Valid values are "basic", "apiKey" or "oauth2".
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"` // Valid values are "query" or "header".
|
||||
Flow string `json:"flow,omitempty" yaml:"flow,omitempty"` // Valid values are "implicit", "password", "application" or "accessCode".
|
||||
AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
|
||||
TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
|
||||
Scopes map[string]string `json:"scopes,omitempty" yaml:"scopes,omitempty"` // The available scopes for the OAuth2 security scheme.
|
||||
}
|
||||
|
||||
// Tag Allows adding meta data to a single tag that is used by the Operation Object
|
||||
type Tag struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
}
|
||||
|
||||
// ExternalDocs include Additional external documentation
|
||||
type ExternalDocs struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
}
|
25
vendor/github.com/astaxie/beego/utils/caller.go
generated
vendored
25
vendor/github.com/astaxie/beego/utils/caller.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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 utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// GetFuncName get function name
|
||||
func GetFuncName(i interface{}) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
||||
}
|
478
vendor/github.com/astaxie/beego/utils/debug.go
generated
vendored
478
vendor/github.com/astaxie/beego/utils/debug.go
generated
vendored
@ -1,478 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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 utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
dunno = []byte("???")
|
||||
centerDot = []byte("·")
|
||||
dot = []byte(".")
|
||||
)
|
||||
|
||||
type pointerInfo struct {
|
||||
prev *pointerInfo
|
||||
n int
|
||||
addr uintptr
|
||||
pos int
|
||||
used []int
|
||||
}
|
||||
|
||||
// Display print the data in console
|
||||
func Display(data ...interface{}) {
|
||||
display(true, data...)
|
||||
}
|
||||
|
||||
// GetDisplayString return data print string
|
||||
func GetDisplayString(data ...interface{}) string {
|
||||
return display(false, data...)
|
||||
}
|
||||
|
||||
func display(displayed bool, data ...interface{}) string {
|
||||
var pc, file, line, ok = runtime.Caller(2)
|
||||
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
var buf = new(bytes.Buffer)
|
||||
|
||||
fmt.Fprintf(buf, "[Debug] at %s() [%s:%d]\n", function(pc), file, line)
|
||||
|
||||
fmt.Fprintf(buf, "\n[Variables]\n")
|
||||
|
||||
for i := 0; i < len(data); i += 2 {
|
||||
var output = fomateinfo(len(data[i].(string))+3, data[i+1])
|
||||
fmt.Fprintf(buf, "%s = %s", data[i], output)
|
||||
}
|
||||
|
||||
if displayed {
|
||||
log.Print(buf)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// return data dump and format bytes
|
||||
func fomateinfo(headlen int, data ...interface{}) []byte {
|
||||
var buf = new(bytes.Buffer)
|
||||
|
||||
if len(data) > 1 {
|
||||
fmt.Fprint(buf, " ")
|
||||
|
||||
fmt.Fprint(buf, "[")
|
||||
|
||||
fmt.Fprintln(buf)
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
var buf2 = new(bytes.Buffer)
|
||||
var pointers *pointerInfo
|
||||
var interfaces = make([]reflect.Value, 0, 10)
|
||||
|
||||
printKeyValue(buf2, reflect.ValueOf(v), &pointers, &interfaces, nil, true, " ", 1)
|
||||
|
||||
if k < len(data)-1 {
|
||||
fmt.Fprint(buf2, ", ")
|
||||
}
|
||||
|
||||
fmt.Fprintln(buf2)
|
||||
|
||||
buf.Write(buf2.Bytes())
|
||||
}
|
||||
|
||||
if len(data) > 1 {
|
||||
fmt.Fprintln(buf)
|
||||
|
||||
fmt.Fprint(buf, " ")
|
||||
|
||||
fmt.Fprint(buf, "]")
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// check data is golang basic type
|
||||
func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, interfaces *[]reflect.Value) bool {
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return true
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return true
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Chan:
|
||||
return true
|
||||
case reflect.Invalid:
|
||||
return true
|
||||
case reflect.Interface:
|
||||
for _, in := range *interfaces {
|
||||
if reflect.DeepEqual(in, val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case reflect.UnsafePointer:
|
||||
if val.IsNil() {
|
||||
return true
|
||||
}
|
||||
|
||||
var elem = val.Elem()
|
||||
|
||||
if isSimpleType(elem, elem.Kind(), pointers, interfaces) {
|
||||
return true
|
||||
}
|
||||
|
||||
var addr = val.Elem().UnsafeAddr()
|
||||
|
||||
for p := *pointers; p != nil; p = p.prev {
|
||||
if addr == p.addr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// dump value
|
||||
func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) {
|
||||
var t = val.Kind()
|
||||
|
||||
switch t {
|
||||
case reflect.Bool:
|
||||
fmt.Fprint(buf, val.Bool())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
fmt.Fprint(buf, val.Int())
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
|
||||
fmt.Fprint(buf, val.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fmt.Fprint(buf, val.Float())
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
fmt.Fprint(buf, val.Complex())
|
||||
case reflect.UnsafePointer:
|
||||
fmt.Fprintf(buf, "unsafe.Pointer(0x%X)", val.Pointer())
|
||||
case reflect.Ptr:
|
||||
if val.IsNil() {
|
||||
fmt.Fprint(buf, "nil")
|
||||
return
|
||||
}
|
||||
|
||||
var addr = val.Elem().UnsafeAddr()
|
||||
|
||||
for p := *pointers; p != nil; p = p.prev {
|
||||
if addr == p.addr {
|
||||
p.used = append(p.used, buf.Len())
|
||||
fmt.Fprintf(buf, "0x%X", addr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
*pointers = &pointerInfo{
|
||||
prev: *pointers,
|
||||
addr: addr,
|
||||
pos: buf.Len(),
|
||||
used: make([]int, 0),
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, "&")
|
||||
|
||||
printKeyValue(buf, val.Elem(), pointers, interfaces, structFilter, formatOutput, indent, level)
|
||||
case reflect.String:
|
||||
fmt.Fprint(buf, "\"", val.String(), "\"")
|
||||
case reflect.Interface:
|
||||
var value = val.Elem()
|
||||
|
||||
if !value.IsValid() {
|
||||
fmt.Fprint(buf, "nil")
|
||||
} else {
|
||||
for _, in := range *interfaces {
|
||||
if reflect.DeepEqual(in, val) {
|
||||
fmt.Fprint(buf, "repeat")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
*interfaces = append(*interfaces, val)
|
||||
|
||||
printKeyValue(buf, value, pointers, interfaces, structFilter, formatOutput, indent, level+1)
|
||||
}
|
||||
case reflect.Struct:
|
||||
var t = val.Type()
|
||||
|
||||
fmt.Fprint(buf, t)
|
||||
fmt.Fprint(buf, "{")
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
if formatOutput {
|
||||
fmt.Fprintln(buf)
|
||||
} else {
|
||||
fmt.Fprint(buf, " ")
|
||||
}
|
||||
|
||||
var name = t.Field(i).Name
|
||||
|
||||
if formatOutput {
|
||||
for ind := 0; ind < level; ind++ {
|
||||
fmt.Fprint(buf, indent)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, name)
|
||||
fmt.Fprint(buf, ": ")
|
||||
|
||||
if structFilter != nil && structFilter(t.String(), name) {
|
||||
fmt.Fprint(buf, "ignore")
|
||||
} else {
|
||||
printKeyValue(buf, val.Field(i), pointers, interfaces, structFilter, formatOutput, indent, level+1)
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, ",")
|
||||
}
|
||||
|
||||
if formatOutput {
|
||||
fmt.Fprintln(buf)
|
||||
|
||||
for ind := 0; ind < level-1; ind++ {
|
||||
fmt.Fprint(buf, indent)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(buf, " ")
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, "}")
|
||||
case reflect.Array, reflect.Slice:
|
||||
fmt.Fprint(buf, val.Type())
|
||||
fmt.Fprint(buf, "{")
|
||||
|
||||
var allSimple = true
|
||||
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
var elem = val.Index(i)
|
||||
|
||||
var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces)
|
||||
|
||||
if !isSimple {
|
||||
allSimple = false
|
||||
}
|
||||
|
||||
if formatOutput && !isSimple {
|
||||
fmt.Fprintln(buf)
|
||||
} else {
|
||||
fmt.Fprint(buf, " ")
|
||||
}
|
||||
|
||||
if formatOutput && !isSimple {
|
||||
for ind := 0; ind < level; ind++ {
|
||||
fmt.Fprint(buf, indent)
|
||||
}
|
||||
}
|
||||
|
||||
printKeyValue(buf, elem, pointers, interfaces, structFilter, formatOutput, indent, level+1)
|
||||
|
||||
if i != val.Len()-1 || !allSimple {
|
||||
fmt.Fprint(buf, ",")
|
||||
}
|
||||
}
|
||||
|
||||
if formatOutput && !allSimple {
|
||||
fmt.Fprintln(buf)
|
||||
|
||||
for ind := 0; ind < level-1; ind++ {
|
||||
fmt.Fprint(buf, indent)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(buf, " ")
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, "}")
|
||||
case reflect.Map:
|
||||
var t = val.Type()
|
||||
var keys = val.MapKeys()
|
||||
|
||||
fmt.Fprint(buf, t)
|
||||
fmt.Fprint(buf, "{")
|
||||
|
||||
var allSimple = true
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
var elem = val.MapIndex(keys[i])
|
||||
|
||||
var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces)
|
||||
|
||||
if !isSimple {
|
||||
allSimple = false
|
||||
}
|
||||
|
||||
if formatOutput && !isSimple {
|
||||
fmt.Fprintln(buf)
|
||||
} else {
|
||||
fmt.Fprint(buf, " ")
|
||||
}
|
||||
|
||||
if formatOutput && !isSimple {
|
||||
for ind := 0; ind <= level; ind++ {
|
||||
fmt.Fprint(buf, indent)
|
||||
}
|
||||
}
|
||||
|
||||
printKeyValue(buf, keys[i], pointers, interfaces, structFilter, formatOutput, indent, level+1)
|
||||
fmt.Fprint(buf, ": ")
|
||||
printKeyValue(buf, elem, pointers, interfaces, structFilter, formatOutput, indent, level+1)
|
||||
|
||||
if i != val.Len()-1 || !allSimple {
|
||||
fmt.Fprint(buf, ",")
|
||||
}
|
||||
}
|
||||
|
||||
if formatOutput && !allSimple {
|
||||
fmt.Fprintln(buf)
|
||||
|
||||
for ind := 0; ind < level-1; ind++ {
|
||||
fmt.Fprint(buf, indent)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(buf, " ")
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, "}")
|
||||
case reflect.Chan:
|
||||
fmt.Fprint(buf, val.Type())
|
||||
case reflect.Invalid:
|
||||
fmt.Fprint(buf, "invalid")
|
||||
default:
|
||||
fmt.Fprint(buf, "unknow")
|
||||
}
|
||||
}
|
||||
|
||||
// PrintPointerInfo dump pointer value
|
||||
func PrintPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) {
|
||||
var anyused = false
|
||||
var pointerNum = 0
|
||||
|
||||
for p := pointers; p != nil; p = p.prev {
|
||||
if len(p.used) > 0 {
|
||||
anyused = true
|
||||
}
|
||||
pointerNum++
|
||||
p.n = pointerNum
|
||||
}
|
||||
|
||||
if anyused {
|
||||
var pointerBufs = make([][]rune, pointerNum+1)
|
||||
|
||||
for i := 0; i < len(pointerBufs); i++ {
|
||||
var pointerBuf = make([]rune, buf.Len()+headlen)
|
||||
|
||||
for j := 0; j < len(pointerBuf); j++ {
|
||||
pointerBuf[j] = ' '
|
||||
}
|
||||
|
||||
pointerBufs[i] = pointerBuf
|
||||
}
|
||||
|
||||
for pn := 0; pn <= pointerNum; pn++ {
|
||||
for p := pointers; p != nil; p = p.prev {
|
||||
if len(p.used) > 0 && p.n >= pn {
|
||||
if pn == p.n {
|
||||
pointerBufs[pn][p.pos+headlen] = '└'
|
||||
|
||||
var maxpos = 0
|
||||
|
||||
for i, pos := range p.used {
|
||||
if i < len(p.used)-1 {
|
||||
pointerBufs[pn][pos+headlen] = '┴'
|
||||
} else {
|
||||
pointerBufs[pn][pos+headlen] = '┘'
|
||||
}
|
||||
|
||||
maxpos = pos
|
||||
}
|
||||
|
||||
for i := 0; i < maxpos-p.pos-1; i++ {
|
||||
if pointerBufs[pn][i+p.pos+headlen+1] == ' ' {
|
||||
pointerBufs[pn][i+p.pos+headlen+1] = '─'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pointerBufs[pn][p.pos+headlen] = '│'
|
||||
|
||||
for _, pos := range p.used {
|
||||
if pointerBufs[pn][pos+headlen] == ' ' {
|
||||
pointerBufs[pn][pos+headlen] = '│'
|
||||
} else {
|
||||
pointerBufs[pn][pos+headlen] = '┼'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(string(pointerBufs[pn]) + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stack get stack bytes
|
||||
func Stack(skip int, indent string) []byte {
|
||||
var buf = new(bytes.Buffer)
|
||||
|
||||
for i := skip; ; i++ {
|
||||
var pc, file, line, ok = runtime.Caller(i)
|
||||
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
buf.WriteString(indent)
|
||||
|
||||
fmt.Fprintf(buf, "at %s() [%s:%d]\n", function(pc), file, line)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// return the name of the function containing the PC if possible,
|
||||
func function(pc uintptr) []byte {
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
return dunno
|
||||
}
|
||||
name := []byte(fn.Name())
|
||||
// The name includes the path name to the package, which is unnecessary
|
||||
// since the file name is already included. Plus, it has center dots.
|
||||
// That is, we see
|
||||
// runtime/debug.*T·ptrmethod
|
||||
// and want
|
||||
// *T.ptrmethod
|
||||
if period := bytes.Index(name, dot); period >= 0 {
|
||||
name = name[period+1:]
|
||||
}
|
||||
name = bytes.Replace(name, centerDot, dot, -1)
|
||||
return name
|
||||
}
|
101
vendor/github.com/astaxie/beego/utils/file.go
generated
vendored
101
vendor/github.com/astaxie/beego/utils/file.go
generated
vendored
@ -1,101 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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 utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// SelfPath gets compiled executable file absolute path
|
||||
func SelfPath() string {
|
||||
path, _ := filepath.Abs(os.Args[0])
|
||||
return path
|
||||
}
|
||||
|
||||
// SelfDir gets compiled executable file directory
|
||||
func SelfDir() string {
|
||||
return filepath.Dir(SelfPath())
|
||||
}
|
||||
|
||||
// FileExists reports whether the named file or directory exists.
|
||||
func FileExists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SearchFile Search a file in paths.
|
||||
// this is often used in search config file in /etc ~/
|
||||
func SearchFile(filename string, paths ...string) (fullpath string, err error) {
|
||||
for _, path := range paths {
|
||||
if fullpath = filepath.Join(path, filename); FileExists(fullpath) {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.New(fullpath + " not found in paths")
|
||||
return
|
||||
}
|
||||
|
||||
// GrepFile like command grep -E
|
||||
// for example: GrepFile(`^hello`, "hello.txt")
|
||||
// \n is striped while read
|
||||
func GrepFile(patten string, filename string) (lines []string, err error) {
|
||||
re, err := regexp.Compile(patten)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lines = make([]string, 0)
|
||||
reader := bufio.NewReader(fd)
|
||||
prefix := ""
|
||||
isLongLine := false
|
||||
for {
|
||||
byteLine, isPrefix, er := reader.ReadLine()
|
||||
if er != nil && er != io.EOF {
|
||||
return nil, er
|
||||
}
|
||||
if er == io.EOF {
|
||||
break
|
||||
}
|
||||
line := string(byteLine)
|
||||
if isPrefix {
|
||||
prefix += line
|
||||
continue
|
||||
} else {
|
||||
isLongLine = true
|
||||
}
|
||||
|
||||
line = prefix + line
|
||||
if isLongLine {
|
||||
prefix = ""
|
||||
}
|
||||
if re.MatchString(line) {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
return lines, nil
|
||||
}
|
423
vendor/github.com/astaxie/beego/utils/mail.go
generated
vendored
423
vendor/github.com/astaxie/beego/utils/mail.go
generated
vendored
@ -1,423 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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 utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
maxLineLength = 76
|
||||
|
||||
upperhex = "0123456789ABCDEF"
|
||||
)
|
||||
|
||||
// Email is the type used for email messages
|
||||
type Email struct {
|
||||
Auth smtp.Auth
|
||||
Identity string `json:"identity"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
From string `json:"from"`
|
||||
To []string
|
||||
Bcc []string
|
||||
Cc []string
|
||||
Subject string
|
||||
Text string // Plaintext message (optional)
|
||||
HTML string // Html message (optional)
|
||||
Headers textproto.MIMEHeader
|
||||
Attachments []*Attachment
|
||||
ReadReceipt []string
|
||||
}
|
||||
|
||||
// Attachment is a struct representing an email attachment.
|
||||
// Based on the mime/multipart.FileHeader struct, Attachment contains the name, MIMEHeader, and content of the attachment in question
|
||||
type Attachment struct {
|
||||
Filename string
|
||||
Header textproto.MIMEHeader
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// NewEMail create new Email struct with config json.
|
||||
// config json is followed from Email struct fields.
|
||||
func NewEMail(config string) *Email {
|
||||
e := new(Email)
|
||||
e.Headers = textproto.MIMEHeader{}
|
||||
err := json.Unmarshal([]byte(config), e)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Bytes Make all send information to byte
|
||||
func (e *Email) Bytes() ([]byte, error) {
|
||||
buff := &bytes.Buffer{}
|
||||
w := multipart.NewWriter(buff)
|
||||
// Set the appropriate headers (overwriting any conflicts)
|
||||
// Leave out Bcc (only included in envelope headers)
|
||||
e.Headers.Set("To", strings.Join(e.To, ","))
|
||||
if e.Cc != nil {
|
||||
e.Headers.Set("Cc", strings.Join(e.Cc, ","))
|
||||
}
|
||||
e.Headers.Set("From", e.From)
|
||||
e.Headers.Set("Subject", e.Subject)
|
||||
if len(e.ReadReceipt) != 0 {
|
||||
e.Headers.Set("Disposition-Notification-To", strings.Join(e.ReadReceipt, ","))
|
||||
}
|
||||
e.Headers.Set("MIME-Version", "1.0")
|
||||
|
||||
// Write the envelope headers (including any custom headers)
|
||||
if err := headerToBytes(buff, e.Headers); err != nil {
|
||||
return nil, fmt.Errorf("Failed to render message headers: %s", err)
|
||||
}
|
||||
|
||||
e.Headers.Set("Content-Type", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))
|
||||
fmt.Fprintf(buff, "%s:", "Content-Type")
|
||||
fmt.Fprintf(buff, " %s\r\n", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))
|
||||
|
||||
// Start the multipart/mixed part
|
||||
fmt.Fprintf(buff, "--%s\r\n", w.Boundary())
|
||||
header := textproto.MIMEHeader{}
|
||||
// Check to see if there is a Text or HTML field
|
||||
if e.Text != "" || e.HTML != "" {
|
||||
subWriter := multipart.NewWriter(buff)
|
||||
// Create the multipart alternative part
|
||||
header.Set("Content-Type", fmt.Sprintf("multipart/alternative;\r\n boundary=%s\r\n", subWriter.Boundary()))
|
||||
// Write the header
|
||||
if err := headerToBytes(buff, header); err != nil {
|
||||
return nil, fmt.Errorf("Failed to render multipart message headers: %s", err)
|
||||
}
|
||||
// Create the body sections
|
||||
if e.Text != "" {
|
||||
header.Set("Content-Type", fmt.Sprintf("text/plain; charset=UTF-8"))
|
||||
header.Set("Content-Transfer-Encoding", "quoted-printable")
|
||||
if _, err := subWriter.CreatePart(header); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Write the text
|
||||
if err := quotePrintEncode(buff, e.Text); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if e.HTML != "" {
|
||||
header.Set("Content-Type", fmt.Sprintf("text/html; charset=UTF-8"))
|
||||
header.Set("Content-Transfer-Encoding", "quoted-printable")
|
||||
if _, err := subWriter.CreatePart(header); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Write the text
|
||||
if err := quotePrintEncode(buff, e.HTML); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := subWriter.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Create attachment part, if necessary
|
||||
for _, a := range e.Attachments {
|
||||
ap, err := w.CreatePart(a.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Write the base64Wrapped content to the part
|
||||
base64Wrap(ap, a.Content)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buff.Bytes(), nil
|
||||
}
|
||||
|
||||
// AttachFile Add attach file to the send mail
|
||||
func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
|
||||
if len(args) < 1 && len(args) > 2 {
|
||||
err = errors.New("Must specify a file name and number of parameters can not exceed at least two")
|
||||
return
|
||||
}
|
||||
filename := args[0]
|
||||
id := ""
|
||||
if len(args) > 1 {
|
||||
id = args[1]
|
||||
}
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ct := mime.TypeByExtension(filepath.Ext(filename))
|
||||
basename := path.Base(filename)
|
||||
return e.Attach(f, basename, ct, id)
|
||||
}
|
||||
|
||||
// Attach is used to attach content from an io.Reader to the email.
|
||||
// Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type.
|
||||
func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) {
|
||||
if len(args) < 1 && len(args) > 2 {
|
||||
err = errors.New("Must specify the file type and number of parameters can not exceed at least two")
|
||||
return
|
||||
}
|
||||
c := args[0] //Content-Type
|
||||
id := ""
|
||||
if len(args) > 1 {
|
||||
id = args[1] //Content-ID
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
if _, err = io.Copy(&buffer, r); err != nil {
|
||||
return
|
||||
}
|
||||
at := &Attachment{
|
||||
Filename: filename,
|
||||
Header: textproto.MIMEHeader{},
|
||||
Content: buffer.Bytes(),
|
||||
}
|
||||
// Get the Content-Type to be used in the MIMEHeader
|
||||
if c != "" {
|
||||
at.Header.Set("Content-Type", c)
|
||||
} else {
|
||||
// If the Content-Type is blank, set the Content-Type to "application/octet-stream"
|
||||
at.Header.Set("Content-Type", "application/octet-stream")
|
||||
}
|
||||
if id != "" {
|
||||
at.Header.Set("Content-Disposition", fmt.Sprintf("inline;\r\n filename=\"%s\"", filename))
|
||||
at.Header.Set("Content-ID", fmt.Sprintf("<%s>", id))
|
||||
} else {
|
||||
at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
|
||||
}
|
||||
at.Header.Set("Content-Transfer-Encoding", "base64")
|
||||
e.Attachments = append(e.Attachments, at)
|
||||
return at, nil
|
||||
}
|
||||
|
||||
// Send will send out the mail
|
||||
func (e *Email) Send() error {
|
||||
if e.Auth == nil {
|
||||
e.Auth = smtp.PlainAuth(e.Identity, e.Username, e.Password, e.Host)
|
||||
}
|
||||
// Merge the To, Cc, and Bcc fields
|
||||
to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
|
||||
to = append(append(append(to, e.To...), e.Cc...), e.Bcc...)
|
||||
// Check to make sure there is at least one recipient and one "From" address
|
||||
if len(to) == 0 {
|
||||
return errors.New("Must specify at least one To address")
|
||||
}
|
||||
|
||||
// Use the username if no From is provided
|
||||
if len(e.From) == 0 {
|
||||
e.From = e.Username
|
||||
}
|
||||
|
||||
from, err := mail.ParseAddress(e.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// use mail's RFC 2047 to encode any string
|
||||
e.Subject = qEncode("utf-8", e.Subject)
|
||||
|
||||
raw, err := e.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return smtp.SendMail(e.Host+":"+strconv.Itoa(e.Port), e.Auth, from.Address, to, raw)
|
||||
}
|
||||
|
||||
// quotePrintEncode writes the quoted-printable text to the IO Writer (according to RFC 2045)
|
||||
func quotePrintEncode(w io.Writer, s string) error {
|
||||
var buf [3]byte
|
||||
mc := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
// We're assuming Unix style text formats as input (LF line break), and
|
||||
// quoted-printble uses CRLF line breaks. (Literal CRs will become
|
||||
// "=0D", but probably shouldn't be there to begin with!)
|
||||
if c == '\n' {
|
||||
io.WriteString(w, "\r\n")
|
||||
mc = 0
|
||||
continue
|
||||
}
|
||||
|
||||
var nextOut []byte
|
||||
if isPrintable(c) {
|
||||
nextOut = append(buf[:0], c)
|
||||
} else {
|
||||
nextOut = buf[:]
|
||||
qpEscape(nextOut, c)
|
||||
}
|
||||
|
||||
// Add a soft line break if the next (encoded) byte would push this line
|
||||
// to or past the limit.
|
||||
if mc+len(nextOut) >= maxLineLength {
|
||||
if _, err := io.WriteString(w, "=\r\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
mc = 0
|
||||
}
|
||||
|
||||
if _, err := w.Write(nextOut); err != nil {
|
||||
return err
|
||||
}
|
||||
mc += len(nextOut)
|
||||
}
|
||||
// No trailing end-of-line?? Soft line break, then. TODO: is this sane?
|
||||
if mc > 0 {
|
||||
io.WriteString(w, "=\r\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isPrintable returns true if the rune given is "printable" according to RFC 2045, false otherwise
|
||||
func isPrintable(c byte) bool {
|
||||
return (c >= '!' && c <= '<') || (c >= '>' && c <= '~') || (c == ' ' || c == '\n' || c == '\t')
|
||||
}
|
||||
|
||||
// qpEscape is a helper function for quotePrintEncode which escapes a
|
||||
// non-printable byte. Expects len(dest) == 3.
|
||||
func qpEscape(dest []byte, c byte) {
|
||||
const nums = "0123456789ABCDEF"
|
||||
dest[0] = '='
|
||||
dest[1] = nums[(c&0xf0)>>4]
|
||||
dest[2] = nums[(c & 0xf)]
|
||||
}
|
||||
|
||||
// headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
|
||||
func headerToBytes(w io.Writer, t textproto.MIMEHeader) error {
|
||||
for k, v := range t {
|
||||
// Write the header key
|
||||
_, err := fmt.Fprintf(w, "%s:", k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Write each value in the header
|
||||
for _, c := range v {
|
||||
_, err := fmt.Fprintf(w, " %s\r\n", c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// base64Wrap encodes the attachment content, and wraps it according to RFC 2045 standards (every 76 chars)
|
||||
// The output is then written to the specified io.Writer
|
||||
func base64Wrap(w io.Writer, b []byte) {
|
||||
// 57 raw bytes per 76-byte base64 line.
|
||||
const maxRaw = 57
|
||||
// Buffer for each line, including trailing CRLF.
|
||||
var buffer [maxLineLength + len("\r\n")]byte
|
||||
copy(buffer[maxLineLength:], "\r\n")
|
||||
// Process raw chunks until there's no longer enough to fill a line.
|
||||
for len(b) >= maxRaw {
|
||||
base64.StdEncoding.Encode(buffer[:], b[:maxRaw])
|
||||
w.Write(buffer[:])
|
||||
b = b[maxRaw:]
|
||||
}
|
||||
// Handle the last chunk of bytes.
|
||||
if len(b) > 0 {
|
||||
out := buffer[:base64.StdEncoding.EncodedLen(len(b))]
|
||||
base64.StdEncoding.Encode(out, b)
|
||||
out = append(out, "\r\n"...)
|
||||
w.Write(out)
|
||||
}
|
||||
}
|
||||
|
||||
// Encode returns the encoded-word form of s. If s is ASCII without special
|
||||
// characters, it is returned unchanged. The provided charset is the IANA
|
||||
// charset name of s. It is case insensitive.
|
||||
// RFC 2047 encoded-word
|
||||
func qEncode(charset, s string) string {
|
||||
if !needsEncoding(s) {
|
||||
return s
|
||||
}
|
||||
return encodeWord(charset, s)
|
||||
}
|
||||
|
||||
func needsEncoding(s string) bool {
|
||||
for _, b := range s {
|
||||
if (b < ' ' || b > '~') && b != '\t' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// encodeWord encodes a string into an encoded-word.
|
||||
func encodeWord(charset, s string) string {
|
||||
buf := getBuffer()
|
||||
|
||||
buf.WriteString("=?")
|
||||
buf.WriteString(charset)
|
||||
buf.WriteByte('?')
|
||||
buf.WriteByte('q')
|
||||
buf.WriteByte('?')
|
||||
|
||||
enc := make([]byte, 3)
|
||||
for i := 0; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case b == ' ':
|
||||
buf.WriteByte('_')
|
||||
case b <= '~' && b >= '!' && b != '=' && b != '?' && b != '_':
|
||||
buf.WriteByte(b)
|
||||
default:
|
||||
enc[0] = '='
|
||||
enc[1] = upperhex[b>>4]
|
||||
enc[2] = upperhex[b&0x0f]
|
||||
buf.Write(enc)
|
||||
}
|
||||
}
|
||||
buf.WriteString("?=")
|
||||
|
||||
es := buf.String()
|
||||
putBuffer(buf)
|
||||
return es
|
||||
}
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
func getBuffer() *bytes.Buffer {
|
||||
return bufPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
func putBuffer(buf *bytes.Buffer) {
|
||||
if buf.Len() > 1024 {
|
||||
return
|
||||
}
|
||||
buf.Reset()
|
||||
bufPool.Put(buf)
|
||||
}
|
44
vendor/github.com/astaxie/beego/utils/rand.go
generated
vendored
44
vendor/github.com/astaxie/beego/utils/rand.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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 utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
r "math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
var alphaNum = []byte(`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`)
|
||||
|
||||
// RandomCreateBytes generate random []byte by specify chars.
|
||||
func RandomCreateBytes(n int, alphabets ...byte) []byte {
|
||||
if len(alphabets) == 0 {
|
||||
alphabets = alphaNum
|
||||
}
|
||||
var bytes = make([]byte, n)
|
||||
var randBy bool
|
||||
if num, err := rand.Read(bytes); num != n || err != nil {
|
||||
r.Seed(time.Now().UnixNano())
|
||||
randBy = true
|
||||
}
|
||||
for i, b := range bytes {
|
||||
if randBy {
|
||||
bytes[i] = alphabets[r.Intn(len(alphabets))]
|
||||
} else {
|
||||
bytes[i] = alphabets[b%byte(len(alphabets))]
|
||||
}
|
||||
}
|
||||
return bytes
|
||||
}
|
91
vendor/github.com/astaxie/beego/utils/safemap.go
generated
vendored
91
vendor/github.com/astaxie/beego/utils/safemap.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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 utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// BeeMap is a map with lock
|
||||
type BeeMap struct {
|
||||
lock *sync.RWMutex
|
||||
bm map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// NewBeeMap return new safemap
|
||||
func NewBeeMap() *BeeMap {
|
||||
return &BeeMap{
|
||||
lock: new(sync.RWMutex),
|
||||
bm: make(map[interface{}]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Get from maps return the k's value
|
||||
func (m *BeeMap) Get(k interface{}) interface{} {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
if val, ok := m.bm[k]; ok {
|
||||
return val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set Maps the given key and value. Returns false
|
||||
// if the key is already in the map and changes nothing.
|
||||
func (m *BeeMap) Set(k interface{}, v interface{}) bool {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
if val, ok := m.bm[k]; !ok {
|
||||
m.bm[k] = v
|
||||
} else if val != v {
|
||||
m.bm[k] = v
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Check Returns true if k is exist in the map.
|
||||
func (m *BeeMap) Check(k interface{}) bool {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
_, ok := m.bm[k]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Delete the given key and value.
|
||||
func (m *BeeMap) Delete(k interface{}) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
delete(m.bm, k)
|
||||
}
|
||||
|
||||
// Items returns all items in safemap.
|
||||
func (m *BeeMap) Items() map[interface{}]interface{} {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
r := make(map[interface{}]interface{})
|
||||
for k, v := range m.bm {
|
||||
r[k] = v
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Count returns the number of items within the map.
|
||||
func (m *BeeMap) Count() int {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
return len(m.bm)
|
||||
}
|
170
vendor/github.com/astaxie/beego/utils/slice.go
generated
vendored
170
vendor/github.com/astaxie/beego/utils/slice.go
generated
vendored
@ -1,170 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed 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 utils
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type reducetype func(interface{}) interface{}
|
||||
type filtertype func(interface{}) bool
|
||||
|
||||
// InSlice checks given string in string slice or not.
|
||||
func InSlice(v string, sl []string) bool {
|
||||
for _, vv := range sl {
|
||||
if vv == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InSliceIface checks given interface in interface slice.
|
||||
func InSliceIface(v interface{}, sl []interface{}) bool {
|
||||
for _, vv := range sl {
|
||||
if vv == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SliceRandList generate an int slice from min to max.
|
||||
func SliceRandList(min, max int) []int {
|
||||
if max < min {
|
||||
min, max = max, min
|
||||
}
|
||||
length := max - min + 1
|
||||
t0 := time.Now()
|
||||
rand.Seed(int64(t0.Nanosecond()))
|
||||
list := rand.Perm(length)
|
||||
for index := range list {
|
||||
list[index] += min
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// SliceMerge merges interface slices to one slice.
|
||||
func SliceMerge(slice1, slice2 []interface{}) (c []interface{}) {
|
||||
c = append(slice1, slice2...)
|
||||
return
|
||||
}
|
||||
|
||||
// SliceReduce generates a new slice after parsing every value by reduce function
|
||||
func SliceReduce(slice []interface{}, a reducetype) (dslice []interface{}) {
|
||||
for _, v := range slice {
|
||||
dslice = append(dslice, a(v))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceRand returns random one from slice.
|
||||
func SliceRand(a []interface{}) (b interface{}) {
|
||||
randnum := rand.Intn(len(a))
|
||||
b = a[randnum]
|
||||
return
|
||||
}
|
||||
|
||||
// SliceSum sums all values in int64 slice.
|
||||
func SliceSum(intslice []int64) (sum int64) {
|
||||
for _, v := range intslice {
|
||||
sum += v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceFilter generates a new slice after filter function.
|
||||
func SliceFilter(slice []interface{}, a filtertype) (ftslice []interface{}) {
|
||||
for _, v := range slice {
|
||||
if a(v) {
|
||||
ftslice = append(ftslice, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceDiff returns diff slice of slice1 - slice2.
|
||||
func SliceDiff(slice1, slice2 []interface{}) (diffslice []interface{}) {
|
||||
for _, v := range slice1 {
|
||||
if !InSliceIface(v, slice2) {
|
||||
diffslice = append(diffslice, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceIntersect returns slice that are present in all the slice1 and slice2.
|
||||
func SliceIntersect(slice1, slice2 []interface{}) (diffslice []interface{}) {
|
||||
for _, v := range slice1 {
|
||||
if InSliceIface(v, slice2) {
|
||||
diffslice = append(diffslice, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceChunk separates one slice to some sized slice.
|
||||
func SliceChunk(slice []interface{}, size int) (chunkslice [][]interface{}) {
|
||||
if size >= len(slice) {
|
||||
chunkslice = append(chunkslice, slice)
|
||||
return
|
||||
}
|
||||
end := size
|
||||
for i := 0; i <= (len(slice) - size); i += size {
|
||||
chunkslice = append(chunkslice, slice[i:end])
|
||||
end += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceRange generates a new slice from begin to end with step duration of int64 number.
|
||||
func SliceRange(start, end, step int64) (intslice []int64) {
|
||||
for i := start; i <= end; i += step {
|
||||
intslice = append(intslice, i)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SlicePad prepends size number of val into slice.
|
||||
func SlicePad(slice []interface{}, size int, val interface{}) []interface{} {
|
||||
if size <= len(slice) {
|
||||
return slice
|
||||
}
|
||||
for i := 0; i < (size - len(slice)); i++ {
|
||||
slice = append(slice, val)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// SliceUnique cleans repeated values in slice.
|
||||
func SliceUnique(slice []interface{}) (uniqueslice []interface{}) {
|
||||
for _, v := range slice {
|
||||
if !InSliceIface(v, uniqueslice) {
|
||||
uniqueslice = append(uniqueslice, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceShuffle shuffles a slice.
|
||||
func SliceShuffle(slice []interface{}) []interface{} {
|
||||
for i := 0; i < len(slice); i++ {
|
||||
a := rand.Intn(len(slice))
|
||||
b := rand.Intn(len(slice))
|
||||
slice[a], slice[b] = slice[b], slice[a]
|
||||
}
|
||||
return slice
|
||||
}
|
24
vendor/github.com/cosiner/argv/LICENSE
generated
vendored
24
vendor/github.com/cosiner/argv/LICENSE
generated
vendored
@ -1,24 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 aihui zhu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
34
vendor/github.com/cosiner/argv/argv.go
generated
vendored
34
vendor/github.com/cosiner/argv/argv.go
generated
vendored
@ -1,34 +0,0 @@
|
||||
// Package argv parse command line string into arguments array using the bash syntax.
|
||||
package argv
|
||||
|
||||
import "strings"
|
||||
|
||||
// ParseEnv parsing environment variables as key/value pair.
|
||||
//
|
||||
// Item will be ignored if one of the key and value is empty.
|
||||
func ParseEnv(env []string) map[string]string {
|
||||
var m map[string]string
|
||||
for _, e := range env {
|
||||
secs := strings.SplitN(e, "=", 2)
|
||||
if len(secs) == 2 {
|
||||
key := strings.TrimSpace(secs[0])
|
||||
val := strings.TrimSpace(secs[1])
|
||||
if key == "" || val == "" {
|
||||
continue
|
||||
}
|
||||
if m == nil {
|
||||
m = make(map[string]string)
|
||||
}
|
||||
m[key] = val
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Argv split cmdline string as array of argument array by the '|' character.
|
||||
//
|
||||
// The parsing rules is same as bash. The environment variable will be replaced
|
||||
// and string surround by '`' will be passed to reverse quote parser.
|
||||
func Argv(cmdline []rune, env map[string]string, reverseQuoteParser ReverseQuoteParser) ([][]string, error) {
|
||||
return NewParser(NewScanner(cmdline, env), reverseQuoteParser).Parse()
|
||||
}
|
79
vendor/github.com/cosiner/argv/cmd.go
generated
vendored
79
vendor/github.com/cosiner/argv/cmd.go
generated
vendored
@ -1,79 +0,0 @@
|
||||
package argv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Run execute cmdline string and return the output
|
||||
func Run(cmdline []rune, env map[string]string) ([]rune, error) {
|
||||
args, err := Argv(cmdline, env, Run)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmds, err := Cmds(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
err = Pipe(nil, output, cmds...)
|
||||
str := output.String()
|
||||
str = strings.TrimSpace(str)
|
||||
return []rune(str), err
|
||||
}
|
||||
|
||||
// Cmds generate exec.Cmd for each command.
|
||||
func Cmds(args [][]string) ([]*exec.Cmd, error) {
|
||||
var cmds []*exec.Cmd
|
||||
for _, argv := range args {
|
||||
if len(argv) == 0 {
|
||||
return nil, errors.New("invalid cmd")
|
||||
}
|
||||
|
||||
cmds = append(cmds, exec.Command(argv[0], argv[1:]...))
|
||||
}
|
||||
return cmds, nil
|
||||
}
|
||||
|
||||
// Pipe pipe previous command's stdout to next command's stdin, if in or
|
||||
// out is nil, it will be ignored.
|
||||
func Pipe(in io.Reader, out io.Writer, cmds ...*exec.Cmd) error {
|
||||
l := len(cmds)
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 1; i < l; i++ {
|
||||
cmds[i].Stdin, err = cmds[i-1].StdoutPipe()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if in != nil {
|
||||
cmds[0].Stdin = in
|
||||
}
|
||||
if out != nil {
|
||||
cmds[l-1].Stdout = out
|
||||
}
|
||||
for i := range cmds {
|
||||
err = cmds[i].Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range cmds {
|
||||
err = cmds[i].Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
222
vendor/github.com/cosiner/argv/parser.go
generated
vendored
222
vendor/github.com/cosiner/argv/parser.go
generated
vendored
@ -1,222 +0,0 @@
|
||||
package argv
|
||||
|
||||
import "errors"
|
||||
|
||||
type (
|
||||
// ReverseQuoteParser parse strings quoted by '`' and return it's result. Commonly,
|
||||
// it should run it os command.
|
||||
ReverseQuoteParser func([]rune, map[string]string) ([]rune, error)
|
||||
|
||||
// Parser take tokens from Scanner, and do syntax checking, and generate the splitted arguments array.
|
||||
Parser struct {
|
||||
s *Scanner
|
||||
tokbuf []Token
|
||||
r ReverseQuoteParser
|
||||
|
||||
sections [][]string
|
||||
currSection []string
|
||||
|
||||
currStrValid bool
|
||||
currStr []rune
|
||||
}
|
||||
)
|
||||
|
||||
// NewParser create a cmdline string parser.
|
||||
func NewParser(s *Scanner, r ReverseQuoteParser) *Parser {
|
||||
if r == nil {
|
||||
r = func(r []rune, env map[string]string) ([]rune, error) {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &Parser{
|
||||
s: s,
|
||||
r: r,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) nextToken() (Token, error) {
|
||||
if l := len(p.tokbuf); l > 0 {
|
||||
tok := p.tokbuf[l-1]
|
||||
p.tokbuf = p.tokbuf[:l-1]
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
return p.s.Next()
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrInvalidSyntax was reported if there is a syntax error in command line string.
|
||||
ErrInvalidSyntax = errors.New("invalid syntax")
|
||||
)
|
||||
|
||||
func (p *Parser) unreadToken(tok Token) {
|
||||
p.tokbuf = append(p.tokbuf, tok)
|
||||
}
|
||||
|
||||
// Parse split command line string into arguments array.
|
||||
//
|
||||
// EBNF:
|
||||
// Cmdline = Section [ Pipe Cmdline ]
|
||||
// Section = [Space] SpacedSection { SpacedSection }
|
||||
// SpacedSection = MultipleUnit [Space]
|
||||
// MultipleUnit = Unit {Unit}
|
||||
// Unit = String | ReverseQuote
|
||||
func (p *Parser) Parse() ([][]string, error) {
|
||||
err := p.cmdline()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.sections, nil
|
||||
}
|
||||
|
||||
func (p *Parser) cmdline() error {
|
||||
err := p.section()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.endSection()
|
||||
|
||||
tok, err := p.nextToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tok.Type == TokEOF {
|
||||
return nil
|
||||
}
|
||||
if !p.accept(tok.Type, TokPipe) {
|
||||
return ErrInvalidSyntax
|
||||
}
|
||||
return p.cmdline()
|
||||
}
|
||||
|
||||
func (p *Parser) section() error {
|
||||
leftSpace, err := p.optional(TokSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var isFirst = true
|
||||
for {
|
||||
unit, err := p.spacedSection()
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
} else {
|
||||
if err == ErrInvalidSyntax {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.appendUnit(leftSpace, unit)
|
||||
leftSpace = unit.rightSpace
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type unit struct {
|
||||
rightSpace bool
|
||||
toks []Token
|
||||
}
|
||||
|
||||
func (p *Parser) spacedSection() (u unit, err error) {
|
||||
u.toks, err = p.multipleUnit()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
u.rightSpace, err = p.optional(TokSpace)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Parser) multipleUnit() ([]Token, error) {
|
||||
var (
|
||||
toks []Token
|
||||
isFirst = true
|
||||
)
|
||||
for {
|
||||
tok, err := p.unit()
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
} else {
|
||||
if err == ErrInvalidSyntax {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toks = append(toks, tok)
|
||||
}
|
||||
return toks, nil
|
||||
}
|
||||
|
||||
func (p *Parser) unit() (Token, error) {
|
||||
tok, err := p.nextToken()
|
||||
if err != nil {
|
||||
return tok, err
|
||||
}
|
||||
if p.accept(tok.Type, TokString, TokReversequote) {
|
||||
return tok, nil
|
||||
}
|
||||
p.unreadToken(tok)
|
||||
return tok, ErrInvalidSyntax
|
||||
}
|
||||
|
||||
func (p *Parser) optional(typ TokenType) (bool, error) {
|
||||
tok, err := p.nextToken()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var ok bool
|
||||
if ok = p.accept(tok.Type, typ); !ok {
|
||||
p.unreadToken(tok)
|
||||
}
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (p *Parser) accept(t TokenType, types ...TokenType) bool {
|
||||
for _, typ := range types {
|
||||
if t == typ {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Parser) appendUnit(leftSpace bool, u unit) error {
|
||||
if leftSpace {
|
||||
p.currStr = p.currStr[:0]
|
||||
}
|
||||
for _, tok := range u.toks {
|
||||
if tok.Type == TokReversequote {
|
||||
val, err := p.r(tok.Value, p.s.envs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.currStr = append(p.currStr, val...)
|
||||
} else {
|
||||
p.currStr = append(p.currStr, tok.Value...)
|
||||
}
|
||||
}
|
||||
p.currStrValid = true
|
||||
if u.rightSpace {
|
||||
p.currSection = append(p.currSection, string(p.currStr))
|
||||
p.currStr = p.currStr[:0]
|
||||
p.currStrValid = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) endSection() {
|
||||
if p.currStrValid {
|
||||
p.currSection = append(p.currSection, string(p.currStr))
|
||||
}
|
||||
p.currStr = p.currStr[:0]
|
||||
p.currStrValid = false
|
||||
if len(p.currSection) > 0 {
|
||||
p.sections = append(p.sections, p.currSection)
|
||||
p.currSection = nil
|
||||
}
|
||||
}
|
282
vendor/github.com/cosiner/argv/scanner.go
generated
vendored
282
vendor/github.com/cosiner/argv/scanner.go
generated
vendored
@ -1,282 +0,0 @@
|
||||
package argv
|
||||
|
||||
import "unicode"
|
||||
|
||||
// Scanner is a cmdline string scanner.
|
||||
//
|
||||
// It split cmdline string to tokens: space, string, pipe, reverse quote string.
|
||||
type Scanner struct {
|
||||
env map[string]string
|
||||
|
||||
text []rune
|
||||
rpos int
|
||||
dollarBuf []rune
|
||||
}
|
||||
|
||||
// NewScanner create a scanner and init it's internal states.
|
||||
func NewScanner(text []rune, env map[string]string) *Scanner {
|
||||
return &Scanner{
|
||||
text: text,
|
||||
env: env,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) envs() map[string]string {
|
||||
return s.env
|
||||
}
|
||||
|
||||
const _RuneEOF = 0
|
||||
|
||||
func (s *Scanner) nextRune() rune {
|
||||
if s.rpos >= len(s.text) {
|
||||
return _RuneEOF
|
||||
}
|
||||
|
||||
r := s.text[s.rpos]
|
||||
s.rpos++
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *Scanner) unreadRune(r rune) {
|
||||
if r != _RuneEOF {
|
||||
s.rpos--
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) isEscapeChars(r rune) (rune, bool) {
|
||||
switch r {
|
||||
case 'a':
|
||||
return '\a', true
|
||||
case 'b':
|
||||
return '\b', true
|
||||
case 'f':
|
||||
return '\f', true
|
||||
case 'n':
|
||||
return '\n', true
|
||||
case 'r':
|
||||
return '\r', true
|
||||
case 't':
|
||||
return '\t', true
|
||||
case 'v':
|
||||
return '\v', true
|
||||
case '\\':
|
||||
return '\\', true
|
||||
case '$':
|
||||
return '$', true
|
||||
}
|
||||
return r, false
|
||||
}
|
||||
|
||||
func (s *Scanner) endEnv(r rune) bool {
|
||||
if r == '_' || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// TokenType is the type of tokens recognized by the scanner.
|
||||
type TokenType uint32
|
||||
|
||||
// Token is generated by the scanner with a type and value.
|
||||
type Token struct {
|
||||
Type TokenType
|
||||
Value []rune
|
||||
}
|
||||
|
||||
const (
|
||||
// TokString for string, single quoted string and double quoted string
|
||||
TokString TokenType = iota + 1
|
||||
// TokPipe is the '|' character
|
||||
TokPipe
|
||||
// TokReversequote is reverse quoted string
|
||||
TokReversequote
|
||||
// TokSpace represent space character sequence
|
||||
TokSpace
|
||||
// TokEOF means the input end.
|
||||
TokEOF
|
||||
)
|
||||
|
||||
func (s *Scanner) getEnv(name string) string {
|
||||
return s.env[name]
|
||||
}
|
||||
|
||||
func (s *Scanner) specialVar(r rune) (string, bool) {
|
||||
switch r {
|
||||
case '0', '*', '#', '@', '?', '$':
|
||||
v, has := s.env[string(r)]
|
||||
return v, has
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) checkDollarStart(tok *Token, r rune, from, switchTo uint8) uint8 {
|
||||
state := from
|
||||
nr := s.nextRune()
|
||||
if val, has := s.specialVar(nr); has {
|
||||
if val != "" {
|
||||
tok.Value = append(tok.Value, []rune(val)...)
|
||||
}
|
||||
} else if s.endEnv(nr) {
|
||||
tok.Value = append(tok.Value, r)
|
||||
s.unreadRune(nr)
|
||||
} else {
|
||||
state = switchTo
|
||||
s.dollarBuf = append(s.dollarBuf[:0], nr)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func (s *Scanner) checkDollarEnd(tok *Token, r rune, from, switchTo uint8) uint8 {
|
||||
var state = from
|
||||
if s.endEnv(r) {
|
||||
tok.Value = append(tok.Value, []rune(s.getEnv(string(s.dollarBuf)))...)
|
||||
state = switchTo
|
||||
s.unreadRune(r)
|
||||
} else {
|
||||
s.dollarBuf = append(s.dollarBuf, r)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// Next return next token, if it reach the end, TOK_EOF will be returned.
|
||||
//
|
||||
// Error is returned for invalid syntax such as unpaired quotes.
|
||||
func (s *Scanner) Next() (Token, error) {
|
||||
const (
|
||||
Initial = iota + 1
|
||||
Space
|
||||
ReverseQuote
|
||||
String
|
||||
StringDollar
|
||||
StringQuoteSingle
|
||||
StringQuoteDouble
|
||||
StringQuoteDoubleDollar
|
||||
)
|
||||
|
||||
var (
|
||||
tok Token
|
||||
|
||||
state uint8 = Initial
|
||||
)
|
||||
s.dollarBuf = s.dollarBuf[:0]
|
||||
for {
|
||||
r := s.nextRune()
|
||||
switch state {
|
||||
case Initial:
|
||||
switch {
|
||||
case r == _RuneEOF:
|
||||
tok.Type = TokEOF
|
||||
return tok, nil
|
||||
case r == '|':
|
||||
tok.Type = TokPipe
|
||||
return tok, nil
|
||||
case r == '`':
|
||||
state = ReverseQuote
|
||||
case unicode.IsSpace(r):
|
||||
state = Space
|
||||
s.unreadRune(r)
|
||||
default:
|
||||
state = String
|
||||
s.unreadRune(r)
|
||||
}
|
||||
case Space:
|
||||
if r == _RuneEOF || !unicode.IsSpace(r) {
|
||||
s.unreadRune(r)
|
||||
tok.Type = TokSpace
|
||||
return tok, nil
|
||||
}
|
||||
case ReverseQuote:
|
||||
switch r {
|
||||
case _RuneEOF:
|
||||
return tok, ErrInvalidSyntax
|
||||
case '`':
|
||||
tok.Type = TokReversequote
|
||||
return tok, nil
|
||||
default:
|
||||
tok.Value = append(tok.Value, r)
|
||||
}
|
||||
case String:
|
||||
switch {
|
||||
case r == _RuneEOF || r == '|' || r == '`' || unicode.IsSpace(r):
|
||||
tok.Type = TokString
|
||||
s.unreadRune(r)
|
||||
return tok, nil
|
||||
case r == '\'':
|
||||
state = StringQuoteSingle
|
||||
case r == '"':
|
||||
state = StringQuoteDouble
|
||||
case r == '\\':
|
||||
nr := s.nextRune()
|
||||
if nr == _RuneEOF {
|
||||
return tok, ErrInvalidSyntax
|
||||
}
|
||||
tok.Value = append(tok.Value, nr)
|
||||
case r == '$':
|
||||
state = s.checkDollarStart(&tok, r, state, StringDollar)
|
||||
default:
|
||||
tok.Value = append(tok.Value, r)
|
||||
}
|
||||
case StringDollar:
|
||||
state = s.checkDollarEnd(&tok, r, state, String)
|
||||
case StringQuoteSingle:
|
||||
switch r {
|
||||
case _RuneEOF:
|
||||
return tok, ErrInvalidSyntax
|
||||
case '\'':
|
||||
state = String
|
||||
case '\\':
|
||||
nr := s.nextRune()
|
||||
if escape, ok := s.isEscapeChars(nr); ok {
|
||||
tok.Value = append(tok.Value, escape)
|
||||
} else {
|
||||
tok.Value = append(tok.Value, r)
|
||||
s.unreadRune(nr)
|
||||
}
|
||||
default:
|
||||
tok.Value = append(tok.Value, r)
|
||||
}
|
||||
case StringQuoteDouble:
|
||||
switch r {
|
||||
case _RuneEOF:
|
||||
return tok, ErrInvalidSyntax
|
||||
case '"':
|
||||
state = String
|
||||
case '\\':
|
||||
nr := s.nextRune()
|
||||
if nr == _RuneEOF {
|
||||
return tok, ErrInvalidSyntax
|
||||
}
|
||||
if escape, ok := s.isEscapeChars(nr); ok {
|
||||
tok.Value = append(tok.Value, escape)
|
||||
} else {
|
||||
tok.Value = append(tok.Value, r)
|
||||
s.unreadRune(nr)
|
||||
}
|
||||
case '$':
|
||||
state = s.checkDollarStart(&tok, r, state, StringQuoteDoubleDollar)
|
||||
default:
|
||||
tok.Value = append(tok.Value, r)
|
||||
}
|
||||
case StringQuoteDoubleDollar:
|
||||
state = s.checkDollarEnd(&tok, r, state, StringQuoteDouble)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan is a utility function help split input text as tokens.
|
||||
func Scan(text []rune, env map[string]string) ([]Token, error) {
|
||||
s := NewScanner(text, env)
|
||||
var tokens []Token
|
||||
for {
|
||||
tok, err := s.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokens = append(tokens, tok)
|
||||
if tok.Type == TokEOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
20
vendor/github.com/derekparker/delve/LICENSE
generated
vendored
20
vendor/github.com/derekparker/delve/LICENSE
generated
vendored
@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Derek Parker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
181
vendor/github.com/derekparker/delve/pkg/config/config.go
generated
vendored
181
vendor/github.com/derekparker/delve/pkg/config/config.go
generated
vendored
@ -1,181 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
configDir string = ".dlv"
|
||||
configFile string = "config.yml"
|
||||
)
|
||||
|
||||
// Describes a rule for substitution of path to source code file.
|
||||
type SubstitutePathRule struct {
|
||||
// Directory path will be substituted if it matches `From`.
|
||||
From string
|
||||
// Path to which substitution is performed.
|
||||
To string
|
||||
}
|
||||
|
||||
// Slice of source code path substitution rules.
|
||||
type SubstitutePathRules []SubstitutePathRule
|
||||
|
||||
// Config defines all configuration options available to be set through the config file.
|
||||
type Config struct {
|
||||
// Commands aliases.
|
||||
Aliases map[string][]string `yaml:"aliases"`
|
||||
// Source code path substitution rules.
|
||||
SubstitutePath SubstitutePathRules `yaml:"substitute-path"`
|
||||
|
||||
// MaxStringLen is the maximum string length that the commands print,
|
||||
// locals, args and vars should read (in verbose mode).
|
||||
MaxStringLen *int `yaml:"max-string-len,omitempty"`
|
||||
// MaxArrayValues is the maximum number of array items that the commands
|
||||
// print, locals, args and vars should read (in verbose mode).
|
||||
MaxArrayValues *int `yaml:"max-array-values,omitempty"`
|
||||
|
||||
// If ShowLocationExpr is true whatis will print the DWARF location
|
||||
// expression for its argument.
|
||||
ShowLocationExpr bool `yaml:"show-location-expr"`
|
||||
|
||||
// Source list line-number color (3/4 bit color codes as defined
|
||||
// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
|
||||
SourceListLineColor int `yaml:"source-list-line-color"`
|
||||
}
|
||||
|
||||
// LoadConfig attempts to populate a Config object from the config.yml file.
|
||||
func LoadConfig() *Config {
|
||||
err := createConfigPath()
|
||||
if err != nil {
|
||||
fmt.Printf("Could not create config directory: %v.", err)
|
||||
return nil
|
||||
}
|
||||
fullConfigFile, err := GetConfigFilePath(configFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to get config file path: %v.", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(fullConfigFile)
|
||||
if err != nil {
|
||||
f, err = createDefaultConfig(fullConfigFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating default config file: %v", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("Closing config file failed: %v.", err)
|
||||
}
|
||||
}()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to read config data: %v.", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var c Config
|
||||
err = yaml.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to decode config file: %v.", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func SaveConfig(conf *Config) error {
|
||||
fullConfigFile, err := GetConfigFilePath(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := yaml.Marshal(*conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(fullConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func createDefaultConfig(path string) (*os.File, error) {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to create config file: %v.", err)
|
||||
}
|
||||
err = writeDefaultConfig(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to write default configuration: %v.", err)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func writeDefaultConfig(f *os.File) error {
|
||||
_, err := f.WriteString(
|
||||
`# Configuration file for the delve debugger.
|
||||
|
||||
# This is the default configuration file. Available options are provided, but disabled.
|
||||
# Delete the leading hash mark to enable an item.
|
||||
|
||||
# Uncomment the following line and set your preferred ANSI foreground color
|
||||
# for source line numbers in the (list) command (if unset, default is 34,
|
||||
# dark blue) See https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit
|
||||
# source-list-line-color: 34
|
||||
|
||||
# Provided aliases will be added to the default aliases for a given command.
|
||||
aliases:
|
||||
# command: ["alias1", "alias2"]
|
||||
|
||||
# Define sources path substitution rules. Can be used to rewrite a source path stored
|
||||
# in program's debug information, if the sources were moved to a different place
|
||||
# between compilation and debugging.
|
||||
# Note that substitution rules will not be used for paths passed to "break" and "trace"
|
||||
# commands.
|
||||
substitute-path:
|
||||
# - {from: path, to: path}
|
||||
|
||||
# Maximum number of elements loaded from an array.
|
||||
# max-array-values: 64
|
||||
|
||||
# Maximum loaded string length.
|
||||
# max-string-len: 64
|
||||
|
||||
# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
|
||||
# show-location-expr: true
|
||||
`)
|
||||
return err
|
||||
}
|
||||
|
||||
// createConfigPath creates the directory structure at which all config files are saved.
|
||||
func createConfigPath() error {
|
||||
path, err := GetConfigFilePath("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(path, 0700)
|
||||
}
|
||||
|
||||
// GetConfigFilePath gets the full path to the given config file name.
|
||||
func GetConfigFilePath(file string) (string, error) {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(usr.HomeDir, configDir, file), nil
|
||||
}
|
63
vendor/github.com/derekparker/delve/pkg/config/split.go
generated
vendored
63
vendor/github.com/derekparker/delve/pkg/config/split.go
generated
vendored
@ -1,63 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Like strings.Fields but ignores spaces inside areas surrounded
|
||||
// by the specified quote character.
|
||||
// To specify a single quote use backslash to escape it: '\''
|
||||
func SplitQuotedFields(in string, quote rune) []string {
|
||||
type stateEnum int
|
||||
const (
|
||||
inSpace stateEnum = iota
|
||||
inField
|
||||
inQuote
|
||||
inQuoteEscaped
|
||||
)
|
||||
state := inSpace
|
||||
r := []string{}
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, ch := range in {
|
||||
switch state {
|
||||
case inSpace:
|
||||
if ch == quote {
|
||||
state = inQuote
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
buf.WriteRune(ch)
|
||||
state = inField
|
||||
}
|
||||
|
||||
case inField:
|
||||
if ch == quote {
|
||||
state = inQuote
|
||||
} else if unicode.IsSpace(ch) {
|
||||
r = append(r, buf.String())
|
||||
buf.Reset()
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuote:
|
||||
if ch == quote {
|
||||
state = inField
|
||||
} else if ch == '\\' {
|
||||
state = inQuoteEscaped
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuoteEscaped:
|
||||
buf.WriteRune(ch)
|
||||
state = inQuote
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() != 0 {
|
||||
r = append(r, buf.String())
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
77
vendor/github.com/derekparker/delve/pkg/dwarf/frame/entries.go
generated
vendored
77
vendor/github.com/derekparker/delve/pkg/dwarf/frame/entries.go
generated
vendored
@ -1,77 +0,0 @@
|
||||
package frame
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Represents a Common Information Entry in
|
||||
// the Dwarf .debug_frame section.
|
||||
type CommonInformationEntry struct {
|
||||
Length uint32
|
||||
CIE_id uint32
|
||||
Version uint8
|
||||
Augmentation string
|
||||
CodeAlignmentFactor uint64
|
||||
DataAlignmentFactor int64
|
||||
ReturnAddressRegister uint64
|
||||
InitialInstructions []byte
|
||||
staticBase uint64
|
||||
}
|
||||
|
||||
// Represents a Frame Descriptor Entry in the
|
||||
// Dwarf .debug_frame section.
|
||||
type FrameDescriptionEntry struct {
|
||||
Length uint32
|
||||
CIE *CommonInformationEntry
|
||||
Instructions []byte
|
||||
begin, size uint64
|
||||
order binary.ByteOrder
|
||||
}
|
||||
|
||||
// Returns whether or not the given address is within the
|
||||
// bounds of this frame.
|
||||
func (fde *FrameDescriptionEntry) Cover(addr uint64) bool {
|
||||
return (addr - fde.begin) < fde.size
|
||||
}
|
||||
|
||||
// Address of first location for this frame.
|
||||
func (fde *FrameDescriptionEntry) Begin() uint64 {
|
||||
return fde.begin
|
||||
}
|
||||
|
||||
// Address of last location for this frame.
|
||||
func (fde *FrameDescriptionEntry) End() uint64 {
|
||||
return fde.begin + fde.size
|
||||
}
|
||||
|
||||
// Set up frame for the given PC.
|
||||
func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext {
|
||||
return executeDwarfProgramUntilPC(fde, pc)
|
||||
}
|
||||
|
||||
type FrameDescriptionEntries []*FrameDescriptionEntry
|
||||
|
||||
func NewFrameIndex() FrameDescriptionEntries {
|
||||
return make(FrameDescriptionEntries, 0, 1000)
|
||||
}
|
||||
|
||||
type ErrNoFDEForPC struct {
|
||||
PC uint64
|
||||
}
|
||||
|
||||
func (err *ErrNoFDEForPC) Error() string {
|
||||
return fmt.Sprintf("could not find FDE for PC %#v", err.PC)
|
||||
}
|
||||
|
||||
// Returns the Frame Description Entry for the given PC.
|
||||
func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) {
|
||||
idx := sort.Search(len(fdes), func(i int) bool {
|
||||
return fdes[i].Cover(pc) || fdes[i].Begin() >= pc
|
||||
})
|
||||
if idx == len(fdes) || !fdes[idx].Cover(pc) {
|
||||
return nil, &ErrNoFDEForPC{pc}
|
||||
}
|
||||
return fdes[idx], nil
|
||||
}
|
164
vendor/github.com/derekparker/delve/pkg/dwarf/frame/expression_constants.go
generated
vendored
164
vendor/github.com/derekparker/delve/pkg/dwarf/frame/expression_constants.go
generated
vendored
@ -1,164 +0,0 @@
|
||||
package frame
|
||||
|
||||
// Operation opcodes
|
||||
const (
|
||||
DW_OP_addr = 0x03
|
||||
DW_OP_const1s = 0x09
|
||||
)
|
||||
|
||||
const (
|
||||
DW_OP_const2u = 0x0a
|
||||
DW_OP_const2s = 0x0b
|
||||
DW_OP_const4u = iota
|
||||
DW_OP_const4s
|
||||
DW_OP_const8u
|
||||
DW_OP_const8s
|
||||
DW_OP_constu
|
||||
DW_OP_consts
|
||||
DW_OP_dup
|
||||
DW_OP_drop
|
||||
DW_OP_over
|
||||
DW_OP_pick
|
||||
DW_OP_swap
|
||||
DW_OP_rot
|
||||
DW_OP_xderef
|
||||
DW_OP_abs
|
||||
DW_OP_and
|
||||
DW_OP_div
|
||||
DW_OP_minus
|
||||
DW_OP_mod
|
||||
DW_OP_mul
|
||||
DW_OP_neg
|
||||
DW_OP_not
|
||||
DW_OP_or
|
||||
DW_OP_plus
|
||||
DW_OP_plus_uconst
|
||||
DW_OP_shl
|
||||
DW_OP_shr
|
||||
DW_OP_shra
|
||||
DW_OP_xor
|
||||
DW_OP_skip
|
||||
DW_OP_bra
|
||||
DW_OP_eq
|
||||
DW_OP_ge
|
||||
DW_OP_gt
|
||||
DW_OP_le
|
||||
DW_OP_lt
|
||||
DW_OP_ne
|
||||
)
|
||||
|
||||
const (
|
||||
DW_OP_lit0 = 0x30
|
||||
DW_OP_lit1 = 0x31
|
||||
DW_OP_lit2 = iota
|
||||
DW_OP_lit3
|
||||
DW_OP_lit4
|
||||
DW_OP_lit5
|
||||
DW_OP_lit6
|
||||
DW_OP_lit7
|
||||
DW_OP_lit8
|
||||
DW_OP_lit9
|
||||
DW_OP_lit10
|
||||
DW_OP_lit11
|
||||
DW_OP_lit12
|
||||
DW_OP_lit13
|
||||
DW_OP_lit14
|
||||
DW_OP_lit15
|
||||
DW_OP_lit16
|
||||
DW_OP_lit17
|
||||
DW_OP_lit18
|
||||
DW_OP_lit19
|
||||
DW_OP_lit20
|
||||
DW_OP_lit21
|
||||
DW_OP_lit22
|
||||
DW_OP_lit23
|
||||
DW_OP_lit24
|
||||
DW_OP_lit25
|
||||
DW_OP_lit26
|
||||
DW_OP_lit27
|
||||
DW_OP_lit28
|
||||
DW_OP_lit29
|
||||
DW_OP_lit30
|
||||
DW_OP_lit31
|
||||
DW_OP_reg0
|
||||
DW_OP_reg1
|
||||
DW_OP_reg2
|
||||
DW_OP_reg3
|
||||
DW_OP_reg4
|
||||
DW_OP_reg5
|
||||
DW_OP_reg6
|
||||
DW_OP_reg7
|
||||
DW_OP_reg8
|
||||
DW_OP_reg9
|
||||
DW_OP_reg10
|
||||
DW_OP_reg11
|
||||
DW_OP_reg12
|
||||
DW_OP_reg13
|
||||
DW_OP_reg14
|
||||
DW_OP_reg15
|
||||
DW_OP_reg16
|
||||
DW_OP_reg17
|
||||
DW_OP_reg18
|
||||
DW_OP_reg19
|
||||
DW_OP_reg20
|
||||
DW_OP_reg21
|
||||
DW_OP_reg22
|
||||
DW_OP_reg23
|
||||
DW_OP_reg24
|
||||
DW_OP_reg25
|
||||
DW_OP_reg26
|
||||
DW_OP_reg27
|
||||
DW_OP_reg28
|
||||
DW_OP_reg29
|
||||
DW_OP_reg30
|
||||
DW_OP_reg31
|
||||
DW_OP_breg0
|
||||
DW_OP_breg1
|
||||
DW_OP_breg2
|
||||
DW_OP_breg3
|
||||
DW_OP_breg4
|
||||
DW_OP_breg5
|
||||
DW_OP_breg6
|
||||
DW_OP_breg7
|
||||
DW_OP_breg8
|
||||
DW_OP_breg9
|
||||
DW_OP_breg10
|
||||
DW_OP_breg11
|
||||
DW_OP_breg12
|
||||
DW_OP_breg13
|
||||
DW_OP_breg14
|
||||
DW_OP_breg15
|
||||
DW_OP_breg16
|
||||
DW_OP_breg17
|
||||
DW_OP_breg18
|
||||
DW_OP_breg19
|
||||
DW_OP_breg20
|
||||
DW_OP_breg21
|
||||
DW_OP_breg22
|
||||
DW_OP_breg23
|
||||
DW_OP_breg24
|
||||
DW_OP_breg25
|
||||
DW_OP_breg26
|
||||
DW_OP_breg27
|
||||
DW_OP_breg28
|
||||
DW_OP_breg29
|
||||
DW_OP_breg30
|
||||
DW_OP_breg31
|
||||
DW_OP_regx
|
||||
DW_OP_fbreg
|
||||
DW_OP_bregx
|
||||
DW_OP_piece
|
||||
DW_OP_deref_size
|
||||
DW_OP_xderef_size
|
||||
DW_OP_nop
|
||||
DW_OP_push_object_address
|
||||
DW_OP_call2
|
||||
DW_OP_call4
|
||||
DW_OP_call_ref
|
||||
DW_OP_form_tls_address
|
||||
DW_OP_call_frame_cfa
|
||||
DW_OP_bit_piece
|
||||
|
||||
DW_OP_lo_user = 0xe0
|
||||
DW_OP_hi_user = 0xff
|
||||
)
|
134
vendor/github.com/derekparker/delve/pkg/dwarf/frame/parser.go
generated
vendored
134
vendor/github.com/derekparker/delve/pkg/dwarf/frame/parser.go
generated
vendored
@ -1,134 +0,0 @@
|
||||
// Package frame contains data structures and
|
||||
// related functions for parsing and searching
|
||||
// through Dwarf .debug_frame data.
|
||||
package frame
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type parsefunc func(*parseContext) parsefunc
|
||||
|
||||
type parseContext struct {
|
||||
staticBase uint64
|
||||
|
||||
buf *bytes.Buffer
|
||||
entries FrameDescriptionEntries
|
||||
common *CommonInformationEntry
|
||||
frame *FrameDescriptionEntry
|
||||
length uint32
|
||||
}
|
||||
|
||||
// Parse takes in data (a byte slice) and returns a slice of
|
||||
// commonInformationEntry structures. Each commonInformationEntry
|
||||
// has a slice of frameDescriptionEntry structures.
|
||||
func Parse(data []byte, order binary.ByteOrder, staticBase uint64) FrameDescriptionEntries {
|
||||
var (
|
||||
buf = bytes.NewBuffer(data)
|
||||
pctx = &parseContext{buf: buf, entries: NewFrameIndex(), staticBase: staticBase}
|
||||
)
|
||||
|
||||
for fn := parselength; buf.Len() != 0; {
|
||||
fn = fn(pctx)
|
||||
}
|
||||
|
||||
for i := range pctx.entries {
|
||||
pctx.entries[i].order = order
|
||||
}
|
||||
|
||||
return pctx.entries
|
||||
}
|
||||
|
||||
func cieEntry(data []byte) bool {
|
||||
return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
|
||||
}
|
||||
|
||||
func parselength(ctx *parseContext) parsefunc {
|
||||
binary.Read(ctx.buf, binary.LittleEndian, &ctx.length)
|
||||
|
||||
if ctx.length == 0 {
|
||||
// ZERO terminator
|
||||
return parselength
|
||||
}
|
||||
|
||||
var data = ctx.buf.Next(4)
|
||||
|
||||
ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
|
||||
|
||||
if cieEntry(data) {
|
||||
ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase}
|
||||
return parseCIE
|
||||
}
|
||||
|
||||
ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: ctx.common}
|
||||
return parseFDE
|
||||
}
|
||||
|
||||
func parseFDE(ctx *parseContext) parsefunc {
|
||||
r := ctx.buf.Next(int(ctx.length))
|
||||
|
||||
ctx.frame.begin = binary.LittleEndian.Uint64(r[:8]) + ctx.staticBase
|
||||
ctx.frame.size = binary.LittleEndian.Uint64(r[8:16])
|
||||
|
||||
// Insert into the tree after setting address range begin
|
||||
// otherwise compares won't work.
|
||||
ctx.entries = append(ctx.entries, ctx.frame)
|
||||
|
||||
// The rest of this entry consists of the instructions
|
||||
// so we can just grab all of the data from the buffer
|
||||
// cursor to length.
|
||||
ctx.frame.Instructions = r[16:]
|
||||
ctx.length = 0
|
||||
|
||||
return parselength
|
||||
}
|
||||
|
||||
func parseCIE(ctx *parseContext) parsefunc {
|
||||
data := ctx.buf.Next(int(ctx.length))
|
||||
buf := bytes.NewBuffer(data)
|
||||
// parse version
|
||||
ctx.common.Version = data[0]
|
||||
|
||||
// parse augmentation
|
||||
ctx.common.Augmentation, _ = util.ParseString(buf)
|
||||
|
||||
// parse code alignment factor
|
||||
ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
|
||||
|
||||
// parse data alignment factor
|
||||
ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
|
||||
|
||||
// parse return address register
|
||||
ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
|
||||
|
||||
// parse initial instructions
|
||||
// The rest of this entry consists of the instructions
|
||||
// so we can just grab all of the data from the buffer
|
||||
// cursor to length.
|
||||
ctx.common.InitialInstructions = buf.Bytes() //ctx.buf.Next(int(ctx.length))
|
||||
ctx.length = 0
|
||||
|
||||
return parselength
|
||||
}
|
||||
|
||||
// DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
|
||||
// Trick borrowed from "debug/dwarf".New()
|
||||
func DwarfEndian(infoSec []byte) binary.ByteOrder {
|
||||
if len(infoSec) < 6 {
|
||||
return binary.BigEndian
|
||||
}
|
||||
x, y := infoSec[4], infoSec[5]
|
||||
switch {
|
||||
case x == 0 && y == 0:
|
||||
return binary.BigEndian
|
||||
case x == 0:
|
||||
return binary.BigEndian
|
||||
case y == 0:
|
||||
return binary.LittleEndian
|
||||
default:
|
||||
return binary.BigEndian
|
||||
}
|
||||
}
|
424
vendor/github.com/derekparker/delve/pkg/dwarf/frame/table.go
generated
vendored
424
vendor/github.com/derekparker/delve/pkg/dwarf/frame/table.go
generated
vendored
@ -1,424 +0,0 @@
|
||||
package frame
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type DWRule struct {
|
||||
Rule Rule
|
||||
Offset int64
|
||||
Reg uint64
|
||||
Expression []byte
|
||||
}
|
||||
|
||||
type FrameContext struct {
|
||||
loc uint64
|
||||
order binary.ByteOrder
|
||||
address uint64
|
||||
CFA DWRule
|
||||
Regs map[uint64]DWRule
|
||||
initialRegs map[uint64]DWRule
|
||||
prevRegs map[uint64]DWRule
|
||||
buf *bytes.Buffer
|
||||
cie *CommonInformationEntry
|
||||
RetAddrReg uint64
|
||||
codeAlignment uint64
|
||||
dataAlignment int64
|
||||
}
|
||||
|
||||
// Instructions used to recreate the table from the .debug_frame data.
|
||||
const (
|
||||
DW_CFA_nop = 0x0 // No ops
|
||||
DW_CFA_set_loc = 0x01 // op1: address
|
||||
DW_CFA_advance_loc1 = iota // op1: 1-bytes delta
|
||||
DW_CFA_advance_loc2 // op1: 2-byte delta
|
||||
DW_CFA_advance_loc4 // op1: 4-byte delta
|
||||
DW_CFA_offset_extended // op1: ULEB128 register, op2: ULEB128 offset
|
||||
DW_CFA_restore_extended // op1: ULEB128 register
|
||||
DW_CFA_undefined // op1: ULEB128 register
|
||||
DW_CFA_same_value // op1: ULEB128 register
|
||||
DW_CFA_register // op1: ULEB128 register, op2: ULEB128 register
|
||||
DW_CFA_remember_state // No ops
|
||||
DW_CFA_restore_state // No ops
|
||||
DW_CFA_def_cfa // op1: ULEB128 register, op2: ULEB128 offset
|
||||
DW_CFA_def_cfa_register // op1: ULEB128 register
|
||||
DW_CFA_def_cfa_offset // op1: ULEB128 offset
|
||||
DW_CFA_def_cfa_expression // op1: BLOCK
|
||||
DW_CFA_expression // op1: ULEB128 register, op2: BLOCK
|
||||
DW_CFA_offset_extended_sf // op1: ULEB128 register, op2: SLEB128 BLOCK
|
||||
DW_CFA_def_cfa_sf // op1: ULEB128 register, op2: SLEB128 offset
|
||||
DW_CFA_def_cfa_offset_sf // op1: SLEB128 offset
|
||||
DW_CFA_val_offset // op1: ULEB128, op2: ULEB128
|
||||
DW_CFA_val_offset_sf // op1: ULEB128, op2: SLEB128
|
||||
DW_CFA_val_expression // op1: ULEB128, op2: BLOCK
|
||||
DW_CFA_lo_user = 0x1c // op1: BLOCK
|
||||
DW_CFA_hi_user = 0x3f // op1: ULEB128 register, op2: BLOCK
|
||||
DW_CFA_advance_loc = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
|
||||
DW_CFA_offset = (0x2 << 6) // High 2 bits: 0x2, low 6: register
|
||||
DW_CFA_restore = (0x3 << 6) // High 2 bits: 0x3, low 6: register
|
||||
)
|
||||
|
||||
// Rules defined for register values.
|
||||
type Rule byte
|
||||
|
||||
const (
|
||||
RuleUndefined Rule = iota
|
||||
RuleSameVal
|
||||
RuleOffset
|
||||
RuleValOffset
|
||||
RuleRegister
|
||||
RuleExpression
|
||||
RuleValExpression
|
||||
RuleArchitectural
|
||||
RuleCFA // Value is rule.Reg + rule.Offset
|
||||
RuleFramePointer // Value is stored at address rule.Reg + rule.Offset, but only if it's less than the current CFA, otherwise same value
|
||||
)
|
||||
|
||||
const low_6_offset = 0x3f
|
||||
|
||||
type instruction func(frame *FrameContext)
|
||||
|
||||
// // Mapping from DWARF opcode to function.
|
||||
var fnlookup = map[byte]instruction{
|
||||
DW_CFA_advance_loc: advanceloc,
|
||||
DW_CFA_offset: offset,
|
||||
DW_CFA_restore: restore,
|
||||
DW_CFA_set_loc: setloc,
|
||||
DW_CFA_advance_loc1: advanceloc1,
|
||||
DW_CFA_advance_loc2: advanceloc2,
|
||||
DW_CFA_advance_loc4: advanceloc4,
|
||||
DW_CFA_offset_extended: offsetextended,
|
||||
DW_CFA_restore_extended: restoreextended,
|
||||
DW_CFA_undefined: undefined,
|
||||
DW_CFA_same_value: samevalue,
|
||||
DW_CFA_register: register,
|
||||
DW_CFA_remember_state: rememberstate,
|
||||
DW_CFA_restore_state: restorestate,
|
||||
DW_CFA_def_cfa: defcfa,
|
||||
DW_CFA_def_cfa_register: defcfaregister,
|
||||
DW_CFA_def_cfa_offset: defcfaoffset,
|
||||
DW_CFA_def_cfa_expression: defcfaexpression,
|
||||
DW_CFA_expression: expression,
|
||||
DW_CFA_offset_extended_sf: offsetextendedsf,
|
||||
DW_CFA_def_cfa_sf: defcfasf,
|
||||
DW_CFA_def_cfa_offset_sf: defcfaoffsetsf,
|
||||
DW_CFA_val_offset: valoffset,
|
||||
DW_CFA_val_offset_sf: valoffsetsf,
|
||||
DW_CFA_val_expression: valexpression,
|
||||
DW_CFA_lo_user: louser,
|
||||
DW_CFA_hi_user: hiuser,
|
||||
}
|
||||
|
||||
func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
|
||||
initialInstructions := make([]byte, len(cie.InitialInstructions))
|
||||
copy(initialInstructions, cie.InitialInstructions)
|
||||
frame := &FrameContext{
|
||||
cie: cie,
|
||||
Regs: make(map[uint64]DWRule),
|
||||
RetAddrReg: cie.ReturnAddressRegister,
|
||||
initialRegs: make(map[uint64]DWRule),
|
||||
prevRegs: make(map[uint64]DWRule),
|
||||
codeAlignment: cie.CodeAlignmentFactor,
|
||||
dataAlignment: cie.DataAlignmentFactor,
|
||||
buf: bytes.NewBuffer(initialInstructions),
|
||||
}
|
||||
|
||||
frame.ExecuteDwarfProgram()
|
||||
return frame
|
||||
}
|
||||
|
||||
// Unwind the stack to find the return address register.
|
||||
func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameContext {
|
||||
frame := executeCIEInstructions(fde.CIE)
|
||||
frame.order = fde.order
|
||||
frame.loc = fde.Begin()
|
||||
frame.address = pc
|
||||
frame.ExecuteUntilPC(fde.Instructions)
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
func (frame *FrameContext) ExecuteDwarfProgram() {
|
||||
for frame.buf.Len() > 0 {
|
||||
executeDwarfInstruction(frame)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute dwarf instructions.
|
||||
func (frame *FrameContext) ExecuteUntilPC(instructions []byte) {
|
||||
frame.buf.Truncate(0)
|
||||
frame.buf.Write(instructions)
|
||||
|
||||
// We only need to execute the instructions until
|
||||
// ctx.loc > ctx.address (which is the address we
|
||||
// are currently at in the traced process).
|
||||
for frame.address >= frame.loc && frame.buf.Len() > 0 {
|
||||
executeDwarfInstruction(frame)
|
||||
}
|
||||
}
|
||||
|
||||
func executeDwarfInstruction(frame *FrameContext) {
|
||||
instruction, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not read from instruction buffer")
|
||||
}
|
||||
|
||||
if instruction == DW_CFA_nop {
|
||||
return
|
||||
}
|
||||
|
||||
fn := lookupFunc(instruction, frame.buf)
|
||||
|
||||
fn(frame)
|
||||
}
|
||||
|
||||
func lookupFunc(instruction byte, buf *bytes.Buffer) instruction {
|
||||
const high_2_bits = 0xc0
|
||||
var restore bool
|
||||
|
||||
// Special case the 3 opcodes that have their argument encoded in the opcode itself.
|
||||
switch instruction & high_2_bits {
|
||||
case DW_CFA_advance_loc:
|
||||
instruction = DW_CFA_advance_loc
|
||||
restore = true
|
||||
|
||||
case DW_CFA_offset:
|
||||
instruction = DW_CFA_offset
|
||||
restore = true
|
||||
|
||||
case DW_CFA_restore:
|
||||
instruction = DW_CFA_restore
|
||||
restore = true
|
||||
}
|
||||
|
||||
if restore {
|
||||
// Restore the last byte as it actually contains the argument for the opcode.
|
||||
err := buf.UnreadByte()
|
||||
if err != nil {
|
||||
panic("Could not unread byte")
|
||||
}
|
||||
}
|
||||
|
||||
fn, ok := fnlookup[instruction]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Encountered an unexpected DWARF CFA opcode: %#v", instruction))
|
||||
}
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func advanceloc(frame *FrameContext) {
|
||||
b, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not read byte")
|
||||
}
|
||||
|
||||
delta := b & low_6_offset
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func advanceloc1(frame *FrameContext) {
|
||||
delta, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not read byte")
|
||||
}
|
||||
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func advanceloc2(frame *FrameContext) {
|
||||
var delta uint16
|
||||
binary.Read(frame.buf, frame.order, &delta)
|
||||
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func advanceloc4(frame *FrameContext) {
|
||||
var delta uint32
|
||||
binary.Read(frame.buf, frame.order, &delta)
|
||||
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func offset(frame *FrameContext) {
|
||||
b, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var (
|
||||
reg = b & low_6_offset
|
||||
offset, _ = util.DecodeULEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.Regs[uint64(reg)] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
|
||||
}
|
||||
|
||||
func restore(frame *FrameContext) {
|
||||
b, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
reg := uint64(b & low_6_offset)
|
||||
oldrule, ok := frame.initialRegs[reg]
|
||||
if ok {
|
||||
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
||||
} else {
|
||||
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
||||
}
|
||||
}
|
||||
|
||||
func setloc(frame *FrameContext) {
|
||||
var loc uint64
|
||||
binary.Read(frame.buf, frame.order, &loc)
|
||||
|
||||
frame.loc = loc + frame.cie.staticBase
|
||||
}
|
||||
|
||||
func offsetextended(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeULEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.Regs[reg] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
|
||||
}
|
||||
|
||||
func undefined(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
||||
}
|
||||
|
||||
func samevalue(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.Regs[reg] = DWRule{Rule: RuleSameVal}
|
||||
}
|
||||
|
||||
func register(frame *FrameContext) {
|
||||
reg1, _ := util.DecodeULEB128(frame.buf)
|
||||
reg2, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.Regs[reg1] = DWRule{Reg: reg2, Rule: RuleRegister}
|
||||
}
|
||||
|
||||
func rememberstate(frame *FrameContext) {
|
||||
frame.prevRegs = frame.Regs
|
||||
}
|
||||
|
||||
func restorestate(frame *FrameContext) {
|
||||
frame.Regs = frame.prevRegs
|
||||
}
|
||||
|
||||
func restoreextended(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
|
||||
oldrule, ok := frame.initialRegs[reg]
|
||||
if ok {
|
||||
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
||||
} else {
|
||||
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
||||
}
|
||||
}
|
||||
|
||||
func defcfa(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
offset, _ := util.DecodeULEB128(frame.buf)
|
||||
|
||||
frame.CFA.Rule = RuleCFA
|
||||
frame.CFA.Reg = reg
|
||||
frame.CFA.Offset = int64(offset)
|
||||
}
|
||||
|
||||
func defcfaregister(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.CFA.Reg = reg
|
||||
}
|
||||
|
||||
func defcfaoffset(frame *FrameContext) {
|
||||
offset, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.CFA.Offset = int64(offset)
|
||||
}
|
||||
|
||||
func defcfasf(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
offset, _ := util.DecodeSLEB128(frame.buf)
|
||||
|
||||
frame.CFA.Rule = RuleCFA
|
||||
frame.CFA.Reg = reg
|
||||
frame.CFA.Offset = offset * frame.dataAlignment
|
||||
}
|
||||
|
||||
func defcfaoffsetsf(frame *FrameContext) {
|
||||
offset, _ := util.DecodeSLEB128(frame.buf)
|
||||
offset *= frame.dataAlignment
|
||||
frame.CFA.Offset = offset
|
||||
}
|
||||
|
||||
func defcfaexpression(frame *FrameContext) {
|
||||
var (
|
||||
l, _ = util.DecodeULEB128(frame.buf)
|
||||
expr = frame.buf.Next(int(l))
|
||||
)
|
||||
|
||||
frame.CFA.Expression = expr
|
||||
frame.CFA.Rule = RuleExpression
|
||||
}
|
||||
|
||||
func expression(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
l, _ = util.DecodeULEB128(frame.buf)
|
||||
expr = frame.buf.Next(int(l))
|
||||
)
|
||||
|
||||
frame.Regs[reg] = DWRule{Rule: RuleExpression, Expression: expr}
|
||||
}
|
||||
|
||||
func offsetextendedsf(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeSLEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleOffset}
|
||||
}
|
||||
|
||||
func valoffset(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeULEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.Regs[reg] = DWRule{Offset: int64(offset), Rule: RuleValOffset}
|
||||
}
|
||||
|
||||
func valoffsetsf(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeSLEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleValOffset}
|
||||
}
|
||||
|
||||
func valexpression(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
l, _ = util.DecodeULEB128(frame.buf)
|
||||
expr = frame.buf.Next(int(l))
|
||||
)
|
||||
|
||||
frame.Regs[reg] = DWRule{Rule: RuleValExpression, Expression: expr}
|
||||
}
|
||||
|
||||
func louser(frame *FrameContext) {
|
||||
frame.buf.Next(1)
|
||||
}
|
||||
|
||||
func hiuser(frame *FrameContext) {
|
||||
frame.buf.Next(1)
|
||||
}
|
107
vendor/github.com/derekparker/delve/pkg/dwarf/godwarf/sections.go
generated
vendored
107
vendor/github.com/derekparker/delve/pkg/dwarf/godwarf/sections.go
generated
vendored
@ -1,107 +0,0 @@
|
||||
package godwarf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// GetDebugSectionElf returns the data contents of the specified debug
|
||||
// section, decompressing it if it is compressed.
|
||||
// For example GetDebugSectionElf("line") will return the contents of
|
||||
// .debug_line, if .debug_line doesn't exist it will try to return the
|
||||
// decompressed contents of .zdebug_line.
|
||||
func GetDebugSectionElf(f *elf.File, name string) ([]byte, error) {
|
||||
sec := f.Section(".debug_" + name)
|
||||
if sec != nil {
|
||||
return sec.Data()
|
||||
}
|
||||
sec = f.Section(".zdebug_" + name)
|
||||
if sec == nil {
|
||||
return nil, fmt.Errorf("could not find .debug_%s section", name)
|
||||
}
|
||||
b, err := sec.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decompressMaybe(b)
|
||||
}
|
||||
|
||||
// GetDebugSectionPE returns the data contents of the specified debug
|
||||
// section, decompressing it if it is compressed.
|
||||
// For example GetDebugSectionPE("line") will return the contents of
|
||||
// .debug_line, if .debug_line doesn't exist it will try to return the
|
||||
// decompressed contents of .zdebug_line.
|
||||
func GetDebugSectionPE(f *pe.File, name string) ([]byte, error) {
|
||||
sec := f.Section(".debug_" + name)
|
||||
if sec != nil {
|
||||
return peSectionData(sec)
|
||||
}
|
||||
sec = f.Section(".zdebug_" + name)
|
||||
if sec == nil {
|
||||
return nil, fmt.Errorf("could not find .debug_%s section", name)
|
||||
}
|
||||
b, err := peSectionData(sec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decompressMaybe(b)
|
||||
}
|
||||
|
||||
func peSectionData(sec *pe.Section) ([]byte, error) {
|
||||
b, err := sec.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
||||
b = b[:sec.VirtualSize]
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GetDebugSectionMacho returns the data contents of the specified debug
|
||||
// section, decompressing it if it is compressed.
|
||||
// For example GetDebugSectionMacho("line") will return the contents of
|
||||
// __debug_line, if __debug_line doesn't exist it will try to return the
|
||||
// decompressed contents of __zdebug_line.
|
||||
func GetDebugSectionMacho(f *macho.File, name string) ([]byte, error) {
|
||||
sec := f.Section("__debug_" + name)
|
||||
if sec != nil {
|
||||
return sec.Data()
|
||||
}
|
||||
sec = f.Section("__zdebug_" + name)
|
||||
if sec == nil {
|
||||
return nil, fmt.Errorf("could not find .debug_%s section", name)
|
||||
}
|
||||
b, err := sec.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decompressMaybe(b)
|
||||
}
|
||||
|
||||
func decompressMaybe(b []byte) ([]byte, error) {
|
||||
if len(b) < 12 || string(b[:4]) != "ZLIB" {
|
||||
// not compressed
|
||||
return b, nil
|
||||
}
|
||||
|
||||
dlen := binary.BigEndian.Uint64(b[4:12])
|
||||
dbuf := make([]byte, dlen)
|
||||
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, dbuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbuf, nil
|
||||
}
|
878
vendor/github.com/derekparker/delve/pkg/dwarf/godwarf/type.go
generated
vendored
878
vendor/github.com/derekparker/delve/pkg/dwarf/godwarf/type.go
generated
vendored
@ -1,878 +0,0 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
// DWARF type information structures.
|
||||
// The format is heavily biased toward C, but for simplicity
|
||||
// the String methods use a pseudo-Go syntax.
|
||||
|
||||
// Borrowed from golang.org/x/debug/dwarf/type.go
|
||||
|
||||
package godwarf
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
const (
|
||||
AttrGoKind dwarf.Attr = 0x2900
|
||||
AttrGoKey dwarf.Attr = 0x2901
|
||||
AttrGoElem dwarf.Attr = 0x2902
|
||||
AttrGoEmbeddedField dwarf.Attr = 0x2903
|
||||
AttrGoRuntimeType dwarf.Attr = 0x2904
|
||||
)
|
||||
|
||||
// Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry.
|
||||
const (
|
||||
encAddress = 0x01
|
||||
encBoolean = 0x02
|
||||
encComplexFloat = 0x03
|
||||
encFloat = 0x04
|
||||
encSigned = 0x05
|
||||
encSignedChar = 0x06
|
||||
encUnsigned = 0x07
|
||||
encUnsignedChar = 0x08
|
||||
encImaginaryFloat = 0x09
|
||||
)
|
||||
|
||||
// A Type conventionally represents a pointer to any of the
|
||||
// specific Type structures (CharType, StructType, etc.).
|
||||
//TODO: remove this use dwarf.Type
|
||||
type Type interface {
|
||||
Common() *CommonType
|
||||
String() string
|
||||
Size() int64
|
||||
}
|
||||
|
||||
// A CommonType holds fields common to multiple types.
|
||||
// If a field is not known or not applicable for a given type,
|
||||
// the zero value is used.
|
||||
type CommonType struct {
|
||||
ByteSize int64 // size of value of this type, in bytes
|
||||
Name string // name that can be used to refer to type
|
||||
ReflectKind reflect.Kind // the reflect kind of the type.
|
||||
Offset dwarf.Offset // the offset at which this type was read
|
||||
}
|
||||
|
||||
func (c *CommonType) Common() *CommonType { return c }
|
||||
|
||||
func (c *CommonType) Size() int64 { return c.ByteSize }
|
||||
|
||||
// Basic types
|
||||
|
||||
// A BasicType holds fields common to all basic types.
|
||||
type BasicType struct {
|
||||
CommonType
|
||||
BitSize int64
|
||||
BitOffset int64
|
||||
}
|
||||
|
||||
func (b *BasicType) Basic() *BasicType { return b }
|
||||
|
||||
func (t *BasicType) String() string {
|
||||
if t.Name != "" {
|
||||
return t.Name
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
|
||||
// A CharType represents a signed character type.
|
||||
type CharType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// A UcharType represents an unsigned character type.
|
||||
type UcharType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// An IntType represents a signed integer type.
|
||||
type IntType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// A UintType represents an unsigned integer type.
|
||||
type UintType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// A FloatType represents a floating point type.
|
||||
type FloatType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// A ComplexType represents a complex floating point type.
|
||||
type ComplexType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// A BoolType represents a boolean type.
|
||||
type BoolType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// An AddrType represents a machine address type.
|
||||
type AddrType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type.
|
||||
type UnspecifiedType struct {
|
||||
BasicType
|
||||
}
|
||||
|
||||
// qualifiers
|
||||
|
||||
// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier.
|
||||
type QualType struct {
|
||||
CommonType
|
||||
Qual string
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (t *QualType) String() string { return t.Qual + " " + t.Type.String() }
|
||||
|
||||
func (t *QualType) Size() int64 { return t.Type.Size() }
|
||||
|
||||
// An ArrayType represents a fixed size array type.
|
||||
type ArrayType struct {
|
||||
CommonType
|
||||
Type Type
|
||||
StrideBitSize int64 // if > 0, number of bits to hold each element
|
||||
Count int64 // if == -1, an incomplete array, like char x[].
|
||||
}
|
||||
|
||||
func (t *ArrayType) String() string {
|
||||
return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
|
||||
}
|
||||
|
||||
func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() }
|
||||
|
||||
// A VoidType represents the C void type.
|
||||
type VoidType struct {
|
||||
CommonType
|
||||
}
|
||||
|
||||
func (t *VoidType) String() string { return "void" }
|
||||
|
||||
// A PtrType represents a pointer type.
|
||||
type PtrType struct {
|
||||
CommonType
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (t *PtrType) String() string { return "*" + t.Type.String() }
|
||||
|
||||
// A StructType represents a struct, union, or C++ class type.
|
||||
type StructType struct {
|
||||
CommonType
|
||||
StructName string
|
||||
Kind string // "struct", "union", or "class".
|
||||
Field []*StructField
|
||||
Incomplete bool // if true, struct, union, class is declared but not defined
|
||||
}
|
||||
|
||||
// A StructField represents a field in a struct, union, or C++ class type.
|
||||
type StructField struct {
|
||||
Name string
|
||||
Type Type
|
||||
ByteOffset int64
|
||||
ByteSize int64
|
||||
BitOffset int64 // within the ByteSize bytes at ByteOffset
|
||||
BitSize int64 // zero if not a bit field
|
||||
Embedded bool
|
||||
}
|
||||
|
||||
func (t *StructType) String() string {
|
||||
if t.StructName != "" {
|
||||
return t.Kind + " " + t.StructName
|
||||
}
|
||||
return t.Defn()
|
||||
}
|
||||
|
||||
func (t *StructType) Defn() string {
|
||||
s := t.Kind
|
||||
if t.StructName != "" {
|
||||
s += " " + t.StructName
|
||||
}
|
||||
if t.Incomplete {
|
||||
s += " /*incomplete*/"
|
||||
return s
|
||||
}
|
||||
s += " {"
|
||||
for i, f := range t.Field {
|
||||
if i > 0 {
|
||||
s += "; "
|
||||
}
|
||||
s += f.Name + " " + f.Type.String()
|
||||
s += "@" + strconv.FormatInt(f.ByteOffset, 10)
|
||||
if f.BitSize > 0 {
|
||||
s += " : " + strconv.FormatInt(f.BitSize, 10)
|
||||
s += "@" + strconv.FormatInt(f.BitOffset, 10)
|
||||
}
|
||||
}
|
||||
s += "}"
|
||||
return s
|
||||
}
|
||||
|
||||
// A SliceType represents a Go slice type. It looks like a StructType, describing
|
||||
// the runtime-internal structure, with extra fields.
|
||||
type SliceType struct {
|
||||
StructType
|
||||
ElemType Type
|
||||
}
|
||||
|
||||
func (t *SliceType) String() string {
|
||||
if t.Name != "" {
|
||||
return t.Name
|
||||
}
|
||||
return "[]" + t.ElemType.String()
|
||||
}
|
||||
|
||||
// A StringType represents a Go string type. It looks like a StructType, describing
|
||||
// the runtime-internal structure, but we wrap it for neatness.
|
||||
type StringType struct {
|
||||
StructType
|
||||
}
|
||||
|
||||
func (t *StringType) String() string {
|
||||
if t.Name != "" {
|
||||
return t.Name
|
||||
}
|
||||
return "string"
|
||||
}
|
||||
|
||||
// An InterfaceType represents a Go interface.
|
||||
type InterfaceType struct {
|
||||
TypedefType
|
||||
}
|
||||
|
||||
func (t *InterfaceType) String() string {
|
||||
if t.Name != "" {
|
||||
return t.Name
|
||||
}
|
||||
return "Interface"
|
||||
}
|
||||
|
||||
// An EnumType represents an enumerated type.
|
||||
// The only indication of its native integer type is its ByteSize
|
||||
// (inside CommonType).
|
||||
type EnumType struct {
|
||||
CommonType
|
||||
EnumName string
|
||||
Val []*EnumValue
|
||||
}
|
||||
|
||||
// An EnumValue represents a single enumeration value.
|
||||
type EnumValue struct {
|
||||
Name string
|
||||
Val int64
|
||||
}
|
||||
|
||||
func (t *EnumType) String() string {
|
||||
s := "enum"
|
||||
if t.EnumName != "" {
|
||||
s += " " + t.EnumName
|
||||
}
|
||||
s += " {"
|
||||
for i, v := range t.Val {
|
||||
if i > 0 {
|
||||
s += "; "
|
||||
}
|
||||
s += v.Name + "=" + strconv.FormatInt(v.Val, 10)
|
||||
}
|
||||
s += "}"
|
||||
return s
|
||||
}
|
||||
|
||||
// A FuncType represents a function type.
|
||||
type FuncType struct {
|
||||
CommonType
|
||||
ReturnType Type
|
||||
ParamType []Type
|
||||
}
|
||||
|
||||
func (t *FuncType) String() string {
|
||||
s := "func("
|
||||
for i, t := range t.ParamType {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += t.String()
|
||||
}
|
||||
s += ")"
|
||||
if t.ReturnType != nil {
|
||||
s += " " + t.ReturnType.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// A DotDotDotType represents the variadic ... function parameter.
|
||||
type DotDotDotType struct {
|
||||
CommonType
|
||||
}
|
||||
|
||||
func (t *DotDotDotType) String() string { return "..." }
|
||||
|
||||
// A TypedefType represents a named type.
|
||||
type TypedefType struct {
|
||||
CommonType
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (t *TypedefType) String() string { return t.Name }
|
||||
|
||||
func (t *TypedefType) Size() int64 { return t.Type.Size() }
|
||||
|
||||
// A MapType represents a Go map type. It looks like a TypedefType, describing
|
||||
// the runtime-internal structure, with extra fields.
|
||||
type MapType struct {
|
||||
TypedefType
|
||||
KeyType Type
|
||||
ElemType Type
|
||||
}
|
||||
|
||||
func (t *MapType) String() string {
|
||||
if t.Name != "" {
|
||||
return t.Name
|
||||
}
|
||||
return "map[" + t.KeyType.String() + "]" + t.ElemType.String()
|
||||
}
|
||||
|
||||
// A ChanType represents a Go channel type.
|
||||
type ChanType struct {
|
||||
TypedefType
|
||||
ElemType Type
|
||||
}
|
||||
|
||||
func (t *ChanType) String() string {
|
||||
if t.Name != "" {
|
||||
return t.Name
|
||||
}
|
||||
return "chan " + t.ElemType.String()
|
||||
}
|
||||
|
||||
// Type reads the type at off in the DWARF ``info'' section.
|
||||
func ReadType(d *dwarf.Data, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
|
||||
return readType(d, "info", d.Reader(), off, typeCache)
|
||||
}
|
||||
|
||||
func getKind(e *dwarf.Entry) reflect.Kind {
|
||||
integer, _ := e.Val(AttrGoKind).(int64)
|
||||
return reflect.Kind(integer)
|
||||
}
|
||||
|
||||
// readType reads a type from r at off of name using and updating a
|
||||
// type cache.
|
||||
func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
|
||||
if t, ok := typeCache[off]; ok {
|
||||
return t, nil
|
||||
}
|
||||
r.Seek(off)
|
||||
e, err := r.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addressSize := r.AddressSize()
|
||||
if e == nil || e.Offset != off {
|
||||
return nil, dwarf.DecodeError{name, off, "no type at offset"}
|
||||
}
|
||||
|
||||
// Parse type from dwarf.Entry.
|
||||
// Must always set typeCache[off] before calling
|
||||
// d.Type recursively, to handle circular types correctly.
|
||||
var typ Type
|
||||
|
||||
nextDepth := 0
|
||||
|
||||
// Get next child; set err if error happens.
|
||||
next := func() *dwarf.Entry {
|
||||
if !e.Children {
|
||||
return nil
|
||||
}
|
||||
// Only return direct children.
|
||||
// Skip over composite entries that happen to be nested
|
||||
// inside this one. Most DWARF generators wouldn't generate
|
||||
// such a thing, but clang does.
|
||||
// See golang.org/issue/6472.
|
||||
for {
|
||||
kid, err1 := r.Next()
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
return nil
|
||||
}
|
||||
if kid.Tag == 0 {
|
||||
if nextDepth > 0 {
|
||||
nextDepth--
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if kid.Children {
|
||||
nextDepth++
|
||||
}
|
||||
if nextDepth > 0 {
|
||||
continue
|
||||
}
|
||||
return kid
|
||||
}
|
||||
}
|
||||
|
||||
// Get Type referred to by dwarf.Entry's attr.
|
||||
// Set err if error happens. Not having a type is an error.
|
||||
typeOf := func(e *dwarf.Entry, attr dwarf.Attr) Type {
|
||||
tval := e.Val(attr)
|
||||
var t Type
|
||||
switch toff := tval.(type) {
|
||||
case dwarf.Offset:
|
||||
if t, err = readType(d, name, d.Reader(), toff, typeCache); err != nil {
|
||||
return nil
|
||||
}
|
||||
case uint64:
|
||||
err = dwarf.DecodeError{name, e.Offset, "DWARFv4 section debug_types unsupported"}
|
||||
return nil
|
||||
default:
|
||||
// It appears that no Type means "void".
|
||||
return new(VoidType)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
switch e.Tag {
|
||||
case dwarf.TagArrayType:
|
||||
// Multi-dimensional array. (DWARF v2 §5.4)
|
||||
// Attributes:
|
||||
// AttrType:subtype [required]
|
||||
// AttrStrideSize: distance in bits between each element of the array
|
||||
// AttrStride: distance in bytes between each element of the array
|
||||
// AttrByteSize: size of entire array
|
||||
// Children:
|
||||
// TagSubrangeType or TagEnumerationType giving one dimension.
|
||||
// dimensions are in left to right order.
|
||||
t := new(ArrayType)
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.ReflectKind = getKind(e)
|
||||
typ = t
|
||||
typeCache[off] = t
|
||||
if t.Type = typeOf(e, dwarf.AttrType); err != nil {
|
||||
goto Error
|
||||
}
|
||||
if bytes, ok := e.Val(dwarf.AttrStride).(int64); ok {
|
||||
t.StrideBitSize = 8 * bytes
|
||||
} else if bits, ok := e.Val(dwarf.AttrStrideSize).(int64); ok {
|
||||
t.StrideBitSize = bits
|
||||
} else {
|
||||
// If there's no stride specified, assume it's the size of the
|
||||
// array's element type.
|
||||
t.StrideBitSize = 8 * t.Type.Size()
|
||||
}
|
||||
|
||||
// Accumulate dimensions,
|
||||
ndim := 0
|
||||
for kid := next(); kid != nil; kid = next() {
|
||||
// TODO(rsc): Can also be TagEnumerationType
|
||||
// but haven't seen that in the wild yet.
|
||||
switch kid.Tag {
|
||||
case dwarf.TagSubrangeType:
|
||||
count, ok := kid.Val(dwarf.AttrCount).(int64)
|
||||
if !ok {
|
||||
// Old binaries may have an upper bound instead.
|
||||
count, ok = kid.Val(dwarf.AttrUpperBound).(int64)
|
||||
if ok {
|
||||
count++ // Length is one more than upper bound.
|
||||
} else {
|
||||
count = -1 // As in x[].
|
||||
}
|
||||
}
|
||||
if ndim == 0 {
|
||||
t.Count = count
|
||||
} else {
|
||||
// Multidimensional array.
|
||||
// Create new array type underneath this one.
|
||||
t.Type = &ArrayType{Type: t.Type, Count: count}
|
||||
}
|
||||
ndim++
|
||||
case dwarf.TagEnumerationType:
|
||||
err = dwarf.DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}
|
||||
goto Error
|
||||
}
|
||||
}
|
||||
if ndim == 0 {
|
||||
// LLVM generates this for x[].
|
||||
t.Count = -1
|
||||
}
|
||||
|
||||
case dwarf.TagBaseType:
|
||||
// Basic type. (DWARF v2 §5.1)
|
||||
// Attributes:
|
||||
// AttrName: name of base type in programming language of the compilation unit [required]
|
||||
// AttrEncoding: encoding value for type (encFloat etc) [required]
|
||||
// AttrByteSize: size of type in bytes [required]
|
||||
// AttrBitOffset: for sub-byte types, size in bits
|
||||
// AttrBitSize: for sub-byte types, bit offset of high order bit in the AttrByteSize bytes
|
||||
name, _ := e.Val(dwarf.AttrName).(string)
|
||||
enc, ok := e.Val(dwarf.AttrEncoding).(int64)
|
||||
if !ok {
|
||||
err = dwarf.DecodeError{name, e.Offset, "missing encoding attribute for " + name}
|
||||
goto Error
|
||||
}
|
||||
switch enc {
|
||||
default:
|
||||
err = dwarf.DecodeError{name, e.Offset, "unrecognized encoding attribute value"}
|
||||
goto Error
|
||||
|
||||
case encAddress:
|
||||
typ = new(AddrType)
|
||||
case encBoolean:
|
||||
typ = new(BoolType)
|
||||
case encComplexFloat:
|
||||
typ = new(ComplexType)
|
||||
if name == "complex" {
|
||||
// clang writes out 'complex' instead of 'complex float' or 'complex double'.
|
||||
// clang also writes out a byte size that we can use to distinguish.
|
||||
// See issue 8694.
|
||||
switch byteSize, _ := e.Val(dwarf.AttrByteSize).(int64); byteSize {
|
||||
case 8:
|
||||
name = "complex float"
|
||||
case 16:
|
||||
name = "complex double"
|
||||
}
|
||||
}
|
||||
case encFloat:
|
||||
typ = new(FloatType)
|
||||
case encSigned:
|
||||
typ = new(IntType)
|
||||
case encUnsigned:
|
||||
typ = new(UintType)
|
||||
case encSignedChar:
|
||||
typ = new(CharType)
|
||||
case encUnsignedChar:
|
||||
typ = new(UcharType)
|
||||
}
|
||||
typeCache[off] = typ
|
||||
t := typ.(interface {
|
||||
Basic() *BasicType
|
||||
}).Basic()
|
||||
t.Name = name
|
||||
t.BitSize, _ = e.Val(dwarf.AttrBitSize).(int64)
|
||||
t.BitOffset, _ = e.Val(dwarf.AttrBitOffset).(int64)
|
||||
t.ReflectKind = getKind(e)
|
||||
|
||||
case dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType:
|
||||
// Structure, union, or class type. (DWARF v2 §5.5)
|
||||
// Also Slices and Strings (Go-specific).
|
||||
// Attributes:
|
||||
// AttrName: name of struct, union, or class
|
||||
// AttrByteSize: byte size [required]
|
||||
// AttrDeclaration: if true, struct/union/class is incomplete
|
||||
// AttrGoElem: present for slices only.
|
||||
// Children:
|
||||
// TagMember to describe one member.
|
||||
// AttrName: name of member [required]
|
||||
// AttrType: type of member [required]
|
||||
// AttrByteSize: size in bytes
|
||||
// AttrBitOffset: bit offset within bytes for bit fields
|
||||
// AttrBitSize: bit size for bit fields
|
||||
// AttrDataMemberLoc: location within struct [required for struct, class]
|
||||
// There is much more to handle C++, all ignored for now.
|
||||
t := new(StructType)
|
||||
t.ReflectKind = getKind(e)
|
||||
switch t.ReflectKind {
|
||||
case reflect.Slice:
|
||||
slice := new(SliceType)
|
||||
slice.ElemType = typeOf(e, AttrGoElem)
|
||||
t = &slice.StructType
|
||||
typ = slice
|
||||
case reflect.String:
|
||||
str := new(StringType)
|
||||
t = &str.StructType
|
||||
typ = str
|
||||
default:
|
||||
typ = t
|
||||
}
|
||||
typeCache[off] = typ
|
||||
switch e.Tag {
|
||||
case dwarf.TagClassType:
|
||||
t.Kind = "class"
|
||||
case dwarf.TagStructType:
|
||||
t.Kind = "struct"
|
||||
case dwarf.TagUnionType:
|
||||
t.Kind = "union"
|
||||
}
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.StructName, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.Incomplete = e.Val(dwarf.AttrDeclaration) != nil
|
||||
t.Field = make([]*StructField, 0, 8)
|
||||
var lastFieldType Type
|
||||
var lastFieldBitOffset int64
|
||||
for kid := next(); kid != nil; kid = next() {
|
||||
if kid.Tag == dwarf.TagMember {
|
||||
f := new(StructField)
|
||||
if f.Type = typeOf(kid, dwarf.AttrType); err != nil {
|
||||
goto Error
|
||||
}
|
||||
switch loc := kid.Val(dwarf.AttrDataMemberLoc).(type) {
|
||||
case []byte:
|
||||
// TODO: Should have original compilation
|
||||
// unit here, not unknownFormat.
|
||||
if len(loc) == 0 {
|
||||
// Empty exprloc. f.ByteOffset=0.
|
||||
break
|
||||
}
|
||||
b := util.MakeBuf(d, util.UnknownFormat{}, "location", 0, loc)
|
||||
op_ := op.Opcode(b.Uint8())
|
||||
switch op_ {
|
||||
case op.DW_OP_plus_uconst:
|
||||
// Handle opcode sequence [DW_OP_plus_uconst <uleb128>]
|
||||
f.ByteOffset = int64(b.Uint())
|
||||
b.AssertEmpty()
|
||||
case op.DW_OP_consts:
|
||||
// Handle opcode sequence [DW_OP_consts <sleb128> DW_OP_plus]
|
||||
f.ByteOffset = b.Int()
|
||||
op_ = op.Opcode(b.Uint8())
|
||||
if op_ != op.DW_OP_plus {
|
||||
err = dwarf.DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op_)}
|
||||
goto Error
|
||||
}
|
||||
b.AssertEmpty()
|
||||
default:
|
||||
err = dwarf.DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op_)}
|
||||
goto Error
|
||||
}
|
||||
if b.Err != nil {
|
||||
err = b.Err
|
||||
goto Error
|
||||
}
|
||||
case int64:
|
||||
f.ByteOffset = loc
|
||||
}
|
||||
|
||||
haveBitOffset := false
|
||||
f.Name, _ = kid.Val(dwarf.AttrName).(string)
|
||||
f.ByteSize, _ = kid.Val(dwarf.AttrByteSize).(int64)
|
||||
f.BitOffset, haveBitOffset = kid.Val(dwarf.AttrBitOffset).(int64)
|
||||
f.BitSize, _ = kid.Val(dwarf.AttrBitSize).(int64)
|
||||
f.Embedded, _ = kid.Val(AttrGoEmbeddedField).(bool)
|
||||
t.Field = append(t.Field, f)
|
||||
|
||||
bito := f.BitOffset
|
||||
if !haveBitOffset {
|
||||
bito = f.ByteOffset * 8
|
||||
}
|
||||
if bito == lastFieldBitOffset && t.Kind != "union" {
|
||||
// Last field was zero width. Fix array length.
|
||||
// (DWARF writes out 0-length arrays as if they were 1-length arrays.)
|
||||
zeroArray(lastFieldType)
|
||||
}
|
||||
lastFieldType = f.Type
|
||||
lastFieldBitOffset = bito
|
||||
}
|
||||
}
|
||||
if t.Kind != "union" {
|
||||
b, ok := e.Val(dwarf.AttrByteSize).(int64)
|
||||
if ok && b*8 == lastFieldBitOffset {
|
||||
// Final field must be zero width. Fix array length.
|
||||
zeroArray(lastFieldType)
|
||||
}
|
||||
}
|
||||
|
||||
case dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType:
|
||||
// Type modifier (DWARF v2 §5.2)
|
||||
// Attributes:
|
||||
// AttrType: subtype
|
||||
t := new(QualType)
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.ReflectKind = getKind(e)
|
||||
typ = t
|
||||
typeCache[off] = t
|
||||
if t.Type = typeOf(e, dwarf.AttrType); err != nil {
|
||||
goto Error
|
||||
}
|
||||
switch e.Tag {
|
||||
case dwarf.TagConstType:
|
||||
t.Qual = "const"
|
||||
case dwarf.TagRestrictType:
|
||||
t.Qual = "restrict"
|
||||
case dwarf.TagVolatileType:
|
||||
t.Qual = "volatile"
|
||||
}
|
||||
|
||||
case dwarf.TagEnumerationType:
|
||||
// Enumeration type (DWARF v2 §5.6)
|
||||
// Attributes:
|
||||
// AttrName: enum name if any
|
||||
// AttrByteSize: bytes required to represent largest value
|
||||
// Children:
|
||||
// TagEnumerator:
|
||||
// AttrName: name of constant
|
||||
// AttrConstValue: value of constant
|
||||
t := new(EnumType)
|
||||
t.ReflectKind = getKind(e)
|
||||
typ = t
|
||||
typeCache[off] = t
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.EnumName, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.Val = make([]*EnumValue, 0, 8)
|
||||
for kid := next(); kid != nil; kid = next() {
|
||||
if kid.Tag == dwarf.TagEnumerator {
|
||||
f := new(EnumValue)
|
||||
f.Name, _ = kid.Val(dwarf.AttrName).(string)
|
||||
f.Val, _ = kid.Val(dwarf.AttrConstValue).(int64)
|
||||
n := len(t.Val)
|
||||
if n >= cap(t.Val) {
|
||||
val := make([]*EnumValue, n, n*2)
|
||||
copy(val, t.Val)
|
||||
t.Val = val
|
||||
}
|
||||
t.Val = t.Val[0 : n+1]
|
||||
t.Val[n] = f
|
||||
}
|
||||
}
|
||||
|
||||
case dwarf.TagPointerType:
|
||||
// Type modifier (DWARF v2 §5.2)
|
||||
// Attributes:
|
||||
// AttrType: subtype [not required! void* has no AttrType]
|
||||
// AttrAddrClass: address class [ignored]
|
||||
t := new(PtrType)
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.ReflectKind = getKind(e)
|
||||
typ = t
|
||||
typeCache[off] = t
|
||||
if e.Val(dwarf.AttrType) == nil {
|
||||
t.Type = &VoidType{}
|
||||
break
|
||||
}
|
||||
t.Type = typeOf(e, dwarf.AttrType)
|
||||
|
||||
case dwarf.TagSubroutineType:
|
||||
// Subroutine type. (DWARF v2 §5.7)
|
||||
// Attributes:
|
||||
// AttrType: type of return value if any
|
||||
// AttrName: possible name of type [ignored]
|
||||
// AttrPrototyped: whether used ANSI C prototype [ignored]
|
||||
// Children:
|
||||
// TagFormalParameter: typed parameter
|
||||
// AttrType: type of parameter
|
||||
// TagUnspecifiedParameter: final ...
|
||||
t := new(FuncType)
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.ReflectKind = getKind(e)
|
||||
typ = t
|
||||
typeCache[off] = t
|
||||
if t.ReturnType = typeOf(e, dwarf.AttrType); err != nil {
|
||||
goto Error
|
||||
}
|
||||
t.ParamType = make([]Type, 0, 8)
|
||||
for kid := next(); kid != nil; kid = next() {
|
||||
var tkid Type
|
||||
switch kid.Tag {
|
||||
default:
|
||||
continue
|
||||
case dwarf.TagFormalParameter:
|
||||
if tkid = typeOf(kid, dwarf.AttrType); err != nil {
|
||||
goto Error
|
||||
}
|
||||
case dwarf.TagUnspecifiedParameters:
|
||||
tkid = &DotDotDotType{}
|
||||
}
|
||||
t.ParamType = append(t.ParamType, tkid)
|
||||
}
|
||||
|
||||
case dwarf.TagTypedef:
|
||||
// Typedef (DWARF v2 §5.3)
|
||||
// Also maps and channels (Go-specific).
|
||||
// Attributes:
|
||||
// AttrName: name [required]
|
||||
// AttrType: type definition [required]
|
||||
// AttrGoKey: present for maps.
|
||||
// AttrGoElem: present for maps and channels.
|
||||
t := new(TypedefType)
|
||||
t.ReflectKind = getKind(e)
|
||||
switch t.ReflectKind {
|
||||
case reflect.Map:
|
||||
m := new(MapType)
|
||||
m.KeyType = typeOf(e, AttrGoKey)
|
||||
m.ElemType = typeOf(e, AttrGoElem)
|
||||
t = &m.TypedefType
|
||||
typ = m
|
||||
case reflect.Chan:
|
||||
c := new(ChanType)
|
||||
c.ElemType = typeOf(e, AttrGoElem)
|
||||
t = &c.TypedefType
|
||||
typ = c
|
||||
case reflect.Interface:
|
||||
it := new(InterfaceType)
|
||||
t = &it.TypedefType
|
||||
typ = it
|
||||
default:
|
||||
typ = t
|
||||
}
|
||||
typeCache[off] = typ
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
t.Type = typeOf(e, dwarf.AttrType)
|
||||
|
||||
case dwarf.TagUnspecifiedType:
|
||||
// Unspecified type (DWARF v3 §5.2)
|
||||
// Attributes:
|
||||
// AttrName: name
|
||||
t := new(UnspecifiedType)
|
||||
typ = t
|
||||
typeCache[off] = t
|
||||
t.Name, _ = e.Val(dwarf.AttrName).(string)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
goto Error
|
||||
}
|
||||
|
||||
typ.Common().Offset = off
|
||||
|
||||
{
|
||||
b, ok := e.Val(dwarf.AttrByteSize).(int64)
|
||||
if !ok {
|
||||
b = -1
|
||||
switch t := typ.(type) {
|
||||
case *TypedefType:
|
||||
b = t.Type.Size()
|
||||
case *MapType:
|
||||
b = t.Type.Size()
|
||||
case *ChanType:
|
||||
b = t.Type.Size()
|
||||
case *InterfaceType:
|
||||
b = t.Type.Size()
|
||||
case *PtrType:
|
||||
b = int64(addressSize)
|
||||
case *FuncType:
|
||||
// on Go < 1.10 function types do not have a DW_AT_byte_size attribute.
|
||||
b = int64(addressSize)
|
||||
}
|
||||
}
|
||||
typ.Common().ByteSize = b
|
||||
}
|
||||
return typ, nil
|
||||
|
||||
Error:
|
||||
// If the parse fails, take the type out of the cache
|
||||
// so that the next call with this offset doesn't hit
|
||||
// the cache and return success.
|
||||
delete(typeCache, off)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func zeroArray(t Type) {
|
||||
for {
|
||||
at, ok := t.(*ArrayType)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
at.Count = 0
|
||||
t = at.Type
|
||||
}
|
||||
}
|
152
vendor/github.com/derekparker/delve/pkg/dwarf/line/line_parser.go
generated
vendored
152
vendor/github.com/derekparker/delve/pkg/dwarf/line/line_parser.go
generated
vendored
@ -1,152 +0,0 @@
|
||||
package line
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type DebugLinePrologue struct {
|
||||
UnitLength uint32
|
||||
Version uint16
|
||||
Length uint32
|
||||
MinInstrLength uint8
|
||||
InitialIsStmt uint8
|
||||
LineBase int8
|
||||
LineRange uint8
|
||||
OpcodeBase uint8
|
||||
StdOpLengths []uint8
|
||||
}
|
||||
|
||||
type DebugLineInfo struct {
|
||||
Prologue *DebugLinePrologue
|
||||
IncludeDirs []string
|
||||
FileNames []*FileEntry
|
||||
Instructions []byte
|
||||
Lookup map[string]*FileEntry
|
||||
|
||||
Logf func(string, ...interface{})
|
||||
|
||||
// stateMachineCache[pc] is a state machine stopped at pc
|
||||
stateMachineCache map[uint64]*StateMachine
|
||||
|
||||
// lastMachineCache[pc] is a state machine stopped at an address after pc
|
||||
lastMachineCache map[uint64]*StateMachine
|
||||
|
||||
// staticBase is the address at which the executable is loaded, 0 for non-PIEs
|
||||
staticBase uint64
|
||||
}
|
||||
|
||||
type FileEntry struct {
|
||||
Path string
|
||||
DirIdx uint64
|
||||
LastModTime uint64
|
||||
Length uint64
|
||||
}
|
||||
|
||||
type DebugLines []*DebugLineInfo
|
||||
|
||||
// ParseAll parses all debug_line segments found in data
|
||||
func ParseAll(data []byte, logfn func(string, ...interface{}), staticBase uint64) DebugLines {
|
||||
var (
|
||||
lines = make(DebugLines, 0)
|
||||
buf = bytes.NewBuffer(data)
|
||||
)
|
||||
|
||||
// We have to parse multiple file name tables here.
|
||||
for buf.Len() > 0 {
|
||||
lines = append(lines, Parse("", buf, logfn, staticBase))
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
// Parse parses a single debug_line segment from buf. Compdir is the
|
||||
// DW_AT_comp_dir attribute of the associated compile unit.
|
||||
func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{}), staticBase uint64) *DebugLineInfo {
|
||||
dbl := new(DebugLineInfo)
|
||||
dbl.Logf = logfn
|
||||
dbl.staticBase = staticBase
|
||||
dbl.Lookup = make(map[string]*FileEntry)
|
||||
if compdir != "" {
|
||||
dbl.IncludeDirs = append(dbl.IncludeDirs, compdir)
|
||||
}
|
||||
|
||||
dbl.stateMachineCache = make(map[uint64]*StateMachine)
|
||||
dbl.lastMachineCache = make(map[uint64]*StateMachine)
|
||||
|
||||
parseDebugLinePrologue(dbl, buf)
|
||||
parseIncludeDirs(dbl, buf)
|
||||
parseFileEntries(dbl, buf)
|
||||
|
||||
// Instructions size calculation breakdown:
|
||||
// - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
|
||||
// - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
|
||||
// - So you have UnitLength - PrologueLength - (version_length_bytes(2) + prologue_length_bytes(4)).
|
||||
dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6))
|
||||
|
||||
return dbl
|
||||
}
|
||||
|
||||
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
|
||||
p := new(DebugLinePrologue)
|
||||
|
||||
p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4))
|
||||
p.Version = binary.LittleEndian.Uint16(buf.Next(2))
|
||||
p.Length = binary.LittleEndian.Uint32(buf.Next(4))
|
||||
p.MinInstrLength = uint8(buf.Next(1)[0])
|
||||
p.InitialIsStmt = uint8(buf.Next(1)[0])
|
||||
p.LineBase = int8(buf.Next(1)[0])
|
||||
p.LineRange = uint8(buf.Next(1)[0])
|
||||
p.OpcodeBase = uint8(buf.Next(1)[0])
|
||||
|
||||
p.StdOpLengths = make([]uint8, p.OpcodeBase-1)
|
||||
binary.Read(buf, binary.LittleEndian, &p.StdOpLengths)
|
||||
|
||||
dbl.Prologue = p
|
||||
}
|
||||
|
||||
func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
for {
|
||||
str, _ := util.ParseString(buf)
|
||||
if str == "" {
|
||||
break
|
||||
}
|
||||
|
||||
info.IncludeDirs = append(info.IncludeDirs, str)
|
||||
}
|
||||
}
|
||||
|
||||
func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
for {
|
||||
entry := readFileEntry(info, buf, true)
|
||||
if entry.Path == "" {
|
||||
break
|
||||
}
|
||||
|
||||
info.FileNames = append(info.FileNames, entry)
|
||||
info.Lookup[entry.Path] = entry
|
||||
}
|
||||
}
|
||||
|
||||
func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool) *FileEntry {
|
||||
entry := new(FileEntry)
|
||||
|
||||
entry.Path, _ = util.ParseString(buf)
|
||||
if entry.Path == "" && exitOnEmptyPath {
|
||||
return entry
|
||||
}
|
||||
|
||||
entry.DirIdx, _ = util.DecodeULEB128(buf)
|
||||
entry.LastModTime, _ = util.DecodeULEB128(buf)
|
||||
entry.Length, _ = util.DecodeULEB128(buf)
|
||||
if !filepath.IsAbs(entry.Path) {
|
||||
if entry.DirIdx >= 0 && entry.DirIdx < uint64(len(info.IncludeDirs)) {
|
||||
entry.Path = filepath.Join(info.IncludeDirs[entry.DirIdx], entry.Path)
|
||||
}
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
450
vendor/github.com/derekparker/delve/pkg/dwarf/line/state_machine.go
generated
vendored
450
vendor/github.com/derekparker/delve/pkg/dwarf/line/state_machine.go
generated
vendored
@ -1,450 +0,0 @@
|
||||
package line
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type Location struct {
|
||||
File string
|
||||
Line int
|
||||
Address uint64
|
||||
Delta int
|
||||
}
|
||||
|
||||
type StateMachine struct {
|
||||
dbl *DebugLineInfo
|
||||
file string
|
||||
line int
|
||||
address uint64
|
||||
column uint
|
||||
isStmt bool
|
||||
basicBlock bool
|
||||
endSeq bool
|
||||
lastDelta int
|
||||
prologueEnd bool
|
||||
epilogueBegin bool
|
||||
// valid is true if the current value of the state machine is the address of
|
||||
// an instruction (using the terminology used by DWARF spec the current
|
||||
// value of the state machine should be appended to the matrix representing
|
||||
// the compilation unit)
|
||||
valid bool
|
||||
|
||||
started bool
|
||||
|
||||
buf *bytes.Buffer // remaining instructions
|
||||
opcodes []opcodefn
|
||||
|
||||
definedFiles []*FileEntry // files defined with DW_LINE_define_file
|
||||
|
||||
lastAddress uint64
|
||||
lastFile string
|
||||
lastLine int
|
||||
}
|
||||
|
||||
type opcodeKind uint8
|
||||
|
||||
const (
|
||||
specialOpcode opcodeKind = iota
|
||||
standardOpcode
|
||||
extendedOpcode
|
||||
)
|
||||
|
||||
type opcodefn func(*StateMachine, *bytes.Buffer)
|
||||
|
||||
// Special opcodes
|
||||
const (
|
||||
DW_LNS_copy = 1
|
||||
DW_LNS_advance_pc = 2
|
||||
DW_LNS_advance_line = 3
|
||||
DW_LNS_set_file = 4
|
||||
DW_LNS_set_column = 5
|
||||
DW_LNS_negate_stmt = 6
|
||||
DW_LNS_set_basic_block = 7
|
||||
DW_LNS_const_add_pc = 8
|
||||
DW_LNS_fixed_advance_pc = 9
|
||||
DW_LNS_prologue_end = 10
|
||||
DW_LNS_epilogue_begin = 11
|
||||
)
|
||||
|
||||
// Extended opcodes
|
||||
const (
|
||||
DW_LINE_end_sequence = 1
|
||||
DW_LINE_set_address = 2
|
||||
DW_LINE_define_file = 3
|
||||
)
|
||||
|
||||
var standardopcodes = map[byte]opcodefn{
|
||||
DW_LNS_copy: copyfn,
|
||||
DW_LNS_advance_pc: advancepc,
|
||||
DW_LNS_advance_line: advanceline,
|
||||
DW_LNS_set_file: setfile,
|
||||
DW_LNS_set_column: setcolumn,
|
||||
DW_LNS_negate_stmt: negatestmt,
|
||||
DW_LNS_set_basic_block: setbasicblock,
|
||||
DW_LNS_const_add_pc: constaddpc,
|
||||
DW_LNS_fixed_advance_pc: fixedadvancepc,
|
||||
DW_LNS_prologue_end: prologueend,
|
||||
DW_LNS_epilogue_begin: epiloguebegin,
|
||||
}
|
||||
|
||||
var extendedopcodes = map[byte]opcodefn{
|
||||
DW_LINE_end_sequence: endsequence,
|
||||
DW_LINE_set_address: setaddress,
|
||||
DW_LINE_define_file: definefile,
|
||||
}
|
||||
|
||||
func newStateMachine(dbl *DebugLineInfo, instructions []byte) *StateMachine {
|
||||
opcodes := make([]opcodefn, len(standardopcodes)+1)
|
||||
opcodes[0] = execExtendedOpcode
|
||||
for op := range standardopcodes {
|
||||
opcodes[op] = standardopcodes[op]
|
||||
}
|
||||
sm := &StateMachine{dbl: dbl, file: dbl.FileNames[0].Path, line: 1, buf: bytes.NewBuffer(instructions), opcodes: opcodes, isStmt: dbl.Prologue.InitialIsStmt == uint8(1), address: dbl.staticBase}
|
||||
return sm
|
||||
}
|
||||
|
||||
// Returns all PCs for a given file/line. Useful for loops where the 'for' line
|
||||
// could be split amongst 2 PCs.
|
||||
func (lineInfo *DebugLineInfo) AllPCsForFileLine(f string, l int) (pcs []uint64) {
|
||||
if lineInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
lastAddr uint64
|
||||
sm = newStateMachine(lineInfo, lineInfo.Instructions)
|
||||
)
|
||||
|
||||
for {
|
||||
if err := sm.next(); err != nil {
|
||||
if lineInfo.Logf != nil {
|
||||
lineInfo.Logf("AllPCsForFileLine error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if sm.line == l && sm.file == f && sm.address != lastAddr && sm.isStmt && sm.valid {
|
||||
pcs = append(pcs, sm.address)
|
||||
lastAddr = sm.address
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var NoSourceError = errors.New("no source available")
|
||||
|
||||
// AllPCsBetween returns all PC addresses between begin and end (including both begin and end) that have the is_stmt flag set and do not belong to excludeFile:excludeLine
|
||||
func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64, excludeFile string, excludeLine int) ([]uint64, error) {
|
||||
if lineInfo == nil {
|
||||
return nil, NoSourceError
|
||||
}
|
||||
|
||||
var (
|
||||
pcs []uint64
|
||||
lastaddr uint64
|
||||
sm = newStateMachine(lineInfo, lineInfo.Instructions)
|
||||
)
|
||||
|
||||
for {
|
||||
if err := sm.next(); err != nil {
|
||||
if lineInfo.Logf != nil {
|
||||
lineInfo.Logf("AllPCsBetween error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if !sm.valid {
|
||||
continue
|
||||
}
|
||||
if sm.address > end {
|
||||
break
|
||||
}
|
||||
if (sm.address >= begin && sm.address > lastaddr) && sm.isStmt && ((sm.file != excludeFile) || (sm.line != excludeLine)) {
|
||||
lastaddr = sm.address
|
||||
pcs = append(pcs, sm.address)
|
||||
}
|
||||
}
|
||||
return pcs, nil
|
||||
}
|
||||
|
||||
// copy returns a copy of this state machine, running the returned state
|
||||
// machine will not affect sm.
|
||||
func (sm *StateMachine) copy() *StateMachine {
|
||||
var r StateMachine
|
||||
r = *sm
|
||||
r.buf = bytes.NewBuffer(sm.buf.Bytes())
|
||||
return &r
|
||||
}
|
||||
|
||||
func (lineInfo *DebugLineInfo) stateMachineForEntry(basePC uint64) (sm *StateMachine) {
|
||||
sm = lineInfo.stateMachineCache[basePC]
|
||||
if sm == nil {
|
||||
sm = newStateMachine(lineInfo, lineInfo.Instructions)
|
||||
sm.PCToLine(basePC)
|
||||
lineInfo.stateMachineCache[basePC] = sm
|
||||
}
|
||||
sm = sm.copy()
|
||||
return
|
||||
}
|
||||
|
||||
// PCToLine returns the filename and line number associated with pc.
|
||||
// If pc isn't found inside lineInfo's table it will return the filename and
|
||||
// line number associated with the closest PC address preceding pc.
|
||||
// basePC will be used for caching, it's normally the entry point for the
|
||||
// function containing pc.
|
||||
func (lineInfo *DebugLineInfo) PCToLine(basePC, pc uint64) (string, int) {
|
||||
if lineInfo == nil {
|
||||
return "", 0
|
||||
}
|
||||
if basePC > pc {
|
||||
panic(fmt.Errorf("basePC after pc %#x %#x", basePC, pc))
|
||||
}
|
||||
|
||||
var sm *StateMachine
|
||||
if basePC == 0 {
|
||||
sm = newStateMachine(lineInfo, lineInfo.Instructions)
|
||||
} else {
|
||||
// Try to use the last state machine that we used for this function, if
|
||||
// there isn't one or it's already past pc try to clone the cached state
|
||||
// machine stopped at the entry point of the function.
|
||||
// As a last resort start from the start of the debug_line section.
|
||||
sm = lineInfo.lastMachineCache[basePC]
|
||||
if sm == nil || sm.lastAddress > pc {
|
||||
sm = lineInfo.stateMachineForEntry(basePC)
|
||||
lineInfo.lastMachineCache[basePC] = sm
|
||||
}
|
||||
}
|
||||
|
||||
file, line, _ := sm.PCToLine(pc)
|
||||
return file, line
|
||||
}
|
||||
|
||||
func (sm *StateMachine) PCToLine(pc uint64) (string, int, bool) {
|
||||
if !sm.started {
|
||||
if err := sm.next(); err != nil {
|
||||
if sm.dbl.Logf != nil {
|
||||
sm.dbl.Logf("PCToLine error: %v", err)
|
||||
}
|
||||
return "", 0, false
|
||||
}
|
||||
}
|
||||
if sm.lastAddress > pc {
|
||||
return "", 0, false
|
||||
}
|
||||
for {
|
||||
if sm.valid {
|
||||
if sm.address > pc {
|
||||
return sm.lastFile, sm.lastLine, true
|
||||
}
|
||||
if sm.address == pc {
|
||||
return sm.file, sm.line, true
|
||||
}
|
||||
}
|
||||
if err := sm.next(); err != nil {
|
||||
if sm.dbl.Logf != nil {
|
||||
sm.dbl.Logf("PCToLine error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if sm.valid {
|
||||
return sm.file, sm.line, true
|
||||
}
|
||||
return "", 0, false
|
||||
}
|
||||
|
||||
// LineToPC returns the first PC address associated with filename:lineno.
|
||||
func (lineInfo *DebugLineInfo) LineToPC(filename string, lineno int) uint64 {
|
||||
if lineInfo == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
sm := newStateMachine(lineInfo, lineInfo.Instructions)
|
||||
|
||||
// if no instruction marked is_stmt is found fallback to the first
|
||||
// instruction assigned to the filename:line.
|
||||
var fallbackPC uint64
|
||||
|
||||
for {
|
||||
if err := sm.next(); err != nil {
|
||||
if lineInfo.Logf != nil && err != io.EOF {
|
||||
lineInfo.Logf("LineToPC error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if sm.line == lineno && sm.file == filename && sm.valid {
|
||||
if sm.isStmt {
|
||||
return sm.address
|
||||
} else if fallbackPC == 0 {
|
||||
fallbackPC = sm.address
|
||||
}
|
||||
}
|
||||
}
|
||||
return fallbackPC
|
||||
}
|
||||
|
||||
// PrologueEndPC returns the first PC address marked as prologue_end in the half open interval [start, end)
|
||||
func (lineInfo *DebugLineInfo) PrologueEndPC(start, end uint64) (pc uint64, file string, line int, ok bool) {
|
||||
sm := lineInfo.stateMachineForEntry(start)
|
||||
for {
|
||||
if sm.valid {
|
||||
if sm.address >= end {
|
||||
return 0, "", 0, false
|
||||
}
|
||||
if sm.prologueEnd {
|
||||
return sm.address, sm.file, sm.line, true
|
||||
}
|
||||
}
|
||||
if err := sm.next(); err != nil {
|
||||
if lineInfo.Logf != nil {
|
||||
lineInfo.Logf("PrologueEnd error: %v", err)
|
||||
}
|
||||
return 0, "", 0, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *StateMachine) next() error {
|
||||
sm.started = true
|
||||
if sm.valid {
|
||||
sm.lastAddress, sm.lastFile, sm.lastLine = sm.address, sm.file, sm.line
|
||||
|
||||
// valid is set by either a special opcode or a DW_LNS_copy, in both cases
|
||||
// we need to reset basic_block, prologue_end and epilogue_begin
|
||||
sm.basicBlock = false
|
||||
sm.prologueEnd = false
|
||||
sm.epilogueBegin = false
|
||||
}
|
||||
if sm.endSeq {
|
||||
sm.endSeq = false
|
||||
sm.file = sm.dbl.FileNames[0].Path
|
||||
sm.line = 1
|
||||
sm.column = 0
|
||||
sm.isStmt = sm.dbl.Prologue.InitialIsStmt == uint8(1)
|
||||
sm.basicBlock = false
|
||||
}
|
||||
b, err := sm.buf.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b < sm.dbl.Prologue.OpcodeBase {
|
||||
if int(b) < len(sm.opcodes) {
|
||||
sm.valid = false
|
||||
sm.opcodes[b](sm, sm.buf)
|
||||
} else {
|
||||
// unimplemented standard opcode, read the number of arguments specified
|
||||
// in the prologue and do nothing with them
|
||||
opnum := sm.dbl.Prologue.StdOpLengths[b-1]
|
||||
for i := 0; i < int(opnum); i++ {
|
||||
util.DecodeSLEB128(sm.buf)
|
||||
}
|
||||
fmt.Printf("unknown opcode\n")
|
||||
}
|
||||
} else {
|
||||
execSpecialOpcode(sm, b)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func execSpecialOpcode(sm *StateMachine, instr byte) {
|
||||
var (
|
||||
opcode = uint8(instr)
|
||||
decoded = opcode - sm.dbl.Prologue.OpcodeBase
|
||||
)
|
||||
|
||||
sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
|
||||
sm.line += sm.lastDelta
|
||||
sm.address += uint64(decoded/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
||||
sm.valid = true
|
||||
}
|
||||
|
||||
func execExtendedOpcode(sm *StateMachine, buf *bytes.Buffer) {
|
||||
_, _ = util.DecodeULEB128(buf)
|
||||
b, _ := buf.ReadByte()
|
||||
if fn, ok := extendedopcodes[b]; ok {
|
||||
fn(sm, buf)
|
||||
}
|
||||
}
|
||||
|
||||
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.valid = true
|
||||
}
|
||||
|
||||
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
addr, _ := util.DecodeULEB128(buf)
|
||||
sm.address += addr * uint64(sm.dbl.Prologue.MinInstrLength)
|
||||
}
|
||||
|
||||
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
|
||||
line, _ := util.DecodeSLEB128(buf)
|
||||
sm.line += int(line)
|
||||
sm.lastDelta = int(line)
|
||||
}
|
||||
|
||||
func setfile(sm *StateMachine, buf *bytes.Buffer) {
|
||||
i, _ := util.DecodeULEB128(buf)
|
||||
if i-1 < uint64(len(sm.dbl.FileNames)) {
|
||||
sm.file = sm.dbl.FileNames[i-1].Path
|
||||
} else {
|
||||
j := (i - 1) - uint64(len(sm.dbl.FileNames))
|
||||
if j < uint64(len(sm.definedFiles)) {
|
||||
sm.file = sm.definedFiles[j].Path
|
||||
} else {
|
||||
sm.file = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
|
||||
c, _ := util.DecodeULEB128(buf)
|
||||
sm.column = uint(c)
|
||||
}
|
||||
|
||||
func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.isStmt = !sm.isStmt
|
||||
}
|
||||
|
||||
func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.basicBlock = true
|
||||
}
|
||||
|
||||
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.address += uint64((255-sm.dbl.Prologue.OpcodeBase)/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
||||
}
|
||||
|
||||
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
var operand uint16
|
||||
binary.Read(buf, binary.LittleEndian, &operand)
|
||||
|
||||
sm.address += uint64(operand)
|
||||
}
|
||||
|
||||
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.endSeq = true
|
||||
sm.valid = true
|
||||
}
|
||||
|
||||
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
|
||||
var addr uint64
|
||||
|
||||
binary.Read(buf, binary.LittleEndian, &addr)
|
||||
|
||||
sm.address = addr + sm.dbl.staticBase
|
||||
}
|
||||
|
||||
func definefile(sm *StateMachine, buf *bytes.Buffer) {
|
||||
entry := readFileEntry(sm.dbl, sm.buf, false)
|
||||
sm.definedFiles = append(sm.definedFiles, entry)
|
||||
}
|
||||
|
||||
func prologueend(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.prologueEnd = true
|
||||
}
|
||||
|
||||
func epiloguebegin(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.epilogueBegin = true
|
||||
}
|
197
vendor/github.com/derekparker/delve/pkg/dwarf/op/op.go
generated
vendored
197
vendor/github.com/derekparker/delve/pkg/dwarf/op/op.go
generated
vendored
@ -1,197 +0,0 @@
|
||||
package op
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type Opcode byte
|
||||
|
||||
//go:generate go run ../../../scripts/gen-opcodes.go opcodes.table opcodes.go
|
||||
|
||||
type stackfn func(Opcode, *context) error
|
||||
|
||||
type context struct {
|
||||
buf *bytes.Buffer
|
||||
stack []int64
|
||||
pieces []Piece
|
||||
reg bool
|
||||
|
||||
DwarfRegisters
|
||||
}
|
||||
|
||||
// Piece is a piece of memory stored either at an address or in a register.
|
||||
type Piece struct {
|
||||
Size int
|
||||
Addr int64
|
||||
RegNum uint64
|
||||
IsRegister bool
|
||||
}
|
||||
|
||||
// ExecuteStackProgram executes a DWARF location expression and returns
|
||||
// either an address (int64), or a slice of Pieces for location expressions
|
||||
// that don't evaluate to an address (such as register and composite expressions).
|
||||
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, []Piece, error) {
|
||||
ctxt := &context{
|
||||
buf: bytes.NewBuffer(instructions),
|
||||
stack: make([]int64, 0, 3),
|
||||
DwarfRegisters: regs,
|
||||
}
|
||||
|
||||
for {
|
||||
opcodeByte, err := ctxt.buf.ReadByte()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
opcode := Opcode(opcodeByte)
|
||||
if ctxt.reg && opcode != DW_OP_piece {
|
||||
break
|
||||
}
|
||||
fn, ok := oplut[opcode]
|
||||
if !ok {
|
||||
return 0, nil, fmt.Errorf("invalid instruction %#v", opcode)
|
||||
}
|
||||
|
||||
err = fn(opcode, ctxt)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if ctxt.pieces != nil {
|
||||
return 0, ctxt.pieces, nil
|
||||
}
|
||||
|
||||
if len(ctxt.stack) == 0 {
|
||||
return 0, nil, errors.New("empty OP stack")
|
||||
}
|
||||
|
||||
return ctxt.stack[len(ctxt.stack)-1], nil, nil
|
||||
}
|
||||
|
||||
// PrettyPrint prints instructions to out.
|
||||
func PrettyPrint(out io.Writer, instructions []byte) {
|
||||
in := bytes.NewBuffer(instructions)
|
||||
|
||||
for {
|
||||
opcode, err := in.ReadByte()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if name, hasname := opcodeName[Opcode(opcode)]; hasname {
|
||||
io.WriteString(out, name)
|
||||
out.Write([]byte{' '})
|
||||
} else {
|
||||
fmt.Fprintf(out, "%#x ", opcode)
|
||||
}
|
||||
for _, arg := range opcodeArgs[Opcode(opcode)] {
|
||||
switch arg {
|
||||
case 's':
|
||||
n, _ := util.DecodeSLEB128(in)
|
||||
fmt.Fprintf(out, "%#x ", n)
|
||||
case 'u':
|
||||
n, _ := util.DecodeULEB128(in)
|
||||
fmt.Fprintf(out, "%#x ", n)
|
||||
case '1':
|
||||
var x uint8
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case '2':
|
||||
var x uint16
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case '4':
|
||||
var x uint32
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case '8':
|
||||
var x uint64
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case 'B':
|
||||
sz, _ := util.DecodeULEB128(in)
|
||||
data := make([]byte, sz)
|
||||
sz2, _ := in.Read(data)
|
||||
data = data[:sz2]
|
||||
fmt.Fprintf(out, "%d [%x] ", sz, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func callframecfa(opcode Opcode, ctxt *context) error {
|
||||
if ctxt.CFA == 0 {
|
||||
return fmt.Errorf("Could not retrieve CFA for current PC")
|
||||
}
|
||||
ctxt.stack = append(ctxt.stack, int64(ctxt.CFA))
|
||||
return nil
|
||||
}
|
||||
|
||||
func addr(opcode Opcode, ctxt *context) error {
|
||||
ctxt.stack = append(ctxt.stack, int64(binary.LittleEndian.Uint64(ctxt.buf.Next(8))+ctxt.StaticBase))
|
||||
return nil
|
||||
}
|
||||
|
||||
func plus(opcode Opcode, ctxt *context) error {
|
||||
var (
|
||||
slen = len(ctxt.stack)
|
||||
digits = ctxt.stack[slen-2 : slen]
|
||||
st = ctxt.stack[:slen-2]
|
||||
)
|
||||
|
||||
ctxt.stack = append(st, digits[0]+digits[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func plusuconsts(opcode Opcode, ctxt *context) error {
|
||||
slen := len(ctxt.stack)
|
||||
num, _ := util.DecodeULEB128(ctxt.buf)
|
||||
ctxt.stack[slen-1] = ctxt.stack[slen-1] + int64(num)
|
||||
return nil
|
||||
}
|
||||
|
||||
func consts(opcode Opcode, ctxt *context) error {
|
||||
num, _ := util.DecodeSLEB128(ctxt.buf)
|
||||
ctxt.stack = append(ctxt.stack, num)
|
||||
return nil
|
||||
}
|
||||
|
||||
func framebase(opcode Opcode, ctxt *context) error {
|
||||
num, _ := util.DecodeSLEB128(ctxt.buf)
|
||||
ctxt.stack = append(ctxt.stack, ctxt.FrameBase+num)
|
||||
return nil
|
||||
}
|
||||
|
||||
func register(opcode Opcode, ctxt *context) error {
|
||||
ctxt.reg = true
|
||||
if opcode == DW_OP_regx {
|
||||
n, _ := util.DecodeSLEB128(ctxt.buf)
|
||||
ctxt.pieces = append(ctxt.pieces, Piece{IsRegister: true, RegNum: uint64(n)})
|
||||
} else {
|
||||
ctxt.pieces = append(ctxt.pieces, Piece{IsRegister: true, RegNum: uint64(opcode - DW_OP_reg0)})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func piece(opcode Opcode, ctxt *context) error {
|
||||
sz, _ := util.DecodeULEB128(ctxt.buf)
|
||||
if ctxt.reg {
|
||||
ctxt.reg = false
|
||||
ctxt.pieces[len(ctxt.pieces)-1].Size = int(sz)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(ctxt.stack) == 0 {
|
||||
return errors.New("empty OP stack")
|
||||
}
|
||||
|
||||
addr := ctxt.stack[len(ctxt.stack)-1]
|
||||
ctxt.pieces = append(ctxt.pieces, Piece{Size: int(sz), Addr: addr})
|
||||
ctxt.stack = ctxt.stack[:0]
|
||||
return nil
|
||||
}
|
515
vendor/github.com/derekparker/delve/pkg/dwarf/op/opcodes.go
generated
vendored
515
vendor/github.com/derekparker/delve/pkg/dwarf/op/opcodes.go
generated
vendored
@ -1,515 +0,0 @@
|
||||
// THIS FILE IS AUTOGENERATED, EDIT opcodes.table INSTEAD
|
||||
|
||||
package op
|
||||
|
||||
const (
|
||||
DW_OP_addr Opcode = 0x03
|
||||
DW_OP_deref Opcode = 0x06
|
||||
DW_OP_const1u Opcode = 0x08
|
||||
DW_OP_const1s Opcode = 0x09
|
||||
DW_OP_const2u Opcode = 0x0a
|
||||
DW_OP_const2s Opcode = 0x0b
|
||||
DW_OP_const4u Opcode = 0x0c
|
||||
DW_OP_const4s Opcode = 0x0d
|
||||
DW_OP_const8u Opcode = 0x0e
|
||||
DW_OP_const8s Opcode = 0x0f
|
||||
DW_OP_constu Opcode = 0x10
|
||||
DW_OP_consts Opcode = 0x11
|
||||
DW_OP_dup Opcode = 0x12
|
||||
DW_OP_drop Opcode = 0x13
|
||||
DW_OP_over Opcode = 0x14
|
||||
DW_OP_pick Opcode = 0x15
|
||||
DW_OP_swap Opcode = 0x16
|
||||
DW_OP_rot Opcode = 0x17
|
||||
DW_OP_xderef Opcode = 0x18
|
||||
DW_OP_abs Opcode = 0x19
|
||||
DW_OP_and Opcode = 0x1a
|
||||
DW_OP_div Opcode = 0x1b
|
||||
DW_OP_minus Opcode = 0x1c
|
||||
DW_OP_mod Opcode = 0x1d
|
||||
DW_OP_mul Opcode = 0x1e
|
||||
DW_OP_neg Opcode = 0x1f
|
||||
DW_OP_not Opcode = 0x20
|
||||
DW_OP_or Opcode = 0x21
|
||||
DW_OP_plus Opcode = 0x22
|
||||
DW_OP_plus_uconst Opcode = 0x23
|
||||
DW_OP_shl Opcode = 0x24
|
||||
DW_OP_shr Opcode = 0x25
|
||||
DW_OP_shra Opcode = 0x26
|
||||
DW_OP_xor Opcode = 0x27
|
||||
DW_OP_bra Opcode = 0x28
|
||||
DW_OP_eq Opcode = 0x29
|
||||
DW_OP_ge Opcode = 0x2a
|
||||
DW_OP_gt Opcode = 0x2b
|
||||
DW_OP_le Opcode = 0x2c
|
||||
DW_OP_lt Opcode = 0x2d
|
||||
DW_OP_ne Opcode = 0x2e
|
||||
DW_OP_skip Opcode = 0x2f
|
||||
DW_OP_lit0 Opcode = 0x30
|
||||
DW_OP_lit1 Opcode = 0x31
|
||||
DW_OP_lit2 Opcode = 0x32
|
||||
DW_OP_lit3 Opcode = 0x33
|
||||
DW_OP_lit4 Opcode = 0x34
|
||||
DW_OP_lit5 Opcode = 0x35
|
||||
DW_OP_lit6 Opcode = 0x36
|
||||
DW_OP_lit7 Opcode = 0x37
|
||||
DW_OP_lit8 Opcode = 0x38
|
||||
DW_OP_lit9 Opcode = 0x39
|
||||
DW_OP_lit10 Opcode = 0x3a
|
||||
DW_OP_lit11 Opcode = 0x3b
|
||||
DW_OP_lit12 Opcode = 0x3c
|
||||
DW_OP_lit13 Opcode = 0x3d
|
||||
DW_OP_lit14 Opcode = 0x3e
|
||||
DW_OP_lit15 Opcode = 0x3f
|
||||
DW_OP_lit16 Opcode = 0x40
|
||||
DW_OP_lit17 Opcode = 0x41
|
||||
DW_OP_lit18 Opcode = 0x42
|
||||
DW_OP_lit19 Opcode = 0x43
|
||||
DW_OP_lit20 Opcode = 0x44
|
||||
DW_OP_lit21 Opcode = 0x45
|
||||
DW_OP_lit22 Opcode = 0x46
|
||||
DW_OP_lit23 Opcode = 0x47
|
||||
DW_OP_lit24 Opcode = 0x48
|
||||
DW_OP_lit25 Opcode = 0x49
|
||||
DW_OP_lit26 Opcode = 0x4a
|
||||
DW_OP_lit27 Opcode = 0x4b
|
||||
DW_OP_lit28 Opcode = 0x4c
|
||||
DW_OP_lit29 Opcode = 0x4d
|
||||
DW_OP_lit30 Opcode = 0x4e
|
||||
DW_OP_lit31 Opcode = 0x4f
|
||||
DW_OP_reg0 Opcode = 0x50
|
||||
DW_OP_reg1 Opcode = 0x51
|
||||
DW_OP_reg2 Opcode = 0x52
|
||||
DW_OP_reg3 Opcode = 0x53
|
||||
DW_OP_reg4 Opcode = 0x54
|
||||
DW_OP_reg5 Opcode = 0x55
|
||||
DW_OP_reg6 Opcode = 0x56
|
||||
DW_OP_reg7 Opcode = 0x57
|
||||
DW_OP_reg8 Opcode = 0x58
|
||||
DW_OP_reg9 Opcode = 0x59
|
||||
DW_OP_reg10 Opcode = 0x5a
|
||||
DW_OP_reg11 Opcode = 0x5b
|
||||
DW_OP_reg12 Opcode = 0x5c
|
||||
DW_OP_reg13 Opcode = 0x5d
|
||||
DW_OP_reg14 Opcode = 0x5e
|
||||
DW_OP_reg15 Opcode = 0x5f
|
||||
DW_OP_reg16 Opcode = 0x60
|
||||
DW_OP_reg17 Opcode = 0x61
|
||||
DW_OP_reg18 Opcode = 0x62
|
||||
DW_OP_reg19 Opcode = 0x63
|
||||
DW_OP_reg20 Opcode = 0x64
|
||||
DW_OP_reg21 Opcode = 0x65
|
||||
DW_OP_reg22 Opcode = 0x66
|
||||
DW_OP_reg23 Opcode = 0x67
|
||||
DW_OP_reg24 Opcode = 0x68
|
||||
DW_OP_reg25 Opcode = 0x69
|
||||
DW_OP_reg26 Opcode = 0x6a
|
||||
DW_OP_reg27 Opcode = 0x6b
|
||||
DW_OP_reg28 Opcode = 0x6c
|
||||
DW_OP_reg29 Opcode = 0x6d
|
||||
DW_OP_reg30 Opcode = 0x6e
|
||||
DW_OP_reg31 Opcode = 0x6f
|
||||
DW_OP_breg0 Opcode = 0x70
|
||||
DW_OP_breg1 Opcode = 0x71
|
||||
DW_OP_breg2 Opcode = 0x72
|
||||
DW_OP_breg3 Opcode = 0x73
|
||||
DW_OP_breg4 Opcode = 0x74
|
||||
DW_OP_breg5 Opcode = 0x75
|
||||
DW_OP_breg6 Opcode = 0x76
|
||||
DW_OP_breg7 Opcode = 0x77
|
||||
DW_OP_breg8 Opcode = 0x78
|
||||
DW_OP_breg9 Opcode = 0x79
|
||||
DW_OP_breg10 Opcode = 0x7a
|
||||
DW_OP_breg11 Opcode = 0x7b
|
||||
DW_OP_breg12 Opcode = 0x7c
|
||||
DW_OP_breg13 Opcode = 0x7d
|
||||
DW_OP_breg14 Opcode = 0x7e
|
||||
DW_OP_breg15 Opcode = 0x7f
|
||||
DW_OP_breg16 Opcode = 0x80
|
||||
DW_OP_breg17 Opcode = 0x81
|
||||
DW_OP_breg18 Opcode = 0x82
|
||||
DW_OP_breg19 Opcode = 0x83
|
||||
DW_OP_breg20 Opcode = 0x84
|
||||
DW_OP_breg21 Opcode = 0x85
|
||||
DW_OP_breg22 Opcode = 0x86
|
||||
DW_OP_breg23 Opcode = 0x87
|
||||
DW_OP_breg24 Opcode = 0x88
|
||||
DW_OP_breg25 Opcode = 0x89
|
||||
DW_OP_breg26 Opcode = 0x8a
|
||||
DW_OP_breg27 Opcode = 0x8b
|
||||
DW_OP_breg28 Opcode = 0x8c
|
||||
DW_OP_breg29 Opcode = 0x8d
|
||||
DW_OP_breg30 Opcode = 0x8e
|
||||
DW_OP_breg31 Opcode = 0x8f
|
||||
DW_OP_regx Opcode = 0x90
|
||||
DW_OP_fbreg Opcode = 0x91
|
||||
DW_OP_bregx Opcode = 0x92
|
||||
DW_OP_piece Opcode = 0x93
|
||||
DW_OP_deref_size Opcode = 0x94
|
||||
DW_OP_xderef_size Opcode = 0x95
|
||||
DW_OP_nop Opcode = 0x96
|
||||
DW_OP_push_object_address Opcode = 0x97
|
||||
DW_OP_call2 Opcode = 0x98
|
||||
DW_OP_call4 Opcode = 0x99
|
||||
DW_OP_call_ref Opcode = 0x9a
|
||||
DW_OP_form_tls_address Opcode = 0x9b
|
||||
DW_OP_call_frame_cfa Opcode = 0x9c
|
||||
DW_OP_bit_piece Opcode = 0x9d
|
||||
DW_OP_implicit_value Opcode = 0x9e
|
||||
DW_OP_stack_value Opcode = 0x9f
|
||||
)
|
||||
|
||||
var opcodeName = map[Opcode]string{
|
||||
DW_OP_addr: "DW_OP_addr",
|
||||
DW_OP_deref: "DW_OP_deref",
|
||||
DW_OP_const1u: "DW_OP_const1u",
|
||||
DW_OP_const1s: "DW_OP_const1s",
|
||||
DW_OP_const2u: "DW_OP_const2u",
|
||||
DW_OP_const2s: "DW_OP_const2s",
|
||||
DW_OP_const4u: "DW_OP_const4u",
|
||||
DW_OP_const4s: "DW_OP_const4s",
|
||||
DW_OP_const8u: "DW_OP_const8u",
|
||||
DW_OP_const8s: "DW_OP_const8s",
|
||||
DW_OP_constu: "DW_OP_constu",
|
||||
DW_OP_consts: "DW_OP_consts",
|
||||
DW_OP_dup: "DW_OP_dup",
|
||||
DW_OP_drop: "DW_OP_drop",
|
||||
DW_OP_over: "DW_OP_over",
|
||||
DW_OP_pick: "DW_OP_pick",
|
||||
DW_OP_swap: "DW_OP_swap",
|
||||
DW_OP_rot: "DW_OP_rot",
|
||||
DW_OP_xderef: "DW_OP_xderef",
|
||||
DW_OP_abs: "DW_OP_abs",
|
||||
DW_OP_and: "DW_OP_and",
|
||||
DW_OP_div: "DW_OP_div",
|
||||
DW_OP_minus: "DW_OP_minus",
|
||||
DW_OP_mod: "DW_OP_mod",
|
||||
DW_OP_mul: "DW_OP_mul",
|
||||
DW_OP_neg: "DW_OP_neg",
|
||||
DW_OP_not: "DW_OP_not",
|
||||
DW_OP_or: "DW_OP_or",
|
||||
DW_OP_plus: "DW_OP_plus",
|
||||
DW_OP_plus_uconst: "DW_OP_plus_uconst",
|
||||
DW_OP_shl: "DW_OP_shl",
|
||||
DW_OP_shr: "DW_OP_shr",
|
||||
DW_OP_shra: "DW_OP_shra",
|
||||
DW_OP_xor: "DW_OP_xor",
|
||||
DW_OP_bra: "DW_OP_bra",
|
||||
DW_OP_eq: "DW_OP_eq",
|
||||
DW_OP_ge: "DW_OP_ge",
|
||||
DW_OP_gt: "DW_OP_gt",
|
||||
DW_OP_le: "DW_OP_le",
|
||||
DW_OP_lt: "DW_OP_lt",
|
||||
DW_OP_ne: "DW_OP_ne",
|
||||
DW_OP_skip: "DW_OP_skip",
|
||||
DW_OP_lit0: "DW_OP_lit0",
|
||||
DW_OP_lit1: "DW_OP_lit1",
|
||||
DW_OP_lit2: "DW_OP_lit2",
|
||||
DW_OP_lit3: "DW_OP_lit3",
|
||||
DW_OP_lit4: "DW_OP_lit4",
|
||||
DW_OP_lit5: "DW_OP_lit5",
|
||||
DW_OP_lit6: "DW_OP_lit6",
|
||||
DW_OP_lit7: "DW_OP_lit7",
|
||||
DW_OP_lit8: "DW_OP_lit8",
|
||||
DW_OP_lit9: "DW_OP_lit9",
|
||||
DW_OP_lit10: "DW_OP_lit10",
|
||||
DW_OP_lit11: "DW_OP_lit11",
|
||||
DW_OP_lit12: "DW_OP_lit12",
|
||||
DW_OP_lit13: "DW_OP_lit13",
|
||||
DW_OP_lit14: "DW_OP_lit14",
|
||||
DW_OP_lit15: "DW_OP_lit15",
|
||||
DW_OP_lit16: "DW_OP_lit16",
|
||||
DW_OP_lit17: "DW_OP_lit17",
|
||||
DW_OP_lit18: "DW_OP_lit18",
|
||||
DW_OP_lit19: "DW_OP_lit19",
|
||||
DW_OP_lit20: "DW_OP_lit20",
|
||||
DW_OP_lit21: "DW_OP_lit21",
|
||||
DW_OP_lit22: "DW_OP_lit22",
|
||||
DW_OP_lit23: "DW_OP_lit23",
|
||||
DW_OP_lit24: "DW_OP_lit24",
|
||||
DW_OP_lit25: "DW_OP_lit25",
|
||||
DW_OP_lit26: "DW_OP_lit26",
|
||||
DW_OP_lit27: "DW_OP_lit27",
|
||||
DW_OP_lit28: "DW_OP_lit28",
|
||||
DW_OP_lit29: "DW_OP_lit29",
|
||||
DW_OP_lit30: "DW_OP_lit30",
|
||||
DW_OP_lit31: "DW_OP_lit31",
|
||||
DW_OP_reg0: "DW_OP_reg0",
|
||||
DW_OP_reg1: "DW_OP_reg1",
|
||||
DW_OP_reg2: "DW_OP_reg2",
|
||||
DW_OP_reg3: "DW_OP_reg3",
|
||||
DW_OP_reg4: "DW_OP_reg4",
|
||||
DW_OP_reg5: "DW_OP_reg5",
|
||||
DW_OP_reg6: "DW_OP_reg6",
|
||||
DW_OP_reg7: "DW_OP_reg7",
|
||||
DW_OP_reg8: "DW_OP_reg8",
|
||||
DW_OP_reg9: "DW_OP_reg9",
|
||||
DW_OP_reg10: "DW_OP_reg10",
|
||||
DW_OP_reg11: "DW_OP_reg11",
|
||||
DW_OP_reg12: "DW_OP_reg12",
|
||||
DW_OP_reg13: "DW_OP_reg13",
|
||||
DW_OP_reg14: "DW_OP_reg14",
|
||||
DW_OP_reg15: "DW_OP_reg15",
|
||||
DW_OP_reg16: "DW_OP_reg16",
|
||||
DW_OP_reg17: "DW_OP_reg17",
|
||||
DW_OP_reg18: "DW_OP_reg18",
|
||||
DW_OP_reg19: "DW_OP_reg19",
|
||||
DW_OP_reg20: "DW_OP_reg20",
|
||||
DW_OP_reg21: "DW_OP_reg21",
|
||||
DW_OP_reg22: "DW_OP_reg22",
|
||||
DW_OP_reg23: "DW_OP_reg23",
|
||||
DW_OP_reg24: "DW_OP_reg24",
|
||||
DW_OP_reg25: "DW_OP_reg25",
|
||||
DW_OP_reg26: "DW_OP_reg26",
|
||||
DW_OP_reg27: "DW_OP_reg27",
|
||||
DW_OP_reg28: "DW_OP_reg28",
|
||||
DW_OP_reg29: "DW_OP_reg29",
|
||||
DW_OP_reg30: "DW_OP_reg30",
|
||||
DW_OP_reg31: "DW_OP_reg31",
|
||||
DW_OP_breg0: "DW_OP_breg0",
|
||||
DW_OP_breg1: "DW_OP_breg1",
|
||||
DW_OP_breg2: "DW_OP_breg2",
|
||||
DW_OP_breg3: "DW_OP_breg3",
|
||||
DW_OP_breg4: "DW_OP_breg4",
|
||||
DW_OP_breg5: "DW_OP_breg5",
|
||||
DW_OP_breg6: "DW_OP_breg6",
|
||||
DW_OP_breg7: "DW_OP_breg7",
|
||||
DW_OP_breg8: "DW_OP_breg8",
|
||||
DW_OP_breg9: "DW_OP_breg9",
|
||||
DW_OP_breg10: "DW_OP_breg10",
|
||||
DW_OP_breg11: "DW_OP_breg11",
|
||||
DW_OP_breg12: "DW_OP_breg12",
|
||||
DW_OP_breg13: "DW_OP_breg13",
|
||||
DW_OP_breg14: "DW_OP_breg14",
|
||||
DW_OP_breg15: "DW_OP_breg15",
|
||||
DW_OP_breg16: "DW_OP_breg16",
|
||||
DW_OP_breg17: "DW_OP_breg17",
|
||||
DW_OP_breg18: "DW_OP_breg18",
|
||||
DW_OP_breg19: "DW_OP_breg19",
|
||||
DW_OP_breg20: "DW_OP_breg20",
|
||||
DW_OP_breg21: "DW_OP_breg21",
|
||||
DW_OP_breg22: "DW_OP_breg22",
|
||||
DW_OP_breg23: "DW_OP_breg23",
|
||||
DW_OP_breg24: "DW_OP_breg24",
|
||||
DW_OP_breg25: "DW_OP_breg25",
|
||||
DW_OP_breg26: "DW_OP_breg26",
|
||||
DW_OP_breg27: "DW_OP_breg27",
|
||||
DW_OP_breg28: "DW_OP_breg28",
|
||||
DW_OP_breg29: "DW_OP_breg29",
|
||||
DW_OP_breg30: "DW_OP_breg30",
|
||||
DW_OP_breg31: "DW_OP_breg31",
|
||||
DW_OP_regx: "DW_OP_regx",
|
||||
DW_OP_fbreg: "DW_OP_fbreg",
|
||||
DW_OP_bregx: "DW_OP_bregx",
|
||||
DW_OP_piece: "DW_OP_piece",
|
||||
DW_OP_deref_size: "DW_OP_deref_size",
|
||||
DW_OP_xderef_size: "DW_OP_xderef_size",
|
||||
DW_OP_nop: "DW_OP_nop",
|
||||
DW_OP_push_object_address: "DW_OP_push_object_address",
|
||||
DW_OP_call2: "DW_OP_call2",
|
||||
DW_OP_call4: "DW_OP_call4",
|
||||
DW_OP_call_ref: "DW_OP_call_ref",
|
||||
DW_OP_form_tls_address: "DW_OP_form_tls_address",
|
||||
DW_OP_call_frame_cfa: "DW_OP_call_frame_cfa",
|
||||
DW_OP_bit_piece: "DW_OP_bit_piece",
|
||||
DW_OP_implicit_value: "DW_OP_implicit_value",
|
||||
DW_OP_stack_value: "DW_OP_stack_value",
|
||||
}
|
||||
var opcodeArgs = map[Opcode]string{
|
||||
DW_OP_addr: "8",
|
||||
DW_OP_deref: "",
|
||||
DW_OP_const1u: "1",
|
||||
DW_OP_const1s: "1",
|
||||
DW_OP_const2u: "2",
|
||||
DW_OP_const2s: "2",
|
||||
DW_OP_const4u: "4",
|
||||
DW_OP_const4s: "4",
|
||||
DW_OP_const8u: "8",
|
||||
DW_OP_const8s: "8",
|
||||
DW_OP_constu: "u",
|
||||
DW_OP_consts: "s",
|
||||
DW_OP_dup: "",
|
||||
DW_OP_drop: "",
|
||||
DW_OP_over: "",
|
||||
DW_OP_pick: "",
|
||||
DW_OP_swap: "",
|
||||
DW_OP_rot: "",
|
||||
DW_OP_xderef: "",
|
||||
DW_OP_abs: "",
|
||||
DW_OP_and: "",
|
||||
DW_OP_div: "",
|
||||
DW_OP_minus: "",
|
||||
DW_OP_mod: "",
|
||||
DW_OP_mul: "",
|
||||
DW_OP_neg: "",
|
||||
DW_OP_not: "",
|
||||
DW_OP_or: "",
|
||||
DW_OP_plus: "",
|
||||
DW_OP_plus_uconst: "u",
|
||||
DW_OP_shl: "",
|
||||
DW_OP_shr: "",
|
||||
DW_OP_shra: "",
|
||||
DW_OP_xor: "",
|
||||
DW_OP_bra: "2",
|
||||
DW_OP_eq: "",
|
||||
DW_OP_ge: "",
|
||||
DW_OP_gt: "",
|
||||
DW_OP_le: "",
|
||||
DW_OP_lt: "",
|
||||
DW_OP_ne: "",
|
||||
DW_OP_skip: "2",
|
||||
DW_OP_lit0: "",
|
||||
DW_OP_lit1: "",
|
||||
DW_OP_lit2: "",
|
||||
DW_OP_lit3: "",
|
||||
DW_OP_lit4: "",
|
||||
DW_OP_lit5: "",
|
||||
DW_OP_lit6: "",
|
||||
DW_OP_lit7: "",
|
||||
DW_OP_lit8: "",
|
||||
DW_OP_lit9: "",
|
||||
DW_OP_lit10: "",
|
||||
DW_OP_lit11: "",
|
||||
DW_OP_lit12: "",
|
||||
DW_OP_lit13: "",
|
||||
DW_OP_lit14: "",
|
||||
DW_OP_lit15: "",
|
||||
DW_OP_lit16: "",
|
||||
DW_OP_lit17: "",
|
||||
DW_OP_lit18: "",
|
||||
DW_OP_lit19: "",
|
||||
DW_OP_lit20: "",
|
||||
DW_OP_lit21: "",
|
||||
DW_OP_lit22: "",
|
||||
DW_OP_lit23: "",
|
||||
DW_OP_lit24: "",
|
||||
DW_OP_lit25: "",
|
||||
DW_OP_lit26: "",
|
||||
DW_OP_lit27: "",
|
||||
DW_OP_lit28: "",
|
||||
DW_OP_lit29: "",
|
||||
DW_OP_lit30: "",
|
||||
DW_OP_lit31: "",
|
||||
DW_OP_reg0: "",
|
||||
DW_OP_reg1: "",
|
||||
DW_OP_reg2: "",
|
||||
DW_OP_reg3: "",
|
||||
DW_OP_reg4: "",
|
||||
DW_OP_reg5: "",
|
||||
DW_OP_reg6: "",
|
||||
DW_OP_reg7: "",
|
||||
DW_OP_reg8: "",
|
||||
DW_OP_reg9: "",
|
||||
DW_OP_reg10: "",
|
||||
DW_OP_reg11: "",
|
||||
DW_OP_reg12: "",
|
||||
DW_OP_reg13: "",
|
||||
DW_OP_reg14: "",
|
||||
DW_OP_reg15: "",
|
||||
DW_OP_reg16: "",
|
||||
DW_OP_reg17: "",
|
||||
DW_OP_reg18: "",
|
||||
DW_OP_reg19: "",
|
||||
DW_OP_reg20: "",
|
||||
DW_OP_reg21: "",
|
||||
DW_OP_reg22: "",
|
||||
DW_OP_reg23: "",
|
||||
DW_OP_reg24: "",
|
||||
DW_OP_reg25: "",
|
||||
DW_OP_reg26: "",
|
||||
DW_OP_reg27: "",
|
||||
DW_OP_reg28: "",
|
||||
DW_OP_reg29: "",
|
||||
DW_OP_reg30: "",
|
||||
DW_OP_reg31: "",
|
||||
DW_OP_breg0: "s",
|
||||
DW_OP_breg1: "s",
|
||||
DW_OP_breg2: "s",
|
||||
DW_OP_breg3: "s",
|
||||
DW_OP_breg4: "s",
|
||||
DW_OP_breg5: "s",
|
||||
DW_OP_breg6: "s",
|
||||
DW_OP_breg7: "s",
|
||||
DW_OP_breg8: "s",
|
||||
DW_OP_breg9: "s",
|
||||
DW_OP_breg10: "s",
|
||||
DW_OP_breg11: "s",
|
||||
DW_OP_breg12: "s",
|
||||
DW_OP_breg13: "s",
|
||||
DW_OP_breg14: "s",
|
||||
DW_OP_breg15: "s",
|
||||
DW_OP_breg16: "s",
|
||||
DW_OP_breg17: "s",
|
||||
DW_OP_breg18: "s",
|
||||
DW_OP_breg19: "s",
|
||||
DW_OP_breg20: "s",
|
||||
DW_OP_breg21: "s",
|
||||
DW_OP_breg22: "s",
|
||||
DW_OP_breg23: "s",
|
||||
DW_OP_breg24: "s",
|
||||
DW_OP_breg25: "s",
|
||||
DW_OP_breg26: "s",
|
||||
DW_OP_breg27: "s",
|
||||
DW_OP_breg28: "s",
|
||||
DW_OP_breg29: "s",
|
||||
DW_OP_breg30: "s",
|
||||
DW_OP_breg31: "s",
|
||||
DW_OP_regx: "s",
|
||||
DW_OP_fbreg: "s",
|
||||
DW_OP_bregx: "us",
|
||||
DW_OP_piece: "u",
|
||||
DW_OP_deref_size: "1",
|
||||
DW_OP_xderef_size: "1",
|
||||
DW_OP_nop: "",
|
||||
DW_OP_push_object_address: "",
|
||||
DW_OP_call2: "2",
|
||||
DW_OP_call4: "4",
|
||||
DW_OP_call_ref: "4",
|
||||
DW_OP_form_tls_address: "",
|
||||
DW_OP_call_frame_cfa: "",
|
||||
DW_OP_bit_piece: "uu",
|
||||
DW_OP_implicit_value: "B",
|
||||
DW_OP_stack_value: "",
|
||||
}
|
||||
var oplut = map[Opcode]stackfn{
|
||||
DW_OP_addr: addr,
|
||||
DW_OP_consts: consts,
|
||||
DW_OP_plus: plus,
|
||||
DW_OP_plus_uconst: plusuconsts,
|
||||
DW_OP_reg0: register,
|
||||
DW_OP_reg1: register,
|
||||
DW_OP_reg2: register,
|
||||
DW_OP_reg3: register,
|
||||
DW_OP_reg4: register,
|
||||
DW_OP_reg5: register,
|
||||
DW_OP_reg6: register,
|
||||
DW_OP_reg7: register,
|
||||
DW_OP_reg8: register,
|
||||
DW_OP_reg9: register,
|
||||
DW_OP_reg10: register,
|
||||
DW_OP_reg11: register,
|
||||
DW_OP_reg12: register,
|
||||
DW_OP_reg13: register,
|
||||
DW_OP_reg14: register,
|
||||
DW_OP_reg15: register,
|
||||
DW_OP_reg16: register,
|
||||
DW_OP_reg17: register,
|
||||
DW_OP_reg18: register,
|
||||
DW_OP_reg19: register,
|
||||
DW_OP_reg20: register,
|
||||
DW_OP_reg21: register,
|
||||
DW_OP_reg22: register,
|
||||
DW_OP_reg23: register,
|
||||
DW_OP_reg24: register,
|
||||
DW_OP_reg25: register,
|
||||
DW_OP_reg26: register,
|
||||
DW_OP_reg27: register,
|
||||
DW_OP_reg28: register,
|
||||
DW_OP_reg29: register,
|
||||
DW_OP_reg30: register,
|
||||
DW_OP_reg31: register,
|
||||
DW_OP_regx: register,
|
||||
DW_OP_fbreg: framebase,
|
||||
DW_OP_piece: piece,
|
||||
DW_OP_call_frame_cfa: callframecfa,
|
||||
}
|
175
vendor/github.com/derekparker/delve/pkg/dwarf/op/opcodes.table
generated
vendored
175
vendor/github.com/derekparker/delve/pkg/dwarf/op/opcodes.table
generated
vendored
@ -1,175 +0,0 @@
|
||||
// This file is used by scripts/gen-opcodes.go to generate
|
||||
// pkg/dwarf/op/opcodes.go
|
||||
// Lines starting with // are comments and will be discarded.
|
||||
// Non empty lines contain the following tab separated fields:
|
||||
//
|
||||
// <opcode name> <opcode code> <arguments> <function name>
|
||||
//
|
||||
// With the last column, <function name>, being optional.
|
||||
//
|
||||
// The arguments field should contain a string with one character for each
|
||||
// argument of the opcode:
|
||||
//
|
||||
// s signed variable length integer
|
||||
// u unsigned variable length integer
|
||||
// 1 one byte unsigned integer
|
||||
// 2 two bytes unsigned integer
|
||||
// 4 four bytes unsigned integer
|
||||
// 8 eight bytes unsigned integer
|
||||
// B an unsigned variable length integer 'n' followed by n a block of n bytes
|
||||
|
||||
|
||||
DW_OP_addr 0x03 "8" addr
|
||||
DW_OP_deref 0x06 ""
|
||||
DW_OP_const1u 0x08 "1"
|
||||
DW_OP_const1s 0x09 "1"
|
||||
DW_OP_const2u 0x0a "2"
|
||||
DW_OP_const2s 0x0b "2"
|
||||
DW_OP_const4u 0x0c "4"
|
||||
DW_OP_const4s 0x0d "4"
|
||||
DW_OP_const8u 0x0e "8"
|
||||
DW_OP_const8s 0x0f "8"
|
||||
DW_OP_constu 0x10 "u"
|
||||
DW_OP_consts 0x11 "s" consts
|
||||
DW_OP_dup 0x12 ""
|
||||
DW_OP_drop 0x13 ""
|
||||
DW_OP_over 0x14 ""
|
||||
DW_OP_pick 0x15 ""
|
||||
DW_OP_swap 0x16 ""
|
||||
DW_OP_rot 0x17 ""
|
||||
DW_OP_xderef 0x18 ""
|
||||
DW_OP_abs 0x19 ""
|
||||
DW_OP_and 0x1a ""
|
||||
DW_OP_div 0x1b ""
|
||||
DW_OP_minus 0x1c ""
|
||||
DW_OP_mod 0x1d ""
|
||||
DW_OP_mul 0x1e ""
|
||||
DW_OP_neg 0x1f ""
|
||||
DW_OP_not 0x20 ""
|
||||
DW_OP_or 0x21 ""
|
||||
DW_OP_plus 0x22 "" plus
|
||||
DW_OP_plus_uconst 0x23 "u" plusuconsts
|
||||
DW_OP_shl 0x24 ""
|
||||
DW_OP_shr 0x25 ""
|
||||
DW_OP_shra 0x26 ""
|
||||
DW_OP_xor 0x27 ""
|
||||
DW_OP_bra 0x28 "2"
|
||||
DW_OP_eq 0x29 ""
|
||||
DW_OP_ge 0x2a ""
|
||||
DW_OP_gt 0x2b ""
|
||||
DW_OP_le 0x2c ""
|
||||
DW_OP_lt 0x2d ""
|
||||
DW_OP_ne 0x2e ""
|
||||
DW_OP_skip 0x2f "2"
|
||||
DW_OP_lit0 0x30 ""
|
||||
DW_OP_lit1 0x31 ""
|
||||
DW_OP_lit2 0x32 ""
|
||||
DW_OP_lit3 0x33 ""
|
||||
DW_OP_lit4 0x34 ""
|
||||
DW_OP_lit5 0x35 ""
|
||||
DW_OP_lit6 0x36 ""
|
||||
DW_OP_lit7 0x37 ""
|
||||
DW_OP_lit8 0x38 ""
|
||||
DW_OP_lit9 0x39 ""
|
||||
DW_OP_lit10 0x3a ""
|
||||
DW_OP_lit11 0x3b ""
|
||||
DW_OP_lit12 0x3c ""
|
||||
DW_OP_lit13 0x3d ""
|
||||
DW_OP_lit14 0x3e ""
|
||||
DW_OP_lit15 0x3f ""
|
||||
DW_OP_lit16 0x40 ""
|
||||
DW_OP_lit17 0x41 ""
|
||||
DW_OP_lit18 0x42 ""
|
||||
DW_OP_lit19 0x43 ""
|
||||
DW_OP_lit20 0x44 ""
|
||||
DW_OP_lit21 0x45 ""
|
||||
DW_OP_lit22 0x46 ""
|
||||
DW_OP_lit23 0x47 ""
|
||||
DW_OP_lit24 0x48 ""
|
||||
DW_OP_lit25 0x49 ""
|
||||
DW_OP_lit26 0x4a ""
|
||||
DW_OP_lit27 0x4b ""
|
||||
DW_OP_lit28 0x4c ""
|
||||
DW_OP_lit29 0x4d ""
|
||||
DW_OP_lit30 0x4e ""
|
||||
DW_OP_lit31 0x4f ""
|
||||
DW_OP_reg0 0x50 "" register
|
||||
DW_OP_reg1 0x51 "" register
|
||||
DW_OP_reg2 0x52 "" register
|
||||
DW_OP_reg3 0x53 "" register
|
||||
DW_OP_reg4 0x54 "" register
|
||||
DW_OP_reg5 0x55 "" register
|
||||
DW_OP_reg6 0x56 "" register
|
||||
DW_OP_reg7 0x57 "" register
|
||||
DW_OP_reg8 0x58 "" register
|
||||
DW_OP_reg9 0x59 "" register
|
||||
DW_OP_reg10 0x5a "" register
|
||||
DW_OP_reg11 0x5b "" register
|
||||
DW_OP_reg12 0x5c "" register
|
||||
DW_OP_reg13 0x5d "" register
|
||||
DW_OP_reg14 0x5e "" register
|
||||
DW_OP_reg15 0x5f "" register
|
||||
DW_OP_reg16 0x60 "" register
|
||||
DW_OP_reg17 0x61 "" register
|
||||
DW_OP_reg18 0x62 "" register
|
||||
DW_OP_reg19 0x63 "" register
|
||||
DW_OP_reg20 0x64 "" register
|
||||
DW_OP_reg21 0x65 "" register
|
||||
DW_OP_reg22 0x66 "" register
|
||||
DW_OP_reg23 0x67 "" register
|
||||
DW_OP_reg24 0x68 "" register
|
||||
DW_OP_reg25 0x69 "" register
|
||||
DW_OP_reg26 0x6a "" register
|
||||
DW_OP_reg27 0x6b "" register
|
||||
DW_OP_reg28 0x6c "" register
|
||||
DW_OP_reg29 0x6d "" register
|
||||
DW_OP_reg30 0x6e "" register
|
||||
DW_OP_reg31 0x6f "" register
|
||||
DW_OP_breg0 0x70 "s"
|
||||
DW_OP_breg1 0x71 "s"
|
||||
DW_OP_breg2 0x72 "s"
|
||||
DW_OP_breg3 0x73 "s"
|
||||
DW_OP_breg4 0x74 "s"
|
||||
DW_OP_breg5 0x75 "s"
|
||||
DW_OP_breg6 0x76 "s"
|
||||
DW_OP_breg7 0x77 "s"
|
||||
DW_OP_breg8 0x78 "s"
|
||||
DW_OP_breg9 0x79 "s"
|
||||
DW_OP_breg10 0x7a "s"
|
||||
DW_OP_breg11 0x7b "s"
|
||||
DW_OP_breg12 0x7c "s"
|
||||
DW_OP_breg13 0x7d "s"
|
||||
DW_OP_breg14 0x7e "s"
|
||||
DW_OP_breg15 0x7f "s"
|
||||
DW_OP_breg16 0x80 "s"
|
||||
DW_OP_breg17 0x81 "s"
|
||||
DW_OP_breg18 0x82 "s"
|
||||
DW_OP_breg19 0x83 "s"
|
||||
DW_OP_breg20 0x84 "s"
|
||||
DW_OP_breg21 0x85 "s"
|
||||
DW_OP_breg22 0x86 "s"
|
||||
DW_OP_breg23 0x87 "s"
|
||||
DW_OP_breg24 0x88 "s"
|
||||
DW_OP_breg25 0x89 "s"
|
||||
DW_OP_breg26 0x8a "s"
|
||||
DW_OP_breg27 0x8b "s"
|
||||
DW_OP_breg28 0x8c "s"
|
||||
DW_OP_breg29 0x8d "s"
|
||||
DW_OP_breg30 0x8e "s"
|
||||
DW_OP_breg31 0x8f "s"
|
||||
DW_OP_regx 0x90 "s" register
|
||||
DW_OP_fbreg 0x91 "s" framebase
|
||||
DW_OP_bregx 0x92 "us"
|
||||
DW_OP_piece 0x93 "u" piece
|
||||
DW_OP_deref_size 0x94 "1"
|
||||
DW_OP_xderef_size 0x95 "1"
|
||||
DW_OP_nop 0x96 ""
|
||||
DW_OP_push_object_address 0x97 ""
|
||||
DW_OP_call2 0x98 "2"
|
||||
DW_OP_call4 0x99 "4"
|
||||
DW_OP_call_ref 0x9a "4"
|
||||
DW_OP_form_tls_address 0x9b ""
|
||||
DW_OP_call_frame_cfa 0x9c "" callframecfa
|
||||
DW_OP_bit_piece 0x9d "uu"
|
||||
DW_OP_implicit_value 0x9e "B"
|
||||
DW_OP_stack_value 0x9f ""
|
102
vendor/github.com/derekparker/delve/pkg/dwarf/op/regs.go
generated
vendored
102
vendor/github.com/derekparker/delve/pkg/dwarf/op/regs.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
package op
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
type DwarfRegisters struct {
|
||||
StaticBase uint64
|
||||
|
||||
CFA int64
|
||||
FrameBase int64
|
||||
ObjBase int64
|
||||
Regs []*DwarfRegister
|
||||
|
||||
ByteOrder binary.ByteOrder
|
||||
PCRegNum uint64
|
||||
SPRegNum uint64
|
||||
BPRegNum uint64
|
||||
}
|
||||
|
||||
type DwarfRegister struct {
|
||||
Uint64Val uint64
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
// Uint64Val returns the uint64 value of register idx.
|
||||
func (regs *DwarfRegisters) Uint64Val(idx uint64) uint64 {
|
||||
reg := regs.Reg(idx)
|
||||
if reg == nil {
|
||||
return 0
|
||||
}
|
||||
return regs.Regs[idx].Uint64Val
|
||||
}
|
||||
|
||||
// Bytes returns the bytes value of register idx, nil if the register is not
|
||||
// defined.
|
||||
func (regs *DwarfRegisters) Bytes(idx uint64) []byte {
|
||||
reg := regs.Reg(idx)
|
||||
if reg == nil {
|
||||
return nil
|
||||
}
|
||||
if reg.Bytes == nil {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, regs.ByteOrder, reg.Uint64Val)
|
||||
reg.Bytes = buf.Bytes()
|
||||
}
|
||||
return reg.Bytes
|
||||
}
|
||||
|
||||
// Reg returns register idx or nil if the register is not defined.
|
||||
func (regs *DwarfRegisters) Reg(idx uint64) *DwarfRegister {
|
||||
if idx >= uint64(len(regs.Regs)) {
|
||||
return nil
|
||||
}
|
||||
return regs.Regs[idx]
|
||||
}
|
||||
|
||||
func (regs *DwarfRegisters) PC() uint64 {
|
||||
return regs.Uint64Val(regs.PCRegNum)
|
||||
}
|
||||
|
||||
func (regs *DwarfRegisters) SP() uint64 {
|
||||
return regs.Uint64Val(regs.SPRegNum)
|
||||
}
|
||||
|
||||
func (regs *DwarfRegisters) BP() uint64 {
|
||||
return regs.Uint64Val(regs.BPRegNum)
|
||||
}
|
||||
|
||||
// AddReg adds register idx to regs.
|
||||
func (regs *DwarfRegisters) AddReg(idx uint64, reg *DwarfRegister) {
|
||||
if idx >= uint64(len(regs.Regs)) {
|
||||
newRegs := make([]*DwarfRegister, idx+1)
|
||||
copy(newRegs, regs.Regs)
|
||||
regs.Regs = newRegs
|
||||
}
|
||||
regs.Regs[idx] = reg
|
||||
}
|
||||
|
||||
func DwarfRegisterFromUint64(v uint64) *DwarfRegister {
|
||||
return &DwarfRegister{Uint64Val: v}
|
||||
}
|
||||
|
||||
func DwarfRegisterFromBytes(bytes []byte) *DwarfRegister {
|
||||
var v uint64
|
||||
switch len(bytes) {
|
||||
case 1:
|
||||
v = uint64(bytes[0])
|
||||
case 2:
|
||||
x := binary.LittleEndian.Uint16(bytes)
|
||||
v = uint64(x)
|
||||
case 4:
|
||||
x := binary.LittleEndian.Uint32(bytes)
|
||||
v = uint64(x)
|
||||
default:
|
||||
if len(bytes) >= 8 {
|
||||
v = binary.LittleEndian.Uint64(bytes[:8])
|
||||
}
|
||||
}
|
||||
return &DwarfRegister{Uint64Val: v, Bytes: bytes}
|
||||
}
|
446
vendor/github.com/derekparker/delve/pkg/dwarf/reader/reader.go
generated
vendored
446
vendor/github.com/derekparker/delve/pkg/dwarf/reader/reader.go
generated
vendored
@ -1,446 +0,0 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
)
|
||||
|
||||
type Reader struct {
|
||||
*dwarf.Reader
|
||||
depth int
|
||||
}
|
||||
|
||||
// New returns a reader for the specified dwarf data.
|
||||
func New(data *dwarf.Data) *Reader {
|
||||
return &Reader{data.Reader(), 0}
|
||||
}
|
||||
|
||||
// Seek moves the reader to an arbitrary offset.
|
||||
func (reader *Reader) Seek(off dwarf.Offset) {
|
||||
reader.depth = 0
|
||||
reader.Reader.Seek(off)
|
||||
}
|
||||
|
||||
// SeekToEntry moves the reader to an arbitrary entry.
|
||||
func (reader *Reader) SeekToEntry(entry *dwarf.Entry) error {
|
||||
reader.Seek(entry.Offset)
|
||||
// Consume the current entry so .Next works as intended
|
||||
_, err := reader.Next()
|
||||
return err
|
||||
}
|
||||
|
||||
// SeekToFunctionEntry moves the reader to the function that includes the
|
||||
// specified program counter.
|
||||
func (reader *Reader) SeekToFunction(pc RelAddr) (*dwarf.Entry, error) {
|
||||
reader.Seek(0)
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Tag != dwarf.TagSubprogram {
|
||||
continue
|
||||
}
|
||||
|
||||
lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
highpc, ok := entry.Val(dwarf.AttrHighpc).(uint64)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if lowpc <= uint64(pc) && highpc > uint64(pc) {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to find function context")
|
||||
}
|
||||
|
||||
// Returns the address for the named entry.
|
||||
func (reader *Reader) AddrFor(name string, staticBase uint64) (uint64, error) {
|
||||
entry, err := reader.FindEntryNamed(name, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{StaticBase: staticBase}, instructions)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(addr), nil
|
||||
}
|
||||
|
||||
// Returns the address for the named struct member. Expects the reader to be at the parent entry
|
||||
// or one of the parents children, thus does not seek to parent by itself.
|
||||
func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (uint64, error) {
|
||||
for {
|
||||
entry, err := reader.NextMemberVariable()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if entry == nil {
|
||||
return 0, fmt.Errorf("nil entry for member named %s", member)
|
||||
}
|
||||
name, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok || name != member {
|
||||
continue
|
||||
}
|
||||
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...))
|
||||
return uint64(addr), err
|
||||
}
|
||||
}
|
||||
|
||||
var TypeNotFoundErr = errors.New("no type entry found, use 'types' for a list of valid types")
|
||||
|
||||
// SeekToType moves the reader to the type specified by the entry,
|
||||
// optionally resolving typedefs and pointer types. If the reader is set
|
||||
// to a struct type the NextMemberVariable call can be used to walk all member data.
|
||||
func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resolvePointerTypes bool) (*dwarf.Entry, error) {
|
||||
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("entry does not have a type attribute")
|
||||
}
|
||||
|
||||
// Seek to the first type offset
|
||||
reader.Seek(offset)
|
||||
|
||||
// Walk the types to the base
|
||||
for typeEntry, err := reader.Next(); typeEntry != nil; typeEntry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if typeEntry.Tag == dwarf.TagTypedef && !resolveTypedefs {
|
||||
return typeEntry, nil
|
||||
}
|
||||
|
||||
if typeEntry.Tag == dwarf.TagPointerType && !resolvePointerTypes {
|
||||
return typeEntry, nil
|
||||
}
|
||||
|
||||
offset, ok = typeEntry.Val(dwarf.AttrType).(dwarf.Offset)
|
||||
if !ok {
|
||||
return typeEntry, nil
|
||||
}
|
||||
|
||||
reader.Seek(offset)
|
||||
}
|
||||
|
||||
return nil, TypeNotFoundErr
|
||||
}
|
||||
|
||||
func (reader *Reader) NextType() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch entry.Tag {
|
||||
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SeekToTypeNamed moves the reader to the type specified by the name.
|
||||
// If the reader is set to a struct type the NextMemberVariable call
|
||||
// can be used to walk all member data.
|
||||
func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
||||
// Walk the types to the base
|
||||
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if n == name {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, TypeNotFoundErr
|
||||
}
|
||||
|
||||
// Finds the entry for 'name'.
|
||||
func (reader *Reader) FindEntryNamed(name string, member bool) (*dwarf.Entry, error) {
|
||||
depth := 1
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Children {
|
||||
depth++
|
||||
}
|
||||
|
||||
if entry.Tag == 0 {
|
||||
depth--
|
||||
if depth <= 0 {
|
||||
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
if member {
|
||||
if entry.Tag != dwarf.TagMember {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
n, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok || n != name {
|
||||
continue
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
||||
}
|
||||
|
||||
func (reader *Reader) InstructionsForEntryNamed(name string, member bool) ([]byte, error) {
|
||||
entry, err := reader.FindEntryNamed(name, member)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var attr dwarf.Attr
|
||||
if member {
|
||||
attr = dwarf.AttrDataMemberLoc
|
||||
} else {
|
||||
attr = dwarf.AttrLocation
|
||||
}
|
||||
instr, ok := entry.Val(attr).([]byte)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid typecast for Dwarf instructions")
|
||||
}
|
||||
return instr, nil
|
||||
}
|
||||
|
||||
func (reader *Reader) InstructionsForEntry(entry *dwarf.Entry) ([]byte, error) {
|
||||
if entry.Tag == dwarf.TagMember {
|
||||
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("member data has no data member location attribute")
|
||||
}
|
||||
// clone slice to prevent stomping on the dwarf data
|
||||
return append([]byte{}, instructions...), nil
|
||||
}
|
||||
|
||||
// non-member
|
||||
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("entry has no location attribute")
|
||||
}
|
||||
|
||||
// clone slice to prevent stomping on the dwarf data
|
||||
return append([]byte{}, instructions...), nil
|
||||
}
|
||||
|
||||
// NextMemberVariable moves the reader to the next debug entry that describes a member variable and returns the entry.
|
||||
func (reader *Reader) NextMemberVariable() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// All member variables will be at the same depth
|
||||
reader.SkipChildren()
|
||||
|
||||
// End of the current depth
|
||||
if entry.Tag == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if entry.Tag == dwarf.TagMember {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
// No more items
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NextPackageVariable moves the reader to the next debug entry that describes a package variable.
|
||||
// Any TagVariable entry that is not inside a sub prgram entry and is marked external is considered a package variable.
|
||||
func (reader *Reader) NextPackageVariable() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Tag == dwarf.TagVariable {
|
||||
ext, ok := entry.Val(dwarf.AttrExternal).(bool)
|
||||
if ok && ext {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore everything inside sub programs
|
||||
if entry.Tag == dwarf.TagSubprogram {
|
||||
reader.SkipChildren()
|
||||
}
|
||||
}
|
||||
|
||||
// No more items
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Tag == dwarf.TagCompileUnit {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Entry represents a debug_info entry.
|
||||
// When calling Val, if the entry does not have the specified attribute, the
|
||||
// entry specified by DW_AT_abstract_origin will be searched recursively.
|
||||
type Entry interface {
|
||||
Val(dwarf.Attr) interface{}
|
||||
}
|
||||
|
||||
type compositeEntry []*dwarf.Entry
|
||||
|
||||
func (ce compositeEntry) Val(attr dwarf.Attr) interface{} {
|
||||
for _, e := range ce {
|
||||
if r := e.Val(attr); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAbstractOrigin loads the entry corresponding to the
|
||||
// DW_AT_abstract_origin of entry and returns a combination of entry and its
|
||||
// abstract origin.
|
||||
func LoadAbstractOrigin(entry *dwarf.Entry, aordr *dwarf.Reader) (Entry, dwarf.Offset) {
|
||||
ao, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
|
||||
if !ok {
|
||||
return entry, entry.Offset
|
||||
}
|
||||
|
||||
r := []*dwarf.Entry{entry}
|
||||
|
||||
for {
|
||||
aordr.Seek(ao)
|
||||
e, _ := aordr.Next()
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
r = append(r, e)
|
||||
|
||||
ao, ok = e.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return compositeEntry(r), entry.Offset
|
||||
}
|
||||
|
||||
// InlineStackReader provides a way to read the stack of inlined calls at a
|
||||
// specified PC address.
|
||||
type InlineStackReader struct {
|
||||
dwarf *dwarf.Data
|
||||
reader *dwarf.Reader
|
||||
entry *dwarf.Entry
|
||||
depth int
|
||||
pc uint64
|
||||
err error
|
||||
}
|
||||
|
||||
// InlineStack returns an InlineStackReader for the specified function and
|
||||
// PC address.
|
||||
// If pc is 0 then all inlined calls will be returned.
|
||||
func InlineStack(dwarf *dwarf.Data, fnoff dwarf.Offset, pc RelAddr) *InlineStackReader {
|
||||
reader := dwarf.Reader()
|
||||
reader.Seek(fnoff)
|
||||
return &InlineStackReader{dwarf: dwarf, reader: reader, entry: nil, depth: 0, pc: uint64(pc)}
|
||||
}
|
||||
|
||||
// Next reads next inlined call in the stack, returns false if there aren't any.
|
||||
func (irdr *InlineStackReader) Next() bool {
|
||||
if irdr.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for {
|
||||
irdr.entry, irdr.err = irdr.reader.Next()
|
||||
if irdr.entry == nil || irdr.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch irdr.entry.Tag {
|
||||
case 0:
|
||||
irdr.depth--
|
||||
if irdr.depth == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
case dwarf.TagLexDwarfBlock, dwarf.TagSubprogram, dwarf.TagInlinedSubroutine:
|
||||
var recur bool
|
||||
if irdr.pc != 0 {
|
||||
recur, irdr.err = entryRangesContains(irdr.dwarf, irdr.entry, irdr.pc)
|
||||
} else {
|
||||
recur = true
|
||||
}
|
||||
if recur {
|
||||
irdr.depth++
|
||||
if irdr.entry.Tag == dwarf.TagInlinedSubroutine {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if irdr.depth == 0 {
|
||||
return false
|
||||
}
|
||||
irdr.reader.SkipChildren()
|
||||
}
|
||||
|
||||
default:
|
||||
irdr.reader.SkipChildren()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entry returns the DIE for the current inlined call.
|
||||
func (irdr *InlineStackReader) Entry() *dwarf.Entry {
|
||||
return irdr.entry
|
||||
}
|
||||
|
||||
// Err returns an error, if any was encountered.
|
||||
func (irdr *InlineStackReader) Err() error {
|
||||
return irdr.err
|
||||
}
|
||||
|
||||
// SkipChildren skips all children of the current inlined call.
|
||||
func (irdr *InlineStackReader) SkipChildren() {
|
||||
irdr.reader.SkipChildren()
|
||||
}
|
114
vendor/github.com/derekparker/delve/pkg/dwarf/reader/variables.go
generated
vendored
114
vendor/github.com/derekparker/delve/pkg/dwarf/reader/variables.go
generated
vendored
@ -1,114 +0,0 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"debug/dwarf"
|
||||
)
|
||||
|
||||
// RelAddr is an address relative to the static base. For normal executables
|
||||
// this is just a normal memory address, for PIE it's a relative address.
|
||||
type RelAddr uint64
|
||||
|
||||
func ToRelAddr(addr uint64, staticBase uint64) RelAddr {
|
||||
return RelAddr(addr - staticBase)
|
||||
}
|
||||
|
||||
// VariableReader provides a way of reading the local variables and formal
|
||||
// parameters of a function that are visible at the specified PC address.
|
||||
type VariableReader struct {
|
||||
dwarf *dwarf.Data
|
||||
reader *dwarf.Reader
|
||||
entry *dwarf.Entry
|
||||
depth int
|
||||
onlyVisible bool
|
||||
pc uint64
|
||||
line int
|
||||
err error
|
||||
}
|
||||
|
||||
// Variables returns a VariableReader for the function or lexical block at off.
|
||||
// If onlyVisible is true only variables visible at pc will be returned by
|
||||
// the VariableReader.
|
||||
func Variables(dwarf *dwarf.Data, off dwarf.Offset, pc RelAddr, line int, onlyVisible bool) *VariableReader {
|
||||
reader := dwarf.Reader()
|
||||
reader.Seek(off)
|
||||
return &VariableReader{dwarf: dwarf, reader: reader, entry: nil, depth: 0, onlyVisible: onlyVisible, pc: uint64(pc), line: line, err: nil}
|
||||
}
|
||||
|
||||
// Next reads the next variable entry, returns false if there aren't any.
|
||||
func (vrdr *VariableReader) Next() bool {
|
||||
if vrdr.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for {
|
||||
vrdr.entry, vrdr.err = vrdr.reader.Next()
|
||||
if vrdr.entry == nil || vrdr.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch vrdr.entry.Tag {
|
||||
case 0:
|
||||
vrdr.depth--
|
||||
if vrdr.depth == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
case dwarf.TagLexDwarfBlock, dwarf.TagSubprogram, dwarf.TagInlinedSubroutine:
|
||||
recur := true
|
||||
if vrdr.onlyVisible {
|
||||
recur, vrdr.err = entryRangesContains(vrdr.dwarf, vrdr.entry, vrdr.pc)
|
||||
if vrdr.err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if recur && vrdr.entry.Children {
|
||||
vrdr.depth++
|
||||
} else {
|
||||
if vrdr.depth == 0 {
|
||||
return false
|
||||
}
|
||||
vrdr.reader.SkipChildren()
|
||||
}
|
||||
|
||||
default:
|
||||
if vrdr.depth == 0 {
|
||||
vrdr.err = errors.New("offset was not lexical block or subprogram")
|
||||
return false
|
||||
}
|
||||
if declLine, ok := vrdr.entry.Val(dwarf.AttrDeclLine).(int64); !ok || vrdr.line >= int(declLine) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func entryRangesContains(dwarf *dwarf.Data, entry *dwarf.Entry, pc uint64) (bool, error) {
|
||||
rngs, err := dwarf.Ranges(entry)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, rng := range rngs {
|
||||
if pc >= rng[0] && pc < rng[1] {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Entry returns the current variable entry.
|
||||
func (vrdr *VariableReader) Entry() *dwarf.Entry {
|
||||
return vrdr.entry
|
||||
}
|
||||
|
||||
// Depth returns the depth of the current scope
|
||||
func (vrdr *VariableReader) Depth() int {
|
||||
return vrdr.depth
|
||||
}
|
||||
|
||||
// Err returns the error if there was one.
|
||||
func (vrdr *VariableReader) Err() error {
|
||||
return vrdr.err
|
||||
}
|
151
vendor/github.com/derekparker/delve/pkg/dwarf/util/buf.go
generated
vendored
151
vendor/github.com/derekparker/delve/pkg/dwarf/util/buf.go
generated
vendored
@ -1,151 +0,0 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
// Buffered reading and decoding of DWARF data streams.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Data buffer being decoded.
|
||||
type buf struct {
|
||||
dwarf *dwarf.Data
|
||||
format dataFormat
|
||||
name string
|
||||
off dwarf.Offset
|
||||
data []byte
|
||||
Err error
|
||||
}
|
||||
|
||||
// Data format, other than byte order. This affects the handling of
|
||||
// certain field formats.
|
||||
type dataFormat interface {
|
||||
// DWARF version number. Zero means unknown.
|
||||
version() int
|
||||
|
||||
// 64-bit DWARF format?
|
||||
dwarf64() (dwarf64 bool, isKnown bool)
|
||||
|
||||
// Size of an address, in bytes. Zero means unknown.
|
||||
addrsize() int
|
||||
}
|
||||
|
||||
// Some parts of DWARF have no data format, e.g., abbrevs.
|
||||
type UnknownFormat struct{}
|
||||
|
||||
func (u UnknownFormat) version() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (u UnknownFormat) dwarf64() (bool, bool) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
func (u UnknownFormat) addrsize() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func MakeBuf(d *dwarf.Data, format dataFormat, name string, off dwarf.Offset, data []byte) buf {
|
||||
return buf{d, format, name, off, data, nil}
|
||||
}
|
||||
|
||||
func (b *buf) slice(length int) buf {
|
||||
n := *b
|
||||
data := b.data
|
||||
b.skip(length) // Will validate length.
|
||||
n.data = data[:length]
|
||||
return n
|
||||
}
|
||||
|
||||
func (b *buf) Uint8() uint8 {
|
||||
if len(b.data) < 1 {
|
||||
b.error("underflow")
|
||||
return 0
|
||||
}
|
||||
val := b.data[0]
|
||||
b.data = b.data[1:]
|
||||
b.off++
|
||||
return val
|
||||
}
|
||||
|
||||
func (b *buf) bytes(n int) []byte {
|
||||
if len(b.data) < n {
|
||||
b.error("underflow")
|
||||
return nil
|
||||
}
|
||||
data := b.data[0:n]
|
||||
b.data = b.data[n:]
|
||||
b.off += dwarf.Offset(n)
|
||||
return data
|
||||
}
|
||||
|
||||
func (b *buf) skip(n int) { b.bytes(n) }
|
||||
|
||||
// string returns the NUL-terminated (C-like) string at the start of the buffer.
|
||||
// The terminal NUL is discarded.
|
||||
func (b *buf) string() string {
|
||||
for i := 0; i < len(b.data); i++ {
|
||||
if b.data[i] == 0 {
|
||||
s := string(b.data[0:i])
|
||||
b.data = b.data[i+1:]
|
||||
b.off += dwarf.Offset(i + 1)
|
||||
return s
|
||||
}
|
||||
}
|
||||
b.error("underflow")
|
||||
return ""
|
||||
}
|
||||
|
||||
// Read a varint, which is 7 bits per byte, little endian.
|
||||
// the 0x80 bit means read another byte.
|
||||
func (b *buf) Varint() (c uint64, bits uint) {
|
||||
for i := 0; i < len(b.data); i++ {
|
||||
byte := b.data[i]
|
||||
c |= uint64(byte&0x7F) << bits
|
||||
bits += 7
|
||||
if byte&0x80 == 0 {
|
||||
b.off += dwarf.Offset(i + 1)
|
||||
b.data = b.data[i+1:]
|
||||
return c, bits
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Unsigned int is just a varint.
|
||||
func (b *buf) Uint() uint64 {
|
||||
x, _ := b.Varint()
|
||||
return x
|
||||
}
|
||||
|
||||
// Signed int is a sign-extended varint.
|
||||
func (b *buf) Int() int64 {
|
||||
ux, bits := b.Varint()
|
||||
x := int64(ux)
|
||||
if x&(1<<(bits-1)) != 0 {
|
||||
x |= -1 << bits
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// AssertEmpty checks that everything has been read from b.
|
||||
func (b *buf) AssertEmpty() {
|
||||
if len(b.data) == 0 {
|
||||
return
|
||||
}
|
||||
if len(b.data) > 5 {
|
||||
b.error(fmt.Sprintf("unexpected extra data: %x...", b.data[0:5]))
|
||||
}
|
||||
b.error(fmt.Sprintf("unexpected extra data: %x", b.data))
|
||||
}
|
||||
|
||||
func (b *buf) error(s string) {
|
||||
if b.Err == nil {
|
||||
b.data = nil
|
||||
b.Err = dwarf.DecodeError{b.name, b.off, s}
|
||||
}
|
||||
}
|
126
vendor/github.com/derekparker/delve/pkg/dwarf/util/util.go
generated
vendored
126
vendor/github.com/derekparker/delve/pkg/dwarf/util/util.go
generated
vendored
@ -1,126 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// The Little Endian Base 128 format is defined in the DWARF v4 standard,
|
||||
// section 7.6, page 161 and following.
|
||||
|
||||
// DecodeULEB128 decodes an unsigned Little Endian Base 128
|
||||
// represented number.
|
||||
func DecodeULEB128(buf *bytes.Buffer) (uint64, uint32) {
|
||||
var (
|
||||
result uint64
|
||||
shift uint64
|
||||
length uint32
|
||||
)
|
||||
|
||||
if buf.Len() == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
for {
|
||||
b, err := buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not parse ULEB128 value")
|
||||
}
|
||||
length++
|
||||
|
||||
result |= uint64((uint(b) & 0x7f) << shift)
|
||||
|
||||
// If high order bit is 1.
|
||||
if b&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
shift += 7
|
||||
}
|
||||
|
||||
return result, length
|
||||
}
|
||||
|
||||
// DecodeSLEB128 decodes a signed Little Endian Base 128
|
||||
// represented number.
|
||||
func DecodeSLEB128(buf *bytes.Buffer) (int64, uint32) {
|
||||
var (
|
||||
b byte
|
||||
err error
|
||||
result int64
|
||||
shift uint64
|
||||
length uint32
|
||||
)
|
||||
|
||||
if buf.Len() == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
for {
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not parse SLEB128 value")
|
||||
}
|
||||
length++
|
||||
|
||||
result |= int64((int64(b) & 0x7f) << shift)
|
||||
shift += 7
|
||||
if b&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (shift < 8*uint64(length)) && (b&0x40 > 0) {
|
||||
result |= -(1 << shift)
|
||||
}
|
||||
|
||||
return result, length
|
||||
}
|
||||
|
||||
// EncodeULEB128 encodes x to the unsigned Little Endian Base 128 format
|
||||
// into out.
|
||||
func EncodeULEB128(out io.ByteWriter, x uint64) {
|
||||
for {
|
||||
b := byte(x & 0x7f)
|
||||
x = x >> 7
|
||||
if x != 0 {
|
||||
b = b | 0x80
|
||||
}
|
||||
out.WriteByte(b)
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeSLEB128 encodes x to the signed Little Endian Base 128 format
|
||||
// into out.
|
||||
func EncodeSLEB128(out io.ByteWriter, x int64) {
|
||||
for {
|
||||
b := byte(x & 0x7f)
|
||||
x >>= 7
|
||||
|
||||
signb := b & 0x40
|
||||
|
||||
last := false
|
||||
if (x == 0 && signb == 0) || (x == -1 && signb != 0) {
|
||||
last = true
|
||||
} else {
|
||||
b = b | 0x80
|
||||
}
|
||||
out.WriteByte(b)
|
||||
|
||||
if last {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ParseString(data *bytes.Buffer) (string, uint32) {
|
||||
str, err := data.ReadString(0x0)
|
||||
if err != nil {
|
||||
panic("Could not parse string")
|
||||
}
|
||||
|
||||
return str[:len(str)-1], uint32(len(str))
|
||||
}
|
178
vendor/github.com/derekparker/delve/pkg/goversion/go_version.go
generated
vendored
178
vendor/github.com/derekparker/delve/pkg/goversion/go_version.go
generated
vendored
@ -1,178 +0,0 @@
|
||||
package goversion
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GoVersion represents the Go version of
|
||||
// the Go compiler version used to compile
|
||||
// the target binary.
|
||||
type GoVersion struct {
|
||||
Major int
|
||||
Minor int
|
||||
Rev int
|
||||
Beta int
|
||||
RC int
|
||||
Proposal string
|
||||
}
|
||||
|
||||
var (
|
||||
GoVer18Beta = GoVersion{1, 8, -1, 0, 0, ""}
|
||||
)
|
||||
|
||||
// Parse parses a go version string
|
||||
func Parse(ver string) (GoVersion, bool) {
|
||||
var r GoVersion
|
||||
var err1, err2, err3 error
|
||||
|
||||
if strings.HasPrefix(ver, "devel") {
|
||||
return GoVersion{-1, 0, 0, 0, 0, ""}, true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(ver, "go") {
|
||||
ver := strings.Split(ver, " ")[0]
|
||||
v := strings.SplitN(ver[2:], ".", 4)
|
||||
switch len(v) {
|
||||
case 2:
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
vr := strings.SplitN(v[1], "beta", 2)
|
||||
if len(vr) == 2 {
|
||||
r.Beta, err3 = strconv.Atoi(vr[1])
|
||||
} else {
|
||||
vr = strings.SplitN(v[1], "rc", 2)
|
||||
if len(vr) == 2 {
|
||||
r.RC, err3 = strconv.Atoi(vr[1])
|
||||
} else {
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
if err2 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
|
||||
r.Minor, err2 = strconv.Atoi(vr[0])
|
||||
r.Rev = -1
|
||||
r.Proposal = ""
|
||||
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return r, true
|
||||
|
||||
case 3:
|
||||
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
r.Rev, err3 = strconv.Atoi(v[2])
|
||||
r.Proposal = ""
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return r, true
|
||||
|
||||
case 4:
|
||||
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
r.Rev, err3 = strconv.Atoi(v[2])
|
||||
r.Proposal = v[3]
|
||||
if err1 != nil || err2 != nil || err3 != nil || r.Proposal == "" {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return r, true
|
||||
|
||||
default:
|
||||
return GoVersion{}, false
|
||||
}
|
||||
}
|
||||
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
// AfterOrEqual returns whether one GoVersion is after or
|
||||
// equal to the other.
|
||||
func (v *GoVersion) AfterOrEqual(b GoVersion) bool {
|
||||
if v.Major < b.Major {
|
||||
return false
|
||||
} else if v.Major > b.Major {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Minor < b.Minor {
|
||||
return false
|
||||
} else if v.Minor > b.Minor {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Rev < b.Rev {
|
||||
return false
|
||||
} else if v.Rev > b.Rev {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Beta < b.Beta {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.RC < b.RC {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsDevel returns whether the GoVersion
|
||||
// is a development version.
|
||||
func (v *GoVersion) IsDevel() bool {
|
||||
return v.Major < 0
|
||||
}
|
||||
|
||||
const goVersionPrefix = "go version "
|
||||
|
||||
// Installed runs "go version" and parses the output
|
||||
func Installed() (GoVersion, bool) {
|
||||
out, err := exec.Command("go", "version").CombinedOutput()
|
||||
if err != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
s := string(out)
|
||||
|
||||
if !strings.HasPrefix(s, goVersionPrefix) {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return Parse(s[len(goVersionPrefix):])
|
||||
}
|
||||
|
||||
// VersionAfterOrEqual checks that version (as returned by runtime.Version()
|
||||
// or go version) is major.minor or a later version, or a development
|
||||
// version.
|
||||
func VersionAfterOrEqual(version string, major, minor int) bool {
|
||||
ver, _ := Parse(version)
|
||||
if ver.IsDevel() {
|
||||
return true
|
||||
}
|
||||
return ver.AfterOrEqual(GoVersion{major, minor, -1, 0, 0, ""})
|
||||
}
|
||||
|
||||
const producerVersionPrefix = "Go cmd/compile "
|
||||
|
||||
// ProducerAfterOrEqual checks that the DW_AT_producer version is
|
||||
// major.minor or a later version, or a development version.
|
||||
func ProducerAfterOrEqual(producer string, major, minor int) bool {
|
||||
if strings.HasPrefix(producer, producerVersionPrefix) {
|
||||
producer = producer[len(producerVersionPrefix):]
|
||||
}
|
||||
ver, _ := Parse(producer)
|
||||
if ver.IsDevel() {
|
||||
return true
|
||||
}
|
||||
return ver.AfterOrEqual(GoVersion{major, minor, -1, 0, 0, ""})
|
||||
}
|
83
vendor/github.com/derekparker/delve/pkg/logflags/logflags.go
generated
vendored
83
vendor/github.com/derekparker/delve/pkg/logflags/logflags.go
generated
vendored
@ -1,83 +0,0 @@
|
||||
package logflags
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var debugger = false
|
||||
var gdbWire = false
|
||||
var lldbServerOutput = false
|
||||
var debugLineErrors = false
|
||||
var rpc = false
|
||||
var fnCall = false
|
||||
|
||||
// GdbWire returns true if the gdbserial package should log all the packets
|
||||
// exchanged with the stub.
|
||||
func GdbWire() bool {
|
||||
return gdbWire
|
||||
}
|
||||
|
||||
// Debugger returns true if the debugger package should log.
|
||||
func Debugger() bool {
|
||||
return debugger
|
||||
}
|
||||
|
||||
// LLDBServerOutput returns true if the output of the LLDB server should be
|
||||
// redirected to standard output instead of suppressed.
|
||||
func LLDBServerOutput() bool {
|
||||
return lldbServerOutput
|
||||
}
|
||||
|
||||
// DebugLineErrors returns true if pkg/dwarf/line should log its recoverable
|
||||
// errors.
|
||||
func DebugLineErrors() bool {
|
||||
return debugLineErrors
|
||||
}
|
||||
|
||||
// RPC returns true if rpc messages should be logged.
|
||||
func RPC() bool {
|
||||
return rpc
|
||||
}
|
||||
|
||||
// FnCall returns true if the function call protocol should be logged.
|
||||
func FnCall() bool {
|
||||
return fnCall
|
||||
}
|
||||
|
||||
var errLogstrWithoutLog = errors.New("--log-output specified without --log")
|
||||
|
||||
// Setup sets debugger flags based on the contents of logstr.
|
||||
func Setup(logFlag bool, logstr string) error {
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
if !logFlag {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
if logstr != "" {
|
||||
return errLogstrWithoutLog
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if logstr == "" {
|
||||
logstr = "debugger"
|
||||
}
|
||||
v := strings.Split(logstr, ",")
|
||||
for _, logcmd := range v {
|
||||
switch logcmd {
|
||||
case "debugger":
|
||||
debugger = true
|
||||
case "gdbwire":
|
||||
gdbWire = true
|
||||
case "lldbout":
|
||||
lldbServerOutput = true
|
||||
case "debuglineerr":
|
||||
debugLineErrors = true
|
||||
case "rpc":
|
||||
rpc = true
|
||||
case "fncall":
|
||||
fnCall = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
306
vendor/github.com/derekparker/delve/pkg/proc/arch.go
generated
vendored
306
vendor/github.com/derekparker/delve/pkg/proc/arch.go
generated
vendored
@ -1,306 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
|
||||
// Arch defines an interface for representing a
|
||||
// CPU architecture.
|
||||
type Arch interface {
|
||||
PtrSize() int
|
||||
BreakpointInstruction() []byte
|
||||
BreakpointSize() int
|
||||
DerefTLS() bool
|
||||
FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext
|
||||
RegSize(uint64) int
|
||||
RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters
|
||||
GoroutineToDwarfRegisters(*G) op.DwarfRegisters
|
||||
}
|
||||
|
||||
// AMD64 represents the AMD64 CPU architecture.
|
||||
type AMD64 struct {
|
||||
ptrSize int
|
||||
breakInstruction []byte
|
||||
breakInstructionLen int
|
||||
gStructOffset uint64
|
||||
hardwareBreakpointUsage []bool
|
||||
goos string
|
||||
|
||||
// crosscall2fn is the DIE of crosscall2, a function used by the go runtime
|
||||
// to call C functions. This function in go 1.9 (and previous versions) had
|
||||
// a bad frame descriptor which needs to be fixed to generate good stack
|
||||
// traces.
|
||||
crosscall2fn *Function
|
||||
|
||||
// sigreturnfn is the DIE of runtime.sigreturn, the return trampoline for
|
||||
// the signal handler. See comment in FixFrameUnwindContext for a
|
||||
// description of why this is needed.
|
||||
sigreturnfn *Function
|
||||
}
|
||||
|
||||
const (
|
||||
amd64DwarfIPRegNum uint64 = 16
|
||||
amd64DwarfSPRegNum uint64 = 7
|
||||
amd64DwarfBPRegNum uint64 = 6
|
||||
)
|
||||
|
||||
// AMD64Arch returns an initialized AMD64
|
||||
// struct.
|
||||
func AMD64Arch(goos string) *AMD64 {
|
||||
var breakInstr = []byte{0xCC}
|
||||
|
||||
return &AMD64{
|
||||
ptrSize: 8,
|
||||
breakInstruction: breakInstr,
|
||||
breakInstructionLen: len(breakInstr),
|
||||
hardwareBreakpointUsage: make([]bool, 4),
|
||||
goos: goos,
|
||||
}
|
||||
}
|
||||
|
||||
// PtrSize returns the size of a pointer
|
||||
// on this architecture.
|
||||
func (a *AMD64) PtrSize() int {
|
||||
return a.ptrSize
|
||||
}
|
||||
|
||||
// BreakpointInstruction returns the Breakpoint
|
||||
// instruction for this architecture.
|
||||
func (a *AMD64) BreakpointInstruction() []byte {
|
||||
return a.breakInstruction
|
||||
}
|
||||
|
||||
// BreakpointSize returns the size of the
|
||||
// breakpoint instruction on this architecture.
|
||||
func (a *AMD64) BreakpointSize() int {
|
||||
return a.breakInstructionLen
|
||||
}
|
||||
|
||||
// DerefTLS returns true if the value of regs.TLS()+GStructOffset() is a
|
||||
// pointer to the G struct
|
||||
func (a *AMD64) DerefTLS() bool {
|
||||
return a.goos == "windows"
|
||||
}
|
||||
|
||||
const (
|
||||
crosscall2SPOffsetBad = 0x8
|
||||
crosscall2SPOffsetWindows = 0x118
|
||||
crosscall2SPOffsetNonWindows = 0x58
|
||||
)
|
||||
|
||||
// FixFrameUnwindContext adds default architecture rules to fctxt or returns
|
||||
// the default frame unwind context if fctxt is nil.
|
||||
func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
|
||||
if a.sigreturnfn == nil {
|
||||
a.sigreturnfn = bi.LookupFunc["runtime.sigreturn"]
|
||||
}
|
||||
|
||||
if fctxt == nil || (a.sigreturnfn != nil && pc >= a.sigreturnfn.Entry && pc < a.sigreturnfn.End) {
|
||||
//if true {
|
||||
// When there's no frame descriptor entry use BP (the frame pointer) instead
|
||||
// - return register is [bp + a.PtrSize()] (i.e. [cfa-a.PtrSize()])
|
||||
// - cfa is bp + a.PtrSize()*2
|
||||
// - bp is [bp] (i.e. [cfa-a.PtrSize()*2])
|
||||
// - sp is cfa
|
||||
|
||||
// When the signal handler runs it will move the execution to the signal
|
||||
// handling stack (installed using the sigaltstack system call).
|
||||
// This isn't a proper stack switch: the pointer to g in TLS will still
|
||||
// refer to whatever g was executing on that thread before the signal was
|
||||
// received.
|
||||
// Since go did not execute a stack switch the previous value of sp, pc
|
||||
// and bp is not saved inside g.sched, as it normally would.
|
||||
// The only way to recover is to either read sp/pc from the signal context
|
||||
// parameter (the ucontext_t* parameter) or to unconditionally follow the
|
||||
// frame pointer when we get to runtime.sigreturn (which is what we do
|
||||
// here).
|
||||
|
||||
return &frame.FrameContext{
|
||||
RetAddrReg: amd64DwarfIPRegNum,
|
||||
Regs: map[uint64]frame.DWRule{
|
||||
amd64DwarfIPRegNum: frame.DWRule{
|
||||
Rule: frame.RuleOffset,
|
||||
Offset: int64(-a.PtrSize()),
|
||||
},
|
||||
amd64DwarfBPRegNum: frame.DWRule{
|
||||
Rule: frame.RuleOffset,
|
||||
Offset: int64(-2 * a.PtrSize()),
|
||||
},
|
||||
amd64DwarfSPRegNum: frame.DWRule{
|
||||
Rule: frame.RuleValOffset,
|
||||
Offset: 0,
|
||||
},
|
||||
},
|
||||
CFA: frame.DWRule{
|
||||
Rule: frame.RuleCFA,
|
||||
Reg: amd64DwarfBPRegNum,
|
||||
Offset: int64(2 * a.PtrSize()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if a.crosscall2fn == nil {
|
||||
a.crosscall2fn = bi.LookupFunc["crosscall2"]
|
||||
}
|
||||
|
||||
if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End {
|
||||
rule := fctxt.CFA
|
||||
if rule.Offset == crosscall2SPOffsetBad {
|
||||
switch a.goos {
|
||||
case "windows":
|
||||
rule.Offset += crosscall2SPOffsetWindows
|
||||
default:
|
||||
rule.Offset += crosscall2SPOffsetNonWindows
|
||||
}
|
||||
}
|
||||
fctxt.CFA = rule
|
||||
}
|
||||
|
||||
// We assume that RBP is the frame pointer and we want to keep it updated,
|
||||
// so that we can use it to unwind the stack even when we encounter frames
|
||||
// without descriptor entries.
|
||||
// If there isn't a rule already we emit one.
|
||||
if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined {
|
||||
fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{
|
||||
Rule: frame.RuleFramePointer,
|
||||
Reg: amd64DwarfBPRegNum,
|
||||
Offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
return fctxt
|
||||
}
|
||||
|
||||
// RegSize returns the size (in bytes) of register regnum.
|
||||
// The mapping between hardware registers and DWARF registers is specified
|
||||
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
|
||||
// figure 3.36
|
||||
// https://www.uclibc.org/docs/psABI-x86_64.pdf
|
||||
func (a *AMD64) RegSize(regnum uint64) int {
|
||||
// XMM registers
|
||||
if regnum > amd64DwarfIPRegNum && regnum <= 32 {
|
||||
return 16
|
||||
}
|
||||
// x87 registers
|
||||
if regnum >= 33 && regnum <= 40 {
|
||||
return 10
|
||||
}
|
||||
return 8
|
||||
}
|
||||
|
||||
// The mapping between hardware registers and DWARF registers is specified
|
||||
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
|
||||
// figure 3.36
|
||||
// https://www.uclibc.org/docs/psABI-x86_64.pdf
|
||||
|
||||
var asm64DwarfToHardware = map[int]x86asm.Reg{
|
||||
0: x86asm.RAX,
|
||||
1: x86asm.RDX,
|
||||
2: x86asm.RCX,
|
||||
3: x86asm.RBX,
|
||||
4: x86asm.RSI,
|
||||
5: x86asm.RDI,
|
||||
8: x86asm.R8,
|
||||
9: x86asm.R9,
|
||||
10: x86asm.R10,
|
||||
11: x86asm.R11,
|
||||
12: x86asm.R12,
|
||||
13: x86asm.R13,
|
||||
14: x86asm.R14,
|
||||
15: x86asm.R15,
|
||||
}
|
||||
|
||||
var amd64DwarfToName = map[int]string{
|
||||
17: "XMM0",
|
||||
18: "XMM1",
|
||||
19: "XMM2",
|
||||
20: "XMM3",
|
||||
21: "XMM4",
|
||||
22: "XMM5",
|
||||
23: "XMM6",
|
||||
24: "XMM7",
|
||||
25: "XMM8",
|
||||
26: "XMM9",
|
||||
27: "XMM10",
|
||||
28: "XMM11",
|
||||
29: "XMM12",
|
||||
30: "XMM13",
|
||||
31: "XMM14",
|
||||
32: "XMM15",
|
||||
33: "ST(0)",
|
||||
34: "ST(1)",
|
||||
35: "ST(2)",
|
||||
36: "ST(3)",
|
||||
37: "ST(4)",
|
||||
38: "ST(5)",
|
||||
39: "ST(6)",
|
||||
40: "ST(7)",
|
||||
49: "Eflags",
|
||||
50: "Es",
|
||||
51: "Cs",
|
||||
52: "Ss",
|
||||
53: "Ds",
|
||||
54: "Fs",
|
||||
55: "Gs",
|
||||
58: "Fs_base",
|
||||
59: "Gs_base",
|
||||
64: "MXCSR",
|
||||
65: "CW",
|
||||
66: "SW",
|
||||
}
|
||||
|
||||
func maxAmd64DwarfRegister() int {
|
||||
max := int(amd64DwarfIPRegNum)
|
||||
for i := range asm64DwarfToHardware {
|
||||
if i > max {
|
||||
max = i
|
||||
}
|
||||
}
|
||||
for i := range amd64DwarfToName {
|
||||
if i > max {
|
||||
max = i
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// RegistersToDwarfRegisters converts hardware registers to the format used
|
||||
// by the DWARF expression interpreter.
|
||||
func (a *AMD64) RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters {
|
||||
dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1)
|
||||
|
||||
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
|
||||
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP())
|
||||
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP())
|
||||
|
||||
for dwarfReg, asmReg := range asm64DwarfToHardware {
|
||||
v, err := regs.Get(int(asmReg))
|
||||
if err == nil {
|
||||
dregs[dwarfReg] = op.DwarfRegisterFromUint64(v)
|
||||
}
|
||||
}
|
||||
|
||||
for _, reg := range regs.Slice() {
|
||||
for dwarfReg, regName := range amd64DwarfToName {
|
||||
if regName == reg.Name {
|
||||
dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return op.DwarfRegisters{StaticBase: staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
|
||||
}
|
||||
|
||||
// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked
|
||||
// goroutine in the format used by the DWARF expression interpreter.
|
||||
func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters {
|
||||
dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1)
|
||||
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC)
|
||||
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP)
|
||||
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP)
|
||||
return op.DwarfRegisters{StaticBase: g.variable.bi.staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
|
||||
}
|
878
vendor/github.com/derekparker/delve/pkg/proc/bininfo.go
generated
vendored
878
vendor/github.com/derekparker/delve/pkg/proc/bininfo.go
generated
vendored
@ -1,878 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
"github.com/derekparker/delve/pkg/goversion"
|
||||
)
|
||||
|
||||
// BinaryInfo holds information on the binary being executed.
|
||||
type BinaryInfo struct {
|
||||
lastModified time.Time // Time the executable of this process was last modified
|
||||
|
||||
GOOS string
|
||||
closer io.Closer
|
||||
sepDebugCloser io.Closer
|
||||
|
||||
staticBase uint64
|
||||
|
||||
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||
packageMap map[string]string
|
||||
|
||||
Arch Arch
|
||||
dwarf *dwarf.Data
|
||||
frameEntries frame.FrameDescriptionEntries
|
||||
loclist loclistReader
|
||||
compileUnits []*compileUnit
|
||||
types map[string]dwarf.Offset
|
||||
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
||||
gStructOffset uint64
|
||||
|
||||
// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
|
||||
Functions []Function
|
||||
// Sources is a list of all source files found in debug_line.
|
||||
Sources []string
|
||||
// LookupFunc maps function names to a description of the function.
|
||||
LookupFunc map[string]*Function
|
||||
|
||||
typeCache map[dwarf.Offset]godwarf.Type
|
||||
|
||||
loadModuleDataOnce sync.Once
|
||||
moduleData []moduleData
|
||||
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
||||
|
||||
// runtimeTypeToDIE maps between the offset of a runtime._type in
|
||||
// runtime.moduledata.types and the offset of the DIE in debug_info. This
|
||||
// map is filled by using the extended attribute godwarf.AttrGoRuntimeType
|
||||
// which was added in go 1.11.
|
||||
runtimeTypeToDIE map[uint64]runtimeTypeDIE
|
||||
|
||||
// consts[off] lists all the constants with the type defined at offset off.
|
||||
consts constantsMap
|
||||
|
||||
loadErrMu sync.Mutex
|
||||
loadErr error
|
||||
|
||||
dwarfReader *dwarf.Reader
|
||||
}
|
||||
|
||||
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
||||
var ErrUnsupportedLinuxArch = errors.New("unsupported architecture - only linux/amd64 is supported")
|
||||
|
||||
// ErrUnsupportedWindowsArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
||||
var ErrUnsupportedWindowsArch = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
|
||||
// ErrUnsupportedDarwinArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
||||
var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
|
||||
var ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE")
|
||||
|
||||
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
|
||||
|
||||
type compileUnit struct {
|
||||
Name string // univocal name for non-go compile units
|
||||
LowPC uint64
|
||||
Ranges [][2]uint64
|
||||
|
||||
entry *dwarf.Entry // debug_info entry describing this compile unit
|
||||
isgo bool // true if this is the go compile unit
|
||||
lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit
|
||||
concreteInlinedFns []inlinedFn // list of concrete inlined functions within this compile unit
|
||||
optimized bool // this compile unit is optimized
|
||||
producer string // producer attribute
|
||||
|
||||
startOffset, endOffset dwarf.Offset // interval of offsets contained in this compile unit
|
||||
}
|
||||
|
||||
type partialUnitConstant struct {
|
||||
name string
|
||||
typ dwarf.Offset
|
||||
value int64
|
||||
}
|
||||
|
||||
type partialUnit struct {
|
||||
entry *dwarf.Entry
|
||||
types map[string]dwarf.Offset
|
||||
variables []packageVar
|
||||
constants []partialUnitConstant
|
||||
functions []Function
|
||||
}
|
||||
|
||||
// inlinedFn represents a concrete inlined function, e.g.
|
||||
// an entry for the generated code of an inlined function.
|
||||
type inlinedFn struct {
|
||||
Name string // Name of the function that was inlined
|
||||
LowPC, HighPC uint64 // Address range of the generated inlined instructions
|
||||
CallFile string // File of the call site of the inlined function
|
||||
CallLine int64 // Line of the call site of the inlined function
|
||||
Parent *Function // The function that contains this inlined function
|
||||
}
|
||||
|
||||
// Function describes a function in the target program.
|
||||
type Function struct {
|
||||
Name string
|
||||
Entry, End uint64 // same as DW_AT_lowpc and DW_AT_highpc
|
||||
offset dwarf.Offset
|
||||
cu *compileUnit
|
||||
}
|
||||
|
||||
// PackageName returns the package part of the symbol name,
|
||||
// or the empty string if there is none.
|
||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
||||
func (fn *Function) PackageName() string {
|
||||
return packageName(fn.Name)
|
||||
}
|
||||
|
||||
func packageName(name string) string {
|
||||
pathend := strings.LastIndex(name, "/")
|
||||
if pathend < 0 {
|
||||
pathend = 0
|
||||
}
|
||||
|
||||
if i := strings.Index(name[pathend:], "."); i != -1 {
|
||||
return name[:pathend+i]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ReceiverName returns the receiver type name of this symbol,
|
||||
// or the empty string if there is none.
|
||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
||||
func (fn *Function) ReceiverName() string {
|
||||
pathend := strings.LastIndex(fn.Name, "/")
|
||||
if pathend < 0 {
|
||||
pathend = 0
|
||||
}
|
||||
l := strings.Index(fn.Name[pathend:], ".")
|
||||
r := strings.LastIndex(fn.Name[pathend:], ".")
|
||||
if l == -1 || r == -1 || l == r {
|
||||
return ""
|
||||
}
|
||||
return fn.Name[pathend+l+1 : pathend+r]
|
||||
}
|
||||
|
||||
// BaseName returns the symbol name without the package or receiver name.
|
||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
||||
func (fn *Function) BaseName() string {
|
||||
if i := strings.LastIndex(fn.Name, "."); i != -1 {
|
||||
return fn.Name[i+1:]
|
||||
}
|
||||
return fn.Name
|
||||
}
|
||||
|
||||
// Optimized returns true if the function was optimized by the compiler.
|
||||
func (fn *Function) Optimized() bool {
|
||||
return fn.cu.optimized
|
||||
}
|
||||
|
||||
type constantsMap map[dwarf.Offset]*constantType
|
||||
|
||||
type constantType struct {
|
||||
initialized bool
|
||||
values []constantValue
|
||||
}
|
||||
|
||||
type constantValue struct {
|
||||
name string
|
||||
fullName string
|
||||
value int64
|
||||
singleBit bool
|
||||
}
|
||||
|
||||
// packageVar represents a package-level variable (or a C global variable).
|
||||
// If a global variable does not have an address (for example it's stored in
|
||||
// a register, or non-contiguously) addr will be 0.
|
||||
type packageVar struct {
|
||||
name string
|
||||
offset dwarf.Offset
|
||||
addr uint64
|
||||
}
|
||||
|
||||
type loclistReader struct {
|
||||
data []byte
|
||||
cur int
|
||||
ptrSz int
|
||||
}
|
||||
|
||||
func (rdr *loclistReader) Seek(off int) {
|
||||
rdr.cur = off
|
||||
}
|
||||
|
||||
func (rdr *loclistReader) read(sz int) []byte {
|
||||
r := rdr.data[rdr.cur : rdr.cur+sz]
|
||||
rdr.cur += sz
|
||||
return r
|
||||
}
|
||||
|
||||
func (rdr *loclistReader) oneAddr() uint64 {
|
||||
switch rdr.ptrSz {
|
||||
case 4:
|
||||
addr := binary.LittleEndian.Uint32(rdr.read(rdr.ptrSz))
|
||||
if addr == ^uint32(0) {
|
||||
return ^uint64(0)
|
||||
}
|
||||
return uint64(addr)
|
||||
case 8:
|
||||
addr := uint64(binary.LittleEndian.Uint64(rdr.read(rdr.ptrSz)))
|
||||
return addr
|
||||
default:
|
||||
panic("bad address size")
|
||||
}
|
||||
}
|
||||
|
||||
func (rdr *loclistReader) Next(e *loclistEntry) bool {
|
||||
e.lowpc = rdr.oneAddr()
|
||||
e.highpc = rdr.oneAddr()
|
||||
|
||||
if e.lowpc == 0 && e.highpc == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if e.BaseAddressSelection() {
|
||||
e.instr = nil
|
||||
return true
|
||||
}
|
||||
|
||||
instrlen := binary.LittleEndian.Uint16(rdr.read(2))
|
||||
e.instr = rdr.read(int(instrlen))
|
||||
return true
|
||||
}
|
||||
|
||||
type loclistEntry struct {
|
||||
lowpc, highpc uint64
|
||||
instr []byte
|
||||
}
|
||||
|
||||
type runtimeTypeDIE struct {
|
||||
offset dwarf.Offset
|
||||
kind int64
|
||||
}
|
||||
|
||||
func (e *loclistEntry) BaseAddressSelection() bool {
|
||||
return e.lowpc == ^uint64(0)
|
||||
}
|
||||
|
||||
type buildIDHeader struct {
|
||||
Namesz uint32
|
||||
Descsz uint32
|
||||
Type uint32
|
||||
}
|
||||
|
||||
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
|
||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
||||
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
||||
|
||||
// TODO: find better way to determine proc arch (perhaps use executable file info).
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
r.Arch = AMD64Arch(goos)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// LoadBinaryInfo will load and store the information from the binary at 'path'.
|
||||
// It is expected this will be called in parallel with other initialization steps
|
||||
// so a sync.WaitGroup must be provided.
|
||||
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
||||
fi, err := os.Stat(path)
|
||||
if err == nil {
|
||||
bi.lastModified = fi.ModTime()
|
||||
}
|
||||
|
||||
switch bi.GOOS {
|
||||
case "linux":
|
||||
return bi.LoadBinaryInfoElf(path, entryPoint, wg)
|
||||
case "windows":
|
||||
return bi.LoadBinaryInfoPE(path, entryPoint, wg)
|
||||
case "darwin":
|
||||
return bi.LoadBinaryInfoMacho(path, entryPoint, wg)
|
||||
}
|
||||
return errors.New("unsupported operating system")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GStructOffset returns the offset of the G
|
||||
// struct in thread local storage.
|
||||
func (bi *BinaryInfo) GStructOffset() uint64 {
|
||||
return bi.gStructOffset
|
||||
}
|
||||
|
||||
// LastModified returns the last modified time of the binary.
|
||||
func (bi *BinaryInfo) LastModified() time.Time {
|
||||
return bi.lastModified
|
||||
}
|
||||
|
||||
// DwarfReader returns a reader for the dwarf data
|
||||
func (bi *BinaryInfo) DwarfReader() *reader.Reader {
|
||||
return reader.New(bi.dwarf)
|
||||
}
|
||||
|
||||
// Types returns list of types present in the debugged program.
|
||||
func (bi *BinaryInfo) Types() ([]string, error) {
|
||||
types := make([]string, 0, len(bi.types))
|
||||
for k := range bi.types {
|
||||
types = append(types, k)
|
||||
}
|
||||
return types, nil
|
||||
}
|
||||
|
||||
// PCToLine converts an instruction address to a file/line/function.
|
||||
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *Function) {
|
||||
fn := bi.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return "", 0, nil
|
||||
}
|
||||
f, ln := fn.cu.lineInfo.PCToLine(fn.Entry, pc)
|
||||
return f, ln, fn
|
||||
}
|
||||
|
||||
// LineToPC converts a file:line into a memory address.
|
||||
func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Function, err error) {
|
||||
for _, cu := range bi.compileUnits {
|
||||
if cu.lineInfo.Lookup[filename] != nil {
|
||||
pc = cu.lineInfo.LineToPC(filename, lineno)
|
||||
if pc == 0 {
|
||||
// Check to see if this file:line belongs to the call site
|
||||
// of an inlined function.
|
||||
for _, ifn := range cu.concreteInlinedFns {
|
||||
if strings.Contains(ifn.CallFile, filename) && ifn.CallLine == int64(lineno) {
|
||||
pc = ifn.LowPC
|
||||
fn = ifn.Parent
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
fn = bi.PCToFunc(pc)
|
||||
if fn != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("could not find %s:%d", filename, lineno)
|
||||
return
|
||||
}
|
||||
|
||||
// AllPCsForFileLine returns all PC addresses for the given filename:lineno.
|
||||
func (bi *BinaryInfo) AllPCsForFileLine(filename string, lineno int) []uint64 {
|
||||
r := make([]uint64, 0, 1)
|
||||
for _, cu := range bi.compileUnits {
|
||||
if cu.lineInfo.Lookup[filename] != nil {
|
||||
r = append(r, cu.lineInfo.AllPCsForFileLine(filename, lineno)...)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// PCToFunc returns the function containing the given PC address
|
||||
func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
|
||||
i := sort.Search(len(bi.Functions), func(i int) bool {
|
||||
fn := bi.Functions[i]
|
||||
return pc <= fn.Entry || (fn.Entry <= pc && pc < fn.End)
|
||||
})
|
||||
if i != len(bi.Functions) {
|
||||
fn := &bi.Functions[i]
|
||||
if fn.Entry <= pc && pc < fn.End {
|
||||
return fn
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes all internal readers.
|
||||
func (bi *BinaryInfo) Close() error {
|
||||
if bi.sepDebugCloser != nil {
|
||||
bi.sepDebugCloser.Close()
|
||||
}
|
||||
return bi.closer.Close()
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) setLoadError(fmtstr string, args ...interface{}) {
|
||||
bi.loadErrMu.Lock()
|
||||
bi.loadErr = fmt.Errorf(fmtstr, args...)
|
||||
bi.loadErrMu.Unlock()
|
||||
}
|
||||
|
||||
// LoadError returns any internal load error.
|
||||
func (bi *BinaryInfo) LoadError() error {
|
||||
return bi.loadErr
|
||||
}
|
||||
|
||||
type nilCloser struct{}
|
||||
|
||||
func (c *nilCloser) Close() error { return nil }
|
||||
|
||||
// LoadFromData creates a new BinaryInfo object using the specified data.
|
||||
// This is used for debugging BinaryInfo, you should use LoadBinary instead.
|
||||
func (bi *BinaryInfo) LoadFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) {
|
||||
bi.closer = (*nilCloser)(nil)
|
||||
bi.sepDebugCloser = (*nilCloser)(nil)
|
||||
bi.dwarf = dwdata
|
||||
|
||||
if debugFrameBytes != nil {
|
||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), bi.staticBase)
|
||||
}
|
||||
|
||||
bi.loclistInit(debugLocBytes)
|
||||
|
||||
bi.loadDebugInfoMaps(debugLineBytes, nil, nil)
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) loclistInit(data []byte) {
|
||||
bi.loclist.data = data
|
||||
bi.loclist.ptrSz = bi.Arch.PtrSize()
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint64) ([]byte, string, error) {
|
||||
a := entry.Val(attr)
|
||||
if a == nil {
|
||||
return nil, "", fmt.Errorf("no location attribute %s", attr)
|
||||
}
|
||||
if instr, ok := a.([]byte); ok {
|
||||
var descr bytes.Buffer
|
||||
fmt.Fprintf(&descr, "[block] ")
|
||||
op.PrettyPrint(&descr, instr)
|
||||
return instr, descr.String(), nil
|
||||
}
|
||||
off, ok := a.(int64)
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("could not interpret location attribute %s", attr)
|
||||
}
|
||||
if bi.loclist.data == nil {
|
||||
return nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x (no debug_loc section found)", off, pc)
|
||||
}
|
||||
instr := bi.loclistEntry(off, pc)
|
||||
if instr == nil {
|
||||
return nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
|
||||
}
|
||||
var descr bytes.Buffer
|
||||
fmt.Fprintf(&descr, "[%#x:%#x] ", off, pc)
|
||||
op.PrettyPrint(&descr, instr)
|
||||
return instr, descr.String(), nil
|
||||
}
|
||||
|
||||
// Location returns the location described by attribute attr of entry.
|
||||
// This will either be an int64 address or a slice of Pieces for locations
|
||||
// that don't correspond to a single memory address (registers, composite
|
||||
// locations).
|
||||
func (bi *BinaryInfo) Location(entry reader.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, string, error) {
|
||||
instr, descr, err := bi.locationExpr(entry, attr, pc)
|
||||
if err != nil {
|
||||
return 0, nil, "", err
|
||||
}
|
||||
addr, pieces, err := op.ExecuteStackProgram(regs, instr)
|
||||
return addr, pieces, descr, err
|
||||
}
|
||||
|
||||
// loclistEntry returns the loclist entry in the loclist starting at off,
|
||||
// for address pc.
|
||||
func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
|
||||
var base uint64
|
||||
if cu := bi.findCompileUnit(pc); cu != nil {
|
||||
base = cu.LowPC
|
||||
}
|
||||
|
||||
bi.loclist.Seek(int(off))
|
||||
var e loclistEntry
|
||||
for bi.loclist.Next(&e) {
|
||||
if e.BaseAddressSelection() {
|
||||
base = e.highpc
|
||||
continue
|
||||
}
|
||||
if pc >= e.lowpc+base && pc < e.highpc+base {
|
||||
return e.instr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// findCompileUnit returns the compile unit containing address pc.
|
||||
func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
|
||||
for _, cu := range bi.compileUnits {
|
||||
for _, rng := range cu.Ranges {
|
||||
if pc >= rng[0] && pc < rng[1] {
|
||||
return cu
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
|
||||
for _, cu := range bi.compileUnits {
|
||||
if off >= cu.startOffset && off < cu.endOffset {
|
||||
return cu
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Producer returns the value of DW_AT_producer.
|
||||
func (bi *BinaryInfo) Producer() string {
|
||||
for _, cu := range bi.compileUnits {
|
||||
if cu.isgo && cu.producer != "" {
|
||||
return cu.producer
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Type returns the Dwarf type entry at `offset`.
|
||||
func (bi *BinaryInfo) Type(offset dwarf.Offset) (godwarf.Type, error) {
|
||||
return godwarf.ReadType(bi.dwarf, offset, bi.typeCache)
|
||||
}
|
||||
|
||||
// ELF ///////////////////////////////////////////////////////////////
|
||||
|
||||
// ErrNoBuildIDNote is used in openSeparateDebugInfo to signal there's no
|
||||
// build-id note on the binary, so LoadBinaryInfoElf will return
|
||||
// the error message coming from elfFile.DWARF() instead.
|
||||
type ErrNoBuildIDNote struct{}
|
||||
|
||||
func (e *ErrNoBuildIDNote) Error() string {
|
||||
return "can't find build-id note on binary"
|
||||
}
|
||||
|
||||
// openSeparateDebugInfo searches for a file containing the separate
|
||||
// debug info for the binary using the "build ID" method as described
|
||||
// in GDB's documentation [1], and if found returns two handles, one
|
||||
// for the bare file, and another for its corresponding elf.File.
|
||||
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
||||
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File, error) {
|
||||
buildid := exe.Section(".note.gnu.build-id")
|
||||
if buildid == nil {
|
||||
return nil, nil, &ErrNoBuildIDNote{}
|
||||
}
|
||||
|
||||
br := buildid.Open()
|
||||
bh := new(buildIDHeader)
|
||||
if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
|
||||
return nil, nil, errors.New("can't read build-id header: " + err.Error())
|
||||
}
|
||||
|
||||
name := make([]byte, bh.Namesz)
|
||||
if err := binary.Read(br, binary.LittleEndian, name); err != nil {
|
||||
return nil, nil, errors.New("can't read build-id name: " + err.Error())
|
||||
}
|
||||
|
||||
if strings.TrimSpace(string(name)) != "GNU\x00" {
|
||||
return nil, nil, errors.New("invalid build-id signature")
|
||||
}
|
||||
|
||||
descBinary := make([]byte, bh.Descsz)
|
||||
if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
|
||||
return nil, nil, errors.New("can't read build-id desc: " + err.Error())
|
||||
}
|
||||
desc := hex.EncodeToString(descBinary)
|
||||
|
||||
debugPath := fmt.Sprintf("/usr/lib/debug/.build-id/%s/%s.debug", desc[:2], desc[2:])
|
||||
sepFile, err := os.OpenFile(debugPath, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("can't open separate debug file: " + err.Error())
|
||||
}
|
||||
|
||||
elfFile, err := elf.NewFile(sepFile)
|
||||
if err != nil {
|
||||
sepFile.Close()
|
||||
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, err.Error())
|
||||
}
|
||||
|
||||
if elfFile.Machine != elf.EM_X86_64 {
|
||||
sepFile.Close()
|
||||
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, ErrUnsupportedLinuxArch.Error())
|
||||
}
|
||||
|
||||
return sepFile, elfFile, nil
|
||||
}
|
||||
|
||||
// LoadBinaryInfoElf specifically loads information from an ELF binary.
|
||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
||||
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bi.closer = exe
|
||||
elfFile, err := elf.NewFile(exe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elfFile.Machine != elf.EM_X86_64 {
|
||||
return ErrUnsupportedLinuxArch
|
||||
}
|
||||
|
||||
if entryPoint != 0 {
|
||||
bi.staticBase = entryPoint - elfFile.Entry
|
||||
} else {
|
||||
if elfFile.Type == elf.ET_DYN {
|
||||
return ErrCouldNotDetermineRelocation
|
||||
}
|
||||
}
|
||||
|
||||
dwarfFile := elfFile
|
||||
|
||||
bi.dwarf, err = elfFile.DWARF()
|
||||
if err != nil {
|
||||
var sepFile *os.File
|
||||
var serr error
|
||||
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile)
|
||||
if serr != nil {
|
||||
if _, ok := serr.(*ErrNoBuildIDNote); ok {
|
||||
return err
|
||||
}
|
||||
return serr
|
||||
}
|
||||
bi.sepDebugCloser = sepFile
|
||||
bi.dwarf, err = dwarfFile.DWARF()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bi.dwarfReader = bi.dwarf.Reader()
|
||||
|
||||
debugLineBytes, err := godwarf.GetDebugSectionElf(dwarfFile, "line")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debugLocBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loc")
|
||||
bi.loclistInit(debugLocBytes)
|
||||
|
||||
wg.Add(3)
|
||||
go bi.parseDebugFrameElf(dwarfFile, wg)
|
||||
go bi.loadDebugInfoMaps(debugLineBytes, wg, nil)
|
||||
go bi.setGStructOffsetElf(dwarfFile, wg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameData, err := godwarf.GetDebugSectionElf(exe, "frame")
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .debug_frame section: %v", err)
|
||||
return
|
||||
}
|
||||
debugInfoData, err := godwarf.GetDebugSectionElf(exe, "info")
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .debug_info section: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
bi.frameEntries = frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoData), bi.staticBase)
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
// This is a bit arcane. Essentially:
|
||||
// - If the program is pure Go, it can do whatever it wants, and puts the G
|
||||
// pointer at %fs-8.
|
||||
// - Otherwise, Go asks the external linker to place the G pointer by
|
||||
// emitting runtime.tlsg, a TLS symbol, which is relocated to the chosen
|
||||
// offset in libc's TLS block.
|
||||
symbols, err := exe.Symbols()
|
||||
if err != nil {
|
||||
bi.setLoadError("could not parse ELF symbols: %v", err)
|
||||
return
|
||||
}
|
||||
var tlsg *elf.Symbol
|
||||
for _, symbol := range symbols {
|
||||
if symbol.Name == "runtime.tlsg" {
|
||||
s := symbol
|
||||
tlsg = &s
|
||||
break
|
||||
}
|
||||
}
|
||||
if tlsg == nil {
|
||||
bi.gStructOffset = ^uint64(8) + 1 // -8
|
||||
return
|
||||
}
|
||||
var tls *elf.Prog
|
||||
for _, prog := range exe.Progs {
|
||||
if prog.Type == elf.PT_TLS {
|
||||
tls = prog
|
||||
break
|
||||
}
|
||||
}
|
||||
// The TLS register points to the end of the TLS block, which is
|
||||
// tls.Memsz long. runtime.tlsg is an offset from the beginning of that block.
|
||||
bi.gStructOffset = ^(tls.Memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
|
||||
}
|
||||
|
||||
// PE ////////////////////////////////////////////////////////////////
|
||||
|
||||
const _IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
|
||||
|
||||
// LoadBinaryInfoPE specifically loads information from a PE binary.
|
||||
func (bi *BinaryInfo) LoadBinaryInfoPE(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
||||
peFile, closer, err := openExecutablePathPE(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bi.closer = closer
|
||||
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
return ErrUnsupportedWindowsArch
|
||||
}
|
||||
bi.dwarf, err = peFile.DWARF()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//TODO(aarzilli): actually test this when Go supports PIE buildmode on Windows.
|
||||
opth := peFile.OptionalHeader.(*pe.OptionalHeader64)
|
||||
if entryPoint != 0 {
|
||||
bi.staticBase = entryPoint - opth.ImageBase
|
||||
} else {
|
||||
if opth.DllCharacteristics&_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE != 0 {
|
||||
return ErrCouldNotDetermineRelocation
|
||||
}
|
||||
}
|
||||
|
||||
bi.dwarfReader = bi.dwarf.Reader()
|
||||
|
||||
debugLineBytes, err := godwarf.GetDebugSectionPE(peFile, "line")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debugLocBytes, _ := godwarf.GetDebugSectionPE(peFile, "loc")
|
||||
bi.loclistInit(debugLocBytes)
|
||||
|
||||
wg.Add(2)
|
||||
go bi.parseDebugFramePE(peFile, wg)
|
||||
go bi.loadDebugInfoMaps(debugLineBytes, wg, nil)
|
||||
|
||||
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
||||
// to G struct per:
|
||||
// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
|
||||
|
||||
bi.gStructOffset = 0x28
|
||||
return nil
|
||||
}
|
||||
|
||||
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
peFile, err := pe.NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return peFile, f, nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .debug_frame section: %v", err)
|
||||
return
|
||||
}
|
||||
debugInfoBytes, err := godwarf.GetDebugSectionPE(exe, "info")
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .debug_info section: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), bi.staticBase)
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||
for _, s := range f.Symbols {
|
||||
if s.Name != name {
|
||||
continue
|
||||
}
|
||||
if s.SectionNumber <= 0 {
|
||||
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
||||
}
|
||||
if len(f.Sections) < int(s.SectionNumber) {
|
||||
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no %s symbol found", name)
|
||||
}
|
||||
|
||||
// MACH-O ////////////////////////////////////////////////////////////
|
||||
|
||||
// LoadBinaryInfoMacho specifically loads information from a Mach-O binary.
|
||||
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
||||
exe, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bi.closer = exe
|
||||
if exe.Cpu != macho.CpuAmd64 {
|
||||
return ErrUnsupportedDarwinArch
|
||||
}
|
||||
bi.dwarf, err = exe.DWARF()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bi.dwarfReader = bi.dwarf.Reader()
|
||||
|
||||
debugLineBytes, err := godwarf.GetDebugSectionMacho(exe, "line")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debugLocBytes, _ := godwarf.GetDebugSectionMacho(exe, "loc")
|
||||
bi.loclistInit(debugLocBytes)
|
||||
|
||||
wg.Add(2)
|
||||
go bi.parseDebugFrameMacho(exe, wg)
|
||||
go bi.loadDebugInfoMaps(debugLineBytes, wg, bi.setGStructOffsetMacho)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) setGStructOffsetMacho() {
|
||||
// In go1.11 it's 0x30, before 0x8a0, see:
|
||||
// https://github.com/golang/go/issues/23617
|
||||
// and go commit b3a854c733257c5249c3435ffcee194f8439676a
|
||||
producer := bi.Producer()
|
||||
if producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 11) {
|
||||
bi.gStructOffset = 0x30
|
||||
return
|
||||
}
|
||||
bi.gStructOffset = 0x8a0
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameBytes, err := godwarf.GetDebugSectionMacho(exe, "frame")
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get __debug_frame section: %v", err)
|
||||
return
|
||||
}
|
||||
debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info")
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .debug_info section: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), bi.staticBase)
|
||||
}
|
437
vendor/github.com/derekparker/delve/pkg/proc/breakpoints.go
generated
vendored
437
vendor/github.com/derekparker/delve/pkg/proc/breakpoints.go
generated
vendored
@ -1,437 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Breakpoint represents a breakpoint. Stores information on the break
|
||||
// point including the byte of data that originally was stored at that
|
||||
// address.
|
||||
type Breakpoint struct {
|
||||
// File & line information for printing.
|
||||
FunctionName string
|
||||
File string
|
||||
Line int
|
||||
|
||||
Addr uint64 // Address breakpoint is set for.
|
||||
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
|
||||
Name string // User defined name of the breakpoint
|
||||
ID int // Monotonically increasing ID.
|
||||
|
||||
// Kind describes whether this is an internal breakpoint (for next'ing or
|
||||
// stepping).
|
||||
// A single breakpoint can be both a UserBreakpoint and some kind of
|
||||
// internal breakpoint, but it can not be two different kinds of internal
|
||||
// breakpoint.
|
||||
Kind BreakpointKind
|
||||
|
||||
// Breakpoint information
|
||||
Tracepoint bool // Tracepoint flag
|
||||
Goroutine bool // Retrieve goroutine information
|
||||
Stacktrace int // Number of stack frames to retrieve
|
||||
Variables []string // Variables to evaluate
|
||||
LoadArgs *LoadConfig
|
||||
LoadLocals *LoadConfig
|
||||
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
|
||||
TotalHitCount uint64 // Number of times a breakpoint has been reached
|
||||
|
||||
// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
|
||||
// will also check if the caller is runtime.gopanic or if the return
|
||||
// address is in the DeferReturns array.
|
||||
// Next uses NextDeferBreakpoints for the breakpoint it sets on the
|
||||
// deferred function, DeferReturns is populated with the
|
||||
// addresses of calls to runtime.deferreturn in the current
|
||||
// function. This insures that the breakpoint on the deferred
|
||||
// function only triggers on panic or on the defer call to
|
||||
// the function, not when the function is called directly
|
||||
DeferReturns []uint64
|
||||
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
|
||||
Cond ast.Expr
|
||||
// internalCond is the same as Cond but used for the condition of internal breakpoints
|
||||
internalCond ast.Expr
|
||||
|
||||
// ReturnInfo describes how to collect return variables when this
|
||||
// breakpoint is hit as a return breakpoint.
|
||||
returnInfo *returnBreakpointInfo
|
||||
}
|
||||
|
||||
// BreakpointKind determines the behavior of delve when the
|
||||
// breakpoint is reached.
|
||||
type BreakpointKind uint16
|
||||
|
||||
const (
|
||||
// UserBreakpoint is a user set breakpoint
|
||||
UserBreakpoint BreakpointKind = (1 << iota)
|
||||
// NextBreakpoint is a breakpoint set by Next, Continue
|
||||
// will stop on it and delete it
|
||||
NextBreakpoint
|
||||
// NextDeferBreakpoint is a breakpoint set by Next on the
|
||||
// first deferred function. In addition to checking their condition
|
||||
// breakpoints of this kind will also check that the function has been
|
||||
// called by runtime.gopanic or through runtime.deferreturn.
|
||||
NextDeferBreakpoint
|
||||
// StepBreakpoint is a breakpoint set by Step on a CALL instruction,
|
||||
// Continue will set a new breakpoint (of NextBreakpoint kind) on the
|
||||
// destination of CALL, delete this breakpoint and then continue again
|
||||
StepBreakpoint
|
||||
)
|
||||
|
||||
func (bp *Breakpoint) String() string {
|
||||
return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
|
||||
}
|
||||
|
||||
// BreakpointExistsError is returned when trying to set a breakpoint at
|
||||
// an address that already has a breakpoint set for it.
|
||||
type BreakpointExistsError struct {
|
||||
File string
|
||||
Line int
|
||||
Addr uint64
|
||||
}
|
||||
|
||||
func (bpe BreakpointExistsError) Error() string {
|
||||
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.File, bpe.Line, bpe.Addr)
|
||||
}
|
||||
|
||||
// InvalidAddressError represents the result of
|
||||
// attempting to set a breakpoint at an invalid address.
|
||||
type InvalidAddressError struct {
|
||||
Address uint64
|
||||
}
|
||||
|
||||
func (iae InvalidAddressError) Error() string {
|
||||
return fmt.Sprintf("Invalid address %#v\n", iae.Address)
|
||||
}
|
||||
|
||||
type returnBreakpointInfo struct {
|
||||
retFrameCond ast.Expr
|
||||
fn *Function
|
||||
frameOffset int64
|
||||
spOffset int64
|
||||
}
|
||||
|
||||
// CheckCondition evaluates bp's condition on thread.
|
||||
func (bp *Breakpoint) CheckCondition(thread Thread) BreakpointState {
|
||||
bpstate := BreakpointState{Breakpoint: bp, Active: false, Internal: false, CondError: nil}
|
||||
if bp.Cond == nil && bp.internalCond == nil {
|
||||
bpstate.Active = true
|
||||
bpstate.Internal = bp.IsInternal()
|
||||
return bpstate
|
||||
}
|
||||
nextDeferOk := true
|
||||
if bp.Kind&NextDeferBreakpoint != 0 {
|
||||
frames, err := ThreadStacktrace(thread, 2)
|
||||
if err == nil {
|
||||
ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
|
||||
isdeferreturn := false
|
||||
if len(frames) >= 1 {
|
||||
for _, pc := range bp.DeferReturns {
|
||||
if frames[0].Ret == pc {
|
||||
isdeferreturn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
nextDeferOk = ispanic || isdeferreturn
|
||||
}
|
||||
}
|
||||
if bp.IsInternal() {
|
||||
// Check internalCondition if this is also an internal breakpoint
|
||||
bpstate.Active, bpstate.CondError = evalBreakpointCondition(thread, bp.internalCond)
|
||||
bpstate.Active = bpstate.Active && nextDeferOk
|
||||
if bpstate.Active || bpstate.CondError != nil {
|
||||
bpstate.Internal = true
|
||||
return bpstate
|
||||
}
|
||||
}
|
||||
if bp.IsUser() {
|
||||
// Check normal condition if this is also a user breakpoint
|
||||
bpstate.Active, bpstate.CondError = evalBreakpointCondition(thread, bp.Cond)
|
||||
}
|
||||
return bpstate
|
||||
}
|
||||
|
||||
// IsInternal returns true if bp is an internal breakpoint.
|
||||
// User-set breakpoints can overlap with internal breakpoints, in that case
|
||||
// both IsUser and IsInternal will be true.
|
||||
func (bp *Breakpoint) IsInternal() bool {
|
||||
return bp.Kind != UserBreakpoint
|
||||
}
|
||||
|
||||
// IsUser returns true if bp is a user-set breakpoint.
|
||||
// User-set breakpoints can overlap with internal breakpoints, in that case
|
||||
// both IsUser and IsInternal will be true.
|
||||
func (bp *Breakpoint) IsUser() bool {
|
||||
return bp.Kind&UserBreakpoint != 0
|
||||
}
|
||||
|
||||
func evalBreakpointCondition(thread Thread, cond ast.Expr) (bool, error) {
|
||||
if cond == nil {
|
||||
return true, nil
|
||||
}
|
||||
scope, err := GoroutineScope(thread)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
v, err := scope.evalAST(cond)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("error evaluating expression: %v", err)
|
||||
}
|
||||
if v.Kind != reflect.Bool {
|
||||
return true, errors.New("condition expression not boolean")
|
||||
}
|
||||
v.loadValue(loadFullValue)
|
||||
if v.Unreadable != nil {
|
||||
return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
|
||||
}
|
||||
return constant.BoolVal(v.Value), nil
|
||||
}
|
||||
|
||||
// NoBreakpointError is returned when trying to
|
||||
// clear a breakpoint that does not exist.
|
||||
type NoBreakpointError struct {
|
||||
Addr uint64
|
||||
}
|
||||
|
||||
func (nbp NoBreakpointError) Error() string {
|
||||
return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
|
||||
}
|
||||
|
||||
// BreakpointMap represents an (address, breakpoint) map.
|
||||
type BreakpointMap struct {
|
||||
M map[uint64]*Breakpoint
|
||||
|
||||
breakpointIDCounter int
|
||||
internalBreakpointIDCounter int
|
||||
}
|
||||
|
||||
// NewBreakpointMap creates a new BreakpointMap.
|
||||
func NewBreakpointMap() BreakpointMap {
|
||||
return BreakpointMap{
|
||||
M: make(map[uint64]*Breakpoint),
|
||||
}
|
||||
}
|
||||
|
||||
// ResetBreakpointIDCounter resets the breakpoint ID counter of bpmap.
|
||||
func (bpmap *BreakpointMap) ResetBreakpointIDCounter() {
|
||||
bpmap.breakpointIDCounter = 0
|
||||
}
|
||||
|
||||
type writeBreakpointFn func(addr uint64) (file string, line int, fn *Function, originalData []byte, err error)
|
||||
type clearBreakpointFn func(*Breakpoint) error
|
||||
|
||||
// Set creates a breakpoint at addr calling writeBreakpoint. Do not call this
|
||||
// function, call proc.Process.SetBreakpoint instead, this function exists
|
||||
// to implement proc.Process.SetBreakpoint.
|
||||
func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
|
||||
if bp, ok := bpmap.M[addr]; ok {
|
||||
// We can overlap one internal breakpoint with one user breakpoint, we
|
||||
// need to support this otherwise a conditional breakpoint can mask a
|
||||
// breakpoint set by next or step.
|
||||
if (kind != UserBreakpoint && bp.Kind != UserBreakpoint) || (kind == UserBreakpoint && bp.IsUser()) {
|
||||
return bp, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||
}
|
||||
bp.Kind |= kind
|
||||
if kind != UserBreakpoint {
|
||||
bp.internalCond = cond
|
||||
} else {
|
||||
bp.Cond = cond
|
||||
}
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
f, l, fn, originalData, err := writeBreakpoint(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newBreakpoint := &Breakpoint{
|
||||
FunctionName: fn.Name,
|
||||
File: f,
|
||||
Line: l,
|
||||
Addr: addr,
|
||||
Kind: kind,
|
||||
OriginalData: originalData,
|
||||
HitCount: map[int]uint64{},
|
||||
}
|
||||
|
||||
if kind != UserBreakpoint {
|
||||
bpmap.internalBreakpointIDCounter++
|
||||
newBreakpoint.ID = bpmap.internalBreakpointIDCounter
|
||||
newBreakpoint.internalCond = cond
|
||||
} else {
|
||||
bpmap.breakpointIDCounter++
|
||||
newBreakpoint.ID = bpmap.breakpointIDCounter
|
||||
newBreakpoint.Cond = cond
|
||||
}
|
||||
|
||||
bpmap.M[addr] = newBreakpoint
|
||||
|
||||
return newBreakpoint, nil
|
||||
}
|
||||
|
||||
// SetWithID creates a breakpoint at addr, with the specified ID.
|
||||
func (bpmap *BreakpointMap) SetWithID(id int, addr uint64, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
|
||||
bp, err := bpmap.Set(addr, UserBreakpoint, nil, writeBreakpoint)
|
||||
if err == nil {
|
||||
bp.ID = id
|
||||
bpmap.breakpointIDCounter--
|
||||
}
|
||||
return bp, err
|
||||
}
|
||||
|
||||
// Clear clears the breakpoint at addr.
|
||||
// Do not call this function call proc.Process.ClearBreakpoint instead.
|
||||
func (bpmap *BreakpointMap) Clear(addr uint64, clearBreakpoint clearBreakpointFn) (*Breakpoint, error) {
|
||||
bp, ok := bpmap.M[addr]
|
||||
if !ok {
|
||||
return nil, NoBreakpointError{Addr: addr}
|
||||
}
|
||||
|
||||
bp.Kind &= ^UserBreakpoint
|
||||
bp.Cond = nil
|
||||
if bp.Kind != 0 {
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
if err := clearBreakpoint(bp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(bpmap.M, addr)
|
||||
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// ClearInternalBreakpoints removes all internal breakpoints from the map,
|
||||
// calling clearBreakpoint on each one.
|
||||
// Do not call this function, call proc.Process.ClearInternalBreakpoints
|
||||
// instead, this function is used to implement that.
|
||||
func (bpmap *BreakpointMap) ClearInternalBreakpoints(clearBreakpoint clearBreakpointFn) error {
|
||||
for addr, bp := range bpmap.M {
|
||||
bp.Kind = bp.Kind & UserBreakpoint
|
||||
bp.internalCond = nil
|
||||
bp.returnInfo = nil
|
||||
if bp.Kind != 0 {
|
||||
continue
|
||||
}
|
||||
if err := clearBreakpoint(bp); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(bpmap.M, addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasInternalBreakpoints returns true if bpmap has at least one internal
|
||||
// breakpoint set.
|
||||
func (bpmap *BreakpointMap) HasInternalBreakpoints() bool {
|
||||
for _, bp := range bpmap.M {
|
||||
if bp.IsInternal() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BreakpointState describes the state of a breakpoint in a thread.
|
||||
type BreakpointState struct {
|
||||
*Breakpoint
|
||||
// Active is true if the breakpoint condition was met.
|
||||
Active bool
|
||||
// Internal is true if the breakpoint was matched as an internal
|
||||
// breakpoint.
|
||||
Internal bool
|
||||
// CondError contains any error encountered while evaluating the
|
||||
// breakpoint's condition.
|
||||
CondError error
|
||||
}
|
||||
|
||||
// Clear zeros the struct.
|
||||
func (bpstate *BreakpointState) Clear() {
|
||||
bpstate.Breakpoint = nil
|
||||
bpstate.Active = false
|
||||
bpstate.Internal = false
|
||||
bpstate.CondError = nil
|
||||
}
|
||||
|
||||
func (bpstate *BreakpointState) String() string {
|
||||
s := bpstate.Breakpoint.String()
|
||||
if bpstate.Active {
|
||||
s += " active"
|
||||
}
|
||||
if bpstate.Internal {
|
||||
s += " internal"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func configureReturnBreakpoint(bi *BinaryInfo, bp *Breakpoint, topframe *Stackframe, retFrameCond ast.Expr) {
|
||||
if topframe.Current.Fn == nil {
|
||||
return
|
||||
}
|
||||
bp.returnInfo = &returnBreakpointInfo{
|
||||
retFrameCond: retFrameCond,
|
||||
fn: topframe.Current.Fn,
|
||||
frameOffset: topframe.FrameOffset(),
|
||||
spOffset: topframe.FrameOffset() - int64(bi.Arch.PtrSize()), // must be the value that SP had at the entry point of the function
|
||||
}
|
||||
}
|
||||
|
||||
func (rbpi *returnBreakpointInfo) Collect(thread Thread) []*Variable {
|
||||
if rbpi == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
g, err := GetG(thread)
|
||||
if err != nil {
|
||||
return returnInfoError("could not get g", err, thread)
|
||||
}
|
||||
scope, err := GoroutineScope(thread)
|
||||
if err != nil {
|
||||
return returnInfoError("could not get scope", err, thread)
|
||||
}
|
||||
v, err := scope.evalAST(rbpi.retFrameCond)
|
||||
if err != nil || v.Unreadable != nil || v.Kind != reflect.Bool {
|
||||
// This condition was evaluated as part of the breakpoint condition
|
||||
// evaluation, if the errors happen they will be reported as part of the
|
||||
// condition errors.
|
||||
return nil
|
||||
}
|
||||
if !constant.BoolVal(v.Value) {
|
||||
// Breakpoint not hit as a return breakpoint.
|
||||
return nil
|
||||
}
|
||||
|
||||
oldFrameOffset := rbpi.frameOffset + int64(g.stackhi)
|
||||
oldSP := uint64(rbpi.spOffset + int64(g.stackhi))
|
||||
err = fakeFunctionEntryScope(scope, rbpi.fn, oldFrameOffset, oldSP)
|
||||
if err != nil {
|
||||
return returnInfoError("could not read function entry", err, thread)
|
||||
}
|
||||
|
||||
vars, err := scope.Locals()
|
||||
if err != nil {
|
||||
return returnInfoError("could not evaluate return variables", err, thread)
|
||||
}
|
||||
vars = filterVariables(vars, func(v *Variable) bool {
|
||||
return (v.Flags & VariableReturnArgument) != 0
|
||||
})
|
||||
|
||||
// Go saves the return variables in the opposite order that the user
|
||||
// specifies them so here we reverse the slice to make it easier to
|
||||
// understand.
|
||||
for i := 0; i < len(vars)/2; i++ {
|
||||
vars[i], vars[len(vars)-i-1] = vars[len(vars)-i-1], vars[i]
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func returnInfoError(descr string, err error, mem MemoryReadWriter) []*Variable {
|
||||
v := newConstant(constant.MakeString(fmt.Sprintf("%s: %v", descr, err.Error())), mem)
|
||||
v.Name = "return value read error"
|
||||
return []*Variable{v}
|
||||
}
|
526
vendor/github.com/derekparker/delve/pkg/proc/core/core.go
generated
vendored
526
vendor/github.com/derekparker/delve/pkg/proc/core/core.go
generated
vendored
@ -1,526 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// A SplicedMemory represents a memory space formed from multiple regions,
|
||||
// each of which may override previously regions. For example, in the following
|
||||
// core, the program text was loaded at 0x400000:
|
||||
// Start End Page Offset
|
||||
// 0x0000000000400000 0x000000000044f000 0x0000000000000000
|
||||
// but then it's partially overwritten with an RW mapping whose data is stored
|
||||
// in the core file:
|
||||
// Type Offset VirtAddr PhysAddr
|
||||
// FileSiz MemSiz Flags Align
|
||||
// LOAD 0x0000000000004000 0x000000000049a000 0x0000000000000000
|
||||
// 0x0000000000002000 0x0000000000002000 RW 1000
|
||||
// This can be represented in a SplicedMemory by adding the original region,
|
||||
// then putting the RW mapping on top of it.
|
||||
type SplicedMemory struct {
|
||||
readers []readerEntry
|
||||
}
|
||||
|
||||
type readerEntry struct {
|
||||
offset uintptr
|
||||
length uintptr
|
||||
reader proc.MemoryReader
|
||||
}
|
||||
|
||||
// Add adds a new region to the SplicedMemory, which may override existing regions.
|
||||
func (r *SplicedMemory) Add(reader proc.MemoryReader, off, length uintptr) {
|
||||
if length == 0 {
|
||||
return
|
||||
}
|
||||
end := off + length - 1
|
||||
newReaders := make([]readerEntry, 0, len(r.readers))
|
||||
add := func(e readerEntry) {
|
||||
if e.length == 0 {
|
||||
return
|
||||
}
|
||||
newReaders = append(newReaders, e)
|
||||
}
|
||||
inserted := false
|
||||
// Walk through the list of regions, fixing up any that overlap and inserting the new one.
|
||||
for _, entry := range r.readers {
|
||||
entryEnd := entry.offset + entry.length - 1
|
||||
switch {
|
||||
case entryEnd < off:
|
||||
// Entry is completely before the new region.
|
||||
add(entry)
|
||||
case end < entry.offset:
|
||||
// Entry is completely after the new region.
|
||||
if !inserted {
|
||||
add(readerEntry{off, length, reader})
|
||||
inserted = true
|
||||
}
|
||||
add(entry)
|
||||
case off <= entry.offset && entryEnd <= end:
|
||||
// Entry is completely overwritten by the new region. Drop.
|
||||
case entry.offset < off && entryEnd <= end:
|
||||
// New region overwrites the end of the entry.
|
||||
entry.length = off - entry.offset
|
||||
add(entry)
|
||||
case off <= entry.offset && end < entryEnd:
|
||||
// New reader overwrites the beginning of the entry.
|
||||
if !inserted {
|
||||
add(readerEntry{off, length, reader})
|
||||
inserted = true
|
||||
}
|
||||
overlap := entry.offset - off
|
||||
entry.offset += overlap
|
||||
entry.length -= overlap
|
||||
add(entry)
|
||||
case entry.offset < off && end < entryEnd:
|
||||
// New region punches a hole in the entry. Split it in two and put the new region in the middle.
|
||||
add(readerEntry{entry.offset, off - entry.offset, entry.reader})
|
||||
add(readerEntry{off, length, reader})
|
||||
add(readerEntry{end + 1, entryEnd - end, entry.reader})
|
||||
inserted = true
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled case: existing entry is %v len %v, new is %v len %v", entry.offset, entry.length, off, length))
|
||||
}
|
||||
}
|
||||
if !inserted {
|
||||
newReaders = append(newReaders, readerEntry{off, length, reader})
|
||||
}
|
||||
r.readers = newReaders
|
||||
}
|
||||
|
||||
// ReadMemory implements MemoryReader.ReadMemory.
|
||||
func (r *SplicedMemory) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
|
||||
started := false
|
||||
for _, entry := range r.readers {
|
||||
if entry.offset+entry.length < addr {
|
||||
if !started {
|
||||
continue
|
||||
}
|
||||
return n, fmt.Errorf("hit unmapped area at %v after %v bytes", addr, n)
|
||||
}
|
||||
|
||||
// Don't go past the region.
|
||||
pb := buf
|
||||
if addr+uintptr(len(buf)) > entry.offset+entry.length {
|
||||
pb = pb[:entry.offset+entry.length-addr]
|
||||
}
|
||||
pn, err := entry.reader.ReadMemory(pb, addr)
|
||||
n += pn
|
||||
if err != nil || pn != len(pb) {
|
||||
return n, err
|
||||
}
|
||||
buf = buf[pn:]
|
||||
addr += uintptr(pn)
|
||||
if len(buf) == 0 {
|
||||
// Done, don't bother scanning the rest.
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, fmt.Errorf("offset %v did not match any regions", addr)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// OffsetReaderAt wraps a ReaderAt into a MemoryReader, subtracting a fixed
|
||||
// offset from the address. This is useful to represent a mapping in an address
|
||||
// space. For example, if program text is mapped in at 0x400000, an
|
||||
// OffsetReaderAt with offset 0x400000 can be wrapped around file.Open(program)
|
||||
// to return the results of a read in that part of the address space.
|
||||
type OffsetReaderAt struct {
|
||||
reader io.ReaderAt
|
||||
offset uintptr
|
||||
}
|
||||
|
||||
// ReadMemory will read the memory at addr-offset.
|
||||
func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
|
||||
return r.reader.ReadAt(buf, int64(addr-r.offset))
|
||||
}
|
||||
|
||||
// Process represents a core file.
|
||||
type Process struct {
|
||||
bi *proc.BinaryInfo
|
||||
core *Core
|
||||
breakpoints proc.BreakpointMap
|
||||
currentThread *Thread
|
||||
selectedGoroutine *proc.G
|
||||
common proc.CommonProcess
|
||||
}
|
||||
|
||||
// Thread represents a thread in the core file being debugged.
|
||||
type Thread struct {
|
||||
th *LinuxPrStatus
|
||||
fpregs []proc.Register
|
||||
p *Process
|
||||
common proc.CommonThread
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrWriteCore is returned when attempting to write to the core
|
||||
// process memory.
|
||||
ErrWriteCore = errors.New("can not write to core process")
|
||||
|
||||
// ErrShortRead is returned on a short read.
|
||||
ErrShortRead = errors.New("short read")
|
||||
|
||||
// ErrContinueCore is returned when trying to continue execution of a core process.
|
||||
ErrContinueCore = errors.New("can not continue execution of core process")
|
||||
|
||||
// ErrChangeRegisterCore is returned when trying to change register values for core files.
|
||||
ErrChangeRegisterCore = errors.New("can not change register values of core process")
|
||||
)
|
||||
|
||||
// OpenCore will open the core file and return a Process struct.
|
||||
func OpenCore(corePath, exePath string) (*Process, error) {
|
||||
core, err := readCore(corePath, exePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &Process{
|
||||
core: core,
|
||||
breakpoints: proc.NewBreakpointMap(),
|
||||
bi: proc.NewBinaryInfo("linux", "amd64"),
|
||||
}
|
||||
for _, thread := range core.Threads {
|
||||
thread.p = p
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
err = p.bi.LoadBinaryInfo(exePath, core.entryPoint, &wg)
|
||||
wg.Wait()
|
||||
if err == nil {
|
||||
err = p.bi.LoadError()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, th := range p.core.Threads {
|
||||
p.currentThread = th
|
||||
break
|
||||
}
|
||||
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// BinInfo will return the binary info.
|
||||
func (p *Process) BinInfo() *proc.BinaryInfo {
|
||||
return p.bi
|
||||
}
|
||||
|
||||
// Recorded returns whether this is a live or recorded process. Always returns true for core files.
|
||||
func (p *Process) Recorded() (bool, string) { return true, "" }
|
||||
|
||||
// Restart will only return an error for core files, as they are not executing.
|
||||
func (p *Process) Restart(string) error { return ErrContinueCore }
|
||||
|
||||
// Direction will only return an error as you cannot continue a core process.
|
||||
func (p *Process) Direction(proc.Direction) error { return ErrContinueCore }
|
||||
|
||||
// When does not apply to core files, it is to support the Mozilla 'rr' backend.
|
||||
func (p *Process) When() (string, error) { return "", nil }
|
||||
|
||||
// Checkpoint for core files returns an error, there is no execution of a core file.
|
||||
func (p *Process) Checkpoint(string) (int, error) { return -1, ErrContinueCore }
|
||||
|
||||
// Checkpoints returns nil on core files, you cannot set checkpoints when debugging core files.
|
||||
func (p *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, nil }
|
||||
|
||||
// ClearCheckpoint clears a checkpoint, but will only return an error for core files.
|
||||
func (p *Process) ClearCheckpoint(int) error { return errors.New("checkpoint not found") }
|
||||
|
||||
// ReadMemory will return memory from the core file at the specified location and put the
|
||||
// read memory into `data`, returning the length read, and returning an error if
|
||||
// the length read is shorter than the length of the `data` buffer.
|
||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
n, err = t.p.core.ReadMemory(data, addr)
|
||||
if err == nil && n != len(data) {
|
||||
err = ErrShortRead
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteMemory will only return an error for core files, you cannot write
|
||||
// to the memory of a core process.
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
return 0, ErrWriteCore
|
||||
}
|
||||
|
||||
// Location returns the location of this thread based on
|
||||
// the value of the instruction pointer register.
|
||||
func (t *Thread) Location() (*proc.Location, error) {
|
||||
f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
|
||||
return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
// Breakpoint returns the current breakpoint this thread is stopped at.
|
||||
// For core files this always returns an empty BreakpointState struct, as
|
||||
// there are no breakpoints when debugging core files.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return proc.BreakpointState{}
|
||||
}
|
||||
|
||||
// ThreadID returns the ID for this thread.
|
||||
func (t *Thread) ThreadID() int {
|
||||
return int(t.th.Pid)
|
||||
}
|
||||
|
||||
// Registers returns the current value of the registers for this thread.
|
||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
r := &Registers{&t.th.Reg, nil}
|
||||
if floatingPoint {
|
||||
r.fpregs = t.fpregs
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// RestoreRegisters will only return an error for core files,
|
||||
// you cannot change register values for core files.
|
||||
func (t *Thread) RestoreRegisters(proc.Registers) error {
|
||||
return ErrChangeRegisterCore
|
||||
}
|
||||
|
||||
// Arch returns the architecture the target is built for and executing on.
|
||||
func (t *Thread) Arch() proc.Arch {
|
||||
return t.p.bi.Arch
|
||||
}
|
||||
|
||||
// BinInfo returns information about the binary.
|
||||
func (t *Thread) BinInfo() *proc.BinaryInfo {
|
||||
return t.p.bi
|
||||
}
|
||||
|
||||
// StepInstruction will only return an error for core files,
|
||||
// you cannot execute a core file.
|
||||
func (t *Thread) StepInstruction() error {
|
||||
return ErrContinueCore
|
||||
}
|
||||
|
||||
// Blocked will return false always for core files as there is
|
||||
// no execution.
|
||||
func (t *Thread) Blocked() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint will always just return nil
|
||||
// for core files, as there are no breakpoints in core files.
|
||||
func (t *Thread) SetCurrentBreakpoint() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Common returns a struct containing common information
|
||||
// across thread implementations.
|
||||
func (t *Thread) Common() *proc.CommonThread {
|
||||
return &t.common
|
||||
}
|
||||
|
||||
// SetPC will always return an error, you cannot
|
||||
// change register values when debugging core files.
|
||||
func (t *Thread) SetPC(uint64) error {
|
||||
return ErrChangeRegisterCore
|
||||
}
|
||||
|
||||
// SetSP will always return an error, you cannot
|
||||
// change register values when debugging core files.
|
||||
func (t *Thread) SetSP(uint64) error {
|
||||
return ErrChangeRegisterCore
|
||||
}
|
||||
|
||||
// SetDX will always return an error, you cannot
|
||||
// change register values when debugging core files.
|
||||
func (t *Thread) SetDX(uint64) error {
|
||||
return ErrChangeRegisterCore
|
||||
}
|
||||
|
||||
// Breakpoints will return all breakpoints for the process.
|
||||
func (p *Process) Breakpoints() *proc.BreakpointMap {
|
||||
return &p.breakpoints
|
||||
}
|
||||
|
||||
// ClearBreakpoint will always return an error as you cannot set or clear
|
||||
// breakpoints on core files.
|
||||
func (p *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
|
||||
return nil, proc.NoBreakpointError{Addr: addr}
|
||||
}
|
||||
|
||||
// ClearInternalBreakpoints will always return nil and have no
|
||||
// effect since you cannot set breakpoints on core files.
|
||||
func (p *Process) ClearInternalBreakpoints() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContinueOnce will always return an error because you
|
||||
// cannot control execution of a core file.
|
||||
func (p *Process) ContinueOnce() (proc.Thread, error) {
|
||||
return nil, ErrContinueCore
|
||||
}
|
||||
|
||||
// StepInstruction will always return an error
|
||||
// as you cannot control execution of a core file.
|
||||
func (p *Process) StepInstruction() error {
|
||||
return ErrContinueCore
|
||||
}
|
||||
|
||||
// RequestManualStop will return nil and have no effect
|
||||
// as you cannot control execution of a core file.
|
||||
func (p *Process) RequestManualStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckAndClearManualStopRequest will always return false and
|
||||
// have no effect since there are no manual stop requests as
|
||||
// there is no controlling execution of a core file.
|
||||
func (p *Process) CheckAndClearManualStopRequest() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// CurrentThread returns the current active thread.
|
||||
func (p *Process) CurrentThread() proc.Thread {
|
||||
return p.currentThread
|
||||
}
|
||||
|
||||
// Detach will always return nil and have no
|
||||
// effect as you cannot detach from a core file
|
||||
// and have it continue execution or exit.
|
||||
func (p *Process) Detach(bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Valid returns whether the process is active. Always returns true
|
||||
// for core files as it cannot exit or be otherwise detached from.
|
||||
func (p *Process) Valid() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Common returns common information across Process
|
||||
// implementations.
|
||||
func (p *Process) Common() *proc.CommonProcess {
|
||||
return &p.common
|
||||
}
|
||||
|
||||
// Pid returns the process ID of this process.
|
||||
func (p *Process) Pid() int {
|
||||
return p.core.Pid
|
||||
}
|
||||
|
||||
// ResumeNotify is a no-op on core files as we cannot
|
||||
// control execution.
|
||||
func (p *Process) ResumeNotify(chan<- struct{}) {
|
||||
}
|
||||
|
||||
// SelectedGoroutine returns the current active and selected
|
||||
// goroutine.
|
||||
func (p *Process) SelectedGoroutine() *proc.G {
|
||||
return p.selectedGoroutine
|
||||
}
|
||||
|
||||
// SetBreakpoint will always return an error for core files as you cannot write memory or control execution.
|
||||
func (p *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
||||
return nil, ErrWriteCore
|
||||
}
|
||||
|
||||
// SwitchGoroutine will change the selected and active goroutine.
|
||||
func (p *Process) SwitchGoroutine(gid int) error {
|
||||
g, err := proc.FindGoroutine(p, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.Thread != nil {
|
||||
return p.SwitchThread(g.Thread.ThreadID())
|
||||
}
|
||||
p.selectedGoroutine = g
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwitchThread will change the selected and active thread.
|
||||
func (p *Process) SwitchThread(tid int) error {
|
||||
if th, ok := p.core.Threads[tid]; ok {
|
||||
p.currentThread = th
|
||||
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// ThreadList will return a list of all threads currently in the process.
|
||||
func (p *Process) ThreadList() []proc.Thread {
|
||||
r := make([]proc.Thread, 0, len(p.core.Threads))
|
||||
for _, v := range p.core.Threads {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// FindThread will return the thread with the corresponding thread ID.
|
||||
func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
|
||||
t, ok := p.core.Threads[threadID]
|
||||
return t, ok
|
||||
}
|
||||
|
||||
// Registers represents the CPU registers.
|
||||
type Registers struct {
|
||||
*LinuxCoreRegisters
|
||||
fpregs []proc.Register
|
||||
}
|
||||
|
||||
// Slice will return a slice containing all registers and their values.
|
||||
func (r *Registers) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.Rip},
|
||||
{"Rsp", r.Rsp},
|
||||
{"Rax", r.Rax},
|
||||
{"Rbx", r.Rbx},
|
||||
{"Rcx", r.Rcx},
|
||||
{"Rdx", r.Rdx},
|
||||
{"Rdi", r.Rdi},
|
||||
{"Rsi", r.Rsi},
|
||||
{"Rbp", r.Rbp},
|
||||
{"R8", r.R8},
|
||||
{"R9", r.R9},
|
||||
{"R10", r.R10},
|
||||
{"R11", r.R11},
|
||||
{"R12", r.R12},
|
||||
{"R13", r.R13},
|
||||
{"R14", r.R14},
|
||||
{"R15", r.R15},
|
||||
{"Orig_rax", r.Orig_rax},
|
||||
{"Cs", r.Cs},
|
||||
{"Eflags", r.Eflags},
|
||||
{"Ss", r.Ss},
|
||||
{"Fs_base", r.Fs_base},
|
||||
{"Gs_base", r.Gs_base},
|
||||
{"Ds", r.Ds},
|
||||
{"Es", r.Es},
|
||||
{"Fs", r.Fs},
|
||||
{"Gs", r.Gs},
|
||||
}
|
||||
out := make([]proc.Register, 0, len(regs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// Copy will return a copy of the registers that is guarenteed
|
||||
// not to change.
|
||||
func (r *Registers) Copy() proc.Registers {
|
||||
return r
|
||||
}
|
556
vendor/github.com/derekparker/delve/pkg/proc/core/linux_amd64_core.go
generated
vendored
556
vendor/github.com/derekparker/delve/pkg/proc/core/linux_amd64_core.go
generated
vendored
@ -1,556 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/pkg/proc/linutil"
|
||||
)
|
||||
|
||||
// Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on
|
||||
// all systems.
|
||||
type LinuxCoreRegisters struct {
|
||||
R15 uint64
|
||||
R14 uint64
|
||||
R13 uint64
|
||||
R12 uint64
|
||||
Rbp uint64
|
||||
Rbx uint64
|
||||
R11 uint64
|
||||
R10 uint64
|
||||
R9 uint64
|
||||
R8 uint64
|
||||
Rax uint64
|
||||
Rcx uint64
|
||||
Rdx uint64
|
||||
Rsi uint64
|
||||
Rdi uint64
|
||||
Orig_rax uint64
|
||||
Rip uint64
|
||||
Cs uint64
|
||||
Eflags uint64
|
||||
Rsp uint64
|
||||
Ss uint64
|
||||
Fs_base uint64
|
||||
Gs_base uint64
|
||||
Ds uint64
|
||||
Es uint64
|
||||
Fs uint64
|
||||
Gs uint64
|
||||
}
|
||||
|
||||
// Copied from golang.org/x/sys/unix.Timeval since it's not available on all
|
||||
// systems.
|
||||
type LinuxCoreTimeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
// NT_FILE is file mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
|
||||
const NT_FILE elf.NType = 0x46494c45 // "FILE".
|
||||
|
||||
// NT_X86_XSTATE is other registers, including AVX and such.
|
||||
const NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAVE area.
|
||||
|
||||
// NT_AUXV is the note type for notes containing a copy of the Auxv array
|
||||
const NT_AUXV elf.NType = 0x6
|
||||
|
||||
// PC returns the value of RIP.
|
||||
func (r *LinuxCoreRegisters) PC() uint64 {
|
||||
return r.Rip
|
||||
}
|
||||
|
||||
// SP returns the value of RSP.
|
||||
func (r *LinuxCoreRegisters) SP() uint64 {
|
||||
return r.Rsp
|
||||
}
|
||||
|
||||
// BP returns the value of RBP.
|
||||
func (r *LinuxCoreRegisters) BP() uint64 {
|
||||
return r.Rbp
|
||||
}
|
||||
|
||||
// CX returns the value of RCX.
|
||||
func (r *LinuxCoreRegisters) CX() uint64 {
|
||||
return r.Rcx
|
||||
}
|
||||
|
||||
// TLS returns the location of the thread local storate,
|
||||
// which will be the value of Fs_base.
|
||||
func (r *LinuxCoreRegisters) TLS() uint64 {
|
||||
return r.Fs_base
|
||||
}
|
||||
|
||||
// GAddr returns the address of the G struct. Always returns 0
|
||||
// and false for core files.
|
||||
func (r *LinuxCoreRegisters) GAddr() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Get returns the value of the register requested via the
|
||||
// register number, returning an error if that register
|
||||
// could not be found.
|
||||
func (r *LinuxCoreRegisters) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.Rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.Rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.Rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.Rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.Rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.Rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.Rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.Rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.Rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.Rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.Rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.Rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.R8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.R9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.R10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.R11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.R12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.R13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.R14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.R15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.Rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.Rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.Rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.Rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.Rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.Rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.Rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.Rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.R8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.R9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.R10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.R11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.R12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.R13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.R14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.R15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.Rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.Rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.Rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.Rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.Rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.Rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.Rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.Rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.R8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.R9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.R10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.R11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.R12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.R13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.R14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.R15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.Rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.Rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.Rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.Rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.Rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.Rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.Rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.Rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.R8, nil
|
||||
case x86asm.R9:
|
||||
return r.R9, nil
|
||||
case x86asm.R10:
|
||||
return r.R10, nil
|
||||
case x86asm.R11:
|
||||
return r.R11, nil
|
||||
case x86asm.R12:
|
||||
return r.R12, nil
|
||||
case x86asm.R13:
|
||||
return r.R13, nil
|
||||
case x86asm.R14:
|
||||
return r.R14, nil
|
||||
case x86asm.R15:
|
||||
return r.R15, nil
|
||||
}
|
||||
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
// readCore reads a core file from corePath corresponding to the executable at
|
||||
// exePath. For details on the Linux ELF core format, see:
|
||||
// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
|
||||
// http://uhlo.blogspot.fr/2012/05/brief-look-into-core-dumps.html,
|
||||
// elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c,
|
||||
// and, if absolutely desperate, readelf.c from the binutils source.
|
||||
func readCore(corePath, exePath string) (*Core, error) {
|
||||
coreFile, err := elf.Open(corePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exe, err := os.Open(exePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exeELF, err := elf.NewFile(exe)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if coreFile.Type != elf.ET_CORE {
|
||||
return nil, fmt.Errorf("%v is not a core file", coreFile)
|
||||
}
|
||||
if exeELF.Type != elf.ET_EXEC && exeELF.Type != elf.ET_DYN {
|
||||
return nil, fmt.Errorf("%v is not an exe file", exeELF)
|
||||
}
|
||||
|
||||
notes, err := readNotes(coreFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
memory := buildMemory(coreFile, exeELF, exe, notes)
|
||||
entryPoint := findEntryPoint(notes)
|
||||
|
||||
core := &Core{
|
||||
MemoryReader: memory,
|
||||
Threads: map[int]*Thread{},
|
||||
entryPoint: entryPoint,
|
||||
}
|
||||
|
||||
var lastThread *Thread
|
||||
for _, note := range notes {
|
||||
switch note.Type {
|
||||
case elf.NT_PRSTATUS:
|
||||
t := note.Desc.(*LinuxPrStatus)
|
||||
lastThread = &Thread{t, nil, nil, proc.CommonThread{}}
|
||||
core.Threads[int(t.Pid)] = lastThread
|
||||
case NT_X86_XSTATE:
|
||||
if lastThread != nil {
|
||||
lastThread.fpregs = note.Desc.(*proc.LinuxX86Xstate).Decode()
|
||||
}
|
||||
case elf.NT_PRPSINFO:
|
||||
core.Pid = int(note.Desc.(*LinuxPrPsInfo).Pid)
|
||||
}
|
||||
}
|
||||
return core, nil
|
||||
}
|
||||
|
||||
// Core represents a core file.
|
||||
type Core struct {
|
||||
proc.MemoryReader
|
||||
Threads map[int]*Thread
|
||||
Pid int
|
||||
|
||||
entryPoint uint64
|
||||
}
|
||||
|
||||
// Note is a note from the PT_NOTE prog.
|
||||
// Relevant types:
|
||||
// - NT_FILE: File mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
|
||||
// - NT_PRPSINFO: Information about a process, including PID and signal. Desc is a LinuxPrPsInfo.
|
||||
// - NT_PRSTATUS: Information about a thread, including base registers, state, etc. Desc is a LinuxPrStatus.
|
||||
// - NT_FPREGSET (Not implemented): x87 floating point registers.
|
||||
// - NT_X86_XSTATE: Other registers, including AVX and such.
|
||||
type Note struct {
|
||||
Type elf.NType
|
||||
Name string
|
||||
Desc interface{} // Decoded Desc from the
|
||||
}
|
||||
|
||||
// readNotes reads all the notes from the notes prog in core.
|
||||
func readNotes(core *elf.File) ([]*Note, error) {
|
||||
var notesProg *elf.Prog
|
||||
for _, prog := range core.Progs {
|
||||
if prog.Type == elf.PT_NOTE {
|
||||
notesProg = prog
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r := notesProg.Open()
|
||||
notes := []*Note{}
|
||||
for {
|
||||
note, err := readNote(r)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notes = append(notes, note)
|
||||
}
|
||||
|
||||
return notes, nil
|
||||
}
|
||||
|
||||
// readNote reads a single note from r, decoding the descriptor if possible.
|
||||
func readNote(r io.ReadSeeker) (*Note, error) {
|
||||
// Notes are laid out as described in the SysV ABI:
|
||||
// http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section
|
||||
note := &Note{}
|
||||
hdr := &ELFNotesHdr{}
|
||||
|
||||
err := binary.Read(r, binary.LittleEndian, hdr)
|
||||
if err != nil {
|
||||
return nil, err // don't wrap so readNotes sees EOF.
|
||||
}
|
||||
note.Type = elf.NType(hdr.Type)
|
||||
|
||||
name := make([]byte, hdr.Namesz)
|
||||
if _, err := r.Read(name); err != nil {
|
||||
return nil, fmt.Errorf("reading name: %v", err)
|
||||
}
|
||||
note.Name = string(name)
|
||||
if err := skipPadding(r, 4); err != nil {
|
||||
return nil, fmt.Errorf("aligning after name: %v", err)
|
||||
}
|
||||
desc := make([]byte, hdr.Descsz)
|
||||
if _, err := r.Read(desc); err != nil {
|
||||
return nil, fmt.Errorf("reading desc: %v", err)
|
||||
}
|
||||
descReader := bytes.NewReader(desc)
|
||||
switch note.Type {
|
||||
case elf.NT_PRSTATUS:
|
||||
note.Desc = &LinuxPrStatus{}
|
||||
if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
|
||||
return nil, fmt.Errorf("reading NT_PRSTATUS: %v", err)
|
||||
}
|
||||
case elf.NT_PRPSINFO:
|
||||
note.Desc = &LinuxPrPsInfo{}
|
||||
if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
|
||||
return nil, fmt.Errorf("reading NT_PRPSINFO: %v", err)
|
||||
}
|
||||
case NT_FILE:
|
||||
// No good documentation reference, but the structure is
|
||||
// simply a header, including entry count, followed by that
|
||||
// many entries, and then the file name of each entry,
|
||||
// null-delimited. Not reading the names here.
|
||||
data := &LinuxNTFile{}
|
||||
if err := binary.Read(descReader, binary.LittleEndian, &data.LinuxNTFileHdr); err != nil {
|
||||
return nil, fmt.Errorf("reading NT_FILE header: %v", err)
|
||||
}
|
||||
for i := 0; i < int(data.Count); i++ {
|
||||
entry := &LinuxNTFileEntry{}
|
||||
if err := binary.Read(descReader, binary.LittleEndian, entry); err != nil {
|
||||
return nil, fmt.Errorf("reading NT_FILE entry %v: %v", i, err)
|
||||
}
|
||||
data.entries = append(data.entries, entry)
|
||||
}
|
||||
note.Desc = data
|
||||
case NT_X86_XSTATE:
|
||||
var fpregs proc.LinuxX86Xstate
|
||||
if err := proc.LinuxX86XstateRead(desc, true, &fpregs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
note.Desc = &fpregs
|
||||
case NT_AUXV:
|
||||
note.Desc = desc
|
||||
}
|
||||
if err := skipPadding(r, 4); err != nil {
|
||||
return nil, fmt.Errorf("aligning after desc: %v", err)
|
||||
}
|
||||
return note, nil
|
||||
}
|
||||
|
||||
// skipPadding moves r to the next multiple of pad.
|
||||
func skipPadding(r io.ReadSeeker, pad int64) error {
|
||||
pos, err := r.Seek(0, os.SEEK_CUR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pos%pad == 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err := r.Seek(pad-(pos%pad), os.SEEK_CUR); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildMemory(core, exeELF *elf.File, exe io.ReaderAt, notes []*Note) proc.MemoryReader {
|
||||
memory := &SplicedMemory{}
|
||||
|
||||
// For now, assume all file mappings are to the exe.
|
||||
for _, note := range notes {
|
||||
if note.Type == NT_FILE {
|
||||
fileNote := note.Desc.(*LinuxNTFile)
|
||||
for _, entry := range fileNote.entries {
|
||||
r := &OffsetReaderAt{
|
||||
reader: exe,
|
||||
offset: uintptr(entry.Start - (entry.FileOfs * fileNote.PageSize)),
|
||||
}
|
||||
memory.Add(r, uintptr(entry.Start), uintptr(entry.End-entry.Start))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Load memory segments from exe and then from the core file,
|
||||
// allowing the corefile to overwrite previously loaded segments
|
||||
for _, elfFile := range []*elf.File{exeELF, core} {
|
||||
for _, prog := range elfFile.Progs {
|
||||
if prog.Type == elf.PT_LOAD {
|
||||
if prog.Filesz == 0 {
|
||||
continue
|
||||
}
|
||||
r := &OffsetReaderAt{
|
||||
reader: prog.ReaderAt,
|
||||
offset: uintptr(prog.Vaddr),
|
||||
}
|
||||
memory.Add(r, uintptr(prog.Vaddr), uintptr(prog.Filesz))
|
||||
}
|
||||
}
|
||||
}
|
||||
return memory
|
||||
}
|
||||
|
||||
func findEntryPoint(notes []*Note) uint64 {
|
||||
for _, note := range notes {
|
||||
if note.Type == NT_AUXV {
|
||||
return linutil.EntryPointFromAuxvAMD64(note.Desc.([]byte))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// LinuxPrPsInfo has various structures from the ELF spec and the Linux kernel.
|
||||
// AMD64 specific primarily because of unix.PtraceRegs, but also
|
||||
// because some of the fields are word sized.
|
||||
// See http://lxr.free-electrons.com/source/include/uapi/linux/elfcore.h
|
||||
type LinuxPrPsInfo struct {
|
||||
State uint8
|
||||
Sname int8
|
||||
Zomb uint8
|
||||
Nice int8
|
||||
_ [4]uint8
|
||||
Flag uint64
|
||||
Uid, Gid uint32
|
||||
Pid, Ppid, Pgrp, Sid int32
|
||||
Fname [16]uint8
|
||||
Args [80]uint8
|
||||
}
|
||||
|
||||
// LinuxPrStatus is a copy of the prstatus kernel struct.
|
||||
type LinuxPrStatus struct {
|
||||
Siginfo LinuxSiginfo
|
||||
Cursig uint16
|
||||
_ [2]uint8
|
||||
Sigpend uint64
|
||||
Sighold uint64
|
||||
Pid, Ppid, Pgrp, Sid int32
|
||||
Utime, Stime, CUtime, CStime LinuxCoreTimeval
|
||||
Reg LinuxCoreRegisters
|
||||
Fpvalid int32
|
||||
}
|
||||
|
||||
// LinuxSiginfo is a copy of the
|
||||
// siginfo kernel struct.
|
||||
type LinuxSiginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
}
|
||||
|
||||
// LinuxNTFile contains information on mapped files.
|
||||
type LinuxNTFile struct {
|
||||
LinuxNTFileHdr
|
||||
entries []*LinuxNTFileEntry
|
||||
}
|
||||
|
||||
// LinuxNTFileHdr is a header struct for NTFile.
|
||||
type LinuxNTFileHdr struct {
|
||||
Count uint64
|
||||
PageSize uint64
|
||||
}
|
||||
|
||||
// LinuxNTFileEntry is an entry of an NT_FILE note.
|
||||
type LinuxNTFileEntry struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
FileOfs uint64
|
||||
}
|
||||
|
||||
// ELFNotesHdr is the ELF Notes header.
|
||||
// Same size on 64 and 32-bit machines.
|
||||
type ELFNotesHdr struct {
|
||||
Namesz uint32
|
||||
Descsz uint32
|
||||
Type uint32
|
||||
}
|
121
vendor/github.com/derekparker/delve/pkg/proc/disasm.go
generated
vendored
121
vendor/github.com/derekparker/delve/pkg/proc/disasm.go
generated
vendored
@ -1,121 +0,0 @@
|
||||
package proc
|
||||
|
||||
import "sort"
|
||||
|
||||
// AsmInstruction represents one assembly instruction.
|
||||
type AsmInstruction struct {
|
||||
Loc Location
|
||||
DestLoc *Location
|
||||
Bytes []byte
|
||||
Breakpoint bool
|
||||
AtPC bool
|
||||
Inst *archInst
|
||||
}
|
||||
|
||||
// AssemblyFlavour is the assembly syntax to display.
|
||||
type AssemblyFlavour int
|
||||
|
||||
const (
|
||||
// GNUFlavour will display GNU assembly syntax.
|
||||
GNUFlavour = AssemblyFlavour(iota)
|
||||
// IntelFlavour will display Intel assembly syntax.
|
||||
IntelFlavour
|
||||
// GoFlavour will display Go assembly syntax.
|
||||
GoFlavour
|
||||
)
|
||||
|
||||
// Disassemble disassembles target memory between startPC and endPC, marking
|
||||
// the current instruction being executed in goroutine g.
|
||||
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
|
||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
|
||||
func Disassemble(dbp Process, g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g == nil {
|
||||
ct := dbp.CurrentThread()
|
||||
regs, _ := ct.Registers(false)
|
||||
return disassemble(ct, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
|
||||
}
|
||||
|
||||
var regs Registers
|
||||
var mem MemoryReadWriter = dbp.CurrentThread()
|
||||
if g.Thread != nil {
|
||||
mem = g.Thread
|
||||
regs, _ = g.Thread.Registers(false)
|
||||
}
|
||||
|
||||
return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
|
||||
}
|
||||
|
||||
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startPC, endPC uint64, singleInstr bool) ([]AsmInstruction, error) {
|
||||
mem := make([]byte, int(endPC-startPC))
|
||||
_, err := memrw.ReadMemory(mem, uintptr(startPC))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := make([]AsmInstruction, 0, len(mem)/15)
|
||||
pc := startPC
|
||||
|
||||
var curpc uint64
|
||||
if regs != nil {
|
||||
curpc = regs.PC()
|
||||
}
|
||||
|
||||
for len(mem) > 0 {
|
||||
bp, atbp := breakpoints.M[pc]
|
||||
if atbp {
|
||||
for i := range bp.OriginalData {
|
||||
mem[i] = bp.OriginalData[i]
|
||||
}
|
||||
}
|
||||
file, line, fn := bi.PCToLine(pc)
|
||||
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
|
||||
inst, err := asmDecode(mem, pc)
|
||||
if err == nil {
|
||||
atpc := (regs != nil) && (curpc == pc)
|
||||
destloc := resolveCallArg(inst, atpc, regs, memrw, bi)
|
||||
r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
|
||||
|
||||
pc += uint64(inst.Size())
|
||||
mem = mem[inst.Size():]
|
||||
} else {
|
||||
r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
|
||||
pc++
|
||||
mem = mem[1:]
|
||||
}
|
||||
if singleInstr {
|
||||
break
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Looks up symbol (either functions or global variables) at address addr.
|
||||
// Used by disassembly formatter.
|
||||
func (bi *BinaryInfo) symLookup(addr uint64) (string, uint64) {
|
||||
fn := bi.PCToFunc(addr)
|
||||
if fn != nil {
|
||||
if fn.Entry == addr {
|
||||
// only report the function name if it's the exact address because it's
|
||||
// easier to read the absolute address than function_name+offset.
|
||||
return fn.Name, fn.Entry
|
||||
}
|
||||
return "", 0
|
||||
}
|
||||
i := sort.Search(len(bi.packageVars), func(i int) bool {
|
||||
return bi.packageVars[i].addr >= addr
|
||||
})
|
||||
if i >= len(bi.packageVars) {
|
||||
return "", 0
|
||||
}
|
||||
if bi.packageVars[i].addr > addr {
|
||||
// report previous variable + offset if i-th variable starts after addr
|
||||
i--
|
||||
}
|
||||
if i > 0 {
|
||||
return bi.packageVars[i].name, bi.packageVars[i].addr
|
||||
}
|
||||
return "", 0
|
||||
}
|
189
vendor/github.com/derekparker/delve/pkg/proc/disasm_amd64.go
generated
vendored
189
vendor/github.com/derekparker/delve/pkg/proc/disasm_amd64.go
generated
vendored
@ -1,189 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
|
||||
var maxInstructionLength uint64 = 15
|
||||
|
||||
type archInst x86asm.Inst
|
||||
|
||||
func asmDecode(mem []byte, pc uint64) (*archInst, error) {
|
||||
inst, err := x86asm.Decode(mem, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patchPCRel(pc, &inst)
|
||||
r := archInst(inst)
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func (inst *archInst) Size() int {
|
||||
return inst.Len
|
||||
}
|
||||
|
||||
// converts PC relative arguments to absolute addresses
|
||||
func patchPCRel(pc uint64, inst *x86asm.Inst) {
|
||||
for i := range inst.Args {
|
||||
rel, isrel := inst.Args[i].(x86asm.Rel)
|
||||
if isrel {
|
||||
inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Text will return the assembly instructions in human readable format according to
|
||||
// the flavour specified.
|
||||
func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
|
||||
if inst.Inst == nil {
|
||||
return "?"
|
||||
}
|
||||
|
||||
var text string
|
||||
|
||||
switch flavour {
|
||||
case GNUFlavour:
|
||||
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
|
||||
case GoFlavour:
|
||||
text = x86asm.GoSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
|
||||
case IntelFlavour:
|
||||
fallthrough
|
||||
default:
|
||||
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
// IsCall returns true if the instruction is a CALL or LCALL instruction.
|
||||
func (inst *AsmInstruction) IsCall() bool {
|
||||
if inst.Inst == nil {
|
||||
return false
|
||||
}
|
||||
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
|
||||
}
|
||||
|
||||
func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
|
||||
if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pc uint64
|
||||
var err error
|
||||
|
||||
switch arg := inst.Args[0].(type) {
|
||||
case x86asm.Imm:
|
||||
pc = uint64(arg)
|
||||
case x86asm.Reg:
|
||||
if !currentGoroutine || regs == nil {
|
||||
return nil
|
||||
}
|
||||
pc, err = regs.Get(int(arg))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
case x86asm.Mem:
|
||||
if !currentGoroutine || regs == nil {
|
||||
return nil
|
||||
}
|
||||
if arg.Segment != 0 {
|
||||
return nil
|
||||
}
|
||||
base, err1 := regs.Get(int(arg.Base))
|
||||
index, err2 := regs.Get(int(arg.Index))
|
||||
if err1 != nil || err2 != nil {
|
||||
return nil
|
||||
}
|
||||
addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
|
||||
//TODO: should this always be 64 bits instead of inst.MemBytes?
|
||||
pcbytes := make([]byte, inst.MemBytes)
|
||||
_, err := mem.ReadMemory(pcbytes, addr)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pc = binary.LittleEndian.Uint64(pcbytes)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
file, line, fn := bininfo.PCToLine(pc)
|
||||
if fn == nil {
|
||||
return nil
|
||||
}
|
||||
return &Location{PC: pc, File: file, Line: line, Fn: fn}
|
||||
}
|
||||
|
||||
type instrseq []x86asm.Op
|
||||
|
||||
// Possible stacksplit prologues are inserted by stacksplit in
|
||||
// $GOROOT/src/cmd/internal/obj/x86/obj6.go.
|
||||
// The stacksplit prologue will always begin with loading curg in CX, this
|
||||
// instruction is added by load_g_cx in the same file and is either 1 or 2
|
||||
// MOVs.
|
||||
var prologues []instrseq
|
||||
|
||||
func init() {
|
||||
var tinyStacksplit = instrseq{x86asm.CMP, x86asm.JBE}
|
||||
var smallStacksplit = instrseq{x86asm.LEA, x86asm.CMP, x86asm.JBE}
|
||||
var bigStacksplit = instrseq{x86asm.MOV, x86asm.CMP, x86asm.JE, x86asm.LEA, x86asm.SUB, x86asm.CMP, x86asm.JBE}
|
||||
var unixGetG = instrseq{x86asm.MOV}
|
||||
var windowsGetG = instrseq{x86asm.MOV, x86asm.MOV}
|
||||
|
||||
prologues = make([]instrseq, 0, 2*3)
|
||||
for _, getG := range []instrseq{unixGetG, windowsGetG} {
|
||||
for _, stacksplit := range []instrseq{tinyStacksplit, smallStacksplit, bigStacksplit} {
|
||||
prologue := make(instrseq, 0, len(getG)+len(stacksplit))
|
||||
prologue = append(prologue, getG...)
|
||||
prologue = append(prologue, stacksplit...)
|
||||
prologues = append(prologues, prologue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// firstPCAfterPrologueDisassembly returns the address of the first
|
||||
// instruction after the prologue for function fn by disassembling fn and
|
||||
// matching the instructions against known split-stack prologue patterns.
|
||||
// If sameline is set firstPCAfterPrologueDisassembly will always return an
|
||||
// address associated with the same line as fn.Entry
|
||||
func firstPCAfterPrologueDisassembly(p Process, fn *Function, sameline bool) (uint64, error) {
|
||||
var mem MemoryReadWriter = p.CurrentThread()
|
||||
breakpoints := p.Breakpoints()
|
||||
bi := p.BinInfo()
|
||||
text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End, false)
|
||||
if err != nil {
|
||||
return fn.Entry, err
|
||||
}
|
||||
|
||||
if len(text) <= 0 {
|
||||
return fn.Entry, nil
|
||||
}
|
||||
|
||||
for _, prologue := range prologues {
|
||||
if len(prologue) >= len(text) {
|
||||
continue
|
||||
}
|
||||
if checkPrologue(text, prologue) {
|
||||
r := &text[len(prologue)]
|
||||
if sameline {
|
||||
if r.Loc.Line != text[0].Loc.Line {
|
||||
return fn.Entry, nil
|
||||
}
|
||||
}
|
||||
return r.Loc.PC, nil
|
||||
}
|
||||
}
|
||||
|
||||
return fn.Entry, nil
|
||||
}
|
||||
|
||||
func checkPrologue(s []AsmInstruction, prologuePattern instrseq) bool {
|
||||
line := s[0].Loc.Line
|
||||
for i, op := range prologuePattern {
|
||||
if s[i].Inst.Op != op || s[i].Loc.Line != line {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
9
vendor/github.com/derekparker/delve/pkg/proc/doc.go
generated
vendored
9
vendor/github.com/derekparker/delve/pkg/proc/doc.go
generated
vendored
@ -1,9 +0,0 @@
|
||||
// Package proc is a low-level package that provides methods to manipulate
|
||||
// the process we are debugging.
|
||||
//
|
||||
// proc implements all core functionality including:
|
||||
// * creating / attaching to a process
|
||||
// * process manipulation (step, next, continue, halt)
|
||||
// * methods to explore the memory of the process
|
||||
//
|
||||
package proc
|
1477
vendor/github.com/derekparker/delve/pkg/proc/eval.go
generated
vendored
1477
vendor/github.com/derekparker/delve/pkg/proc/eval.go
generated
vendored
File diff suppressed because it is too large
Load Diff
561
vendor/github.com/derekparker/delve/pkg/proc/fncall.go
generated
vendored
561
vendor/github.com/derekparker/delve/pkg/proc/fncall.go
generated
vendored
@ -1,561 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
"github.com/derekparker/delve/pkg/logflags"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
|
||||
// This file implements the function call injection introduced in go1.11.
|
||||
//
|
||||
// The protocol is described in $GOROOT/src/runtime/asm_amd64.s in the
|
||||
// comments for function runtime·debugCallV1.
|
||||
//
|
||||
// There are two main entry points here. The first one is CallFunction which
|
||||
// evaluates a function call expression, sets up the function call on the
|
||||
// selected goroutine and resumes execution of the process.
|
||||
//
|
||||
// The second one is (*FunctionCallState).step() which is called every time
|
||||
// the process stops at a breakpoint inside one of the debug injcetion
|
||||
// functions.
|
||||
|
||||
const (
|
||||
debugCallFunctionNamePrefix1 = "debugCall"
|
||||
debugCallFunctionNamePrefix2 = "runtime.debugCall"
|
||||
debugCallFunctionName = "runtime.debugCallV1"
|
||||
)
|
||||
|
||||
var (
|
||||
errFuncCallUnsupported = errors.New("function calls not supported by this version of Go")
|
||||
errFuncCallUnsupportedBackend = errors.New("backend does not support function calls")
|
||||
errFuncCallInProgress = errors.New("cannot call function while another function call is already in progress")
|
||||
errNotACallExpr = errors.New("not a function call")
|
||||
errNoGoroutine = errors.New("no goroutine selected")
|
||||
errGoroutineNotRunning = errors.New("selected goroutine not running")
|
||||
errNotEnoughStack = errors.New("not enough stack space")
|
||||
errTooManyArguments = errors.New("too many arguments")
|
||||
errNotEnoughArguments = errors.New("not enough arguments")
|
||||
errNoAddrUnsupported = errors.New("arguments to a function call must have an address")
|
||||
errNotAGoFunction = errors.New("not a Go function")
|
||||
)
|
||||
|
||||
type functionCallState struct {
|
||||
// inProgress is true if a function call is in progress
|
||||
inProgress bool
|
||||
// finished is true if the function call terminated
|
||||
finished bool
|
||||
// savedRegs contains the saved registers
|
||||
savedRegs Registers
|
||||
// expr contains an expression describing the current function call
|
||||
expr string
|
||||
// err contains a saved error
|
||||
err error
|
||||
// fn is the function that is being called
|
||||
fn *Function
|
||||
// closureAddr is the address of the closure being called
|
||||
closureAddr uint64
|
||||
// argmem contains the argument frame of this function call
|
||||
argmem []byte
|
||||
// retvars contains the return variables after the function call terminates without panic'ing
|
||||
retvars []*Variable
|
||||
// retLoadCfg is the load configuration used to load return values
|
||||
retLoadCfg *LoadConfig
|
||||
// panicvar is a variable used to store the value of the panic, if the
|
||||
// called function panics.
|
||||
panicvar *Variable
|
||||
}
|
||||
|
||||
// CallFunction starts a debugger injected function call on the current thread of p.
|
||||
// See runtime.debugCallV1 in $GOROOT/src/runtime/asm_amd64.s for a
|
||||
// description of the protocol.
|
||||
func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
|
||||
bi := p.BinInfo()
|
||||
if !p.Common().fncallEnabled {
|
||||
return errFuncCallUnsupportedBackend
|
||||
}
|
||||
fncall := &p.Common().fncallState
|
||||
if fncall.inProgress {
|
||||
return errFuncCallInProgress
|
||||
}
|
||||
|
||||
*fncall = functionCallState{}
|
||||
|
||||
dbgcallfn := bi.LookupFunc[debugCallFunctionName]
|
||||
if dbgcallfn == nil {
|
||||
return errFuncCallUnsupported
|
||||
}
|
||||
|
||||
// check that the selected goroutine is running
|
||||
g := p.SelectedGoroutine()
|
||||
if g == nil {
|
||||
return errNoGoroutine
|
||||
}
|
||||
if g.Status != Grunning || g.Thread == nil {
|
||||
return errGoroutineNotRunning
|
||||
}
|
||||
|
||||
// check that there are at least 256 bytes free on the stack
|
||||
regs, err := g.Thread.Registers(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
regs = regs.Copy()
|
||||
if regs.SP()-256 <= g.stacklo {
|
||||
return errNotEnoughStack
|
||||
}
|
||||
_, err = regs.Get(int(x86asm.RAX))
|
||||
if err != nil {
|
||||
return errFuncCallUnsupportedBackend
|
||||
}
|
||||
|
||||
fn, closureAddr, argvars, err := funcCallEvalExpr(p, expr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
argmem, err := funcCallArgFrame(fn, argvars, g, bi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := callOP(bi, g.Thread, regs, dbgcallfn.Entry); err != nil {
|
||||
return err
|
||||
}
|
||||
// write the desired argument frame size at SP-(2*pointer_size) (the extra pointer is the saved PC)
|
||||
if err := writePointer(bi, g.Thread, regs.SP()-3*uint64(bi.Arch.PtrSize()), uint64(len(argmem))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fncall.inProgress = true
|
||||
fncall.savedRegs = regs
|
||||
fncall.expr = expr
|
||||
fncall.fn = fn
|
||||
fncall.closureAddr = closureAddr
|
||||
fncall.argmem = argmem
|
||||
fncall.retLoadCfg = retLoadCfg
|
||||
|
||||
fncallLog("function call initiated %v frame size %d\n", fn, len(argmem))
|
||||
|
||||
return Continue(p)
|
||||
}
|
||||
|
||||
func fncallLog(fmtstr string, args ...interface{}) {
|
||||
if !logflags.FnCall() {
|
||||
return
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{"layer": "proc", "kind": "fncall"}).Infof(fmtstr, args...)
|
||||
}
|
||||
|
||||
// writePointer writes val as an architecture pointer at addr in mem.
|
||||
func writePointer(bi *BinaryInfo, mem MemoryReadWriter, addr, val uint64) error {
|
||||
ptrbuf := make([]byte, bi.Arch.PtrSize())
|
||||
|
||||
// TODO: use target architecture endianness instead of LittleEndian
|
||||
switch len(ptrbuf) {
|
||||
case 4:
|
||||
binary.LittleEndian.PutUint32(ptrbuf, uint32(val))
|
||||
case 8:
|
||||
binary.LittleEndian.PutUint64(ptrbuf, val)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported pointer size %d", len(ptrbuf)))
|
||||
}
|
||||
_, err := mem.WriteMemory(uintptr(addr), ptrbuf)
|
||||
return err
|
||||
}
|
||||
|
||||
// callOP simulates a call instruction on the given thread:
|
||||
// * pushes the current value of PC on the stack (adjusting SP)
|
||||
// * changes the value of PC to callAddr
|
||||
// Note: regs are NOT updated!
|
||||
func callOP(bi *BinaryInfo, thread Thread, regs Registers, callAddr uint64) error {
|
||||
sp := regs.SP()
|
||||
// push PC on the stack
|
||||
sp -= uint64(bi.Arch.PtrSize())
|
||||
if err := thread.SetSP(sp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writePointer(bi, thread, sp, regs.PC()); err != nil {
|
||||
return err
|
||||
}
|
||||
return thread.SetPC(callAddr)
|
||||
}
|
||||
|
||||
// funcCallEvalExpr evaluates expr, which must be a function call, returns
|
||||
// the function being called and its arguments.
|
||||
func funcCallEvalExpr(p Process, expr string) (fn *Function, closureAddr uint64, argvars []*Variable, err error) {
|
||||
bi := p.BinInfo()
|
||||
scope, err := GoroutineScope(p.CurrentThread())
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
|
||||
t, err := parser.ParseExpr(expr)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
callexpr, iscall := t.(*ast.CallExpr)
|
||||
if !iscall {
|
||||
return nil, 0, nil, errNotACallExpr
|
||||
}
|
||||
|
||||
fnvar, err := scope.evalAST(callexpr.Fun)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
if fnvar.Kind != reflect.Func {
|
||||
return nil, 0, nil, fmt.Errorf("expression %q is not a function", exprToString(callexpr.Fun))
|
||||
}
|
||||
fnvar.loadValue(LoadConfig{false, 0, 0, 0, 0})
|
||||
if fnvar.Unreadable != nil {
|
||||
return nil, 0, nil, fnvar.Unreadable
|
||||
}
|
||||
if fnvar.Base == 0 {
|
||||
return nil, 0, nil, errors.New("nil pointer dereference")
|
||||
}
|
||||
fn = bi.PCToFunc(uint64(fnvar.Base))
|
||||
if fn == nil {
|
||||
return nil, 0, nil, fmt.Errorf("could not find DIE for function %q", exprToString(callexpr.Fun))
|
||||
}
|
||||
if !fn.cu.isgo {
|
||||
return nil, 0, nil, errNotAGoFunction
|
||||
}
|
||||
|
||||
argvars = make([]*Variable, 0, len(callexpr.Args)+1)
|
||||
if len(fnvar.Children) > 0 {
|
||||
// receiver argument
|
||||
argvars = append(argvars, &fnvar.Children[0])
|
||||
}
|
||||
for i := range callexpr.Args {
|
||||
argvar, err := scope.evalAST(callexpr.Args[i])
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
argvar.Name = exprToString(callexpr.Args[i])
|
||||
argvars = append(argvars, argvar)
|
||||
}
|
||||
|
||||
return fn, fnvar.funcvalAddr(), argvars, nil
|
||||
}
|
||||
|
||||
type funcCallArg struct {
|
||||
name string
|
||||
typ godwarf.Type
|
||||
off int64
|
||||
isret bool
|
||||
}
|
||||
|
||||
// funcCallArgFrame checks type and pointer escaping for the arguments and
|
||||
// returns the argument frame.
|
||||
func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo) (argmem []byte, err error) {
|
||||
argFrameSize, formalArgs, err := funcCallArgs(fn, bi, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(actualArgs) > len(formalArgs) {
|
||||
return nil, errTooManyArguments
|
||||
}
|
||||
if len(actualArgs) < len(formalArgs) {
|
||||
return nil, errNotEnoughArguments
|
||||
}
|
||||
|
||||
// constructs arguments frame
|
||||
argmem = make([]byte, argFrameSize)
|
||||
argmemWriter := &bufferMemoryReadWriter{argmem}
|
||||
for i := range formalArgs {
|
||||
formalArg := &formalArgs[i]
|
||||
actualArg := actualArgs[i]
|
||||
|
||||
//TODO(aarzilli): only apply the escapeCheck to leaking parameters.
|
||||
if err := escapeCheck(actualArg, formalArg.name, g); err != nil {
|
||||
return nil, fmt.Errorf("can not pass %s to %s: %v", actualArg.Name, formalArg.name, err)
|
||||
}
|
||||
|
||||
//TODO(aarzilli): autmoatic wrapping in interfaces for cases not handled
|
||||
// by convertToEface.
|
||||
|
||||
formalArgVar := newVariable(formalArg.name, uintptr(formalArg.off+fakeAddress), formalArg.typ, bi, argmemWriter)
|
||||
if err := formalArgVar.setValue(actualArg, actualArg.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return argmem, nil
|
||||
}
|
||||
|
||||
func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize int64, formalArgs []funcCallArg, err error) {
|
||||
const CFA = 0x1000
|
||||
vrdr := reader.Variables(bi.dwarf, fn.offset, reader.ToRelAddr(fn.Entry, bi.staticBase), int(^uint(0)>>1), false)
|
||||
|
||||
// typechecks arguments, calculates argument frame size
|
||||
for vrdr.Next() {
|
||||
e := vrdr.Entry()
|
||||
if e.Tag != dwarf.TagFormalParameter {
|
||||
continue
|
||||
}
|
||||
entry, argname, typ, err := readVarEntry(e, bi)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
typ = resolveTypedef(typ)
|
||||
locprog, _, err := bi.locationExpr(entry, dwarf.AttrLocation, fn.Entry)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("could not get argument location of %s: %v", argname, err)
|
||||
}
|
||||
off, _, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("unsupported location expression for argument %s: %v", argname, err)
|
||||
}
|
||||
|
||||
off -= CFA
|
||||
|
||||
if e := off + typ.Size(); e > argFrameSize {
|
||||
argFrameSize = e
|
||||
}
|
||||
|
||||
if isret, _ := entry.Val(dwarf.AttrVarParam).(bool); !isret || includeRet {
|
||||
formalArgs = append(formalArgs, funcCallArg{name: argname, typ: typ, off: off, isret: isret})
|
||||
}
|
||||
}
|
||||
if err := vrdr.Err(); err != nil {
|
||||
return 0, nil, fmt.Errorf("DWARF read error: %v", err)
|
||||
}
|
||||
|
||||
sort.Slice(formalArgs, func(i, j int) bool {
|
||||
return formalArgs[i].off < formalArgs[j].off
|
||||
})
|
||||
|
||||
return argFrameSize, formalArgs, nil
|
||||
}
|
||||
|
||||
func escapeCheck(v *Variable, name string, g *G) error {
|
||||
switch v.Kind {
|
||||
case reflect.Ptr:
|
||||
w := v.maybeDereference()
|
||||
return escapeCheckPointer(w.Addr, name, g)
|
||||
case reflect.Chan, reflect.String, reflect.Slice:
|
||||
return escapeCheckPointer(v.Base, name, g)
|
||||
case reflect.Map:
|
||||
sv := v.clone()
|
||||
sv.RealType = resolveTypedef(&(v.RealType.(*godwarf.MapType).TypedefType))
|
||||
sv = sv.maybeDereference()
|
||||
return escapeCheckPointer(sv.Addr, name, g)
|
||||
case reflect.Struct:
|
||||
t := v.RealType.(*godwarf.StructType)
|
||||
for _, field := range t.Field {
|
||||
fv, _ := v.toField(field)
|
||||
if err := escapeCheck(fv, fmt.Sprintf("%s.%s", name, field.Name), g); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Array:
|
||||
for i := int64(0); i < v.Len; i++ {
|
||||
sv, _ := v.sliceAccess(int(i))
|
||||
if err := escapeCheck(sv, fmt.Sprintf("%s[%d]", name, i), g); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Func:
|
||||
if err := escapeCheckPointer(uintptr(v.funcvalAddr()), name, g); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func escapeCheckPointer(addr uintptr, name string, g *G) error {
|
||||
if uint64(addr) >= g.stacklo && uint64(addr) < g.stackhi {
|
||||
return fmt.Errorf("stack object passed to escaping pointer: %s", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
debugCallAXPrecheckFailed = 8
|
||||
debugCallAXCompleteCall = 0
|
||||
debugCallAXReadReturn = 1
|
||||
debugCallAXReadPanic = 2
|
||||
debugCallAXRestoreRegisters = 16
|
||||
)
|
||||
|
||||
func (fncall *functionCallState) step(p Process) {
|
||||
bi := p.BinInfo()
|
||||
|
||||
thread := p.CurrentThread()
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
fncall.err = err
|
||||
fncall.finished = true
|
||||
fncall.inProgress = false
|
||||
return
|
||||
}
|
||||
regs = regs.Copy()
|
||||
|
||||
rax, _ := regs.Get(int(x86asm.RAX))
|
||||
|
||||
if logflags.FnCall() {
|
||||
loc, _ := thread.Location()
|
||||
var pc uint64
|
||||
var fnname string
|
||||
if loc != nil {
|
||||
pc = loc.PC
|
||||
if loc.Fn != nil {
|
||||
fnname = loc.Fn.Name
|
||||
}
|
||||
}
|
||||
fncallLog("function call interrupt rax=%#x (PC=%#x in %s)\n", rax, pc, fnname)
|
||||
}
|
||||
|
||||
switch rax {
|
||||
case debugCallAXPrecheckFailed:
|
||||
// get error from top of the stack and return it to user
|
||||
errvar, err := readTopstackVariable(thread, regs, "string", loadFullValue)
|
||||
if err != nil {
|
||||
fncall.err = fmt.Errorf("could not get precheck error reason: %v", err)
|
||||
break
|
||||
}
|
||||
errvar.Name = "err"
|
||||
fncall.err = fmt.Errorf("%v", constant.StringVal(errvar.Value))
|
||||
|
||||
case debugCallAXCompleteCall:
|
||||
// write arguments to the stack, call final function
|
||||
n, err := thread.WriteMemory(uintptr(regs.SP()), fncall.argmem)
|
||||
if err != nil {
|
||||
fncall.err = fmt.Errorf("could not write arguments: %v", err)
|
||||
}
|
||||
if n != len(fncall.argmem) {
|
||||
fncall.err = fmt.Errorf("short argument write: %d %d", n, len(fncall.argmem))
|
||||
}
|
||||
if fncall.closureAddr != 0 {
|
||||
// When calling a function pointer we must set the DX register to the
|
||||
// address of the function pointer itself.
|
||||
thread.SetDX(fncall.closureAddr)
|
||||
}
|
||||
callOP(bi, thread, regs, fncall.fn.Entry)
|
||||
|
||||
case debugCallAXRestoreRegisters:
|
||||
// runtime requests that we restore the registers (all except pc and sp),
|
||||
// this is also the last step of the function call protocol.
|
||||
fncall.finished = true
|
||||
pc, sp := regs.PC(), regs.SP()
|
||||
if err := thread.RestoreRegisters(fncall.savedRegs); err != nil {
|
||||
fncall.err = fmt.Errorf("could not restore registers: %v", err)
|
||||
}
|
||||
if err := thread.SetPC(pc); err != nil {
|
||||
fncall.err = fmt.Errorf("could not restore PC: %v", err)
|
||||
}
|
||||
if err := thread.SetSP(sp); err != nil {
|
||||
fncall.err = fmt.Errorf("could not restore SP: %v", err)
|
||||
}
|
||||
if err := stepInstructionOut(p, thread, debugCallFunctionName, debugCallFunctionName); err != nil {
|
||||
fncall.err = fmt.Errorf("could not step out of %s: %v", debugCallFunctionName, err)
|
||||
}
|
||||
|
||||
case debugCallAXReadReturn:
|
||||
// read return arguments from stack
|
||||
if fncall.retLoadCfg == nil || fncall.panicvar != nil {
|
||||
break
|
||||
}
|
||||
scope, err := ThreadScope(thread)
|
||||
if err != nil {
|
||||
fncall.err = fmt.Errorf("could not get return values: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
// pretend we are still inside the function we called
|
||||
fakeFunctionEntryScope(scope, fncall.fn, int64(regs.SP()), regs.SP()-uint64(bi.Arch.PtrSize()))
|
||||
|
||||
fncall.retvars, err = scope.Locals()
|
||||
if err != nil {
|
||||
fncall.err = fmt.Errorf("could not get return values: %v", err)
|
||||
break
|
||||
}
|
||||
fncall.retvars = filterVariables(fncall.retvars, func(v *Variable) bool {
|
||||
return (v.Flags & VariableReturnArgument) != 0
|
||||
})
|
||||
|
||||
loadValues(fncall.retvars, *fncall.retLoadCfg)
|
||||
|
||||
case debugCallAXReadPanic:
|
||||
// read panic value from stack
|
||||
if fncall.retLoadCfg == nil {
|
||||
return
|
||||
}
|
||||
fncall.panicvar, err = readTopstackVariable(thread, regs, "interface {}", *fncall.retLoadCfg)
|
||||
if err != nil {
|
||||
fncall.err = fmt.Errorf("could not get panic: %v", err)
|
||||
break
|
||||
}
|
||||
fncall.panicvar.Name = "~panic"
|
||||
fncall.panicvar.loadValue(*fncall.retLoadCfg)
|
||||
if fncall.panicvar.Unreadable != nil {
|
||||
fncall.err = fmt.Errorf("could not get panic: %v", fncall.panicvar.Unreadable)
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
// Got an unknown AX value, this is probably bad but the safest thing
|
||||
// possible is to ignore it and hope it didn't matter.
|
||||
fncallLog("unknown value of AX %#x", rax)
|
||||
}
|
||||
}
|
||||
|
||||
func readTopstackVariable(thread Thread, regs Registers, typename string, loadCfg LoadConfig) (*Variable, error) {
|
||||
bi := thread.BinInfo()
|
||||
scope, err := ThreadScope(thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ, err := bi.findType(typename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := scope.newVariable("", uintptr(regs.SP()), typ, scope.Mem)
|
||||
v.loadValue(loadCfg)
|
||||
if v.Unreadable != nil {
|
||||
return nil, v.Unreadable
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// fakeEntryScope alters scope to pretend that we are at the entry point of
|
||||
// fn and CFA and SP are the ones passed as argument.
|
||||
// This function is used to create a scope for a call frame that doesn't
|
||||
// exist anymore, to read the return variables of an injected function call,
|
||||
// or after a stepout command.
|
||||
func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64) error {
|
||||
scope.PC = fn.Entry
|
||||
scope.Fn = fn
|
||||
scope.File, scope.Line, _ = scope.BinInfo.PCToLine(fn.Entry)
|
||||
|
||||
scope.Regs.CFA = cfa
|
||||
scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = sp
|
||||
|
||||
scope.BinInfo.dwarfReader.Seek(fn.offset)
|
||||
e, err := scope.BinInfo.dwarfReader.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scope.Regs.FrameBase, _, _, _ = scope.BinInfo.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fncall *functionCallState) returnValues() []*Variable {
|
||||
if fncall.panicvar != nil {
|
||||
return []*Variable{fncall.panicvar}
|
||||
}
|
||||
return fncall.retvars
|
||||
}
|
1877
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver.go
generated
vendored
1877
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1233
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver_conn.go
generated
vendored
1233
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver_conn.go
generated
vendored
File diff suppressed because it is too large
Load Diff
16
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver_unix.go
generated
vendored
16
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver_unix.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
// +build linux darwin
|
||||
|
||||
package gdbserial
|
||||
|
||||
import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sysProcAttr(foreground bool) *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{Setpgid: true, Pgid: 0, Foreground: foreground}
|
||||
}
|
||||
|
||||
func foregroundSignalsIgnore() {
|
||||
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
||||
}
|
10
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver_windows.go
generated
vendored
10
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver_windows.go
generated
vendored
@ -1,10 +0,0 @@
|
||||
package gdbserial
|
||||
|
||||
import "syscall"
|
||||
|
||||
func sysProcAttr(foreground bool) *syscall.SysProcAttr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func foregroundSignalsIgnore() {
|
||||
}
|
267
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/rr.go
generated
vendored
267
vendor/github.com/derekparker/delve/pkg/proc/gdbserial/rr.go
generated
vendored
@ -1,267 +0,0 @@
|
||||
package gdbserial
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Record uses rr to record the execution of the specified program and
|
||||
// returns the trace directory's path.
|
||||
func Record(cmd []string, wd string, quiet bool) (tracedir string, err error) {
|
||||
if err := checkRRAvailabe(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rfd, wfd, err := os.Pipe()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
args := make([]string, 0, len(cmd)+2)
|
||||
args = append(args, "record", "--print-trace-dir=3")
|
||||
args = append(args, cmd...)
|
||||
rrcmd := exec.Command("rr", args...)
|
||||
rrcmd.Stdin = os.Stdin
|
||||
if !quiet {
|
||||
rrcmd.Stdout = os.Stdout
|
||||
rrcmd.Stderr = os.Stderr
|
||||
}
|
||||
rrcmd.ExtraFiles = []*os.File{wfd}
|
||||
rrcmd.Dir = wd
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
bs, _ := ioutil.ReadAll(rfd)
|
||||
tracedir = strings.TrimSpace(string(bs))
|
||||
close(done)
|
||||
}()
|
||||
|
||||
err = rrcmd.Run()
|
||||
// ignore run errors, it could be the program crashing
|
||||
wfd.Close()
|
||||
<-done
|
||||
return
|
||||
}
|
||||
|
||||
// Replay starts an instance of rr in replay mode, with the specified trace
|
||||
// directory, and connects to it.
|
||||
func Replay(tracedir string, quiet bool) (*Process, error) {
|
||||
if err := checkRRAvailabe(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rrcmd := exec.Command("rr", "replay", "--dbgport=0", tracedir)
|
||||
rrcmd.Stdout = os.Stdout
|
||||
stderr, err := rrcmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rrcmd.SysProcAttr = sysProcAttr(false)
|
||||
|
||||
initch := make(chan rrInit)
|
||||
go rrStderrParser(stderr, initch, quiet)
|
||||
|
||||
err = rrcmd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
init := <-initch
|
||||
if init.err != nil {
|
||||
rrcmd.Process.Kill()
|
||||
return nil, init.err
|
||||
}
|
||||
|
||||
p := New(rrcmd.Process)
|
||||
p.tracedir = tracedir
|
||||
err = p.Dial(init.port, init.exe, 0)
|
||||
if err != nil {
|
||||
rrcmd.Process.Kill()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// ErrPerfEventParanoid is the error returned by Reply and Record if
|
||||
// /proc/sys/kernel/perf_event_paranoid is greater than 1.
|
||||
type ErrPerfEventParanoid struct {
|
||||
actual int
|
||||
}
|
||||
|
||||
func (err ErrPerfEventParanoid) Error() string {
|
||||
return fmt.Sprintf("rr needs /proc/sys/kernel/perf_event_paranoid <= 1, but it is %d", err.actual)
|
||||
}
|
||||
|
||||
func checkRRAvailabe() error {
|
||||
if _, err := exec.LookPath("rr"); err != nil {
|
||||
return &ErrBackendUnavailable{}
|
||||
}
|
||||
|
||||
// Check that /proc/sys/kernel/perf_event_paranoid doesn't exist or is <= 1.
|
||||
buf, err := ioutil.ReadFile("/proc/sys/kernel/perf_event_paranoid")
|
||||
if err == nil {
|
||||
perfEventParanoid, _ := strconv.Atoi(strings.TrimSpace(string(buf)))
|
||||
if perfEventParanoid > 1 {
|
||||
return ErrPerfEventParanoid{perfEventParanoid}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type rrInit struct {
|
||||
port string
|
||||
exe string
|
||||
err error
|
||||
}
|
||||
|
||||
const (
|
||||
rrGdbCommandPrefix = " gdb "
|
||||
rrGdbLaunchPrefix = "Launch gdb with"
|
||||
targetCmd = "target extended-remote "
|
||||
)
|
||||
|
||||
func rrStderrParser(stderr io.Reader, initch chan<- rrInit, quiet bool) {
|
||||
rd := bufio.NewReader(stderr)
|
||||
for {
|
||||
line, err := rd.ReadString('\n')
|
||||
if err != nil {
|
||||
initch <- rrInit{"", "", err}
|
||||
close(initch)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, rrGdbCommandPrefix) {
|
||||
initch <- rrParseGdbCommand(line[len(rrGdbCommandPrefix):])
|
||||
close(initch)
|
||||
break
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, rrGdbLaunchPrefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
os.Stderr.Write([]byte(line))
|
||||
}
|
||||
}
|
||||
|
||||
io.Copy(os.Stderr, rd)
|
||||
}
|
||||
|
||||
type ErrMalformedRRGdbCommand struct {
|
||||
line, reason string
|
||||
}
|
||||
|
||||
func (err *ErrMalformedRRGdbCommand) Error() string {
|
||||
return fmt.Sprintf("malformed gdb command %q: %s", err.line, err.reason)
|
||||
}
|
||||
|
||||
func rrParseGdbCommand(line string) rrInit {
|
||||
port := ""
|
||||
fields := splitQuotedFields(line)
|
||||
for i := 0; i < len(fields); i++ {
|
||||
switch fields[i] {
|
||||
case "-ex":
|
||||
if i+1 >= len(fields) {
|
||||
return rrInit{err: &ErrMalformedRRGdbCommand{line, "-ex not followed by an argument"}}
|
||||
}
|
||||
arg := fields[i+1]
|
||||
|
||||
if !strings.HasPrefix(arg, targetCmd) {
|
||||
continue
|
||||
}
|
||||
|
||||
port = arg[len(targetCmd):]
|
||||
i++
|
||||
|
||||
case "-l":
|
||||
// skip argument
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return rrInit{err: &ErrMalformedRRGdbCommand{line, "could not find -ex argument"}}
|
||||
}
|
||||
|
||||
exe := fields[len(fields)-1]
|
||||
|
||||
return rrInit{port: port, exe: exe}
|
||||
}
|
||||
|
||||
// Like strings.Fields but ignores spaces inside areas surrounded
|
||||
// by single quotes.
|
||||
// To specify a single quote use backslash to escape it: '\''
|
||||
func splitQuotedFields(in string) []string {
|
||||
type stateEnum int
|
||||
const (
|
||||
inSpace stateEnum = iota
|
||||
inField
|
||||
inQuote
|
||||
inQuoteEscaped
|
||||
)
|
||||
state := inSpace
|
||||
r := []string{}
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, ch := range in {
|
||||
switch state {
|
||||
case inSpace:
|
||||
if ch == '\'' {
|
||||
state = inQuote
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
buf.WriteRune(ch)
|
||||
state = inField
|
||||
}
|
||||
|
||||
case inField:
|
||||
if ch == '\'' {
|
||||
state = inQuote
|
||||
} else if unicode.IsSpace(ch) {
|
||||
r = append(r, buf.String())
|
||||
buf.Reset()
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuote:
|
||||
if ch == '\'' {
|
||||
state = inField
|
||||
} else if ch == '\\' {
|
||||
state = inQuoteEscaped
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuoteEscaped:
|
||||
buf.WriteRune(ch)
|
||||
state = inQuote
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() != 0 {
|
||||
r = append(r, buf.String())
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// RecordAndReplay acts like calling Record and then Replay.
|
||||
func RecordAndReplay(cmd []string, wd string, quiet bool) (p *Process, tracedir string, err error) {
|
||||
tracedir, err = Record(cmd, wd, quiet)
|
||||
if tracedir == "" {
|
||||
return nil, "", err
|
||||
}
|
||||
p, err = Replay(tracedir, quiet)
|
||||
return p, tracedir, err
|
||||
}
|
129
vendor/github.com/derekparker/delve/pkg/proc/interface.go
generated
vendored
129
vendor/github.com/derekparker/delve/pkg/proc/interface.go
generated
vendored
@ -1,129 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
// Process represents the target of the debugger. This
|
||||
// target could be a system process, core file, etc.
|
||||
//
|
||||
// Implementations of Process are not required to be thread safe and users
|
||||
// of Process should not assume they are.
|
||||
// There is one exception to this rule: it is safe to call RequestManualStop
|
||||
// concurrently with ContinueOnce.
|
||||
type Process interface {
|
||||
Info
|
||||
ProcessManipulation
|
||||
BreakpointManipulation
|
||||
RecordingManipulation
|
||||
}
|
||||
|
||||
// RecordingManipulation is an interface for manipulating process recordings.
|
||||
type RecordingManipulation interface {
|
||||
// Recorded returns true if the current process is a recording and the path
|
||||
// to the trace directory.
|
||||
Recorded() (recorded bool, tracedir string)
|
||||
// Restart restarts the recording from the specified position, or from the
|
||||
// last checkpoint if pos == "".
|
||||
// If pos starts with 'c' it's a checkpoint ID, otherwise it's an event
|
||||
// number.
|
||||
Restart(pos string) error
|
||||
// Direction changes execution direction.
|
||||
Direction(Direction) error
|
||||
// When returns current recording position.
|
||||
When() (string, error)
|
||||
// Checkpoint sets a checkpoint at the current position.
|
||||
Checkpoint(where string) (id int, err error)
|
||||
// Checkpoints returns the list of currently set checkpoint.
|
||||
Checkpoints() ([]Checkpoint, error)
|
||||
// ClearCheckpoint removes a checkpoint.
|
||||
ClearCheckpoint(id int) error
|
||||
}
|
||||
|
||||
// Direction is the direction of execution for the target process.
|
||||
type Direction int8
|
||||
|
||||
const (
|
||||
// Forward direction executes the target normally.
|
||||
Forward Direction = 0
|
||||
// Backward direction executes the target in reverse.
|
||||
Backward Direction = 1
|
||||
)
|
||||
|
||||
// Checkpoint is a checkpoint
|
||||
type Checkpoint struct {
|
||||
ID int
|
||||
When string
|
||||
Where string
|
||||
}
|
||||
|
||||
// Info is an interface that provides general information on the target.
|
||||
type Info interface {
|
||||
Pid() int
|
||||
// ResumeNotify specifies a channel that will be closed the next time
|
||||
// ContinueOnce finishes resuming the target.
|
||||
ResumeNotify(chan<- struct{})
|
||||
// Valid returns true if this Process can be used. When it returns false it
|
||||
// also returns an error describing why the Process is invalid (either
|
||||
// ErrProcessExited or ProcessDetachedError).
|
||||
Valid() (bool, error)
|
||||
BinInfo() *BinaryInfo
|
||||
// Common returns a struct with fields common to all backends
|
||||
Common() *CommonProcess
|
||||
|
||||
ThreadInfo
|
||||
GoroutineInfo
|
||||
}
|
||||
|
||||
// ThreadInfo is an interface for getting information on active threads
|
||||
// in the process.
|
||||
type ThreadInfo interface {
|
||||
FindThread(threadID int) (Thread, bool)
|
||||
ThreadList() []Thread
|
||||
CurrentThread() Thread
|
||||
}
|
||||
|
||||
// GoroutineInfo is an interface for getting information on running goroutines.
|
||||
type GoroutineInfo interface {
|
||||
SelectedGoroutine() *G
|
||||
}
|
||||
|
||||
// ProcessManipulation is an interface for changing the execution state of a process.
|
||||
type ProcessManipulation interface {
|
||||
ContinueOnce() (trapthread Thread, err error)
|
||||
StepInstruction() error
|
||||
SwitchThread(int) error
|
||||
SwitchGoroutine(int) error
|
||||
RequestManualStop() error
|
||||
// CheckAndClearManualStopRequest returns true the first time it's called
|
||||
// after a call to RequestManualStop.
|
||||
CheckAndClearManualStopRequest() bool
|
||||
Detach(bool) error
|
||||
}
|
||||
|
||||
// BreakpointManipulation is an interface for managing breakpoints.
|
||||
type BreakpointManipulation interface {
|
||||
Breakpoints() *BreakpointMap
|
||||
SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error)
|
||||
ClearBreakpoint(addr uint64) (*Breakpoint, error)
|
||||
ClearInternalBreakpoints() error
|
||||
}
|
||||
|
||||
// CommonProcess contains fields used by this package, common to all
|
||||
// implementations of the Process interface.
|
||||
type CommonProcess struct {
|
||||
allGCache []*G
|
||||
fncallState functionCallState
|
||||
fncallEnabled bool
|
||||
}
|
||||
|
||||
// NewCommonProcess returns a struct with fields common across
|
||||
// all process implementations.
|
||||
func NewCommonProcess(fncallEnabled bool) CommonProcess {
|
||||
return CommonProcess{fncallEnabled: fncallEnabled}
|
||||
}
|
||||
|
||||
// ClearAllGCache clears the cached contents of the cache for runtime.allgs.
|
||||
func (p *CommonProcess) ClearAllGCache() {
|
||||
p.allGCache = nil
|
||||
}
|
39
vendor/github.com/derekparker/delve/pkg/proc/linutil/auxv.go
generated
vendored
39
vendor/github.com/derekparker/delve/pkg/proc/linutil/auxv.go
generated
vendored
@ -1,39 +0,0 @@
|
||||
package linutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
_AT_NULL_AMD64 = 0
|
||||
_AT_ENTRY_AMD64 = 9
|
||||
)
|
||||
|
||||
// EntryPointFromAuxv searches the elf auxiliary vector for the entry point
|
||||
// address.
|
||||
// For a description of the auxiliary vector (auxv) format see:
|
||||
// System V Application Binary Interface, AMD64 Architecture Processor
|
||||
// Supplement, section 3.4.3
|
||||
func EntryPointFromAuxvAMD64(auxv []byte) uint64 {
|
||||
rd := bytes.NewBuffer(auxv)
|
||||
|
||||
for {
|
||||
var tag, val uint64
|
||||
err := binary.Read(rd, binary.LittleEndian, &tag)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
err = binary.Read(rd, binary.LittleEndian, &val)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case _AT_NULL_AMD64:
|
||||
return 0
|
||||
case _AT_ENTRY_AMD64:
|
||||
return val
|
||||
}
|
||||
}
|
||||
}
|
4
vendor/github.com/derekparker/delve/pkg/proc/linutil/doc.go
generated
vendored
4
vendor/github.com/derekparker/delve/pkg/proc/linutil/doc.go
generated
vendored
@ -1,4 +0,0 @@
|
||||
// This package contains functions and data structures used by both the
|
||||
// linux implementation of the native backend and the core backend to deal
|
||||
// with structures used by the linux kernel.
|
||||
package linutil
|
156
vendor/github.com/derekparker/delve/pkg/proc/mem.go
generated
vendored
156
vendor/github.com/derekparker/delve/pkg/proc/mem.go
generated
vendored
@ -1,156 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
)
|
||||
|
||||
const cacheEnabled = true
|
||||
|
||||
// MemoryReader is like io.ReaderAt, but the offset is a uintptr so that it
|
||||
// can address all of 64-bit memory.
|
||||
// Redundant with memoryReadWriter but more easily suited to working with
|
||||
// the standard io package.
|
||||
type MemoryReader interface {
|
||||
// ReadMemory is just like io.ReaderAt.ReadAt.
|
||||
ReadMemory(buf []byte, addr uintptr) (n int, err error)
|
||||
}
|
||||
|
||||
// MemoryReadWriter is an interface for reading or writing to
|
||||
// the targets memory. This allows us to read from the actual
|
||||
// target memory or possibly a cache.
|
||||
type MemoryReadWriter interface {
|
||||
MemoryReader
|
||||
WriteMemory(addr uintptr, data []byte) (written int, err error)
|
||||
}
|
||||
|
||||
type memCache struct {
|
||||
loaded bool
|
||||
cacheAddr uintptr
|
||||
cache []byte
|
||||
mem MemoryReadWriter
|
||||
}
|
||||
|
||||
func (m *memCache) contains(addr uintptr, size int) bool {
|
||||
return addr >= m.cacheAddr && addr <= (m.cacheAddr+uintptr(len(m.cache)-size))
|
||||
}
|
||||
|
||||
func (m *memCache) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
if m.contains(addr, len(data)) {
|
||||
if !m.loaded {
|
||||
_, err := m.mem.ReadMemory(m.cache, m.cacheAddr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
m.loaded = true
|
||||
}
|
||||
copy(data, m.cache[addr-m.cacheAddr:])
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
return m.mem.ReadMemory(data, addr)
|
||||
}
|
||||
|
||||
func (m *memCache) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
return m.mem.WriteMemory(addr, data)
|
||||
}
|
||||
|
||||
func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter {
|
||||
if !cacheEnabled {
|
||||
return mem
|
||||
}
|
||||
if size <= 0 {
|
||||
return mem
|
||||
}
|
||||
switch cacheMem := mem.(type) {
|
||||
case *memCache:
|
||||
if cacheMem.contains(addr, size) {
|
||||
return mem
|
||||
}
|
||||
case *compositeMemory:
|
||||
return mem
|
||||
}
|
||||
return &memCache{false, addr, make([]byte, size), mem}
|
||||
}
|
||||
|
||||
// fakeAddress used by extractVarInfoFromEntry for variables that do not
|
||||
// have a memory address, we can't use 0 because a lot of code (likely
|
||||
// including client code) assumes that addr == 0 is nil
|
||||
const fakeAddress = 0xbeef0000
|
||||
|
||||
// compositeMemory represents a chunk of memory that is stored in CPU
|
||||
// registers or non-contiguously.
|
||||
//
|
||||
// When optimizations are enabled the compiler will store some variables
|
||||
// into registers and sometimes it will also store structs non-contiguously
|
||||
// with some fields stored into CPU registers and other fields stored in
|
||||
// memory.
|
||||
type compositeMemory struct {
|
||||
realmem MemoryReadWriter
|
||||
regs op.DwarfRegisters
|
||||
pieces []op.Piece
|
||||
data []byte
|
||||
}
|
||||
|
||||
func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) *compositeMemory {
|
||||
cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}}
|
||||
for _, piece := range pieces {
|
||||
if piece.IsRegister {
|
||||
reg := regs.Bytes(piece.RegNum)
|
||||
sz := piece.Size
|
||||
if sz == 0 && len(pieces) == 1 {
|
||||
sz = len(reg)
|
||||
}
|
||||
cmem.data = append(cmem.data, reg[:sz]...)
|
||||
} else {
|
||||
buf := make([]byte, piece.Size)
|
||||
mem.ReadMemory(buf, uintptr(piece.Addr))
|
||||
cmem.data = append(cmem.data, buf...)
|
||||
}
|
||||
}
|
||||
return cmem
|
||||
}
|
||||
|
||||
func (mem *compositeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
|
||||
addr -= fakeAddress
|
||||
if addr >= uintptr(len(mem.data)) || addr+uintptr(len(data)) > uintptr(len(mem.data)) {
|
||||
return 0, errors.New("read out of bounds")
|
||||
}
|
||||
copy(data, mem.data[addr:addr+uintptr(len(data))])
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (mem *compositeMemory) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
//TODO(aarzilli): implement
|
||||
return 0, errors.New("can't write composite memory")
|
||||
}
|
||||
|
||||
// DereferenceMemory returns a MemoryReadWriter that can read and write the
|
||||
// memory pointed to by pointers in this memory.
|
||||
// Normally mem and mem.Dereference are the same object, they are different
|
||||
// only if this MemoryReadWriter is used to access memory outside of the
|
||||
// normal address space of the inferior process (such as data contained in
|
||||
// registers, or composite memory).
|
||||
func DereferenceMemory(mem MemoryReadWriter) MemoryReadWriter {
|
||||
switch mem := mem.(type) {
|
||||
case *compositeMemory:
|
||||
return mem.realmem
|
||||
}
|
||||
return mem
|
||||
}
|
||||
|
||||
// bufferMemoryReadWriter is dummy a MemoryReadWriter backed by a []byte.
|
||||
type bufferMemoryReadWriter struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (mem *bufferMemoryReadWriter) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
|
||||
copy(buf, mem.buf[addr-fakeAddress:][:len(buf)])
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (mem *bufferMemoryReadWriter) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
copy(mem.buf[addr-fakeAddress:], data)
|
||||
return len(data), nil
|
||||
}
|
203
vendor/github.com/derekparker/delve/pkg/proc/moduledata.go
generated
vendored
203
vendor/github.com/derekparker/delve/pkg/proc/moduledata.go
generated
vendored
@ -1,203 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// delve counterpart to runtime.moduledata
|
||||
type moduleData struct {
|
||||
types, etypes uintptr
|
||||
typemapVar *Variable
|
||||
}
|
||||
|
||||
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) {
|
||||
bi.loadModuleDataOnce.Do(func() {
|
||||
scope := globalScope(bi, mem)
|
||||
var md *Variable
|
||||
md, err = scope.findGlobal("runtime.firstmoduledata")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for md.Addr != 0 {
|
||||
var typesVar, etypesVar, nextVar, typemapVar *Variable
|
||||
var types, etypes uint64
|
||||
|
||||
if typesVar, err = md.structMember("types"); err != nil {
|
||||
return
|
||||
}
|
||||
if etypesVar, err = md.structMember("etypes"); err != nil {
|
||||
return
|
||||
}
|
||||
if nextVar, err = md.structMember("next"); err != nil {
|
||||
return
|
||||
}
|
||||
if typemapVar, err = md.structMember("typemap"); err != nil {
|
||||
return
|
||||
}
|
||||
if types, err = typesVar.asUint(); err != nil {
|
||||
return
|
||||
}
|
||||
if etypes, err = etypesVar.asUint(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
bi.moduleData = append(bi.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
|
||||
|
||||
md = nextVar.maybeDereference()
|
||||
if md.Unreadable != nil {
|
||||
err = md.Unreadable
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func findModuleDataForType(bi *BinaryInfo, typeAddr uintptr, mem MemoryReadWriter) (*moduleData, error) {
|
||||
if err := loadModuleData(bi, mem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var md *moduleData
|
||||
for i := range bi.moduleData {
|
||||
if typeAddr >= bi.moduleData[i].types && typeAddr < bi.moduleData[i].etypes {
|
||||
md = &bi.moduleData[i]
|
||||
}
|
||||
}
|
||||
|
||||
return md, nil
|
||||
}
|
||||
|
||||
func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (*Variable, error) {
|
||||
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
|
||||
md, err := findModuleDataForType(bi, typeAddr, mem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtyp, err := bi.findType("runtime._type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if md == nil {
|
||||
v, err := reflectOffsMapAccess(bi, off, mem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue(LoadConfig{false, 1, 0, 0, -1})
|
||||
addr, _ := constant.Int64Val(v.Value)
|
||||
return v.newVariable(v.Name, uintptr(addr), rtyp, mem), nil
|
||||
}
|
||||
|
||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
res := md.types + uintptr(off)
|
||||
|
||||
return newVariable("", res, rtyp, bi, mem), nil
|
||||
}
|
||||
|
||||
func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
|
||||
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
|
||||
if err = loadModuleData(bi, mem); err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
for _, md := range bi.moduleData {
|
||||
if typeAddr >= md.types && typeAddr < md.etypes {
|
||||
return loadName(bi, md.types+off, mem)
|
||||
}
|
||||
}
|
||||
|
||||
v, err := reflectOffsMapAccess(bi, off, mem)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
resv := v.maybeDereference()
|
||||
if resv.Unreadable != nil {
|
||||
return "", "", 0, resv.Unreadable
|
||||
}
|
||||
|
||||
return loadName(bi, resv.Addr, mem)
|
||||
}
|
||||
|
||||
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) {
|
||||
scope := globalScope(bi, mem)
|
||||
reflectOffs, err := scope.findGlobal("runtime.reflectOffs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reflectOffsm, err := reflectOffs.structMember("m")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem))
|
||||
}
|
||||
|
||||
const (
|
||||
// flags for the name struct (see 'type name struct' in $GOROOT/src/reflect/type.go)
|
||||
nameflagExported = 1 << 0
|
||||
nameflagHasTag = 1 << 1
|
||||
nameflagHasPkg = 1 << 2
|
||||
)
|
||||
|
||||
func loadName(bi *BinaryInfo, addr uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
|
||||
off := addr
|
||||
namedata := make([]byte, 3)
|
||||
_, err = mem.ReadMemory(namedata, off)
|
||||
off += 3
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
namelen := uint16(namedata[1])<<8 | uint16(namedata[2])
|
||||
|
||||
rawstr := make([]byte, int(namelen))
|
||||
_, err = mem.ReadMemory(rawstr, off)
|
||||
off += uintptr(namelen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
name = string(rawstr)
|
||||
|
||||
if namedata[0]&nameflagHasTag != 0 {
|
||||
taglendata := make([]byte, 2)
|
||||
_, err = mem.ReadMemory(taglendata, off)
|
||||
off += 2
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
taglen := uint16(taglendata[0])<<8 | uint16(taglendata[1])
|
||||
|
||||
rawstr := make([]byte, int(taglen))
|
||||
_, err = mem.ReadMemory(rawstr, off)
|
||||
off += uintptr(taglen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
tag = string(rawstr)
|
||||
}
|
||||
|
||||
if namedata[0]&nameflagHasPkg != 0 {
|
||||
pkgdata := make([]byte, 4)
|
||||
_, err = mem.ReadMemory(pkgdata, off)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
// see func pkgPath in $GOROOT/src/reflect/type.go
|
||||
copy((*[4]byte)(unsafe.Pointer(&pkgpathoff))[:], pkgdata)
|
||||
}
|
||||
|
||||
return name, tag, pkgpathoff, nil
|
||||
}
|
283
vendor/github.com/derekparker/delve/pkg/proc/native/exc.h
generated
vendored
283
vendor/github.com/derekparker/delve/pkg/proc/native/exc.h
generated
vendored
@ -1,283 +0,0 @@
|
||||
#ifndef _exc_user_
|
||||
#define _exc_user_
|
||||
|
||||
/* Module exc */
|
||||
|
||||
#include <string.h>
|
||||
#include <mach/ndr.h>
|
||||
#include <mach/boolean.h>
|
||||
#include <mach/kern_return.h>
|
||||
#include <mach/notify.h>
|
||||
#include <mach/mach_types.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/mig_errors.h>
|
||||
#include <mach/port.h>
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifndef KERNEL
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<mach/mig_voucher_support.h>)
|
||||
#ifndef USING_VOUCHERS
|
||||
#define USING_VOUCHERS
|
||||
#endif
|
||||
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#define __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#endif // __has_include(<mach/mach_voucher_types.h>)
|
||||
#endif // __has_include
|
||||
#endif // !KERNEL
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
|
||||
#ifdef AUTOTEST
|
||||
#ifndef FUNCTION_PTR_T
|
||||
#define FUNCTION_PTR_T
|
||||
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
|
||||
typedef struct {
|
||||
char *name;
|
||||
function_ptr_t function;
|
||||
} function_table_entry;
|
||||
typedef function_table_entry *function_table_t;
|
||||
#endif /* FUNCTION_PTR_T */
|
||||
#endif /* AUTOTEST */
|
||||
|
||||
#ifndef exc_MSG_COUNT
|
||||
#define exc_MSG_COUNT 3
|
||||
#endif /* exc_MSG_COUNT */
|
||||
|
||||
#include <mach/std_types.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#ifdef __BeforeMigUserHeader
|
||||
__BeforeMigUserHeader
|
||||
#endif /* __BeforeMigUserHeader */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
|
||||
/* Routine exception_raise */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
);
|
||||
|
||||
/* Routine exception_raise_state */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
/* Routine exception_raise_state_identity */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
/********************** Caution **************************/
|
||||
/* The following data types should be used to calculate */
|
||||
/* maximum message sizes only. The actual message may be */
|
||||
/* smaller, and the position of the arguments within the */
|
||||
/* message layout may vary from what is presented here. */
|
||||
/* For example, if any of the arguments are variable- */
|
||||
/* sized, and less than the maximum is sent, the data */
|
||||
/* will be packed tight in the actual message to reduce */
|
||||
/* the presence of holes. */
|
||||
/********************** Caution **************************/
|
||||
|
||||
/* typedefs for all requests */
|
||||
|
||||
#ifndef __Request__exc_subsystem__defined
|
||||
#define __Request__exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
} __Request__exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Request__exc_subsystem__defined */
|
||||
|
||||
/* union of all requests */
|
||||
|
||||
#ifndef __RequestUnion__exc_subsystem__defined
|
||||
#define __RequestUnion__exc_subsystem__defined
|
||||
union __RequestUnion__exc_subsystem {
|
||||
__Request__exception_raise_t Request_exception_raise;
|
||||
__Request__exception_raise_state_t Request_exception_raise_state;
|
||||
__Request__exception_raise_state_identity_t Request_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__exc_subsystem__defined */
|
||||
/* typedefs for all replies */
|
||||
|
||||
#ifndef __Reply__exc_subsystem__defined
|
||||
#define __Reply__exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply__exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Reply__exc_subsystem__defined */
|
||||
|
||||
/* union of all replies */
|
||||
|
||||
#ifndef __ReplyUnion__exc_subsystem__defined
|
||||
#define __ReplyUnion__exc_subsystem__defined
|
||||
union __ReplyUnion__exc_subsystem {
|
||||
__Reply__exception_raise_t Reply_exception_raise;
|
||||
__Reply__exception_raise_state_t Reply_exception_raise_state;
|
||||
__Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__exc_subsystem__defined */
|
||||
|
||||
#ifndef subsystem_to_name_map_exc
|
||||
#define subsystem_to_name_map_exc \
|
||||
{ "exception_raise", 2401 },\
|
||||
{ "exception_raise_state", 2402 },\
|
||||
{ "exception_raise_state_identity", 2403 }
|
||||
#endif
|
||||
|
||||
#ifdef __AfterMigUserHeader
|
||||
__AfterMigUserHeader
|
||||
#endif /* __AfterMigUserHeader */
|
||||
|
||||
#endif /* _exc_user_ */
|
770
vendor/github.com/derekparker/delve/pkg/proc/native/exc_user_darwin.c
generated
vendored
770
vendor/github.com/derekparker/delve/pkg/proc/native/exc_user_darwin.c
generated
vendored
@ -1,770 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
/*
|
||||
* IDENTIFICATION:
|
||||
* stub generated Sun Feb 22 20:54:31 2015
|
||||
* with a MiG generated by bootstrap_cmds-91
|
||||
* OPTIONS:
|
||||
*/
|
||||
#define __MIG_check__Reply__exc_subsystem__ 1
|
||||
|
||||
#include "exc.h"
|
||||
|
||||
|
||||
#ifndef mig_internal
|
||||
#define mig_internal static __inline__
|
||||
#endif /* mig_internal */
|
||||
|
||||
#ifndef mig_external
|
||||
#define mig_external
|
||||
#endif /* mig_external */
|
||||
|
||||
#if !defined(__MigTypeCheck) && defined(TypeCheck)
|
||||
#define __MigTypeCheck TypeCheck /* Legacy setting */
|
||||
#endif /* !defined(__MigTypeCheck) */
|
||||
|
||||
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
|
||||
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
|
||||
#endif /* !defined(__MigKernelSpecificCode) */
|
||||
|
||||
#ifndef LimitCheck
|
||||
#define LimitCheck 0
|
||||
#endif /* LimitCheck */
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ( ((a) < (b))? (a): (b) )
|
||||
#endif /* min */
|
||||
|
||||
#if !defined(_WALIGN_)
|
||||
#define _WALIGN_(x) (((x) + 3) & ~3)
|
||||
#endif /* !defined(_WALIGN_) */
|
||||
|
||||
#if !defined(_WALIGNSZ_)
|
||||
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
|
||||
#endif /* !defined(_WALIGNSZ_) */
|
||||
|
||||
#ifndef UseStaticTemplates
|
||||
#define UseStaticTemplates 0
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#ifndef __MachMsgErrorWithTimeout
|
||||
#define __MachMsgErrorWithTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
case MACH_SEND_TIMED_OUT: \
|
||||
case MACH_RCV_TIMED_OUT: \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithTimeout */
|
||||
|
||||
#ifndef __MachMsgErrorWithoutTimeout
|
||||
#define __MachMsgErrorWithoutTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithoutTimeout */
|
||||
|
||||
#ifndef __DeclareSendRpc
|
||||
#define __DeclareSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendRpc */
|
||||
|
||||
#ifndef __BeforeSendRpc
|
||||
#define __BeforeSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendRpc */
|
||||
|
||||
#ifndef __AfterSendRpc
|
||||
#define __AfterSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendRpc */
|
||||
|
||||
#ifndef __DeclareSendSimple
|
||||
#define __DeclareSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendSimple */
|
||||
|
||||
#ifndef __BeforeSendSimple
|
||||
#define __BeforeSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendSimple */
|
||||
|
||||
#ifndef __AfterSendSimple
|
||||
#define __AfterSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendSimple */
|
||||
|
||||
#define msgh_request_port msgh_remote_port
|
||||
#define msgh_reply_port msgh_local_port
|
||||
|
||||
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_t(__Reply__exception_raise_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_t __Reply;
|
||||
if (Out0P->Head.msgh_id != 2501) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
(Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
{
|
||||
return Out0P->RetCode;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise */
|
||||
mig_external kern_return_t exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_t__defined */
|
||||
|
||||
__DeclareSendRpc(2401, "exception_raise")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 8) + ((4 * codeCnt));
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2401;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2401, "exception_raise")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2401, "exception_raise")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_t((__Reply__exception_raise_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_t__defined) */
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_state_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_state_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_t(__Reply__exception_raise_state_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_state_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2502) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_state_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise_state */
|
||||
mig_external kern_return_t exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_state_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_state_t__defined */
|
||||
|
||||
__DeclareSendRpc(2402, "exception_raise_state")
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (4 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits =
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2402;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2402, "exception_raise_state")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2402, "exception_raise_state")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_state_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_state_t((__Reply__exception_raise_state_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_state_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_state_identity_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_identity_t(__Reply__exception_raise_state_identity_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_state_identity_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2503) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise_state_identity */
|
||||
mig_external kern_return_t exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_state_identity_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_state_identity_t__defined */
|
||||
|
||||
__DeclareSendRpc(2403, "exception_raise_state_identity")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (4 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2403;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2403, "exception_raise_state_identity")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2403, "exception_raise_state_identity")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_state_identity_t((__Reply__exception_raise_state_identity_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
114
vendor/github.com/derekparker/delve/pkg/proc/native/exec_darwin.c
generated
vendored
114
vendor/github.com/derekparker/delve/pkg/proc/native/exec_darwin.c
generated
vendored
@ -1,114 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "exec_darwin.h"
|
||||
#include "stdio.h"
|
||||
|
||||
extern char** environ;
|
||||
|
||||
int
|
||||
close_exec_pipe(int fd[2]) {
|
||||
if (pipe(fd) < 0) return -1;
|
||||
if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1;
|
||||
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fork_exec(char *argv0, char **argv, int size,
|
||||
char *wd,
|
||||
task_t *task,
|
||||
mach_port_t *port_set,
|
||||
mach_port_t *exception_port,
|
||||
mach_port_t *notification_port)
|
||||
{
|
||||
// Since we're using mach exceptions instead of signals,
|
||||
// we need to coordinate between parent and child via pipes
|
||||
// to ensure that the parent has set the exception ports on
|
||||
// the child task before it execs.
|
||||
int fd[2];
|
||||
if (close_exec_pipe(fd) < 0) return -1;
|
||||
|
||||
// Create another pipe to signal the parent on exec.
|
||||
int efd[2];
|
||||
if (close_exec_pipe(efd) < 0) return -1;
|
||||
|
||||
kern_return_t kret;
|
||||
pid_t pid = fork();
|
||||
if (pid > 0) {
|
||||
// In parent.
|
||||
close(fd[0]);
|
||||
close(efd[1]);
|
||||
kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
char msg = 'c';
|
||||
write(fd[1], &msg, 1);
|
||||
close(fd[1]);
|
||||
|
||||
char w;
|
||||
size_t n = read(efd[0], &w, 1);
|
||||
close(efd[0]);
|
||||
if (n != 0) {
|
||||
// Child died, reap it.
|
||||
waitpid(pid, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
// Fork succeeded, we are in the child.
|
||||
int pret, cret;
|
||||
char sig;
|
||||
|
||||
close(fd[1]);
|
||||
read(fd[0], &sig, 1);
|
||||
close(fd[0]);
|
||||
|
||||
// Create a new process group.
|
||||
if (setpgid(0, 0) < 0) {
|
||||
perror("setpgid");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set errno to zero before a call to ptrace.
|
||||
// It is documented that ptrace can return -1 even
|
||||
// for successful calls.
|
||||
errno = 0;
|
||||
pret = ptrace(PT_TRACE_ME, 0, 0, 0);
|
||||
if (pret != 0 && errno != 0) {
|
||||
perror("ptrace");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Change working directory if wd is not empty.
|
||||
if (wd && wd[0]) {
|
||||
errno = 0;
|
||||
cret = chdir(wd);
|
||||
if (cret != 0 && errno != 0) {
|
||||
char *error_msg;
|
||||
asprintf(&error_msg, "%s '%s'", "chdir", wd);
|
||||
perror(error_msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
pret = ptrace(PT_SIGEXC, 0, 0, 0);
|
||||
if (pret != 0 && errno != 0) {
|
||||
perror("ptrace");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
// Create the child process.
|
||||
execve(argv0, argv, environ);
|
||||
|
||||
// We should never reach here, but if we did something went wrong.
|
||||
// Write a message to parent to alert that exec failed.
|
||||
char msg = 'd';
|
||||
write(efd[1], &msg, 1);
|
||||
close(efd[1]);
|
||||
|
||||
exit(1);
|
||||
}
|
12
vendor/github.com/derekparker/delve/pkg/proc/native/exec_darwin.h
generated
vendored
12
vendor/github.com/derekparker/delve/pkg/proc/native/exec_darwin.h
generated
vendored
@ -1,12 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "proc_darwin.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int
|
||||
fork_exec(char *, char **, int, char *, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
|
119
vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc.defs
generated
vendored
119
vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc.defs
generated
vendored
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. The rights granted to you under the License
|
||||
* may not be used to create, or enable the creation or redistribution of,
|
||||
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||
* circumvent, violate, or enable the circumvention or violation of, any
|
||||
* terms of an Apple operating system software license agreement.
|
||||
*
|
||||
* Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/*
|
||||
* @OSF_COPYRIGHT@
|
||||
*/
|
||||
/*
|
||||
* Mach Operating System
|
||||
* Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission to use, copy, modify and distribute this software and its
|
||||
* documentation is hereby granted, provided that both the copyright
|
||||
* notice and this permission notice appear in all copies of the
|
||||
* software, derivative works or modified versions, and any portions
|
||||
* thereof, and that both notices appear in supporting documentation.
|
||||
*
|
||||
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
|
||||
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
|
||||
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
||||
*
|
||||
* Carnegie Mellon requests users of this software to return to
|
||||
*
|
||||
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
|
||||
* School of Computer Science
|
||||
* Carnegie Mellon University
|
||||
* Pittsburgh PA 15213-3890
|
||||
*
|
||||
* any improvements or extensions that they make and grant Carnegie Mellon
|
||||
* the rights to redistribute these changes.
|
||||
*/
|
||||
/*
|
||||
*/
|
||||
/*
|
||||
* Abstract:
|
||||
* MiG definitions file for Mach exception interface.
|
||||
*/
|
||||
|
||||
subsystem
|
||||
#if KERNEL_USER
|
||||
KernelUser
|
||||
#endif
|
||||
mach_exc 2405;
|
||||
|
||||
#include <mach/std_types.defs>
|
||||
#include <mach/mach_types.defs>
|
||||
|
||||
ServerPrefix catch_;
|
||||
|
||||
type mach_exception_data_t = array[*:2] of int64_t;
|
||||
type exception_type_t = int;
|
||||
|
||||
routine mach_exception_raise(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
thread : mach_port_move_send_t;
|
||||
task : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
thread : mach_port_t;
|
||||
task : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t
|
||||
);
|
||||
|
||||
routine mach_exception_raise_state(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t, const;
|
||||
inout flavor : int;
|
||||
old_state : thread_state_t, const;
|
||||
out new_state : thread_state_t);
|
||||
|
||||
routine mach_exception_raise_state_identity(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
thread : mach_port_move_send_t;
|
||||
task : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
thread : mach_port_t;
|
||||
task : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t;
|
||||
inout flavor : int;
|
||||
old_state : thread_state_t;
|
||||
out new_state : thread_state_t);
|
||||
|
||||
/* vim: set ft=c : */
|
283
vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc.h
generated
vendored
283
vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc.h
generated
vendored
@ -1,283 +0,0 @@
|
||||
#ifndef _mach_exc_user_
|
||||
#define _mach_exc_user_
|
||||
|
||||
/* Module mach_exc */
|
||||
|
||||
#include <string.h>
|
||||
#include <mach/ndr.h>
|
||||
#include <mach/boolean.h>
|
||||
#include <mach/kern_return.h>
|
||||
#include <mach/notify.h>
|
||||
#include <mach/mach_types.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/mig_errors.h>
|
||||
#include <mach/port.h>
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifndef KERNEL
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<mach/mig_voucher_support.h>)
|
||||
#ifndef USING_VOUCHERS
|
||||
#define USING_VOUCHERS
|
||||
#endif
|
||||
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#define __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#endif // __has_include(<mach/mach_voucher_types.h>)
|
||||
#endif // __has_include
|
||||
#endif // !KERNEL
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
|
||||
#ifdef AUTOTEST
|
||||
#ifndef FUNCTION_PTR_T
|
||||
#define FUNCTION_PTR_T
|
||||
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
|
||||
typedef struct {
|
||||
char *name;
|
||||
function_ptr_t function;
|
||||
} function_table_entry;
|
||||
typedef function_table_entry *function_table_t;
|
||||
#endif /* FUNCTION_PTR_T */
|
||||
#endif /* AUTOTEST */
|
||||
|
||||
#ifndef mach_exc_MSG_COUNT
|
||||
#define mach_exc_MSG_COUNT 3
|
||||
#endif /* mach_exc_MSG_COUNT */
|
||||
|
||||
#include <mach/std_types.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#ifdef __BeforeMigUserHeader
|
||||
__BeforeMigUserHeader
|
||||
#endif /* __BeforeMigUserHeader */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
|
||||
/* Routine mach_exception_raise */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
);
|
||||
|
||||
/* Routine mach_exception_raise_state */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
/* Routine mach_exception_raise_state_identity */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
/********************** Caution **************************/
|
||||
/* The following data types should be used to calculate */
|
||||
/* maximum message sizes only. The actual message may be */
|
||||
/* smaller, and the position of the arguments within the */
|
||||
/* message layout may vary from what is presented here. */
|
||||
/* For example, if any of the arguments are variable- */
|
||||
/* sized, and less than the maximum is sent, the data */
|
||||
/* will be packed tight in the actual message to reduce */
|
||||
/* the presence of holes. */
|
||||
/********************** Caution **************************/
|
||||
|
||||
/* typedefs for all requests */
|
||||
|
||||
#ifndef __Request__mach_exc_subsystem__defined
|
||||
#define __Request__mach_exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
} __Request__mach_exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__mach_exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__mach_exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Request__mach_exc_subsystem__defined */
|
||||
|
||||
/* union of all requests */
|
||||
|
||||
#ifndef __RequestUnion__mach_exc_subsystem__defined
|
||||
#define __RequestUnion__mach_exc_subsystem__defined
|
||||
union __RequestUnion__mach_exc_subsystem {
|
||||
__Request__mach_exception_raise_t Request_mach_exception_raise;
|
||||
__Request__mach_exception_raise_state_t Request_mach_exception_raise_state;
|
||||
__Request__mach_exception_raise_state_identity_t Request_mach_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
|
||||
/* typedefs for all replies */
|
||||
|
||||
#ifndef __Reply__mach_exc_subsystem__defined
|
||||
#define __Reply__mach_exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply__mach_exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__mach_exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__mach_exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Reply__mach_exc_subsystem__defined */
|
||||
|
||||
/* union of all replies */
|
||||
|
||||
#ifndef __ReplyUnion__mach_exc_subsystem__defined
|
||||
#define __ReplyUnion__mach_exc_subsystem__defined
|
||||
union __ReplyUnion__mach_exc_subsystem {
|
||||
__Reply__mach_exception_raise_t Reply_mach_exception_raise;
|
||||
__Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state;
|
||||
__Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
|
||||
|
||||
#ifndef subsystem_to_name_map_mach_exc
|
||||
#define subsystem_to_name_map_mach_exc \
|
||||
{ "mach_exception_raise", 2401 },\
|
||||
{ "mach_exception_raise_state", 2402 },\
|
||||
{ "mach_exception_raise_state_identity", 2403 }
|
||||
#endif
|
||||
|
||||
#ifdef __AfterMigUserHeader
|
||||
__AfterMigUserHeader
|
||||
#endif /* __AfterMigUserHeader */
|
||||
|
||||
#endif /* _mach_exc_user_ */
|
770
vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc_user_darwin.c
generated
vendored
770
vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc_user_darwin.c
generated
vendored
@ -1,770 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
/*
|
||||
* IDENTIFICATION:
|
||||
* stub generated Sat Feb 21 18:10:52 2015
|
||||
* with a MiG generated by bootstrap_cmds-91
|
||||
* OPTIONS:
|
||||
*/
|
||||
#define __MIG_check__Reply__mach_exc_subsystem__ 1
|
||||
|
||||
#include "mach_exc.h"
|
||||
|
||||
|
||||
#ifndef mig_internal
|
||||
#define mig_internal static __inline__
|
||||
#endif /* mig_internal */
|
||||
|
||||
#ifndef mig_external
|
||||
#define mig_external
|
||||
#endif /* mig_external */
|
||||
|
||||
#if !defined(__MigTypeCheck) && defined(TypeCheck)
|
||||
#define __MigTypeCheck TypeCheck /* Legacy setting */
|
||||
#endif /* !defined(__MigTypeCheck) */
|
||||
|
||||
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
|
||||
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
|
||||
#endif /* !defined(__MigKernelSpecificCode) */
|
||||
|
||||
#ifndef LimitCheck
|
||||
#define LimitCheck 0
|
||||
#endif /* LimitCheck */
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ( ((a) < (b))? (a): (b) )
|
||||
#endif /* min */
|
||||
|
||||
#if !defined(_WALIGN_)
|
||||
#define _WALIGN_(x) (((x) + 3) & ~3)
|
||||
#endif /* !defined(_WALIGN_) */
|
||||
|
||||
#if !defined(_WALIGNSZ_)
|
||||
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
|
||||
#endif /* !defined(_WALIGNSZ_) */
|
||||
|
||||
#ifndef UseStaticTemplates
|
||||
#define UseStaticTemplates 0
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#ifndef __MachMsgErrorWithTimeout
|
||||
#define __MachMsgErrorWithTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
case MACH_SEND_TIMED_OUT: \
|
||||
case MACH_RCV_TIMED_OUT: \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithTimeout */
|
||||
|
||||
#ifndef __MachMsgErrorWithoutTimeout
|
||||
#define __MachMsgErrorWithoutTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithoutTimeout */
|
||||
|
||||
#ifndef __DeclareSendRpc
|
||||
#define __DeclareSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendRpc */
|
||||
|
||||
#ifndef __BeforeSendRpc
|
||||
#define __BeforeSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendRpc */
|
||||
|
||||
#ifndef __AfterSendRpc
|
||||
#define __AfterSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendRpc */
|
||||
|
||||
#ifndef __DeclareSendSimple
|
||||
#define __DeclareSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendSimple */
|
||||
|
||||
#ifndef __BeforeSendSimple
|
||||
#define __BeforeSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendSimple */
|
||||
|
||||
#ifndef __AfterSendSimple
|
||||
#define __AfterSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendSimple */
|
||||
|
||||
#define msgh_request_port msgh_remote_port
|
||||
#define msgh_reply_port msgh_local_port
|
||||
|
||||
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_t(__Reply__mach_exception_raise_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_t __Reply;
|
||||
if (Out0P->Head.msgh_id != 2505) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
(Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
{
|
||||
return Out0P->RetCode;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise */
|
||||
mig_external kern_return_t mach_exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_t__defined */
|
||||
|
||||
__DeclareSendRpc(2405, "mach_exception_raise")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 16) + ((8 * codeCnt));
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2405;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2405, "mach_exception_raise")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2405, "mach_exception_raise")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_t((__Reply__mach_exception_raise_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_t__defined) */
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_state_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_state_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_state_t(__Reply__mach_exception_raise_state_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_state_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2506) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_state_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise_state */
|
||||
mig_external kern_return_t mach_exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_state_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_state_t__defined */
|
||||
|
||||
__DeclareSendRpc(2406, "mach_exception_raise_state")
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (8 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 912) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 16);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits =
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2406;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2406, "mach_exception_raise_state")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2406, "mach_exception_raise_state")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_state_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_state_t((__Reply__mach_exception_raise_state_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_state_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_state_identity_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_state_identity_t(__Reply__mach_exception_raise_state_identity_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_state_identity_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2507) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise_state_identity */
|
||||
mig_external kern_return_t mach_exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_state_identity_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_state_identity_t__defined */
|
||||
|
||||
__DeclareSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (8 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 912) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 16);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2407;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_state_identity_t((__Reply__mach_exception_raise_state_identity_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
128
vendor/github.com/derekparker/delve/pkg/proc/native/nonative_darwin.go
generated
vendored
128
vendor/github.com/derekparker/delve/pkg/proc/native/nonative_darwin.go
generated
vendored
@ -1,128 +0,0 @@
|
||||
//+build darwin,!macnative
|
||||
|
||||
package native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
|
||||
|
||||
// Launch returns ErrNativeBackendDisabled.
|
||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
return nil, ErrNativeBackendDisabled
|
||||
}
|
||||
|
||||
// Attach returns ErrNativeBackendDisabled.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
return nil, ErrNativeBackendDisabled
|
||||
}
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
type WaitStatus struct{}
|
||||
|
||||
// OSSpecificDetails holds information specific to the OSX/Darwin
|
||||
// operating system / kernel.
|
||||
type OSSpecificDetails struct{}
|
||||
|
||||
// OSProcessDetails holds Darwin specific information.
|
||||
type OSProcessDetails struct{}
|
||||
|
||||
func findExecutable(path string, pid int) string {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() (err error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) kill() (err error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) detach(kill bool) error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (dbp *Process) entryPoint() (uint64, error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
// Blocked returns true if the thread is blocked
|
||||
func (t *Thread) Blocked() bool {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
// SetPC sets the value of the PC register.
|
||||
func (thread *Thread) SetPC(pc uint64) error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
// SetSP sets the value of the SP register.
|
||||
func (thread *Thread) SetSP(sp uint64) error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
// SetDX sets the value of the DX register.
|
||||
func (thread *Thread) SetDX(dx uint64) error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
// ReadMemory reads len(buf) bytes at addr into buf.
|
||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
// WriteMemory writes the contents of data at addr.
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters(sr proc.Registers) error {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at
|
||||
// the operating system level.
|
||||
func (t *Thread) Stopped() bool {
|
||||
panic(ErrNativeBackendDisabled)
|
||||
}
|
453
vendor/github.com/derekparker/delve/pkg/proc/native/proc.go
generated
vendored
453
vendor/github.com/derekparker/delve/pkg/proc/native/proc.go
generated
vendored
@ -1,453 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Process represents all of the information the debugger
|
||||
// is holding onto regarding the process we are debugging.
|
||||
type Process struct {
|
||||
bi *proc.BinaryInfo
|
||||
pid int // Process Pid
|
||||
|
||||
// Breakpoint table, holds information on breakpoints.
|
||||
// Maps instruction address to Breakpoint struct.
|
||||
breakpoints proc.BreakpointMap
|
||||
|
||||
// List of threads mapped as such: pid -> *Thread
|
||||
threads map[int]*Thread
|
||||
|
||||
// Active thread
|
||||
currentThread *Thread
|
||||
|
||||
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
|
||||
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
||||
selectedGoroutine *proc.G
|
||||
|
||||
common proc.CommonProcess
|
||||
os *OSProcessDetails
|
||||
firstStart bool
|
||||
stopMu sync.Mutex
|
||||
resumeChan chan<- struct{}
|
||||
ptraceChan chan func()
|
||||
ptraceDoneChan chan interface{}
|
||||
childProcess bool // this process was launched, not attached to
|
||||
manualStopRequested bool
|
||||
|
||||
exited, detached bool
|
||||
}
|
||||
|
||||
// New returns an initialized Process struct. Before returning,
|
||||
// it will also launch a goroutine in order to handle ptrace(2)
|
||||
// functions. For more information, see the documentation on
|
||||
// `handlePtraceFuncs`.
|
||||
func New(pid int) *Process {
|
||||
dbp := &Process{
|
||||
pid: pid,
|
||||
threads: make(map[int]*Thread),
|
||||
breakpoints: proc.NewBreakpointMap(),
|
||||
firstStart: true,
|
||||
os: new(OSProcessDetails),
|
||||
ptraceChan: make(chan func()),
|
||||
ptraceDoneChan: make(chan interface{}),
|
||||
bi: proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
go dbp.handlePtraceFuncs()
|
||||
return dbp
|
||||
}
|
||||
|
||||
// BinInfo will return the binary info struct associated with this process.
|
||||
func (dbp *Process) BinInfo() *proc.BinaryInfo {
|
||||
return dbp.bi
|
||||
}
|
||||
|
||||
// Recorded always returns false for the native proc backend.
|
||||
func (dbp *Process) Recorded() (bool, string) { return false, "" }
|
||||
|
||||
// Restart will always return an error in the native proc backend, only for
|
||||
// recorded traces.
|
||||
func (dbp *Process) Restart(string) error { return proc.ErrNotRecorded }
|
||||
|
||||
// Direction will always return an error in the native proc backend, only for
|
||||
// recorded traces.
|
||||
func (dbp *Process) Direction(proc.Direction) error { return proc.ErrNotRecorded }
|
||||
|
||||
// When will always return an empty string and nil, not supported on native proc backend.
|
||||
func (dbp *Process) When() (string, error) { return "", nil }
|
||||
|
||||
// Checkpoint will always return an error on the native proc backend,
|
||||
// only supported for recorded traces.
|
||||
func (dbp *Process) Checkpoint(string) (int, error) { return -1, proc.ErrNotRecorded }
|
||||
|
||||
// Checkpoints will always return an error on the native proc backend,
|
||||
// only supported for recorded traces.
|
||||
func (dbp *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, proc.ErrNotRecorded }
|
||||
|
||||
// ClearCheckpoint will always return an error on the native proc backend,
|
||||
// only supported in recorded traces.
|
||||
func (dbp *Process) ClearCheckpoint(int) error { return proc.ErrNotRecorded }
|
||||
|
||||
// Detach from the process being debugged, optionally killing it.
|
||||
func (dbp *Process) Detach(kill bool) (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if kill && dbp.childProcess {
|
||||
err := dbp.kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbp.bi.Close()
|
||||
return nil
|
||||
}
|
||||
if !kill {
|
||||
// Clean up any breakpoints we've set.
|
||||
for _, bp := range dbp.breakpoints.M {
|
||||
if bp != nil {
|
||||
_, err := dbp.ClearBreakpoint(bp.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = dbp.detach(kill)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if kill {
|
||||
err = killProcess(dbp.pid)
|
||||
}
|
||||
})
|
||||
dbp.detached = true
|
||||
dbp.postExit()
|
||||
return
|
||||
}
|
||||
|
||||
// Valid returns whether the process is still attached to and
|
||||
// has not exited.
|
||||
func (dbp *Process) Valid() (bool, error) {
|
||||
if dbp.detached {
|
||||
return false, &proc.ProcessDetachedError{}
|
||||
}
|
||||
if dbp.exited {
|
||||
return false, &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ResumeNotify specifies a channel that will be closed the next time
|
||||
// ContinueOnce finishes resuming the target.
|
||||
func (dbp *Process) ResumeNotify(ch chan<- struct{}) {
|
||||
dbp.resumeChan = ch
|
||||
}
|
||||
|
||||
// Pid returns the process ID.
|
||||
func (dbp *Process) Pid() int {
|
||||
return dbp.pid
|
||||
}
|
||||
|
||||
// SelectedGoroutine returns the current selected,
|
||||
// active goroutine.
|
||||
func (dbp *Process) SelectedGoroutine() *proc.G {
|
||||
return dbp.selectedGoroutine
|
||||
}
|
||||
|
||||
// ThreadList returns a list of threads in the process.
|
||||
func (dbp *Process) ThreadList() []proc.Thread {
|
||||
r := make([]proc.Thread, 0, len(dbp.threads))
|
||||
for _, v := range dbp.threads {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// FindThread attempts to find the thread with the specified ID.
|
||||
func (dbp *Process) FindThread(threadID int) (proc.Thread, bool) {
|
||||
th, ok := dbp.threads[threadID]
|
||||
return th, ok
|
||||
}
|
||||
|
||||
// CurrentThread returns the current selected, active thread.
|
||||
func (dbp *Process) CurrentThread() proc.Thread {
|
||||
return dbp.currentThread
|
||||
}
|
||||
|
||||
// Breakpoints returns a list of breakpoints currently set.
|
||||
func (dbp *Process) Breakpoints() *proc.BreakpointMap {
|
||||
return &dbp.breakpoints
|
||||
}
|
||||
|
||||
// LoadInformation finds the executable and then uses it
|
||||
// to parse the following information:
|
||||
// * Dwarf .debug_frame section
|
||||
// * Dwarf .debug_line section
|
||||
// * Go symbol table.
|
||||
func (dbp *Process) LoadInformation(path string) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
path = findExecutable(path, dbp.pid)
|
||||
|
||||
entryPoint, err := dbp.entryPoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go dbp.loadProcessInformation(&wg)
|
||||
err = dbp.bi.LoadBinaryInfo(path, entryPoint, &wg)
|
||||
wg.Wait()
|
||||
if err == nil {
|
||||
err = dbp.bi.LoadError()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// RequestManualStop sets the `halt` flag and
|
||||
// sends SIGSTOP to all threads.
|
||||
func (dbp *Process) RequestManualStop() error {
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
dbp.stopMu.Lock()
|
||||
defer dbp.stopMu.Unlock()
|
||||
dbp.manualStopRequested = true
|
||||
return dbp.requestManualStop()
|
||||
}
|
||||
|
||||
// CheckAndClearManualStopRequest checks if a manual stop has
|
||||
// been requested, and then clears that state.
|
||||
func (dbp *Process) CheckAndClearManualStopRequest() bool {
|
||||
dbp.stopMu.Lock()
|
||||
defer dbp.stopMu.Unlock()
|
||||
|
||||
msr := dbp.manualStopRequested
|
||||
dbp.manualStopRequested = false
|
||||
|
||||
return msr
|
||||
}
|
||||
|
||||
func (dbp *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) {
|
||||
f, l, fn := dbp.bi.PCToLine(uint64(addr))
|
||||
if fn == nil {
|
||||
return "", 0, nil, nil, proc.InvalidAddressError{Address: addr}
|
||||
}
|
||||
|
||||
originalData := make([]byte, dbp.bi.Arch.BreakpointSize())
|
||||
_, err := dbp.currentThread.ReadMemory(originalData, uintptr(addr))
|
||||
if err != nil {
|
||||
return "", 0, nil, nil, err
|
||||
}
|
||||
if err := dbp.writeSoftwareBreakpoint(dbp.currentThread, addr); err != nil {
|
||||
return "", 0, nil, nil, err
|
||||
}
|
||||
|
||||
return f, l, fn, originalData, nil
|
||||
}
|
||||
|
||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
||||
// break point table.
|
||||
func (dbp *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
||||
return dbp.breakpoints.Set(addr, kind, cond, dbp.writeBreakpoint)
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the breakpoint at addr.
|
||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
|
||||
if dbp.exited {
|
||||
return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
return dbp.breakpoints.Clear(addr, dbp.currentThread.ClearBreakpoint)
|
||||
}
|
||||
|
||||
// ContinueOnce will continue the target until it stops.
|
||||
// This could be the result of a breakpoint or signal.
|
||||
func (dbp *Process) ContinueOnce() (proc.Thread, error) {
|
||||
if dbp.exited {
|
||||
return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.common.ClearAllGCache()
|
||||
for _, th := range dbp.threads {
|
||||
th.CurrentBreakpoint.Clear()
|
||||
}
|
||||
|
||||
if dbp.resumeChan != nil {
|
||||
close(dbp.resumeChan)
|
||||
dbp.resumeChan = nil
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.stop(trapthread); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return trapthread, err
|
||||
}
|
||||
|
||||
// StepInstruction will continue the current thread for exactly
|
||||
// one instruction. This method affects only the thread
|
||||
// associated with the selected goroutine. All other
|
||||
// threads will remain stopped.
|
||||
func (dbp *Process) StepInstruction() (err error) {
|
||||
thread := dbp.currentThread
|
||||
if dbp.selectedGoroutine != nil {
|
||||
if dbp.selectedGoroutine.Thread == nil {
|
||||
// Step called on parked goroutine
|
||||
if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
return proc.Continue(dbp)
|
||||
}
|
||||
thread = dbp.selectedGoroutine.Thread.(*Thread)
|
||||
}
|
||||
dbp.common.ClearAllGCache()
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
err = thread.StepInstruction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = thread.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g, _ := proc.GetG(thread); g != nil {
|
||||
dbp.selectedGoroutine = g
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwitchThread changes from current thread to the thread specified by `tid`.
|
||||
func (dbp *Process) SwitchThread(tid int) error {
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
if th, ok := dbp.threads[tid]; ok {
|
||||
dbp.currentThread = th
|
||||
dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// SwitchGoroutine changes from current thread to the thread
|
||||
// running the specified goroutine.
|
||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
g, err := proc.FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.Thread != nil {
|
||||
return dbp.SwitchThread(g.Thread.ThreadID())
|
||||
}
|
||||
dbp.selectedGoroutine = g
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindBreakpoint finds the breakpoint for the given pc.
|
||||
func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
|
||||
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
||||
if bp, ok := dbp.breakpoints.M[pc-uint64(dbp.bi.Arch.BreakpointSize())]; ok {
|
||||
return bp, true
|
||||
}
|
||||
// Directly use addr to lookup breakpoint.
|
||||
if bp, ok := dbp.breakpoints.M[pc]; ok {
|
||||
return bp, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Returns a new Process struct.
|
||||
func initializeDebugProcess(dbp *Process, path string) (*Process, error) {
|
||||
err := dbp.LoadInformation(path)
|
||||
if err != nil {
|
||||
return dbp, err
|
||||
}
|
||||
|
||||
if err := dbp.updateThreadList(); err != nil {
|
||||
return dbp, err
|
||||
}
|
||||
|
||||
// selectedGoroutine can not be set correctly by the call to updateThreadList
|
||||
// because without calling SetGStructOffset we can not read the G struct of currentThread
|
||||
// but without calling updateThreadList we can not examine memory to determine
|
||||
// the offset of g struct inside TLS
|
||||
dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread)
|
||||
|
||||
proc.CreateUnrecoveredPanicBreakpoint(dbp, dbp.writeBreakpoint, &dbp.breakpoints)
|
||||
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
// ClearInternalBreakpoints will clear all non-user set breakpoints. These
|
||||
// breakpoints are set for internal operations such as 'next'.
|
||||
func (dbp *Process) ClearInternalBreakpoints() error {
|
||||
return dbp.breakpoints.ClearInternalBreakpoints(func(bp *proc.Breakpoint) error {
|
||||
if err := dbp.currentThread.ClearBreakpoint(bp); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, thread := range dbp.threads {
|
||||
if thread.CurrentBreakpoint.Breakpoint == bp {
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (dbp *Process) handlePtraceFuncs() {
|
||||
// We must ensure here that we are running on the same thread during
|
||||
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
|
||||
// all commands after PTRACE_ATTACH to come from the same thread.
|
||||
runtime.LockOSThread()
|
||||
|
||||
for fn := range dbp.ptraceChan {
|
||||
fn()
|
||||
dbp.ptraceDoneChan <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) execPtraceFunc(fn func()) {
|
||||
dbp.ptraceChan <- fn
|
||||
<-dbp.ptraceDoneChan
|
||||
}
|
||||
|
||||
func (dbp *Process) postExit() {
|
||||
dbp.exited = true
|
||||
close(dbp.ptraceChan)
|
||||
close(dbp.ptraceDoneChan)
|
||||
dbp.bi.Close()
|
||||
}
|
||||
|
||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
||||
_, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction())
|
||||
return err
|
||||
}
|
||||
|
||||
// Common returns common information across Process
|
||||
// implementations
|
||||
func (dbp *Process) Common() *proc.CommonProcess {
|
||||
return &dbp.common
|
||||
}
|
233
vendor/github.com/derekparker/delve/pkg/proc/native/proc_darwin.c
generated
vendored
233
vendor/github.com/derekparker/delve/pkg/proc/native/proc_darwin.c
generated
vendored
@ -1,233 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "proc_darwin.h"
|
||||
|
||||
static const unsigned char info_plist[]
|
||||
__attribute__ ((section ("__TEXT,__info_plist"),used)) =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
|
||||
" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
||||
"<plist version=\"1.0\">\n"
|
||||
"<dict>\n"
|
||||
" <key>CFBundleIdentifier</key>\n"
|
||||
" <string>org.dlv</string>\n"
|
||||
" <key>CFBundleName</key>\n"
|
||||
" <string>delve</string>\n"
|
||||
" <key>CFBundleVersion</key>\n"
|
||||
" <string>1.0</string>\n"
|
||||
" <key>SecTaskAccess</key>\n"
|
||||
" <array>\n"
|
||||
" <string>allowed</string>\n"
|
||||
" <string>debug</string>\n"
|
||||
" </array>\n"
|
||||
"</dict>\n"
|
||||
"</plist>\n";
|
||||
|
||||
kern_return_t
|
||||
acquire_mach_task(int tid,
|
||||
task_t *task,
|
||||
mach_port_t *port_set,
|
||||
mach_port_t *exception_port,
|
||||
mach_port_t *notification_port)
|
||||
{
|
||||
kern_return_t kret;
|
||||
mach_port_t prev_not;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
kret = task_for_pid(self, tid, task);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Allocate exception port.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, exception_port);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_insert_right(self, *exception_port, *exception_port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = task_set_exception_ports(*task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
|
||||
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Allocate notification port to alert of when the process dies.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, notification_port);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_insert_right(self, *notification_port, *notification_port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_request_notification(self, *task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
|
||||
MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Create port set.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_PORT_SET, port_set);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Move exception and notification ports to port set.
|
||||
kret = mach_port_move_member(self, *exception_port, *port_set);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return mach_port_move_member(self, *notification_port, *port_set);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
reset_exception_ports(task_t task, mach_port_t *exception_port, mach_port_t *notification_port) {
|
||||
kern_return_t kret;
|
||||
mach_port_t prev_not;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
kret = task_set_exception_ports(task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
|
||||
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_request_notification(self, task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
|
||||
MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
char *
|
||||
find_executable(int pid) {
|
||||
static char pathbuf[PATH_MAX];
|
||||
proc_pidpath(pid, pathbuf, PATH_MAX);
|
||||
return pathbuf;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_threads(task_t task, void *slice, int limit) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) {
|
||||
return kret;
|
||||
}
|
||||
|
||||
if (count > limit) {
|
||||
vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
return -2;
|
||||
}
|
||||
|
||||
memcpy(slice, (void*)list, count*sizeof(list[0]));
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return (kern_return_t)0;
|
||||
}
|
||||
|
||||
int
|
||||
thread_count(task_t task) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
mach_port_t
|
||||
mach_port_wait(mach_port_t port_set, task_t *task, int nonblocking) {
|
||||
kern_return_t kret;
|
||||
thread_act_t thread;
|
||||
NDR_record_t *ndr;
|
||||
integer_t *data;
|
||||
union
|
||||
{
|
||||
mach_msg_header_t hdr;
|
||||
char data[256];
|
||||
} msg;
|
||||
mach_msg_option_t opts = MACH_RCV_MSG|MACH_RCV_INTERRUPT;
|
||||
if (nonblocking) {
|
||||
opts |= MACH_RCV_TIMEOUT;
|
||||
}
|
||||
|
||||
// Wait for mach msg.
|
||||
kret = mach_msg(&msg.hdr, opts,
|
||||
0, sizeof(msg.data), port_set, 10, MACH_PORT_NULL);
|
||||
if (kret == MACH_RCV_INTERRUPTED) return kret;
|
||||
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||
|
||||
|
||||
switch (msg.hdr.msgh_id) {
|
||||
case 2401: { // Exception
|
||||
// 2401 is the exception_raise event, defined in:
|
||||
// http://opensource.apple.com/source/xnu/xnu-2422.1.72/osfmk/mach/exc.defs?txt
|
||||
// compile this file with mig to get the C version of the description
|
||||
|
||||
mach_msg_body_t *bod = (mach_msg_body_t*)(&msg.hdr + 1);
|
||||
mach_msg_port_descriptor_t *desc = (mach_msg_port_descriptor_t *)(bod + 1);
|
||||
thread = desc[0].name;
|
||||
*task = desc[1].name;
|
||||
ndr = (NDR_record_t *)(desc + 2);
|
||||
data = (integer_t *)(ndr + 1);
|
||||
|
||||
if (thread_suspend(thread) != KERN_SUCCESS) return 0;
|
||||
// Send our reply back so the kernel knows this exception has been handled.
|
||||
kret = mach_send_reply(msg.hdr);
|
||||
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||
if (data[2] == EXC_SOFT_SIGNAL) {
|
||||
if (data[3] != SIGTRAP) {
|
||||
if (thread_resume(thread) != KERN_SUCCESS) return 0;
|
||||
return mach_port_wait(port_set, task, nonblocking);
|
||||
}
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
case 72: { // Death
|
||||
// 72 is mach_notify_dead_name, defined in:
|
||||
// https://opensource.apple.com/source/xnu/xnu-1228.7.58/osfmk/mach/notify.defs?txt
|
||||
// compile this file with mig to get the C version of the description
|
||||
ndr = (NDR_record_t *)(&msg.hdr + 1);
|
||||
*task = *((mach_port_name_t *)(ndr + 1));
|
||||
return msg.hdr.msgh_local_port;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t hdr) {
|
||||
mig_reply_error_t reply;
|
||||
mach_msg_header_t *rh = &reply.Head;
|
||||
rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(hdr.msgh_bits), 0);
|
||||
rh->msgh_remote_port = hdr.msgh_remote_port;
|
||||
rh->msgh_size = (mach_msg_size_t) sizeof(mig_reply_error_t);
|
||||
rh->msgh_local_port = MACH_PORT_NULL;
|
||||
rh->msgh_id = hdr.msgh_id + 100;
|
||||
|
||||
reply.NDR = NDR_record;
|
||||
reply.RetCode = KERN_SUCCESS;
|
||||
|
||||
return mach_msg(&reply.Head, MACH_SEND_MSG|MACH_SEND_INTERRUPT, rh->msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t task, mach_port_t thread, mach_port_t exception_port, exception_type_t exception) {
|
||||
return exception_raise(exception_port, thread, task, exception, 0, 0);
|
||||
}
|
||||
|
||||
task_t
|
||||
get_task_for_pid(int pid) {
|
||||
task_t task = 0;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
task_for_pid(self, pid, &task);
|
||||
return task;
|
||||
}
|
||||
|
||||
int
|
||||
task_is_valid(task_t task) {
|
||||
struct task_basic_info info;
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
return task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS;
|
||||
}
|
470
vendor/github.com/derekparker/delve/pkg/proc/native/proc_darwin.go
generated
vendored
470
vendor/github.com/derekparker/delve/pkg/proc/native/proc_darwin.go
generated
vendored
@ -1,470 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
// #include "proc_darwin.h"
|
||||
// #include "threads_darwin.h"
|
||||
// #include "exec_darwin.h"
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Darwin specific information.
|
||||
type OSProcessDetails struct {
|
||||
task C.task_t // mach task for the debugged process.
|
||||
exceptionPort C.mach_port_t // mach port for receiving mach exceptions.
|
||||
notificationPort C.mach_port_t // mach port for dead name notification (process exit).
|
||||
initialized bool
|
||||
halt bool
|
||||
|
||||
// the main port we use, will return messages from both the
|
||||
// exception and notification ports.
|
||||
portSet C.mach_port_t
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process. Uses a
|
||||
// custom fork/exec process in order to take advantage of
|
||||
// PT_SIGEXC on Darwin which will turn Unix signals into
|
||||
// Mach exceptions.
|
||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, proc.ErrNotExecutable
|
||||
}
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Make sure the binary exists.
|
||||
if filepath.Base(cmd[0]) == cmd[0] {
|
||||
if _, err := exec.LookPath(cmd[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(argv0Go); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
argv0 := C.CString(argv0Go)
|
||||
argvSlice := make([]*C.char, 0, len(cmd)+1)
|
||||
for _, arg := range cmd {
|
||||
argvSlice = append(argvSlice, C.CString(arg))
|
||||
}
|
||||
// argv array must be null terminated.
|
||||
argvSlice = append(argvSlice, nil)
|
||||
|
||||
dbp := New(0)
|
||||
var pid int
|
||||
dbp.execPtraceFunc(func() {
|
||||
ret := C.fork_exec(argv0, &argvSlice[0], C.int(len(argvSlice)),
|
||||
C.CString(wd),
|
||||
&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
|
||||
&dbp.os.notificationPort)
|
||||
pid = int(ret)
|
||||
})
|
||||
if pid <= 0 {
|
||||
return nil, fmt.Errorf("could not fork/exec")
|
||||
}
|
||||
dbp.pid = pid
|
||||
dbp.childProcess = true
|
||||
for i := range argvSlice {
|
||||
C.free(unsafe.Pointer(argvSlice[i]))
|
||||
}
|
||||
|
||||
// Initialize enough of the Process state so that we can use resume and
|
||||
// trapWait to wait until the child process calls execve.
|
||||
|
||||
for {
|
||||
task := C.get_task_for_pid(C.int(dbp.pid))
|
||||
// The task_for_pid call races with the fork call. This can
|
||||
// result in the parent task being returned instead of the child.
|
||||
if task != dbp.os.task {
|
||||
err = dbp.updateThreadListForTask(task)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err != couldNotGetThreadCount && err != couldNotGetThreadList {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.common.ClearAllGCache()
|
||||
for _, th := range dbp.threads {
|
||||
th.CurrentBreakpoint.Clear()
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.stop(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.os.initialized = true
|
||||
dbp, err = initializeDebugProcess(dbp, argv0Go)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dbp.SwitchThread(trapthread.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbp, err
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
dbp := New(pid)
|
||||
|
||||
kret := C.acquire_mach_task(C.int(pid),
|
||||
&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
|
||||
&dbp.os.notificationPort)
|
||||
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not attach to %d", pid)
|
||||
}
|
||||
|
||||
dbp.os.initialized = true
|
||||
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _, err = dbp.wait(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp, err = initializeDebugProcess(dbp, "")
|
||||
if err != nil {
|
||||
dbp.Detach(false)
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
// Kill kills the process.
|
||||
func (dbp *Process) kill() (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
err = sys.Kill(-dbp.pid, sys.SIGKILL)
|
||||
if err != nil {
|
||||
return errors.New("could not deliver signal: " + err.Error())
|
||||
}
|
||||
for port := range dbp.threads {
|
||||
if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
|
||||
return errors.New("could not resume task")
|
||||
}
|
||||
}
|
||||
for {
|
||||
var task C.task_t
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
|
||||
if port == dbp.os.notificationPort {
|
||||
break
|
||||
}
|
||||
}
|
||||
dbp.postExit()
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() (err error) {
|
||||
var (
|
||||
task = C.mach_port_t(dbp.os.task)
|
||||
thread = C.mach_port_t(dbp.currentThread.os.threadAct)
|
||||
exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
|
||||
)
|
||||
dbp.os.halt = true
|
||||
kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not raise mach exception")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var couldNotGetThreadCount = errors.New("could not get thread count")
|
||||
var couldNotGetThreadList = errors.New("could not get thread list")
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
return dbp.updateThreadListForTask(dbp.os.task)
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadListForTask(task C.task_t) error {
|
||||
var (
|
||||
err error
|
||||
kret C.kern_return_t
|
||||
count C.int
|
||||
list []uint32
|
||||
)
|
||||
|
||||
for {
|
||||
count = C.thread_count(task)
|
||||
if count == -1 {
|
||||
return couldNotGetThreadCount
|
||||
}
|
||||
list = make([]uint32, count)
|
||||
|
||||
// TODO(dp) might be better to malloc mem in C and then free it here
|
||||
// instead of getting count above and passing in a slice
|
||||
kret = C.get_threads(task, unsafe.Pointer(&list[0]), count)
|
||||
if kret != -2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return couldNotGetThreadList
|
||||
}
|
||||
|
||||
for _, thread := range dbp.threads {
|
||||
thread.os.exists = false
|
||||
}
|
||||
|
||||
for _, port := range list {
|
||||
thread, ok := dbp.threads[int(port)]
|
||||
if !ok {
|
||||
thread, err = dbp.addThread(int(port), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
thread.os.exists = true
|
||||
}
|
||||
|
||||
for threadID, thread := range dbp.threads {
|
||||
if !thread.os.exists {
|
||||
delete(dbp.threads, threadID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
|
||||
if thread, ok := dbp.threads[port]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
thread := &Thread{
|
||||
ID: port,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
dbp.threads[port] = thread
|
||||
thread.os.threadAct = C.thread_act_t(port)
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(thread.ID)
|
||||
}
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func findExecutable(path string, pid int) string {
|
||||
if path == "" {
|
||||
path = C.GoString(C.find_executable(C.int(pid)))
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
for {
|
||||
task := dbp.os.task
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
|
||||
|
||||
switch port {
|
||||
case dbp.os.notificationPort:
|
||||
// on macOS >= 10.12.1 the task_t changes after an execve, we could
|
||||
// receive the notification for the death of the pre-execve task_t,
|
||||
// this could also happen *before* we are notified that our task_t has
|
||||
// changed.
|
||||
if dbp.os.task != task {
|
||||
continue
|
||||
}
|
||||
if !dbp.os.initialized {
|
||||
if pidtask := C.get_task_for_pid(C.int(dbp.pid)); pidtask != 0 && dbp.os.task != pidtask {
|
||||
continue
|
||||
}
|
||||
}
|
||||
_, status, err := dbp.wait(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp.postExit()
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
|
||||
case C.MACH_RCV_INTERRUPTED:
|
||||
dbp.stopMu.Lock()
|
||||
halt := dbp.os.halt
|
||||
dbp.stopMu.Unlock()
|
||||
if !halt {
|
||||
// Call trapWait again, it seems
|
||||
// MACH_RCV_INTERRUPTED is emitted before
|
||||
// process natural death _sometimes_.
|
||||
continue
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
case 0:
|
||||
return nil, fmt.Errorf("error while waiting for task")
|
||||
}
|
||||
|
||||
// In macOS 10.12.1 if we received a notification for a task other than
|
||||
// the inferior's task and the inferior's task is no longer valid, this
|
||||
// means inferior called execve and its task_t changed.
|
||||
if dbp.os.task != task && C.task_is_valid(dbp.os.task) == 0 {
|
||||
dbp.os.task = task
|
||||
kret := C.reset_exception_ports(dbp.os.task, &dbp.os.exceptionPort, &dbp.os.notificationPort)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not follow task across exec: %d\n", kret)
|
||||
}
|
||||
}
|
||||
|
||||
// Since we cannot be notified of new threads on OS X
|
||||
// this is as good a time as any to check for them.
|
||||
dbp.updateThreadList()
|
||||
th, ok := dbp.threads[int(port)]
|
||||
if !ok {
|
||||
dbp.stopMu.Lock()
|
||||
halt := dbp.os.halt
|
||||
dbp.stopMu.Unlock()
|
||||
if halt {
|
||||
dbp.os.halt = false
|
||||
return th, nil
|
||||
}
|
||||
if dbp.firstStart || th.singleStepping {
|
||||
dbp.firstStart = false
|
||||
return th, nil
|
||||
}
|
||||
if err := th.Continue(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return th, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) waitForStop() ([]int, error) {
|
||||
ports := make([]int, 0, len(dbp.threads))
|
||||
count := 0
|
||||
for {
|
||||
var task C.task_t
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(1))
|
||||
if port != 0 && port != dbp.os.notificationPort && port != C.MACH_RCV_INTERRUPTED {
|
||||
count = 0
|
||||
ports = append(ports, int(port))
|
||||
} else {
|
||||
n := C.num_running_threads(dbp.os.task)
|
||||
if n == 0 {
|
||||
return ports, nil
|
||||
} else if n < 0 {
|
||||
return nil, fmt.Errorf("error waiting for thread stop %d", n)
|
||||
} else if count > 16 {
|
||||
return nil, fmt.Errorf("could not stop process %d", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
var status sys.WaitStatus
|
||||
wpid, err := sys.Wait4(pid, &status, options, nil)
|
||||
return wpid, &status, err
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGINT)
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
if err != ErrContinueThread {
|
||||
return err
|
||||
}
|
||||
_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
|
||||
if werr == nil && status.Exited() {
|
||||
dbp.postExit()
|
||||
return proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
// all threads stopped over a breakpoint are made to step over it
|
||||
for _, thread := range dbp.threads {
|
||||
if thread.CurrentBreakpoint.Breakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
for _, thread := range dbp.threads {
|
||||
if err := thread.resume(); err != nil {
|
||||
return dbp.exitGuard(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// stop stops all running threads and sets breakpoints
|
||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if !th.Stopped() {
|
||||
if err := th.stop(); err != nil {
|
||||
return dbp.exitGuard(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ports, err := dbp.waitForStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !dbp.os.initialized {
|
||||
return nil
|
||||
}
|
||||
trapthread.SetCurrentBreakpoint()
|
||||
for _, port := range ports {
|
||||
if th, ok := dbp.threads[port]; ok {
|
||||
err := th.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) detach(kill bool) error {
|
||||
return PtraceDetach(dbp.pid, 0)
|
||||
}
|
||||
|
||||
func (dbp *Process) entryPoint() (uint64, error) {
|
||||
//TODO(aarzilli): implement this
|
||||
return 0, nil
|
||||
}
|
56
vendor/github.com/derekparker/delve/pkg/proc/native/proc_darwin.h
generated
vendored
56
vendor/github.com/derekparker/delve/pkg/proc/native/proc_darwin.h
generated
vendored
@ -1,56 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <libproc.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include "mach_exc.h"
|
||||
#include "exc.h"
|
||||
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
boolean_t exc_server(
|
||||
mach_msg_header_t *InHeadP,
|
||||
mach_msg_header_t *OutHeadP);
|
||||
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
boolean_t mach_exc_server(
|
||||
mach_msg_header_t *InHeadP,
|
||||
mach_msg_header_t *OutHeadP);
|
||||
|
||||
kern_return_t
|
||||
acquire_mach_task(int, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
|
||||
|
||||
char *
|
||||
find_executable(int pid);
|
||||
|
||||
kern_return_t
|
||||
get_threads(task_t task, void *data,int limit);
|
||||
|
||||
int
|
||||
thread_count(task_t task);
|
||||
|
||||
mach_port_t
|
||||
mach_port_wait(mach_port_t, task_t*, int);
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t);
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t, mach_port_t, mach_port_t, exception_type_t);
|
||||
|
||||
kern_return_t
|
||||
reset_exception_ports(task_t task, mach_port_t *exception_port, mach_port_t *notification_port);
|
||||
|
||||
task_t
|
||||
get_task_for_pid(int pid);
|
||||
|
||||
int
|
||||
task_is_valid(task_t task);
|
493
vendor/github.com/derekparker/delve/pkg/proc/native/proc_linux.go
generated
vendored
493
vendor/github.com/derekparker/delve/pkg/proc/native/proc_linux.go
generated
vendored
@ -1,493 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/pkg/proc/linutil"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
// Process statuses
|
||||
const (
|
||||
StatusSleeping = 'S'
|
||||
StatusRunning = 'R'
|
||||
StatusTraceStop = 't'
|
||||
StatusZombie = 'Z'
|
||||
|
||||
// Kernel 2.6 has TraceStop as T
|
||||
// TODO(derekparker) Since this means something different based on the
|
||||
// version of the kernel ('T' is job control stop on modern 3.x+ kernels) we
|
||||
// may want to differentiate at some point.
|
||||
StatusTraceStopT = 'T'
|
||||
)
|
||||
|
||||
// OSProcessDetails contains Linux specific
|
||||
// process details.
|
||||
type OSProcessDetails struct {
|
||||
comm string
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process. First entry in
|
||||
// `cmd` is the program to run, and then rest are the arguments
|
||||
// to be supplied to that process. `wd` is working directory of the program.
|
||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
var (
|
||||
process *exec.Cmd
|
||||
err error
|
||||
)
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, proc.ErrNotExecutable
|
||||
}
|
||||
|
||||
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
||||
// exec.(*Process).Start will fail if we try to send a process to
|
||||
// foreground but we are not attached to a terminal.
|
||||
foreground = false
|
||||
}
|
||||
|
||||
dbp := New(0)
|
||||
dbp.common = proc.NewCommonProcess(true)
|
||||
dbp.execPtraceFunc(func() {
|
||||
process = exec.Command(cmd[0])
|
||||
process.Args = cmd
|
||||
process.Stdout = os.Stdout
|
||||
process.Stderr = os.Stderr
|
||||
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground}
|
||||
if foreground {
|
||||
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
||||
process.Stdin = os.Stdin
|
||||
}
|
||||
if wd != "" {
|
||||
process.Dir = wd
|
||||
}
|
||||
err = process.Start()
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp.pid = process.Process.Pid
|
||||
dbp.childProcess = true
|
||||
_, _, err = dbp.wait(process.Process.Pid, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||
}
|
||||
return initializeDebugProcess(dbp, process.Path)
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
dbp := New(pid)
|
||||
dbp.common = proc.NewCommonProcess(true)
|
||||
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _, err = dbp.wait(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp, err = initializeDebugProcess(dbp, "")
|
||||
if err != nil {
|
||||
dbp.Detach(false)
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
// kill kills the target process.
|
||||
func (dbp *Process) kill() (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if !dbp.threads[dbp.pid].Stopped() {
|
||||
return errors.New("process must be stopped in order to kill it")
|
||||
}
|
||||
if err = sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
|
||||
return errors.New("could not deliver signal " + err.Error())
|
||||
}
|
||||
if _, _, err = dbp.wait(dbp.pid, 0); err != nil {
|
||||
return
|
||||
}
|
||||
dbp.postExit()
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() (err error) {
|
||||
return sys.Kill(dbp.pid, sys.SIGTRAP)
|
||||
}
|
||||
|
||||
// Attach to a newly created thread, and store that thread in our list of
|
||||
// known threads.
|
||||
func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
|
||||
if thread, ok := dbp.threads[tid]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if attach {
|
||||
dbp.execPtraceFunc(func() { err = sys.PtraceAttach(tid) })
|
||||
if err != nil && err != sys.EPERM {
|
||||
// Do not return err if err == EPERM,
|
||||
// we may already be tracing this thread due to
|
||||
// PTRACE_O_TRACECLONE. We will surely blow up later
|
||||
// if we truly don't have permissions.
|
||||
return nil, fmt.Errorf("could not attach to new thread %d %s", tid, err)
|
||||
}
|
||||
pid, status, err := dbp.waitFast(tid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if status.Exited() {
|
||||
return nil, fmt.Errorf("thread already exited %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
||||
if err == syscall.ESRCH {
|
||||
if _, _, err = dbp.waitFast(tid); err != nil {
|
||||
return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
|
||||
}
|
||||
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
||||
if err == syscall.ESRCH {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not set options for new traced thread %d %s", tid, err)
|
||||
}
|
||||
}
|
||||
|
||||
dbp.threads[tid] = &Thread{
|
||||
ID: tid,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(tid)
|
||||
}
|
||||
return dbp.threads[tid], nil
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
tids, _ := filepath.Glob(fmt.Sprintf("/proc/%d/task/*", dbp.pid))
|
||||
for _, tidpath := range tids {
|
||||
tidstr := filepath.Base(tidpath)
|
||||
tid, err := strconv.Atoi(tidstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := dbp.addThread(tid, tid != dbp.pid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findExecutable(path string, pid int) string {
|
||||
if path == "" {
|
||||
path = fmt.Sprintf("/proc/%d/exe", pid)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
return dbp.trapWaitInternal(pid, false)
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWaitInternal(pid int, halt bool) (*Thread, error) {
|
||||
for {
|
||||
wpid, status, err := dbp.wait(pid, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
||||
}
|
||||
if wpid == 0 {
|
||||
continue
|
||||
}
|
||||
th, ok := dbp.threads[wpid]
|
||||
if ok {
|
||||
th.Status = (*WaitStatus)(status)
|
||||
}
|
||||
if status.Exited() {
|
||||
if wpid == dbp.pid {
|
||||
dbp.postExit()
|
||||
return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()}
|
||||
}
|
||||
delete(dbp.threads, wpid)
|
||||
continue
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
||||
// A traced thread has cloned a new thread, grab the pid and
|
||||
// add it to our list of traced threads.
|
||||
var cloned uint
|
||||
dbp.execPtraceFunc(func() { cloned, err = sys.PtraceGetEventMsg(wpid) })
|
||||
if err != nil {
|
||||
if err == sys.ESRCH {
|
||||
// thread died while we were adding it
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not get event message: %s", err)
|
||||
}
|
||||
th, err = dbp.addThread(int(cloned), false)
|
||||
if err != nil {
|
||||
if err == sys.ESRCH {
|
||||
// thread died while we were adding it
|
||||
delete(dbp.threads, int(cloned))
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if halt {
|
||||
th.os.running = false
|
||||
dbp.threads[int(wpid)].os.running = false
|
||||
return nil, nil
|
||||
}
|
||||
if err = th.Continue(); err != nil {
|
||||
if err == sys.ESRCH {
|
||||
// thread died while we were adding it
|
||||
delete(dbp.threads, th.ID)
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
||||
}
|
||||
if err = dbp.threads[int(wpid)].Continue(); err != nil {
|
||||
if err != sys.ESRCH {
|
||||
return nil, fmt.Errorf("could not continue existing thread %d %s", wpid, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if th == nil {
|
||||
// Sometimes we get an unknown thread, ignore it?
|
||||
continue
|
||||
}
|
||||
if (halt && status.StopSignal() == sys.SIGSTOP) || (status.StopSignal() == sys.SIGTRAP) {
|
||||
th.os.running = false
|
||||
return th, nil
|
||||
}
|
||||
if th != nil {
|
||||
// TODO(dp) alert user about unexpected signals here.
|
||||
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
||||
if err == sys.ESRCH {
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
|
||||
if err == nil {
|
||||
// removes newline character
|
||||
comm = bytes.TrimSuffix(comm, []byte("\n"))
|
||||
}
|
||||
|
||||
if comm == nil || len(comm) <= 0 {
|
||||
stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
|
||||
if err != nil {
|
||||
fmt.Printf("Could not read proc stat: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
|
||||
rexp, err := regexp.Compile(expr)
|
||||
if err != nil {
|
||||
fmt.Printf("Regexp compile error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
match := rexp.FindSubmatch(stat)
|
||||
if match == nil {
|
||||
fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.pid)
|
||||
os.Exit(1)
|
||||
}
|
||||
comm = match[1]
|
||||
}
|
||||
dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
|
||||
}
|
||||
|
||||
func status(pid int, comm string) rune {
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
|
||||
if err != nil {
|
||||
return '\000'
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var (
|
||||
p int
|
||||
state rune
|
||||
)
|
||||
|
||||
// The second field of /proc/pid/stat is the name of the task in parenthesis.
|
||||
// The name of the task is the base name of the executable for this process limited to TASK_COMM_LEN characters
|
||||
// Since both parenthesis and spaces can appear inside the name of the task and no escaping happens we need to read the name of the executable first
|
||||
// See: include/linux/sched.c:315 and include/linux/sched.c:1510
|
||||
fmt.Fscanf(f, "%d ("+comm+") %c", &p, &state)
|
||||
return state
|
||||
}
|
||||
|
||||
// waitFast is like wait but does not handle process-exit correctly
|
||||
func (dbp *Process) waitFast(pid int) (int, *sys.WaitStatus, error) {
|
||||
var s sys.WaitStatus
|
||||
wpid, err := sys.Wait4(pid, &s, sys.WALL, nil)
|
||||
return wpid, &s, err
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
var s sys.WaitStatus
|
||||
if (pid != dbp.pid) || (options != 0) {
|
||||
wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
|
||||
return wpid, &s, err
|
||||
}
|
||||
// If we call wait4/waitpid on a thread that is the leader of its group,
|
||||
// with options == 0, while ptracing and the thread leader has exited leaving
|
||||
// zombies of its own then waitpid hangs forever this is apparently intended
|
||||
// behaviour in the linux kernel because it's just so convenient.
|
||||
// Therefore we call wait4 in a loop with WNOHANG, sleeping a while between
|
||||
// calls and exiting when either wait4 succeeds or we find out that the thread
|
||||
// has become a zombie.
|
||||
// References:
|
||||
// https://sourceware.org/bugzilla/show_bug.cgi?id=12702
|
||||
// https://sourceware.org/bugzilla/show_bug.cgi?id=10095
|
||||
// https://sourceware.org/bugzilla/attachment.cgi?id=5685
|
||||
for {
|
||||
wpid, err := sys.Wait4(pid, &s, sys.WNOHANG|sys.WALL|options, nil)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if wpid != 0 {
|
||||
return wpid, &s, err
|
||||
}
|
||||
if status(pid, dbp.os.comm) == StatusZombie {
|
||||
return pid, nil, nil
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
if err != sys.ESRCH {
|
||||
return err
|
||||
}
|
||||
if status(dbp.pid, dbp.os.comm) == StatusZombie {
|
||||
_, err := dbp.trapWaitInternal(-1, false)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
// all threads stopped over a breakpoint are made to step over it
|
||||
for _, thread := range dbp.threads {
|
||||
if thread.CurrentBreakpoint.Breakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
for _, thread := range dbp.threads {
|
||||
if err := thread.resume(); err != nil && err != sys.ESRCH {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// stop stops all running threads threads and sets breakpoints
|
||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if !th.Stopped() {
|
||||
if err := th.stop(); err != nil {
|
||||
return dbp.exitGuard(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wait for all threads to stop
|
||||
for {
|
||||
allstopped := true
|
||||
for _, th := range dbp.threads {
|
||||
if th.os.running {
|
||||
allstopped = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allstopped {
|
||||
break
|
||||
}
|
||||
_, err := dbp.trapWaitInternal(-1, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set breakpoints on all threads
|
||||
for _, th := range dbp.threads {
|
||||
if th.CurrentBreakpoint.Breakpoint == nil {
|
||||
if err := th.SetCurrentBreakpoint(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) detach(kill bool) error {
|
||||
for threadID := range dbp.threads {
|
||||
err := PtraceDetach(threadID, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if kill {
|
||||
return nil
|
||||
}
|
||||
// For some reason the process will sometimes enter stopped state after a
|
||||
// detach, this doesn't happen immediately either.
|
||||
// We have to wait a bit here, then check if the main thread is stopped and
|
||||
// SIGCONT it if it is.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if s := status(dbp.pid, dbp.os.comm); s == 'T' {
|
||||
sys.Kill(dbp.pid, sys.SIGCONT)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) entryPoint() (uint64, error) {
|
||||
auxvbuf, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/auxv", dbp.pid))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("could not read auxiliary vector: %v", err)
|
||||
}
|
||||
|
||||
return linutil.EntryPointFromAuxvAMD64(auxvbuf), nil
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGINT)
|
||||
}
|
497
vendor/github.com/derekparker/delve/pkg/proc/native/proc_windows.go
generated
vendored
497
vendor/github.com/derekparker/delve/pkg/proc/native/proc_windows.go
generated
vendored
@ -1,497 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Windows specific information.
|
||||
type OSProcessDetails struct {
|
||||
hProcess syscall.Handle
|
||||
breakThread int
|
||||
entryPoint uint64
|
||||
}
|
||||
|
||||
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
peFile, err := pe.NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return peFile, f, nil
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process.
|
||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the binary exists and is an executable file
|
||||
if filepath.Base(cmd[0]) == cmd[0] {
|
||||
if _, err := exec.LookPath(cmd[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
_, closer, err := openExecutablePathPE(argv0Go)
|
||||
if err != nil {
|
||||
return nil, proc.ErrNotExecutable
|
||||
}
|
||||
closer.Close()
|
||||
|
||||
var p *os.Process
|
||||
dbp := New(0)
|
||||
dbp.common = proc.NewCommonProcess(true)
|
||||
dbp.execPtraceFunc(func() {
|
||||
attr := &os.ProcAttr{
|
||||
Dir: wd,
|
||||
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
|
||||
Sys: &syscall.SysProcAttr{
|
||||
CreationFlags: _DEBUG_ONLY_THIS_PROCESS,
|
||||
},
|
||||
}
|
||||
p, err = os.StartProcess(argv0Go, cmd, attr)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer p.Release()
|
||||
|
||||
dbp.pid = p.Pid
|
||||
dbp.childProcess = true
|
||||
|
||||
return newDebugProcess(dbp, argv0Go)
|
||||
}
|
||||
|
||||
// newDebugProcess prepares process pid for debugging.
|
||||
func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
||||
// It should not actually be possible for the
|
||||
// call to waitForDebugEvent to fail, since Windows
|
||||
// will always fire a CREATE_PROCESS_DEBUG_EVENT event
|
||||
// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
|
||||
// Attaching with DebugActiveProcess has similar effect.
|
||||
var err error
|
||||
var tid, exitCode int
|
||||
dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
// Suspend all threads so that the call to _ContinueDebugEvent will
|
||||
// not resume the target.
|
||||
for _, thread := range dbp.threads {
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return initializeDebugProcess(dbp, exepath)
|
||||
}
|
||||
|
||||
// findExePath searches for process pid, and returns its executable path.
|
||||
func findExePath(pid int) (string, error) {
|
||||
// Original code suggested different approach (see below).
|
||||
// Maybe it could be useful in the future.
|
||||
//
|
||||
// Find executable path from PID/handle on Windows:
|
||||
// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
|
||||
|
||||
p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer syscall.CloseHandle(p)
|
||||
|
||||
n := uint32(128)
|
||||
for {
|
||||
buf := make([]uint16, int(n))
|
||||
err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
|
||||
switch err {
|
||||
case syscall.ERROR_INSUFFICIENT_BUFFER:
|
||||
// try bigger buffer
|
||||
n *= 2
|
||||
// but stop if it gets too big
|
||||
if n > 10000 {
|
||||
return "", err
|
||||
}
|
||||
case nil:
|
||||
return syscall.UTF16ToString(buf[:n]), nil
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
// TODO: Probably should have SeDebugPrivilege before starting here.
|
||||
err := _DebugActiveProcess(uint32(pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exepath, err := findExePath(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp, err := newDebugProcess(New(pid), exepath)
|
||||
if err != nil {
|
||||
if dbp != nil {
|
||||
dbp.Detach(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
// kill kills the process.
|
||||
func (dbp *Process) kill() error {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
|
||||
p, err := os.FindProcess(dbp.pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.Release()
|
||||
|
||||
// TODO: Should not have to ignore failures here,
|
||||
// but some tests appear to Kill twice causing
|
||||
// this to fail on second attempt.
|
||||
_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
|
||||
|
||||
dbp.execPtraceFunc(func() {
|
||||
dbp.waitForDebugEvent(waitBlocking | waitDontHandleExceptions)
|
||||
})
|
||||
|
||||
p.Wait()
|
||||
|
||||
dbp.postExit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() error {
|
||||
return _DebugBreakProcess(dbp.os.hProcess)
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
// We ignore this request since threads are being
|
||||
// tracked as they are created/killed in waitForDebugEvent.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*Thread, error) {
|
||||
if thread, ok := dbp.threads[threadID]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
thread := &Thread{
|
||||
ID: threadID,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
thread.os.hThread = hThread
|
||||
dbp.threads[threadID] = thread
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(thread.ID)
|
||||
}
|
||||
if suspendNewThreads {
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func findExecutable(path string, pid int) string {
|
||||
return path
|
||||
}
|
||||
|
||||
type waitForDebugEventFlags int
|
||||
|
||||
const (
|
||||
waitBlocking waitForDebugEventFlags = 1 << iota
|
||||
waitSuspendNewThreads
|
||||
waitDontHandleExceptions
|
||||
)
|
||||
|
||||
func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
|
||||
var debugEvent _DEBUG_EVENT
|
||||
shouldExit := false
|
||||
for {
|
||||
continueStatus := uint32(_DBG_CONTINUE)
|
||||
var milliseconds uint32 = 0
|
||||
if flags&waitBlocking != 0 {
|
||||
milliseconds = syscall.INFINITE
|
||||
}
|
||||
// Wait for a debug event...
|
||||
err := _WaitForDebugEvent(&debugEvent, milliseconds)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// ... handle each event kind ...
|
||||
unionPtr := unsafe.Pointer(&debugEvent.U[0])
|
||||
switch debugEvent.DebugEventCode {
|
||||
case _CREATE_PROCESS_DEBUG_EVENT:
|
||||
debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
|
||||
hFile := debugInfo.File
|
||||
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||
err = syscall.CloseHandle(hFile)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
dbp.os.entryPoint = uint64(debugInfo.BaseOfImage)
|
||||
dbp.os.hProcess = debugInfo.Process
|
||||
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
break
|
||||
case _CREATE_THREAD_DEBUG_EVENT:
|
||||
debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
|
||||
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
break
|
||||
case _EXIT_THREAD_DEBUG_EVENT:
|
||||
delete(dbp.threads, int(debugEvent.ThreadId))
|
||||
break
|
||||
case _OUTPUT_DEBUG_STRING_EVENT:
|
||||
//TODO: Handle debug output strings
|
||||
break
|
||||
case _LOAD_DLL_DEBUG_EVENT:
|
||||
debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
|
||||
hFile := debugInfo.File
|
||||
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||
err = syscall.CloseHandle(hFile)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
break
|
||||
case _UNLOAD_DLL_DEBUG_EVENT:
|
||||
break
|
||||
case _RIP_EVENT:
|
||||
break
|
||||
case _EXCEPTION_DEBUG_EVENT:
|
||||
if flags&waitDontHandleExceptions != 0 {
|
||||
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
|
||||
break
|
||||
}
|
||||
exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
|
||||
tid := int(debugEvent.ThreadId)
|
||||
|
||||
switch code := exception.ExceptionRecord.ExceptionCode; code {
|
||||
case _EXCEPTION_BREAKPOINT:
|
||||
|
||||
// check if the exception address really is a breakpoint instruction, if
|
||||
// it isn't we already removed that breakpoint and we can't deal with
|
||||
// this exception anymore.
|
||||
atbp := true
|
||||
if thread, found := dbp.threads[tid]; found {
|
||||
data := make([]byte, dbp.bi.Arch.BreakpointSize())
|
||||
if _, err := thread.ReadMemory(data, exception.ExceptionRecord.ExceptionAddress); err == nil {
|
||||
instr := dbp.bi.Arch.BreakpointInstruction()
|
||||
for i := range instr {
|
||||
if data[i] != instr[i] {
|
||||
atbp = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !atbp {
|
||||
thread.SetPC(uint64(exception.ExceptionRecord.ExceptionAddress))
|
||||
}
|
||||
}
|
||||
|
||||
if atbp {
|
||||
dbp.os.breakThread = tid
|
||||
return tid, 0, nil
|
||||
} else {
|
||||
continueStatus = _DBG_CONTINUE
|
||||
}
|
||||
case _EXCEPTION_SINGLE_STEP:
|
||||
dbp.os.breakThread = tid
|
||||
return tid, 0, nil
|
||||
default:
|
||||
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
|
||||
}
|
||||
case _EXIT_PROCESS_DEBUG_EVENT:
|
||||
debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
|
||||
exitCode = int(debugInfo.ExitCode)
|
||||
shouldExit = true
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
|
||||
}
|
||||
|
||||
// .. and then continue unless we received an event that indicated we should break into debugger.
|
||||
err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if shouldExit {
|
||||
return 0, exitCode, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
var err error
|
||||
var tid, exitCode int
|
||||
dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
th := dbp.threads[tid]
|
||||
return th, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
return 0, nil, fmt.Errorf("not implemented: wait")
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
for _, thread := range dbp.threads {
|
||||
if thread.CurrentBreakpoint.Breakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
for _, thread := range dbp.threads {
|
||||
_, err := _ResumeThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// stop stops all running threads threads and sets breakpoints
|
||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
|
||||
// While the debug event that stopped the target was being propagated
|
||||
// other target threads could generate other debug events.
|
||||
// After this function we need to know about all the threads
|
||||
// stopped on a breakpoint. To do that we first suspend all target
|
||||
// threads and then repeatedly call _ContinueDebugEvent followed by
|
||||
// waitForDebugEvent in non-blocking mode.
|
||||
// We need to explicitly call SuspendThread because otherwise the
|
||||
// call to _ContinueDebugEvent will resume execution of some of the
|
||||
// target threads.
|
||||
|
||||
err = trapthread.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, thread := range dbp.threads {
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
var err error
|
||||
var tid int
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||
if err == nil {
|
||||
tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tid == 0 {
|
||||
break
|
||||
}
|
||||
err = dbp.threads[tid].SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) detach(kill bool) error {
|
||||
if !kill {
|
||||
for _, thread := range dbp.threads {
|
||||
_, err := _ResumeThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return _DebugActiveProcessStop(uint32(dbp.pid))
|
||||
}
|
||||
|
||||
func (dbp *Process) entryPoint() (uint64, error) {
|
||||
return dbp.os.entryPoint, nil
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
p, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.Release()
|
||||
|
||||
return p.Kill()
|
||||
}
|
30
vendor/github.com/derekparker/delve/pkg/proc/native/ptrace_darwin.go
generated
vendored
30
vendor/github.com/derekparker/delve/pkg/proc/native/ptrace_darwin.go
generated
vendored
@ -1,30 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
import sys "golang.org/x/sys/unix"
|
||||
|
||||
// PtraceAttach executes the sys.PtraceAttach call.
|
||||
func PtraceAttach(pid int) error {
|
||||
return sys.PtraceAttach(pid)
|
||||
}
|
||||
|
||||
// PtraceDetach executes the PT_DETACH ptrace call.
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
return ptrace(sys.PT_DETACH, tid, 1, uintptr(sig))
|
||||
}
|
||||
|
||||
// PtraceCont executes the PTRACE_CONT ptrace call.
|
||||
func PtraceCont(tid, sig int) error {
|
||||
return ptrace(sys.PTRACE_CONT, tid, 1, 0)
|
||||
}
|
||||
|
||||
// PtraceSingleStep returns PT_STEP ptrace call.
|
||||
func PtraceSingleStep(tid int) error {
|
||||
return ptrace(sys.PT_STEP, tid, 1, 0)
|
||||
}
|
||||
|
||||
func ptrace(request, pid int, addr uintptr, data uintptr) (err error) {
|
||||
_, _, err = sys.Syscall6(sys.SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
|
||||
return
|
||||
}
|
83
vendor/github.com/derekparker/delve/pkg/proc/native/ptrace_linux.go
generated
vendored
83
vendor/github.com/derekparker/delve/pkg/proc/native/ptrace_linux.go
generated
vendored
@ -1,83 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// PtraceAttach executes the sys.PtraceAttach call.
|
||||
func PtraceAttach(pid int) error {
|
||||
return sys.PtraceAttach(pid)
|
||||
}
|
||||
|
||||
// PtraceDetach calls ptrace(PTRACE_DETACH).
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
_, _, err := sys.Syscall6(sys.SYS_PTRACE, sys.PTRACE_DETACH, uintptr(tid), 1, uintptr(sig), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtraceCont executes ptrace PTRACE_CONT
|
||||
func PtraceCont(tid, sig int) error {
|
||||
return sys.PtraceCont(tid, sig)
|
||||
}
|
||||
|
||||
// PtraceSingleStep executes ptrace PTRACE_SINGLE_STEP.
|
||||
func PtraceSingleStep(tid int) error {
|
||||
return sys.PtraceSingleStep(tid)
|
||||
}
|
||||
|
||||
// PtracePokeUser execute ptrace PTRACE_POKE_USER.
|
||||
func PtracePokeUser(tid int, off, addr uintptr) error {
|
||||
_, _, err := sys.Syscall6(sys.SYS_PTRACE, sys.PTRACE_POKEUSR, uintptr(tid), uintptr(off), uintptr(addr), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtracePeekUser execute ptrace PTRACE_PEEK_USER.
|
||||
func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
|
||||
var val uintptr
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_PTRACE, syscall.PTRACE_PEEKUSR, uintptr(tid), uintptr(off), uintptr(unsafe.Pointer(&val)), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// PtraceGetRegset returns floating point registers of the specified thread
|
||||
// using PTRACE.
|
||||
// See amd64_linux_fetch_inferior_registers in gdb/amd64-linux-nat.c.html
|
||||
// and amd64_supply_xsave in gdb/amd64-tdep.c.html
|
||||
// and Section 13.1 (and following) of Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture
|
||||
func PtraceGetRegset(tid int) (regset proc.LinuxX86Xstate, err error) {
|
||||
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.PtraceFpRegs)), 0, 0)
|
||||
if err == syscall.Errno(0) || err == syscall.ENODEV {
|
||||
// ignore ENODEV, it just means this CPU doesn't have X87 registers (??)
|
||||
err = nil
|
||||
}
|
||||
|
||||
var xstateargs [_X86_XSTATE_MAX_SIZE]byte
|
||||
iov := sys.Iovec{Base: &xstateargs[0], Len: _X86_XSTATE_MAX_SIZE}
|
||||
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETREGSET, uintptr(tid), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
if err == syscall.ENODEV {
|
||||
// ignore ENODEV, it just means this CPU or kernel doesn't support XSTATE, see https://github.com/derekparker/delve/issues/1022
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
|
||||
regset.Xsave = xstateargs[:iov.Len]
|
||||
err = proc.LinuxX86XstateRead(regset.Xsave, false, ®set)
|
||||
return regset, err
|
||||
}
|
375
vendor/github.com/derekparker/delve/pkg/proc/native/registers_darwin_amd64.go
generated
vendored
375
vendor/github.com/derekparker/delve/pkg/proc/native/registers_darwin_amd64.go
generated
vendored
@ -1,375 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Regs represents CPU registers on an AMD64 processor.
|
||||
type Regs struct {
|
||||
rax uint64
|
||||
rbx uint64
|
||||
rcx uint64
|
||||
rdx uint64
|
||||
rdi uint64
|
||||
rsi uint64
|
||||
rbp uint64
|
||||
rsp uint64
|
||||
r8 uint64
|
||||
r9 uint64
|
||||
r10 uint64
|
||||
r11 uint64
|
||||
r12 uint64
|
||||
r13 uint64
|
||||
r14 uint64
|
||||
r15 uint64
|
||||
rip uint64
|
||||
rflags uint64
|
||||
cs uint64
|
||||
fs uint64
|
||||
gs uint64
|
||||
gsBase uint64
|
||||
fpregs []proc.Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.rip},
|
||||
{"Rsp", r.rsp},
|
||||
{"Rax", r.rax},
|
||||
{"Rbx", r.rbx},
|
||||
{"Rcx", r.rcx},
|
||||
{"Rdx", r.rdx},
|
||||
{"Rdi", r.rdi},
|
||||
{"Rsi", r.rsi},
|
||||
{"Rbp", r.rbp},
|
||||
{"R8", r.r8},
|
||||
{"R9", r.r9},
|
||||
{"R10", r.r10},
|
||||
{"R11", r.r11},
|
||||
{"R12", r.r12},
|
||||
{"R13", r.r13},
|
||||
{"R14", r.r14},
|
||||
{"R15", r.r15},
|
||||
{"Rflags", r.rflags},
|
||||
{"Cs", r.cs},
|
||||
{"Fs", r.fs},
|
||||
{"Gs", r.gs},
|
||||
{"Gs_base", r.gsBase},
|
||||
}
|
||||
out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Rflags" {
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the current program counter
|
||||
// i.e. the RIP CPU register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.rip
|
||||
}
|
||||
|
||||
// SP returns the stack pointer location,
|
||||
// i.e. the RSP register.
|
||||
func (r *Regs) SP() uint64 {
|
||||
return r.rsp
|
||||
}
|
||||
|
||||
func (r *Regs) BP() uint64 {
|
||||
return r.rbp
|
||||
}
|
||||
|
||||
// CX returns the value of the RCX register.
|
||||
func (r *Regs) CX() uint64 {
|
||||
return r.rcx
|
||||
}
|
||||
|
||||
// TLS returns the value of the register
|
||||
// that contains the location of the thread
|
||||
// local storage segment.
|
||||
func (r *Regs) TLS() uint64 {
|
||||
return r.gsBase
|
||||
}
|
||||
|
||||
func (r *Regs) GAddr() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (thread *Thread) SetPC(pc uint64) error {
|
||||
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not set pc")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetSP sets the RSP register to the value specified by `pc`.
|
||||
func (thread *Thread) SetSP(sp uint64) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (thread *Thread) SetDX(dx uint64) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (r *Regs) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.r8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.r9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.r10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.r11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.r12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.r13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.r14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.r15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.r8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.r9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.r10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.r11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.r12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.r13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.r14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.r15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.r8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.r9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.r10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.r11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.r12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.r13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.r14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.r15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.r8, nil
|
||||
case x86asm.R9:
|
||||
return r.r9, nil
|
||||
case x86asm.R10:
|
||||
return r.r10, nil
|
||||
case x86asm.R11:
|
||||
return r.r11, nil
|
||||
case x86asm.R12:
|
||||
return r.r12, nil
|
||||
case x86asm.R13:
|
||||
return r.r13, nil
|
||||
case x86asm.R14:
|
||||
return r.r14, nil
|
||||
case x86asm.R15:
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
var state C.x86_thread_state64_t
|
||||
var identity C.thread_identifier_info_data_t
|
||||
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get registers")
|
||||
}
|
||||
kret = C.get_identity(C.mach_port_name_t(thread.os.threadAct), &identity)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get thread identity informations")
|
||||
}
|
||||
/*
|
||||
thread_identifier_info::thread_handle contains the base of the
|
||||
thread-specific data area, which on x86 and x86_64 is the thread’s base
|
||||
address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
|
||||
thread_info_internal() gets the value from
|
||||
machine_thread::cthread_self, which is the same value used to set the
|
||||
%gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
|
||||
act_machine_switch_pcb().
|
||||
--
|
||||
comment copied from chromium's crashpad
|
||||
https://chromium.googlesource.com/crashpad/crashpad/+/master/snapshot/mac/process_reader.cc
|
||||
*/
|
||||
regs := &Regs{
|
||||
rax: uint64(state.__rax),
|
||||
rbx: uint64(state.__rbx),
|
||||
rcx: uint64(state.__rcx),
|
||||
rdx: uint64(state.__rdx),
|
||||
rdi: uint64(state.__rdi),
|
||||
rsi: uint64(state.__rsi),
|
||||
rbp: uint64(state.__rbp),
|
||||
rsp: uint64(state.__rsp),
|
||||
r8: uint64(state.__r8),
|
||||
r9: uint64(state.__r9),
|
||||
r10: uint64(state.__r10),
|
||||
r11: uint64(state.__r11),
|
||||
r12: uint64(state.__r12),
|
||||
r13: uint64(state.__r13),
|
||||
r14: uint64(state.__r14),
|
||||
r15: uint64(state.__r15),
|
||||
rip: uint64(state.__rip),
|
||||
rflags: uint64(state.__rflags),
|
||||
cs: uint64(state.__cs),
|
||||
fs: uint64(state.__fs),
|
||||
gs: uint64(state.__gs),
|
||||
gsBase: uint64(identity.thread_handle),
|
||||
}
|
||||
|
||||
if floatingPoint {
|
||||
// https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/thread_status.h?txt
|
||||
var fpstate C.x86_float_state64_t
|
||||
kret = C.get_fpu_registers(C.mach_port_name_t(thread.os.threadAct), &fpstate)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get floating point registers")
|
||||
}
|
||||
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
|
||||
regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
|
||||
regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
|
||||
|
||||
for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} {
|
||||
stb := C.GoBytes(unsafe.Pointer(st), 10)
|
||||
mantissa := binary.LittleEndian.Uint64(stb[:8])
|
||||
exponent := binary.LittleEndian.Uint16(stb[8:])
|
||||
regs.fpregs = proc.AppendX87Reg(regs.fpregs, i, exponent, mantissa)
|
||||
}
|
||||
|
||||
regs.fpregs = proc.AppendMxcsrReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr))
|
||||
regs.fpregs = proc.AppendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
|
||||
|
||||
for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} {
|
||||
regs.fpregs = proc.AppendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
|
||||
}
|
||||
}
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (r *Regs) Copy() proc.Registers {
|
||||
//TODO(aarzilli): implement this to support function calls
|
||||
return nil
|
||||
}
|
342
vendor/github.com/derekparker/delve/pkg/proc/native/registers_linux_amd64.go
generated
vendored
342
vendor/github.com/derekparker/delve/pkg/proc/native/registers_linux_amd64.go
generated
vendored
@ -1,342 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Regs is a wrapper for sys.PtraceRegs.
|
||||
type Regs struct {
|
||||
regs *sys.PtraceRegs
|
||||
fpregs []proc.Register
|
||||
fpregset *proc.LinuxX86Xstate
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.regs.Rip},
|
||||
{"Rsp", r.regs.Rsp},
|
||||
{"Rax", r.regs.Rax},
|
||||
{"Rbx", r.regs.Rbx},
|
||||
{"Rcx", r.regs.Rcx},
|
||||
{"Rdx", r.regs.Rdx},
|
||||
{"Rdi", r.regs.Rdi},
|
||||
{"Rsi", r.regs.Rsi},
|
||||
{"Rbp", r.regs.Rbp},
|
||||
{"R8", r.regs.R8},
|
||||
{"R9", r.regs.R9},
|
||||
{"R10", r.regs.R10},
|
||||
{"R11", r.regs.R11},
|
||||
{"R12", r.regs.R12},
|
||||
{"R13", r.regs.R13},
|
||||
{"R14", r.regs.R14},
|
||||
{"R15", r.regs.R15},
|
||||
{"Orig_rax", r.regs.Orig_rax},
|
||||
{"Cs", r.regs.Cs},
|
||||
{"Eflags", r.regs.Eflags},
|
||||
{"Ss", r.regs.Ss},
|
||||
{"Fs_base", r.regs.Fs_base},
|
||||
{"Gs_base", r.regs.Gs_base},
|
||||
{"Ds", r.regs.Ds},
|
||||
{"Es", r.regs.Es},
|
||||
{"Fs", r.regs.Fs},
|
||||
{"Gs", r.regs.Gs},
|
||||
}
|
||||
out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the value of RIP register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.regs.PC()
|
||||
}
|
||||
|
||||
// SP returns the value of RSP register.
|
||||
func (r *Regs) SP() uint64 {
|
||||
return r.regs.Rsp
|
||||
}
|
||||
|
||||
func (r *Regs) BP() uint64 {
|
||||
return r.regs.Rbp
|
||||
}
|
||||
|
||||
// CX returns the value of RCX register.
|
||||
func (r *Regs) CX() uint64 {
|
||||
return r.regs.Rcx
|
||||
}
|
||||
|
||||
// TLS returns the address of the thread
|
||||
// local storage memory segment.
|
||||
func (r *Regs) TLS() uint64 {
|
||||
return r.regs.Fs_base
|
||||
}
|
||||
|
||||
func (r *Regs) GAddr() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// SetPC sets RIP to the value specified by 'pc'.
|
||||
func (thread *Thread) SetPC(pc uint64) error {
|
||||
ir, err := registers(thread, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r := ir.(*Regs)
|
||||
r.regs.SetPC(pc)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
|
||||
return err
|
||||
}
|
||||
|
||||
// SetSP sets RSP to the value specified by 'sp'
|
||||
func (thread *Thread) SetSP(sp uint64) (err error) {
|
||||
var ir proc.Registers
|
||||
ir, err = registers(thread, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r := ir.(*Regs)
|
||||
r.regs.Rsp = sp
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
|
||||
return
|
||||
}
|
||||
|
||||
func (thread *Thread) SetDX(dx uint64) (err error) {
|
||||
var ir proc.Registers
|
||||
ir, err = registers(thread, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r := ir.(*Regs)
|
||||
r.regs.Rdx = dx
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Regs) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.regs.Rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.regs.Rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.regs.Rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.regs.Rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.regs.Rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.regs.Rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.regs.Rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.regs.Rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.regs.Rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.regs.Rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.regs.Rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.regs.Rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.regs.R8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.regs.R9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.regs.R10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.regs.R11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.regs.R12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.regs.R13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.regs.R14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.regs.R15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.regs.Rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.regs.Rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.regs.Rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.regs.Rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.regs.Rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.regs.Rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.regs.Rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.regs.Rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.regs.R8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.regs.R9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.regs.R10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.regs.R11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.regs.R12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.regs.R13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.regs.R14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.regs.R15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.regs.Rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.regs.Rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.regs.Rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.regs.Rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.regs.Rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.regs.Rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.regs.Rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.regs.Rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.regs.R8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.regs.R9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.regs.R10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.regs.R11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.regs.R12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.regs.R13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.regs.R14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.regs.R15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.regs.Rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.regs.Rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.regs.Rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.regs.Rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.regs.Rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.regs.Rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.regs.Rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.regs.Rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.regs.R8, nil
|
||||
case x86asm.R9:
|
||||
return r.regs.R9, nil
|
||||
case x86asm.R10:
|
||||
return r.regs.R10, nil
|
||||
case x86asm.R11:
|
||||
return r.regs.R11, nil
|
||||
case x86asm.R12:
|
||||
return r.regs.R12, nil
|
||||
case x86asm.R13:
|
||||
return r.regs.R13, nil
|
||||
case x86asm.R14:
|
||||
return r.regs.R14, nil
|
||||
case x86asm.R15:
|
||||
return r.regs.R15, nil
|
||||
}
|
||||
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
var (
|
||||
regs sys.PtraceRegs
|
||||
err error
|
||||
)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, ®s) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &Regs{®s, nil, nil}
|
||||
if floatingPoint {
|
||||
var fpregset proc.LinuxX86Xstate
|
||||
r.fpregs, fpregset, err = thread.fpRegisters()
|
||||
r.fpregset = &fpregset
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
const (
|
||||
_X86_XSTATE_MAX_SIZE = 2688
|
||||
_NT_X86_XSTATE = 0x202
|
||||
|
||||
_XSAVE_HEADER_START = 512
|
||||
_XSAVE_HEADER_LEN = 64
|
||||
_XSAVE_EXTENDED_REGION_START = 576
|
||||
_XSAVE_SSE_REGION_LEN = 416
|
||||
)
|
||||
|
||||
func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs proc.LinuxX86Xstate, err error) {
|
||||
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
|
||||
regs = fpregs.Decode()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not get floating point registers: %v", err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Copy returns a copy of these registers that is
|
||||
// guarenteed not to change.
|
||||
func (r *Regs) Copy() proc.Registers {
|
||||
var rr Regs
|
||||
rr.regs = &sys.PtraceRegs{}
|
||||
rr.fpregset = &proc.LinuxX86Xstate{}
|
||||
*(rr.regs) = *(r.regs)
|
||||
if r.fpregset != nil {
|
||||
*(rr.fpregset) = *(r.fpregset)
|
||||
}
|
||||
if r.fpregs != nil {
|
||||
rr.fpregs = make([]proc.Register, len(r.fpregs))
|
||||
copy(rr.fpregs, r.fpregs)
|
||||
}
|
||||
return &rr
|
||||
}
|
390
vendor/github.com/derekparker/delve/pkg/proc/native/registers_windows_amd64.go
generated
vendored
390
vendor/github.com/derekparker/delve/pkg/proc/native/registers_windows_amd64.go
generated
vendored
@ -1,390 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Regs represents CPU registers on an AMD64 processor.
|
||||
type Regs struct {
|
||||
rax uint64
|
||||
rbx uint64
|
||||
rcx uint64
|
||||
rdx uint64
|
||||
rdi uint64
|
||||
rsi uint64
|
||||
rbp uint64
|
||||
rsp uint64
|
||||
r8 uint64
|
||||
r9 uint64
|
||||
r10 uint64
|
||||
r11 uint64
|
||||
r12 uint64
|
||||
r13 uint64
|
||||
r14 uint64
|
||||
r15 uint64
|
||||
rip uint64
|
||||
eflags uint64
|
||||
cs uint64
|
||||
fs uint64
|
||||
gs uint64
|
||||
tls uint64
|
||||
context *_CONTEXT
|
||||
fltSave *_XMM_SAVE_AREA32
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.rip},
|
||||
{"Rsp", r.rsp},
|
||||
{"Rax", r.rax},
|
||||
{"Rbx", r.rbx},
|
||||
{"Rcx", r.rcx},
|
||||
{"Rdx", r.rdx},
|
||||
{"Rdi", r.rdi},
|
||||
{"Rsi", r.rsi},
|
||||
{"Rbp", r.rbp},
|
||||
{"R8", r.r8},
|
||||
{"R9", r.r9},
|
||||
{"R10", r.r10},
|
||||
{"R11", r.r11},
|
||||
{"R12", r.r12},
|
||||
{"R13", r.r13},
|
||||
{"R14", r.r14},
|
||||
{"R15", r.r15},
|
||||
{"Eflags", r.eflags},
|
||||
{"Cs", r.cs},
|
||||
{"Fs", r.fs},
|
||||
{"Gs", r.gs},
|
||||
{"TLS", r.tls},
|
||||
}
|
||||
outlen := len(regs)
|
||||
if r.fltSave != nil {
|
||||
outlen += 6 + 8 + 2 + 16
|
||||
}
|
||||
out := make([]proc.Register, 0, outlen)
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
if r.fltSave != nil {
|
||||
out = proc.AppendWordReg(out, "CW", r.fltSave.ControlWord)
|
||||
out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
|
||||
out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord))
|
||||
out = proc.AppendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
|
||||
out = proc.AppendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
|
||||
out = proc.AppendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
|
||||
|
||||
for i := range r.fltSave.FloatRegisters {
|
||||
out = proc.AppendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
|
||||
}
|
||||
|
||||
out = proc.AppendMxcsrReg(out, "MXCSR", uint64(r.fltSave.MxCsr))
|
||||
out = proc.AppendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
|
||||
|
||||
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 {
|
||||
out = proc.AppendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the current program counter
|
||||
// i.e. the RIP CPU register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.rip
|
||||
}
|
||||
|
||||
// SP returns the stack pointer location,
|
||||
// i.e. the RSP register.
|
||||
func (r *Regs) SP() uint64 {
|
||||
return r.rsp
|
||||
}
|
||||
|
||||
func (r *Regs) BP() uint64 {
|
||||
return r.rbp
|
||||
}
|
||||
|
||||
// CX returns the value of the RCX register.
|
||||
func (r *Regs) CX() uint64 {
|
||||
return r.rcx
|
||||
}
|
||||
|
||||
// TLS returns the value of the register
|
||||
// that contains the location of the thread
|
||||
// local storage segment.
|
||||
func (r *Regs) TLS() uint64 {
|
||||
return r.tls
|
||||
}
|
||||
|
||||
func (r *Regs) GAddr() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (thread *Thread) SetPC(pc uint64) error {
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
err := _GetThreadContext(thread.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.Rip = pc
|
||||
|
||||
return _SetThreadContext(thread.os.hThread, context)
|
||||
}
|
||||
|
||||
// SetSP sets the RSP register to the value specified by `sp`.
|
||||
func (thread *Thread) SetSP(sp uint64) error {
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
err := _GetThreadContext(thread.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.Rsp = sp
|
||||
|
||||
return _SetThreadContext(thread.os.hThread, context)
|
||||
}
|
||||
|
||||
func (thread *Thread) SetDX(dx uint64) error {
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
err := _GetThreadContext(thread.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.Rdx = dx
|
||||
|
||||
return _SetThreadContext(thread.os.hThread, context)
|
||||
}
|
||||
|
||||
func (r *Regs) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.r8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.r9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.r10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.r11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.r12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.r13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.r14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.r15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.r8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.r9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.r10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.r11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.r12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.r13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.r14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.r15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.r8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.r9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.r10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.r11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.r12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.r13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.r14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.r15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.r8, nil
|
||||
case x86asm.R9:
|
||||
return r.r9, nil
|
||||
case x86asm.R10:
|
||||
return r.r10, nil
|
||||
case x86asm.R11:
|
||||
return r.r11, nil
|
||||
case x86asm.R12:
|
||||
return r.r12, nil
|
||||
case x86asm.R13:
|
||||
return r.r13, nil
|
||||
case x86asm.R14:
|
||||
return r.r14, nil
|
||||
case x86asm.R15:
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
context := newCONTEXT()
|
||||
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
err := _GetThreadContext(thread.os.hThread, context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var threadInfo _THREAD_BASIC_INFORMATION
|
||||
status := _NtQueryInformationThread(thread.os.hThread, _ThreadBasicInformation, uintptr(unsafe.Pointer(&threadInfo)), uint32(unsafe.Sizeof(threadInfo)), nil)
|
||||
if !_NT_SUCCESS(status) {
|
||||
return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status)
|
||||
}
|
||||
|
||||
regs := &Regs{
|
||||
rax: uint64(context.Rax),
|
||||
rbx: uint64(context.Rbx),
|
||||
rcx: uint64(context.Rcx),
|
||||
rdx: uint64(context.Rdx),
|
||||
rdi: uint64(context.Rdi),
|
||||
rsi: uint64(context.Rsi),
|
||||
rbp: uint64(context.Rbp),
|
||||
rsp: uint64(context.Rsp),
|
||||
r8: uint64(context.R8),
|
||||
r9: uint64(context.R9),
|
||||
r10: uint64(context.R10),
|
||||
r11: uint64(context.R11),
|
||||
r12: uint64(context.R12),
|
||||
r13: uint64(context.R13),
|
||||
r14: uint64(context.R14),
|
||||
r15: uint64(context.R15),
|
||||
rip: uint64(context.Rip),
|
||||
eflags: uint64(context.EFlags),
|
||||
cs: uint64(context.SegCs),
|
||||
fs: uint64(context.SegFs),
|
||||
gs: uint64(context.SegGs),
|
||||
tls: uint64(threadInfo.TebBaseAddress),
|
||||
}
|
||||
|
||||
if floatingPoint {
|
||||
regs.fltSave = &context.FltSave
|
||||
}
|
||||
regs.context = context
|
||||
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (r *Regs) Copy() proc.Registers {
|
||||
var rr Regs
|
||||
rr = *r
|
||||
rr.context = newCONTEXT()
|
||||
*(rr.context) = *(r.context)
|
||||
rr.fltSave = &rr.context.FltSave
|
||||
return &rr
|
||||
}
|
112
vendor/github.com/derekparker/delve/pkg/proc/native/syscall_windows.go
generated
vendored
112
vendor/github.com/derekparker/delve/pkg/proc/native/syscall_windows.go
generated
vendored
@ -1,112 +0,0 @@
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type _NTSTATUS int32
|
||||
|
||||
type _CLIENT_ID struct {
|
||||
UniqueProcess syscall.Handle
|
||||
UniqueThread syscall.Handle
|
||||
}
|
||||
|
||||
type _THREAD_BASIC_INFORMATION struct {
|
||||
ExitStatus _NTSTATUS
|
||||
TebBaseAddress uintptr
|
||||
ClientId _CLIENT_ID
|
||||
AffinityMask uintptr
|
||||
Priority int32
|
||||
BasePriority int32
|
||||
}
|
||||
|
||||
type _CREATE_PROCESS_DEBUG_INFO struct {
|
||||
File syscall.Handle
|
||||
Process syscall.Handle
|
||||
Thread syscall.Handle
|
||||
BaseOfImage uintptr
|
||||
DebugInfoFileOffset uint32
|
||||
DebugInfoSize uint32
|
||||
ThreadLocalBase uintptr
|
||||
StartAddress uintptr
|
||||
ImageName uintptr
|
||||
Unicode uint16
|
||||
}
|
||||
|
||||
type _CREATE_THREAD_DEBUG_INFO struct {
|
||||
Thread syscall.Handle
|
||||
ThreadLocalBase uintptr
|
||||
StartAddress uintptr
|
||||
}
|
||||
|
||||
type _EXIT_PROCESS_DEBUG_INFO struct {
|
||||
ExitCode uint32
|
||||
}
|
||||
|
||||
type _LOAD_DLL_DEBUG_INFO struct {
|
||||
File syscall.Handle
|
||||
BaseOfDll uintptr
|
||||
DebugInfoFileOffset uint32
|
||||
DebugInfoSize uint32
|
||||
ImageName uintptr
|
||||
Unicode uint16
|
||||
}
|
||||
|
||||
type _EXCEPTION_DEBUG_INFO struct {
|
||||
ExceptionRecord _EXCEPTION_RECORD
|
||||
FirstChance uint32
|
||||
}
|
||||
|
||||
type _EXCEPTION_RECORD struct {
|
||||
ExceptionCode uint32
|
||||
ExceptionFlags uint32
|
||||
ExceptionRecord *_EXCEPTION_RECORD
|
||||
ExceptionAddress uintptr
|
||||
NumberParameters uint32
|
||||
ExceptionInformation [_EXCEPTION_MAXIMUM_PARAMETERS]uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
_ThreadBasicInformation = 0
|
||||
|
||||
_DBG_CONTINUE = 0x00010002
|
||||
_DBG_EXCEPTION_NOT_HANDLED = 0x80010001
|
||||
|
||||
_EXCEPTION_DEBUG_EVENT = 1
|
||||
_CREATE_THREAD_DEBUG_EVENT = 2
|
||||
_CREATE_PROCESS_DEBUG_EVENT = 3
|
||||
_EXIT_THREAD_DEBUG_EVENT = 4
|
||||
_EXIT_PROCESS_DEBUG_EVENT = 5
|
||||
_LOAD_DLL_DEBUG_EVENT = 6
|
||||
_UNLOAD_DLL_DEBUG_EVENT = 7
|
||||
_OUTPUT_DEBUG_STRING_EVENT = 8
|
||||
_RIP_EVENT = 9
|
||||
|
||||
// DEBUG_ONLY_THIS_PROCESS tracks https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
|
||||
_DEBUG_ONLY_THIS_PROCESS = 0x00000002
|
||||
|
||||
_EXCEPTION_BREAKPOINT = 0x80000003
|
||||
_EXCEPTION_SINGLE_STEP = 0x80000004
|
||||
|
||||
_EXCEPTION_MAXIMUM_PARAMETERS = 15
|
||||
)
|
||||
|
||||
func _NT_SUCCESS(x _NTSTATUS) bool {
|
||||
return x >= 0
|
||||
}
|
||||
|
||||
//sys _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) = ntdll.NtQueryInformationThread
|
||||
//sys _GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.GetThreadContext
|
||||
//sys _SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.SetThreadContext
|
||||
//sys _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.SuspendThread
|
||||
//sys _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.ResumeThread
|
||||
//sys _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) = kernel32.ContinueDebugEvent
|
||||
//sys _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) = kernel32.WriteProcessMemory
|
||||
//sys _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) = kernel32.ReadProcessMemory
|
||||
//sys _DebugBreakProcess(process syscall.Handle) (err error) = kernel32.DebugBreakProcess
|
||||
//sys _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) = kernel32.WaitForDebugEvent
|
||||
//sys _DebugActiveProcess(processid uint32) (err error) = kernel32.DebugActiveProcess
|
||||
//sys _DebugActiveProcessStop(processid uint32) (err error) = kernel32.DebugActiveProcessStop
|
||||
//sys _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) = kernel32.QueryFullProcessImageNameW
|
114
vendor/github.com/derekparker/delve/pkg/proc/native/syscall_windows_amd64.go
generated
vendored
114
vendor/github.com/derekparker/delve/pkg/proc/native/syscall_windows_amd64.go
generated
vendored
@ -1,114 +0,0 @@
|
||||
package native
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
_CONTEXT_AMD64 = 0x100000
|
||||
_CONTEXT_CONTROL = (_CONTEXT_AMD64 | 0x1)
|
||||
_CONTEXT_INTEGER = (_CONTEXT_AMD64 | 0x2)
|
||||
_CONTEXT_SEGMENTS = (_CONTEXT_AMD64 | 0x4)
|
||||
_CONTEXT_FLOATING_POINT = (_CONTEXT_AMD64 | 0x8)
|
||||
_CONTEXT_DEBUG_REGISTERS = (_CONTEXT_AMD64 | 0x10)
|
||||
_CONTEXT_FULL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT)
|
||||
_CONTEXT_ALL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_SEGMENTS | _CONTEXT_FLOATING_POINT | _CONTEXT_DEBUG_REGISTERS)
|
||||
_CONTEXT_EXCEPTION_ACTIVE = 0x8000000
|
||||
_CONTEXT_SERVICE_ACTIVE = 0x10000000
|
||||
_CONTEXT_EXCEPTION_REQUEST = 0x40000000
|
||||
_CONTEXT_EXCEPTION_REPORTING = 0x80000000
|
||||
)
|
||||
|
||||
type _M128A struct {
|
||||
Low uint64
|
||||
High int64
|
||||
}
|
||||
|
||||
type _XMM_SAVE_AREA32 struct {
|
||||
ControlWord uint16
|
||||
StatusWord uint16
|
||||
TagWord byte
|
||||
Reserved1 byte
|
||||
ErrorOpcode uint16
|
||||
ErrorOffset uint32
|
||||
ErrorSelector uint16
|
||||
Reserved2 uint16
|
||||
DataOffset uint32
|
||||
DataSelector uint16
|
||||
Reserved3 uint16
|
||||
MxCsr uint32
|
||||
MxCsr_Mask uint32
|
||||
FloatRegisters [8]_M128A
|
||||
XmmRegisters [256]byte
|
||||
Reserved4 [96]byte
|
||||
}
|
||||
|
||||
type _CONTEXT struct {
|
||||
P1Home uint64
|
||||
P2Home uint64
|
||||
P3Home uint64
|
||||
P4Home uint64
|
||||
P5Home uint64
|
||||
P6Home uint64
|
||||
|
||||
ContextFlags uint32
|
||||
MxCsr uint32
|
||||
|
||||
SegCs uint16
|
||||
SegDs uint16
|
||||
SegEs uint16
|
||||
SegFs uint16
|
||||
SegGs uint16
|
||||
SegSs uint16
|
||||
EFlags uint32
|
||||
|
||||
Dr0 uint64
|
||||
Dr1 uint64
|
||||
Dr2 uint64
|
||||
Dr3 uint64
|
||||
Dr6 uint64
|
||||
Dr7 uint64
|
||||
|
||||
Rax uint64
|
||||
Rcx uint64
|
||||
Rdx uint64
|
||||
Rbx uint64
|
||||
Rsp uint64
|
||||
Rbp uint64
|
||||
Rsi uint64
|
||||
Rdi uint64
|
||||
R8 uint64
|
||||
R9 uint64
|
||||
R10 uint64
|
||||
R11 uint64
|
||||
R12 uint64
|
||||
R13 uint64
|
||||
R14 uint64
|
||||
R15 uint64
|
||||
|
||||
Rip uint64
|
||||
|
||||
FltSave _XMM_SAVE_AREA32
|
||||
|
||||
VectorRegister [26]_M128A
|
||||
VectorControl uint64
|
||||
|
||||
DebugControl uint64
|
||||
LastBranchToRip uint64
|
||||
LastBranchFromRip uint64
|
||||
LastExceptionToRip uint64
|
||||
LastExceptionFromRip uint64
|
||||
}
|
||||
|
||||
// newCONTEXT allocates Windows CONTEXT structure aligned to 16 bytes.
|
||||
func newCONTEXT() *_CONTEXT {
|
||||
var c *_CONTEXT
|
||||
buf := make([]byte, unsafe.Sizeof(*c)+15)
|
||||
return (*_CONTEXT)(unsafe.Pointer((uintptr(unsafe.Pointer(&buf[15]))) &^ 15))
|
||||
}
|
||||
|
||||
type _DEBUG_EVENT struct {
|
||||
DebugEventCode uint32
|
||||
ProcessId uint32
|
||||
ThreadId uint32
|
||||
_ uint32 // to align Union properly
|
||||
U [160]byte
|
||||
}
|
174
vendor/github.com/derekparker/delve/pkg/proc/native/threads.go
generated
vendored
174
vendor/github.com/derekparker/delve/pkg/proc/native/threads.go
generated
vendored
@ -1,174 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Thread represents a single thread in the traced process
|
||||
// ID represents the thread id or port, Process holds a reference to the
|
||||
// Process struct that contains info on the process as
|
||||
// a whole, and Status represents the last result of a `wait` call
|
||||
// on this thread.
|
||||
type Thread struct {
|
||||
ID int // Thread ID or mach port
|
||||
Status *WaitStatus // Status returned from last wait call
|
||||
CurrentBreakpoint proc.BreakpointState // Breakpoint thread is currently stopped at
|
||||
|
||||
dbp *Process
|
||||
singleStepping bool
|
||||
os *OSSpecificDetails
|
||||
common proc.CommonThread
|
||||
}
|
||||
|
||||
// Continue the execution of this thread.
|
||||
//
|
||||
// If we are currently at a breakpoint, we'll clear it
|
||||
// first and then resume execution. Thread will continue until
|
||||
// it hits a breakpoint or is signaled.
|
||||
func (t *Thread) Continue() error {
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check whether we are stopped at a breakpoint, and
|
||||
// if so, single step over it before continuing.
|
||||
if _, ok := t.dbp.FindBreakpoint(pc); ok {
|
||||
if err := t.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return t.resume()
|
||||
}
|
||||
|
||||
// StepInstruction steps a single instruction.
|
||||
//
|
||||
// Executes exactly one instruction and then returns.
|
||||
// If the thread is at a breakpoint, we first clear it,
|
||||
// execute the instruction, and then replace the breakpoint.
|
||||
// Otherwise we simply execute the next instruction.
|
||||
func (t *Thread) StepInstruction() (err error) {
|
||||
t.singleStepping = true
|
||||
defer func() {
|
||||
t.singleStepping = false
|
||||
}()
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bp, ok := t.dbp.FindBreakpoint(pc)
|
||||
if ok {
|
||||
// Clear the breakpoint so that we can continue execution.
|
||||
err = t.ClearBreakpoint(bp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Restore breakpoint now that we have passed it.
|
||||
defer func() {
|
||||
err = t.dbp.writeSoftwareBreakpoint(t, bp.Addr)
|
||||
}()
|
||||
}
|
||||
|
||||
err = t.singleStep()
|
||||
if err != nil {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("step failed: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location returns the threads location, including the file:line
|
||||
// of the corresponding source code, the function we're in
|
||||
// and the current instruction address.
|
||||
func (t *Thread) Location() (*proc.Location, error) {
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, l, fn := t.dbp.bi.PCToLine(pc)
|
||||
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
// Arch returns the architecture the binary is
|
||||
// compiled for and executing on.
|
||||
func (t *Thread) Arch() proc.Arch {
|
||||
return t.dbp.bi.Arch
|
||||
}
|
||||
|
||||
// BinInfo returns information on the binary.
|
||||
func (t *Thread) BinInfo() *proc.BinaryInfo {
|
||||
return t.dbp.bi
|
||||
}
|
||||
|
||||
// Common returns information common across Process
|
||||
// implementations.
|
||||
func (t *Thread) Common() *proc.CommonThread {
|
||||
return &t.common
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint sets the current breakpoint that this
|
||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
|
||||
func (t *Thread) SetCurrentBreakpoint() error {
|
||||
t.CurrentBreakpoint.Clear()
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bp, ok := t.dbp.FindBreakpoint(pc); ok {
|
||||
if err = t.SetPC(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
t.CurrentBreakpoint = bp.CheckCondition(t)
|
||||
if t.CurrentBreakpoint.Breakpoint != nil && t.CurrentBreakpoint.Active {
|
||||
if g, err := proc.GetG(t); err == nil {
|
||||
t.CurrentBreakpoint.HitCount[g.ID]++
|
||||
}
|
||||
t.CurrentBreakpoint.TotalHitCount++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Breakpoint returns the current breakpoint that is active
|
||||
// on this thread.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return t.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// ThreadID returns the ID of this thread.
|
||||
func (t *Thread) ThreadID() int {
|
||||
return t.ID
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the specified breakpoint.
|
||||
func (t *Thread) ClearBreakpoint(bp *proc.Breakpoint) error {
|
||||
if _, err := t.WriteMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
|
||||
return fmt.Errorf("could not clear breakpoint %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Registers obtains register values from the debugged process.
|
||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
return registers(t, floatingPoint)
|
||||
}
|
||||
|
||||
// RestoreRegisters will set the value of the CPU registers to those
|
||||
// passed in via 'savedRegs'.
|
||||
func (t *Thread) RestoreRegisters(savedRegs proc.Registers) error {
|
||||
return t.restoreRegisters(savedRegs)
|
||||
}
|
||||
|
||||
// PC returns the current program counter value for this thread.
|
||||
func (t *Thread) PC() (uint64, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return regs.PC(), nil
|
||||
}
|
179
vendor/github.com/derekparker/delve/pkg/proc/native/threads_darwin.c
generated
vendored
179
vendor/github.com/derekparker/delve/pkg/proc/native/threads_darwin.c
generated
vendored
@ -1,179 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "threads_darwin.h"
|
||||
|
||||
int
|
||||
write_memory(task_t task, mach_vm_address_t addr, void *d, mach_msg_type_number_t len) {
|
||||
kern_return_t kret;
|
||||
vm_region_submap_short_info_data_64_t info;
|
||||
mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
|
||||
mach_vm_size_t l = len;
|
||||
mach_port_t objname;
|
||||
|
||||
if (len == 1) l = 2;
|
||||
kret = mach_vm_region((vm_map_t)task, &(mach_vm_address_t){addr}, (mach_vm_size_t*)&l, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &objname);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
// Set permissions to enable writting to this memory location
|
||||
kret = mach_vm_protect(task, addr, len, FALSE, VM_PROT_WRITE|VM_PROT_COPY|VM_PROT_READ);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
kret = mach_vm_write((vm_map_t)task, addr, (vm_offset_t)d, len);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
// Restore virtual memory permissions
|
||||
kret = mach_vm_protect(task, addr, len, FALSE, info.protection);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
read_memory(task_t task, mach_vm_address_t addr, void *d, mach_msg_type_number_t len) {
|
||||
kern_return_t kret;
|
||||
pointer_t data;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = mach_vm_read((vm_map_t)task, addr, len, &data, &count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
memcpy(d, (void *)data, len);
|
||||
|
||||
kret = vm_deallocate(task, data, len);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_registers(mach_port_name_t task, x86_thread_state64_t *state) {
|
||||
kern_return_t kret;
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
// TODO(dp) - possible memory leak - vm_deallocate state
|
||||
return thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)state, &stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_fpu_registers(mach_port_name_t task, x86_float_state64_t *state) {
|
||||
kern_return_t kret;
|
||||
mach_msg_type_number_t stateCount = x86_FLOAT_STATE64_COUNT;
|
||||
return thread_get_state(task, x86_FLOAT_STATE64, (thread_state_t)state, &stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_identity(mach_port_name_t task, thread_identifier_info_data_t *idinfo) {
|
||||
mach_msg_type_number_t idinfoCount = THREAD_IDENTIFIER_INFO_COUNT;
|
||||
return thread_info(task, THREAD_IDENTIFIER_INFO, (thread_info_t)idinfo, &idinfoCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
set_registers(mach_port_name_t task, x86_thread_state64_t *state) {
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
return thread_set_state(task, x86_THREAD_STATE64, (thread_state_t)state, stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
set_pc(thread_act_t task, uint64_t pc) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t state;
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)&state, &stateCount);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
state.__rip = pc;
|
||||
|
||||
return thread_set_state(task, x86_THREAD_STATE64, (thread_state_t)&state, stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
single_step(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t regs;
|
||||
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, &count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Set trap bit in rflags
|
||||
regs.__rflags |= 0x100UL;
|
||||
|
||||
kret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return resume_thread(thread);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
resume_thread(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
for (int i = 0; i < info.suspend_count; i++) {
|
||||
kret = thread_resume(thread);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
}
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
clear_trap_flag(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t regs;
|
||||
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, &count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Clear trap bit in rflags
|
||||
regs.__rflags ^= 0x100UL;
|
||||
|
||||
return thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, count);
|
||||
}
|
||||
|
||||
int
|
||||
thread_blocked(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return info.suspend_count;
|
||||
}
|
||||
|
||||
int
|
||||
num_running_threads(task_t task) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
int i, n = 0;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) {
|
||||
return -kret;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
thread_act_t thread = list[i];
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
|
||||
if (kret == KERN_SUCCESS) {
|
||||
if (info.suspend_count == 0) {
|
||||
++n;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return -kret;
|
||||
|
||||
return n;
|
||||
}
|
153
vendor/github.com/derekparker/delve/pkg/proc/native/threads_darwin.go
generated
vendored
153
vendor/github.com/derekparker/delve/pkg/proc/native/threads_darwin.go
generated
vendored
@ -1,153 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
// #include "proc_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails holds information specific to the OSX/Darwin
|
||||
// operating system / kernel.
|
||||
type OSSpecificDetails struct {
|
||||
threadAct C.thread_act_t
|
||||
registers C.x86_thread_state64_t
|
||||
exists bool
|
||||
}
|
||||
|
||||
// ErrContinueThread is the error returned when a thread could not
|
||||
// be continued.
|
||||
var ErrContinueThread = fmt.Errorf("could not continue thread")
|
||||
|
||||
func (t *Thread) stop() (err error) {
|
||||
kret := C.thread_suspend(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
errStr := C.GoString(C.mach_error_string(C.mach_error_t(kret)))
|
||||
// check that the thread still exists before complaining
|
||||
err2 := t.dbp.updateThreadList()
|
||||
if err2 != nil {
|
||||
err = fmt.Errorf("could not suspend thread %d %s (additionally could not update thread list: %v)", t.ID, errStr, err2)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := t.dbp.threads[t.ID]; ok {
|
||||
err = fmt.Errorf("could not suspend thread %d %s", t.ID, errStr)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
kret := C.single_step(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not single step")
|
||||
}
|
||||
for {
|
||||
twthread, err := t.dbp.trapWait(t.dbp.pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if twthread.ID == t.ID {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
kret = C.clear_trap_flag(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not clear CPU trap flag")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
// TODO(dp) set flag for ptrace stops
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.dbp.pid, 0) })
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
kret := C.resume_thread(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return ErrContinueThread
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thread) Blocked() bool {
|
||||
// TODO(dp) cache the func pc to remove this lookup
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
pc := regs.PC()
|
||||
fn := t.BinInfo().PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.kevent", "runtime.mach_semaphore_wait", "runtime.usleep", "runtime.mach_semaphore_timedwait":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at
|
||||
// the operating system level.
|
||||
func (t *Thread) Stopped() bool {
|
||||
return C.thread_blocked(t.os.threadAct) > C.int(0)
|
||||
}
|
||||
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var (
|
||||
vmData = unsafe.Pointer(&data[0])
|
||||
vmAddr = C.mach_vm_address_t(addr)
|
||||
length = C.mach_msg_type_number_t(len(data))
|
||||
)
|
||||
if ret := C.write_memory(t.dbp.os.task, vmAddr, vmData, length); ret < 0 {
|
||||
return 0, fmt.Errorf("could not write memory")
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var (
|
||||
vmData = unsafe.Pointer(&buf[0])
|
||||
vmAddr = C.mach_vm_address_t(addr)
|
||||
length = C.mach_msg_type_number_t(len(buf))
|
||||
)
|
||||
|
||||
ret := C.read_memory(t.dbp.os.task, vmAddr, vmData, length)
|
||||
if ret < 0 {
|
||||
return 0, fmt.Errorf("could not read memory")
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters(sr proc.Registers) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
43
vendor/github.com/derekparker/delve/pkg/proc/native/threads_darwin.h
generated
vendored
43
vendor/github.com/derekparker/delve/pkg/proc/native/threads_darwin.h
generated
vendored
@ -1,43 +0,0 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/thread_info.h>
|
||||
|
||||
int
|
||||
write_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
|
||||
|
||||
int
|
||||
read_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
|
||||
|
||||
kern_return_t
|
||||
get_registers(mach_port_name_t, x86_thread_state64_t*);
|
||||
|
||||
kern_return_t
|
||||
get_fpu_registers(mach_port_name_t, x86_float_state64_t *);
|
||||
|
||||
kern_return_t
|
||||
set_pc(thread_act_t, uint64_t);
|
||||
|
||||
kern_return_t
|
||||
single_step(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
clear_trap_flag(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
resume_thread(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
set_registers(mach_port_name_t, x86_thread_state64_t*);
|
||||
|
||||
kern_return_t
|
||||
get_identity(mach_port_name_t, thread_identifier_info_data_t *);
|
||||
|
||||
int
|
||||
thread_blocked(thread_act_t thread);
|
||||
|
||||
int
|
||||
num_running_threads(task_t task);
|
132
vendor/github.com/derekparker/delve/pkg/proc/native/threads_linux.go
generated
vendored
132
vendor/github.com/derekparker/delve/pkg/proc/native/threads_linux.go
generated
vendored
@ -1,132 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails hold Linux specific
|
||||
// process details.
|
||||
type OSSpecificDetails struct {
|
||||
registers sys.PtraceRegs
|
||||
running bool
|
||||
}
|
||||
|
||||
func (t *Thread) stop() (err error) {
|
||||
err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("stop err %s on thread %d", err, t.ID)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at
|
||||
// the operating system level.
|
||||
func (t *Thread) Stopped() bool {
|
||||
state := status(t.ID, t.dbp.os.comm)
|
||||
return state == StatusTraceStop || state == StatusTraceStopT
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
return t.resumeWithSig(0)
|
||||
}
|
||||
|
||||
func (t *Thread) resumeWithSig(sig int) (err error) {
|
||||
t.os.running = true
|
||||
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.ID, sig) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() (err error) {
|
||||
for {
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.ID) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wpid, status, err := t.dbp.waitFast(t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (status == nil || status.Exited()) && wpid == t.dbp.pid {
|
||||
t.dbp.postExit()
|
||||
rs := 0
|
||||
if status != nil {
|
||||
rs = status.ExitStatus()
|
||||
}
|
||||
return proc.ErrProcessExited{Pid: t.dbp.pid, Status: rs}
|
||||
}
|
||||
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Thread) Blocked() bool {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
pc := regs.PC()
|
||||
fn := t.BinInfo().PCToFunc(pc)
|
||||
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
|
||||
sr := savedRegs.(*Regs)
|
||||
|
||||
var restoreRegistersErr error
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
restoreRegistersErr = sys.PtraceSetRegs(t.ID, sr.regs)
|
||||
if restoreRegistersErr != nil {
|
||||
return
|
||||
}
|
||||
if sr.fpregset.Xsave != nil {
|
||||
iov := sys.Iovec{Base: &sr.fpregset.Xsave[0], Len: uint64(len(sr.fpregset.Xsave))}
|
||||
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.fpregset.PtraceFpRegs)), 0, 0)
|
||||
return
|
||||
})
|
||||
if restoreRegistersErr == syscall.Errno(0) {
|
||||
restoreRegistersErr = nil
|
||||
}
|
||||
return restoreRegistersErr
|
||||
}
|
||||
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, addr, data) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
t.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(t.ID, addr, data) })
|
||||
if err == nil {
|
||||
n = len(data)
|
||||
}
|
||||
return
|
||||
}
|
158
vendor/github.com/derekparker/delve/pkg/proc/native/threads_windows.go
generated
vendored
158
vendor/github.com/derekparker/delve/pkg/proc/native/threads_windows.go
generated
vendored
@ -1,158 +0,0 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails holds information specific to the Windows
|
||||
// operating system / kernel.
|
||||
type OSSpecificDetails struct {
|
||||
hThread syscall.Handle
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
// Set the processor TRAP flag
|
||||
err := _GetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.EFlags |= 0x100
|
||||
|
||||
err = _SetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = _ResumeThread(t.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
var tid, exitCode int
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = t.dbp.waitForDebugEvent(waitBlocking | waitSuspendNewThreads)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tid == 0 {
|
||||
t.dbp.postExit()
|
||||
return proc.ErrProcessExited{Pid: t.dbp.pid, Status: exitCode}
|
||||
}
|
||||
|
||||
if t.dbp.os.breakThread == t.ID {
|
||||
break
|
||||
}
|
||||
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
|
||||
})
|
||||
}
|
||||
|
||||
_, err = _SuspendThread(t.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unset the processor TRAP flag
|
||||
err = _GetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.EFlags &= ^uint32(0x100)
|
||||
|
||||
return _SetThreadContext(t.os.hThread, context)
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
||||
//thread that we last broke on.
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Thread) Blocked() bool {
|
||||
// TODO: Probably incorrect - what are the runtime functions that
|
||||
// indicate blocking on Windows?
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
pc := regs.PC()
|
||||
fn := t.BinInfo().PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.kevent", "runtime.usleep":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at the operating system
|
||||
// level. On windows this always returns true.
|
||||
func (t *Thread) Stopped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var count uintptr
|
||||
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
var ErrShortRead = errors.New("short read")
|
||||
|
||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var count uintptr
|
||||
err := _ReadProcessMemory(t.dbp.os.hProcess, addr, &buf[0], uintptr(len(buf)), &count)
|
||||
if err == nil && count != uintptr(len(buf)) {
|
||||
err = ErrShortRead
|
||||
}
|
||||
return int(count), err
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
|
||||
return _SetThreadContext(t.os.hThread, savedRegs.(*Regs).context)
|
||||
}
|
181
vendor/github.com/derekparker/delve/pkg/proc/native/zsyscall_windows.go
generated
vendored
181
vendor/github.com/derekparker/delve/pkg/proc/native/zsyscall_windows.go
generated
vendored
@ -1,181 +0,0 @@
|
||||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread")
|
||||
procGetThreadContext = modkernel32.NewProc("GetThreadContext")
|
||||
procSetThreadContext = modkernel32.NewProc("SetThreadContext")
|
||||
procSuspendThread = modkernel32.NewProc("SuspendThread")
|
||||
procResumeThread = modkernel32.NewProc("ResumeThread")
|
||||
procContinueDebugEvent = modkernel32.NewProc("ContinueDebugEvent")
|
||||
procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory")
|
||||
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
||||
procDebugBreakProcess = modkernel32.NewProc("DebugBreakProcess")
|
||||
procWaitForDebugEvent = modkernel32.NewProc("WaitForDebugEvent")
|
||||
procDebugActiveProcess = modkernel32.NewProc("DebugActiveProcess")
|
||||
procDebugActiveProcessStop = modkernel32.NewProc("DebugActiveProcessStop")
|
||||
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
|
||||
)
|
||||
|
||||
func _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) {
|
||||
r0, _, _ := syscall.Syscall6(procNtQueryInformationThread.Addr(), 5, uintptr(threadHandle), uintptr(infoclass), uintptr(info), uintptr(infolen), uintptr(unsafe.Pointer(retlen)), 0)
|
||||
status = _NTSTATUS(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procSuspendThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||
prevsuspcount = uint32(r0)
|
||||
if prevsuspcount == 0xffffffff {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procResumeThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||
prevsuspcount = uint32(r0)
|
||||
if prevsuspcount == 0xffffffff {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procContinueDebugEvent.Addr(), 3, uintptr(processid), uintptr(threadid), uintptr(continuestatus))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procWriteProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(byteswritten)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(bytesread)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugBreakProcess(process syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugBreakProcess.Addr(), 1, uintptr(process), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procWaitForDebugEvent.Addr(), 2, uintptr(unsafe.Pointer(debugevent)), uintptr(milliseconds), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugActiveProcess(processid uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugActiveProcess.Addr(), 1, uintptr(processid), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugActiveProcessStop(processid uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugActiveProcessStop.Addr(), 1, uintptr(processid), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procQueryFullProcessImageNameW.Addr(), 4, uintptr(process), uintptr(flags), uintptr(unsafe.Pointer(exename)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
658
vendor/github.com/derekparker/delve/pkg/proc/proc.go
generated
vendored
658
vendor/github.com/derekparker/delve/pkg/proc/proc.go
generated
vendored
@ -1,658 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrNotExecutable is returned after attempting to execute a non-executable file
|
||||
// to begin a debug session.
|
||||
var ErrNotExecutable = errors.New("not an executable file")
|
||||
|
||||
// ErrNotRecorded is returned when an action is requested that is
|
||||
// only possible on recorded (traced) programs.
|
||||
var ErrNotRecorded = errors.New("not a recording")
|
||||
|
||||
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
|
||||
const UnrecoveredPanic = "unrecovered-panic"
|
||||
|
||||
// ErrProcessExited indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
type ErrProcessExited struct {
|
||||
Pid int
|
||||
Status int
|
||||
}
|
||||
|
||||
func (pe ErrProcessExited) Error() string {
|
||||
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
// ProcessDetachedError indicates that we detached from the target process.
|
||||
type ProcessDetachedError struct {
|
||||
}
|
||||
|
||||
func (pe ProcessDetachedError) Error() string {
|
||||
return "detached from the process"
|
||||
}
|
||||
|
||||
// FindFileLocation returns the PC for a given file:line.
|
||||
// Assumes that `file` is normalized to lower case and '/' on Windows.
|
||||
func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
||||
pc, fn, err := p.BinInfo().LineToPC(fileName, lineno)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if fn.Entry == pc {
|
||||
pc, _ = FirstPCAfterPrologue(p, fn, true)
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// ErrFunctionNotFound is returned when failing to find the
|
||||
// function named 'FuncName' within the binary.
|
||||
type ErrFunctionNotFound struct {
|
||||
FuncName string
|
||||
}
|
||||
|
||||
func (err *ErrFunctionNotFound) Error() string {
|
||||
return fmt.Sprintf("Could not find function %s\n", err.FuncName)
|
||||
}
|
||||
|
||||
// FindFunctionLocation finds address of a function's line
|
||||
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
|
||||
// If lineOffset is passed FindFunctionLocation will return the address of that line
|
||||
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
|
||||
// Note that setting breakpoints at that address will cause surprising behavior:
|
||||
// https://github.com/derekparker/delve/issues/170
|
||||
func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
bi := p.BinInfo()
|
||||
origfn := bi.LookupFunc[funcName]
|
||||
if origfn == nil {
|
||||
return 0, &ErrFunctionNotFound{funcName}
|
||||
}
|
||||
|
||||
if firstLine {
|
||||
return FirstPCAfterPrologue(p, origfn, false)
|
||||
} else if lineOffset > 0 {
|
||||
filename, lineno := origfn.cu.lineInfo.PCToLine(origfn.Entry, origfn.Entry)
|
||||
breakAddr, _, err := bi.LineToPC(filename, lineno+lineOffset)
|
||||
return breakAddr, err
|
||||
}
|
||||
|
||||
return origfn.Entry, nil
|
||||
}
|
||||
|
||||
// Next continues execution until the next source line.
|
||||
func Next(dbp Process) (err error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
if err = next(dbp, false, false); err != nil {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// Continue continues execution of the debugged
|
||||
// process. It will continue until it hits a breakpoint
|
||||
// or is otherwise stopped.
|
||||
func Continue(dbp Process) error {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, thread := range dbp.ThreadList() {
|
||||
thread.Common().returnValues = nil
|
||||
}
|
||||
dbp.CheckAndClearManualStopRequest()
|
||||
defer func() {
|
||||
// Make sure we clear internal breakpoints if we simultaneously receive a
|
||||
// manual stop request and hit a breakpoint.
|
||||
if dbp.CheckAndClearManualStopRequest() {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
if dbp.CheckAndClearManualStopRequest() {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return nil
|
||||
}
|
||||
trapthread, err := dbp.ContinueOnce()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
threads := dbp.ThreadList()
|
||||
|
||||
if err := pickCurrentThread(dbp, trapthread, threads); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curthread := dbp.CurrentThread()
|
||||
curbp := curthread.Breakpoint()
|
||||
|
||||
switch {
|
||||
case curbp.Breakpoint == nil:
|
||||
// runtime.Breakpoint, manual stop or debugCallV1-related stop
|
||||
recorded, _ := dbp.Recorded()
|
||||
if recorded {
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
|
||||
loc, err := curthread.Location()
|
||||
if err != nil || loc.Fn == nil {
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
|
||||
switch {
|
||||
case loc.Fn.Name == "runtime.breakpoint":
|
||||
// Single-step current thread until we exit runtime.breakpoint and
|
||||
// runtime.Breakpoint.
|
||||
// On go < 1.8 it was sufficient to single-step twice on go1.8 a change
|
||||
// to the compiler requires 4 steps.
|
||||
if err := stepInstructionOut(dbp, curthread, "runtime.breakpoint", "runtime.Breakpoint"); err != nil {
|
||||
return err
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
case strings.HasPrefix(loc.Fn.Name, debugCallFunctionNamePrefix1) || strings.HasPrefix(loc.Fn.Name, debugCallFunctionNamePrefix2):
|
||||
fncall := &dbp.Common().fncallState
|
||||
if !fncall.inProgress {
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
fncall.step(dbp)
|
||||
// only stop execution if the function call finished
|
||||
if fncall.finished {
|
||||
fncall.inProgress = false
|
||||
if fncall.err != nil {
|
||||
return fncall.err
|
||||
}
|
||||
curthread.Common().returnValues = fncall.returnValues()
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
default:
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
case curbp.Active && curbp.Internal:
|
||||
if curbp.Kind == StepBreakpoint {
|
||||
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
||||
if err := conditionErrors(threads); err != nil {
|
||||
return err
|
||||
}
|
||||
regs, err := curthread.Registers(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pc := regs.PC()
|
||||
text, err := disassemble(curthread, regs, dbp.Breakpoints(), dbp.BinInfo(), pc, pc+maxInstructionLength, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// here we either set a breakpoint into the destination of the CALL
|
||||
// instruction or we determined that the called function is hidden,
|
||||
// either way we need to resume execution
|
||||
if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(curthread)
|
||||
if err := dbp.ClearInternalBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
case curbp.Active:
|
||||
onNextGoroutine, err := onNextGoroutine(curthread, dbp.Breakpoints())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if onNextGoroutine {
|
||||
err := dbp.ClearInternalBreakpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if curbp.Name == UnrecoveredPanic {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
default:
|
||||
// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func conditionErrors(threads []Thread) error {
|
||||
var condErr error
|
||||
for _, th := range threads {
|
||||
if bp := th.Breakpoint(); bp.Breakpoint != nil && bp.CondError != nil {
|
||||
if condErr == nil {
|
||||
condErr = bp.CondError
|
||||
} else {
|
||||
return fmt.Errorf("multiple errors evaluating conditions")
|
||||
}
|
||||
}
|
||||
}
|
||||
return condErr
|
||||
}
|
||||
|
||||
// pick a new dbp.currentThread, with the following priority:
|
||||
// - a thread with onTriggeredInternalBreakpoint() == true
|
||||
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
|
||||
// - trapthread
|
||||
func pickCurrentThread(dbp Process, trapthread Thread, threads []Thread) error {
|
||||
for _, th := range threads {
|
||||
if bp := th.Breakpoint(); bp.Active && bp.Internal {
|
||||
return dbp.SwitchThread(th.ThreadID())
|
||||
}
|
||||
}
|
||||
if bp := trapthread.Breakpoint(); bp.Active {
|
||||
return dbp.SwitchThread(trapthread.ThreadID())
|
||||
}
|
||||
for _, th := range threads {
|
||||
if bp := th.Breakpoint(); bp.Active {
|
||||
return dbp.SwitchThread(th.ThreadID())
|
||||
}
|
||||
}
|
||||
return dbp.SwitchThread(trapthread.ThreadID())
|
||||
}
|
||||
|
||||
// stepInstructionOut repeatedly calls StepInstruction until the current
|
||||
// function is neither fnname1 or fnname2.
|
||||
// This function is used to step out of runtime.Breakpoint as well as
|
||||
// runtime.debugCallV1.
|
||||
func stepInstructionOut(dbp Process, curthread Thread, fnname1, fnname2 string) error {
|
||||
for {
|
||||
if err := curthread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := curthread.Location()
|
||||
if err != nil || loc.Fn == nil || (loc.Fn.Name != fnname1 && loc.Fn.Name != fnname2) {
|
||||
if g := dbp.SelectedGoroutine(); g != nil {
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step will continue until another source line is reached.
|
||||
// Will step into functions.
|
||||
func Step(dbp Process) (err error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
if err = next(dbp, true, false); err != nil {
|
||||
switch err.(type) {
|
||||
case ErrThreadBlocked: // Noop
|
||||
default:
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// SameGoroutineCondition returns an expression that evaluates to true when
|
||||
// the current goroutine is g.
|
||||
func SameGoroutineCondition(g *G) ast.Expr {
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return &ast.BinaryExpr{
|
||||
Op: token.EQL,
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "runtime"},
|
||||
Sel: &ast.Ident{Name: "curg"},
|
||||
},
|
||||
Sel: &ast.Ident{Name: "goid"},
|
||||
},
|
||||
Y: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(g.ID)},
|
||||
}
|
||||
}
|
||||
|
||||
func frameoffCondition(frameoff int64) ast.Expr {
|
||||
return &ast.BinaryExpr{
|
||||
Op: token.EQL,
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "runtime"},
|
||||
Sel: &ast.Ident{Name: "frameoff"},
|
||||
},
|
||||
Y: &ast.BasicLit{Kind: token.INT, Value: strconv.FormatInt(frameoff, 10)},
|
||||
}
|
||||
}
|
||||
|
||||
func andFrameoffCondition(cond ast.Expr, frameoff int64) ast.Expr {
|
||||
if cond == nil {
|
||||
return nil
|
||||
}
|
||||
return &ast.BinaryExpr{
|
||||
Op: token.LAND,
|
||||
X: cond,
|
||||
Y: frameoffCondition(frameoff),
|
||||
}
|
||||
}
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
// function currently being executed or a deferred function is executed
|
||||
func StepOut(dbp Process) error {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
selg := dbp.SelectedGoroutine()
|
||||
curthread := dbp.CurrentThread()
|
||||
|
||||
topframe, retframe, err := topframe(selg, curthread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
}()
|
||||
|
||||
if topframe.Inlined {
|
||||
if err := next(dbp, false, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success = true
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
sameGCond := SameGoroutineCondition(selg)
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.FrameOffset())
|
||||
|
||||
var deferpc uint64
|
||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||
if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
|
||||
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond)
|
||||
if err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil {
|
||||
// For StepOut we do not want to step into the deferred function
|
||||
// when it's called by runtime.deferreturn so we do not populate
|
||||
// DeferReturns.
|
||||
bp.DeferReturns = []uint64{}
|
||||
}
|
||||
}
|
||||
|
||||
if topframe.Ret == 0 && deferpc == 0 {
|
||||
return errors.New("nothing to stepout to")
|
||||
}
|
||||
|
||||
if topframe.Ret != 0 {
|
||||
bp, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond)
|
||||
if err != nil {
|
||||
if _, isexists := err.(BreakpointExistsError); !isexists {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil {
|
||||
configureReturnBreakpoint(dbp.BinInfo(), bp, &topframe, retFrameCond)
|
||||
}
|
||||
}
|
||||
|
||||
if bp := curthread.Breakpoint(); bp.Breakpoint == nil {
|
||||
curthread.SetCurrentBreakpoint()
|
||||
}
|
||||
|
||||
success = true
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// GoroutinesInfo returns an array of G structures representing the information
|
||||
// Delve cares about from the internal runtime G structure.
|
||||
func GoroutinesInfo(dbp Process) ([]*G, error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dbp.Common().allGCache != nil {
|
||||
return dbp.Common().allGCache, nil
|
||||
}
|
||||
|
||||
var (
|
||||
threadg = map[int]*G{}
|
||||
allg []*G
|
||||
rdr = dbp.BinInfo().DwarfReader()
|
||||
)
|
||||
|
||||
threads := dbp.ThreadList()
|
||||
for _, th := range threads {
|
||||
if th.Blocked() {
|
||||
continue
|
||||
}
|
||||
g, _ := GetG(th)
|
||||
if g != nil {
|
||||
threadg[g.ID] = g
|
||||
}
|
||||
}
|
||||
|
||||
addr, err := rdr.AddrFor("runtime.allglen", dbp.BinInfo().staticBase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allglenBytes := make([]byte, 8)
|
||||
_, err = dbp.CurrentThread().ReadMemory(allglenBytes, uintptr(addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
||||
|
||||
rdr.Seek(0)
|
||||
allgentryaddr, err := rdr.AddrFor("runtime.allgs", dbp.BinInfo().staticBase)
|
||||
if err != nil {
|
||||
// try old name (pre Go 1.6)
|
||||
allgentryaddr, err = rdr.AddrFor("runtime.allg", dbp.BinInfo().staticBase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
|
||||
_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||
|
||||
for i := uint64(0); i < allglen; i++ {
|
||||
gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true)
|
||||
if err != nil {
|
||||
allg = append(allg, &G{Unreadable: err})
|
||||
continue
|
||||
}
|
||||
g, err := gvar.parseG()
|
||||
if err != nil {
|
||||
allg = append(allg, &G{Unreadable: err})
|
||||
continue
|
||||
}
|
||||
if thg, allocated := threadg[g.ID]; allocated {
|
||||
loc, err := thg.Thread.Location()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Thread = thg.Thread
|
||||
// Prefer actual thread location information.
|
||||
g.CurrentLoc = *loc
|
||||
g.SystemStack = thg.SystemStack
|
||||
}
|
||||
if g.Status != Gdead {
|
||||
allg = append(allg, g)
|
||||
}
|
||||
}
|
||||
dbp.Common().allGCache = allg
|
||||
|
||||
return allg, nil
|
||||
}
|
||||
|
||||
// FindGoroutine returns a G struct representing the goroutine
|
||||
// specified by `gid`.
|
||||
func FindGoroutine(dbp Process, gid int) (*G, error) {
|
||||
if gid == -1 {
|
||||
return dbp.SelectedGoroutine(), nil
|
||||
}
|
||||
|
||||
gs, err := GoroutinesInfo(dbp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range gs {
|
||||
if gs[i].ID == gid {
|
||||
if gs[i].Unreadable != nil {
|
||||
return nil, gs[i].Unreadable
|
||||
}
|
||||
return gs[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown goroutine %d", gid)
|
||||
}
|
||||
|
||||
// ConvertEvalScope returns a new EvalScope in the context of the
|
||||
// specified goroutine ID and stack frame.
|
||||
func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct := dbp.CurrentThread()
|
||||
g, err := FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g == nil {
|
||||
return ThreadScope(ct)
|
||||
}
|
||||
|
||||
var thread MemoryReadWriter
|
||||
if g.Thread == nil {
|
||||
thread = ct
|
||||
} else {
|
||||
thread = g.Thread
|
||||
}
|
||||
|
||||
locs, err := g.Stacktrace(frame+1, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if frame >= len(locs) {
|
||||
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
||||
}
|
||||
|
||||
return FrameToScope(dbp.BinInfo(), thread, g, locs[frame:]...), nil
|
||||
}
|
||||
|
||||
// FrameToScope returns a new EvalScope for frames[0].
|
||||
// If frames has at least two elements all memory between
|
||||
// frames[0].Regs.SP() and frames[1].Regs.CFA will be cached.
|
||||
// Otherwise all memory between frames[0].Regs.SP() and frames[0].Regs.CFA
|
||||
// will be cached.
|
||||
func FrameToScope(bi *BinaryInfo, thread MemoryReadWriter, g *G, frames ...Stackframe) *EvalScope {
|
||||
var gvar *Variable
|
||||
if g != nil {
|
||||
gvar = g.variable
|
||||
}
|
||||
|
||||
// Creates a cacheMem that will preload the entire stack frame the first
|
||||
// time any local variable is read.
|
||||
// Remember that the stack grows downward in memory.
|
||||
minaddr := frames[0].Regs.SP()
|
||||
var maxaddr uint64
|
||||
if len(frames) > 1 && frames[0].SystemStack == frames[1].SystemStack {
|
||||
maxaddr = uint64(frames[1].Regs.CFA)
|
||||
} else {
|
||||
maxaddr = uint64(frames[0].Regs.CFA)
|
||||
}
|
||||
if maxaddr > minaddr && maxaddr-minaddr < maxFramePrefetchSize {
|
||||
thread = cacheMemory(thread, uintptr(minaddr), int(maxaddr-minaddr))
|
||||
}
|
||||
|
||||
s := &EvalScope{Location: frames[0].Call, Regs: frames[0].Regs, Mem: thread, Gvar: gvar, BinInfo: bi, frameOffset: frames[0].FrameOffset()}
|
||||
s.PC = frames[0].lastpc
|
||||
return s
|
||||
}
|
||||
|
||||
// CreateUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint.
|
||||
// This function is meant to be called by implementations of the Process interface.
|
||||
func CreateUnrecoveredPanicBreakpoint(p Process, writeBreakpoint writeBreakpointFn, breakpoints *BreakpointMap) {
|
||||
panicpc, err := FindFunctionLocation(p, "runtime.startpanic", true, 0)
|
||||
if _, isFnNotFound := err.(*ErrFunctionNotFound); isFnNotFound {
|
||||
panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
|
||||
}
|
||||
if err == nil {
|
||||
bp, err := breakpoints.SetWithID(-1, panicpc, writeBreakpoint)
|
||||
if err == nil {
|
||||
bp.Name = UnrecoveredPanic
|
||||
bp.Variables = []string{"runtime.curg._panic.arg"}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FirstPCAfterPrologue returns the address of the first
|
||||
// instruction after the prologue for function fn.
|
||||
// If sameline is set FirstPCAfterPrologue will always return an
|
||||
// address associated with the same line as fn.Entry.
|
||||
func FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error) {
|
||||
pc, _, line, ok := fn.cu.lineInfo.PrologueEndPC(fn.Entry, fn.End)
|
||||
if ok {
|
||||
if !sameline {
|
||||
return pc, nil
|
||||
}
|
||||
_, entryLine := fn.cu.lineInfo.PCToLine(fn.Entry, fn.Entry)
|
||||
if entryLine == line {
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
|
||||
pc, err := firstPCAfterPrologueDisassembly(p, fn, sameline)
|
||||
if err != nil {
|
||||
return fn.Entry, err
|
||||
}
|
||||
|
||||
if pc == fn.Entry {
|
||||
// Look for the first instruction with the stmt flag set, so that setting a
|
||||
// breakpoint with file:line and with the function name always result on
|
||||
// the same instruction being selected.
|
||||
entryFile, entryLine := fn.cu.lineInfo.PCToLine(fn.Entry, fn.Entry)
|
||||
if pc, _, err := p.BinInfo().LineToPC(entryFile, entryLine); err == nil && pc >= fn.Entry && pc < fn.End {
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
348
vendor/github.com/derekparker/delve/pkg/proc/registers.go
generated
vendored
348
vendor/github.com/derekparker/delve/pkg/proc/registers.go
generated
vendored
@ -1,348 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Registers is an interface for a generic register type. The
|
||||
// interface encapsulates the generic values / actions
|
||||
// we need independent of arch. The concrete register types
|
||||
// will be different depending on OS/Arch.
|
||||
type Registers interface {
|
||||
PC() uint64
|
||||
SP() uint64
|
||||
BP() uint64
|
||||
CX() uint64
|
||||
TLS() uint64
|
||||
// GAddr returns the address of the G variable if it is known, 0 and false otherwise
|
||||
GAddr() (uint64, bool)
|
||||
Get(int) (uint64, error)
|
||||
Slice() []Register
|
||||
// Copy returns a copy of the registers that is guaranteed not to change
|
||||
// when the registers of the associated thread change.
|
||||
Copy() Registers
|
||||
}
|
||||
|
||||
// Register represents a CPU register.
|
||||
type Register struct {
|
||||
Name string
|
||||
Bytes []byte
|
||||
Value string
|
||||
}
|
||||
|
||||
// AppendWordReg appends a word (16 bit) register to regs.
|
||||
func AppendWordReg(regs []Register, name string, value uint16) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#04x", value)})
|
||||
}
|
||||
|
||||
// AppendDwordReg appends a double word (32 bit) register to regs.
|
||||
func AppendDwordReg(regs []Register, name string, value uint32) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#08x", value)})
|
||||
}
|
||||
|
||||
// AppendQwordReg appends a quad word (64 bit) register to regs.
|
||||
func AppendQwordReg(regs []Register, name string, value uint64) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#016x", value)})
|
||||
}
|
||||
|
||||
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes()[:size], descr.Describe(value, size)})
|
||||
}
|
||||
|
||||
// AppendEflagReg appends EFLAG register to regs.
|
||||
func AppendEflagReg(regs []Register, name string, value uint64) []Register {
|
||||
return appendFlagReg(regs, name, value, eflagsDescription, 64)
|
||||
}
|
||||
|
||||
// AppendMxcsrReg appends MXCSR register to regs.
|
||||
func AppendMxcsrReg(regs []Register, name string, value uint64) []Register {
|
||||
return appendFlagReg(regs, name, value, mxcsrDescription, 32)
|
||||
}
|
||||
|
||||
// AppendX87Reg appends a 80 bit float register to regs.
|
||||
func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register {
|
||||
var f float64
|
||||
fset := false
|
||||
|
||||
const (
|
||||
_SIGNBIT = 1 << 15
|
||||
_EXP_BIAS = (1 << 14) - 1 // 2^(n-1) - 1 = 16383
|
||||
_SPECIALEXP = (1 << 15) - 1 // all bits set
|
||||
_HIGHBIT = 1 << 63
|
||||
_QUIETBIT = 1 << 62
|
||||
)
|
||||
|
||||
sign := 1.0
|
||||
if exponent&_SIGNBIT != 0 {
|
||||
sign = -1.0
|
||||
}
|
||||
exponent &= ^uint16(_SIGNBIT)
|
||||
|
||||
NaN := math.NaN()
|
||||
Inf := math.Inf(+1)
|
||||
|
||||
switch exponent {
|
||||
case 0:
|
||||
switch {
|
||||
case mantissa == 0:
|
||||
f = sign * 0.0
|
||||
fset = true
|
||||
case mantissa&_HIGHBIT != 0:
|
||||
f = NaN
|
||||
fset = true
|
||||
}
|
||||
case _SPECIALEXP:
|
||||
switch {
|
||||
case mantissa&_HIGHBIT == 0:
|
||||
f = sign * Inf
|
||||
fset = true
|
||||
default:
|
||||
f = NaN // signaling NaN
|
||||
fset = true
|
||||
}
|
||||
default:
|
||||
if mantissa&_HIGHBIT == 0 {
|
||||
f = NaN
|
||||
fset = true
|
||||
}
|
||||
}
|
||||
|
||||
if !fset {
|
||||
significand := float64(mantissa) / (1 << 63)
|
||||
f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS))
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, exponent)
|
||||
binary.Write(&buf, binary.LittleEndian, mantissa)
|
||||
|
||||
return append(regs, Register{fmt.Sprintf("ST(%d)", index), buf.Bytes(), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
|
||||
}
|
||||
|
||||
// AppendSSEReg appends a 256 bit SSE register to regs.
|
||||
func AppendSSEReg(regs []Register, name string, xmm []byte) []Register {
|
||||
buf := bytes.NewReader(xmm)
|
||||
|
||||
var out bytes.Buffer
|
||||
var vi [16]uint8
|
||||
for i := range vi {
|
||||
binary.Read(buf, binary.LittleEndian, &vi[i])
|
||||
}
|
||||
|
||||
fmt.Fprintf(&out, "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8], vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0])
|
||||
|
||||
fmt.Fprintf(&out, "\tv2_int={ %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x }", vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0], vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8])
|
||||
|
||||
fmt.Fprintf(&out, "\tv4_int={ %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x }", vi[3], vi[2], vi[1], vi[0], vi[7], vi[6], vi[5], vi[4], vi[11], vi[10], vi[9], vi[8], vi[15], vi[14], vi[13], vi[12])
|
||||
|
||||
fmt.Fprintf(&out, "\tv8_int={ %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x }", vi[1], vi[0], vi[3], vi[2], vi[5], vi[4], vi[7], vi[6], vi[9], vi[8], vi[11], vi[10], vi[13], vi[12], vi[15], vi[14])
|
||||
|
||||
fmt.Fprintf(&out, "\tv16_int={ %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x }", vi[0], vi[1], vi[2], vi[3], vi[4], vi[5], vi[6], vi[7], vi[8], vi[9], vi[10], vi[11], vi[12], vi[13], vi[14], vi[15])
|
||||
|
||||
buf.Seek(0, os.SEEK_SET)
|
||||
var v2 [2]float64
|
||||
for i := range v2 {
|
||||
binary.Read(buf, binary.LittleEndian, &v2[i])
|
||||
}
|
||||
fmt.Fprintf(&out, "\tv2_float={ %g %g }", v2[0], v2[1])
|
||||
|
||||
buf.Seek(0, os.SEEK_SET)
|
||||
var v4 [4]float32
|
||||
for i := range v4 {
|
||||
binary.Read(buf, binary.LittleEndian, &v4[i])
|
||||
}
|
||||
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
||||
|
||||
return append(regs, Register{name, xmm, out.String()})
|
||||
}
|
||||
|
||||
// ErrUnknownRegister is returned when the value of an unknown
|
||||
// register is requested.
|
||||
var ErrUnknownRegister = errors.New("unknown register")
|
||||
|
||||
type flagRegisterDescr []flagDescr
|
||||
type flagDescr struct {
|
||||
name string
|
||||
mask uint64
|
||||
}
|
||||
|
||||
var mxcsrDescription flagRegisterDescr = []flagDescr{
|
||||
{"FZ", 1 << 15},
|
||||
{"RZ/RN", 1<<14 | 1<<13},
|
||||
{"PM", 1 << 12},
|
||||
{"UM", 1 << 11},
|
||||
{"OM", 1 << 10},
|
||||
{"ZM", 1 << 9},
|
||||
{"DM", 1 << 8},
|
||||
{"IM", 1 << 7},
|
||||
{"DAZ", 1 << 6},
|
||||
{"PE", 1 << 5},
|
||||
{"UE", 1 << 4},
|
||||
{"OE", 1 << 3},
|
||||
{"ZE", 1 << 2},
|
||||
{"DE", 1 << 1},
|
||||
{"IE", 1 << 0},
|
||||
}
|
||||
|
||||
var eflagsDescription flagRegisterDescr = []flagDescr{
|
||||
{"CF", 1 << 0},
|
||||
{"", 1 << 1},
|
||||
{"PF", 1 << 2},
|
||||
{"AF", 1 << 4},
|
||||
{"ZF", 1 << 6},
|
||||
{"SF", 1 << 7},
|
||||
{"TF", 1 << 8},
|
||||
{"IF", 1 << 9},
|
||||
{"DF", 1 << 10},
|
||||
{"OF", 1 << 11},
|
||||
{"IOPL", 1<<12 | 1<<13},
|
||||
{"NT", 1 << 14},
|
||||
{"RF", 1 << 16},
|
||||
{"VM", 1 << 17},
|
||||
{"AC", 1 << 18},
|
||||
{"VIF", 1 << 19},
|
||||
{"VIP", 1 << 20},
|
||||
{"ID", 1 << 21},
|
||||
}
|
||||
|
||||
func (descr flagRegisterDescr) Mask() uint64 {
|
||||
var r uint64
|
||||
for _, f := range descr {
|
||||
r = r | f.mask
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
|
||||
var r []string
|
||||
for _, f := range descr {
|
||||
if f.name == "" {
|
||||
continue
|
||||
}
|
||||
// rbm is f.mask with only the right-most bit set:
|
||||
// 0001 1100 -> 0000 0100
|
||||
rbm := f.mask & -f.mask
|
||||
if rbm == f.mask {
|
||||
if reg&f.mask != 0 {
|
||||
r = append(r, f.name)
|
||||
}
|
||||
} else {
|
||||
x := (reg & f.mask) >> uint64(math.Log2(float64(rbm)))
|
||||
r = append(r, fmt.Sprintf("%s=%x", f.name, x))
|
||||
}
|
||||
}
|
||||
if reg & ^descr.Mask() != 0 {
|
||||
r = append(r, fmt.Sprintf("unknown_flags=%x", reg&^descr.Mask()))
|
||||
}
|
||||
return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
|
||||
}
|
||||
|
||||
// PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
|
||||
type PtraceFpRegs struct {
|
||||
Cwd uint16
|
||||
Swd uint16
|
||||
Ftw uint16
|
||||
Fop uint16
|
||||
Rip uint64
|
||||
Rdp uint64
|
||||
Mxcsr uint32
|
||||
MxcrMask uint32
|
||||
StSpace [32]uint32
|
||||
XmmSpace [256]byte
|
||||
Padding [24]uint32
|
||||
}
|
||||
|
||||
// LinuxX86Xstate represents amd64 XSAVE area. See Section 13.1 (and
|
||||
// following) of Intel® 64 and IA-32 Architectures Software Developer’s
|
||||
// Manual, Volume 1: Basic Architecture.
|
||||
type LinuxX86Xstate struct {
|
||||
PtraceFpRegs
|
||||
Xsave []byte // raw xsave area
|
||||
AvxState bool // contains AVX state
|
||||
YmmSpace [256]byte
|
||||
}
|
||||
|
||||
// Decode decodes an XSAVE area to a list of name/value pairs of registers.
|
||||
func (xsave *LinuxX86Xstate) Decode() (regs []Register) {
|
||||
// x87 registers
|
||||
regs = AppendWordReg(regs, "CW", xsave.Cwd)
|
||||
regs = AppendWordReg(regs, "SW", xsave.Swd)
|
||||
regs = AppendWordReg(regs, "TW", xsave.Ftw)
|
||||
regs = AppendWordReg(regs, "FOP", xsave.Fop)
|
||||
regs = AppendQwordReg(regs, "FIP", xsave.Rip)
|
||||
regs = AppendQwordReg(regs, "FDP", xsave.Rdp)
|
||||
|
||||
for i := 0; i < len(xsave.StSpace); i += 4 {
|
||||
regs = AppendX87Reg(regs, i/4, uint16(xsave.StSpace[i+2]), uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i]))
|
||||
}
|
||||
|
||||
// SSE registers
|
||||
regs = AppendMxcsrReg(regs, "MXCSR", uint64(xsave.Mxcsr))
|
||||
regs = AppendDwordReg(regs, "MXCSR_MASK", xsave.MxcrMask)
|
||||
|
||||
for i := 0; i < len(xsave.XmmSpace); i += 16 {
|
||||
regs = AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16])
|
||||
if xsave.AvxState {
|
||||
regs = AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), xsave.YmmSpace[i:i+16])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
_XSAVE_HEADER_START = 512
|
||||
_XSAVE_HEADER_LEN = 64
|
||||
_XSAVE_EXTENDED_REGION_START = 576
|
||||
_XSAVE_SSE_REGION_LEN = 416
|
||||
)
|
||||
|
||||
// LinuxX86XstateRead reads a byte array containing an XSAVE area into regset.
|
||||
// If readLegacy is true regset.PtraceFpRegs will be filled with the
|
||||
// contents of the legacy region of the XSAVE area.
|
||||
// See Section 13.1 (and following) of Intel® 64 and IA-32 Architectures
|
||||
// Software Developer’s Manual, Volume 1: Basic Architecture.
|
||||
func LinuxX86XstateRead(xstateargs []byte, readLegacy bool, regset *LinuxX86Xstate) error {
|
||||
if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= len(xstateargs) {
|
||||
return nil
|
||||
}
|
||||
if readLegacy {
|
||||
rdr := bytes.NewReader(xstateargs[:_XSAVE_HEADER_START])
|
||||
if err := binary.Read(rdr, binary.LittleEndian, ®set.PtraceFpRegs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
|
||||
xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
|
||||
xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
|
||||
|
||||
if xcomp_bv&(1<<63) != 0 {
|
||||
// compact format not supported
|
||||
return nil
|
||||
}
|
||||
|
||||
if xstate_bv&(1<<2) == 0 {
|
||||
// AVX state not present
|
||||
return nil
|
||||
}
|
||||
|
||||
avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:]
|
||||
regset.AvxState = true
|
||||
copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
|
||||
|
||||
return nil
|
||||
}
|
90
vendor/github.com/derekparker/delve/pkg/proc/registers_amd64.go
generated
vendored
90
vendor/github.com/derekparker/delve/pkg/proc/registers_amd64.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
|
||||
var dwarfToAsm = map[int]x86asm.Reg{
|
||||
0: x86asm.RAX,
|
||||
1: x86asm.RDX,
|
||||
2: x86asm.RCX,
|
||||
3: x86asm.RBX,
|
||||
4: x86asm.RSI,
|
||||
5: x86asm.RDI,
|
||||
6: x86asm.RBP,
|
||||
7: x86asm.RSP,
|
||||
8: x86asm.R8,
|
||||
9: x86asm.R9,
|
||||
10: x86asm.R10,
|
||||
11: x86asm.R11,
|
||||
12: x86asm.R12,
|
||||
13: x86asm.R13,
|
||||
14: x86asm.R14,
|
||||
15: x86asm.R15,
|
||||
16: x86asm.RIP,
|
||||
}
|
||||
|
||||
var dwarfToName = map[int]string{
|
||||
17: "XMM0",
|
||||
18: "XMM1",
|
||||
19: "XMM2",
|
||||
20: "XMM3",
|
||||
21: "XMM4",
|
||||
22: "XMM5",
|
||||
23: "XMM6",
|
||||
24: "XMM7",
|
||||
25: "XMM8",
|
||||
26: "XMM9",
|
||||
27: "XMM10",
|
||||
28: "XMM11",
|
||||
29: "XMM12",
|
||||
30: "XMM13",
|
||||
31: "XMM14",
|
||||
32: "XMM15",
|
||||
33: "ST(0)",
|
||||
34: "ST(1)",
|
||||
35: "ST(2)",
|
||||
36: "ST(3)",
|
||||
37: "ST(4)",
|
||||
38: "ST(5)",
|
||||
39: "ST(6)",
|
||||
40: "ST(7)",
|
||||
49: "Eflags",
|
||||
50: "Es",
|
||||
51: "Cs",
|
||||
52: "Ss",
|
||||
53: "Ds",
|
||||
54: "Fs",
|
||||
55: "Gs",
|
||||
58: "Fs_base",
|
||||
59: "Gs_base",
|
||||
64: "MXCSR",
|
||||
65: "CW",
|
||||
66: "SW",
|
||||
}
|
||||
|
||||
// GetDwarfRegister maps between DWARF register numbers and architecture
|
||||
// registers.
|
||||
// The mapping is specified in the System V ABI AMD64 Architecture Processor
|
||||
// Supplement page 57, figure 3.36
|
||||
// https://www.uclibc.org/docs/psABI-x86_64.pdf
|
||||
func GetDwarfRegister(regs Registers, i int) []byte {
|
||||
if asmreg, ok := dwarfToAsm[i]; ok {
|
||||
x, _ := regs.Get(int(asmreg))
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, x)
|
||||
return buf.Bytes()
|
||||
}
|
||||
if regname, ok := dwarfToName[i]; ok {
|
||||
regslice := regs.Slice()
|
||||
for _, reg := range regslice {
|
||||
if reg.Name == regname {
|
||||
return reg.Bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
return []byte{}
|
||||
}
|
687
vendor/github.com/derekparker/delve/pkg/proc/stack.go
generated
vendored
687
vendor/github.com/derekparker/delve/pkg/proc/stack.go
generated
vendored
@ -1,687 +0,0 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"strings"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
)
|
||||
|
||||
// This code is partly adapted from runtime.gentraceback in
|
||||
// $GOROOT/src/runtime/traceback.go
|
||||
|
||||
// Stackframe represents a frame in a system stack.
|
||||
//
|
||||
// Each stack frame has two locations Current and Call.
|
||||
//
|
||||
// For the topmost stackframe Current and Call are the same location.
|
||||
//
|
||||
// For stackframes after the first Current is the location corresponding to
|
||||
// the return address and Call is the location of the CALL instruction that
|
||||
// was last executed on the frame. Note however that Call.PC is always equal
|
||||
// to Current.PC, because finding the correct value for Call.PC would
|
||||
// require disassembling each function in the stacktrace.
|
||||
//
|
||||
// For synthetic stackframes generated for inlined function calls Current.Fn
|
||||
// is the function containing the inlining and Call.Fn in the inlined
|
||||
// function.
|
||||
type Stackframe struct {
|
||||
Current, Call Location
|
||||
|
||||
// Frame registers.
|
||||
Regs op.DwarfRegisters
|
||||
// High address of the stack.
|
||||
stackHi uint64
|
||||
// Return address for this stack frame (as read from the stack frame itself).
|
||||
Ret uint64
|
||||
// Address to the memory location containing the return address
|
||||
addrret uint64
|
||||
// Err is set if an error occurred during stacktrace
|
||||
Err error
|
||||
// SystemStack is true if this frame belongs to a system stack.
|
||||
SystemStack bool
|
||||
// Inlined is true if this frame is actually an inlined call.
|
||||
Inlined bool
|
||||
// Bottom is true if this is the bottom of the stack
|
||||
Bottom bool
|
||||
|
||||
// lastpc is a memory address guaranteed to belong to the last instruction
|
||||
// executed in this stack frame.
|
||||
// For the topmost stack frame this will be the same as Current.PC and
|
||||
// Call.PC, for other stack frames it will usually be Current.PC-1, but
|
||||
// could be different when inlined calls are involved in the stacktrace.
|
||||
// Note that this address isn't guaranteed to belong to the start of an
|
||||
// instruction and, for this reason, should not be propagated outside of
|
||||
// pkg/proc.
|
||||
// Use this value to determine active lexical scopes for the stackframe.
|
||||
lastpc uint64
|
||||
|
||||
// TopmostDefer is the defer that would be at the top of the stack when a
|
||||
// panic unwind would get to this call frame, in other words it's the first
|
||||
// deferred function that will be called if the runtime unwinds past this
|
||||
// call frame.
|
||||
TopmostDefer *Defer
|
||||
|
||||
// Defers is the list of functions deferred by this stack frame (so far).
|
||||
Defers []*Defer
|
||||
}
|
||||
|
||||
// FrameOffset returns the address of the stack frame, absolute for system
|
||||
// stack frames or as an offset from stackhi for goroutine stacks (a
|
||||
// negative value).
|
||||
func (frame *Stackframe) FrameOffset() int64 {
|
||||
if frame.SystemStack {
|
||||
return frame.Regs.CFA
|
||||
}
|
||||
return frame.Regs.CFA - int64(frame.stackHi)
|
||||
}
|
||||
|
||||
// FramePointerOffset returns the value of the frame pointer, absolute for
|
||||
// system stack frames or as an offset from stackhi for goroutine stacks (a
|
||||
// negative value).
|
||||
func (frame *Stackframe) FramePointerOffset() int64 {
|
||||
if frame.SystemStack {
|
||||
return int64(frame.Regs.BP())
|
||||
}
|
||||
return int64(frame.Regs.BP()) - int64(frame.stackHi)
|
||||
}
|
||||
|
||||
// ThreadStacktrace returns the stack trace for thread.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
||||
g, _ := GetG(thread)
|
||||
if g == nil {
|
||||
regs, err := thread.Registers(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(regs, thread.BinInfo().staticBase), 0, nil, -1, nil)
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
return g.Stacktrace(depth, false)
|
||||
}
|
||||
|
||||
func (g *G) stackIterator() (*stackIterator, error) {
|
||||
stkbar, err := g.stkbar()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.Thread != nil {
|
||||
regs, err := g.Thread.Registers(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(regs, g.variable.bi.staticBase), g.stackhi, stkbar, g.stkbarPos, g), nil
|
||||
}
|
||||
return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, stkbar, g.stkbarPos, g), nil
|
||||
}
|
||||
|
||||
// Stacktrace returns the stack trace for a goroutine.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (g *G) Stacktrace(depth int, readDefers bool) ([]Stackframe, error) {
|
||||
it, err := g.stackIterator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frames, err := it.stacktrace(depth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if readDefers {
|
||||
g.readDefers(frames)
|
||||
}
|
||||
return frames, nil
|
||||
}
|
||||
|
||||
// NullAddrError is an error for a null address.
|
||||
type NullAddrError struct{}
|
||||
|
||||
func (n NullAddrError) Error() string {
|
||||
return "NULL address"
|
||||
}
|
||||
|
||||
// stackIterator holds information
|
||||
// required to iterate and walk the program
|
||||
// stack.
|
||||
type stackIterator struct {
|
||||
pc uint64
|
||||
top bool
|
||||
atend bool
|
||||
frame Stackframe
|
||||
bi *BinaryInfo
|
||||
mem MemoryReadWriter
|
||||
err error
|
||||
|
||||
stackhi uint64
|
||||
systemstack bool
|
||||
stackBarrierPC uint64
|
||||
stkbar []savedLR
|
||||
|
||||
// regs is the register set for the current frame
|
||||
regs op.DwarfRegisters
|
||||
|
||||
g *G // the goroutine being stacktraced, nil if we are stacktracing a goroutine-less thread
|
||||
g0_sched_sp uint64 // value of g0.sched.sp (see comments around its use)
|
||||
|
||||
dwarfReader *dwarf.Reader
|
||||
}
|
||||
|
||||
type savedLR struct {
|
||||
ptr uint64
|
||||
val uint64
|
||||
}
|
||||
|
||||
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int, g *G) *stackIterator {
|
||||
stackBarrierFunc := bi.LookupFunc["runtime.stackBarrier"] // stack barriers were removed in Go 1.9
|
||||
var stackBarrierPC uint64
|
||||
if stackBarrierFunc != nil && stkbar != nil {
|
||||
stackBarrierPC = stackBarrierFunc.Entry
|
||||
fn := bi.PCToFunc(regs.PC())
|
||||
if fn != nil && fn.Name == "runtime.stackBarrier" {
|
||||
// We caught the goroutine as it's executing the stack barrier, we must
|
||||
// determine whether or not g.stackPos has already been incremented or not.
|
||||
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < regs.SP() {
|
||||
// runtime.stackBarrier has not incremented stkbarPos.
|
||||
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < regs.SP() {
|
||||
// runtime.stackBarrier has incremented stkbarPos.
|
||||
stkbarPos--
|
||||
} else {
|
||||
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", regs.SP())}
|
||||
}
|
||||
}
|
||||
stkbar = stkbar[stkbarPos:]
|
||||
}
|
||||
var g0_sched_sp uint64
|
||||
systemstack := true
|
||||
if g != nil {
|
||||
systemstack = g.SystemStack
|
||||
g0var, _ := g.variable.fieldVariable("m").structMember("g0")
|
||||
if g0var != nil {
|
||||
g0, _ := g0var.parseG()
|
||||
if g0 != nil {
|
||||
g0_sched_sp = g0.SP
|
||||
}
|
||||
}
|
||||
}
|
||||
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar, systemstack: systemstack, g: g, g0_sched_sp: g0_sched_sp, dwarfReader: bi.dwarf.Reader()}
|
||||
}
|
||||
|
||||
// Next points the iterator to the next stack frame.
|
||||
func (it *stackIterator) Next() bool {
|
||||
if it.err != nil || it.atend {
|
||||
return false
|
||||
}
|
||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||
it.frame = it.newStackframe(ret, retaddr)
|
||||
|
||||
if it.stkbar != nil && it.frame.Ret == it.stackBarrierPC && it.frame.addrret == it.stkbar[0].ptr {
|
||||
// Skip stack barrier frames
|
||||
it.frame.Ret = it.stkbar[0].val
|
||||
it.stkbar = it.stkbar[1:]
|
||||
}
|
||||
|
||||
if it.switchStack() {
|
||||
return true
|
||||
}
|
||||
|
||||
if it.frame.Ret <= 0 {
|
||||
it.atend = true
|
||||
return true
|
||||
}
|
||||
|
||||
it.top = false
|
||||
it.pc = it.frame.Ret
|
||||
it.regs = callFrameRegs
|
||||
return true
|
||||
}
|
||||
|
||||
// asmcgocallSPOffsetSaveSlot is the offset from systemstack.SP where
|
||||
// (goroutine.SP - StackHi) is saved in runtime.asmcgocall after the stack
|
||||
// switch happens.
|
||||
const asmcgocallSPOffsetSaveSlot = 0x28
|
||||
|
||||
// switchStack will use the current frame to determine if it's time to
|
||||
// switch between the system stack and the goroutine stack or vice versa.
|
||||
// Sets it.atend when the top of the stack is reached.
|
||||
func (it *stackIterator) switchStack() bool {
|
||||
if it.frame.Current.Fn == nil {
|
||||
return false
|
||||
}
|
||||
switch it.frame.Current.Fn.Name {
|
||||
case "runtime.asmcgocall":
|
||||
if it.top || !it.systemstack {
|
||||
return false
|
||||
}
|
||||
|
||||
// This function is called by a goroutine to execute a C function and
|
||||
// switches from the goroutine stack to the system stack.
|
||||
// Since we are unwinding the stack from callee to caller we have switch
|
||||
// from the system stack to the goroutine stack.
|
||||
|
||||
off, _ := readIntRaw(it.mem, uintptr(it.regs.SP()+asmcgocallSPOffsetSaveSlot), int64(it.bi.Arch.PtrSize())) // reads "offset of SP from StackHi" from where runtime.asmcgocall saved it
|
||||
oldsp := it.regs.SP()
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = uint64(int64(it.stackhi) - off)
|
||||
|
||||
// runtime.asmcgocall can also be called from inside the system stack,
|
||||
// in that case no stack switch actually happens
|
||||
if it.regs.SP() == oldsp {
|
||||
return false
|
||||
}
|
||||
it.systemstack = false
|
||||
|
||||
// advances to the next frame in the call stack
|
||||
it.frame.addrret = uint64(int64(it.regs.SP()) + int64(it.bi.Arch.PtrSize()))
|
||||
it.frame.Ret, _ = readUintRaw(it.mem, uintptr(it.frame.addrret), int64(it.bi.Arch.PtrSize()))
|
||||
it.pc = it.frame.Ret
|
||||
|
||||
it.top = false
|
||||
return true
|
||||
|
||||
case "runtime.cgocallback_gofunc":
|
||||
// For a detailed description of how this works read the long comment at
|
||||
// the start of $GOROOT/src/runtime/cgocall.go and the source code of
|
||||
// runtime.cgocallback_gofunc in $GOROOT/src/runtime/asm_amd64.s
|
||||
//
|
||||
// When a C functions calls back into go it will eventually call into
|
||||
// runtime.cgocallback_gofunc which is the function that does the stack
|
||||
// switch from the system stack back into the goroutine stack
|
||||
// Since we are going backwards on the stack here we see the transition
|
||||
// as goroutine stack -> system stack.
|
||||
|
||||
if it.top || it.systemstack {
|
||||
return false
|
||||
}
|
||||
|
||||
if it.g0_sched_sp <= 0 {
|
||||
return false
|
||||
}
|
||||
// entering the system stack
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g0_sched_sp
|
||||
// reads the previous value of g0.sched.sp that runtime.cgocallback_gofunc saved on the stack
|
||||
it.g0_sched_sp, _ = readUintRaw(it.mem, uintptr(it.regs.SP()), int64(it.bi.Arch.PtrSize()))
|
||||
it.top = false
|
||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||
frameOnSystemStack := it.newStackframe(ret, retaddr)
|
||||
it.pc = frameOnSystemStack.Ret
|
||||
it.regs = callFrameRegs
|
||||
it.systemstack = true
|
||||
return true
|
||||
|
||||
case "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
|
||||
// Look for "top of stack" functions.
|
||||
it.atend = true
|
||||
return true
|
||||
|
||||
default:
|
||||
if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") {
|
||||
// The runtime switches to the system stack in multiple places.
|
||||
// This usually happens through a call to runtime.systemstack but there
|
||||
// are functions that switch to the system stack manually (for example
|
||||
// runtime.morestack).
|
||||
// Since we are only interested in printing the system stack for cgo
|
||||
// calls we switch directly to the goroutine stack if we detect that the
|
||||
// function at the top of the stack is a runtime function.
|
||||
it.systemstack = false
|
||||
it.top = false
|
||||
it.pc = it.g.PC
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g.SP
|
||||
it.regs.Reg(it.regs.BPRegNum).Uint64Val = it.g.BP
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Frame returns the frame the iterator is pointing at.
|
||||
func (it *stackIterator) Frame() Stackframe {
|
||||
it.frame.Bottom = it.atend
|
||||
return it.frame
|
||||
}
|
||||
|
||||
// Err returns the error encountered during stack iteration.
|
||||
func (it *stackIterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
// frameBase calculates the frame base pseudo-register for DWARF for fn and
|
||||
// the current frame.
|
||||
func (it *stackIterator) frameBase(fn *Function) int64 {
|
||||
it.dwarfReader.Seek(fn.offset)
|
||||
e, err := it.dwarfReader.Next()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
fb, _, _, _ := it.bi.Location(e, dwarf.AttrFrameBase, it.pc, it.regs)
|
||||
return fb
|
||||
}
|
||||
|
||||
func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
|
||||
if retaddr == 0 {
|
||||
it.err = NullAddrError{}
|
||||
return Stackframe{}
|
||||
}
|
||||
f, l, fn := it.bi.PCToLine(it.pc)
|
||||
if fn == nil {
|
||||
f = "?"
|
||||
l = -1
|
||||
} else {
|
||||
it.regs.FrameBase = it.frameBase(fn)
|
||||
}
|
||||
r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, stackHi: it.stackhi, SystemStack: it.systemstack, lastpc: it.pc}
|
||||
if !it.top {
|
||||
fnname := ""
|
||||
if r.Current.Fn != nil {
|
||||
fnname = r.Current.Fn.Name
|
||||
}
|
||||
switch fnname {
|
||||
case "runtime.mstart", "runtime.systemstack_switch":
|
||||
// these frames are inserted by runtime.systemstack and there is no CALL
|
||||
// instruction to look for at pc - 1
|
||||
r.Call = r.Current
|
||||
default:
|
||||
if r.Current.Fn != nil && it.pc == r.Current.Fn.Entry {
|
||||
// if the return address is the entry point of the function that
|
||||
// contains it then this is some kind of fake return frame (for example
|
||||
// runtime.sigreturn) that didn't actually call the current frame,
|
||||
// attempting to get the location of the CALL instruction would just
|
||||
// obfuscate what's going on, since there is no CALL instruction.
|
||||
r.Call = r.Current
|
||||
} else {
|
||||
r.lastpc = it.pc - 1
|
||||
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(it.pc - 1)
|
||||
if r.Call.Fn == nil {
|
||||
r.Call.File = "?"
|
||||
r.Call.Line = -1
|
||||
}
|
||||
r.Call.PC = r.Current.PC
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.Call = r.Current
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
||||
if depth < 0 {
|
||||
return nil, errors.New("negative maximum stack depth")
|
||||
}
|
||||
frames := make([]Stackframe, 0, depth+1)
|
||||
for it.Next() {
|
||||
frames = it.appendInlineCalls(frames, it.Frame())
|
||||
if len(frames) >= depth+1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := it.Err(); err != nil {
|
||||
if len(frames) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
frames = append(frames, Stackframe{Err: err})
|
||||
}
|
||||
return frames, nil
|
||||
}
|
||||
|
||||
func (it *stackIterator) appendInlineCalls(frames []Stackframe, frame Stackframe) []Stackframe {
|
||||
if frame.Call.Fn == nil {
|
||||
return append(frames, frame)
|
||||
}
|
||||
if frame.Call.Fn.cu.lineInfo == nil {
|
||||
return append(frames, frame)
|
||||
}
|
||||
|
||||
callpc := frame.Call.PC
|
||||
if len(frames) > 0 {
|
||||
callpc--
|
||||
}
|
||||
|
||||
irdr := reader.InlineStack(it.bi.dwarf, frame.Call.Fn.offset, reader.ToRelAddr(callpc, it.bi.staticBase))
|
||||
for irdr.Next() {
|
||||
entry, offset := reader.LoadAbstractOrigin(irdr.Entry(), it.dwarfReader)
|
||||
|
||||
fnname, okname := entry.Val(dwarf.AttrName).(string)
|
||||
fileidx, okfileidx := entry.Val(dwarf.AttrCallFile).(int64)
|
||||
line, okline := entry.Val(dwarf.AttrCallLine).(int64)
|
||||
|
||||
if !okname || !okfileidx || !okline {
|
||||
break
|
||||
}
|
||||
if fileidx-1 < 0 || fileidx-1 >= int64(len(frame.Current.Fn.cu.lineInfo.FileNames)) {
|
||||
break
|
||||
}
|
||||
|
||||
inlfn := &Function{Name: fnname, Entry: frame.Call.Fn.Entry, End: frame.Call.Fn.End, offset: offset, cu: frame.Call.Fn.cu}
|
||||
frames = append(frames, Stackframe{
|
||||
Current: frame.Current,
|
||||
Call: Location{
|
||||
frame.Call.PC,
|
||||
frame.Call.File,
|
||||
frame.Call.Line,
|
||||
inlfn,
|
||||
},
|
||||
Regs: frame.Regs,
|
||||
stackHi: frame.stackHi,
|
||||
Ret: frame.Ret,
|
||||
addrret: frame.addrret,
|
||||
Err: frame.Err,
|
||||
SystemStack: frame.SystemStack,
|
||||
Inlined: true,
|
||||
lastpc: frame.lastpc,
|
||||
})
|
||||
|
||||
frame.Call.File = frame.Current.Fn.cu.lineInfo.FileNames[fileidx-1].Path
|
||||
frame.Call.Line = int(line)
|
||||
}
|
||||
|
||||
return append(frames, frame)
|
||||
}
|
||||
|
||||
// advanceRegs calculates it.callFrameRegs using it.regs and the frame
|
||||
// descriptor entry for the current stack frame.
|
||||
// it.regs.CallFrameCFA is updated.
|
||||
func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uint64, retaddr uint64) {
|
||||
fde, err := it.bi.frameEntries.FDEForPC(it.pc)
|
||||
var framectx *frame.FrameContext
|
||||
if _, nofde := err.(*frame.ErrNoFDEForPC); nofde {
|
||||
framectx = it.bi.Arch.FixFrameUnwindContext(nil, it.pc, it.bi)
|
||||
} else {
|
||||
framectx = it.bi.Arch.FixFrameUnwindContext(fde.EstablishFrame(it.pc), it.pc, it.bi)
|
||||
}
|
||||
|
||||
cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0)
|
||||
if cfareg == nil {
|
||||
it.err = fmt.Errorf("CFA becomes undefined at PC %#x", it.pc)
|
||||
return op.DwarfRegisters{StaticBase: it.bi.staticBase}, 0, 0
|
||||
}
|
||||
it.regs.CFA = int64(cfareg.Uint64Val)
|
||||
|
||||
callFrameRegs = op.DwarfRegisters{StaticBase: it.bi.staticBase, ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum, BPRegNum: it.regs.BPRegNum}
|
||||
|
||||
// According to the standard the compiler should be responsible for emitting
|
||||
// rules for the RSP register so that it can then be used to calculate CFA,
|
||||
// however neither Go nor GCC do this.
|
||||
// In the following line we copy GDB's behaviour by assuming this is
|
||||
// implicit.
|
||||
// See also the comment in dwarf2_frame_default_init in
|
||||
// $GDB_SOURCE/dwarf2-frame.c
|
||||
callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg)
|
||||
|
||||
for i, regRule := range framectx.Regs {
|
||||
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
||||
callFrameRegs.AddReg(i, reg)
|
||||
if i == framectx.RetAddrReg {
|
||||
if reg == nil {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("Undefined return address at %#x", it.pc)
|
||||
}
|
||||
it.err = err
|
||||
} else {
|
||||
ret = reg.Uint64Val
|
||||
}
|
||||
retaddr = uint64(it.regs.CFA + regRule.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
return callFrameRegs, ret, retaddr
|
||||
}
|
||||
|
||||
func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, cfa int64) (*op.DwarfRegister, error) {
|
||||
switch rule.Rule {
|
||||
default:
|
||||
fallthrough
|
||||
case frame.RuleUndefined:
|
||||
return nil, nil
|
||||
case frame.RuleSameVal:
|
||||
reg := *it.regs.Reg(regnum)
|
||||
return ®, nil
|
||||
case frame.RuleOffset:
|
||||
return it.readRegisterAt(regnum, uint64(cfa+rule.Offset))
|
||||
case frame.RuleValOffset:
|
||||
return op.DwarfRegisterFromUint64(uint64(cfa + rule.Offset)), nil
|
||||
case frame.RuleRegister:
|
||||
return it.regs.Reg(rule.Reg), nil
|
||||
case frame.RuleExpression:
|
||||
v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return it.readRegisterAt(regnum, uint64(v))
|
||||
case frame.RuleValExpression:
|
||||
v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return op.DwarfRegisterFromUint64(uint64(v)), nil
|
||||
case frame.RuleArchitectural:
|
||||
return nil, errors.New("architectural frame rules are unsupported")
|
||||
case frame.RuleCFA:
|
||||
if it.regs.Reg(rule.Reg) == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return op.DwarfRegisterFromUint64(uint64(int64(it.regs.Uint64Val(rule.Reg)) + rule.Offset)), nil
|
||||
case frame.RuleFramePointer:
|
||||
curReg := it.regs.Reg(rule.Reg)
|
||||
if curReg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if curReg.Uint64Val <= uint64(cfa) {
|
||||
return it.readRegisterAt(regnum, curReg.Uint64Val)
|
||||
}
|
||||
newReg := *curReg
|
||||
return &newReg, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (it *stackIterator) readRegisterAt(regnum uint64, addr uint64) (*op.DwarfRegister, error) {
|
||||
buf := make([]byte, it.bi.Arch.RegSize(regnum))
|
||||
_, err := it.mem.ReadMemory(buf, uintptr(addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return op.DwarfRegisterFromBytes(buf), nil
|
||||
}
|
||||
|
||||
// Defer represents one deferred call
|
||||
type Defer struct {
|
||||
DeferredPC uint64 // Value of field _defer.fn.fn, the deferred function
|
||||
DeferPC uint64 // PC address of instruction that added this defer
|
||||
SP uint64 // Value of SP register when this function was deferred (this field gets adjusted when the stack is moved to match the new stack space)
|
||||
link *Defer // Next deferred function
|
||||
|
||||
variable *Variable
|
||||
Unreadable error
|
||||
}
|
||||
|
||||
// readDefers decorates the frames with the function deferred at each stack frame.
|
||||
func (g *G) readDefers(frames []Stackframe) {
|
||||
curdefer := g.Defer()
|
||||
i := 0
|
||||
|
||||
// scan simultaneously frames and the curdefer linked list, assigning
|
||||
// defers to their associated frames.
|
||||
for {
|
||||
if curdefer == nil || i >= len(frames) {
|
||||
return
|
||||
}
|
||||
if curdefer.Unreadable != nil {
|
||||
// Current defer is unreadable, stick it into the first available frame
|
||||
// (so that it can be reported to the user) and exit
|
||||
frames[i].Defers = append(frames[i].Defers, curdefer)
|
||||
return
|
||||
}
|
||||
if frames[i].Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if frames[i].TopmostDefer == nil {
|
||||
frames[i].TopmostDefer = curdefer
|
||||
}
|
||||
|
||||
if frames[i].SystemStack || curdefer.SP >= uint64(frames[i].Regs.CFA) {
|
||||
// frames[i].Regs.CFA is the value that SP had before the function of
|
||||
// frames[i] was called.
|
||||
// This means that when curdefer.SP == frames[i].Regs.CFA then curdefer
|
||||
// was added by the previous frame.
|
||||
//
|
||||
// curdefer.SP < frames[i].Regs.CFA means curdefer was added by a
|
||||
// function further down the stack.
|
||||
//
|
||||
// SystemStack frames live on a different physical stack and can't be
|
||||
// compared with deferred frames.
|
||||
i++
|
||||
} else {
|
||||
frames[i].Defers = append(frames[i].Defers, curdefer)
|
||||
curdefer = curdefer.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Defer) load() {
|
||||
d.variable.loadValue(LoadConfig{false, 1, 0, 0, -1})
|
||||
if d.variable.Unreadable != nil {
|
||||
d.Unreadable = d.variable.Unreadable
|
||||
return
|
||||
}
|
||||
|
||||
fnvar := d.variable.fieldVariable("fn").maybeDereference()
|
||||
if fnvar.Addr != 0 {
|
||||
fnvar = fnvar.loadFieldNamed("fn")
|
||||
if fnvar.Unreadable == nil {
|
||||
d.DeferredPC, _ = constant.Uint64Val(fnvar.Value)
|
||||
}
|
||||
}
|
||||
|
||||
d.DeferPC, _ = constant.Uint64Val(d.variable.fieldVariable("pc").Value)
|
||||
d.SP, _ = constant.Uint64Val(d.variable.fieldVariable("sp").Value)
|
||||
|
||||
linkvar := d.variable.fieldVariable("link").maybeDereference()
|
||||
if linkvar.Addr != 0 {
|
||||
d.link = &Defer{variable: linkvar}
|
||||
}
|
||||
}
|
||||
|
||||
// errSPDecreased is used when (*Defer).Next detects a corrupted linked
|
||||
// list, specifically when after followin a link pointer the value of SP
|
||||
// decreases rather than increasing or staying the same (the defer list is a
|
||||
// FIFO list, nodes further down the list have been added by function calls
|
||||
// further down the call stack and therefore the SP should always increase).
|
||||
var errSPDecreased = errors.New("corrupted defer list: SP decreased")
|
||||
|
||||
// Next returns the next defer in the linked list
|
||||
func (d *Defer) Next() *Defer {
|
||||
if d.link == nil {
|
||||
return nil
|
||||
}
|
||||
d.link.load()
|
||||
if d.link.SP < d.SP {
|
||||
d.link.Unreadable = errSPDecreased
|
||||
}
|
||||
return d.link
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user