From 80672d33af1ea318e041987470f172d2b8f49d97 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Mon, 5 Dec 2022 16:17:39 +0100 Subject: [PATCH 1/5] Continue instead of break on error when listing libraries Signed-off-by: Evan Lezar --- internal/ldcache/ldcache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/ldcache/ldcache.go b/internal/ldcache/ldcache.go index c26c47ca..15115f3f 100644 --- a/internal/ldcache/ldcache.go +++ b/internal/ldcache/ldcache.go @@ -208,7 +208,7 @@ func (c *ldcache) List() ([]string, []string) { n := bytes.IndexByte(value, 0) if n < 0 { - break + continue } name := filepath.Join(c.root, strn(value, n)) @@ -217,7 +217,7 @@ func (c *ldcache) List() ([]string, []string) { path, err := filepath.EvalSymlinks(name) if err != nil { c.logger.Debugf("could not resolve symlink for %v", name) - break + continue } if processed[path] { continue From f350f0c0bb6013591b6d842bb7aec66393c53a00 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 13 Dec 2022 14:28:49 +0100 Subject: [PATCH 2/5] Refactor resolving of links in ldcache Signed-off-by: Evan Lezar --- internal/ldcache/ldcache.go | 64 ++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/internal/ldcache/ldcache.go b/internal/ldcache/ldcache.go index 15115f3f..e128172c 100644 --- a/internal/ldcache/ldcache.go +++ b/internal/ldcache/ldcache.go @@ -22,6 +22,7 @@ import ( "bytes" "encoding/binary" "errors" + "fmt" "os" "path/filepath" "syscall" @@ -205,18 +206,14 @@ func (c *ldcache) List() ([]string, []string) { continue } value := c.libs[e.Value:] - - n := bytes.IndexByte(value, 0) - if n < 0 { + name := bytesToString(value) + if name == "" { continue } - name := filepath.Join(c.root, strn(value, n)) - c.logger.Debugf("checking %v", string(name)) - - path, err := filepath.EvalSymlinks(name) + path, err := c.resolve(name) if err != nil { - c.logger.Debugf("could not resolve symlink for %v", name) + c.logger.Debugf("Could not resolve entry: %v", err) continue } if processed[path] { @@ -233,10 +230,9 @@ func (c *ldcache) List() ([]string, []string) { // The 32-bit and 64-bit libraries matching the prefixes are returned. func (c *ldcache) Lookup(libs ...string) (paths32, paths64 []string) { c.logger.Debugf("Looking up %v in cache", libs) - type void struct{} var paths *[]string - set := make(map[string]void) + processed := make(map[string]bool) prefix := make([][]byte, len(libs)) for i := range libs { @@ -264,25 +260,22 @@ func (c *ldcache) Lookup(libs ...string) (paths32, paths64 []string) { lib := c.libs[e.Key:] value := c.libs[e.Value:] + name := bytesToString(value) + if name == "" { + continue + } + for _, p := range prefix { if bytes.HasPrefix(lib, p) { - n := bytes.IndexByte(value, 0) - if n < 0 { - break - } - - name := filepath.Join(c.root, strn(value, n)) - c.logger.Debugf("checking %v", string(name)) - - path, err := filepath.EvalSymlinks(name) + path, err := c.resolve(name) if err != nil { - c.logger.Debugf("could not resolve symlink for %v", name) + c.logger.Debugf("Could not resolve entry: %v", err) break } - if _, ok := set[path]; ok { + if processed[path] { break } - set[path] = void{} + processed[path] = true *paths = append(*paths, path) break } @@ -290,3 +283,30 @@ func (c *ldcache) Lookup(libs ...string) (paths32, paths64 []string) { } return } + +// resolve resolves the specified ldcache entry based on the value being processed. +// The input is the name of the entry in the cache. +func (c *ldcache) resolve(target string) (string, error) { + name := filepath.Join(c.root, target) + + c.logger.Debugf("checking %v", name) + + link, err := filepath.EvalSymlinks(name) + if err != nil { + return "", fmt.Errorf("failed to resolve symlink: %v", err) + } + + c.logger.Debugf("Resolved link: '%v' => '%v'", name, link) + return link, nil +} + +// bytesToString converts a byte slice to a string. +// This assumes that the byte slice is null-terminated +func bytesToString(value []byte) string { + n := bytes.IndexByte(value, 0) + if n < 0 { + return "" + } + + return strn(value, n) +} From ccd1961c6066caca854e12fa3aa338b770128a8a Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 13 Dec 2022 14:34:53 +0100 Subject: [PATCH 3/5] Ensure root is included in absolute ldcache paths Signed-off-by: Evan Lezar --- internal/ldcache/ldcache.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/ldcache/ldcache.go b/internal/ldcache/ldcache.go index e128172c..73c63ee7 100644 --- a/internal/ldcache/ldcache.go +++ b/internal/ldcache/ldcache.go @@ -289,13 +289,27 @@ func (c *ldcache) Lookup(libs ...string) (paths32, paths64 []string) { func (c *ldcache) resolve(target string) (string, error) { name := filepath.Join(c.root, target) - c.logger.Debugf("checking %v", name) + c.logger.Debugf("checking %v", string(name)) - link, err := filepath.EvalSymlinks(name) + info, err := os.Lstat(name) + if err != nil { + return "", fmt.Errorf("failed to get file info: %v", info) + } + if info.Mode()&os.ModeSymlink == 0 { + c.logger.Debugf("Resolved regular file: %v", name) + return name, nil + } + + link, err := os.Readlink(name) if err != nil { return "", fmt.Errorf("failed to resolve symlink: %v", err) } + if filepath.IsAbs(link) { + c.logger.Debugf("Found absolute link %v", link) + link = filepath.Join(c.root, link) + } + c.logger.Debugf("Resolved link: '%v' => '%v'", name, link) return link, nil } From 3537d76726dc0f552959bcf692adb1653fa18008 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 13 Dec 2022 14:56:15 +0100 Subject: [PATCH 4/5] Further refactoring of ldcache code Signed-off-by: Evan Lezar --- internal/ldcache/ldcache.go | 123 +++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/internal/ldcache/ldcache.go b/internal/ldcache/ldcache.go index 73c63ee7..ed69b377 100644 --- a/internal/ldcache/ldcache.go +++ b/internal/ldcache/ldcache.go @@ -25,6 +25,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "syscall" "unsafe" @@ -178,13 +179,15 @@ func (c *ldcache) parse() error { return nil } -// List creates a list of libraires in the ldcache. -// The 32-bit and 64-bit libraries are returned separately. -func (c *ldcache) List() ([]string, []string) { - paths := make(map[int][]string) - - processed := make(map[string]bool) +type entry struct { + libname string + bits int + value string +} +// getEntries returns the entires of the ldcache in a go-friendly struct. +func (c *ldcache) getEntries(selected func(string) bool) []entry { + var entries []entry for _, e := range c.entries { bits := 0 if ((e.Flags & flagTypeMask) & flagTypeELF) == 0 { @@ -205,13 +208,39 @@ func (c *ldcache) List() ([]string, []string) { if e.Key > uint32(len(c.libs)) || e.Value > uint32(len(c.libs)) { continue } - value := c.libs[e.Value:] - name := bytesToString(value) - if name == "" { + lib := bytesToString(c.libs[e.Key:]) + if lib == "" { + c.logger.Debugf("Skipping invalid lib") continue } + if !selected(lib) { + continue + } + value := bytesToString(c.libs[e.Value:]) + if value == "" { + c.logger.Debugf("Skipping invalid value for lib %v", lib) + continue + } + e := entry{ + libname: lib, + bits: bits, + value: value, + } - path, err := c.resolve(name) + entries = append(entries, e) + } + + return entries +} + +// List creates a list of libraires in the ldcache. +// The 32-bit and 64-bit libraries are returned separately. +func (c *ldcache) List() ([]string, []string) { + all := func(s string) bool { return true } + paths := make(map[int][]string) + processed := make(map[string]bool) + for _, e := range c.getEntries(all) { + path, err := c.resolve(e.value) if err != nil { c.logger.Debugf("Could not resolve entry: %v", err) continue @@ -219,7 +248,7 @@ func (c *ldcache) List() ([]string, []string) { if processed[path] { continue } - paths[bits] = append(paths[bits], path) + paths[e.bits] = append(paths[e.bits], path) processed[path] = true } @@ -228,60 +257,36 @@ func (c *ldcache) List() ([]string, []string) { // Lookup searches the ldcache for the specified prefixes. // The 32-bit and 64-bit libraries matching the prefixes are returned. -func (c *ldcache) Lookup(libs ...string) (paths32, paths64 []string) { - c.logger.Debugf("Looking up %v in cache", libs) - var paths *[]string +func (c *ldcache) Lookup(libPrefixes ...string) ([]string, []string) { + c.logger.Debugf("Looking up %v in cache", libPrefixes) + paths := make(map[int][]string) processed := make(map[string]bool) - prefix := make([][]byte, len(libs)) - for i := range libs { - prefix[i] = []byte(libs[i]) - } - for _, e := range c.entries { - if ((e.Flags & flagTypeMask) & flagTypeELF) == 0 { - continue - } - switch e.Flags & flagArchMask { - case flagArchX8664: - fallthrough - case flagArchPpc64le: - paths = &paths64 - case flagArchX32: - fallthrough - case flagArchI386: - paths = &paths32 - default: - continue - } - if e.Key > uint32(len(c.libs)) || e.Value > uint32(len(c.libs)) { - continue - } - lib := c.libs[e.Key:] - value := c.libs[e.Value:] - - name := bytesToString(value) - if name == "" { - continue - } - - for _, p := range prefix { - if bytes.HasPrefix(lib, p) { - path, err := c.resolve(name) - if err != nil { - c.logger.Debugf("Could not resolve entry: %v", err) - break - } - if processed[path] { - break - } - processed[path] = true - *paths = append(*paths, path) - break + // We define a functor to check whether a given library name matches any of the prefixes + matchesAnyPrefix := func(s string) bool { + for _, p := range libPrefixes { + if strings.HasPrefix(s, p) { + return true } } + return false } - return + + for _, e := range c.getEntries(matchesAnyPrefix) { + path, err := c.resolve(e.value) + if err != nil { + c.logger.Debug("Could not resolve entry: %v", err) + continue + } + if processed[path] { + continue + } + paths[e.bits] = append(paths[e.bits], path) + processed[path] = true + } + + return paths[32], paths[64] } // resolve resolves the specified ldcache entry based on the value being processed. From 881b1c0e080c7e4121a16014582a07c1a8d87c67 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Thu, 19 Jan 2023 14:08:22 +0100 Subject: [PATCH 5/5] introduce resolveSelected helper Signed-off-by: Evan Lezar --- internal/ldcache/ldcache.go | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/internal/ldcache/ldcache.go b/internal/ldcache/ldcache.go index ed69b377..eefaad2f 100644 --- a/internal/ldcache/ldcache.go +++ b/internal/ldcache/ldcache.go @@ -237,22 +237,8 @@ func (c *ldcache) getEntries(selected func(string) bool) []entry { // The 32-bit and 64-bit libraries are returned separately. func (c *ldcache) List() ([]string, []string) { all := func(s string) bool { return true } - paths := make(map[int][]string) - processed := make(map[string]bool) - for _, e := range c.getEntries(all) { - path, err := c.resolve(e.value) - if err != nil { - c.logger.Debugf("Could not resolve entry: %v", err) - continue - } - if processed[path] { - continue - } - paths[e.bits] = append(paths[e.bits], path) - processed[path] = true - } - return paths[32], paths[64] + return c.resolveSelected(all) } // Lookup searches the ldcache for the specified prefixes. @@ -260,9 +246,6 @@ func (c *ldcache) List() ([]string, []string) { func (c *ldcache) Lookup(libPrefixes ...string) ([]string, []string) { c.logger.Debugf("Looking up %v in cache", libPrefixes) - paths := make(map[int][]string) - processed := make(map[string]bool) - // We define a functor to check whether a given library name matches any of the prefixes matchesAnyPrefix := func(s string) bool { for _, p := range libPrefixes { @@ -273,10 +256,19 @@ func (c *ldcache) Lookup(libPrefixes ...string) ([]string, []string) { return false } - for _, e := range c.getEntries(matchesAnyPrefix) { + return c.resolveSelected(matchesAnyPrefix) +} + +// resolveSelected process the entries in the LDCach based on the supplied filter and returns the resolved paths. +// The paths are separated by bittage. +func (c *ldcache) resolveSelected(selected func(string) bool) ([]string, []string) { + paths := make(map[int][]string) + processed := make(map[string]bool) + + for _, e := range c.getEntries(selected) { path, err := c.resolve(e.value) if err != nil { - c.logger.Debug("Could not resolve entry: %v", err) + c.logger.Debugf("Could not resolve entry: %v", err) continue } if processed[path] {