aboutsummaryrefslogtreecommitdiffhomepage
path: root/ldd
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-10-09 23:51:15 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-10-09 23:51:15 +0900
commitd41b9d2d9ceb60ceb601af991441e70929801c42 (patch)
treeec5b9e35e650f6bec9fd9e603301f0d87b7ede76 /ldd
parent22dfa73efe8a7a15b8867404e6504041a7139406 (diff)
ldd: separate Parse from Exec and trim space
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'ldd')
-rw-r--r--ldd/error.go26
-rw-r--r--ldd/exec.go18
-rw-r--r--ldd/ldd.go28
-rw-r--r--ldd/ldd_test.go95
4 files changed, 139 insertions, 28 deletions
diff --git a/ldd/error.go b/ldd/error.go
index 51aef948..39b63ef9 100644
--- a/ldd/error.go
+++ b/ldd/error.go
@@ -1,11 +1,27 @@
package ldd
-import "fmt"
+import (
+ "errors"
+ "fmt"
+)
-type EntryUnexpectedSegmentsError struct {
- Entry string
+var (
+ ErrUnexpectedSeparator = errors.New("unexpected separator")
+ ErrPathNotAbsolute = errors.New("path not absolute")
+ ErrBadLocationFormat = errors.New("bad location format")
+ ErrUnexpectedNewline = errors.New("unexpected newline")
+)
+
+type EntryUnexpectedSegmentsError string
+
+func (e EntryUnexpectedSegmentsError) Is(err error) bool {
+ var eq EntryUnexpectedSegmentsError
+ if !errors.As(err, &eq) {
+ return false
+ }
+ return e == eq
}
-func (e *EntryUnexpectedSegmentsError) Error() string {
- return fmt.Sprintf("unexpected segments in entry %q", e.Entry)
+func (e EntryUnexpectedSegmentsError) Error() string {
+ return fmt.Sprintf("unexpected segments in entry %q", string(e))
}
diff --git a/ldd/exec.go b/ldd/exec.go
new file mode 100644
index 00000000..d6a2af58
--- /dev/null
+++ b/ldd/exec.go
@@ -0,0 +1,18 @@
+package ldd
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+func Exec(p string) ([]*Entry, error) {
+ t := exec.Command("ldd", p)
+ t.Stdout, t.Stderr = new(strings.Builder), os.Stderr
+ if err := t.Run(); err != nil {
+ return nil, err
+ }
+
+ return Parse(t.Stdout.(fmt.Stringer))
+}
diff --git a/ldd/ldd.go b/ldd/ldd.go
index 989aeca5..6c2aec30 100644
--- a/ldd/ldd.go
+++ b/ldd/ldd.go
@@ -1,49 +1,31 @@
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")
-
+func Parse(stdout fmt.Stringer) ([]*Entry, error) {
+ payload := strings.Split(strings.TrimSpace(stdout.String()), "\n")
result := make([]*Entry, len(payload))
for i, ent := range payload {
if len(ent) == 0 {
- continue
+ return nil, ErrUnexpectedNewline
}
segment := strings.SplitN(ent, " ", 5)
+ // location index
var iL int
switch len(segment) {
@@ -63,7 +45,7 @@ func Exec(p string) ([]*Entry, error) {
Path: segment[2],
}
default:
- return nil, &EntryUnexpectedSegmentsError{ent}
+ return nil, EntryUnexpectedSegmentsError(ent)
}
if loc, err := parseLocation(segment[iL]); err != nil {
diff --git a/ldd/ldd_test.go b/ldd/ldd_test.go
new file mode 100644
index 00000000..ec8a5d0c
--- /dev/null
+++ b/ldd/ldd_test.go
@@ -0,0 +1,95 @@
+package ldd_test
+
+import (
+ "errors"
+ "reflect"
+ "strings"
+ "testing"
+
+ "git.ophivana.moe/cat/fortify/ldd"
+)
+
+func TestParseError(t *testing.T) {
+ testCases := []struct {
+ name, out string
+ wantErr error
+ }{
+ {"unexpected newline", `
+/lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)
+
+libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
+`, ldd.ErrUnexpectedNewline},
+ {"unexpected separator", `
+libzstd.so.1 = /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
+`, ldd.ErrUnexpectedSeparator},
+ {"path not absolute", `
+libzstd.so.1 => usr/lib/libzstd.so.1 (0x7ff71bfd2000)
+`, ldd.ErrPathNotAbsolute},
+ {"unexpected segments", `
+meow libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
+`, ldd.EntryUnexpectedSegmentsError("meow libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)")},
+ {"bad location format", `
+libzstd.so.1 => /usr/lib/libzstd.so.1 7ff71bfd2000
+`, ldd.ErrBadLocationFormat},
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ stdout := new(strings.Builder)
+ stdout.WriteString(tc.out)
+
+ if _, err := ldd.Parse(stdout); !errors.Is(err, tc.wantErr) {
+ t.Errorf("Parse() error = %v, wantErr %v", err, tc.wantErr)
+ }
+ })
+ }
+}
+
+func TestParse(t *testing.T) {
+ testCases := []struct {
+ file, out string
+ want []*ldd.Entry
+ }{
+ {"musl /bin/kmod", `
+/lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)
+libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
+liblzma.so.5 => /usr/lib/liblzma.so.5 (0x7ff71bf9a000)
+libz.so.1 => /lib/libz.so.1 (0x7ff71bf80000)
+libcrypto.so.3 => /lib/libcrypto.so.3 (0x7ff71ba00000)
+libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`,
+ []*ldd.Entry{
+ {"/lib/ld-musl-x86_64.so.1", "", 0x7ff71c0a4000},
+ {"libzstd.so.1", "/usr/lib/libzstd.so.1", 0x7ff71bfd2000},
+ {"liblzma.so.5", "/usr/lib/liblzma.so.5", 0x7ff71bf9a000},
+ {"libz.so.1", "/lib/libz.so.1", 0x7ff71bf80000},
+ {"libcrypto.so.3", "/lib/libcrypto.so.3", 0x7ff71ba00000},
+ {"libc.musl-x86_64.so.1", "/lib/ld-musl-x86_64.so.1", 0x7ff71c0a4000},
+ }},
+ {"glibc /nix/store/rc3n2r3nffpib2gqpxlkjx36frw6n34z-kmod-31/bin/kmod", `
+linux-vdso.so.1 (0x00007ffed65be000)
+libzstd.so.1 => /nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib/libzstd.so.1 (0x00007f3199cd1000)
+liblzma.so.5 => /nix/store/g78jna1i5qhh8gqs4mr64648f0szqgw4-xz-5.4.7/lib/liblzma.so.5 (0x00007f3199ca2000)
+libc.so.6 => /nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6 (0x00007f3199ab5000)
+libpthread.so.0 => /nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libpthread.so.0 (0x00007f3199ab0000)
+/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/ld-linux-x86-64.so.2 => /nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib64/ld-linux-x86-64.so.2 (0x00007f3199da5000)`,
+ []*ldd.Entry{
+ {"linux-vdso.so.1", "", 0x00007ffed65be000},
+ {"libzstd.so.1", "/nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib/libzstd.so.1", 0x00007f3199cd1000},
+ {"liblzma.so.5", "/nix/store/g78jna1i5qhh8gqs4mr64648f0szqgw4-xz-5.4.7/lib/liblzma.so.5", 0x00007f3199ca2000},
+ {"libc.so.6", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6", 0x00007f3199ab5000},
+ {"libpthread.so.0", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libpthread.so.0", 0x00007f3199ab0000},
+ {"/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/ld-linux-x86-64.so.2", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib64/ld-linux-x86-64.so.2", 0x00007f3199da5000},
+ }},
+ }
+ for _, tc := range testCases {
+ t.Run(tc.file, func(t *testing.T) {
+ stdout := new(strings.Builder)
+ stdout.WriteString(tc.out)
+
+ if got, err := ldd.Parse(stdout); err != nil {
+ t.Errorf("Parse() error = %v", err)
+ } else if !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("Parse() got = %#v, want %#v", got, tc.want)
+ }
+ })
+ }
+}