Example #1
0
        /// <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);
        }
Example #2
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); }
        }
Example #3
0
        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);
            }
        }
Example #4
0
        /// <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);
        }