/// <summary> /// Links several files to create an executable (stored to dest). /// </summary> /// <param name="dest">the resulting executable (on success) (non-null)</param> /// <param name="files">the files to link. ".o" files are loaded as object files, otherwise treated as assembly source and assembled</param> /// <param name="entry_point">the main entry point</param> /// <param name="rootdir">the root directory to use for core file lookup - null for default</param> private static int Link(Executable dest, List <string> files, string entry_point, string rootdir) { var objs = new List <KeyValuePair <string, ObjectFile> >(); // load the stdlib files int ret = LoadStdlibObjs(objs, rootdir); if (ret != 0) { return(ret); } // load the provided files foreach (string file in files) { // treat ".o" as object file, otherwise as assembly source ObjectFile obj = new ObjectFile(); ret = file.EndsWith(".o") ? LoadObjectFile(file, obj) : Assemble(file, obj); if (ret != 0) { return(ret); } objs.Add(new KeyValuePair <string, ObjectFile>(file, obj)); } // link the resulting object files into an executable LinkResult res = Assembly.Link(dest, objs.ToArray(), entry_point); // if there was an error, show error message if (res.Error != LinkError.None) { Console.Error.WriteLine($"Link Error:\n{res.ErrorMsg}"); return((int)res.Error); } return(0); }
// ------------------- // // -- executable io -- // // ------------------- // /// <summary> /// Saves a csx64 executable to a file (no format checking). /// </summary> /// <param name="path">the file to save to</param> /// <param name="exe">the csx64 executable to save</param> private static int SaveExecutable(string path, Executable exe) { try { exe.Save(path); return(0); } // things from CSX64 catch (EmptyError) { Console.Error.WriteLine($"Attempt to save empty executable to {path}"); return((int)AsmLnkErrorExt.FormatError); } // things from File.OpenRead catch (ArgumentNullException) { Console.Error.WriteLine("Path was null"); return((int)AsmLnkErrorExt.NullPath); } catch (ArgumentException) { Console.Error.WriteLine($"Path \"{path}\" was invalid"); return((int)AsmLnkErrorExt.InvalidPath); } catch (PathTooLongException) { Console.Error.WriteLine($"Path \"{path}\" was too long"); return((int)AsmLnkErrorExt.InvalidPath); } catch (DirectoryNotFoundException) { Console.Error.WriteLine($"Path \"{path}\" directory was not found"); return((int)AsmLnkErrorExt.DirectoryNotFound); } catch (UnauthorizedAccessException) { Console.Error.WriteLine($"You do not have permission to open \"{path}\" for reading"); return((int)AsmLnkErrorExt.AccessViolation); } catch (FileNotFoundException) { Console.Error.WriteLine($"File \"{path}\" could not be found"); return((int)AsmLnkErrorExt.FileNotFound); } catch (NotSupportedException) { Console.Error.WriteLine($"Path \"{path}\" was of an unsupported format"); return((int)AsmLnkErrorExt.PathFormatUnsupported); } catch (IOException) { Console.Error.WriteLine($"An error occurred while reading file \"{path}\""); return((int)AsmLnkErrorExt.IOError); } // everything else that might happen for some reason catch (Exception ex) { Console.Error.WriteLine($"An error occurred while attempting to execute \"{path}\"\n-> {ex}"); return((int)AsmLnkErrorExt.UnknownError); } }
private static unsafe int Main(string[] args) { try { if (!BitConverter.IsLittleEndian) { Console.Error.WriteLine( @"(Uhoh!! Looks like this platform isn't little-endian! Most everything in CSX64 assumes little-endian, so most of it won't work on this system! )"); return(-1); } // ------------------------------------- // // set up initilization thingys for graphical stuff Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // run statics fom client sources GraphicalComputer.InitStatics(); // ------------------------------------- // cmdln_pack dat = new cmdln_pack(); if (!dat.parse(args)) { return(0); } // ------------------------------------- // // perform the action switch (dat.action) { case ProgramAction.ExecuteConsole: { if (dat.pathspec.Count == 0) { Console.Error.WriteLine("Expected a file to execute"); return(0); } Executable exe = new Executable(); int res = LoadExecutable(dat.pathspec[0], exe); return(res != 0 ? res : RunConsole(exe, dat.pathspec.ToArray(), dat.fsf, dat.time)); } case ProgramAction.ExecuteGraphical: { if (dat.pathspec.Count == 0) { Console.Error.WriteLine("Expected a file to execute"); return(0); } Executable exe = new Executable(); int res = LoadExecutable(dat.pathspec[0], exe); return(res != 0 ? res : RunGraphical(exe, dat.pathspec.ToArray(), dat.fsf)); } case ProgramAction.ExecuteConsoleScript: { if (dat.pathspec.Count == 0) { Console.Error.WriteLine("Expected a file to assemble, link, and execute"); return(0); } AddPredefines(); Executable exe = new Executable(); int res = Link(exe, new List <string>() { dat.pathspec[0] }, dat.entry_point ?? "main", dat.rootdir); return(res != 0 ? res : RunConsole(exe, dat.pathspec.ToArray(), dat.fsf, dat.time)); } case ProgramAction.ExecuteConsoleMultiscript: { if (dat.pathspec.Count == 0) { Console.Error.WriteLine("Expected 1+ files to assemble, link, and execute"); return(0); } AddPredefines(); Executable exe = new Executable(); int res = Link(exe, dat.pathspec, dat.entry_point ?? "main", dat.rootdir); return(res != 0 ? res : RunConsole(exe, new string[] { "<script>" }, dat.fsf, dat.time)); } case ProgramAction.Assemble: { if (dat.pathspec.Count == 0) { Console.Error.WriteLine("Expected 1+ files to assemble"); return(0); } AddPredefines(); ObjectFile obj = new ObjectFile(); // if no explicit output is provided, batch process each pathspec if (dat.output == null) { foreach (string path in dat.pathspec) { int res = Assemble(path, obj); if (res != 0) { return(res); } res = SaveObjectFile(Path.ChangeExtension(path, ".o"), obj); if (res != 0) { return(res); } } return(0); } // otherwise, we're expecting only one input with a named output else { if (dat.pathspec.Count != 1) { Console.Error.WriteLine("Assembler with an explicit output expected only one input"); return(0); } int res = Assemble(dat.pathspec[0], obj); return(res != 0 ? res : SaveObjectFile(dat.output, obj)); } } case ProgramAction.Link: { if (dat.pathspec.Count == 0) { Console.Error.WriteLine("Linker expected 1+ files to link"); return(0); } AddPredefines(); Executable exe = new Executable(); int res = Link(exe, dat.pathspec, dat.entry_point ?? "main", dat.rootdir); return(res != 0 ? res : SaveExecutable(dat.output ?? "a.out", exe)); } } // end switch return(0); } catch (Exception ex) { Console.Error.WriteLine($"UNHANDLED EXCEPTION:\n{ex}"); return(-666); } }
/// <summary> /// Initializes the computer for execution /// </summary> /// <param name="exe">the memory to load before starting execution (memory beyond this range is undefined)</param> /// <param name="args">the command line arguments to provide to the computer. pass null or empty array for none</param> /// <param name="stacksize">the amount of additional space to allocate for the program's stack</param> public void Initialize(Executable exe, string[] args, UInt64 stacksize = 2 * 1024 * 1024) { // get size of memory we need to allocate UInt64 size = exe.TotalSize + stacksize; // make sure we catch overflow from adding stacksize if (size < stacksize) { throw new OverflowException("memory size overflow uint64"); } // make sure it's within max memory usage limits if (size > MaxMemory) { throw new MemoryAllocException("executable size exceeded max memory"); } // get new memory array (does not include header) Memory = new byte[size]; // mark the minimum amount of memory (minimum sys_brk value) (so user can't truncate program data/stack/etc.) MinMemory = size; // copy the executable content into our memory array exe.Content.CopyTo(Memory, 0); // zero the bss segment (should already be done by C# but this makes it more clear and explicit) for (int i = 0; i < (int)exe.bss_seglen; ++i) { Memory[exe.Content.Length + i] = 0; } // randomize the heap/stack segments (C# won't let us create an uninitialized array and bit-zero is too safe - more realistic danger) for (int i = (int)exe.TotalSize; i < Memory.Length; ++i) { Memory[i] = (byte)Rand.Next(); } // set up memory barriers ExeBarrier = exe.text_seglen; ReadonlyBarrier = exe.text_seglen + exe.rodata_seglen; StackBarrier = exe.text_seglen + exe.rodata_seglen + exe.data_seglen + exe.bss_seglen; // set up cpu registers for (int i = 0; i < CPURegisters.Length; ++i) { CPURegisters[i].x64 = Rand.NextUInt64(); } // set up fpu registers FINIT(); // set up vpu registers for (int i = 0; i < ZMMRegisters.Length; ++i) { for (int j = 0; j < 8; ++j) { ZMMRegisters[i].uint64(j) = Rand.NextUInt64(); } } _MXCSR = 0x1f80; // set execution state RIP = 0; RFLAGS = 2; // x86 standard dictates this initial state Running = true; SuspendedRead = false; Error = ErrorCode.None; // get the stack pointer UInt64 stack = (UInt64)Memory.Length; RBP = stack; // RBP points to before we start pushing args // if we have args, handle those if (args != null) { // an array of pointers to command line args in computer memory. // one for each arg, plus a null terminator. UInt64[] cmdarg_pointers = new UInt64[args.Length + 1]; // put each arg on the stack and get their addresses for (int i = 0; i < args.Length; ++i) { // push the arg onto the stack stack -= (UInt64)args[i].Length + 1; SetCString(stack, args[i]); // record pointer to this arg cmdarg_pointers[i] = stack; } // the last pointer is null (C guarantees this, so we will as well) cmdarg_pointers[args.Length] = 0; // make room for the pointer array stack -= 8 * (UInt64)cmdarg_pointers.Length; // write pointer array to memory for (int i = 0; i < cmdarg_pointers.Length; ++i) { SetMem(stack + (UInt64)i * 8, cmdarg_pointers[i]); } } // otherwise we have no cmd line args else { // this case is as above, but where args is empty, so it's extremely trivial stack -= 8; SetMem(stack, 0); } // load arg count and arg array pointer to RDI, RSI RDI = args != null ? (UInt64)args.Length : 0; RSI = stack; // initialize RSP RSP = stack; // also push args to stack (RTL) Push(RSI); Push(RDI); }