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