/* * Create a new memory region in the address space. * * If there are any overlaps between the current address space and the requested one, * this function will unamp the overlapped portions of the address space before * mapping in the new memory region. * * Several clients, including the dynamic linker relies on this feature. See mmap(2) * for details. * * This function requires vaddr and memorySize are aligned to the page boundary. */ internal int AddMapping(uint access, int flags, File file, uint fileOffset, int fileSize, Pointer vaddr, int memorySize) { Contract.Requires(file == null || file.GhostOwner == GhostOwner); Contract.Requires(0 <= fileSize && fileSize <= memorySize); Contract.Requires(file == null || fileSize > 0); Contract.Requires(file != null || (fileSize == 0 && fileOffset == 0)); Contract.Ensures(Brk == Contract.OldValue(Brk)); if (memorySize <= 0 || Arch.ArchDefinition.PageOffset(memorySize) != 0) return -ErrorCode.EINVAL; var diff = Arch.ArchDefinition.PageOffset(vaddr.ToUInt32()); if (diff != 0) return -ErrorCode.EINVAL; var r = RemoveMapping(vaddr, memorySize); if (r != 0) { return r; } var newRegion = new MemoryRegion(GhostOwner, access, flags, file, fileOffset, fileSize, vaddr, memorySize, false); Insert(newRegion); return 0; }
internal int GetUnusedFd() { Contract.Ensures(IsAvailableFd(Contract.Result<int>())); var _this = this; var size = descriptors.Length; var i = finger; while (i < size) { if (IsAvailableFd(i)) { UpdateFinger(i); return i; } ++i; } var new_descriptors = new File[2 * size]; for (var j = 0; j < size; ++j) { new_descriptors[i] = descriptors[i]; } for (var j = size; j < 2 * size; ++j) { new_descriptors[i] = null; } descriptors = new_descriptors; // Proven by Dafny Contract.Assume(IsAvailableFd(size)); UpdateFinger(size); return size; }
internal void Add(int fd, File file) { Contract.Requires(IsAvailableFd(fd)); Contract.Requires(file != null && file.GhostOwner == GhostOwner); Contract.Ensures(IsValidFd(fd)); descriptors[fd] = file; }
internal void InstallFd(int fd, File file) { Contract.Requires(file != null); Contract.Requires(file.GhostOwner == this); Contract.Requires(Files.IsAvailableFd(fd)); Files.Add(fd, file); }
public static int Parse(int helperPid, File file, Process proc, ref UserPtr stackTop) { Contract.Requires(file.GhostOwner == proc); Contract.Requires(proc.Space.GhostOwner == proc); var buf = new byte[Size]; uint pos = 0; if (file.Read(buf, ref pos) != buf.Length) return -ErrorCode.EINVAL; var eh = Read(buf); if (eh.type != ELF32Header.ELF_TYPE_EXECUTABLE || eh.ProgramHeaderOffest == 0) return -ErrorCode.ENOEXEC; proc.EntryPoint = eh.EntryPoint; ELF32ProgramHeader ph = new ELF32ProgramHeader(); var ret = FindInterpreter(file, eh, ref ph); if (ret == -ErrorCode.EINVAL) { Arch.Console.WriteLine("Malformed ELF file"); return ret; } else if (ret == 0) { var interpreterBuf = new byte[ph.FileSize]; pos = ph.offset; if (file.Read(interpreterBuf, ref pos) != interpreterBuf.Length) return -ErrorCode.EINVAL; var interpreterName = new ASCIIString(interpreterBuf); ErrorCode ec; var interpreter_inode = Arch.ArchFS.Open(helperPid, interpreterName, 0, 0, out ec); if (interpreter_inode == null) return -ErrorCode.ENOENT; var interpreter = new File(proc, interpreter_inode, FileFlags.ReadOnly, 0); /* * Parse the information of linker. * * This function will also override the entry point. */ if (Parse(helperPid, interpreter, proc, ref stackTop) != 0) return -ErrorCode.EINVAL; // So now let's copy the program header to the top of the stack, and push auxlirary vectors PushProgramHeaderAndAuxliraryVectors(proc, file, eh, ref stackTop); } return MapInSegments(file, proc, eh); }
internal MemoryRegion(Process owner, uint access, int flags, File file, uint fileOffset, int fileSize, Pointer vaddr, int size, bool isFixed) { Contract.Requires(file == null || file.GhostOwner == owner); Contract.Ensures(GhostOwner == owner); this.Access = access; this.Flags = flags; this.BackingFile = file; this.FileOffset = fileOffset; this.FileSize = fileSize; this.StartAddress = vaddr; this.Size = size; this.Next = null; this.IsFixed = isFixed; this.GhostOwner = owner; if (file != null) file.inode.IncreaseRefCount(); }
private static int PushProgramHeaderAndAuxliraryVectors(Process proc, File file, ELF32Header eh, ref UserPtr stackTop) { var programHeaderLength = eh.NumOfProgramHeader * eh.ProgramHeaderSize; var buf = new byte[programHeaderLength]; uint pos = eh.ProgramHeaderOffest; if (file.Read(buf, ref pos) != programHeaderLength) return -ErrorCode.ENOMEM; stackTop -= programHeaderLength; UserPtr ph_ptr = stackTop; if (ph_ptr.Write(proc, buf) != 0) return -ErrorCode.ENOMEM; // align stackTop = UserPtr.RoundDown(stackTop); var aux_vector = new uint[LengthOfAuxVector]; aux_vector[0] = AT_PHDR; aux_vector[1] = ph_ptr.Value.ToUInt32(); aux_vector[2] = AT_ENTRY; aux_vector[3] = eh.EntryPoint; aux_vector[4] = AT_PHNUM; aux_vector[5] = eh.NumOfProgramHeader; aux_vector[6] = 0; aux_vector[7] = 0; var auxVectorSize = sizeof(uint) * LengthOfAuxVector; stackTop -= auxVectorSize; if (stackTop.Write(proc, aux_vector) != 0) return -ErrorCode.ENOMEM; return 0; }
private static int MapInSegments(File file, Process proc, ELF32Header eh) { Contract.Requires(file != null && file.GhostOwner == proc); Contract.Requires(proc.Space.GhostOwner == proc); var buf = new byte[ELF32ProgramHeader.Size]; var ph = new ELF32ProgramHeader(); // At this point we need to map in all stuff in PT_LOAD for (var i = 0; i < eh.NumOfProgramHeader; ++i) { var pos = (uint)(eh.ProgramHeaderOffest + eh.ProgramHeaderSize * i); if (file.Read(buf, ref pos) != buf.Length) return -ErrorCode.EINVAL; ph = ELF32ProgramHeader.Read(buf); var size = ph.FileSize > ph.MemorySize ? ph.FileSize : ph.MemorySize; if (ph.type != ELF32ProgramHeader.PT_LOAD) continue; // Round address to page boundary var diff = Arch.ArchDefinition.PageOffset(ph.vaddr); var vaddr = new Pointer(ph.vaddr); var offset = ph.offset; var memSize = (int)Arch.ArchDefinition.PageAlign((uint)ph.MemorySize); var fileSize = ph.FileSize; if (diff < 0 || ph.offset < diff || fileSize + diff > file.inode.Size || fileSize <= 0 || memSize <= 0) return -ErrorCode.EINVAL; vaddr -= diff; offset -= (uint)diff; fileSize += diff; if (fileSize > memSize) fileSize = memSize; if (proc.Space.AddMapping(ph.ExpressOSAccessFlag, 0, file, offset, fileSize, vaddr, memSize) != 0) return -ErrorCode.ENOMEM; // Update brk var segmentEnd = (vaddr + memSize).ToUInt32(); if (segmentEnd > proc.Space.Brk) { proc.Space.InitializeBrk(segmentEnd); } } return 0; }
// // We do sync read for this one, since it's simpler.. // public static Process CreateProcess(ASCIIString path, ASCIIString[] argv, ASCIIString[] envp, AndroidApplicationInfo appInfo) { var proc = new Process(path, appInfo); Utils.Assert(!proc.Space.impl._value.isInvalid); uint addr; int workspace_fd; uint workspace_size; proc.helperPid = Arch.IPCStubs.linux_sys_take_helper(out addr, out workspace_fd, out workspace_size); if (proc.helperPid < 0) { Arch.Console.WriteLine("CreateProcess: cannot get helper"); return null; } proc.ShadowBinderVMStart = addr; ErrorCode ec; var inode = Arch.ArchFS.Open(proc.helperPid, path, 0, 0, out ec); if (inode == null) { Arch.Console.WriteLine("CreateProcess: cannot open file"); return null; } var stack_top = new UserPtr(INITIAL_STACK_LOCATION); // 4M Initial stack var stack_size = 4096 * Arch.ArchDefinition.PageSize; proc.Space.AddStackMapping(stack_top, stack_size); stack_top += stack_size; var augmented_envp = CreateEnvpArrayWithWorkspace(envp, proc, workspace_fd, workspace_size); var envp_ptr = PushCharArray(proc, augmented_envp, ref stack_top); if (envp_ptr == null) { Arch.Console.WriteLine("CreateProcess: Push envp failed"); return null; } var argv_ptr = PushCharArray(proc, argv, ref stack_top); if (argv_ptr == null) { Arch.Console.WriteLine("CreateProcess: Push argv failed"); return null; } stack_top = UserPtr.RoundDown(stack_top); // Parse the ELF file, which might push additional info on to the stack // (i.e., aux vectors when the ELF is dynamically linked) var file = new File(proc, inode, FileFlags.ReadWrite, 0); int ret = ELF32Header.Parse(proc.helperPid, file, proc, ref stack_top); if (ret != 0) { Arch.Console.WriteLine("CreateProcess: Parse ELF file failed"); return null; } //%esp The stack contains the arguments and environment: // 0(%esp) argc // 4(%esp) argv[0] // ... // (4*argc)(%esp) NULL // (4*(argc+1))(%esp) envp[0] // ... // NULL if (PushArgumentPointers(proc, envp_ptr, ref stack_top) != 0) return null; if (PushArgumentPointers(proc, argv_ptr, ref stack_top) != 0) return null; if (PushInt(proc, argv_ptr.Length, ref stack_top) != 0) return null; // Stdio var file_stdout = File.CreateStdout(proc); Contract.Assume(proc.Files.IsAvailableFd(Process.STDOUT_FD)); proc.InstallFd(Process.STDOUT_FD, file_stdout); var file_stderr = File.CreateStdout(proc); Contract.Assume(proc.Files.IsAvailableFd(Process.STDERR_FD)); proc.InstallFd(Process.STDERR_FD, file_stderr); var mainThread = Thread.Create(proc); if (appInfo != null) { var p = appInfo.ToParcel(); Globals.LinuxIPCBuffer.CopyFrom(0, p); Arch.IPCStubs.WriteAppInfo(proc.helperPid, p.Length); } // Start the main thread mainThread.Start(new Pointer(proc.EntryPoint), stack_top.Value); return proc; }
private static ASCIIString[] CreateEnvpArrayWithWorkspace(ASCIIString[] envp, Process proc, int workspace_fd, uint workspace_size) { var res = new ASCIIString[envp.Length + 1]; for (int i = 0; i < envp.Length; ++i) res[i] = envp[i]; var inode = new Arch.ArchINode(workspace_fd, workspace_size, proc.helperPid); var file = new File(proc, inode, FileFlags.ReadOnly, 0); var fd = proc.GetUnusedFd(); proc.InstallFd(fd, file); var s = "ANDROID_PROPERTY_WORKSPACE=" + fd.ToString() + "," + workspace_size.ToString(); res[envp.Length] = new ASCIIString(s); return res; }
internal static void HandleOpenFileCompletion(OpenFileCompletion c, int linux_fd, int size) { var current = c.thr; var proc = current.Parent; GenericINode inode = null; int ret = linux_fd; if (ret < 0) { c.Dispose(); current.ReturnFromCompletion(ret); return; } switch (c.fileKind) { case GenericINode.INodeKind.ArchINodeKind: inode = new Arch.ArchINode(linux_fd, (uint)size, proc.helperPid); break; case GenericINode.INodeKind.SecureFSINodeKind: inode = SecureFS.HandleOpenFileCompletion(c, linux_fd, size, ref ret); break; default: break; } if (inode != null) { var file = new File(proc, inode, c.flags, c.mode); ret = proc.GetUnusedFd(); proc.InstallFd(ret, file); } c.Dispose(); current.ReturnFromCompletion(ret); return; }
public static int Open(Thread current, ref Arch.ExceptionRegisters regs, UserPtr filenamePtr, int flags, int mode) { // TODO: Deal with current path var filenameBuf = new byte[PATH_MAX]; var ret = filenamePtr.ReadString(current, filenameBuf); var proc = current.Parent; int fd = 0; GenericINode inode = null; var startTime = Arch.NativeMethods.l4api_get_system_clock(); if (Util.ByteStringCompare(filenameBuf, IPCFilename.GetByteString()) == 0) { fd = proc.GetUnusedFd(); inode = BinderINode.Instance; } else if (Util.ByteStringCompare(filenameBuf, AshmemFileName.GetByteString()) == 0) { var linux_fd = Arch.ArchFS.OpenAndReturnLinuxFd(current.Parent.helperPid, new ASCIIString(filenameBuf), flags, mode); if (linux_fd < 0) return linux_fd; inode = new AshmemINode(linux_fd, current.Parent.helperPid); fd = proc.GetUnusedFd(); } else if (SecureFS.IsSecureFS(current, filenameBuf)) { var completion = SecureFS.OpenAndReadPagesAsync(current, filenameBuf, flags, mode); if (completion == null) return -ErrorCode.ENOMEM; Globals.CompletionQueue.Enqueue(completion); current.SaveState(ref regs); current.AsyncReturn = true; return 0; } else { var filename_len = ret; var completion = Arch.ArchFS.OpenAndGetSizeAsync(current, filenameBuf, flags, mode); if (completion == null) return -ErrorCode.ENOMEM; Globals.CompletionQueue.Enqueue(completion); current.SaveState(ref regs); current.AsyncReturn = true; return 0; } if (fd > 0) { var file = new File(proc, inode, flags, mode); proc.InstallFd(fd, file); } if (SyscallProfiler.Enable) { var endTime = Arch.NativeMethods.l4api_get_system_clock(); SyscallProfiler.AccountOpen((int)inode.kind, (long)(endTime - startTime)); } return fd; }
/* * Write a segment buffer. * * The buffer itself is passed as a reference, because some inode might take the ownership * of the buffer and put it as a part of its completion. In this case the buf is set to empty */ internal int Write(Thread current, ref Arch.ExceptionRegisters regs, ref ByteBufferRef buf, int len, uint pos, File file) { switch (kind) { case INodeKind.ConsoleINodeKind: { uint dummy = 0; return ConsoleINode.WriteImpl(current, buf, len, ref dummy); } case INodeKind.ArchINodeKind: case INodeKind.SocketINodeKind: return ArchINode.ArchWrite(current, ref regs, ref buf, len, pos, file); case INodeKind.SecureFSINodeKind: return SFSINode.SFSWrite(current, ref regs, buf, len, pos, file); } return -ErrorCode.EINVAL; }
internal int Write(Thread current, ref Arch.ExceptionRegisters regs, UserPtr userBuf, int len, uint pos, File file) { var buf = Globals.AllocateAlignedCompletionBuffer(len); if (!buf.isValid) return -ErrorCode.ENOMEM; var l = userBuf.Read(current, buf, len); var bytesToBeWritten = len - l; var ret = Write(current, ref regs, ref buf, bytesToBeWritten, pos, file); // Buffer hasn't been taken by write(), free it here if (buf.isValid) Globals.CompletionQueueAllocator.FreePages(new Pointer(buf.Location), buf.Length >> Arch.ArchDefinition.PageShift); return ret; }
internal int Read(Thread current, ref Arch.ExceptionRegisters regs, UserPtr userBuf, int len, uint pos, File file) { switch (kind) { case INodeKind.ArchINodeKind: case INodeKind.SocketINodeKind: return ArchINode.ArchRead(current, ref regs, userBuf, len, pos, file); case INodeKind.SecureFSINodeKind: return SFSINode.SFSRead(current, ref regs, userBuf, len, pos, file); } return -ErrorCode.EINVAL; }
public static void HandleSocketCompletion(SocketCompletion c, int ret) { var current = c.thr; if (ret < 0) { current.ReturnFromCompletion(ret); return; } var proc = current.Parent; var inode = new SocketINode(ret, proc.helperPid); var file = new File(proc, inode, FileFlags.ReadWriteMask, 0); var fd = proc.GetUnusedFd(); proc.InstallFd(fd, file); current.ReturnFromCompletion(fd); }
private static int FindInterpreter(File file, ELF32Header eh, ref ELF32ProgramHeader ret) { var buf = new byte[ELF32ProgramHeader.Size]; uint pos = 0; ushort i = 0; while (i < eh.NumOfProgramHeader) { pos = (uint)(eh.ProgramHeaderOffest + eh.ProgramHeaderSize * i); if (file.Read(buf, ref pos) != ELF32ProgramHeader.Size) return -ErrorCode.EINVAL; ret = ELF32ProgramHeader.Read(buf); if (ret.type == ELF32ProgramHeader.PT_INTERP) break; ++i; } return i == eh.NumOfProgramHeader ? -ErrorCode.ENOENT : 0; }
public static int Pipe(Thread current, UserPtr pipeFd) { var proc = current.Parent; var helperPid = proc.helperPid; int fd0, fd1; var ret = Arch.IPCStubs.Pipe(helperPid, out fd0, out fd1); if (ret < 0) return ret; var inode1 = new Arch.ArchINode(fd0, 0, helperPid); var inode2 = new Arch.ArchINode(fd1, 0, helperPid); // XXX: are the permission settings correct? var file1 = new File(proc, inode1, FileFlags.ReadWrite, 0); var rfd0 = proc.GetUnusedFd(); proc.InstallFd(rfd0, file1); var file2 = new File(proc, inode2, FileFlags.ReadWrite, 0); var rfd1 = proc.GetUnusedFd(); proc.InstallFd(rfd1, file2); //Arch.Console.Write("pipe: linux_fd ["); //Arch.Console.Write(fd0); //Arch.Console.Write(","); //Arch.Console.Write(fd1); //Arch.Console.Write("] => ["); //Arch.Console.Write(rfd0); //Arch.Console.Write(","); //Arch.Console.Write(rfd1); //Arch.Console.Write("], ret="); //Arch.Console.Write(ret); //Arch.Console.WriteLine(); if (pipeFd.Write(current, rfd0) != 0 || (pipeFd + sizeof(int)).Write(current, rfd1) != 0) { Arch.IPCStubs.Close(helperPid, fd0); Arch.IPCStubs.Close(helperPid, fd1); return -ErrorCode.EFAULT; } return ret; }