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  }