221 lines
7.1 KiB
Go
221 lines
7.1 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package arch
|
|
|
|
import (
|
|
"gvisor.dev/gvisor/pkg/context"
|
|
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
|
"gvisor.dev/gvisor/pkg/usermem"
|
|
)
|
|
|
|
// Stack is a simple wrapper around a usermem.IO and an address. Stack
|
|
// implements marshal.CopyContext, and marshallable values can be pushed or
|
|
// popped from the stack through the marshal.Marshallable interface.
|
|
//
|
|
// Stack is not thread-safe.
|
|
type Stack struct {
|
|
// Our arch info.
|
|
// We use this for automatic Native conversion of usermem.Addrs during
|
|
// Push() and Pop().
|
|
Arch Context
|
|
|
|
// The interface used to actually copy user memory.
|
|
IO usermem.IO
|
|
|
|
// Our current stack bottom.
|
|
Bottom usermem.Addr
|
|
|
|
// Scratch buffer used for marshalling to avoid having to repeatedly
|
|
// allocate scratch memory.
|
|
scratchBuf []byte
|
|
}
|
|
|
|
// scratchBufLen is the default length of Stack.scratchBuf. The
|
|
// largest structs the stack regularly serializes are arch.SignalInfo
|
|
// and arch.UContext64. We'll set the default size as the larger of
|
|
// the two, arch.UContext64.
|
|
var scratchBufLen = (*UContext64)(nil).SizeBytes()
|
|
|
|
// CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer.
|
|
func (s *Stack) CopyScratchBuffer(size int) []byte {
|
|
if len(s.scratchBuf) < size {
|
|
s.scratchBuf = make([]byte, size)
|
|
}
|
|
return s.scratchBuf[:size]
|
|
}
|
|
|
|
// StackBottomMagic is the special address callers must past to all stack
|
|
// marshalling operations to cause the src/dst address to be computed based on
|
|
// the current end of the stack.
|
|
const StackBottomMagic = ^usermem.Addr(0) // usermem.Addr(-1)
|
|
|
|
// CopyOutBytes implements marshal.CopyContext.CopyOutBytes. CopyOutBytes
|
|
// computes an appropriate address based on the current end of the
|
|
// stack. Callers use the sentinel address StackBottomMagic to marshal methods
|
|
// to indicate this.
|
|
func (s *Stack) CopyOutBytes(sentinel usermem.Addr, b []byte) (int, error) {
|
|
if sentinel != StackBottomMagic {
|
|
panic("Attempted to copy out to stack with absolute address")
|
|
}
|
|
c := len(b)
|
|
n, err := s.IO.CopyOut(context.Background(), s.Bottom-usermem.Addr(c), b, usermem.IOOpts{})
|
|
if err == nil && n == c {
|
|
s.Bottom -= usermem.Addr(n)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// CopyInBytes implements marshal.CopyContext.CopyInBytes. CopyInBytes computes
|
|
// an appropriate address based on the current end of the stack. Callers must
|
|
// use the sentinel address StackBottomMagic to marshal methods to indicate
|
|
// this.
|
|
func (s *Stack) CopyInBytes(sentinel usermem.Addr, b []byte) (int, error) {
|
|
if sentinel != StackBottomMagic {
|
|
panic("Attempted to copy in from stack with absolute address")
|
|
}
|
|
n, err := s.IO.CopyIn(context.Background(), s.Bottom, b, usermem.IOOpts{})
|
|
if err == nil {
|
|
s.Bottom += usermem.Addr(n)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// Align aligns the stack to the given offset.
|
|
func (s *Stack) Align(offset int) {
|
|
if s.Bottom%usermem.Addr(offset) != 0 {
|
|
s.Bottom -= (s.Bottom % usermem.Addr(offset))
|
|
}
|
|
}
|
|
|
|
// PushNullTerminatedByteSlice writes bs to the stack, followed by an extra null
|
|
// byte at the end. On error, the contents of the stack and the bottom cursor
|
|
// are undefined.
|
|
func (s *Stack) PushNullTerminatedByteSlice(bs []byte) (int, error) {
|
|
// Note: Stack grows up, so write the terminal null byte first.
|
|
nNull, err := primitive.CopyUint8Out(s, StackBottomMagic, 0)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
n, err := primitive.CopyByteSliceOut(s, StackBottomMagic, bs)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return n + nNull, nil
|
|
}
|
|
|
|
// StackLayout describes the location of the arguments and environment on the
|
|
// stack.
|
|
type StackLayout struct {
|
|
// ArgvStart is the beginning of the argument vector.
|
|
ArgvStart usermem.Addr
|
|
|
|
// ArgvEnd is the end of the argument vector.
|
|
ArgvEnd usermem.Addr
|
|
|
|
// EnvvStart is the beginning of the environment vector.
|
|
EnvvStart usermem.Addr
|
|
|
|
// EnvvEnd is the end of the environment vector.
|
|
EnvvEnd usermem.Addr
|
|
}
|
|
|
|
// Load pushes the given args, env and aux vector to the stack using the
|
|
// well-known format for a new executable. It returns the start and end
|
|
// of the argument and environment vectors.
|
|
func (s *Stack) Load(args []string, env []string, aux Auxv) (StackLayout, error) {
|
|
l := StackLayout{}
|
|
|
|
// Make sure we start with a 16-byte alignment.
|
|
s.Align(16)
|
|
|
|
// Push the environment vector so the end of the argument vector is adjacent to
|
|
// the beginning of the environment vector.
|
|
// While the System V abi for x86_64 does not specify an ordering to the
|
|
// Information Block (the block holding the arg, env, and aux vectors),
|
|
// support features like setproctitle(3) naturally expect these segments
|
|
// to be in this order. See: https://www.uclibc.org/docs/psABI-x86_64.pdf
|
|
// page 29.
|
|
l.EnvvEnd = s.Bottom
|
|
envAddrs := make([]usermem.Addr, len(env))
|
|
for i := len(env) - 1; i >= 0; i-- {
|
|
if _, err := s.PushNullTerminatedByteSlice([]byte(env[i])); err != nil {
|
|
return StackLayout{}, err
|
|
}
|
|
envAddrs[i] = s.Bottom
|
|
}
|
|
l.EnvvStart = s.Bottom
|
|
|
|
// Push our strings.
|
|
l.ArgvEnd = s.Bottom
|
|
argAddrs := make([]usermem.Addr, len(args))
|
|
for i := len(args) - 1; i >= 0; i-- {
|
|
if _, err := s.PushNullTerminatedByteSlice([]byte(args[i])); err != nil {
|
|
return StackLayout{}, err
|
|
}
|
|
argAddrs[i] = s.Bottom
|
|
}
|
|
l.ArgvStart = s.Bottom
|
|
|
|
// We need to align the arguments appropriately.
|
|
//
|
|
// We must finish on a 16-byte alignment, but we'll play it
|
|
// conservatively and finish at 32-bytes. It would be nice to be able
|
|
// to call Align here, but unfortunately we need to align the stack
|
|
// with all the variable sized arrays pushed. So we just need to do
|
|
// some calculations.
|
|
argvSize := s.Arch.Width() * uint(len(args)+1)
|
|
envvSize := s.Arch.Width() * uint(len(env)+1)
|
|
auxvSize := s.Arch.Width() * 2 * uint(len(aux)+1)
|
|
total := usermem.Addr(argvSize) + usermem.Addr(envvSize) + usermem.Addr(auxvSize) + usermem.Addr(s.Arch.Width())
|
|
expectedBottom := s.Bottom - total
|
|
if expectedBottom%32 != 0 {
|
|
s.Bottom -= expectedBottom % 32
|
|
}
|
|
|
|
// Push our auxvec.
|
|
// NOTE: We need an extra zero here per spec.
|
|
// The Push function will automatically terminate
|
|
// strings and arrays with a single null value.
|
|
auxv := make([]usermem.Addr, 0, len(aux))
|
|
for _, a := range aux {
|
|
auxv = append(auxv, usermem.Addr(a.Key), a.Value)
|
|
}
|
|
auxv = append(auxv, usermem.Addr(0))
|
|
_, err := s.pushAddrSliceAndTerminator(auxv)
|
|
if err != nil {
|
|
return StackLayout{}, err
|
|
}
|
|
|
|
// Push environment.
|
|
_, err = s.pushAddrSliceAndTerminator(envAddrs)
|
|
if err != nil {
|
|
return StackLayout{}, err
|
|
}
|
|
|
|
// Push args.
|
|
_, err = s.pushAddrSliceAndTerminator(argAddrs)
|
|
if err != nil {
|
|
return StackLayout{}, err
|
|
}
|
|
|
|
// Push arg count.
|
|
lenP := s.Arch.Native(uintptr(len(args)))
|
|
if _, err = lenP.CopyOut(s, StackBottomMagic); err != nil {
|
|
return StackLayout{}, err
|
|
}
|
|
|
|
return l, nil
|
|
}
|