// ldd packagepackage lddimport ( "debug/elf" "os" "path" "strings")// Lddroot returns a []string containing all the files (including // symbolic links) that the binary depends on. It is like // the SunOS/Solaris/Linux ldd command. The root parameter is used// on systems which contain a full root file system (e.g. ARM file// system on OSX). func Lddroot(cmd, root, liblist string) ([]string, error) { /* it might just be static ... */ ret := []string{} libpath := strings.Split(liblist, ":") known := make(map[string]string, 16) /* this is far larger than we will ever need, but I am in fright of deadlock. */ strings := make(chan string, 256) /* assumption: we get a rooted path. */ binpath := path.Join(root, cmd) /* test to make sure it is there and it's an ELF */ /* fails on Plan 9 but that's not really an issue. */ e, err := elf.Open(binpath) if err != nil { return []string{}, err } else { strings <- cmd } e.Close() for len(strings) > 0 { libdir := "" lib := <-strings if _, ok := known[lib]; ok { continue } /* if it's absolute just open it */ /* note: do the elfOpen before the stat. If it's a symlink loop, * the elf.Open will fail and save us the trouble of having to * figure that out ourselves. */ if path.IsAbs(lib) { binpath = path.Join(root + lib) libdir = "" e, err = elf.Open(binpath) } else { for _, libdir = range libpath { binpath = path.Join(root, libdir, lib) e, err = elf.Open(binpath) if err == nil { break } } } /* nothing? We're done */ if err != nil { return []string{}, err } dynlibs, err := e.ImportedLibraries() /* maybe we should make the chan a []string * but I really like chans */ for _, val := range dynlibs { strings <- val } e.Close() /* there may be none. This might be a leaf like libc.so */ if err != nil { /* tough call. You found it, it's dynamic, * it's broken. I think that's an error. */ return []string{}, err } /* we know this one. */ known[lib] = binpath ret = append(ret, binpath) /* now, what if it was a symlink? * the elf.Open worked; we know it's not a bad * link. So gather up the symlinks and put it all * in there. */ for { linkname, err := os.Readlink(binpath) if err != nil { break } /* NEVER normalize the value you return * to the caller * ALWAYS normalize the binpath */ ret = append(ret, linkname) binpath = path.Join(root, libdir, linkname) known[linkname] = binpath } } return ret, nil}// Ldd returns a []string containing all the files (including // symbolic links) that the binary depends on. It is like // the SunOS/Solaris/Linux ldd command. func Ldd(cmd, liblist string) ([]string, error) { return Lddroot(cmd, "/", liblist)}
// ldd package
package ldd
import (
"debug/elf"
"os"
"path"
"strings"
)
// Lddroot returns a []string containing all the files (including
// symbolic links) that the binary depends on. It is like
// the SunOS/Solaris/Linux ldd command. The root parameter is used
// on systems which contain a full root file system (e.g. ARM file
// system on OSX).
func Lddroot(cmd, root, liblist string) ([]string, error) {
/* it might just be static ... */
ret := []string{}
libpath := strings.Split(liblist, ":")
known := make(map[string]string, 16)
/* this is far larger than we will ever need, but I am in fright of deadlock. */
strings := make(chan string, 256)
/* assumption: we get a rooted path. */
binpath := path.Join(root, cmd)
/* test to make sure it is there and it's an ELF */
/* fails on Plan 9 but that's not really an issue. */
e, err := elf.Open(binpath)
if err != nil {
return []string{}, err
} else {
strings <- cmd
}
e.Close()
for len(strings) > 0 {
libdir := ""
lib := <-strings
if _, ok := known[lib]; ok {
continue
}
/* if it's absolute just open it */
/* note: do the elfOpen before the stat. If it's a symlink loop,
* the elf.Open will fail and save us the trouble of having to
* figure that out ourselves.
*/
if path.IsAbs(lib) {
binpath = path.Join(root + lib)
libdir = ""
e, err = elf.Open(binpath)
} else {
for _, libdir = range libpath {
binpath = path.Join(root, libdir, lib)
e, err = elf.Open(binpath)
if err == nil {
break
}
}
}
/* nothing? We're done */
if err != nil {
return []string{}, err
}
dynlibs, err := e.ImportedLibraries()
/* maybe we should make the chan a []string
* but I really like chans
*/
for _, val := range dynlibs {
strings <- val
}
e.Close()
/* there may be none. This might be a leaf like libc.so
*/
if err != nil {
/* tough call. You found it, it's dynamic,
* it's broken. I think that's an error.
*/
return []string{}, err
}
/* we know this one. */
known[lib] = binpath
ret = append(ret, binpath)
/* now, what if it was a symlink?
* the elf.Open worked; we know it's not a bad
* link. So gather up the symlinks and put it all
* in there.
*/
for {
linkname, err := os.Readlink(binpath)
if err != nil {
break
}
/* NEVER normalize the value you return
* to the caller
* ALWAYS normalize the binpath
*/
ret = append(ret, linkname)
binpath = path.Join(root, libdir, linkname)
known[linkname] = binpath
}
}
return ret, nil
}
// Ldd returns a []string containing all the files (including
// symbolic links) that the binary depends on. It is like
// the SunOS/Solaris/Linux ldd command.
func Ldd(cmd, liblist string) ([]string, error) {
return Lddroot(cmd, "/", liblist)
}