github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/ldd/ldd_unix.go (about) 1 // Copyright 2009-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build freebsd || linux 6 // +build freebsd linux 7 8 // ldd returns all the library dependencies of an executable. 9 // 10 // The way this is done on GNU-based systems is interesting. For each ELF, one 11 // finds the .interp section. If there is no interpreter 12 // there's not much to do. 13 // 14 // If there is an interpreter, we run it with the --list option and the file as 15 // an argument. We need to parse the output. 16 // 17 // For all lines with => as the 2nd field, we take the 3rd field as a 18 // dependency. The field may be a symlink. Rather than stat the link and do 19 // other such fooling around, we can do a readlink on it; if it fails, we just 20 // need to add that file name; if it succeeds, we need to add that file name 21 // and repeat with the next link in the chain. We can let the kernel do the 22 // work of figuring what to do if and when we hit EMLINK. 23 package ldd 24 25 import ( 26 "debug/elf" 27 "fmt" 28 "os" 29 "os/exec" 30 "path/filepath" 31 "strings" 32 ) 33 34 // Follow starts at a pathname and adds it 35 // to a map if it is not there. 36 // If the pathname is a symlink, indicated by the Readlink 37 // succeeding, links repeats and continues 38 // for as long as the name is not found in the map. 39 func follow(l string, names map[string]*FileInfo) error { 40 for { 41 if names[l] != nil { 42 return nil 43 } 44 i, err := os.Lstat(l) 45 if err != nil { 46 return fmt.Errorf("%v", err) 47 } 48 49 names[l] = &FileInfo{FullName: l, FileInfo: i} 50 if i.Mode().IsRegular() { 51 return nil 52 } 53 // If it's a symlink, the read works; if not, it fails. 54 // we can skip testing the type, since we still have to 55 // handle any error if it's a link. 56 next, err := os.Readlink(l) 57 if err != nil { 58 return err 59 } 60 // It may be a relative link, so we need to 61 // make it abs. 62 if filepath.IsAbs(next) { 63 l = next 64 continue 65 } 66 l = filepath.Join(filepath.Dir(l), next) 67 } 68 } 69 70 func parseinterp(input string) ([]string, error) { 71 var names []string 72 for _, p := range strings.Split(input, "\n") { 73 f := strings.Fields(p) 74 if len(f) < 3 { 75 continue 76 } 77 if f[1] != "=>" || len(f[2]) == 0 { 78 continue 79 } 80 if f[0] == f[2] { 81 continue 82 } 83 names = append(names, f[2]) 84 } 85 return names, nil 86 } 87 88 // runinterp runs the interpreter with the --list switch 89 // and the file as an argument. For each returned line 90 // it looks for => as the second field, indicating a 91 // real .so (as opposed to the .vdso or a string like 92 // 'not a dynamic executable'. 93 func runinterp(interp, file string) ([]string, error) { 94 o, err := exec.Command(interp, "--list", file).Output() 95 if err != nil { 96 return nil, err 97 } 98 return parseinterp(string(o)) 99 } 100 101 type FileInfo struct { 102 FullName string 103 os.FileInfo 104 } 105 106 func GetInterp(file string) (string, error) { 107 r, err := os.Open(file) 108 if err != nil { 109 return "fail", err 110 } 111 defer r.Close() 112 f, err := elf.NewFile(r) 113 if err != nil { 114 return "", nil 115 } 116 s := f.Section(".interp") 117 var interp string 118 if s != nil { 119 // If there is an interpreter section, it should be 120 // an error if we can't read it. 121 i, err := s.Data() 122 if err != nil { 123 return "fail", err 124 } 125 // Ignore #! interpreters 126 if len(i) > 1 && i[0] == '#' && i[1] == '!' { 127 return "", nil 128 } 129 // annoyingly, s.Data() seems to return the null at the end and, 130 // weirdly, that seems to confuse the kernel. Truncate it. 131 interp = string(i[:len(i)-1]) 132 } 133 if interp == "" { 134 if f.Type != elf.ET_DYN || f.Class == elf.ELFCLASSNONE { 135 return "", nil 136 } 137 bit64 := true 138 if f.Class != elf.ELFCLASS64 { 139 bit64 = false 140 } 141 142 // This is a shared library. Turns out you can run an 143 // interpreter with --list and this shared library as an 144 // argument. What interpreter do we use? Well, there's no way to 145 // know. You have to guess. I'm not sure why they could not 146 // just put an interp section in .so's but maybe that would 147 // cause trouble somewhere else. 148 interp, err = LdSo(bit64) 149 if err != nil { 150 return "fail", err 151 } 152 } 153 return interp, nil 154 } 155 156 // Ldd returns a list of all library dependencies for a set of files. 157 // 158 // If a file has no dependencies, that is not an error. The only possible error 159 // is if a file does not exist, or it says it has an interpreter but we can't 160 // read it, or we are not able to run its interpreter. 161 // 162 // It's not an error for a file to not be an ELF. 163 func Ldd(names []string) ([]*FileInfo, error) { 164 var ( 165 list = make(map[string]*FileInfo) 166 interps = make(map[string]*FileInfo) 167 libs []*FileInfo 168 ) 169 for _, n := range names { 170 if err := follow(n, list); err != nil { 171 return nil, err 172 } 173 } 174 for _, n := range names { 175 interp, err := GetInterp(n) 176 if err != nil { 177 return nil, err 178 } 179 if interp == "" { 180 continue 181 } 182 // We could just append the interp but people 183 // expect to see that first. 184 if interps[interp] == nil { 185 err := follow(interp, interps) 186 if err != nil { 187 return nil, err 188 } 189 } 190 // oh boy. Now to run the interp and get more names. 191 n, err := runinterp(interp, n) 192 if err != nil { 193 return nil, err 194 } 195 for _, soname := range n { 196 if err := follow(soname, list); err != nil { 197 return nil, err 198 } 199 } 200 } 201 202 for i := range interps { 203 libs = append(libs, interps[i]) 204 } 205 206 for i := range list { 207 libs = append(libs, list[i]) 208 } 209 210 return libs, nil 211 } 212 213 // List returns the dependency file paths of files in names. 214 func List(names []string) ([]string, error) { 215 var list []string 216 l, err := Ldd(names) 217 if err != nil { 218 return nil, err 219 } 220 for i := range l { 221 list = append(list, l[i].FullName) 222 } 223 return list, nil 224 }