github.com/zhyoulun/cilium@v1.6.12/pkg/elf/elf.go (about)

     1  // Copyright 2019 Authors of Cilium
     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 elf
    16  
    17  import (
    18  	"debug/elf"
    19  	"encoding/binary"
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"strings"
    24  	"unsafe"
    25  
    26  	"github.com/cilium/cilium/pkg/lock"
    27  
    28  	"github.com/sirupsen/logrus"
    29  )
    30  
    31  var (
    32  	ignoredPrefixes []string
    33  )
    34  
    35  // ELF is an in-memory representation of a BPF ELF object from the filesystem.
    36  type ELF struct {
    37  	metadata *elf.File
    38  	file     *os.File
    39  	symbols  symbols
    40  	log      *logrus.Entry
    41  
    42  	// lock concurrent writes of the ELF. This library probably isn't far
    43  	// off from allowing concurrent Write() execution, but it's just not
    44  	// supported for now.
    45  	lock.Mutex
    46  }
    47  
    48  // newReader creates a new reader that can safely seek the input in parallel.
    49  func newReader(ra io.ReaderAt) *io.SectionReader {
    50  	// If 1<<63-1 is good enough for pkg/debug/elf, it's good enough for us.
    51  	return io.NewSectionReader(ra, 0, 1<<63-1)
    52  }
    53  
    54  // NewELF returns a new object from the specified reader.
    55  //
    56  // The ELF binary is expected to start at position 0 in the specified reader.
    57  func NewELF(ra io.ReaderAt, scopedLog *logrus.Entry) (*ELF, error) {
    58  	ef, err := elf.NewFile(ra)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("unable to open ELF: %s", err)
    61  	}
    62  
    63  	// EM_NONE is generated by older Clang (eg 3.8.x), which we currently
    64  	// use in Travis. We should be able to drop that part pretty soon.
    65  	if ef.Machine != elf.EM_NONE && ef.Machine != elf.EM_BPF {
    66  		return nil, fmt.Errorf("unsupported ELF machine type %s", ef.Machine)
    67  	}
    68  
    69  	result := &ELF{
    70  		metadata: ef,
    71  		log:      scopedLog,
    72  	}
    73  	if err := result.symbols.extractFrom(ef); err != nil {
    74  		return nil, fmt.Errorf("unable to read ELF symbols: %s", err)
    75  	}
    76  
    77  	return result, nil
    78  }
    79  
    80  // Open an ELF file from the specified path.
    81  func Open(path string) (*ELF, error) {
    82  	scopedLog := log.WithField(srcPath, path)
    83  
    84  	f, err := os.Open(path)
    85  	if err != nil {
    86  		return nil, &os.PathError{
    87  			Op:   "failed to open ELF file",
    88  			Path: path,
    89  			Err:  err,
    90  		}
    91  	}
    92  
    93  	result, err := NewELF(f, scopedLog)
    94  	if err != nil {
    95  		if err2 := f.Close(); err2 != nil {
    96  			scopedLog.WithError(err).Warning("Failed to close ELF")
    97  		}
    98  		return nil, &os.PathError{
    99  			Op:   "failed to parse ELF file",
   100  			Path: path,
   101  			Err:  err,
   102  		}
   103  	}
   104  	result.file = f
   105  	return result, nil
   106  }
   107  
   108  // Close closes the ELF. If the File was created using NewELF directly instead
   109  // of Open, Close has no effect.
   110  func (elf *ELF) Close() (err error) {
   111  	if elf.file != nil {
   112  		err = elf.file.Close()
   113  	}
   114  	return err
   115  }
   116  
   117  func (elf *ELF) readValue(offset int64, size int64) ([]byte, error) {
   118  	reader := io.NewSectionReader(elf.file, offset, size)
   119  	result := make([]byte, size)
   120  	if err := binary.Read(reader, elf.metadata.ByteOrder, &result); err != nil {
   121  		return nil, err
   122  	}
   123  	return result, nil
   124  }
   125  
   126  func (elf *ELF) readOption(key string) (result uint32, err error) {
   127  	opt, exists := elf.symbols.data[key]
   128  	if !exists {
   129  		return 0, fmt.Errorf("no such option %q in ELF", key)
   130  	}
   131  	value, err := elf.readValue(int64(opt.offset), int64(opt.size))
   132  	if err != nil {
   133  		return 0, err
   134  	}
   135  	return elf.metadata.ByteOrder.Uint32(value), err
   136  }
   137  
   138  func (elf *ELF) findString(key string) error {
   139  	opt, exists := elf.symbols.strings[key]
   140  	if !exists {
   141  		return fmt.Errorf("no such string %q in ELF", key)
   142  	}
   143  	if _, err := elf.readValue(int64(opt.offset), int64(opt.size)); err != nil {
   144  		return err
   145  	}
   146  	return nil
   147  }
   148  
   149  // copy the ELF from the reader to the writer, substituting the constants with
   150  // names specified in 'intOptions' with their corresponding values, and the
   151  // strings specified in 'strOptions' with their corresponding values.
   152  //
   153  // Keys in the 'intOptions' / 'strOptions' maps are case-sensitive.
   154  func (elf *ELF) copy(w io.Writer, r *io.SectionReader, intOptions map[string]uint32, strOptions map[string]string) error {
   155  	if len(intOptions) == 0 && len(strOptions) == 0 {
   156  		// Copy the remaining portion of the file
   157  		if _, err := io.Copy(w, r); err != nil {
   158  			return err
   159  		}
   160  		return nil
   161  	}
   162  
   163  	globalOff := uint64(0) // current position in file
   164  	processedOptions := make(map[string]struct{}, len(intOptions)+len(strOptions))
   165  processSymbols:
   166  	for _, symbol := range elf.symbols.sort() {
   167  		scopedLog := log.WithField("symbol", symbol.name)
   168  
   169  		// Figure out the value to substitute
   170  		var value []byte
   171  		switch symbol.kind {
   172  		case symbolUint32:
   173  			v, exists := intOptions[symbol.name]
   174  			if exists {
   175  				value = make([]byte, unsafe.Sizeof(v))
   176  				elf.metadata.ByteOrder.PutUint32(value, v)
   177  			}
   178  		case symbolString:
   179  			v, exists := strOptions[symbol.name]
   180  			if exists {
   181  				if uint64(len(v)) != symbol.size {
   182  					return fmt.Errorf("symbol substitution value %q (len %d) must equal length of symbol name %q (len %d)", v, len(v), symbol.name, symbol.size)
   183  				}
   184  				value = []byte(v)
   185  			}
   186  		}
   187  		if value == nil {
   188  			for _, prefix := range ignoredPrefixes {
   189  				if strings.HasPrefix(symbol.name, prefix) {
   190  					continue processSymbols
   191  				}
   192  			}
   193  			scopedLog.Warning("Skipping symbol substitution")
   194  			continue processSymbols
   195  		}
   196  
   197  		// Copy data up until this symbol into the new file;
   198  		// Write the new value and seek past it.
   199  		dataToCopy := int64(symbol.offset - globalOff)
   200  		if _, err := io.CopyN(w, r, dataToCopy); err != nil {
   201  			return err
   202  		}
   203  		if err := binary.Write(w, elf.metadata.ByteOrder, value); err != nil {
   204  			return fmt.Errorf("failed to substitute %s: %s", symbol.name, err)
   205  		}
   206  		if _, err := r.Seek(int64(symbol.size), io.SeekCurrent); err != nil {
   207  			return err
   208  		}
   209  		processedOptions[symbol.name] = struct{}{}
   210  		globalOff = symbol.offset + symbol.size
   211  	}
   212  
   213  	// Check for additional options that weren't applied
   214  	for symbol := range strOptions {
   215  		if _, processed := processedOptions[symbol]; !processed {
   216  			return fmt.Errorf("no such string %q in ELF", symbol)
   217  		}
   218  	}
   219  	for symbol := range intOptions {
   220  		if _, processed := processedOptions[symbol]; !processed {
   221  			return fmt.Errorf("no such symbol %q in ELF", symbol)
   222  		}
   223  	}
   224  
   225  	// Copy the remaining portion of the file
   226  	if _, err := io.Copy(w, r); err != nil {
   227  		return err
   228  	}
   229  
   230  	return nil
   231  }
   232  
   233  // Write the received ELF to a new file at the specified location, with the
   234  // specified options (indexed by name) substituted:
   235  // - intOptions: 32-bit values substituted in the data section.
   236  // - strOptions: strings susbtituted in the string table. For each key/value
   237  //               pair, both key and value must be same length.
   238  //
   239  // Only one goroutine may Write() the same *ELF concurrently.
   240  //
   241  // On success, writes the new file to the specified path.
   242  // On failure, returns an error and no file is left on the filesystem.
   243  func (elf *ELF) Write(path string, intOptions map[string]uint32, strOptions map[string]string) error {
   244  	elf.Lock()
   245  	defer elf.Unlock()
   246  
   247  	scopedLog := elf.log.WithField(dstPath, path)
   248  
   249  	f, err := os.Create(path)
   250  	if err != nil {
   251  		return &os.PathError{
   252  			Op:   "failed to create ELF file",
   253  			Path: path,
   254  			Err:  err,
   255  		}
   256  	}
   257  	defer func() {
   258  		if err2 := f.Close(); err2 != nil {
   259  			scopedLog.WithError(err).Warning("Failed to close new ELF")
   260  		}
   261  		if err != nil {
   262  			if err2 := os.RemoveAll(path); err2 != nil {
   263  				scopedLog.WithError(err).Warning("Failed to clean up new ELF path on error")
   264  			}
   265  		}
   266  	}()
   267  
   268  	reader := newReader(elf.file)
   269  	if err = elf.copy(f, reader, intOptions, strOptions); err != nil {
   270  		return &os.PathError{
   271  			Op:   "failed to write ELF file:",
   272  			Path: path,
   273  			Err:  err,
   274  		}
   275  	}
   276  	if err = f.Sync(); err != nil {
   277  		return &os.PathError{
   278  			Op:   "failed to sync ELF file:",
   279  			Path: path,
   280  			Err:  err,
   281  		}
   282  	}
   283  
   284  	scopedLog.WithError(err).Debugf("Finished writing ELF")
   285  	return nil
   286  }
   287  
   288  // IgnoreSymbolPrefixes configures the ELF package to ignore symbols that have
   289  // any of the specified prefixes. It must be called by only one thread at a time.
   290  //
   291  // This slice will be iterated once per ELF.Write(), so try not to let it grow
   292  // out of hand...
   293  func IgnoreSymbolPrefixes(prefixes []string) {
   294  	ignoredPrefixes = prefixes
   295  }