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