Skip to content

Commit

Permalink
Merge pull request goplus#829 from luoliwoshang/xtool/nm
Browse files Browse the repository at this point in the history
xtool/nm:flags option & symbol version
  • Loading branch information
xushiwei authored Oct 16, 2024
2 parents 9ea88fe + d682771 commit 5f0e30e
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 14 deletions.
26 changes: 21 additions & 5 deletions chore/nmdump/nmdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down
61 changes: 52 additions & 9 deletions xtool/nm/nm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
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.
Expand All @@ -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()
Expand Down Expand Up @@ -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 != '-'
Expand Down

0 comments on commit 5f0e30e

Please sign in to comment.