github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/uprobetracer/ldcache_parser.go (about) 1 // Copyright 2024 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package uprobetracer 16 17 import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "os" 22 "strings" 23 "unsafe" 24 ) 25 26 type ldCache1Entry struct { 27 Flags int32 28 Key uint32 29 Value uint32 30 } 31 32 type ldCache1 struct { 33 Header [11]int8 34 EntryCount uint32 35 } 36 37 type ldCache2Entry struct { 38 Flags int32 39 Key uint32 40 Value uint32 41 Pad1_ uint32 42 Pad2_ uint64 43 } 44 45 type ldCache2 struct { 46 Header [17]int8 47 Version [3]int8 48 EntryCount uint32 49 StringTableLen uint32 50 Pad_ [5]uint32 51 } 52 53 const ( 54 cache1Header string = "ld.so-1.7.0" 55 cache2Header string = "glibc-ld.so.cache1.1" 56 57 ldCache1EntrySize = uint32(unsafe.Sizeof(ldCache1Entry{})) 58 ldCache1Size = uint32(unsafe.Sizeof(ldCache1{})) 59 ldCache2EntrySize = uint32(unsafe.Sizeof(ldCache2Entry{})) 60 ldCache2Size = uint32(unsafe.Sizeof(ldCache2{})) 61 ) 62 63 type ldEntry struct { 64 Key string 65 Value string 66 } 67 68 func readCacheFormat1(data []byte) []ldEntry { 69 var ldEntries []ldEntry 70 71 ldCache := ldCache1{} 72 if uint32(len(data)) <= ldCache1Size { 73 return nil 74 } 75 err := readFromBytes(&ldCache, data[:ldCache1Size]) 76 if err != nil { 77 return nil 78 } 79 ldEntriesOffset := ldCache1Size 80 ldStringsOffset := ldCache1Size + ldCache1EntrySize*ldCache.EntryCount 81 for i := uint32(0); i < ldCache.EntryCount; i++ { 82 entryOffset := ldEntriesOffset + i*ldCache1EntrySize 83 entry := ldCache1Entry{} 84 if uint32(len(data)) <= entryOffset+ldCache1EntrySize { 85 return nil 86 } 87 err := readFromBytes(&entry, data[entryOffset:entryOffset+ldCache1EntrySize]) 88 if err != nil { 89 return nil 90 } 91 keyOffset := ldStringsOffset + entry.Key 92 valueOffset := ldStringsOffset + entry.Value 93 key := readStringFromBytes(data, keyOffset) 94 value := readStringFromBytes(data, valueOffset) 95 ldEntries = append(ldEntries, ldEntry{key, value}) 96 } 97 return ldEntries 98 } 99 100 func readCacheFormat2(data []byte) []ldEntry { 101 var ldEntries []ldEntry 102 103 if !bytes.Equal([]byte(cache2Header), data[:len(cache2Header)]) { 104 return nil 105 } 106 ldCache := ldCache2{} 107 if uint32(len(data)) <= ldCache2Size { 108 return nil 109 } 110 err := readFromBytes(&ldCache, data[:ldCache2Size]) 111 if err != nil { 112 return nil 113 } 114 ldEntriesOffset := ldCache2Size 115 for i := uint32(0); i < ldCache.EntryCount; i++ { 116 entryOffset := ldEntriesOffset + i*ldCache2EntrySize 117 entry := ldCache2Entry{} 118 if uint32(len(data)) <= entryOffset+ldCache2EntrySize { 119 return nil 120 } 121 err := readFromBytes(&entry, data[entryOffset:entryOffset+ldCache2EntrySize]) 122 if err != nil { 123 return nil 124 } 125 keyOffset := entry.Key 126 valueOffset := entry.Value 127 key := readStringFromBytes(data, keyOffset) 128 value := readStringFromBytes(data, valueOffset) 129 ldEntries = append(ldEntries, ldEntry{key, value}) 130 } 131 return ldEntries 132 } 133 134 // simulate the loader's behaviour, find library path in containers' `/etc/ld.so.cache`. 135 // see https://github.com/bminor/glibc/blob/master/elf/cache.c#L292, the `print_cache` func 136 // see https://github.com/iovisor/bcc/blob/master/src/cc/bcc_proc.c#L508, the `bcc_procutils_which_so` func 137 func parseLdCache(ldCachePath string, libraryName string) ([]string, error) { 138 fileInfo, err := os.Stat(ldCachePath) 139 if err != nil { 140 return nil, fmt.Errorf("stat file %q: %w", ldCachePath, err) 141 } 142 if !fileInfo.Mode().IsRegular() { 143 return nil, fmt.Errorf("ldCache file %q is not regular", ldCachePath) 144 } 145 ldCacheFile, err := os.ReadFile(ldCachePath) 146 if err != nil { 147 return nil, fmt.Errorf("reading file %q: %w", ldCachePath, err) 148 } 149 ldCacheFileSize := uint32(len(ldCacheFile)) 150 151 var ldEntries []ldEntry 152 var filteredLibraries []string 153 154 if bytes.Equal([]byte(cache1Header), ldCacheFile[:len(cache1Header)]) { 155 cache1 := ldCache1{} 156 if uint32(len(ldCacheFile)) <= ldCache1Size { 157 return nil, errors.New("ldCache format error") 158 } 159 err := readFromBytes(&cache1, ldCacheFile[:ldCache1Size]) 160 if err != nil { 161 return nil, errors.New("ldCache format error") 162 } 163 cache1Len := ldCache1Size + cache1.EntryCount*ldCache1EntrySize 164 cache1Len = (cache1Len + 7) / 8 * 8 165 if ldCacheFileSize > (cache1Len + ldCache2Size) { 166 ldEntries = readCacheFormat2(ldCacheFile) 167 } else { 168 ldEntries = readCacheFormat1(ldCacheFile) 169 } 170 } else { 171 ldEntries = readCacheFormat2(ldCacheFile) 172 } 173 174 if ldEntries == nil { 175 return nil, errors.New("ldCache format error") 176 } 177 178 // filter library entries with given library name 179 for _, entry := range ldEntries { 180 if strings.HasPrefix(entry.Key, libraryName+".so") { 181 filteredLibraries = append(filteredLibraries, entry.Value) 182 } 183 } 184 185 return filteredLibraries, nil 186 }