From 94005b0c2285be6b970e7cc71f4318b8a1ef9b83 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 15 Oct 2024 10:38:45 +0800 Subject: [PATCH 1/3] xtool/nm:flags option --- xtool/nm/nm.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/xtool/nm/nm.go b/xtool/nm/nm.go index 23ff57db..156c9a50 100644 --- a/xtool/nm/nm.go +++ b/xtool/nm/nm.go @@ -76,11 +76,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() From ace3c3e421a65b0e3570c3250de87113b8a4dabc Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 15 Oct 2024 12:01:33 +0800 Subject: [PATCH 2/3] nmdump:flags option --- chore/nmdump/nmdump.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/chore/nmdump/nmdump.go b/chore/nmdump/nmdump.go index c0a68042..aa1a2bee 100644 --- a/chore/nmdump/nmdump.go +++ b/chore/nmdump/nmdump.go @@ -25,13 +25,21 @@ import ( ) 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) From d682771c3524e0643bcbca5446c3b9bf7fe6a252 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 15 Oct 2024 14:48:51 +0800 Subject: [PATCH 3/3] xtool/nm:symbol version --- chore/nmdump/nmdump.go | 12 +++++++++-- xtool/nm/nm.go | 46 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/chore/nmdump/nmdump.go b/chore/nmdump/nmdump.go index aa1a2bee..4a0e2a8b 100644 --- a/chore/nmdump/nmdump.go +++ b/chore/nmdump/nmdump.go @@ -22,6 +22,7 @@ import ( "os" "github.com/goplus/llgo/xtool/env/llvm" + nmtool "github.com/goplus/llgo/xtool/nm" ) func main() { @@ -45,10 +46,17 @@ func main() { 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) } } } diff --git a/xtool/nm/nm.go b/xtool/nm/nm.go index 156c9a50..a1c44e0d 100644 --- a/xtool/nm/nm.go +++ b/xtool/nm/nm.go @@ -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. @@ -143,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 != '-'