| File: Program.cs | Web Access |
| Project: src\runtime\src\tools\ilasm\src\ilasm\ilasm.csproj (ilasm) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Parsing; using System.Diagnostics; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Text; namespace ILAssembler; internal sealed class Program { private readonly IlasmRootCommand _command; public Program(IlasmRootCommand command) { _command = command; } public int Run() { Stopwatch? stopwatch = null; if (Get(_command.Clock)) { stopwatch = Stopwatch.StartNew(); } string[]? inputFiles = _command.Result.GetValue(_command.InputFilePaths); bool quiet = Get(_command.Quiet); if (!Get(_command.NoLogo) && !quiet) { Console.WriteLine(IlasmRootCommand.ProductName); Console.WriteLine(); } if (inputFiles is null || inputFiles.Length == 0) { Console.Error.WriteLine("Error: No input file specified"); return 1; } // Validate all input files exist foreach (string file in inputFiles) { if (!File.Exists(file)) { Console.Error.WriteLine($"Error: Input file not found: {file}"); return 1; } } // Determine output file (based on first input file) bool isDll = Get(_command.BuildDll); string? outputPath = Get(_command.OutputFilePath) ?? $"{Path.GetFileNameWithoutExtension(inputFiles[0])}{(isDll ? ".dll" : ".exe")}"; int exitCode = 0; try { // Report each file being assembled foreach (string file in inputFiles) { if (!quiet) { Console.WriteLine($"Assembling '{file}' to {(isDll ? "DLL" : "EXE")} --> '{outputPath}'"); } } // Concatenate all input files var contentBuilder = new StringBuilder(); foreach (string file in inputFiles) { contentBuilder.AppendLine(File.ReadAllText(file)); } string content = contentBuilder.ToString(); // Use the first file as the primary document for source tracking var document = new SourceText(content, inputFiles[0]); // Build options bool errorTolerant = Get(_command.ErrorTolerant); var options = new Options { NoAutoInherit = Get(_command.NoAutoInherit), ErrorTolerant = errorTolerant, }; // Apply PE header overrides from command line int subsystem = Get(_command.Subsystem); if (subsystem != 0) { options.Subsystem = (Subsystem)subsystem; } string? ssver = Get(_command.SubsystemVersion); if (!string.IsNullOrEmpty(ssver)) { var parts = ssver.Split('.'); if (parts.Length == 2 && ushort.TryParse(parts[0], out ushort ssvMajor) && ushort.TryParse(parts[1], out ushort ssvMinor)) { options.SubsystemVersion = (ssvMajor, ssvMinor); } } int alignment = Get(_command.Alignment); if (alignment != 0) { options.FileAlignment = alignment; } long imageBase = Get(_command.ImageBase); if (imageBase != 0) { options.ImageBase = imageBase; } int stackReserve = Get(_command.StackReserve); if (stackReserve != 0) { options.StackReserve = stackReserve; } int flags = Get(_command.Flags); if (flags != 0) { options.CorFlags = (CorFlags)flags; } // Target machine if (Get(_command.TargetX64) || Get(_command.Pe64)) { options.Machine = Machine.Amd64; } else if (Get(_command.TargetArm)) { options.Machine = Machine.Arm; } else if (Get(_command.TargetArm64)) { options.Machine = Machine.Arm64; } // PE characteristics options.AppContainer = Get(_command.AppContainer); options.HighEntropyVA = Get(_command.HighEntropyVa); options.StripReloc = Get(_command.StripReloc); options.Prefer32Bit = Get(_command.Prefer32Bit); // Deterministic and metadata version options.Deterministic = Get(_command.Deterministic); options.MetadataVersion = Get(_command.MetadataVersion); // Debug options options.Debug = Get(_command.Debug); options.Pdb = Get(_command.Pdb); options.DebugMode = Get(_command.DebugMode); // Assembly options options.AssemblyName = Get(_command.AssemblyName); options.KeyFile = Get(_command.KeyFile); options.Optimize = Get(_command.Optimize); options.Fold = Get(_command.Fold); // Set up include path for #include directive resolution string? includePath = Get(_command.IncludePath); string baseDir = Path.GetDirectoryName(Path.GetFullPath(inputFiles[0])) ?? "."; SourceText LoadIncludedDocument(string path) { // Try the path as-is first if (File.Exists(path)) { return new SourceText(File.ReadAllText(path), path); } // Try relative to the base directory string fullPath = Path.Combine(baseDir, path); if (File.Exists(fullPath)) { return new SourceText(File.ReadAllText(fullPath), fullPath); } // Try the include path if specified if (!string.IsNullOrEmpty(includePath)) { fullPath = Path.Combine(includePath, path); if (File.Exists(fullPath)) { return new SourceText(File.ReadAllText(fullPath), fullPath); } } throw new FileNotFoundException($"Include file not found: {path}"); } byte[] LoadResource(string path) { // Try the path as-is first if (File.Exists(path)) { return File.ReadAllBytes(path); } // Try relative to the base directory string fullPath = Path.Combine(baseDir, path); if (File.Exists(fullPath)) { return File.ReadAllBytes(fullPath); } throw new FileNotFoundException($"Resource file not found: {path}"); } // Compile var compiler = new DocumentCompiler(); var (diagnostics, peBuilder) = compiler.Compile( document, LoadIncludedDocument, LoadResource, options); // Report diagnostics bool hasErrors = false; foreach (var diagnostic in diagnostics) { if (diagnostic.Severity == DiagnosticSeverity.Error) { hasErrors = true; Console.Error.WriteLine($"Error: {diagnostic.Location}: {diagnostic.Message}"); } else if (diagnostic.Severity == DiagnosticSeverity.Warning && !errorTolerant) { Console.WriteLine($"Warning: {diagnostic.Location}: {diagnostic.Message}"); } } // In error-tolerant mode, continue even with errors if (peBuilder is null) { Console.Error.WriteLine("***** FAILURE *****"); return 1; } if (hasErrors && !errorTolerant) { Console.Error.WriteLine("***** FAILURE *****"); return 1; } // Write output using var outputStream = File.Create(outputPath); var blobBuilder = new BlobBuilder(); peBuilder.Serialize(blobBuilder); blobBuilder.WriteContentTo(outputStream); if (hasErrors) { if (!quiet) { Console.WriteLine("Output file contains errors"); } } else if (!quiet) { Console.WriteLine("Operation completed successfully"); } } catch (Exception ex) { Console.Error.WriteLine($"Error: {ex.Message}"); exitCode = 1; } if (stopwatch is not null) { stopwatch.Stop(); Console.WriteLine($"Total time: {stopwatch.ElapsedMilliseconds} ms"); } return exitCode; } private T Get<T>(Argument<T> argument) => _command.Result.GetValue(argument)!; private T Get<T>(Option<T> option) => _command.Result.GetValue(option)!; private static int Main(string[] args) => new IlasmRootCommand() .Parse(args) .Invoke(); }