1 using System;
2 using System.IO;
3 using System.Collections;
4 using cmsx.machine;
5 using cmsx.object;
6 using cmsx.util;
7
8 namespace cmsx.kernel
9 {
10 public SharedPtr<ByteStream> ReadFileIntoMemoryStream(INode* inode)
11 {
12 if (Log())
13 {
14 LogMessage("proc.exec", "read.mem.begin");
15 }
16 SharedPtr<ByteStream> memoryStream(new MemoryByteStream());
17 long fileSize = inode->GetFileSize();
18 FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
19 long bytesLeft = fileSize;
20 long offset = 0;
21 while (bytesLeft > 0)
22 {
23 int blockNumber = 0;
24 int blockOffset = 0;
25 fs->GetBlockManager()->GetBlockNumber(inode, offset, blockNumber, blockOffset, false);
26 long bytesToRead = Min(bytesLeft, blockSize - blockOffset);
27 if (bytesToRead == 0)
28 {
29 break;
30 }
31 Block* block = null;
32 if (!fs->IsRootFileSystem() || blockNumber != 0)
33 {
34 block = fs->GetBlockManager()->ReadBlock(BlockKey(blockNumber, inode->Key().fsNumber), null);
35 BlockPutter blockPutter(block);
36 FileBlock* fileBlock = cast<FileBlock*>(block);
37 for (long i = 0; i < bytesToRead; ++i;)
38 {
39 byte x = fileBlock->GetByte(blockOffset + i);
40 memoryStream->Write(x);
41 }
42 }
43 else
44 {
45 for (long i = 0; i < bytesToRead; ++i;)
46 {
47 byte x = 0u;
48 memoryStream->Write(x);
49 }
50 }
51 bytesLeft = bytesLeft - bytesToRead;
52 offset = offset + bytesToRead;
53 }
54 if (Log())
55 {
56 LogMessage("proc.exec", "read.mem.end");
57 }
58 return memoryStream;
59 }
60
61 public SharedPtr<ByteStream> ReadFirstBlockIntoMemoryStream(INode* inode)
62 {
63 SharedPtr<ByteStream> memoryStream(new MemoryByteStream());
64 long fileSize = inode->GetFileSize();
65 FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
66 long bytesLeft = Min(blockSize, fileSize);
67 long offset = 0;
68 while (bytesLeft > 0)
69 {
70 int blockNumber = 0;
71 int blockOffset = 0;
72 fs->GetBlockManager()->GetBlockNumber(inode, offset, blockNumber, blockOffset, false);
73 long bytesToRead = Min(bytesLeft, blockSize - blockOffset);
74 if (bytesToRead == 0)
75 {
76 break;
77 }
78 Block* block = null;
79 if (!fs->IsRootFileSystem() || blockNumber != 0)
80 {
81 block = fs->GetBlockManager()->ReadBlock(BlockKey(blockNumber, inode->Key().fsNumber), null);
82 BlockPutter blockPutter(block);
83 FileBlock* fileBlock = cast<FileBlock*>(block);
84 for (long i = 0; i < bytesToRead; ++i;)
85 {
86 byte x = fileBlock->GetByte(blockOffset + i);
87 memoryStream->Write(x);
88 }
89 }
90 else
91 {
92 for (long i = 0; i < bytesToRead; ++i;)
93 {
94 byte x = 0u;
95 memoryStream->Write(x);
96 }
97 }
98 bytesLeft = bytesLeft - bytesToRead;
99 offset = offset + bytesToRead;
100 }
101 return memoryStream;
102 }
103
104 public SharedPtr<ByteStream> ReadFilePortionIntoMemoryStream(INode* inode, long fileOffset, long count)
105 {
106 SharedPtr<ByteStream> memoryStream(new MemoryByteStream());
107 FileSystem* fs = GetMountTable().GetFileSystem(inode->Key().fsNumber);
108 long offset = fileOffset;
109 while (count > 0)
110 {
111 int blockNumber = 0;
112 int blockOffset = 0;
113 fs->GetBlockManager()->GetBlockNumber(inode, offset, blockNumber, blockOffset, false);
114 long bytesLeft = Min(blockSize, count);
115 long bytesToRead = Min(bytesLeft, blockSize - blockOffset);
116 if (bytesToRead == 0)
117 {
118 break;
119 }
120 Block* block = null;
121 if (!fs->IsRootFileSystem() || blockNumber != 0)
122 {
123 block = fs->GetBlockManager()->ReadBlock(BlockKey(blockNumber, inode->Key().fsNumber), null);
124 BlockPutter blockPutter(block);
125 FileBlock* fileBlock = cast<FileBlock*>(block);
126 for (long i = 0; i < bytesToRead; ++i;)
127 {
128 byte x = fileBlock->GetByte(blockOffset + i);
129 memoryStream->Write(x);
130 }
131 }
132 else
133 {
134 for (long i = 0; i < bytesToRead; ++i;)
135 {
136 byte x = 0u;
137 memoryStream->Write(x);
138 }
139 }
140 offset = offset + bytesToRead;
141 count = count - bytesToRead;
142 }
143 return memoryStream;
144 }
145
146 public void Exec(Machine& machine, ProcessTable& processTable, cmsx.kernel.Process* process, int argc, int envc, ulong bufferAddress, long count)
147 {
148 if (Log())
149 {
150 LogMessage("proc.exec", "begin");
151 }
152 bool followExec = Machine.GetFlag(Machine.Flags.followExec);
153 if (argc < 1)
154 {
155 throw SystemError(EINVAL, "exec: argc must be at least 1");
156 }
157 if (envc < 0)
158 {
159 throw SystemError(EINVAL, "exec: envc is negative");
160 }
161 if (bufferAddress == 0u)
162 {
163 throw SystemError(EINVAL, "exec: buffer address is null");
164 }
165 if (count < 0)
166 {
167 throw SystemError(EINVAL, "exec: count is negative");
168 }
169 UniquePtr<byte> buffer(cast<byte*>(RtMemAlloc(count)));
170 ReadProcessMemory(machine, process, bufferAddress, buffer.Get(), cast<ulong>(count));
171 MemoryReader reader(buffer.Get(), count);
172 List<string> args;
173 for (int i = 0; i < argc; ++i;)
174 {
175 long offset = reader.ReadLong();
176 long length = reader.ReadLong();
177 byte* argPtr = buffer.Get() + offset;
178 MemoryReader argReader(argPtr, length);
179 string arg = argReader.ReadString();
180 if (arg.IsEmpty())
181 {
182 throw SystemError(EINVAL, "exec: argument " + ToString(i) + " is empty");
183 }
184 args.Add(arg);
185 }
186 #assert(!args.IsEmpty());
187 List<string> environment;
188 for (int i = 0; i < envc; ++i;)
189 {
190 long offset = reader.ReadLong();
191 long length = reader.ReadLong();
192 byte* envPtr = buffer.Get() + offset;
193 MemoryReader envReader(envPtr, length);
194 string env = envReader.ReadString();
195 if (env.IsEmpty())
196 {
197 throw SystemError(EINVAL, "exec: environment variable " + ToString(i) + " is empty");
198 }
199 environment.Add(env);
200 }
201 const string& executablePath = args.Front();
202 FileSystem* rootFS = GetMountTable().GetFileSystem(0);
203 INode* inode = rootFS->GetINodeManager()->PathToINode(process, executablePath);
204 if (inode == null)
205 {
206 throw SystemError(ENOENT, "executable '" + executablePath + "' not found");
207 }
208 INodePutter putter(inode);
209 if (inode->Type() != FileType.regular)
210 {
211 throw SystemError(EINVAL, "path '" + executablePath + "' does not denote a regular file");
212 }
213 try
214 {
215 inode->CheckPermissions(process->uid, process->gid, Access.execute);
216 }
217 catch (const Exception& ex)
218 {
219 throw SystemError(EPERM, "cannot execute '" + executablePath + "' using uid " + ToString(process->uid) + ": " + ex.Message());
220 }
221 ulong envDataAddress = 0u;
222 Section poolSection = MakePoolSection(args, environment, envDataAddress);
223 long poolDataLength = poolSection.data.Count();
224 long poolSize = Align(poolDataLength, cast<long>(pageSize));
225 ulong poolSegmentSize = cast<ulong>(poolSize);
226 INodeKey executableINodeKey = inode->Key();
227 cmsx.kernel.Process* prevProcess = processTable.GetProcessFromExecutableINodeProcessMap(executableINodeKey);
228 ExecutableFile* executable = null;
229 if (prevProcess != null && !followExec)
230 {
231 if (Log())
232 {
233 LogMessage("proc.exec", "clone.begin");
234 }
235 SharedPtr<ByteStream> memoryStream = ReadFirstBlockIntoMemoryStream(inode);
236 BinaryReader reader(memoryStream);
237 UniquePtr<BinaryFile> binaryFile = ReadBinaryFile(reader, executablePath, ReadOption.readHeadersOnly);
238 if (binaryFile.Get() is ExecutableFile*)
239 {
240 executable = cast<ExecutableFile*>(binaryFile.Get());
241 FreeProcessMemory(machine.GetMemory(), process);
242 InitializeMemoryTable(machine, process->memoryTable, prevProcess->memoryTable, poolSegmentSize);
243 AllocateMemory(machine.GetMemory(), process->memoryTable, true);
244 Section* codeSection = executable->GetCodeSection();
245 codeSection->SetBaseAddress(4096u);
246 Section* dataSection = executable->GetDataSection();
247 SharedPtr<ByteStream> dataSectionMemoryStream = ReadFilePortionIntoMemoryStream(inode, cast<long>(dataSection->fileOffset), cast<long>(dataSection->length));
248 BinaryReader dataSectionReader(dataSectionMemoryStream);
249 dataSection->Read(dataSectionReader);
250 WriteProcessMemory(machine, process, dataSection->BaseAddress(), dataSection->data.Begin().Ptr(), cast<ulong>(dataSection->data.Count()), Protection.write);
251 Section* symbolSection = executable->GetSymbolSection();
252 SharedPtr<ByteStream> symbolSectionMemoryStream = ReadFilePortionIntoMemoryStream(inode, cast<long>(symbolSection->fileOffset), cast<long>(symbolSection->length));
253 BinaryReader symbolSectionReader(symbolSectionMemoryStream);
254 symbolSection->Read(symbolSectionReader);
255 executable->AddSymbolsToAddressMap();
256 Symbol* mainFunctionSymbol = executable->GetSymbolTable().GetSymbol("Main");
257 process->entryPoint = mainFunctionSymbol->start;
258 SaveContext(machine, process);
259 Symbol* functionTableSymbol = executable->GetSymbolTable().GetSymbol("@function_table");
260 process->functionTableAddress = functionTableSymbol->start;
261 process->functionTableLength = functionTableSymbol->length;
262 if (followExec)
263 {
264 executable = cast<ExecutableFile*>(binaryFile.Release());
265 }
266 }
267 else
268 {
269 throw SystemError(EINVAL, "file '" + executablePath + "' is not a CMSX executable");
270 }
271 if (Log())
272 {
273 LogMessage("proc.exec", "clone.end");
274 }
275 }
276 else
277 {
278 if (Log())
279 {
280 LogMessage("proc.exec", "read.begin");
281 }
282 SharedPtr<ByteStream> memoryStream = ReadFileIntoMemoryStream(inode);
283 BinaryReader reader(memoryStream);
284 UniquePtr<BinaryFile> binaryFile = ReadBinaryFile(reader, executablePath);
285 if (binaryFile.Get() is ExecutableFile*)
286 {
287 executable = cast<ExecutableFile*>(binaryFile.Get());
288 executable->AddSymbolsToAddressMap();
289 Section* codeSection = executable->GetCodeSection();
290 Section* dataSection = executable->GetDataSection();
291 Section* symbolSection = executable->GetSymbolSection();
292 Symbol* mainFunctionSymbol = executable->GetSymbolTable().GetSymbol("Main");
293 process->entryPoint = mainFunctionSymbol->start;
294 ulong textSegmentStartAddress = codeSection->BaseAddress();
295 if (textSegmentStartAddress == 0u)
296 {
297 textSegmentStartAddress = textSegmentStartAddress + 4096u;
298 }
299 ulong textSegmentSize = codeSection->DataLength();
300 ulong dataSegmentSize = dataSection->DataLength();
301 ulong minStackSegmentSize = cast<ulong>(executable->MinStackSize());
302 ulong maxStackSegmentSize = cast<ulong>(executable->MaxStackSize());
303 ulong stackSegmentIncrement = cast<ulong>(executable->StackSizeIncrement());
304 FreeProcessMemory(machine.GetMemory(), process);
305 InitializeProcessMemory(machine, process, textSegmentStartAddress, textSegmentSize, dataSegmentSize, poolSegmentSize, minStackSegmentSize, maxStackSegmentSize, stackSegmentIncrement);
306 if (codeSection->length > 0u)
307 {
308 WriteProcessMemory(machine, process, codeSection->BaseAddress(), codeSection->data.Begin().Ptr(), cast<ulong>(codeSection->data.Count()), Protection.execute);
309 }
310 if (dataSection->length > 0u)
311 {
312 WriteProcessMemory(machine, process, dataSection->BaseAddress(), dataSection->data.Begin().Ptr(), cast<ulong>(dataSection->data.Count()), Protection.write);
313 }
314 Symbol* functionTableSymbol = executable->GetSymbolTable().GetSymbol("@function_table");
315 process->functionTableAddress = functionTableSymbol->start;
316 process->functionTableLength = functionTableSymbol->length;
317 if (followExec)
318 {
319 executable = cast<ExecutableFile*>(binaryFile.Release());
320 }
321 }
322 else
323 {
324 throw SystemError(EINVAL, "file '" + executablePath + "' is not a CMSX executable");
325 }
326 if (Log())
327 {
328 LogMessage("proc.exec", "read.end");
329 }
330 }
331 process->executableINodeKey = executableINodeKey;
332 processTable.AddProcessToExecutableINodeProcessMap(process);
333 ulong poolStart = poolSegmentBaseAddress + cast<ulong>(poolSize);
334 ulong poolEnd = poolSegmentBaseAddress + cast<ulong>(poolSize);
335 process->poolStart = poolStart;
336 process->poolEnd = poolEnd;
337 process->name = Path.GetFileName(executablePath);
338 if (!poolSection.data.IsEmpty())
339 {
340 WriteProcessMemory(machine, process, poolSegmentBaseAddress, poolSection.data.Begin().Ptr(), cast<ulong>(poolSection.data.Count()), Protection.write);
341 int ax = cast<int>(args.Count());
342 ulong bx = poolSegmentBaseAddress;
343 ulong cx = envDataAddress;
344 WriteProcessMemory(machine, process, process->regAXAddress, cast<ulong>(ax), 8u, Protection.write);
345 WriteProcessMemory(machine, process, process->regBXAddress, bx, 8u, Protection.write);
346 WriteProcessMemory(machine, process, process->regCXAddress, cx, 8u, Protection.write);
347 }
348 if (Log())
349 {
350 LogMessage("proc.exec", "ready");
351 }
352 SetProcessReadyToRun(processTable, process);
353 process->unsave = true;
354 if (followExec)
355 {
356 Kernel& kernel = GetKernel();
357 kernel.SetExecutable(executable);
358 Debugger* debugger = kernel.GetDebugger();
359 debugger->Init(process);
360 }
361 if (Log())
362 {
363 LogMessage("proc.exec", "schedule");
364 }
365 Schedule(machine, processTable);
366 if (Log())
367 {
368 LogMessage("proc.exec", "end");
369 }
370 }
371 }