Merge pull request #829 from luoliwoshang/xtool/nm

xtool/nm:flags option & symbol version
This commit is contained in:
xushiwei
2024-10-16 18:11:40 +08:00
committed by GitHub
2 changed files with 73 additions and 14 deletions

View File

@@ -22,25 +22,41 @@ import (
"os"
"github.com/goplus/llgo/xtool/env/llvm"
nmtool "github.com/goplus/llgo/xtool/nm"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: nmdump libfile")
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: nmdump [flags] libfile")
return
}
nm := llvm.New("").Nm()
items, err := nm.List(os.Args[1])
var flags []string
libfile := os.Args[len(os.Args)-1]
if len(os.Args) > 2 {
flags = os.Args[1 : len(os.Args)-1]
}
items, err := nm.List(libfile, flags...)
for _, item := range items {
if item.File != "" {
fmt.Printf("\n%s:\n", item.File)
}
for _, sym := range item.Symbols {
var versionInfo string
switch sym.VersionType {
case nmtool.VersionSpecific:
versionInfo = fmt.Sprintf("@%s", sym.Version)
case nmtool.VersionDefault:
versionInfo = fmt.Sprintf("@@%s", sym.Version)
}
if sym.FAddr {
fmt.Printf("%016x %c %s\n", sym.Addr, sym.Type, sym.Name)
fmt.Printf("%016x %c %s%s\n", sym.Addr, sym.Type, sym.Name, versionInfo)
} else {
fmt.Printf("%16s %c %s\n", "", sym.Type, sym.Name)
fmt.Printf("%16s %c %s%s\n", "", sym.Type, sym.Name, versionInfo)
}
}
}

View File

@@ -62,12 +62,26 @@ const (
LocalASym = SymbolType('s') // Local symbol in an assembler source file
)
// VersionType represents the version type of a symbol.
// This is specific to Linux systems.
// On macOS , this will always be VersionNone.
// https://sourceware.org/binutils/docs/binutils/nm.html
type VersionType int
const (
VersionNone VersionType = iota // No version information
VersionSpecific // Specific version (@)
VersionDefault // Default version (@@)
)
// Symbol represents a symbol in an object file.
type Symbol struct {
Name string // symbol name
Addr uint64 // symbol address
Type SymbolType // symbol type
FAddr bool // address is valid
VersionType VersionType // version type of the symbol
Version string // version information of the symbol
}
// ObjectFile represents an object file.
@@ -76,11 +90,20 @@ type ObjectFile struct {
Symbols []*Symbol // symbols
}
// List lists symbols in an archive file.
func (p *Cmd) List(arfile string) (items []*ObjectFile, err error) {
// List lists symbols in an archive file
// accepts optional nm command flags.
// Note: The available flags may vary depending on the operating system.
// On Linux, the -D flag is used to display dynamic symbols from the dynamic symbol table.
// On macOS, there's no -D flag. The nm command displays all symbols (including dynamic ones) by default.
// This difference is due to the distinct ways Linux (using ELF format) and macOS (using Mach-O format)
// When working with dynamic libraries:
// On Linux: Use 'nm -D /path/to/library.so'
// On macOS: Simply use 'nm /path/to/library.dylib'
func (p *Cmd) List(arfile string, options ...string) (items []*ObjectFile, err error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(p.app, arfile)
args := append(options, arfile)
cmd := exec.Command(p.app, args...)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
e := cmd.Run()
@@ -134,28 +157,48 @@ func listOutput(data []byte) (items []*ObjectFile, err error) {
return
}
var sym *Symbol
var fullSymName string
if is64bits(line) {
fullSymName = string(line[19:])
sym = &Symbol{
Name: string(line[19:]),
Type: SymbolType(line[17]),
}
if sym.FAddr = hasAddr(line); sym.FAddr {
sym.Addr = hexUint64(line)
}
} else {
fullSymName = string(line[11:])
sym = &Symbol{
Name: string(line[11:]),
Type: SymbolType(line[9]),
}
if sym.FAddr = hasAddr(line); sym.FAddr {
sym.Addr = uint64(hexUint32(line))
}
}
sym.Name, sym.VersionType, sym.Version = parseSymName(fullSymName)
item.Symbols = append(item.Symbols, sym)
}
return
}
func parseSymName(symName string) (name string, versionType VersionType, version string) {
if idx := strings.LastIndex(symName, "@"); idx != -1 {
name = symName[:idx]
version = symName[idx+1:]
if idx > 0 && symName[idx-1] == '@' {
versionType = VersionDefault
name = symName[:idx-1]
} else {
versionType = VersionSpecific
}
} else {
name = symName
versionType = VersionNone
}
return
}
func hasAddr(line []byte) bool {
c := line[0]
return c != ' ' && c != '-'