diff --git a/ldd/error.go b/ldd/error.go new file mode 100644 index 0000000..51aef94 --- /dev/null +++ b/ldd/error.go @@ -0,0 +1,11 @@ +package ldd + +import "fmt" + +type EntryUnexpectedSegmentsError struct { + Entry string +} + +func (e *EntryUnexpectedSegmentsError) Error() string { + return fmt.Sprintf("unexpected segments in entry %q", e.Entry) +} diff --git a/ldd/ldd.go b/ldd/ldd.go new file mode 100644 index 0000000..989aeca --- /dev/null +++ b/ldd/ldd.go @@ -0,0 +1,84 @@ +package ldd + +import ( + "errors" + "fmt" + "math" + "os" + "os/exec" + "path" + "strconv" + "strings" +) + +var ( + ErrUnexpectedSeparator = errors.New("unexpected separator") + ErrPathNotAbsolute = errors.New("path not absolute") + ErrBadLocationFormat = errors.New("bad location format") +) + +type Entry struct { + Name string `json:"name,omitempty"` + Path string `json:"path,omitempty"` + Location uint64 `json:"location"` +} + +func Exec(p string) ([]*Entry, error) { + t := exec.Command("ldd", p) + t.Stdout = new(strings.Builder) + t.Stderr = os.Stderr + + if err := t.Run(); err != nil { + return nil, err + } + + out := t.Stdout.(fmt.Stringer).String() + payload := strings.Split(out, "\n") + + result := make([]*Entry, len(payload)) + + for i, ent := range payload { + if len(ent) == 0 { + continue + } + + segment := strings.SplitN(ent, " ", 5) + + var iL int + + switch len(segment) { + case 2: // /lib/ld-musl-x86_64.so.1 (0x7f04d14ef000) + iL = 1 + result[i] = &Entry{Name: segment[0]} + case 4: // libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f04d14ef000) + iL = 3 + if segment[1] != "=>" { + return nil, ErrUnexpectedSeparator + } + if !path.IsAbs(segment[2]) { + return nil, ErrPathNotAbsolute + } + result[i] = &Entry{ + Name: segment[0], + Path: segment[2], + } + default: + return nil, &EntryUnexpectedSegmentsError{ent} + } + + if loc, err := parseLocation(segment[iL]); err != nil { + return nil, err + } else { + result[i].Location = loc + } + } + + return result, nil +} + +func parseLocation(s string) (uint64, error) { + if len(s) < 4 || s[len(s)-1] != ')' || s[:3] != "(0x" { + return math.MaxUint64, ErrBadLocationFormat + } + return strconv.ParseUint(s[3:len(s)-1], 16, 64) +}