github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/loader/elf.go (about)

     1  // Copyright 2018 The gVisor 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 loader
    16  
    17  import (
    18  	"bytes"
    19  	"debug/elf"
    20  	"fmt"
    21  	"io"
    22  
    23  	"github.com/MerlinKodo/gvisor/pkg/abi"
    24  	"github.com/MerlinKodo/gvisor/pkg/abi/linux"
    25  	"github.com/MerlinKodo/gvisor/pkg/context"
    26  	"github.com/MerlinKodo/gvisor/pkg/cpuid"
    27  	"github.com/MerlinKodo/gvisor/pkg/errors/linuxerr"
    28  	"github.com/MerlinKodo/gvisor/pkg/hostarch"
    29  	"github.com/MerlinKodo/gvisor/pkg/log"
    30  	"github.com/MerlinKodo/gvisor/pkg/sentry/arch"
    31  	"github.com/MerlinKodo/gvisor/pkg/sentry/limits"
    32  	"github.com/MerlinKodo/gvisor/pkg/sentry/memmap"
    33  	"github.com/MerlinKodo/gvisor/pkg/sentry/mm"
    34  	"github.com/MerlinKodo/gvisor/pkg/sentry/vfs"
    35  	"github.com/MerlinKodo/gvisor/pkg/usermem"
    36  )
    37  
    38  const (
    39  	// elfMagic identifies an ELF file.
    40  	elfMagic = "\x7fELF"
    41  
    42  	// maxTotalPhdrSize is the maximum combined size of all program
    43  	// headers.  Linux limits this to one page.
    44  	maxTotalPhdrSize = hostarch.PageSize
    45  )
    46  
    47  var (
    48  	// header64Size is the size of elf.Header64.
    49  	header64Size = (*linux.ElfHeader64)(nil).SizeBytes()
    50  
    51  	// Prog64Size is the size of elf.Prog64.
    52  	prog64Size = (*linux.ElfProg64)(nil).SizeBytes()
    53  )
    54  
    55  func progFlagsAsPerms(f elf.ProgFlag) hostarch.AccessType {
    56  	var p hostarch.AccessType
    57  	if f&elf.PF_R == elf.PF_R {
    58  		p.Read = true
    59  	}
    60  	if f&elf.PF_W == elf.PF_W {
    61  		p.Write = true
    62  	}
    63  	if f&elf.PF_X == elf.PF_X {
    64  		p.Execute = true
    65  	}
    66  	return p
    67  }
    68  
    69  // elfInfo contains the metadata needed to load an ELF binary.
    70  type elfInfo struct {
    71  	// os is the target OS of the ELF.
    72  	os abi.OS
    73  
    74  	// arch is the target architecture of the ELF.
    75  	arch arch.Arch
    76  
    77  	// entry is the program entry point.
    78  	entry hostarch.Addr
    79  
    80  	// phdrs are the program headers.
    81  	phdrs []elf.ProgHeader
    82  
    83  	// phdrSize is the size of a single program header in the ELF.
    84  	phdrSize int
    85  
    86  	// phdrOff is the offset of the program headers in the file.
    87  	phdrOff uint64
    88  
    89  	// sharedObject is true if the ELF represents a shared object.
    90  	sharedObject bool
    91  }
    92  
    93  type fullReader interface {
    94  	// ReadFull is the same as vfs.FileDescription.ReadFull.
    95  	ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error)
    96  }
    97  
    98  // parseHeader parse the ELF header, verifying that this is a supported ELF
    99  // file and returning the ELF program headers.
   100  //
   101  // This is similar to elf.NewFile, except that it is more strict about what it
   102  // accepts from the ELF, and it doesn't parse unnecessary parts of the file.
   103  func parseHeader(ctx context.Context, f fullReader) (elfInfo, error) {
   104  	// Check ident first; it will tell us the endianness of the rest of the
   105  	// structs.
   106  	var ident [elf.EI_NIDENT]byte
   107  	_, err := f.ReadFull(ctx, usermem.BytesIOSequence(ident[:]), 0)
   108  	if err != nil {
   109  		log.Infof("Error reading ELF ident: %v", err)
   110  		// The entire ident array always exists.
   111  		if err == io.EOF || err == io.ErrUnexpectedEOF {
   112  			err = linuxerr.ENOEXEC
   113  		}
   114  		return elfInfo{}, err
   115  	}
   116  
   117  	// Only some callers pre-check the ELF magic.
   118  	if !bytes.Equal(ident[:len(elfMagic)], []byte(elfMagic)) {
   119  		log.Infof("File is not an ELF")
   120  		return elfInfo{}, linuxerr.ENOEXEC
   121  	}
   122  
   123  	// We only support 64-bit, little endian binaries
   124  	if class := elf.Class(ident[elf.EI_CLASS]); class != elf.ELFCLASS64 {
   125  		log.Infof("Unsupported ELF class: %v", class)
   126  		return elfInfo{}, linuxerr.ENOEXEC
   127  	}
   128  	if endian := elf.Data(ident[elf.EI_DATA]); endian != elf.ELFDATA2LSB {
   129  		log.Infof("Unsupported ELF endianness: %v", endian)
   130  		return elfInfo{}, linuxerr.ENOEXEC
   131  	}
   132  
   133  	if version := elf.Version(ident[elf.EI_VERSION]); version != elf.EV_CURRENT {
   134  		log.Infof("Unsupported ELF version: %v", version)
   135  		return elfInfo{}, linuxerr.ENOEXEC
   136  	}
   137  	// EI_OSABI is ignored by Linux, which is the only OS supported.
   138  	os := abi.Linux
   139  
   140  	var hdr linux.ElfHeader64
   141  	hdrBuf := make([]byte, header64Size)
   142  	_, err = f.ReadFull(ctx, usermem.BytesIOSequence(hdrBuf), 0)
   143  	if err != nil {
   144  		log.Infof("Error reading ELF header: %v", err)
   145  		// The entire header always exists.
   146  		if err == io.EOF || err == io.ErrUnexpectedEOF {
   147  			err = linuxerr.ENOEXEC
   148  		}
   149  		return elfInfo{}, err
   150  	}
   151  	hdr.UnmarshalUnsafe(hdrBuf)
   152  
   153  	// We support amd64 and arm64.
   154  	var a arch.Arch
   155  	switch machine := elf.Machine(hdr.Machine); machine {
   156  	case elf.EM_X86_64:
   157  		a = arch.AMD64
   158  	case elf.EM_AARCH64:
   159  		a = arch.ARM64
   160  	default:
   161  		log.Infof("Unsupported ELF machine %d", machine)
   162  		return elfInfo{}, linuxerr.ENOEXEC
   163  	}
   164  
   165  	var sharedObject bool
   166  	elfType := elf.Type(hdr.Type)
   167  	switch elfType {
   168  	case elf.ET_EXEC:
   169  		sharedObject = false
   170  	case elf.ET_DYN:
   171  		sharedObject = true
   172  	default:
   173  		log.Infof("Unsupported ELF type %v", elfType)
   174  		return elfInfo{}, linuxerr.ENOEXEC
   175  	}
   176  
   177  	if int(hdr.Phentsize) != prog64Size {
   178  		log.Infof("Unsupported phdr size %d", hdr.Phentsize)
   179  		return elfInfo{}, linuxerr.ENOEXEC
   180  	}
   181  	totalPhdrSize := prog64Size * int(hdr.Phnum)
   182  	if totalPhdrSize < prog64Size {
   183  		log.Warningf("No phdrs or total phdr size overflows: prog64Size: %d phnum: %d", prog64Size, int(hdr.Phnum))
   184  		return elfInfo{}, linuxerr.ENOEXEC
   185  	}
   186  	if totalPhdrSize > maxTotalPhdrSize {
   187  		log.Infof("Too many phdrs (%d): total size %d > %d", hdr.Phnum, totalPhdrSize, maxTotalPhdrSize)
   188  		return elfInfo{}, linuxerr.ENOEXEC
   189  	}
   190  	if int64(hdr.Phoff) < 0 || int64(hdr.Phoff+uint64(totalPhdrSize)) < 0 {
   191  		ctx.Infof("Unsupported phdr offset %d", hdr.Phoff)
   192  		return elfInfo{}, linuxerr.ENOEXEC
   193  	}
   194  
   195  	phdrBuf := make([]byte, totalPhdrSize)
   196  	_, err = f.ReadFull(ctx, usermem.BytesIOSequence(phdrBuf), int64(hdr.Phoff))
   197  	if err != nil {
   198  		log.Infof("Error reading ELF phdrs: %v", err)
   199  		// If phdrs were specified, they should all exist.
   200  		if err == io.EOF || err == io.ErrUnexpectedEOF {
   201  			err = linuxerr.ENOEXEC
   202  		}
   203  		return elfInfo{}, err
   204  	}
   205  
   206  	phdrs := make([]elf.ProgHeader, hdr.Phnum)
   207  	for i := range phdrs {
   208  		var prog64 linux.ElfProg64
   209  		phdrBuf = prog64.UnmarshalUnsafe(phdrBuf)
   210  		phdrs[i] = elf.ProgHeader{
   211  			Type:   elf.ProgType(prog64.Type),
   212  			Flags:  elf.ProgFlag(prog64.Flags),
   213  			Off:    prog64.Off,
   214  			Vaddr:  prog64.Vaddr,
   215  			Paddr:  prog64.Paddr,
   216  			Filesz: prog64.Filesz,
   217  			Memsz:  prog64.Memsz,
   218  			Align:  prog64.Align,
   219  		}
   220  	}
   221  
   222  	return elfInfo{
   223  		os:           os,
   224  		arch:         a,
   225  		entry:        hostarch.Addr(hdr.Entry),
   226  		phdrs:        phdrs,
   227  		phdrOff:      hdr.Phoff,
   228  		phdrSize:     prog64Size,
   229  		sharedObject: sharedObject,
   230  	}, nil
   231  }
   232  
   233  // mapSegment maps a phdr into the Task. offset is the offset to apply to
   234  // phdr.Vaddr.
   235  func mapSegment(ctx context.Context, m *mm.MemoryManager, fd *vfs.FileDescription, phdr *elf.ProgHeader, offset hostarch.Addr) error {
   236  	// We must make a page-aligned mapping.
   237  	adjust := hostarch.Addr(phdr.Vaddr).PageOffset()
   238  
   239  	addr, ok := offset.AddLength(phdr.Vaddr)
   240  	if !ok {
   241  		// If offset != 0 we should have ensured this would fit.
   242  		ctx.Warningf("Computed segment load address overflows: %#x + %#x", phdr.Vaddr, offset)
   243  		return linuxerr.ENOEXEC
   244  	}
   245  	addr -= hostarch.Addr(adjust)
   246  
   247  	fileSize := phdr.Filesz + adjust
   248  	if fileSize < phdr.Filesz {
   249  		ctx.Infof("Computed segment file size overflows: %#x + %#x", phdr.Filesz, adjust)
   250  		return linuxerr.ENOEXEC
   251  	}
   252  	ms, ok := hostarch.Addr(fileSize).RoundUp()
   253  	if !ok {
   254  		ctx.Infof("fileSize %#x too large", fileSize)
   255  		return linuxerr.ENOEXEC
   256  	}
   257  	mapSize := uint64(ms)
   258  
   259  	if mapSize > 0 {
   260  		// This must result in a page-aligned offset. i.e., the original
   261  		// phdr.Off must have the same alignment as phdr.Vaddr. If that is not
   262  		// true, MMap will reject the mapping.
   263  		fileOffset := phdr.Off - adjust
   264  
   265  		prot := progFlagsAsPerms(phdr.Flags)
   266  		mopts := memmap.MMapOpts{
   267  			Length: mapSize,
   268  			Offset: fileOffset,
   269  			Addr:   addr,
   270  			Fixed:  true,
   271  			// Linux will happily allow conflicting segments to map over
   272  			// one another.
   273  			Unmap:    true,
   274  			Private:  true,
   275  			Perms:    prot,
   276  			MaxPerms: hostarch.AnyAccess,
   277  		}
   278  		defer func() {
   279  			if mopts.MappingIdentity != nil {
   280  				mopts.MappingIdentity.DecRef(ctx)
   281  			}
   282  		}()
   283  		if err := fd.ConfigureMMap(ctx, &mopts); err != nil {
   284  			ctx.Infof("File is not memory-mappable: %v", err)
   285  			return err
   286  		}
   287  		if _, err := m.MMap(ctx, mopts); err != nil {
   288  			ctx.Infof("Error mapping PT_LOAD segment %+v at %#x: %v", phdr, addr, err)
   289  			return err
   290  		}
   291  
   292  		// We need to clear the end of the last page that exceeds fileSize so
   293  		// we don't map part of the file beyond fileSize.
   294  		//
   295  		// Note that Linux *does not* clear the portion of the first page
   296  		// before phdr.Off.
   297  		if mapSize > fileSize {
   298  			zeroAddr, ok := addr.AddLength(fileSize)
   299  			if !ok {
   300  				panic(fmt.Sprintf("successfully mmaped address overflows? %#x + %#x", addr, fileSize))
   301  			}
   302  			zeroSize := int64(mapSize - fileSize)
   303  			if zeroSize < 0 {
   304  				panic(fmt.Sprintf("zeroSize too big? %#x", uint64(zeroSize)))
   305  			}
   306  			if _, err := m.ZeroOut(ctx, zeroAddr, zeroSize, usermem.IOOpts{IgnorePermissions: true}); err != nil {
   307  				ctx.Warningf("Failed to zero end of page [%#x, %#x): %v", zeroAddr, zeroAddr+hostarch.Addr(zeroSize), err)
   308  				return err
   309  			}
   310  		}
   311  	}
   312  
   313  	memSize := phdr.Memsz + adjust
   314  	if memSize < phdr.Memsz {
   315  		ctx.Infof("Computed segment mem size overflows: %#x + %#x", phdr.Memsz, adjust)
   316  		return linuxerr.ENOEXEC
   317  	}
   318  
   319  	// Allocate more anonymous pages if necessary.
   320  	if mapSize < memSize {
   321  		anonAddr, ok := addr.AddLength(mapSize)
   322  		if !ok {
   323  			panic(fmt.Sprintf("anonymous memory doesn't fit in pre-sized range? %#x + %#x", addr, mapSize))
   324  		}
   325  		anonSize, ok := hostarch.Addr(memSize - mapSize).RoundUp()
   326  		if !ok {
   327  			ctx.Infof("extra anon pages too large: %#x", memSize-mapSize)
   328  			return linuxerr.ENOEXEC
   329  		}
   330  
   331  		// N.B. Linux uses vm_brk_flags to map these pages, which only
   332  		// honors the X bit, always mapping at least RW. ignoring These
   333  		// pages are not included in the final brk region.
   334  		prot := hostarch.ReadWrite
   335  		if phdr.Flags&elf.PF_X == elf.PF_X {
   336  			prot.Execute = true
   337  		}
   338  
   339  		if _, err := m.MMap(ctx, memmap.MMapOpts{
   340  			Length: uint64(anonSize),
   341  			Addr:   anonAddr,
   342  			// Fixed without Unmap will fail the mmap if something is
   343  			// already at addr.
   344  			Fixed:    true,
   345  			Private:  true,
   346  			Perms:    prot,
   347  			MaxPerms: hostarch.AnyAccess,
   348  		}); err != nil {
   349  			ctx.Infof("Error mapping PT_LOAD segment %v anonymous memory: %v", phdr, err)
   350  			return err
   351  		}
   352  	}
   353  
   354  	return nil
   355  }
   356  
   357  // loadedELF describes an ELF that has been successfully loaded.
   358  type loadedELF struct {
   359  	// os is the target OS of the ELF.
   360  	os abi.OS
   361  
   362  	// arch is the target architecture of the ELF.
   363  	arch arch.Arch
   364  
   365  	// entry is the entry point of the ELF.
   366  	entry hostarch.Addr
   367  
   368  	// start is the end of the ELF.
   369  	start hostarch.Addr
   370  
   371  	// end is the end of the ELF.
   372  	end hostarch.Addr
   373  
   374  	// interpter is the path to the ELF interpreter.
   375  	interpreter string
   376  
   377  	// phdrAddr is the address of the ELF program headers.
   378  	phdrAddr hostarch.Addr
   379  
   380  	// phdrSize is the size of a single program header in the ELF.
   381  	phdrSize int
   382  
   383  	// phdrNum is the number of program headers.
   384  	phdrNum int
   385  
   386  	// auxv contains a subset of ELF-specific auxiliary vector entries:
   387  	//	* AT_PHDR
   388  	//	* AT_PHENT
   389  	//	* AT_PHNUM
   390  	//	* AT_BASE
   391  	//	* AT_ENTRY
   392  	auxv arch.Auxv
   393  }
   394  
   395  // loadParsedELF loads f into mm.
   396  //
   397  // info is the parsed elfInfo from the header.
   398  //
   399  // It does not load the ELF interpreter, or return any auxv entries.
   400  //
   401  // Preconditions: f is an ELF file.
   402  func loadParsedELF(ctx context.Context, m *mm.MemoryManager, fd *vfs.FileDescription, info elfInfo, sharedLoadOffset hostarch.Addr) (loadedELF, error) {
   403  	first := true
   404  	var start, end hostarch.Addr
   405  	var interpreter string
   406  	for _, phdr := range info.phdrs {
   407  		switch phdr.Type {
   408  		case elf.PT_LOAD:
   409  			vaddr := hostarch.Addr(phdr.Vaddr)
   410  			if first {
   411  				first = false
   412  				start = vaddr
   413  			}
   414  			if vaddr < end {
   415  				// NOTE(b/37474556): Linux allows out-of-order
   416  				// segments, in violation of the spec.
   417  				ctx.Infof("PT_LOAD headers out-of-order. %#x < %#x", vaddr, end)
   418  				return loadedELF{}, linuxerr.ENOEXEC
   419  			}
   420  			var ok bool
   421  			end, ok = vaddr.AddLength(phdr.Memsz)
   422  			if !ok {
   423  				ctx.Infof("PT_LOAD header size overflows. %#x + %#x", vaddr, phdr.Memsz)
   424  				return loadedELF{}, linuxerr.ENOEXEC
   425  			}
   426  
   427  		case elf.PT_INTERP:
   428  			if phdr.Filesz < 2 {
   429  				ctx.Infof("PT_INTERP path too small: %v", phdr.Filesz)
   430  				return loadedELF{}, linuxerr.ENOEXEC
   431  			}
   432  			if phdr.Filesz > linux.PATH_MAX {
   433  				ctx.Infof("PT_INTERP path too big: %v", phdr.Filesz)
   434  				return loadedELF{}, linuxerr.ENOEXEC
   435  			}
   436  			if int64(phdr.Off) < 0 || int64(phdr.Off+phdr.Filesz) < 0 {
   437  				ctx.Infof("Unsupported PT_INTERP offset %d", phdr.Off)
   438  				return loadedELF{}, linuxerr.ENOEXEC
   439  			}
   440  
   441  			path := make([]byte, phdr.Filesz)
   442  			_, err := fd.ReadFull(ctx, usermem.BytesIOSequence(path), int64(phdr.Off))
   443  			if err != nil {
   444  				// If an interpreter was specified, it should exist.
   445  				ctx.Infof("Error reading PT_INTERP path: %v", err)
   446  				return loadedELF{}, linuxerr.ENOEXEC
   447  			}
   448  
   449  			if path[len(path)-1] != 0 {
   450  				ctx.Infof("PT_INTERP path not NUL-terminated: %v", path)
   451  				return loadedELF{}, linuxerr.ENOEXEC
   452  			}
   453  
   454  			// Strip NUL-terminator and everything beyond from
   455  			// string. Note that there may be a NUL-terminator
   456  			// before len(path)-1.
   457  			interpreter = string(path[:bytes.IndexByte(path, '\x00')])
   458  			if interpreter == "" {
   459  				// Linux actually attempts to open_exec("\0").
   460  				// open_exec -> do_open_execat fails to check
   461  				// that name != '\0' before calling
   462  				// do_filp_open, which thus opens the working
   463  				// directory.  do_open_execat returns EACCES
   464  				// because the directory is not a regular file.
   465  				//
   466  				// We bypass that nonsense and simply
   467  				// short-circuit with EACCES. Those this does
   468  				// mean that there may be some edge cases where
   469  				// the open path would return a different
   470  				// error.
   471  				ctx.Infof("PT_INTERP path is empty: %v", path)
   472  				return loadedELF{}, linuxerr.EACCES
   473  			}
   474  		}
   475  	}
   476  
   477  	// Shared objects don't have fixed load addresses. We need to pick a
   478  	// base address big enough to fit all segments, so we first create a
   479  	// mapping for the total size just to find a region that is big enough.
   480  	//
   481  	// It is safe to unmap it immediately without racing with another mapping
   482  	// because we are the only one in control of the MemoryManager.
   483  	//
   484  	// Note that the vaddr of the first PT_LOAD segment is ignored when
   485  	// choosing the load address (even if it is non-zero). The vaddr does
   486  	// become an offset from that load address.
   487  	var offset hostarch.Addr
   488  	if info.sharedObject {
   489  		totalSize := end - start
   490  		totalSize, ok := totalSize.RoundUp()
   491  		if !ok {
   492  			ctx.Infof("ELF PT_LOAD segments too big")
   493  			return loadedELF{}, linuxerr.ENOEXEC
   494  		}
   495  
   496  		var err error
   497  		offset, err = m.MMap(ctx, memmap.MMapOpts{
   498  			Length:  uint64(totalSize),
   499  			Addr:    sharedLoadOffset,
   500  			Private: true,
   501  		})
   502  		if err != nil {
   503  			ctx.Infof("Error allocating address space for shared object: %v", err)
   504  			return loadedELF{}, err
   505  		}
   506  		if err := m.MUnmap(ctx, offset, uint64(totalSize)); err != nil {
   507  			panic(fmt.Sprintf("Failed to unmap base address: %v", err))
   508  		}
   509  
   510  		start, ok = start.AddLength(uint64(offset))
   511  		if !ok {
   512  			ctx.Infof(fmt.Sprintf("Start %#x + offset %#x overflows?", start, offset))
   513  			return loadedELF{}, linuxerr.EINVAL
   514  		}
   515  
   516  		end, ok = end.AddLength(uint64(offset))
   517  		if !ok {
   518  			ctx.Infof(fmt.Sprintf("End %#x + offset %#x overflows?", end, offset))
   519  			return loadedELF{}, linuxerr.EINVAL
   520  		}
   521  
   522  		info.entry, ok = info.entry.AddLength(uint64(offset))
   523  		if !ok {
   524  			ctx.Infof("Entrypoint %#x + offset %#x overflows? Is the entrypoint within a segment?", info.entry, offset)
   525  			return loadedELF{}, err
   526  		}
   527  	}
   528  
   529  	// Map PT_LOAD segments.
   530  	for _, phdr := range info.phdrs {
   531  		switch phdr.Type {
   532  		case elf.PT_LOAD:
   533  			if phdr.Memsz == 0 {
   534  				// No need to load segments with size 0, but
   535  				// they exist in some binaries.
   536  				continue
   537  			}
   538  
   539  			if err := mapSegment(ctx, m, fd, &phdr, offset); err != nil {
   540  				ctx.Infof("Failed to map PT_LOAD segment: %+v", phdr)
   541  				return loadedELF{}, err
   542  			}
   543  		}
   544  	}
   545  
   546  	// This assumes that the first segment contains the ELF headers. This
   547  	// may not be true in a malformed ELF, but Linux makes the same
   548  	// assumption.
   549  	phdrAddr, ok := start.AddLength(info.phdrOff)
   550  	if !ok {
   551  		ctx.Warningf("ELF start address %#x + phdr offset %#x overflows", start, info.phdrOff)
   552  		phdrAddr = 0
   553  	}
   554  
   555  	return loadedELF{
   556  		os:          info.os,
   557  		arch:        info.arch,
   558  		entry:       info.entry,
   559  		start:       start,
   560  		end:         end,
   561  		interpreter: interpreter,
   562  		phdrAddr:    phdrAddr,
   563  		phdrSize:    info.phdrSize,
   564  		phdrNum:     len(info.phdrs),
   565  	}, nil
   566  }
   567  
   568  // loadInitialELF loads f into mm.
   569  //
   570  // It creates an arch.Context64 for the ELF and prepares the mm for this arch.
   571  //
   572  // It does not load the ELF interpreter, or return any auxv entries.
   573  //
   574  // Preconditions:
   575  //   - f is an ELF file.
   576  //   - f is the first ELF loaded into m.
   577  func loadInitialELF(ctx context.Context, m *mm.MemoryManager, fs cpuid.FeatureSet, fd *vfs.FileDescription) (loadedELF, *arch.Context64, error) {
   578  	info, err := parseHeader(ctx, fd)
   579  	if err != nil {
   580  		ctx.Infof("Failed to parse initial ELF: %v", err)
   581  		return loadedELF{}, nil, err
   582  	}
   583  
   584  	// Check Image Compatibility.
   585  	if arch.Host != info.arch {
   586  		ctx.Warningf("Found mismatch for platform %s with ELF type %s", arch.Host.String(), info.arch.String())
   587  		return loadedELF{}, nil, linuxerr.ENOEXEC
   588  	}
   589  
   590  	// Create the arch.Context64 now so we can prepare the mmap layout before
   591  	// mapping anything.
   592  	ac := arch.New(info.arch)
   593  
   594  	l, err := m.SetMmapLayout(ac, limits.FromContext(ctx))
   595  	if err != nil {
   596  		ctx.Warningf("Failed to set mmap layout: %v", err)
   597  		return loadedELF{}, nil, err
   598  	}
   599  
   600  	// PIELoadAddress tries to move the ELF out of the way of the default
   601  	// mmap base to ensure that the initial brk has sufficient space to
   602  	// grow.
   603  	le, err := loadParsedELF(ctx, m, fd, info, ac.PIELoadAddress(l))
   604  	return le, ac, err
   605  }
   606  
   607  // loadInterpreterELF loads f into mm.
   608  //
   609  // The interpreter must be for the same OS/Arch as the initial ELF.
   610  //
   611  // It does not return any auxv entries.
   612  //
   613  // Preconditions: f is an ELF file.
   614  func loadInterpreterELF(ctx context.Context, m *mm.MemoryManager, fd *vfs.FileDescription, initial loadedELF) (loadedELF, error) {
   615  	info, err := parseHeader(ctx, fd)
   616  	if err != nil {
   617  		if linuxerr.Equals(linuxerr.ENOEXEC, err) {
   618  			// Bad interpreter.
   619  			err = linuxerr.ELIBBAD
   620  		}
   621  		return loadedELF{}, err
   622  	}
   623  
   624  	if info.os != initial.os {
   625  		ctx.Infof("Initial ELF OS %v and interpreter ELF OS %v differ", initial.os, info.os)
   626  		return loadedELF{}, linuxerr.ELIBBAD
   627  	}
   628  	if info.arch != initial.arch {
   629  		ctx.Infof("Initial ELF arch %v and interpreter ELF arch %v differ", initial.arch, info.arch)
   630  		return loadedELF{}, linuxerr.ELIBBAD
   631  	}
   632  
   633  	// The interpreter is not given a load offset, as its location does not
   634  	// affect brk.
   635  	return loadParsedELF(ctx, m, fd, info, 0)
   636  }
   637  
   638  // loadELF loads args.File into the Task address space.
   639  //
   640  // If loadELF returns ErrSwitchFile it should be called again with the returned
   641  // path and argv.
   642  //
   643  // Preconditions: args.File is an ELF file.
   644  func loadELF(ctx context.Context, args LoadArgs) (loadedELF, *arch.Context64, error) {
   645  	bin, ac, err := loadInitialELF(ctx, args.MemoryManager, args.Features, args.File)
   646  	if err != nil {
   647  		ctx.Infof("Error loading binary: %v", err)
   648  		return loadedELF{}, nil, err
   649  	}
   650  
   651  	var interp loadedELF
   652  	if bin.interpreter != "" {
   653  		// Even if we do not allow the final link of the script to be
   654  		// resolved, the interpreter should still be resolved if it is
   655  		// a symlink.
   656  		args.ResolveFinal = true
   657  		// Refresh the traversal limit.
   658  		*args.RemainingTraversals = linux.MaxSymlinkTraversals
   659  		args.Filename = bin.interpreter
   660  		intFile, err := openPath(ctx, args)
   661  		if err != nil {
   662  			ctx.Infof("Error opening interpreter %s: %v", bin.interpreter, err)
   663  			return loadedELF{}, nil, err
   664  		}
   665  		defer intFile.DecRef(ctx)
   666  
   667  		interp, err = loadInterpreterELF(ctx, args.MemoryManager, intFile, bin)
   668  		if err != nil {
   669  			ctx.Infof("Error loading interpreter: %v", err)
   670  			return loadedELF{}, nil, err
   671  		}
   672  
   673  		if interp.interpreter != "" {
   674  			// No recursive interpreters!
   675  			ctx.Infof("Interpreter requires an interpreter")
   676  			return loadedELF{}, nil, linuxerr.ENOEXEC
   677  		}
   678  	}
   679  
   680  	// ELF-specific auxv entries.
   681  	bin.auxv = arch.Auxv{
   682  		arch.AuxEntry{linux.AT_PHDR, bin.phdrAddr},
   683  		arch.AuxEntry{linux.AT_PHENT, hostarch.Addr(bin.phdrSize)},
   684  		arch.AuxEntry{linux.AT_PHNUM, hostarch.Addr(bin.phdrNum)},
   685  		arch.AuxEntry{linux.AT_ENTRY, bin.entry},
   686  	}
   687  	if bin.interpreter != "" {
   688  		bin.auxv = append(bin.auxv, arch.AuxEntry{linux.AT_BASE, interp.start})
   689  
   690  		// Start in the interpreter.
   691  		// N.B. AT_ENTRY above contains the *original* entry point.
   692  		bin.entry = interp.entry
   693  	} else {
   694  		// Always add AT_BASE even if there is no interpreter.
   695  		bin.auxv = append(bin.auxv, arch.AuxEntry{linux.AT_BASE, 0})
   696  	}
   697  
   698  	return bin, ac, nil
   699  }