├── .devcontainer
├── Dockerfile
├── devcontainer.json
└── postCreateCommand.sh
├── .editorconfig
├── .git-blame-ignore-revs
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── dotnet.yml
│ ├── format.yml
│ └── package.yml
├── .gitignore
├── .idea
└── .idea.nativeaot-patcher
│ └── .idea
│ ├── .gitignore
│ ├── AndroidProjectSystem.xml
│ ├── indexLayout.xml
│ ├── projectSettingsUpdater.xml
│ ├── vcs.xml
│ └── workspace.xml
├── Directory.Build.props
├── Directory.Packages.props
├── Dockerfile
├── LICENSE
├── README.md
├── docs
├── .gitignore
├── api
│ ├── .gitignore
│ └── index.md
├── articles
│ ├── build
│ │ ├── asm-build.md
│ │ ├── ilc-build.md
│ │ ├── kernel-compilation-steps.md
│ │ └── patcher-build.md
│ ├── plugs.md
│ ├── testing.md
│ └── toc.yml
├── docfx.json
├── index.md
└── toc.yml
├── examples
└── KernelExample
│ ├── .gitignore
│ ├── Kernel.csproj
│ ├── Makefile
│ └── src
│ ├── Asm
│ └── io.asm
│ ├── Base64.cs
│ ├── Internal.cs
│ ├── KernelEntry.cs
│ ├── Limine
│ ├── LimineFramebuffer.cs
│ └── LimineID.cs
│ ├── PCScreen.cs
│ ├── Stdlib.cs
│ ├── Stellib.cs
│ ├── limine.conf
│ └── linker.ld
├── global.json
├── nativeaot-patcher.sln
├── src
├── Cosmos.API
│ ├── Attributes
│ │ ├── PlugAttribute.cs
│ │ └── PlugMemberAttribute.cs
│ ├── Cosmos.API.csproj
│ ├── Enum
│ │ └── TargetPlatform.cs
│ └── LabelMaker.cs
├── Cosmos.Asm.Build
│ ├── Cosmos.Asm.Build.csproj
│ ├── Tasks
│ │ ├── LdTask.cs
│ │ └── YasmBuildTask.cs
│ └── build
│ │ ├── Asm.Build.Unix.targets
│ │ ├── Asm.Build.Windows.targets
│ │ ├── Cosmos.Asm.Build.props
│ │ └── Cosmos.Asm.Build.targets
├── Cosmos.Common.Build
│ ├── Cosmos.Common.Build.csproj
│ └── build
│ │ ├── Common.Build.Unix.targets
│ │ ├── Common.Build.Windows.targets
│ │ ├── Cosmos.Common.Build.props
│ │ └── Cosmos.Common.Build.targets
├── Cosmos.Ilc.Build
│ ├── Cosmos.Ilc.Build.csproj
│ └── build
│ │ ├── Cosmos.Ilc.Build.props
│ │ └── Cosmos.Ilc.Build.targets
├── Cosmos.Patcher.Analyzer.CodeFixes
│ ├── Cosmos.Patcher.Analyzer.CodeFixes.csproj
│ ├── Models
│ │ └── ProjectInfo.cs
│ └── PatcherCodeFixProvider.cs
├── Cosmos.Patcher.Analyzer.Package
│ ├── Cosmos.Patcher.Analyzer.Package.csproj
│ └── tools
│ │ ├── install.ps1
│ │ └── uninstall.ps1
├── Cosmos.Patcher.Analyzer
│ ├── Cosmos.Patcher.Analyzer.csproj
│ ├── DiagnosticMessages.cs
│ ├── Extensions
│ │ ├── AttributeExtensions.cs
│ │ ├── EnumerableExtensions.cs
│ │ ├── SymbolExtensions.cs
│ │ └── SyntaxNodeExtensions.cs
│ ├── Models
│ │ └── PlugInfo.cs
│ └── PatcherAnalyzer.cs
├── Cosmos.Patcher.Build
│ ├── Cosmos.Patcher.Build.csproj
│ ├── Tasks
│ │ └── PatcherTask.cs
│ └── build
│ │ ├── Cosmos.Patcher.Build.props
│ │ └── Cosmos.Patcher.Build.targets
└── Cosmos.Patcher
│ ├── Cosmos.Patcher.csproj
│ ├── Extensions
│ └── MethodBodyEx.cs
│ ├── MonoCecilExtension.cs
│ ├── PatchCommand.cs
│ ├── PlugPatcher.cs
│ ├── PlugScanner.cs
│ ├── PlugUtils.cs
│ └── Program.cs
└── tests
├── Cosmos.Asm.Build.Test
├── Cosmos.Asm.Build.Test.csproj
├── GlobalUsings.cs
├── UnitTest1.cs
└── asm
│ └── test.asm
├── Cosmos.NativeLibrary
├── Cosmos.NativeLibrary.vcxproj
├── Cosmos.NativeLibrary.vcxproj.filters
└── nativelibrary.c
├── Cosmos.NativeWrapper
├── Cosmos.NativeWrapper.csproj
├── NativeWrapper.cs
├── NativeWrapperObject.cs
├── NativeWrapperObjectPlug.cs
└── NativeWrapperPlug.cs
├── Cosmos.Patcher.Analyzer.Tests
├── AnalyzerTests.cs
└── Cosmos.Patcher.Analyzer.Tests.csproj
├── Cosmos.Patcher.Tests
├── Cosmos.Patcher.Tests.csproj
├── PlugPatcherTest_ObjectPlugs.cs
└── PlugPatcherTest_StaticPlugs.cs
└── Cosmos.Scanner.Tests
├── Cosmos.Scanner.Tests.csproj
├── PlugScannerTests_LoadPlugMethods.cs
└── PlugScannerTests_LoadPlugs.cs
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the base image for .NET development
2 | FROM mcr.microsoft.com/devcontainers/dotnet:1-9.0-bookworm
3 |
4 | # Install clang, xorriso, lld, and yasm
5 | RUN apt-get update && apt-get install -y clang xorriso lld llvm yasm && apt-get clean
6 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the
2 | // README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
3 | {
4 | "name": "Cosmos Develeopment Container",
5 | // Use a Dockerfile instead of a prebuilt image
6 | "build": {
7 | "dockerfile": "Dockerfile"
8 | },
9 | "features": {
10 | "ghcr.io/devcontainers/features/dotnet:2": {},
11 | "ghcr.io/devcontainers/features/powershell:1": {}
12 | },
13 | "hostRequirements": {
14 | "cpus": 4,
15 | "memory": "8gb"
16 | },
17 |
18 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
19 | // "forwardPorts": [5000, 5001],
20 | // "portsAttributes": {
21 | // "5001": {
22 | // "protocol": "https"
23 | // }
24 | // }
25 |
26 | // Use 'postCreateCommand' to run commands after the container is created.
27 | "postCreateCommand": "./.devcontainer/postCreateCommand.sh",
28 | "customizations": {
29 | "vscode": {
30 | "extensions": [
31 | "ms-dotnettools.csdevkit",
32 | "GitHub.copilot-chat",
33 | "GitHub.copilot",
34 | "ms-vscode.cpptools",
35 | "ms-vscode.cpptools-extension-pack"
36 | ]
37 | },
38 | "settings": {
39 | // Loading projects on demand is better for larger codebases
40 | "omnisharp.enableMsBuildLoadProjectsOnDemand": true,
41 | "omnisharp.enableRoslynAnalyzers": true,
42 | "omnisharp.enableEditorConfigSupport": true,
43 | "omnisharp.enableAsyncCompletion": true
44 | }
45 | }
46 |
47 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
48 | // "remoteUser": "root"
49 | }
50 |
--------------------------------------------------------------------------------
/.devcontainer/postCreateCommand.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | # Pack all projects
6 | dotnet build ./src/Cosmos.API/Cosmos.API.csproj --configuration Release
7 | dotnet build ./src/Cosmos.Patcher.Build/Cosmos.Patcher.Build.csproj --configuration Release
8 | dotnet build ./src/Cosmos.Patcher/Cosmos.Patcher.csproj --configuration Release
9 | dotnet build ./src/Cosmos.Common.Build/Cosmos.Common.Build.csproj --configuration Release
10 | dotnet build ./src/Cosmos.Ilc.Build/Cosmos.Ilc.Build.csproj --configuration Release
11 | dotnet build ./src/Cosmos.Asm.Build/Cosmos.Asm.Build.csproj --configuration Release
12 | dotnet build ./src/Cosmos.Patcher.Analyzer.Package/Cosmos.Patcher.Analyzer.Package.csproj --configuration Release
13 |
14 | # Add output folder as a local NuGet source
15 | dotnet nuget add source "$PWD/artifacts/package/release" --name local-packages
16 |
17 | # Clear all NuGet locals cache
18 | dotnet nuget locals all --clear
19 |
20 | dotnet restore
21 |
22 | dotnet tool install -g ilc
23 | dotnet tool install -g Cosmos.Patcher
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Default settings:
7 | # A newline ending every file
8 | # Use 4 spaces as indentation
9 | [*]
10 | insert_final_newline = true
11 | indent_style = space
12 | indent_size = 4
13 | trim_trailing_whitespace = true
14 |
15 | # Generated code
16 | [*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}]
17 | generated_code = true
18 |
19 | # C# files
20 | [*.cs]
21 | # New line preferences
22 | csharp_new_line_before_open_brace = all
23 | csharp_new_line_before_else = true
24 | csharp_new_line_before_catch = true
25 | csharp_new_line_before_finally = true
26 | csharp_new_line_before_members_in_object_initializers = true
27 | csharp_new_line_before_members_in_anonymous_types = true
28 | csharp_new_line_between_query_expression_clauses = true
29 |
30 | # Indentation preferences
31 | csharp_indent_block_contents = true
32 | csharp_indent_braces = false
33 | csharp_indent_case_contents = true
34 | csharp_indent_case_contents_when_block = false
35 | csharp_indent_switch_labels = true
36 | csharp_indent_labels = one_less_than_current
37 |
38 | # Modifier preferences
39 | csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async:suggestion
40 |
41 | # avoid this. unless absolutely necessary
42 | dotnet_style_qualification_for_field = false:suggestion
43 | dotnet_style_qualification_for_property = false:suggestion
44 | dotnet_style_qualification_for_method = false:suggestion
45 | dotnet_style_qualification_for_event = false:suggestion
46 |
47 | # Types: use keywords instead of BCL types, and permit var only when the type is clear
48 | csharp_style_var_for_built_in_types = false:suggestion
49 | csharp_style_var_when_type_is_apparent = false:none
50 | csharp_style_var_elsewhere = false:suggestion
51 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
52 | dotnet_style_predefined_type_for_member_access = true:suggestion
53 |
54 | # name all constant fields using PascalCase
55 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
56 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
57 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
58 | dotnet_naming_symbols.constant_fields.applicable_kinds = field
59 | dotnet_naming_symbols.constant_fields.required_modifiers = const
60 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case
61 |
62 | # static fields should have s_ prefix
63 | dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
64 | dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
65 | dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
66 | dotnet_naming_symbols.static_fields.applicable_kinds = field
67 | dotnet_naming_symbols.static_fields.required_modifiers = static
68 | dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
69 | dotnet_naming_style.static_prefix_style.required_prefix = s_
70 | dotnet_naming_style.static_prefix_style.capitalization = camel_case
71 |
72 | # internal and private fields should be _camelCase
73 | dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
74 | dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
75 | dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
76 | dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
77 | dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
78 | dotnet_naming_style.camel_case_underscore_style.required_prefix = _
79 | dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
80 |
81 | # Code style defaults
82 | csharp_using_directive_placement = outside_namespace:suggestion
83 | dotnet_sort_system_directives_first = true
84 | csharp_prefer_braces = true:silent
85 | csharp_preserve_single_line_blocks = true:none
86 | csharp_preserve_single_line_statements = false:none
87 | csharp_prefer_static_local_function = true:suggestion
88 | csharp_prefer_simple_using_statement = false:none
89 | csharp_style_prefer_switch_expression = true:suggestion
90 | dotnet_style_readonly_field = true:suggestion
91 |
92 | # Expression-level preferences
93 | dotnet_style_object_initializer = true:suggestion
94 | dotnet_style_collection_initializer = true:suggestion
95 | dotnet_style_prefer_collection_expression = when_types_exactly_match
96 | dotnet_style_explicit_tuple_names = true:suggestion
97 | dotnet_style_coalesce_expression = true:suggestion
98 | dotnet_style_null_propagation = true:suggestion
99 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
100 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
101 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
102 | dotnet_style_prefer_auto_properties = true:suggestion
103 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
104 | dotnet_style_prefer_conditional_expression_over_return = true:silent
105 | csharp_prefer_simple_default_expression = true:suggestion
106 |
107 | # Expression-bodied members
108 | csharp_style_expression_bodied_methods = true:silent
109 | csharp_style_expression_bodied_constructors = true:silent
110 | csharp_style_expression_bodied_operators = true:silent
111 | csharp_style_expression_bodied_properties = true:silent
112 | csharp_style_expression_bodied_indexers = true:silent
113 | csharp_style_expression_bodied_accessors = true:silent
114 | csharp_style_expression_bodied_lambdas = true:silent
115 | csharp_style_expression_bodied_local_functions = true:silent
116 |
117 | # Pattern matching
118 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
119 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
120 | csharp_style_inlined_variable_declaration = true:suggestion
121 |
122 | # Null checking preferences
123 | csharp_style_throw_expression = true:suggestion
124 | csharp_style_conditional_delegate_call = true:suggestion
125 |
126 | # Other features
127 | csharp_style_prefer_index_operator = false:none
128 | csharp_style_prefer_range_operator = false:none
129 | csharp_style_pattern_local_over_anonymous_function = false:none
130 |
131 | # Space preferences
132 | csharp_space_after_cast = false
133 | csharp_space_after_colon_in_inheritance_clause = true
134 | csharp_space_after_comma = true
135 | csharp_space_after_dot = false
136 | csharp_space_after_keywords_in_control_flow_statements = true
137 | csharp_space_after_semicolon_in_for_statement = true
138 | csharp_space_around_binary_operators = before_and_after
139 | csharp_space_around_declaration_statements = do_not_ignore
140 | csharp_space_before_colon_in_inheritance_clause = true
141 | csharp_space_before_comma = false
142 | csharp_space_before_dot = false
143 | csharp_space_before_open_square_brackets = false
144 | csharp_space_before_semicolon_in_for_statement = false
145 | csharp_space_between_empty_square_brackets = false
146 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
147 | csharp_space_between_method_call_name_and_opening_parenthesis = false
148 | csharp_space_between_method_call_parameter_list_parentheses = false
149 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
150 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
151 | csharp_space_between_method_declaration_parameter_list_parentheses = false
152 | csharp_space_between_parentheses = false
153 | csharp_space_between_square_brackets = false
154 |
155 | # License header
156 | file_header_template = This code is licensed under MIT license (see LICENSE for details)
157 |
158 | # C++ Files
159 | [*.{cpp,h,in}]
160 | curly_bracket_next_line = true
161 | indent_brace_style = Allman
162 |
163 | # Xml project files
164 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
165 | indent_size = 2
166 |
167 | [*.{csproj,vbproj,proj,nativeproj,locproj}]
168 | charset = utf-8
169 |
170 | # Xml build files
171 | [*.builds]
172 | indent_size = 2
173 |
174 | # Xml files
175 | [*.{xml,stylecop,resx,ruleset}]
176 | indent_size = 2
177 |
178 | # Xml config files
179 | [*.{props,targets,config,nuspec}]
180 | indent_size = 2
181 |
182 | # YAML config files
183 | [*.{yml,yaml}]
184 | indent_size = 2
185 |
186 | # Shell scripts
187 | [*.sh]
188 | end_of_line = lf
189 |
190 | [*.{cmd,bat}]
191 | end_of_line = crlf
192 |
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # reformat project
2 | 08a5740697f28a26a152e6972838d6b793ab25a7
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for more information:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 | # https://containers.dev/guide/dependabot
6 |
7 | version: 2
8 | updates:
9 | - package-ecosystem: "devcontainers"
10 | directory: "/"
11 | schedule:
12 | interval: weekly
13 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: .NET Tests
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "**" ]
8 |
9 | jobs:
10 | packages:
11 | uses: ./.github/workflows/package.yml
12 | patcher-tests:
13 | name: Run Cosmos.Patcher.Tests
14 | runs-on: ${{ matrix.os }}
15 | strategy:
16 | matrix:
17 | os: [ ubuntu-latest ]
18 | dotnet-version: [ 9.0.x ]
19 | steps:
20 | - name: 📥 Checkout Code
21 | uses: actions/checkout@v4
22 |
23 | - name: 🛠️ Setup .NET
24 | uses: actions/setup-dotnet@v4
25 | with:
26 | dotnet-version: ${{ matrix.dotnet-version }}
27 |
28 | - name: 🔄 Restore Dependencies
29 | run: dotnet restore ./tests/Cosmos.Patcher.Tests/Cosmos.Patcher.Tests.csproj
30 |
31 | - name: 🔨 Build Cosmos.Patcher
32 | run: dotnet build ./src/Cosmos.Patcher/Cosmos.Patcher.csproj
33 |
34 | - name: 🔨 Build Cosmos.Patcher.Build
35 | run: dotnet build ./src/Cosmos.Patcher.Build/Cosmos.Patcher.Build.csproj
36 |
37 | - name: 🔨 Build Cosmos.Patcher.Tests
38 | run: dotnet build ./tests/Cosmos.Patcher.Tests/Cosmos.Patcher.Tests.csproj --configuration Debug --no-restore
39 |
40 | - name: 🚀 Run Tests
41 | run: dotnet test ./tests/Cosmos.Patcher.Tests/Cosmos.Patcher.Tests.csproj --no-build --configuration Debug --logger "trx;LogFileName=Cosmos.Patcher.Tests.trx"
42 |
43 | - name: 📤 Upload Test Results
44 | uses: actions/upload-artifact@v4
45 | with:
46 | name: Cosmos.Patcher.Tests-Results
47 | path: ./tests/Cosmos.Patcher.Tests/TestResults/Cosmos.Patcher.Tests.trx
48 |
49 | scanner-tests:
50 | name: Run Cosmos.Scanner.Tests
51 | runs-on: ${{ matrix.os }}
52 | strategy:
53 | matrix:
54 | os: [ ubuntu-latest ]
55 | dotnet-version: [ 9.0.x ]
56 | steps:
57 | - name: 📥 Checkout Code
58 | uses: actions/checkout@v4
59 |
60 | - name: 🛠️ Setup .NET
61 | uses: actions/setup-dotnet@v4
62 | with:
63 | dotnet-version: ${{ matrix.dotnet-version }}
64 |
65 | - name: 🔄 Restore Dependencies
66 | run: dotnet restore ./tests/Cosmos.Scanner.Tests/Cosmos.Scanner.Tests.csproj
67 |
68 | - name: 🔨 Build Cosmos.Patcher
69 | run: dotnet build ./src/Cosmos.Patcher/Cosmos.Patcher.csproj
70 |
71 | - name: 🔨 Build Cosmos.Patcher.Build
72 | run: dotnet build ./src/Cosmos.Patcher.Build/Cosmos.Patcher.Build.csproj
73 |
74 | - name: 🔨 Build Cosmos.Scanner.Tests
75 | run: dotnet build ./tests/Cosmos.Scanner.Tests/Cosmos.Scanner.Tests.csproj --configuration Debug --no-restore
76 |
77 | - name: 🚀 Run Tests
78 | run: dotnet test ./tests/Cosmos.Scanner.Tests/Cosmos.Scanner.Tests.csproj --no-build --configuration Debug --logger "trx;LogFileName=Cosmos.Scanner.Tests.trx"
79 |
80 | - name: 📤 Upload Test Results
81 | uses: actions/upload-artifact@v4
82 | with:
83 | name: Cosmos.Scanner.Tests-Results
84 | path: ./tests/Cosmos.Scanner.Tests/TestResults/Cosmos.Scanner.Tests.trx
85 |
86 | analyzer-tests:
87 | name: Run Cosmos.Patcher.Analyzer.Tests
88 | runs-on: ${{ matrix.os }}
89 | strategy:
90 | matrix:
91 | os: [ ubuntu-latest ]
92 | dotnet-version: [ 9.0.x ]
93 | steps:
94 | - name: 📥 Checkout Code
95 | uses: actions/checkout@v4
96 |
97 | - name: 🛠️ Setup .NET
98 | uses: actions/setup-dotnet@v4
99 | with:
100 | dotnet-version: ${{ matrix.dotnet-version }}
101 |
102 | - name: 🔄 Restore Dependencies
103 | run: dotnet restore ./tests/Cosmos.Patcher.Analyzer.Tests/Cosmos.Patcher.Analyzer.Tests.csproj
104 |
105 | - name: 🔨 Build Cosmos.Patcher.Analyzer.Tests
106 | run: dotnet build ./tests/Cosmos.Patcher.Analyzer.Tests/Cosmos.Patcher.Analyzer.Tests.csproj --configuration Debug --no-restore
107 |
108 | - name: 🚀 Run Tests
109 | run: dotnet test ./tests/Cosmos.Patcher.Analyzer.Tests/Cosmos.Patcher.Analyzer.Tests.csproj --no-build --configuration Debug --logger "trx;LogFileName=Cosmos.Patcher.Analyzer.Tests.trx"
110 |
111 | - name: 📤 Upload Test Results
112 | uses: actions/upload-artifact@v4
113 | with:
114 | name: Cosmos.Patcher.Analyzer.Tests-Results
115 | path: ./tests/Cosmos.Patcher.Analyzer.Tests/TestResults/Cosmos.Patcher.Analyzer.Tests.trx
116 |
117 | asm-tests:
118 | name: Run Cosmos.Asm.Build.Test
119 | runs-on: ${{ matrix.os }}
120 | strategy:
121 | matrix:
122 | os: [ ubuntu-latest ]
123 | dotnet-version: [ 9.0.x ]
124 | steps:
125 | - name: 📥 Checkout Code
126 | uses: actions/checkout@v4
127 |
128 | - name: 🛠️ Setup .NET
129 | uses: actions/setup-dotnet@v4
130 | with:
131 | dotnet-version: ${{ matrix.dotnet-version }}
132 |
133 | - name: 🔗 Install Linking Tools
134 | run: sudo apt-get update && sudo apt-get install -y yasm
135 |
136 | - name: 🔄 Restore Dependencies
137 | run: dotnet restore ./tests/Cosmos.Asm.Build.Test/Cosmos.Asm.Build.Test.csproj
138 |
139 | - name: 🔨 Build Cosmos.Patcher
140 | run: dotnet build ./src/Cosmos.Patcher/Cosmos.Patcher.csproj
141 |
142 | - name: 🔨 Build Cosmos.Patcher.Build
143 | run: dotnet build ./src/Cosmos.Patcher.Build/Cosmos.Patcher.Build.csproj
144 |
145 | - name: 🔨 Build Cosmos.Asm.Build.Test
146 | run: dotnet build ./tests/Cosmos.Asm.Build.Test/Cosmos.Asm.Build.Test.csproj --configuration Debug --no-restore
147 |
148 | - name: 🚀 Run Tests
149 | run: dotnet test ./tests/Cosmos.Asm.Build.Test/Cosmos.Asm.Build.Test.csproj --no-build --configuration Debug --logger "trx;LogFileName=Cosmos.Asm.Build.Test.trx"
150 |
151 | - name: 📤 Upload Test Results
152 | uses: actions/upload-artifact@v4
153 | with:
154 | name: Cosmos.Asm.Build.Test-Results
155 | path: ./tests/Cosmos.Asm.Build.Test/TestResults/Cosmos.Asm.Build.Test.trx
156 |
157 | unix-iso-tests:
158 | name: Run ISO Build Tests - Unix
159 | needs: [packages]
160 | runs-on: ${{ matrix.os }}
161 | strategy:
162 | matrix:
163 | os: [ ubuntu-latest ]
164 | dotnet-version: [ 9.0.x ]
165 | steps:
166 | - name: 📥 Checkout Code
167 | uses: actions/checkout@v4
168 |
169 | - name: 🛠️ Setup .NET
170 | uses: actions/setup-dotnet@v4
171 | with:
172 | dotnet-version: ${{ matrix.dotnet-version }}
173 |
174 | - name: 🔄 Download Packages
175 | uses: actions/download-artifact@v4
176 | with:
177 | name: Cosmos.Patcher.Packages
178 | path: packages
179 |
180 | - name: 📂 List Directory Contents
181 | run: ls -R $GITHUB_WORKSPACE/packages
182 |
183 | - name: 🛠️ Setup Nuget Local
184 | run: dotnet nuget add source $GITHUB_WORKSPACE/packages
185 |
186 | - name: 🔗 Install Linking Tools
187 | run: sudo apt-get update && sudo apt-get install -y xorriso lld yasm
188 |
189 | - name: 🔧 Install ilc
190 | run: dotnet tool install -g ilc
191 |
192 | - name: 🔧 Install Patcher
193 | run: dotnet tool install -g Cosmos.Patcher
194 |
195 | - name: 🔄 Restore Dependencies
196 | run: dotnet restore
197 |
198 | - name: 🚀 Run Tests
199 | run: dotnet publish -c Debug -r linux-x64 --verbosity detailed ./examples/KernelExample/Kernel.csproj -o ./output
200 |
201 | - name: 🕵️♂️ Verify Output
202 | run: |
203 | [ -f ./output/Kernel.iso ] && echo "ISO exists" || (echo "ISO missing" && exit 1)
204 |
--------------------------------------------------------------------------------
/.github/workflows/format.yml:
--------------------------------------------------------------------------------
1 | name: .NET Format
2 |
3 | on:
4 | pull_request:
5 | branches: [ "main" ]
6 |
7 | jobs:
8 | format:
9 | name: Check Code Formatting
10 | runs-on: ubuntu-latest
11 | strategy:
12 | matrix:
13 | dotnet-version: [ 9.0.x ]
14 |
15 | steps:
16 | - name: Checkout Code
17 | uses: actions/checkout@v4
18 |
19 | - name: Setup .NET
20 | uses: actions/setup-dotnet@v4
21 | with:
22 | dotnet-version: ${{ matrix.dotnet-version }}
23 |
24 | - name: Restore Dependencies
25 | run: dotnet restore
26 |
27 | - name: Check Formatting
28 | run: dotnet format --verify-no-changes --severity error || { echo "::error file=Code Formatting::Some files need formatting. Run 'dotnet format' locally."; exit 1; }
29 |
30 |
--------------------------------------------------------------------------------
/.github/workflows/package.yml:
--------------------------------------------------------------------------------
1 | name: Package
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_call:
8 | jobs:
9 | pack-patcher:
10 | name: Pack Cosmos Patcher
11 | runs-on: ${{ matrix.os }}
12 | strategy:
13 | matrix:
14 | os: [ windows-latest ]
15 | dotnet-version: [ 9.0.x ]
16 |
17 | steps:
18 | - name: Checkout Code
19 | uses: actions/checkout@v4
20 |
21 | - name: Setup .NET
22 | uses: actions/setup-dotnet@v4
23 | with:
24 | dotnet-version: ${{ matrix.dotnet-version }}
25 |
26 | - name: Pack Cosmos.Api
27 | run: dotnet build ./src/Cosmos.API/Cosmos.API.csproj --configuration Release --artifacts-path ./out
28 |
29 | - name: Pack Cosmos.Patcher.Build
30 | run: dotnet build ./src/Cosmos.Patcher.Build/Cosmos.Patcher.Build.csproj --configuration Release --artifacts-path ./out
31 |
32 | - name: Pack Cosmos.Patcher
33 | run: dotnet build ./src/Cosmos.Patcher/Cosmos.Patcher.csproj --configuration Release --artifacts-path ./out
34 |
35 | - name: Pack Cosmos.Common.Build
36 | run: dotnet build ./src/Cosmos.Common.Build/Cosmos.Common.Build.csproj --configuration Release --artifacts-path ./out
37 |
38 | - name: Pack Cosmos.Ilc.Build
39 | run: dotnet build ./src/Cosmos.Ilc.Build/Cosmos.Ilc.Build.csproj --configuration Release --artifacts-path ./out
40 |
41 | - name: Pack Cosmos.Asm.Build
42 | run: dotnet build ./src/Cosmos.Asm.Build/Cosmos.Asm.Build.csproj --configuration Release --artifacts-path ./out
43 |
44 | - name: Pack Cosmos.Patcher.Analyzer.Package
45 | run: dotnet build ./src/Cosmos.Patcher.Analyzer.Package/Cosmos.Patcher.Analyzer.Package.csproj --configuration Release --artifacts-path ./out
46 |
47 | - name: Upload Packages
48 | uses: actions/upload-artifact@v4
49 | with:
50 | name: Cosmos.Patcher.Packages
51 | path: ./out/package/release
52 |
53 |
54 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.o
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.tlog
95 | *.vspscc
96 | *.vssscc
97 | .builds
98 | *.pidb
99 | *.svclog
100 | *.scc
101 | *.d
102 |
103 | # Chutzpah Test files
104 | _Chutzpah*
105 |
106 | # Visual C++ cache files
107 | ipch/
108 | *.aps
109 | *.ncb
110 | *.opendb
111 | *.opensdf
112 | *.sdf
113 | *.cachefile
114 | *.VC.db
115 | *.VC.VC.opendb
116 |
117 | # Visual Studio profiler
118 | *.psess
119 | *.vsp
120 | *.vspx
121 | *.sap
122 |
123 | # Visual Studio Trace Files
124 | *.e2e
125 |
126 | # TFS 2012 Local Workspace
127 | $tf/
128 |
129 | # Guidance Automation Toolkit
130 | *.gpState
131 |
132 | # ReSharper is a .NET coding add-in
133 | _ReSharper*/
134 | *.[Rr]e[Ss]harper
135 | *.DotSettings.user
136 |
137 | # TeamCity is a build add-in
138 | _TeamCity*
139 |
140 | # DotCover is a Code Coverage Tool
141 | *.dotCover
142 |
143 | # AxoCover is a Code Coverage Tool
144 | .axoCover/*
145 | !.axoCover/settings.json
146 |
147 | # Coverlet is a free, cross platform Code Coverage Tool
148 | coverage*.json
149 | coverage*.xml
150 | coverage*.info
151 |
152 | # Visual Studio code coverage results
153 | *.coverage
154 | *.coveragexml
155 |
156 | # NCrunch
157 | _NCrunch_*
158 | .*crunch*.local.xml
159 | nCrunchTemp_*
160 |
161 | # MightyMoose
162 | *.mm.*
163 | AutoTest.Net/
164 |
165 | # Web workbench (sass)
166 | .sass-cache/
167 |
168 | # Installshield output folder
169 | [Ee]xpress/
170 |
171 | # DocProject is a documentation generator add-in
172 | DocProject/buildhelp/
173 | DocProject/Help/*.HxT
174 | DocProject/Help/*.HxC
175 | DocProject/Help/*.hhc
176 | DocProject/Help/*.hhk
177 | DocProject/Help/*.hhp
178 | DocProject/Help/Html2
179 | DocProject/Help/html
180 |
181 | # Click-Once directory
182 | publish/
183 |
184 | # Publish Web Output
185 | *.[Pp]ublish.xml
186 | *.azurePubxml
187 | # Note: Comment the next line if you want to checkin your web deploy settings,
188 | # but database connection strings (with potential passwords) will be unencrypted
189 | *.pubxml
190 | *.publishproj
191 |
192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
193 | # checkin your Azure Web App publish settings, but sensitive information contained
194 | # in these scripts will be unencrypted
195 | PublishScripts/
196 |
197 | # NuGet Packages
198 | *.nupkg
199 | # NuGet Symbol Packages
200 | *.snupkg
201 | # The packages folder can be ignored because of Package Restore
202 | **/[Pp]ackages/*
203 | # except build/, which is used as an MSBuild target.
204 | !**/[Pp]ackages/build/
205 | # Uncomment if necessary however generally it will be regenerated when needed
206 | #!**/[Pp]ackages/repositories.config
207 | # NuGet v3's project.json files produces more ignorable files
208 | *.nuget.props
209 | *.nuget.targets
210 |
211 | # Microsoft Azure Build Output
212 | csx/
213 | *.build.csdef
214 |
215 | # Microsoft Azure Emulator
216 | ecf/
217 | rcf/
218 |
219 | # Windows Store app package directories and files
220 | AppPackages/
221 | BundleArtifacts/
222 | Package.StoreAssociation.xml
223 | _pkginfo.txt
224 | *.appx
225 | *.appxbundle
226 | *.appxupload
227 |
228 | # Visual Studio cache files
229 | # files ending in .cache can be ignored
230 | *.[Cc]ache
231 | # but keep track of directories ending in .cache
232 | !?*.[Cc]ache/
233 |
234 | # Others
235 | ClientBin/
236 | ~$*
237 | *~
238 | *.dbmdl
239 | *.dbproj.schemaview
240 | *.jfm
241 | *.pfx
242 | *.publishsettings
243 | orleans.codegen.cs
244 |
245 | # Including strong name files can present a security risk
246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
247 | #*.snk
248 |
249 | # Since there are multiple workflows, uncomment next line to ignore bower_components
250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
251 | #bower_components/
252 |
253 | # RIA/Silverlight projects
254 | Generated_Code/
255 |
256 | # Backup & report files from converting an old project file
257 | # to a newer Visual Studio version. Backup files are not needed,
258 | # because we have git ;-)
259 | _UpgradeReport_Files/
260 | Backup*/
261 | UpgradeLog*.XML
262 | UpgradeLog*.htm
263 | ServiceFabricBackup/
264 | *.rptproj.bak
265 |
266 | # SQL Server files
267 | *.mdf
268 | *.ldf
269 | *.ndf
270 |
271 | # Business Intelligence projects
272 | *.rdl.data
273 | *.bim.layout
274 | *.bim_*.settings
275 | *.rptproj.rsuser
276 | *- [Bb]ackup.rdl
277 | *- [Bb]ackup ([0-9]).rdl
278 | *- [Bb]ackup ([0-9][0-9]).rdl
279 |
280 | # Microsoft Fakes
281 | FakesAssemblies/
282 |
283 | # GhostDoc plugin setting file
284 | *.GhostDoc.xml
285 |
286 | # Node.js Tools for Visual Studio
287 | .ntvs_analysis.dat
288 | node_modules/
289 |
290 | # Visual Studio 6 build log
291 | *.plg
292 |
293 | # Visual Studio 6 workspace options file
294 | *.opt
295 |
296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
297 | *.vbw
298 |
299 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
300 | *.vbp
301 |
302 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
303 | *.dsw
304 | *.dsp
305 |
306 | # Visual Studio 6 technical files
307 | *.ncb
308 | *.aps
309 |
310 | # Visual Studio LightSwitch build output
311 | **/*.HTMLClient/GeneratedArtifacts
312 | **/*.DesktopClient/GeneratedArtifacts
313 | **/*.DesktopClient/ModelManifest.xml
314 | **/*.Server/GeneratedArtifacts
315 | **/*.Server/ModelManifest.xml
316 | _Pvt_Extensions
317 |
318 | # Paket dependency manager
319 | .paket/paket.exe
320 | paket-files/
321 |
322 | # FAKE - F# Make
323 | .fake/
324 |
325 | # CodeRush personal settings
326 | .cr/personal
327 |
328 | # Python Tools for Visual Studio (PTVS)
329 | __pycache__/
330 | *.pyc
331 |
332 | # Cake - Uncomment if you are using it
333 | # tools/**
334 | # !tools/packages.config
335 |
336 | # Tabs Studio
337 | *.tss
338 |
339 | # Telerik's JustMock configuration file
340 | *.jmconfig
341 |
342 | # BizTalk build output
343 | *.btp.cs
344 | *.btm.cs
345 | *.odx.cs
346 | *.xsd.cs
347 |
348 | # OpenCover UI analysis results
349 | OpenCover/
350 |
351 | # Azure Stream Analytics local run output
352 | ASALocalRun/
353 |
354 | # MSBuild Binary and Structured Log
355 | *.binlog
356 |
357 | # NVidia Nsight GPU debugger configuration file
358 | *.nvuser
359 |
360 | # MFractors (Xamarin productivity tool) working folder
361 | .mfractor/
362 |
363 | # Local History for Visual Studio
364 | .localhistory/
365 |
366 | # Visual Studio History (VSHistory) files
367 | .vshistory/
368 |
369 | # BeatPulse healthcheck temp database
370 | healthchecksdb
371 |
372 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
373 | MigrationBackup/
374 |
375 | # Ionide (cross platform F# VS Code tools) working folder
376 | .ionide/
377 |
378 | # Fody - auto-generated XML schema
379 | FodyWeavers.xsd
380 |
381 | # VS Code files for those working on multiple tools
382 | .vscode/*
383 | !.vscode/settings.json
384 | !.vscode/tasks.json
385 | !.vscode/launch.json
386 | !.vscode/extensions.json
387 | *.code-workspace
388 |
389 | # Local History for Visual Studio Code
390 | .history/
391 |
392 | # Windows Installer files from build outputs
393 | *.cab
394 | *.msi
395 | *.msix
396 | *.msm
397 | *.msp
398 |
399 | # JetBrains Rider
400 | *.sln.iml
401 |
402 | out/
403 |
404 | *.iso
405 | *.efi
406 | main
407 |
408 | /kernel-deps
409 | /ovmf
410 | *.iso
411 | *.hdd
412 | output
413 |
--------------------------------------------------------------------------------
/.idea/.idea.nativeaot-patcher/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | workspace.xml
2 |
--------------------------------------------------------------------------------
/.idea/.idea.nativeaot-patcher/.idea/AndroidProjectSystem.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.idea.nativeaot-patcher/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.nativeaot-patcher/.idea/projectSettingsUpdater.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.nativeaot-patcher/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0
4 | enable
5 | latest
6 | enable
7 | false
8 | false
9 | true
10 | 3.0.0
11 | true
12 | $(NoWarn);1591;CS1998;NU1903;
13 | true
14 | true
15 | true
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | runtime; build; native; contentfiles; analyzers; buildtransitive
35 | all
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
2 |
3 | WORKDIR /app
4 |
5 | COPY . ./
6 |
7 | RUN dotnet restore ./Cosmos.Patcher.Tests/Cosmos.Patcher.Tests.csproj
8 |
9 | RUN dotnet build ./Cosmos.Patcher.Tests/Cosmos.Patcher.Tests.csproj --configuration Debug --no-restore
10 |
11 | CMD ["dotnet", "test", "./Cosmos.Patcher.Tests/Cosmos.Patcher.Tests.csproj", "--no-build", "--configuration", "Debug", "--logger", "trx;LogFileName=Cosmos.Patcher.Tests.trx"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Kaleb McGhie aka zarlo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nativeaot-patcher
2 | [](https://github.com/valentinbreiz/nativeaot-patcher/actions/workflows/dotnet.yml)
3 | [](https://github.com/valentinbreiz/nativeaot-patcher/actions/workflows/package.yml)
4 |
5 | Zarlo's NativeAOT patcher. Main goal is to port Cosmos plug system, assembly loading for NativeAOT. See https://github.com/CosmosOS/Cosmos/issues/3088 for details.
6 |
7 | ## Documentation
8 | - [Wiki](https://github.com/valentinbreiz/nativeaot-patcher/wiki/)
9 |
10 | ## Priority
11 | - [Priority Board](https://github.com/users/valentinbreiz/projects/2/views/2)
12 |
13 | ## Credit:
14 | - [@zarlo](https://github.com/zarlo)
15 | - [@kumja1](https://github.com/kumja1)
16 | - [@Guillermo-Santos](https://github.com/Guillermo-Santos)
17 | - [@valentinbreiz](https://github.com/valentinbreiz)
18 | - [@ascpixi](https://github.com/ascpixi)
19 | - [@ilobilo](https://github.com/ilobilo)
20 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | ###############
2 | # folder #
3 | ###############
4 | /**/DROP/
5 | /**/TEMP/
6 | /**/packages/
7 | /**/bin/
8 | /**/obj/
9 | _site
10 |
--------------------------------------------------------------------------------
/docs/api/.gitignore:
--------------------------------------------------------------------------------
1 | ###############
2 | # temp file #
3 | ###############
4 | *.yml
5 | .manifest
6 |
--------------------------------------------------------------------------------
/docs/api/index.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/valentinbreiz/nativeaot-patcher/f2997cb2146da322481bc0437a54a3634af92924/docs/api/index.md
--------------------------------------------------------------------------------
/docs/articles/build/asm-build.md:
--------------------------------------------------------------------------------
1 | The `Cosmos.Asm.Build` component is part of the Cosmos OS SDK. Its purpose is to compile assembly files into object files, which can then be consumed by other `Cosmos.*.Build` components or custom user targets.
2 |
3 | ---
4 |
5 | ## Input Parameters
6 | | Name | Description | Default Value |
7 | |------------|---------------------------------------------------------------------|---------------------------------------------------|
8 | | `YasmPath` | Full file path to Yasm, the tool used to compile the object files. | Unix: `/usr/bin/yasm`, Windows: To be decided. |
9 | | `AsmSearch`| One or more directory paths that contain assembly files. | None |
10 | | `AsmFiles` | One or more paths to specific assembly files. | None |
11 |
12 | ---
13 |
14 | ## Output
15 | All compiled object files are placed in the intermediate output directory of the project, specifically at `$(IntermediateOutput)\cosmos\asm`.
16 |
17 | ---
18 |
19 | ## Tasks
20 | | Name | Description | Depends On |
21 | |-------------------------|---------------------------------------------------------------------------------------------------|------------------------|
22 | | `FindSourceFilesForYasm`| Filters all `AsmSearch` and `AsmFiles` paths specified by the user. | `Build` |
23 | | `BuildYasm` | Compiles all specified assembly files and places them in the `Cosmos.Asm.Build` output directory. | `FindSourceFilesForYasm` |
24 | | `CleanYasm` | Removes all files generated by `Cosmos.Asm.Build`. | `Clean` |
25 |
26 | ---
27 |
28 | ## Understanding the `AsmSearch` Parameter
29 | The `AsmSearch` parameter defines a collection of directories to scan for `.asm` files. TThe scan is restricted to the root of each specified directory, with no recursive scanning of subdirectories.
30 |
31 | ### Operations
32 | The following operations are performed on the `AsmSearch` parameter:
33 |
34 | 1. **Remove Non-Existent Directories**
35 | Any directory or assembly file that does not exist is removed from the list. A warning with the code `TODO: Pick an error code` is generated for each missing entry.
36 |
37 | 2. **Scan for Architecture-Specific Subfolders**
38 | If a subdirectory matches the current target architecture, it replaces its parent directory as an entry for scanning.
39 |
40 | 3. **Search for Assembly Files**
41 | The remaining entries in the `AsmSearch` parameter are scanned for `.asm` files. These files are then added to the `AsmFiles` parameter as input for the `Yasm` task.
42 |
--------------------------------------------------------------------------------
/docs/articles/build/ilc-build.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | **Cosmos.ilc.Build** is the build system for CosmosOS-projects. It is responsible for compiling the patched dll outputted by **Cosmos.Patcher** into native code which is then linked with native libraries on the target os.
4 |
5 | ---
6 |
7 | ## Workflow
8 |
9 | ### Step 0: Run Patcher
10 | The **Cosmos.ilc.Build**:
11 | - Calls **Cosmos.Patcher** to patch the project assembly.
12 |
13 | ### Step 1: Compile with ILC
14 | The **Cosmos.ilc.Build**:
15 | - Compiles the patched dll to a `.obj` file.
16 |
17 | ### Step 2: Link with native libraries
18 | The **Cosmos.ilc.Build**:
19 | - Links the generated `.obj` file with native libraries
20 | - Outputs a native binary file ready for further processing
21 | ---
22 |
--------------------------------------------------------------------------------
/docs/articles/build/kernel-compilation-steps.md:
--------------------------------------------------------------------------------
1 | This document outlines the build process for the C# kernel, which utilizes .NET NativeAOT compilation combined with a custom patching mechanism ([Plugs](/articles/Plugs.html)) to produce a bootable ELF kernel file and a final bootable ISO image.
2 |
3 | ## Overview
4 |
5 | The compilation process transforms C# source code into a native executable kernel (`Kernel.elf`) and packages it with the Limine bootloader into a bootable ISO image (`.iso`). It involves several stages orchestrated by MSBuild, leveraging custom build components like `Cosmos.Asm.Build`, `Cosmos.Patcher.Analyzer`, `Cosmos.Ilc.Build`, and `Cosmos.Common.Build`. These components provide MSBuild tasks and targets to manage assembly, C# compilation, static analysis, IL patching, NativeAOT compilation, linking, and ISO image creation.
6 |
7 | 
8 | > Visual by [Guillermo-Santos](https://github.com/Guillermo-Santos)
9 |
10 | ## Prerequisites
11 |
12 | Ensure the following tools and SDKs are installed:
13 |
14 | * **.NET SDK**: Version 9.0.104 or later (as specified in `global.json`), including the NativeAOT compilation tools.
15 | * **YASM Assembler**: Required by `Cosmos.Asm.Build` for compiling `.asm` files.
16 | * **LLD Linker (`ld.lld`)**: Part of the LLVM toolchain, required by `Cosmos.Common.Build` for linking object files into the final ELF executable.
17 | * **xorriso**: Required by `Cosmos.Common.Build` for creating the final bootable ISO image.
18 |
19 | ## Compilation Stages
20 |
21 | The build process, orchestrated by MSBuild using tasks and targets from the various `Cosmos.*.Build` components follows these main steps:
22 |
23 | 1. **Assembly Compilation (`.asm` -> `.obj`) - via [Cosmos.Asm.Build](/articles/Cosmos.Asm.Build.html)**:
24 | * Handwritten assembly files (`.asm`) containing low-level x86 and ARM routines (e.g., implementations for functions marked `[RuntimeImport]` like `_native_io_*`) are identified.
25 | * The MSBuild task provided by `Cosmos.Asm.Build`, `YasmBuildTask` is executed.
26 | * `yasm` is invoked (with `-felf64`) for each `.asm` file.
27 | * Resulting object files (*.obj`) are generated.
28 |
29 | 2. **C# Compilation & Static Analysis (`.cs` -> IL `.dll`) - via .NET SDK + `Cosmos.Patcher.Analyzer`**:
30 | * MSBuild invokes the Roslyn C# compiler.
31 | * Kernel source files (`KernelEntry.cs`, `Internal.cs`, `Stdlib.cs`, etc.) are compiled into a standard .NET IL assembly (`Kernel.dll`).
32 | * During compilation, the **`PatcherAnalyzer`** (from `Cosmos.Patcher.Analyzer`) runs, checking code against plug rules (`DiagnosticMessages.cs`) and reporting diagnostics.
33 | * The `PatcherCodeFixProvider` offers IDE assistance.
34 |
35 | 3. **IL Patching (Mono.Cecil) - via [Cosmos.Patcher.Build](/articles/Liquip.Patcher.html)**:
36 | * MSBuild targets execute the `PatcherTask` (from `Cosmos.Patcher.Build.Tasks`).
37 | * `PatcherTask` runs the `Cosmos.Patcher` tool.
38 | * The tool uses **Mono.Cecil** to load the target IL assembly and plug assemblies.
39 | * It **modifies the IL** of target methods based on `[Plug]` attributes, replacing their bodies with the plug code.
40 | * The modified (patched) IL assembly (`Kernel_patched.dll`) is saved.
41 |
42 | 4. **NativeAOT Compilation (ILC: Patched IL -> Native `.obj`) - via [Cosmos.Ilc.Build](/articles/Liquip.ilc.Build.html)**:
43 | * MSBuild targets (likely from **`Cosmos.Ilc.Build`**) invoke the .NET ILCompiler (ILC).
44 | * ILC processes the **patched IL assembly** (`Kernel_patched.dll`).
45 | * It performs tree-shaking and compiles the reachable IL into native object files (`.obj` or `.o`) for the target architecture.
46 |
47 | 5. **Linking (`.obj` -> `.elf`) - via `Cosmos.Common.Build`**:
48 | * MSBuild targets provided by **`Cosmos.Common.Build`** invoke the LLVM linker (`ld.lld`).
49 | * It takes all native object files from ILC (C#) and YASM (`.asm`).
50 | * It links them using a **linker script** (essential for memory layout, entry point `kmain`, etc.).
51 | * It resolves external symbols (like `_native_io_*`).
52 | * The final kernel executable is produced: `Kernel.elf`.
53 |
54 | 6. **ISO Image Creation (`.iso`) - via `Cosmos.Common.Build`**:
55 | * MSBuild targets provided by **`Cosmos.Common.Build`** execute this step.
56 | * `xorriso` assembles the necessary components into a bootable ISO 9660 image:
57 | * Limine bootloader files.
58 | * The bootloader configuration file (`limine.conf`).
59 | * The compiled kernel (`Kernel.elf` from step 5).
60 | * The final output is a bootable `.iso` file (e.g., `Kernel.iso`).
61 |
62 | 7. **Deployment / Execution**:
63 | * The generated `.iso` file can be used with a virtual machine (like QEMU, VMware, VirtualBox) or written to a USB drive to boot on physical hardware.
64 |
65 | ## Build Automation
66 |
67 | These steps are automated via MSBuild targets and custom tasks defined within the project's `.csproj` file and shared build components (`Cosmos.Asm.Build`, `Cosmos.Patcher.Analyzer`, `Cosmos.Ilc.Build`, `Cosmos.Common.Build`). This allows the entire kernel and bootable ISO to be built using standard `dotnet build` or `dotnet publish` commands with appropriate configurations. The goal is to make this process as nuget packages which can be integrated without needing to import .targets files from the `.csproj`.
68 |
--------------------------------------------------------------------------------
/docs/articles/build/patcher-build.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | **Cosmos.ilc.Build** is the build system for CosmosOS-projects. It is responsible for compiling the patched dll outputted by **Cosmos.Patcher** into native code which is then linked with native libraries on the target os.
4 |
5 | ---
6 |
7 | ## Workflow
8 |
9 | ### Step 0: Run Patcher
10 | The **Cosmos.ilc.Build**:
11 | - Calls **Cosmos.Patcher** to patch the project assembly.
12 |
13 | ### Step 1: Compile with ILC
14 | The **Cosmos.ilc.Build**:
15 | - Compiles the patched dll to a `.obj` file.
16 |
17 | ### Step 2: Link with native libraries
18 | The **Cosmos.ilc.Build**:
19 | - Links the generated `.obj` file with native libraries
20 | - Outputs a native binary file ready for further processing
21 | ---
22 |
--------------------------------------------------------------------------------
/docs/articles/plugs.md:
--------------------------------------------------------------------------------
1 | A **plug** is a mechanism that replaces existing methods, variables, or types with custom implementations to enable platform-specific functionality ([what is a plug?](https://cosmosos.github.io/articles/Kernel/Plugs.html)).
2 |
3 | ## Cosmos gen3 plug template
4 |
5 | _Feel free to propose changes_
6 |
7 | ```csharp
8 | using System.IO;
9 |
10 | namespace Cosmos.Plugs;
11 |
12 | [Plug(typeof(System.IO.FileStream))]
13 | public class FileStream
14 | {
15 | /* Plug static fields from System.IO.FileStream */
16 | [PlugMember]
17 | public static ulong StaticField
18 | {
19 | get; set;
20 | }
21 |
22 | /* Plug instance fields from System.IO.FileStream */
23 | [PlugMember]
24 | public static ulong classInstanceField
25 | {
26 | get; set;
27 | }
28 |
29 | /* Add private static fields to plugged class */
30 | [Expose]
31 | private static ulong _privateStaticField;
32 |
33 | /* Add private fields to plugged class */
34 | [Expose]
35 | private static ulong _privateInstanceField;
36 |
37 | /* Add private static methods to plugged class */
38 | [Expose]
39 | private static void PrivateStaticMethod()
40 | {
41 | }
42 |
43 | /* Add private objects methods to plugged class */
44 | [Expose]
45 | private static void PrivateInstanceMethod(FileStream aThis)
46 | {
47 | aThis.WriteByte('a');
48 | }
49 |
50 | /* Plug non static method with aThis to access fields */
51 | [PlugMember]
52 | public static long Seek(FileStream aThis, long offset, SeekOrigin origin)
53 | {
54 | PrivateMethod();
55 | _privateInstanceField = 0;
56 | aThis.WriteByte('a');
57 | classStaticField = 0;
58 | aThis.classInstanceField = 0; //or classInstanceField = 0;
59 | // ...
60 | }
61 |
62 | /* Plug static method */
63 | [PlugMember]
64 | public static long StaticMethod()
65 | {
66 |
67 | }
68 |
69 | /* Access private fields from plugged class */
70 | [PlugMember]
71 | public static long AccessFieldsMethod(FileStream aThis, [FieldAccess(Name = "InstanceField2")] ulong InstanceField2)
72 | {
73 | InstanceField2 = 0;
74 | }
75 |
76 | /* Assembly plug for System.IO.FileStream.WriteByte */
77 | [PlugMember, RuntimeImport("asm_writebyte")]
78 | public static void WriteByte(FileStream aThis, byte b);
79 |
80 | }
81 | ```
82 |
--------------------------------------------------------------------------------
/docs/articles/testing.md:
--------------------------------------------------------------------------------
1 | ## Available Unit Tests
2 | - **Liquip.Patcher.Analyzer.Test**:
3 | Validates that code does not contain plug architecture errors.
4 | - Test_TypeNotFoundDiagnostic
5 | - Test_PlugNotStaticDiagnostic
6 | - Test_MethodNeedsPlugDiagnostic
7 | - Test_AnalyzeAccessedMember
8 | - Test_MethodNotImplemented
9 | - **Liquip.Scanner.Tests**:
10 | Validates that all required plugs are detected correctly.
11 | - LoadPlugMethods_ShouldReturnPublicStaticMethods
12 | - LoadPlugMethods_ShouldReturnEmpty_WhenNoMethodsExist
13 | - LoadPlugMethods_ShouldContainAddMethod_WhenPlugged
14 | - LoadPlugs_ShouldFindPluggedClasses
15 | - LoadPlugs_ShouldIgnoreClassesWithoutPlugAttribute
16 | - LoadPlugs_ShouldHandleOptionalPlugs
17 | - **Liquip.Patcher.Tests**:
18 | Ensures that plugs are applied successfully to target methods and types.
19 | - PatchObjectWithAThis_ShouldPlugInstanceCorrectly
20 | - PatchConstructor_ShouldPlugCtorCorrectly
21 | - PatchType_ShouldReplaceAllMethodsCorrectly
22 | - PatchType_ShouldPlugAssembly
23 | - AddMethod_BehaviorBeforeAndAfterPlug
24 | - **Liquip.ilc.Tests**:
25 | Tests patched assemblies for runtime behavior on NativeAOT.
26 |
27 | ## Use
28 | Two options are available to run the test suite.
29 |
30 | - The first one is to run the tests inside Visual Studio.
31 |
32 | - And the second is to run Liquip.Patcher.Tests using Docker. You have to go at the root of the repository and run:
33 |
34 | `docker build -t patcher-tests .`
35 |
36 | `docker run --rm patcher-tests`
37 |
--------------------------------------------------------------------------------
/docs/articles/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Plugs
2 | href: plugs.md
3 | - name: Cosmos.Asm.Build
4 | href: asm-build.md
5 | - name: Cosmos.ilc.Build
6 | href: ilc-build.md
7 | - name: Cosmos.Patcher.Build
8 | href: patcher-build.md
9 | - name: Kernel Compilation Steps
10 | href: kernel-compilation-steps.md
11 | - name: Testing
12 | href: testing.md
13 |
--------------------------------------------------------------------------------
/docs/docfx.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json",
3 | "metadata": [
4 | {
5 | "src": [
6 | {
7 | "src": "./../Liquip/src",
8 | "files": [
9 | "**/*.csproj"
10 | ],
11 | "exclude": [
12 | "**/bin/**",
13 | "**/obj/**"
14 | ]
15 | }
16 | ],
17 | "dest": "api"
18 | }
19 | ],
20 | "build": {
21 | "content": [
22 | {
23 | "files": [
24 | "**/*.{md,yml}"
25 | ],
26 | "exclude": [
27 | "_site/**"
28 | ]
29 | }
30 | ],
31 | "resource": [
32 | {
33 | "files": [
34 | "images/**"
35 | ]
36 | }
37 | ],
38 | "output": "_site",
39 | "template": [
40 | "default",
41 | "modern"
42 | ],
43 | "globalMetadata": {
44 | "_appName": "Cosmos Gen 3",
45 | "_appTitle": "Cosmos Gen 3",
46 | "_enableSearch": true,
47 | "pdf": false
48 | },
49 | "postProcessors": [],
50 | "markdownEngineName": "markdig",
51 | "noLangKeyword": false,
52 | "keepFileLink": false,
53 | "cleanupCacheHistory": false,
54 | "disableGitFeatures": false
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | _layout: landing
3 | ---
4 |
5 | Welcome to the nativeaot-patcher wiki!
6 |
7 | ## Documentation
8 | - [Kernel Compilation Steps](/articles/build/kernel-compilation-steps.md)
9 | - [Plugs](/articles/plugs.md)
10 | - [Testing](/articles/testing.md)
11 | - [Cosmos.Asm.Build](/articles/build/asm-build.md)
12 | - [Cosmos.Patcher.Build](/articles/build/patcher-build.md)
13 | - [Cosmos.ilc.Build](/articles/build/ilc-build.md)
14 |
15 | ## Resources
16 | - [Cosmos Gen3: The NativeAOT Era and the End of IL2CPU?](https://valentin.bzh/posts/3)
17 | - [NativeAOT Developer Workflow](https://github.com/dotnet/runtime/blob/main/docs/workflow/building/coreclr/nativeaot.md)
18 | - [NativeAOT Limitations](https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/limitations.md)
19 |
20 |
21 | xref link [xrefmap.yml](/xrefmap.yml)
22 |
--------------------------------------------------------------------------------
/docs/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Docs
2 | href: articles/
3 | - name: API
4 | href: api/
--------------------------------------------------------------------------------
/examples/KernelExample/.gitignore:
--------------------------------------------------------------------------------
1 | /limine
2 | /output
3 |
--------------------------------------------------------------------------------
/examples/KernelExample/Kernel.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | true
7 |
8 | RunPatcher;$(IlcDependsOn)
9 | CompileWithIlc;BuildYasm;$(LinkTargetDependsOn)
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | runtime; native; contentfiles; analyzers; buildtransitive
23 | all
24 |
25 |
26 | runtime; native; contentfiles; analyzers; buildtransitive
27 | all
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/examples/KernelExample/Makefile:
--------------------------------------------------------------------------------
1 | DOTNET = dotnet
2 | ARGS = publish -c Debug -r linux-x64
3 | CSPROJ = Kernel.csproj
4 |
5 | QEMU = qemu-system-x86_64
6 | QEMU_OUTPUT = ./output/Kernel.iso
7 | QEMU_ARGS = -cdrom $(QEMU_OUTPUT)
8 |
9 | all: build run
10 |
11 | # Build the kernel
12 | build:
13 | $(DOTNET) $(ARGS) $(CSPROJ)
14 |
15 | # Run the kernel (requires build)
16 | run: build
17 | $(QEMU) $(QEMU_ARGS)
18 |
19 | # Clean the build
20 | clean:
21 | $(DOTNET) clean $(CSPROJ)
22 | rm -rf ./bin/
23 | rm -rf ./obj/
24 | rm -rf ./output/
25 |
26 | .PHONY: build run clean
--------------------------------------------------------------------------------
/examples/KernelExample/src/Asm/io.asm:
--------------------------------------------------------------------------------
1 | global _native_io_write_byte
2 | global _native_io_write_word
3 | global _native_io_write_dword
4 | global _native_io_write_qword
5 |
6 | global _native_io_read_byte
7 | global _native_io_read_word
8 | global _native_io_read_dword
9 | global _native_io_read_qword
10 |
11 | section .text
12 |
13 | ; void out_byte(ushort port, byte value)
14 | _native_io_write_byte:
15 | mov dx, di
16 | mov al, sil
17 | out dx, al
18 | ret
19 |
20 | _native_io_write_word:
21 | mov dx, di
22 | mov ax, si
23 | out dx, ax
24 | ret
25 |
26 | ; void out_dword(ushort port, uint value)
27 | _native_io_write_dword:
28 | mov dx, di
29 | mov eax, esi
30 | out dx, eax
31 | ret
32 |
33 | ; byte in_byte(ushort port)
34 | _native_io_read_byte:
35 | mov dx, di
36 | in al, dx
37 | ret
38 |
39 | ; ushort in_word(ushort port)
40 | _native_io_read_word:
41 | mov dx, di
42 | in ax, dx
43 | ret
44 |
45 | ; uint in_dword(ushort port)
46 | _native_io_read_dword:
47 | mov dx, di
48 | in eax, dx
49 | ret
50 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/Base64.cs:
--------------------------------------------------------------------------------
1 | namespace EarlyBird.Conversion
2 | {
3 | public static unsafe class Base64
4 | {
5 | public static byte* Decode(char* Encoded, uint Length)
6 | {
7 | // Determine padding based on '=' characters
8 | int Padding = 0;
9 | if (Encoded[Length - 1] == '=')
10 | {
11 | Padding++;
12 | if (Encoded[Length - 2] == '=')
13 | {
14 | Padding++;
15 | }
16 | }
17 |
18 | // Calculate the length of the decoded data
19 | int DecodedLength = (int)((Length * 3) / 4 - Padding);
20 | byte* Decoded = (byte*)MemoryOp.Alloc((uint)DecodedLength);
21 |
22 | // Base64 decoding table for A-Z, a-z, 0-9, +, /
23 | byte* Base64Table = (byte*)MemoryOp.Alloc(128);
24 | for (int i = 0; i < 26; i++) Base64Table['A' + i] = (byte)(i); // A-Z -> 0-25
25 | for (int i = 0; i < 26; i++) Base64Table['a' + i] = (byte)(26 + i); // a-z -> 26-51
26 | for (int i = 0; i < 10; i++) Base64Table['0' + i] = (byte)(52 + i); // 0-9 -> 52-61
27 | Base64Table['+'] = 62; // '+' -> 62
28 | Base64Table['/'] = 63; // '/' -> 63
29 |
30 | int DecodedIndex = 0;
31 | for (int i = 0; i < Length; i += 4)
32 | {
33 | byte b0 = Base64Table[Encoded[i]];
34 | byte b1 = Base64Table[Encoded[i + 1]];
35 | byte b2 = Base64Table[Encoded[i + 2]];
36 | byte b3 = Base64Table[Encoded[i + 3]];
37 |
38 | Decoded[DecodedIndex++] = (byte)((b0 << 2) | (b1 >> 4)); // First byte
39 | if (i + 2 < Length - Padding) // Make sure we're not out of bounds
40 | {
41 | Decoded[DecodedIndex++] = (byte)((b1 << 4) | (b2 >> 2)); // Second byte
42 | }
43 | if (i + 3 < Length - Padding) // Make sure we're not out of bounds
44 | {
45 | Decoded[DecodedIndex++] = (byte)((b2 << 6) | b3); // Third byte
46 | }
47 | }
48 |
49 | return Decoded;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/Internal.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace EarlyBird.Internal
5 | {
6 |
7 | public static class Native
8 | {
9 | public static class IO
10 | {
11 | [MethodImpl(MethodImplOptions.InternalCall)]
12 | [RuntimeImport("*", "_native_io_write_byte")]
13 | public static extern void Write8(ushort Port, byte Value);
14 |
15 | [MethodImpl(MethodImplOptions.InternalCall)]
16 | [RuntimeImport("*", "_native_io_write_word")]
17 | public static extern void Write16(ushort Port, ushort Value);
18 |
19 | [MethodImpl(MethodImplOptions.InternalCall)]
20 | [RuntimeImport("*", "_native_io_write_dword")]
21 | public static extern void Write32(ushort Port, uint Value);
22 |
23 | [MethodImpl(MethodImplOptions.InternalCall)]
24 | [RuntimeImport("*", "_native_io_read_byte")]
25 | public static extern byte Read8(ushort Port);
26 |
27 | [MethodImpl(MethodImplOptions.InternalCall)]
28 | [RuntimeImport("*", "_native_io_read_word")]
29 | public static extern ushort Read16(ushort Port);
30 |
31 | [MethodImpl(MethodImplOptions.InternalCall)]
32 | [RuntimeImport("*", "_native_io_read_dword")]
33 | public static extern uint Read32(ushort Port);
34 | }
35 | }
36 |
37 | public static class Serial
38 | {
39 | private static readonly ushort COM1 = 0x3F8;
40 |
41 | private static void WaitForTransmitBufferEmpty()
42 | {
43 | while ((Native.IO.Read8((ushort)(COM1 + 5)) & 0x20) == 0) ;
44 | }
45 |
46 | public static void ComWrite(byte value)
47 | {
48 | // Wait for the transmit buffer to be empty
49 | WaitForTransmitBufferEmpty();
50 | // Write the byte to the COM port
51 | Native.IO.Write8(COM1, value);
52 | }
53 |
54 |
55 |
56 | public static void ComInit()
57 | {
58 | Native.IO.Write8((ushort)(COM1 + 1), 0x00);
59 | Native.IO.Write8((ushort)(COM1 + 3), 0x80);
60 | Native.IO.Write8(COM1, 0x01);
61 | Native.IO.Write8((ushort)(COM1 + 1), 0x00);
62 | Native.IO.Write8((ushort)(COM1 + 3), 0x03);
63 | Native.IO.Write8((ushort)(COM1 + 2), 0xC7);
64 | }
65 |
66 | public static unsafe void WriteString(string str)
67 | {
68 | fixed (char* ptr = str)
69 | {
70 | for (int i = 0; i < str.Length; i++)
71 | {
72 | ComWrite((byte)ptr[i]);
73 | }
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/KernelEntry.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime;
2 | using System.Runtime.CompilerServices;
3 | using Cosmos.Boot.Limine;
4 | using EarlyBird;
5 | using EarlyBird.Internal;
6 |
7 | using static EarlyBird.Graphics;
8 |
9 | unsafe class Program
10 | {
11 | static readonly LimineFramebufferRequest Framebuffer = new();
12 | static readonly LimineHHDMRequest HHDM = new();
13 |
14 | [RuntimeExport("kmain")]
15 | static void Main()
16 | {
17 | MemoryOp.InitializeHeap(HHDM.Offset, 0x1000000);
18 | var fb = Framebuffer.Response->Framebuffers[0];
19 | Canvas.Address = (uint*)fb->Address;
20 | Canvas.Pitch = (uint)fb->Pitch;
21 | Canvas.Width = (uint)fb->Width;
22 | Canvas.Height = (uint)fb->Height;
23 |
24 | Canvas.ClearScreen(Color.Black);
25 |
26 | Canvas.DrawString("CosmosOS booted.", 0, 0, Color.White);
27 |
28 | Serial.ComInit();
29 |
30 | Canvas.DrawString("UART started.", 0, 28, Color.White);
31 |
32 | Serial.WriteString("Hello from UART\n");
33 |
34 | while (true) ;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/Limine/LimineFramebuffer.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace Cosmos.Boot.Limine;
4 |
5 | // Adapted from Azerou.
6 | [StructLayout(LayoutKind.Sequential)]
7 | public readonly struct LimineHHDMRequest()
8 | {
9 | public readonly LimineID ID = new(0x48dcf1cb8ad2b852, 0x63984e959a98244b);
10 | public readonly ulong Revision = 0;
11 | public readonly ulong Offset;
12 | }
13 |
14 | [StructLayout(LayoutKind.Sequential)]
15 | public readonly unsafe struct LimineFramebufferRequest()
16 | {
17 | public readonly LimineID ID = new(0x9d5827dcd881dd75, 0xa3148604f6fab11b);
18 | public readonly ulong Revision = 0;
19 | public readonly LimineFramebufferResponse* Response;
20 | }
21 |
22 | [StructLayout(LayoutKind.Sequential)]
23 | public readonly unsafe struct LimineFramebufferResponse
24 | {
25 | public readonly ulong Revision;
26 | public readonly ulong FramebufferCount;
27 | public readonly LimineFramebuffer** Framebuffers;
28 | }
29 |
30 | public enum LimineFbMemoryModel : byte
31 | {
32 | Rgb = 1
33 | }
34 |
35 | [StructLayout(LayoutKind.Sequential)]
36 | public readonly unsafe struct LimineFramebuffer
37 | {
38 | public readonly void* Address;
39 | public readonly ulong Width;
40 | public readonly ulong Height;
41 | public readonly ulong Pitch;
42 | public readonly ulong BitsPerPixel;
43 | public readonly LimineFbMemoryModel MemoryModel;
44 | public readonly byte RedMaskSize;
45 | public readonly byte RedMaskShift;
46 | public readonly byte GreenMaskSize;
47 | public readonly byte GreenMaskShift;
48 | public readonly byte BlueMaskSize;
49 | public readonly byte BlueMaskShift;
50 | private readonly byte p1, p2, p3, p4, p5, p6, p7;
51 | public readonly ulong EdidSize;
52 | public readonly void* Edid;
53 | }
54 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/Limine/LimineID.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | // Adapted from Azerou.
4 |
5 | namespace Cosmos.Boot.Limine;
6 |
7 | [StructLayout(LayoutKind.Sequential)]
8 | public readonly struct LimineID
9 | {
10 | public readonly ulong One, Two, Three, Four;
11 |
12 | public LimineID(ulong a3, ulong a4)
13 | {
14 | One = 0xc7b1dd30df4c8b88; // LIMINE_COMMON_MAGIC
15 | Two = 0x0a82e883a194f07b;
16 | Three = a3;
17 | Four = a4;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/Stdlib.cs:
--------------------------------------------------------------------------------
1 | #region A couple very basic things
2 |
3 | using System;
4 | using System.Runtime;
5 |
6 | namespace System
7 | {
8 | public struct Void { }
9 |
10 | // The layout of primitive types is special cased because it would be recursive.
11 | // These really don't need any fields to work.
12 | public struct Boolean { }
13 | public struct Char { }
14 | public struct SByte { }
15 | public struct Byte { }
16 | public struct Int16 { }
17 | public struct UInt16 { }
18 | public struct Int32 { }
19 | public struct UInt32 { }
20 | public struct Int64 { }
21 | public struct UInt64 { }
22 | public struct IntPtr { }
23 | public struct UIntPtr { }
24 | public struct Single { }
25 | public struct Double { }
26 |
27 | public class Object
28 | {
29 | #pragma warning disable 169
30 | // The layout of object is a contract with the compiler.
31 | private IntPtr m_pMethodTable;
32 | #pragma warning restore 169
33 | }
34 |
35 | public class Exception
36 | {
37 | public Exception() { }
38 | protected Exception(string message) { }
39 | }
40 |
41 | public abstract class Type { }
42 | public abstract class ValueType { }
43 | public abstract class Enum : ValueType { }
44 |
45 | public struct Nullable where T : struct { }
46 |
47 | public sealed class String
48 | {
49 | public readonly int Length;
50 | }
51 |
52 | public abstract class Array
53 | {
54 | public readonly int Length;
55 | }
56 | public abstract class Delegate { }
57 | public abstract class MulticastDelegate : Delegate { }
58 |
59 | public struct RuntimeTypeHandle { }
60 | public struct RuntimeMethodHandle { }
61 | public struct RuntimeFieldHandle { }
62 |
63 | public class Attribute { }
64 |
65 | public sealed class AttributeUsageAttribute : Attribute
66 | {
67 | public AttributeUsageAttribute(AttributeTargets validOn) { }
68 | public bool AllowMultiple { get; set; }
69 | public bool Inherited { get; set; }
70 | }
71 |
72 | public enum AttributeTargets { }
73 |
74 | public class AppContext
75 | {
76 | public static void SetData(string s, object o) { }
77 | }
78 |
79 | namespace Runtime.CompilerServices
80 | {
81 | public class RuntimeHelpers
82 | {
83 | public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int);
84 | }
85 |
86 | public static class RuntimeFeature
87 | {
88 | public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention);
89 | }
90 |
91 | public enum MethodImplOptions
92 | {
93 | Unmanaged = 0x0004,
94 | NoInlining = 0x0008,
95 | NoOptimization = 0x0040,
96 | AggressiveInlining = 0x0100,
97 | AggressiveOptimization = 0x200,
98 | InternalCall = 0x1000,
99 | }
100 |
101 | //Implementing the MethodImpl attribute for RuntimeExport to work
102 | public sealed class MethodImplAttribute : Attribute
103 | {
104 | public MethodImplAttribute(MethodImplOptions opt) { }
105 | }
106 | }
107 | }
108 |
109 | namespace System.Runtime.InteropServices
110 | {
111 | public class UnmanagedType { }
112 |
113 | public class MarshalDirectiveException : Exception
114 | {
115 | public MarshalDirectiveException()
116 | { }
117 | public MarshalDirectiveException(string message) : base(message) { }
118 | }
119 |
120 | sealed class StructLayoutAttribute : Attribute
121 | {
122 | public StructLayoutAttribute(LayoutKind layoutKind)
123 | {
124 | }
125 | }
126 |
127 | public sealed class DllImportAttribute : Attribute
128 | {
129 | public string EntryPoint;
130 | public CharSet CharSet;
131 | public bool SetLastError;
132 | public bool ExactSpelling;
133 | public CallingConvention CallingConvention;
134 | public bool BestFitMapping;
135 | public bool PreserveSig;
136 | public bool ThrowOnUnmappableChar;
137 |
138 | public string Value { get; }
139 |
140 | public DllImportAttribute(string dllName)
141 | {
142 | Value = dllName;
143 | }
144 | }
145 |
146 | internal enum LayoutKind
147 | {
148 | Sequential = 0, // 0x00000008,
149 | Explicit = 2, // 0x00000010,
150 | Auto = 3, // 0x00000000,
151 | }
152 |
153 | public enum CharSet
154 | {
155 | None = 1, // User didn't specify how to marshal strings.
156 | Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars.
157 | Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars.
158 | Auto = 4, // Marshal Strings in the right way for the target system.
159 | }
160 |
161 | public enum CallingConvention
162 | {
163 | Winapi = 1,
164 | Cdecl = 2,
165 | StdCall = 3,
166 | ThisCall = 4,
167 | FastCall = 5,
168 | }
169 | }
170 | #endregion
171 |
172 | #region Things needed by ILC
173 | namespace System
174 | {
175 | namespace Runtime
176 | {
177 | internal enum InternalGCCollectionMode
178 | {
179 | Default,
180 | Forced,
181 | Optimized
182 | }
183 |
184 | internal sealed class RuntimeExportAttribute : Attribute
185 | {
186 | public RuntimeExportAttribute(string entry) { }
187 | }
188 |
189 | internal sealed class RuntimeImportAttribute : Attribute
190 | {
191 | public string DllName { get; }
192 | public string EntryPoint { get; }
193 |
194 | public RuntimeImportAttribute(string entry)
195 | {
196 | EntryPoint = entry;
197 | }
198 |
199 | public RuntimeImportAttribute(string dllName, string entry)
200 | {
201 | EntryPoint = entry;
202 | DllName = dllName;
203 | }
204 | }
205 | }
206 |
207 | class Array : Array { }
208 | }
209 |
210 | namespace Internal.Runtime.CompilerHelpers
211 | {
212 | // A class that the compiler looks for that has helpers to initialize the
213 | // process. The compiler can gracefully handle the helpers not being present,
214 | // but the class itself being absent is unhandled. Let's add an empty class.
215 | class StartupCodeHelpers
216 | {
217 | // A couple symbols the generated code will need we park them in this class
218 | // for no particular reason. These aid in transitioning to/from managed code.
219 | // Since we don't have a GC, the transition is a no-op.
220 | [RuntimeExport("RhpReversePInvoke")]
221 | static void RhpReversePInvoke(IntPtr frame) { }
222 | [RuntimeExport("RhpReversePInvokeReturn")]
223 | static void RhpReversePInvokeReturn(IntPtr frame) { }
224 | [RuntimeExport("RhpPInvoke")]
225 | static void RhpPInvoke(IntPtr frame) { }
226 | [RuntimeExport("RhpPInvokeReturn")]
227 | static void RhpPInvokeReturn(IntPtr frame) { }
228 |
229 | [RuntimeExport("RhpFallbackFailFast")]
230 | static void RhpFallbackFailFast() { while (true) ; }
231 |
232 | [RuntimeExport("InitializeModules")]
233 | static unsafe void InitializeModules(IntPtr osModule, IntPtr* pModuleHeaders, int count, IntPtr* pClasslibFunctions, int nClasslibFunctions) { }
234 |
235 | }
236 |
237 | public static class ThrowHelpers
238 | {
239 | public static void ThrowNotImplementedException()
240 | {
241 | while (true) ;
242 | }
243 |
244 | public static void ThrowNullReferenceException()
245 | {
246 | while (true) ;
247 | }
248 |
249 | public static void ThrowIndexOutOfRangeException()
250 | {
251 | while (true) ;
252 | }
253 |
254 | public static void ThrowInvalidProgramException()
255 | {
256 | while (true) ;
257 | }
258 |
259 | public static void ThrowTypeLoadException()
260 | {
261 | while (true) ;
262 | }
263 |
264 | public static void ThrowTypeLoadExceptionWithArgument()
265 | {
266 | while (true) ;
267 | }
268 |
269 | public static void ThrowInvalidProgramExceptionWithArgument()
270 | {
271 | while (true) ;
272 | }
273 |
274 | public static void ThrowOverflowException()
275 | {
276 | while (true) ;
277 | }
278 | }
279 | }
280 |
281 | namespace Internal.Runtime
282 | {
283 | internal abstract class ThreadStatics
284 | {
285 | public static unsafe object GetThreadStaticBaseForType(TypeManagerSlot* pModuleData, int typeTlsIndex)
286 | {
287 | return null;
288 | }
289 | }
290 |
291 | internal struct TypeManagerSlot { }
292 | }
293 | #endregion
294 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/Stellib.cs:
--------------------------------------------------------------------------------
1 | using EarlyBird.PSF;
2 |
3 | namespace EarlyBird
4 | {
5 | public class Color
6 | {
7 | public static uint Red = 0xFF0000;
8 | public static uint Green = 0x00FF00;
9 | public static uint Blue = 0x0000FF;
10 | public static uint White = 0xFFFFFF;
11 | public static uint Black = 0x000000;
12 | public static uint Yellow = 0xFFFF00;
13 | public static uint Cyan = 0x00FFFF;
14 | public static uint Magenta = 0xFF00FF;
15 | public static uint Orange = 0xFFA500;
16 | public static uint Purple = 0x800080;
17 | public static uint Pink = 0xFFC0CB;
18 | public static uint Brown = 0xA52A2A;
19 | public static uint Gray = 0x808080;
20 | public static uint LightGray = 0xD3D3D3;
21 | public static uint DarkGray = 0xA9A9A9;
22 | public static uint LightRed = 0xFF7F7F;
23 | public static uint LightGreen = 0x7FFF7F;
24 | public static uint LightBlue = 0x7F7FFF;
25 | public static uint LightYellow = 0xFFFF7F;
26 | public static uint LightCyan = 0x7FFFFF;
27 | public static uint LightMagenta = 0xFF7FFF;
28 | public static uint LightOrange = 0xFFBF00;
29 | public static uint LightPurple = 0xBF00FF;
30 | public static uint LightPink = 0xFFB6C1;
31 | public static uint LightBrown = 0xD2B48C;
32 | public static uint Transparent = 0x00000000; // Transparent color
33 | }
34 |
35 | public static class Math
36 | {
37 | public static int Abs(int value)
38 | {
39 | return value < 0 ? -value : value;
40 | }
41 |
42 | public static int Min(int a, int b)
43 | {
44 | return a < b ? a : b;
45 | }
46 |
47 | public static int Max(int a, int b)
48 | {
49 | return a > b ? a : b;
50 | }
51 | }
52 | public static unsafe class MemoryOp
53 | {
54 |
55 | private static ulong HeapBase;
56 | private static ulong HeapEnd;
57 | private static ulong FreeListHead;
58 |
59 | public static void InitializeHeap(ulong heapBase, ulong heapSize)
60 | {
61 | HeapBase = heapBase;
62 | HeapEnd = heapBase + heapSize;
63 | FreeListHead = heapBase;
64 |
65 | // Initialize the free list with a single large block
66 | *(ulong*)FreeListHead = heapSize; // Block size
67 | *((ulong*)FreeListHead + 1) = 0; // Next block pointer
68 | }
69 |
70 | public static void* Alloc(uint size)
71 | {
72 | size = (uint)((size + 7) & ~7); // Align size to 8 bytes
73 | ulong prev = 0;
74 | ulong current = FreeListHead;
75 |
76 | while (current != 0)
77 | {
78 | ulong blockSize = *(ulong*)current;
79 | ulong next = *((ulong*)current + 1);
80 |
81 | if (blockSize >= size + 16) // Enough space for allocation and metadata
82 | {
83 | ulong remaining = blockSize - size - 16;
84 | if (remaining >= 16) // Split block
85 | {
86 | *(ulong*)(current + 16 + size) = remaining;
87 | *((ulong*)(current + 16 + size) + 1) = next;
88 | *((ulong*)current + 1) = current + 16 + size;
89 | }
90 | else // Use entire block
91 | {
92 | size = (uint)(blockSize) - 16;
93 | *((ulong*)current + 1) = next;
94 | }
95 |
96 | if (prev == 0)
97 | {
98 | FreeListHead = *((ulong*)current + 1);
99 | }
100 | else
101 | {
102 | *((ulong*)prev + 1) = *((ulong*)current + 1);
103 | }
104 |
105 | *(ulong*)current = size; // Store allocated size
106 | return (void*)(current + 16);
107 | }
108 |
109 | prev = current;
110 | current = next;
111 | }
112 |
113 | return null; // Out of memory
114 | }
115 |
116 | public static void Free(void* ptr)
117 | {
118 | ulong block = (ulong)ptr - 16;
119 | ulong blockSize = *(ulong*)block;
120 |
121 | *(ulong*)block = blockSize + 16;
122 | *((ulong*)block + 1) = FreeListHead;
123 | FreeListHead = block;
124 | }
125 |
126 | public static void MemSet(byte* dest, byte value, int count)
127 | {
128 | for (int i = 0; i < count; i++)
129 | {
130 | dest[i] = value;
131 | }
132 | }
133 | public static void MemSet(uint* dest, uint value, int count)
134 | {
135 | for (int i = 0; i < count; i++)
136 | {
137 | dest[i] = value;
138 | }
139 | }
140 |
141 | public static void MemCopy(uint* dest, uint* src, int count)
142 | {
143 | for (int i = 0; i < count; i++)
144 | {
145 | dest[i] = src[i];
146 | }
147 | }
148 |
149 | public static bool MemCmp(uint* dest, uint* src, int count)
150 | {
151 | for (int i = 0; i < count; i++)
152 | {
153 | if (dest[i] != src[i])
154 | {
155 | return false;
156 | }
157 | }
158 | return true;
159 | }
160 |
161 | public static void MemMove(uint* dest, uint* src, int count)
162 | {
163 | if (dest < src)
164 | {
165 | for (int i = 0; i < count; i++)
166 | {
167 | dest[i] = src[i];
168 | }
169 | }
170 | else
171 | {
172 | for (int i = count - 1; i >= 0; i--)
173 | {
174 | dest[i] = src[i];
175 | }
176 | }
177 | }
178 | }
179 |
180 | public static class Graphics
181 | {
182 | public unsafe class Canvas
183 | {
184 | public static uint* Address;
185 | public static uint Width;
186 | public static uint Height;
187 | public static uint Pitch;
188 |
189 | public static void DrawPixel(uint color, int x, int y)
190 | {
191 | if (x >= 0 && x < Width && y >= 0 && y < Height)
192 | {
193 | Address[y * (int)(Pitch / 4) + x] = color;
194 | }
195 | }
196 |
197 | public static void DrawLine(uint color, int x1, int y1, int x2, int y2)
198 | {
199 | int dx = x2 - x1;
200 | int dy = y2 - y1;
201 | int absDx = Math.Abs(dx);
202 | int absDy = Math.Abs(dy);
203 | int sx = (dx > 0) ? 1 : -1;
204 | int sy = (dy > 0) ? 1 : -1;
205 | int err = absDx - absDy;
206 |
207 | while (true)
208 | {
209 | DrawPixel(color, x1, y1);
210 | if (x1 == x2 && y1 == y2) break;
211 | int err2 = err * 2;
212 | if (err2 > -absDy)
213 | {
214 | err -= absDy;
215 | x1 += sx;
216 | }
217 | if (err2 < absDx)
218 | {
219 | err += absDx;
220 | y1 += sy;
221 | }
222 | }
223 | }
224 |
225 | public static void DrawRectangle(uint color, int x, int y, int width, int height)
226 | {
227 | for (int i = 0; i < width; i++)
228 | {
229 | for (int j = 0; j < height; j++)
230 | {
231 | DrawPixel(color, x + i, y + j);
232 | }
233 | }
234 | }
235 |
236 | public static void DrawCircle(uint color, int x, int y, int radius)
237 | {
238 | for (int i = -radius; i <= radius; i++)
239 | {
240 | for (int j = -radius; j <= radius; j++)
241 | {
242 | if (i * i + j * j <= radius * radius)
243 | {
244 | int px = x + j;
245 | int py = y + i;
246 | if (px >= 0 && px < Width && py >= 0 && py < Height)
247 | {
248 | DrawPixel(color, px, py);
249 | }
250 | }
251 | }
252 | }
253 | }
254 |
255 | public static void ClearScreen(uint color)
256 | {
257 | MemoryOp.MemSet(Address, color, (int)((Pitch / 4) * Height));
258 | }
259 |
260 | public static void DrawChar(char c, int x, int y, uint color)
261 | {
262 | PCScreenFont.PutChar(c, x, y, color, Color.Transparent);
263 | }
264 |
265 | public static void DrawString(string text, int x, int y, uint color)
266 | {
267 | PCScreenFont.PutString(text, x, y, color, Color.Transparent);
268 | }
269 | }
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/examples/KernelExample/src/limine.conf:
--------------------------------------------------------------------------------
1 | # Timeout in seconds that Limine will use before automatically booting.
2 | timeout: 0
3 |
4 | # The entry name that will be displayed in the boot menu.
5 | /Limine Template
6 | # We use the Limine boot protocol.
7 | protocol: limine
8 |
9 | # Path to the kernel to boot. boot():/ represents the partition on which limine.conf is located.
10 | path: boot():/boot/Kernel.elf
--------------------------------------------------------------------------------
/examples/KernelExample/src/linker.ld:
--------------------------------------------------------------------------------
1 | OUTPUT_FORMAT(elf64-x86-64)
2 |
3 | ENTRY(kmain)
4 |
5 | PHDRS
6 | {
7 | text PT_LOAD;
8 | rodata PT_LOAD;
9 | data PT_LOAD;
10 | }
11 |
12 | SECTIONS
13 | {
14 | . = 0xffffffff80000000;
15 |
16 | . = ALIGN(CONSTANT(MAXPAGESIZE));
17 |
18 | .text : {
19 | *(.text .text.*)
20 | } :text
21 |
22 | . = ALIGN(CONSTANT(MAXPAGESIZE));
23 |
24 | .rodata : {
25 | *(.rodata .rodata.*)
26 | } :rodata
27 |
28 | . = ALIGN(CONSTANT(MAXPAGESIZE));
29 |
30 | .data : {
31 | *(.data .data.*)
32 | } :data
33 |
34 | .bss : {
35 | *(.bss .bss.*)
36 | *(COMMON)
37 | } :data
38 |
39 | /DISCARD/ : {
40 | *(.eh_frame*)
41 | *(.note .note.*)
42 | }
43 | }
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.104",
4 | "rollForward": "latestFeature"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Cosmos.API/Attributes/PlugAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Cosmos.API.Attributes;
2 |
3 | [AttributeUsage(AttributeTargets.Class, Inherited = false)]
4 | public sealed class PlugAttribute(string targetName, bool isOptional = false, bool replaceBase = false) : Attribute
5 | {
6 | // public TargetPlatform TargetPlatform;
7 |
8 | ///
9 | /// does not have a base type
10 | ///
11 | public PlugAttribute() : this(string.Empty)
12 | {
13 | }
14 |
15 | ///
16 | /// set base type by type
17 | ///
18 | ///
19 | ///
20 | public PlugAttribute(Type target) : this(target.FullName)
21 | {
22 | }
23 |
24 | public PlugAttribute(bool replaceBase) : this(string.Empty, replaceBase: replaceBase)
25 | {
26 | }
27 |
28 | public PlugAttribute(bool isOptional = false, bool replaceBase = false) : this(string.Empty, isOptional,
29 | replaceBase)
30 | {
31 | }
32 |
33 | ///
34 | /// the type as a string
35 | ///
36 | public string? TargetName { get; set; } = targetName;
37 |
38 | ///
39 | /// if the type cant be found skip
40 | ///
41 | public bool IsOptional { get; set; } = isOptional;
42 |
43 | public bool ReplaceBase { get; set; } = replaceBase;
44 | }
45 |
--------------------------------------------------------------------------------
/src/Cosmos.API/Attributes/PlugMemberAttribute.cs:
--------------------------------------------------------------------------------
1 | // This code is licensed under MIT license (see LICENSE for details)
2 |
3 | namespace Cosmos.API.Attributes;
4 |
5 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
6 | public class PlugMemberAttribute(string targetName) : Attribute
7 | {
8 | public string TargetName { get; set; } = targetName;
9 |
10 | public PlugMemberAttribute() : this(string.Empty) { }
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/src/Cosmos.API/Cosmos.API.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 |
5 | true
6 | true
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Cosmos.API/Enum/TargetPlatform.cs:
--------------------------------------------------------------------------------
1 | namespace Cosmos.API.Enum;
2 |
3 | public enum TargetPlatform
4 | {
5 | ///
6 | /// amd64
7 | ///
8 | // ReSharper disable once InconsistentNaming
9 | x86_64,
10 |
11 | ///
12 | /// arm
13 | ///
14 | Arm64,
15 |
16 | ///
17 | /// any
18 | ///
19 | Any
20 | }
21 |
--------------------------------------------------------------------------------
/src/Cosmos.API/LabelMaker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Security.Cryptography;
3 | using System.Text;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace Cosmos.API;
7 |
8 | public static class LabelMaker
9 | {
10 | ///
11 | /// Cache for label names.
12 | ///
13 | private static readonly Dictionary LabelNamesCache = [];
14 |
15 | private static readonly Dictionary AssemblyIds = [];
16 |
17 | // All label naming code should be changed to use this class.
18 |
19 | // Label bases can be up to 200 chars. If larger they will be shortened with an included hash.
20 | // This leaves up to 56 chars for suffix information.
21 |
22 | // Suffixes are a series of tags and have their own prefixes to preserve backwards compat.
23 | // .GUID_xxxxxx
24 | // .IL_0000
25 | // .ASM_00 - future, currently is IL_0000 or IL_0000.00
26 | // Would be nice to combine IL and ASM into IL_0000_00, but the way we work with the assembler currently
27 | // we cant because the ASM labels are issued as local labels.
28 | //
29 | // - Methods use a variety of alphanumeric suffixes for support code.
30 | // - .00 - asm markers at beginning of method
31 | // - .0000.00 IL.ASM marker
32 |
33 | public static int LabelCount { get; private set; }
34 |
35 | // Max length of labels at 256. We use lower here so that we still have room for suffixes for IL positions, etc.
36 | private const int MaxLengthWithoutSuffix = 200;
37 |
38 | public static string Get(MethodBase aMethod)
39 | {
40 | if (LabelNamesCache.TryGetValue(aMethod, out string? result))
41 | {
42 | return result;
43 | }
44 |
45 | result = Final(GetFullName(aMethod));
46 | LabelNamesCache.Add(aMethod, result);
47 | return result;
48 | }
49 |
50 | private const string IllegalIdentifierChars = "&.,+$<>{}-`\'/\\ ()[]*!=";
51 |
52 | // no array bracket, they need to replace, for unique names for used types in methods
53 | private static readonly Regex IllegalCharsReplace = new(@"[&.,+$<>{}\-\`\\'/\\ \(\)\*!=]", RegexOptions.Compiled);
54 |
55 | private static string FilterStringForIncorrectChars(string aName)
56 | {
57 | string? xTempResult = aName;
58 | foreach (char c in IllegalIdentifierChars)
59 | {
60 | xTempResult = xTempResult.Replace(c, '_');
61 | }
62 |
63 | return xTempResult;
64 | }
65 |
66 | private static string Final(string xName)
67 | {
68 | xName = xName.Replace("[]", "array");
69 | xName = xName.Replace("<>", "compilergenerated");
70 | xName = xName.Replace("[,]", "array");
71 | xName = xName.Replace("*", "pointer");
72 | xName = xName.Replace("|", "sLine");
73 |
74 | xName = IllegalCharsReplace.Replace(xName, string.Empty);
75 |
76 | if (xName.Length > MaxLengthWithoutSuffix)
77 | {
78 | using (MD5? xHash = MD5.Create())
79 | {
80 | byte[]? xValue = xHash.ComputeHash(Encoding.GetEncoding(0).GetBytes(xName));
81 | StringBuilder? xSb = new(xName);
82 | // Keep length max same as before.
83 | xSb.Length = MaxLengthWithoutSuffix - xValue.Length * 2;
84 | foreach (byte xByte in xValue)
85 | {
86 | xSb.Append(xByte.ToString("X2"));
87 | }
88 |
89 | xName = xSb.ToString();
90 | }
91 | }
92 |
93 | LabelCount++;
94 | return xName;
95 | }
96 |
97 | ///
98 | /// Get internal name for the type
99 | ///
100 | ///
101 | /// If true, the assembly id is included
102 | ///
103 | public static string GetFullName(Type? aType, bool aAssemblyIncluded = true)
104 | {
105 | if (aType is null)
106 | {
107 | throw new ArgumentException("type is null", nameof(aType));
108 | }
109 |
110 | if (aType.IsGenericParameter)
111 | {
112 | return aType.FullName;
113 | }
114 |
115 | StringBuilder? stringBuilder = new(256);
116 |
117 | if (aAssemblyIncluded)
118 | {
119 | // Start the string with the id of the assembly
120 | Assembly? assembly = aType.Assembly;
121 | if (!AssemblyIds.ContainsKey(assembly))
122 | {
123 | AssemblyIds.Add(assembly, AssemblyIds.Count);
124 | }
125 |
126 | stringBuilder.Append("A" + AssemblyIds[assembly]);
127 | }
128 |
129 | if (aType.IsArray)
130 | {
131 | stringBuilder.Append(GetFullName(aType.GetElementType(), aAssemblyIncluded));
132 | stringBuilder.Append("[");
133 | int xRank = aType.GetArrayRank();
134 | while (xRank > 1)
135 | {
136 | stringBuilder.Append(",");
137 | xRank--;
138 | }
139 |
140 | stringBuilder.Append("]");
141 | return stringBuilder.ToString();
142 | }
143 |
144 | if (aType is { IsByRef: true, HasElementType: true })
145 | {
146 | return "&" + GetFullName(aType.GetElementType(), aAssemblyIncluded);
147 | }
148 |
149 | if (aType is { IsGenericType: true, IsGenericTypeDefinition: false })
150 | {
151 | stringBuilder.Append(GetFullName(aType.GetGenericTypeDefinition(), aAssemblyIncluded));
152 |
153 | stringBuilder.Append("<");
154 | Type[]? xArgs = aType.GetGenericArguments();
155 | for (int i = 0; i < xArgs.Length - 1; i++)
156 | {
157 | stringBuilder.Append(GetFullName(xArgs[i], aAssemblyIncluded));
158 | stringBuilder.Append(", ");
159 | }
160 |
161 | stringBuilder.Append(GetFullName(xArgs.Last(), aAssemblyIncluded));
162 | stringBuilder.Append(">");
163 | }
164 | else
165 | {
166 | stringBuilder.Append(aType.FullName);
167 | }
168 |
169 | return stringBuilder.ToString();
170 | }
171 |
172 | ///
173 | /// Get the full name for the method
174 | ///
175 | ///
176 | /// If true, id of assembly is included
177 | ///
178 | ///
179 | public static string GetFullName(MethodBase aMethod, bool aAssemblyIncluded = true)
180 | {
181 | if (aMethod == null)
182 | {
183 | throw new ArgumentNullException(nameof(aMethod));
184 | }
185 |
186 | StringBuilder? xBuilder = new(256);
187 | string[]? xParts = aMethod.ToString().Split(' ');
188 | MethodInfo? xMethodInfo = aMethod as MethodInfo;
189 | if (xMethodInfo != null)
190 | {
191 | xBuilder.Append(GetFullName(xMethodInfo.ReturnType, aAssemblyIncluded));
192 | }
193 | else
194 | {
195 | ConstructorInfo? xCtor = aMethod as ConstructorInfo;
196 | if (xCtor != null)
197 | {
198 | xBuilder.Append(typeof(void).FullName);
199 | }
200 | else
201 | {
202 | xBuilder.Append(xParts[0]);
203 | }
204 | }
205 |
206 | xBuilder.Append(" ");
207 | if (aMethod.DeclaringType != null)
208 | {
209 | xBuilder.Append(GetFullName(aMethod.DeclaringType, aAssemblyIncluded));
210 | }
211 | else
212 | {
213 | xBuilder.Append("dynamic_method");
214 | }
215 |
216 | xBuilder.Append(".");
217 | if (aMethod.IsGenericMethod && !aMethod.IsGenericMethodDefinition)
218 | {
219 | xBuilder.Append(xMethodInfo.GetGenericMethodDefinition().Name);
220 |
221 | Type[]? xGenArgs = aMethod.GetGenericArguments();
222 | if (xGenArgs.Length > 0)
223 | {
224 | xBuilder.Append("<");
225 | for (int i = 0; i < xGenArgs.Length - 1; i++)
226 | {
227 | xBuilder.Append(GetFullName(xGenArgs[i], aAssemblyIncluded));
228 | xBuilder.Append(", ");
229 | }
230 |
231 | xBuilder.Append(GetFullName(xGenArgs.Last(), aAssemblyIncluded));
232 | xBuilder.Append(">");
233 | }
234 | }
235 | else
236 | {
237 | xBuilder.Append(aMethod.Name);
238 | }
239 |
240 | xBuilder.Append("(");
241 | ParameterInfo[]? xParams = aMethod.GetParameters();
242 | for (int i = 0; i < xParams.Length; i++)
243 | {
244 | if (i == 0 && xParams[i].Name == "aThis")
245 | {
246 | continue;
247 | }
248 |
249 | xBuilder.Append(GetFullName(xParams[i].ParameterType, aAssemblyIncluded));
250 | if (i < xParams.Length - 1)
251 | {
252 | xBuilder.Append(", ");
253 | }
254 | }
255 |
256 | xBuilder.Append(")");
257 | return xBuilder.ToString();
258 | }
259 |
260 | public static string GetFullName(FieldInfo aField) => GetFullName(aField.FieldType, false) + " " +
261 | GetFullName(aField.DeclaringType, false) + "." + aField.Name;
262 |
263 | ///
264 | /// Gets a label for the given static field
265 | ///
266 | ///
267 | ///
268 | ///
269 | /// throws if its not static
270 | public static string GetStaticFieldName(Type aType, string aField) => GetStaticFieldName(aType.GetField(aField));
271 |
272 | ///
273 | /// Gets a label for the given static field
274 | ///
275 | ///
276 | ///
277 | /// throws if its not static
278 | public static string GetStaticFieldName(FieldInfo aField)
279 | {
280 | if (!aField.IsStatic)
281 | {
282 | throw new NotSupportedException($"{aField.Name}: is not static");
283 | }
284 |
285 | return FilterStringForIncorrectChars(
286 | "static_field__" + GetFullName(aField.DeclaringType) + "." + aField.Name);
287 | }
288 |
289 | public static string GetRandomLabel() => $"random_label__{Guid.NewGuid()}";
290 | }
291 |
--------------------------------------------------------------------------------
/src/Cosmos.Asm.Build/Cosmos.Asm.Build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | True
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Cosmos.Asm.Build/Tasks/LdTask.cs:
--------------------------------------------------------------------------------
1 | // This code is licensed under MIT license (see LICENSE for details)
2 |
3 | using System.Text;
4 | using Microsoft.Build.Framework;
5 | using Microsoft.Build.Utilities;
6 |
7 | namespace Cosmos.Asm.Build.Tasks;
8 |
9 | public sealed class LdTask : ToolTask
10 | {
11 | [Required] public string? LdPath { get; set; }
12 | [Required] public string? ObjectPath { get; set; }
13 | [Required] public string? OutputFile { get; set; }
14 |
15 | protected override string GenerateFullPathToTool() =>
16 | LdPath;
17 |
18 | protected override string GenerateCommandLineCommands()
19 | {
20 | StringBuilder sb = new();
21 |
22 | sb.Append($" -o {OutputFile} ");
23 |
24 | IEnumerable paths = Directory.EnumerateFiles(ObjectPath, "*.obj", SearchOption.TopDirectoryOnly);
25 |
26 | sb.Append(string.Join(" ", paths));
27 |
28 | return sb.ToString();
29 | }
30 |
31 | public override bool Execute()
32 | {
33 | Log.LogMessage(MessageImportance.High, "Running Cosmos.Asm-ld...");
34 | Log.LogMessage(MessageImportance.High, $"Tool Path: {LdPath}");
35 | Log.LogMessage(MessageImportance.High, $"Object Path: {ObjectPath}");
36 |
37 | return base.Execute();
38 | }
39 |
40 | protected override string ToolName => "Cosmos.Asm-ld";
41 | }
42 |
--------------------------------------------------------------------------------
/src/Cosmos.Asm.Build/Tasks/YasmBuildTask.cs:
--------------------------------------------------------------------------------
1 | // This code is licensed under MIT license (see LICENSE for details)
2 |
3 | using System.Security.Cryptography;
4 | using System.Text;
5 | using Microsoft.Build.Framework;
6 | using Microsoft.Build.Utilities;
7 |
8 | namespace Cosmos.Asm.Build.Tasks;
9 |
10 | public sealed class YasmBuildTask : ToolTask
11 | {
12 | [Required] public string? YasmPath { get; set; }
13 | [Required] public string[]? SourceFiles { get; set; }
14 | [Required] public string? OutputPath { get; set; }
15 |
16 | protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.Normal;
17 |
18 | protected override string GenerateFullPathToTool() =>
19 | YasmPath!;
20 |
21 | private string? FilePath { get; set; }
22 | private string? FileName { get; set; }
23 |
24 | protected override string GenerateCommandLineCommands()
25 | {
26 | Log.LogMessage(MessageImportance.Low, $"[Debug] Generating command-line args for {FilePath} -> {FileName}");
27 | StringBuilder sb = new();
28 |
29 | sb.Append($" -felf64 ");
30 | sb.Append($" -o {Path.Combine(OutputPath, FileName)} ");
31 | sb.Append($" {FilePath} ");
32 |
33 | return sb.ToString();
34 | }
35 |
36 | public override bool Execute()
37 | {
38 | LogStandardErrorAsError = true;
39 | Log.LogMessage(MessageImportance.High, "Running Cosmos.Asm-Yasm...");
40 | Log.LogMessage(MessageImportance.High, $"Tool Path: {YasmPath}");
41 |
42 | string paths = string.Join(",", SourceFiles);
43 | Log.LogMessage(MessageImportance.High, $"Source Files: {paths}");
44 | Log.LogMessage(MessageImportance.Low, "[Debug] Beginning file matching");
45 |
46 | if (!Directory.Exists(OutputPath))
47 | {
48 | Log.LogMessage(MessageImportance.Low, $"[Debug] Creating output directory: {OutputPath}");
49 | Directory.CreateDirectory(OutputPath);
50 | }
51 |
52 | using SHA1? hasher = SHA1.Create();
53 |
54 | foreach (string file in SourceFiles!)
55 | {
56 | FilePath = file;
57 | using FileStream stream = File.OpenRead(FilePath);
58 | byte[] fileHash = hasher.ComputeHash(stream);
59 | FileName = $"{Path.GetFileNameWithoutExtension(file)}-{BitConverter.ToString(fileHash).Replace("-", "").ToLower()}.obj";
60 | Log.LogMessage(MessageImportance.High, $"[Debug] About to run base.Execute() for {FileName}");
61 |
62 | if (!base.Execute())
63 | {
64 | Log.LogError($"[Debug] YasmBuildTask failed for {FilePath}");
65 | return false;
66 | }
67 | }
68 |
69 | Log.LogMessage(MessageImportance.High, "✅ YasmBuildTask completed successfully.");
70 | return true;
71 | }
72 |
73 | protected override string ToolName => "Cosmos.Asm-Yasm";
74 | }
75 |
--------------------------------------------------------------------------------
/src/Cosmos.Asm.Build/build/Asm.Build.Unix.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Cosmos.Asm.Build/build/Asm.Build.Windows.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Cosmos.Asm.Build/build/Cosmos.Asm.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $(IntermediateOutputPath)cosmos-asm\
6 |
7 |
8 |
9 | /usr/bin/yasm
10 |
11 |
12 |
13 | $(MSBuildThisFileDirectory)\..\lib\netstandard2.0\Cosmos.Asm.Build.dll
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Cosmos.Asm.Build/build/Cosmos.Asm.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | $(RuntimeIdentifier.Split('-')[1])
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | @(AsmSearchPath->'%(FullPath)/*.asm')
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | /usr/bin/yasm
49 |
50 | $(IntermediateOutputPath)/cosmos/asm/
51 |
52 |
53 |
54 |
55 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | $(IntermediateOutputPath)/cosmos/asm/
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/src/Cosmos.Common.Build/Cosmos.Common.Build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | false
5 | True
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Cosmos.Common.Build/build/Common.Build.Unix.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 | xorriso
4 | $(IsoRoot)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Cosmos.Common.Build/build/Common.Build.Windows.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | $([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)/xorriso-exe-for-windows/xorriso.exe'))
17 | cygdrive/c/$(MSBuildProjectDirectoryNoRoot)/output/iso_root
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Cosmos.Common.Build/build/Cosmos.Common.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | GetLinker;$(LinkTargetDependsOn)
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Cosmos.Common.Build/build/Cosmos.Common.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(OutputPath)/cosmos
4 | $(IntermediateOutputPath)/cosmos
5 | $(CosmosIntermediateOutputPath)/iso_root
6 | $(CosmosOutputPath)/$(AssemblyName).iso
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | $([MSBuild]::NormalizePath('$(OutputPath)/$(AssemblyName).elf'))
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/src/Cosmos.Ilc.Build/Cosmos.Ilc.Build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | $(AssemblyName)
5 | 1.0.0
6 | Cosmos
7 | Build tool to run ilc.
8 | false
9 | True
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Cosmos.Ilc.Build/build/Cosmos.Ilc.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @(ResolvedILCompilerPack->'%(PackageDirectory)')
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/Cosmos.Ilc.Build/build/Cosmos.Ilc.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 | latest
4 | Static
5 | Library
6 | true
7 | true
8 | v4.0.30319
9 | false
10 | false
11 | disable
12 | ResolveIlcPath
13 | $(DefaultIlcDependsOn);$(IlcDependsOn)
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 | $(BundledNETCoreAppPackageVersion)
26 | runtime.$(RuntimeIdentifier).Microsoft.DotNet.ILCompiler
27 |
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 | @(ResolvedILCompilerPack->'%(PackageDirectory)')
40 | $([MSBuild]::NormalizePath($(IlcHostPackagePath)/tools/))
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | $([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)'))
49 | $([System.IO.Path]::GetFullPath('$(OutputPath)'))
50 |
51 | $([System.IO.Path]::GetFullPath('$(OutputPath)/cosmos/native/'))
52 | $(FullIntermediateOutputPath)/cosmos/native/
53 | $(NativeOutputPath)$(AssemblyName)
54 |
55 |
56 |
57 |
58 |
59 | $([System.IO.Path]::GetFullPath('$(IlcIntermediateOutputPath)$(AssemblyName).ilc.rsp'))
60 | $([MSBuild]::NormalizePath('$(IlcIntermediateOutputPath)$(AssemblyName).o'))
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer.CodeFixes/Cosmos.Patcher.Analyzer.CodeFixes.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | false
5 | Cosmos.Patcher.Analyzer.CodeFixes
6 |
7 |
8 |
9 |
10 |
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 | all
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer.CodeFixes/Models/ProjectInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Linq;
2 |
3 | namespace Cosmos.Patcher.Analyzer.CodeFixes.Models;
4 |
5 | public readonly record struct ProjectInfo(IEnumerable PlugReferences)
6 | {
7 | public static ProjectInfo From(XDocument csproj) => new(
8 | PlugReferences: csproj.Descendants("ItemGroup")
9 | .Where(x => x.Name == "PlugsReference")
10 | .Select(x => x.Attribute("Include")!.Value)
11 | );
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer.CodeFixes/PatcherCodeFixProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Composition;
3 | using System.Xml.Linq;
4 | using Microsoft.CodeAnalysis;
5 | using Microsoft.CodeAnalysis.CodeActions;
6 | using Microsoft.CodeAnalysis.CodeFixes;
7 | using Microsoft.CodeAnalysis.CSharp;
8 | using Microsoft.CodeAnalysis.CSharp.Syntax;
9 | using Microsoft.CodeAnalysis.Editing;
10 | using ProjectInfo = Cosmos.Patcher.Analyzer.CodeFixes.Models.ProjectInfo;
11 |
12 | namespace Cosmos.Patcher.Analyzer.CodeFixes;
13 |
14 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(PatcherCodeFixProvider))]
15 | [Shared]
16 | public class PatcherCodeFixProvider : CodeFixProvider
17 | {
18 | public sealed override ImmutableArray FixableDiagnosticIds =>
19 | DiagnosticMessages.SupportedDiagnostics.Select(d => d.Id).ToImmutableArray();
20 |
21 | public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
22 |
23 | private string? _currentPath;
24 | private ProjectInfo _currentProject;
25 |
26 | private ProjectInfo? LoadCurrentProject(string projectPath)
27 | {
28 | if (_currentPath != projectPath)
29 | {
30 | _currentPath = projectPath;
31 | _currentProject = ProjectInfo.From(XDocument.Load(_currentPath));
32 | }
33 |
34 | return _currentProject;
35 | }
36 |
37 | public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
38 | {
39 | SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
40 | if (root == null)
41 | {
42 | return;
43 | }
44 |
45 | foreach (Diagnostic diagnostic in context.Diagnostics)
46 | {
47 | if (!diagnostic.Id.StartsWith(PatcherAnalyzer.DiagnosticId))
48 | {
49 | continue;
50 | }
51 |
52 | SyntaxNode declaration = root.FindNode(diagnostic.Location.SourceSpan);
53 | switch (diagnostic.Id)
54 | {
55 | case var id when id == DiagnosticMessages.StaticConstructorTooManyParams.Id:
56 | RegisterCodeFix(context, CodeActions.RemoveExtraParametersTitle,
57 | _ => CodeActions.RemoveExtraParameters(context.Document, declaration), diagnostic);
58 | break;
59 |
60 | case var id when id == DiagnosticMessages.MethodNeedsPlug.Id:
61 | RegisterCodeFix(context, CodeActions.PlugMethodTitle,
62 | c => CodeActions.PlugMethod(context.Document, declaration, c, diagnostic,
63 | LoadCurrentProject(context.Document.Project.FilePath).Value), diagnostic);
64 | break;
65 |
66 | case var id when id == DiagnosticMessages.PlugNameDoesNotMatch.Id:
67 | RegisterCodeFix(context, CodeActions.RenamePlugTitle,
68 | c => CodeActions.RenamePlug(context.Document, declaration, c, diagnostic), diagnostic);
69 | break;
70 |
71 | // case var id when id == DiagnosticMessages.MethodNotImplemented.Id:
72 | // RegisterCodeFix(context, CodeActions.RemoveMethodTitle, c => CodeActions.RemoveMethod(context.Document, declaration, c, diagnostic), diagnostic);
73 | // break;
74 | }
75 | }
76 | }
77 |
78 | private static void RegisterCodeFix(CodeFixContext context, string title,
79 | Func> createChangedSolution, Diagnostic diagnostic) =>
80 | context.RegisterCodeFix(
81 | CodeAction.Create(
82 | title,
83 | createChangedSolution,
84 | title),
85 | diagnostic);
86 | }
87 |
88 | internal static class CodeActions
89 | {
90 | public const string MakeStaticModifierTitle = "Make class static";
91 | public const string PlugMethodTitle = "Plug method";
92 | public const string RenamePlugTitle = "Rename plug";
93 | public const string RemoveExtraParametersTitle = "Remove extra parameters";
94 | // public const string RemoveMethodTitle = "Remove method";
95 |
96 | public static async Task RemoveExtraParameters(Document document, SyntaxNode declaration)
97 | {
98 | if (declaration is not MethodDeclarationSyntax methodDeclaration)
99 | {
100 | return document.Project.Solution;
101 | }
102 |
103 | DocumentEditor editor = await DocumentEditor.CreateAsync(document).ConfigureAwait(false);
104 | editor.ReplaceNode(methodDeclaration, methodDeclaration.WithParameterList(
105 | methodDeclaration.ParameterList.WithParameters(
106 | SyntaxFactory.SeparatedList(
107 | methodDeclaration.ParameterList.Parameters.Where(p => p.Identifier.Text != "aThis")))));
108 | return editor.GetChangedDocument().Project.Solution;
109 | }
110 |
111 | public static async Task RenamePlug(Document document, SyntaxNode declaration, CancellationToken c,
112 | Diagnostic diagnostic)
113 | {
114 | if (declaration is not ClassDeclarationSyntax classDeclaration)
115 | {
116 | return document.Project.Solution;
117 | }
118 |
119 | if (!diagnostic.Properties.TryGetValue("ExpectedName", out string? expectedName))
120 | {
121 | return document.Project.Solution;
122 | }
123 |
124 | DocumentEditor editor = await DocumentEditor.CreateAsync(document, c).ConfigureAwait(false);
125 |
126 | editor.SetName(classDeclaration, expectedName);
127 | return editor.GetChangedDocument().Project.Solution;
128 | }
129 |
130 | public static async Task PlugMethod(Document document, SyntaxNode declaration, CancellationToken c,
131 | Diagnostic diagnostic, ProjectInfo currentProject)
132 | {
133 | SyntaxNode? root = await document.GetSyntaxRootAsync(c).ConfigureAwait(false);
134 | if (root == null || !diagnostic.Properties.TryGetValue("MethodName", out string? methodName) ||
135 | !diagnostic.Properties.TryGetValue("ClassName", out string? className))
136 | return document.Project.Solution;
137 |
138 | DocumentEditor editor = await DocumentEditor.CreateAsync(document, c).ConfigureAwait(false);
139 | SyntaxGenerator syntaxGenerator = editor.Generator;
140 |
141 | SyntaxNode plugMethod = syntaxGenerator.AddAttributes(syntaxGenerator.MethodDeclaration(methodName,
142 | [
143 | syntaxGenerator.ParameterDeclaration("aThis", syntaxGenerator.TypeExpression(SpecialType.System_Object))
144 | ], null, null, Accessibility.Public, DeclarationModifiers.Static), syntaxGenerator.Attribute("PlugMember"));
145 |
146 | if (declaration is ClassDeclarationSyntax classDeclaration)
147 | return await AddMethodToPlug((classDeclaration, document), plugMethod);
148 |
149 | if (diagnostic.Properties.TryGetValue("PlugClass", out string? plugClassName))
150 | {
151 | (ClassDeclarationSyntax PlugClass, Document Document)? plugInfo =
152 | await GetPlugClass(plugClassName, document, currentProject);
153 | if (plugInfo == null)
154 | {
155 | return document.Project.Solution;
156 | }
157 |
158 | return await AddMethodToPlug(plugInfo.Value, plugMethod);
159 | }
160 |
161 | BaseNamespaceDeclarationSyntax? namespaceDeclaration = root
162 | .ChildNodes()
163 | .OfType()
164 | .FirstOrDefault();
165 |
166 | if (namespaceDeclaration == null)
167 | return document.Project.Solution;
168 |
169 | SyntaxNode newPlug = syntaxGenerator.ClassDeclaration($"{className}Impl", null, Accessibility.Public,
170 | DeclarationModifiers.Static, null, null, [plugMethod]);
171 |
172 | editor.AddMember(namespaceDeclaration, newPlug);
173 | return editor.GetChangedDocument().Project.Solution;
174 | }
175 |
176 | private static async Task<(ClassDeclarationSyntax PlugClass, Document Document)?> GetPlugClass(
177 | string? plugClassName, Document document, ProjectInfo currentProject)
178 | {
179 | if (plugClassName == null)
180 | {
181 | return null;
182 | }
183 |
184 | SyntaxNode? root = await document.GetSyntaxRootAsync().ConfigureAwait(false);
185 | if (root == null)
186 | {
187 | return null;
188 | }
189 |
190 | ClassDeclarationSyntax? plugClass = root.DescendantNodes()
191 | .OfType()
192 | .FirstOrDefault(c => c.Identifier.Text == plugClassName);
193 |
194 | if (plugClass != null)
195 | {
196 | return (plugClass, document);
197 | }
198 |
199 | foreach (string reference in currentProject.PlugReferences)
200 | {
201 | Project? project = document.Project.Solution.Projects.FirstOrDefault(p => p.OutputFilePath == reference);
202 | if (project == null)
203 | {
204 | continue;
205 | }
206 |
207 | Compilation? compilation = await project.GetCompilationAsync().ConfigureAwait(false);
208 | INamedTypeSymbol? symbol = compilation?.GetTypeByMetadataName(plugClassName);
209 |
210 | SyntaxReference? syntaxReference = symbol?.DeclaringSyntaxReferences.FirstOrDefault();
211 | if (syntaxReference == null)
212 | {
213 | continue;
214 | }
215 |
216 | SyntaxNode syntaxNode = await syntaxReference.GetSyntaxAsync().ConfigureAwait(false);
217 | return ((ClassDeclarationSyntax)syntaxNode, project.GetDocument(syntaxReference.SyntaxTree)!);
218 | }
219 |
220 | return null;
221 | }
222 |
223 | private static async Task AddMethodToPlug((ClassDeclarationSyntax PlugClass, Document Document) plugInfo,
224 | SyntaxNode plugMethod)
225 | {
226 | DocumentEditor editor = await DocumentEditor.CreateAsync(plugInfo.Document).ConfigureAwait(false);
227 | editor.AddMember(plugInfo.PlugClass, plugMethod);
228 | return editor.GetChangedDocument().Project.Solution;
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer.Package/Cosmos.Patcher.Analyzer.Package.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | true
5 | false
6 | true
7 |
8 |
9 | Cosmos.Patcher.Analyzer
10 | 1.0.0.0
11 | https://github.com/valentinbreiz/nativeaot-patcher/blob/main/LICENSE
12 | https://github.com/valentinbreiz/nativeaot-patcher.git
13 |
14 | https://github.com/valentinbreiz/nativeaot-patcher.git
15 | false
16 | Cosmos.Patcher.Analyzer
17 | Release
18 | Copyright
19 | Cosmos.Patcher.Analyzer, analyzers
20 | true
21 | true
22 | $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer/Cosmos.Patcher.Analyzer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | Cosmos.Patcher.Analyzer
5 | Library
6 | true
7 |
8 | $(MSBuildProjectFile)
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 | all
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer/DiagnosticMessages.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using Microsoft.CodeAnalysis;
3 |
4 | namespace Cosmos.Patcher.Analyzer;
5 |
6 | public sealed class DiagnosticMessages
7 | {
8 | public static readonly DiagnosticDescriptor TypeNotFound = new(
9 | "NAOT0001",
10 | "Type Not Found",
11 | "The specified type '{0}' could not be located",
12 | "Naming",
13 | DiagnosticSeverity.Error,
14 | true,
15 | "Ensure that the type name is correct and that the type is accessible."
16 | );
17 |
18 | public static readonly DiagnosticDescriptor MethodNeedsPlug = new(
19 | "NAOT0002",
20 | "Method Needs Plug",
21 | "Method '{0}' in class '{1}' requires a plug",
22 | "Usage",
23 | DiagnosticSeverity.Error,
24 | true,
25 | "Ensure that the method has a corresponding plug. See http://www.gocosmos.org/docs/plugs/missing/ for more information."
26 | );
27 |
28 | public static readonly DiagnosticDescriptor PlugNameDoesNotMatch = new(
29 | "NAOT0004",
30 | "Plug Name Does Not Match",
31 | "Plug '{0}' should be renamed to '{1}'",
32 | "Naming",
33 | DiagnosticSeverity.Info,
34 | true,
35 | "Ensure that the plug name matches the plugged class name."
36 | );
37 |
38 | public static readonly DiagnosticDescriptor MethodNotImplemented = new(
39 | "NAOT0005",
40 | "Method Not Implemented",
41 | "Method '{0}' does not exist in '{1}'",
42 | "Usage",
43 | DiagnosticSeverity.Info,
44 | true,
45 | "Ensure that the method name is correct and that the method exists."
46 | );
47 |
48 |
49 | public static readonly DiagnosticDescriptor StaticConstructorTooManyParams = new(
50 | "NAOT0006",
51 | "Static Constructor Has Too Many Parameters",
52 | "The static constructor '{0}' contains too many parameters. A static constructor must not have more than one parameter.",
53 | "Usage",
54 | DiagnosticSeverity.Error,
55 | true,
56 | "A static constructor should have at most one parameter."
57 | );
58 |
59 |
60 | public static ImmutableArray SupportedDiagnostics => ImmutableArray.Create(TypeNotFound,
61 | MethodNeedsPlug, PlugNameDoesNotMatch, MethodNotImplemented, StaticConstructorTooManyParams);
62 | }
63 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer/Extensions/AttributeExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp.Syntax;
3 | using Microsoft.CodeAnalysis.Diagnostics;
4 |
5 | namespace Cosmos.Patcher.Analyzer.Extensions;
6 |
7 | public static class AttributeExtensions
8 | {
9 | public static T? GetAttributeValue(this AttributeSyntax attribute, object indexOrString,
10 | SyntaxNodeAnalysisContext context)
11 | {
12 | ExpressionSyntax? expression = GetArgumentExpression(attribute, indexOrString);
13 | return expression != null ? GetValueFromExpression(expression, context) : default;
14 | }
15 |
16 | public static bool GetAttributeValue(this AttributeSyntax attribute, object indexOrString,
17 | SyntaxNodeAnalysisContext context, out T? value)
18 | {
19 | value = GetAttributeValue(attribute, indexOrString, context);
20 | return value != null && (value is not string str || !string.IsNullOrEmpty(str));
21 | }
22 |
23 | public static T? GetAttributeValue(this AttributeData attribute, object indexOrString)
24 | {
25 | TypedConstant argument = GetConstructorArgument(attribute, indexOrString);
26 | return argument.Kind == TypedConstantKind.Error ? default : ConvertArgumentValue(argument);
27 | }
28 |
29 | private static T? GetValueFromExpression(ExpressionSyntax expression, SyntaxNodeAnalysisContext context) =>
30 | expression switch
31 | {
32 | MemberAccessExpressionSyntax { Name: { } name } when typeof(T).IsEnum
33 | => ParseEnum(name.ToString()),
34 | LiteralExpressionSyntax literal
35 | => (T?)literal.Token.Value,
36 | TypeOfExpressionSyntax typeOf
37 | => (T?)(object?)GetTypeFromTypeOf(typeOf, context),
38 | _ => default
39 | };
40 |
41 | private static string? GetTypeFromTypeOf(TypeOfExpressionSyntax typeOf, SyntaxNodeAnalysisContext context)
42 | {
43 | ITypeSymbol? symbol = context.SemanticModel.GetSymbolInfo(typeOf.Type).Symbol as ITypeSymbol;
44 | return symbol is not null
45 | ? symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))
46 | : "Unknown";
47 | }
48 |
49 | private static T? ParseEnum(string name)
50 | {
51 | T value;
52 | return (value = (T)Enum.Parse(typeof(T), name)) != null ? value : default;
53 | }
54 |
55 | private static TypedConstant GetConstructorArgument(AttributeData attribute, object indexOrString) =>
56 | indexOrString switch
57 | {
58 | int index when index >= 0 && index < attribute.ConstructorArguments.Length
59 | => attribute.ConstructorArguments[index],
60 | string name
61 | => attribute.NamedArguments.FirstOrDefault(kvp =>
62 | kvp.Key.Equals(name, StringComparison.OrdinalIgnoreCase)).Value,
63 | _ => default
64 | };
65 |
66 | private static ExpressionSyntax? GetArgumentExpression(AttributeSyntax attribute, object indexOrString) =>
67 | indexOrString switch
68 | {
69 | int index when attribute.ArgumentList?.Arguments.Count > index
70 | => attribute.ArgumentList.Arguments[index].Expression,
71 | string name => attribute.ArgumentList?.Arguments
72 | .FirstOrDefault(a => (a.NameEquals?.Name ?? a.NameColon?.Name)?.ToString() == name)?
73 | .Expression,
74 | _ => null
75 | };
76 |
77 | private static T? ConvertArgumentValue(TypedConstant argument)
78 | {
79 | if (argument.Value is T value)
80 | {
81 | return value;
82 | }
83 |
84 | return typeof(T).IsEnum && argument.Value != null ? ConvertEnum(argument.Value)
85 | : typeof(T).Name == "Type" && argument.Value is ITypeSymbol type ? (T?)type
86 | : default;
87 | }
88 |
89 | private static T? ConvertEnum(object value)
90 | => (T?)Enum.ToObject(typeof(T), value);
91 |
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer/Extensions/EnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Cosmos.Patcher.Analyzer.Extensions;
2 |
3 | public static class EnumerableExtensions
4 | {
5 |
6 | public static bool Any(this IEnumerable enumerable, Func predicate, out T value)
7 | {
8 | using IEnumerator enumerator = enumerable.GetEnumerator();
9 | while (enumerator.MoveNext())
10 | {
11 | if (!predicate(enumerator.Current)) continue;
12 | value = enumerator.Current;
13 | return true;
14 | }
15 |
16 | value = default;
17 | return false;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer/Extensions/SymbolExtensions.cs:
--------------------------------------------------------------------------------
1 | // This code is licensed under MIT license (see LICENSE for details)
2 |
3 | using Microsoft.CodeAnalysis;
4 |
5 | namespace Cosmos.Patcher.Analyzer.Extensions;
6 |
7 | public static class SymbolExtensions
8 | {
9 | public static bool HasAttribute(this ISymbol symbol, params string[] attributeNames) => symbol.GetAttributes().Any(a => attributeNames.Contains(a?.AttributeClass?.Name));
10 | }
11 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer/Extensions/SyntaxNodeExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp.Syntax;
3 | using Microsoft.CodeAnalysis.Text;
4 |
5 | namespace Cosmos.Patcher.Analyzer.Extensions;
6 |
7 | public static class SyntaxNodeExtensions
8 | {
9 | public static bool TryGetMemberByName(this ClassDeclarationSyntax declaration, string name, out T? member)
10 | where T : MemberDeclarationSyntax
11 | {
12 | foreach (MemberDeclarationSyntax memberDeclarationSyntax in declaration.Members)
13 | {
14 | if (memberDeclarationSyntax is not T memberDeclaration)
15 | continue;
16 |
17 | if (memberDeclaration.GetName() != name)
18 | continue;
19 |
20 | member = memberDeclaration;
21 | return true;
22 | }
23 | member = null;
24 | return false;
25 | }
26 |
27 |
28 | public static string? GetName(this MemberDeclarationSyntax member) => member switch
29 | {
30 | MethodDeclarationSyntax method => method.Identifier.ValueText,
31 | PropertyDeclarationSyntax property => property.Identifier.ValueText,
32 | FieldDeclarationSyntax field => field.Declaration.Variables
33 | .FirstOrDefault()?.Identifier.ValueText,
34 | _ => null
35 | };
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Analyzer/Models/PlugInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Cosmos.Patcher.Analyzer.Models;
4 |
5 | public record PlugInfo(bool IsExternal, INamedTypeSymbol PlugSymbol);
6 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Build/Cosmos.Patcher.Build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | Library
5 | True
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 | all
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Build/Tasks/PatcherTask.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 | using Microsoft.Build.Utilities;
3 |
4 | namespace Cosmos.Patcher.Build.Tasks;
5 |
6 | public sealed class PatcherTask : ToolTask
7 | {
8 |
9 | [Required] public string? TargetAssembly { get; set; }
10 |
11 | [Required] public required ITaskItem[] PlugsReferences { get; set; }
12 |
13 | [Required] public required string OutputPath { get; set; }
14 |
15 | protected override string GenerateFullPathToTool() => ToolName;
16 |
17 | protected override string GenerateCommandLineCommands()
18 | {
19 | CommandLineBuilder builder = new();
20 |
21 | // Add main command
22 | builder.AppendSwitch("patch");
23 |
24 | // Add --target arg
25 | builder.AppendSwitch("--target");
26 | builder.AppendFileNameIfNotNull(TargetAssembly);
27 |
28 |
29 | // Add plugs
30 | builder.AppendSwitch("--plugs");
31 | foreach (ITaskItem plug in PlugsReferences)
32 | {
33 | builder.AppendFileNameIfNotNull(plug.ItemSpec);
34 | }
35 |
36 | // Add --output arg
37 | builder.AppendSwitch("--output");
38 | builder.AppendFileNameIfNotNull(OutputPath);
39 |
40 | return builder.ToString();
41 | }
42 |
43 | public override bool Execute()
44 | {
45 | Log.LogMessage(MessageImportance.High, "Running Liquip.Patcher...");
46 | Log.LogMessage(MessageImportance.High, $"Platform: {Environment.OSVersion.Platform}");
47 | Log.LogMessage(MessageImportance.High, $"Target Assembly: {TargetAssembly}");
48 | Log.LogMessage(MessageImportance.High, $"Output Path: {OutputPath}");
49 | Log.LogMessage(MessageImportance.High,
50 | $"Plugs References: {string.Join(", ", PlugsReferences.Select(p => p.ItemSpec))}");
51 | Log.LogMessage(MessageImportance.High, $"Command: {GenerateCommandLineCommands()}");
52 |
53 | return base.Execute();
54 | }
55 |
56 | protected override string ToolName => "Cosmos.Patcher";
57 | }
58 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Build/build/Cosmos.Patcher.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $(MSBuildThisFileDirectory)\..\lib\netstandard2.0\Cosmos.Patcher.Build.dll
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher.Build/build/Cosmos.Patcher.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | true
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | $(IntermediateOutputPath)/cosmos/ref
41 | $(PatcherOutputPath)/$(AssemblyName)_patched.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher/Cosmos.Patcher.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0
4 | Exe
5 |
6 | true
7 | true
8 | True
9 | cosmos.patcher
10 | 1.0.0
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher/Extensions/MethodBodyEx.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Mono.Cecil;
3 | using Mono.Cecil.Cil;
4 | using MonoMod.Utils;
5 |
6 | namespace Cosmos.Patcher.Extensions;
7 |
8 | public static class MethodBodyEx
9 | {
10 | [return: NotNullIfNotNull("bo")]
11 | public static MethodDefinition? ReplaceMethodWithJump(this MethodDefinition? bo, MethodDefinition m)
12 | {
13 | Helpers.ThrowIfArgumentNull(m);
14 |
15 | if (bo == null)
16 | {
17 | return null;
18 | }
19 |
20 | MethodBody? bc = new(m);
21 |
22 | if (!bo.HasParameters)
23 | {
24 | Instruction? jump = Instruction.Create(OpCodes.Call, m);
25 | bc.Instructions.Add(jump);
26 | bc.Instructions.Add(Instruction.Create(OpCodes.Ret));
27 | }
28 |
29 |
30 | bo.Body = bc;
31 |
32 | return bo;
33 | }
34 |
35 | ///
36 | /// replace method body
37 | ///
38 | ///
39 | ///
40 | ///
41 | ///
42 | [return: NotNullIfNotNull("newBody")]
43 | public static MethodBody? ReplaceMethodBody(this MethodBody? newBody, MethodDefinition m)
44 | {
45 | Helpers.ThrowIfArgumentNull(m);
46 |
47 | if (newBody == null)
48 | {
49 | return null;
50 | }
51 |
52 | MethodBody? bc = new(m);
53 | bc.MaxStackSize = newBody.MaxStackSize;
54 | bc.InitLocals = newBody.InitLocals;
55 | bc.LocalVarToken = newBody.LocalVarToken;
56 |
57 | bc.Instructions.AddRange(newBody.Instructions.Select(o =>
58 | {
59 | Instruction? c = Instruction.Create(OpCodes.Nop);
60 | c.OpCode = o.OpCode;
61 | c.Operand = o.Operand;
62 | c.Offset = o.Offset;
63 | return c;
64 | }));
65 |
66 | foreach (Instruction? instruction in bc.Instructions)
67 | {
68 | if (instruction.Operand is Instruction target)
69 | {
70 | instruction.Operand = bc.Instructions[newBody.Instructions.IndexOf(target)];
71 | }
72 | else if (instruction.Operand is Instruction[] targets)
73 | {
74 | instruction.Operand = targets
75 | .Select(i => bc.Instructions[newBody.Instructions.IndexOf(i)])
76 | .ToArray();
77 | }
78 | }
79 |
80 | bc.ExceptionHandlers.AddRange(newBody.ExceptionHandlers.Select(o =>
81 | {
82 | ExceptionHandler? c = new(o.HandlerType);
83 | c.TryStart = o.TryStart == null ? null : bc.Instructions[newBody.Instructions.IndexOf(o.TryStart)];
84 | c.TryEnd = o.TryEnd == null ? null : bc.Instructions[newBody.Instructions.IndexOf(o.TryEnd)];
85 | c.FilterStart = o.FilterStart == null ? null : bc.Instructions[newBody.Instructions.IndexOf(o.FilterStart)];
86 | c.HandlerStart = o.HandlerStart == null
87 | ? null
88 | : bc.Instructions[newBody.Instructions.IndexOf(o.HandlerStart)];
89 | c.HandlerEnd = o.HandlerEnd == null ? null : bc.Instructions[newBody.Instructions.IndexOf(o.HandlerEnd)];
90 | c.CatchType = o.CatchType;
91 | return c;
92 | }));
93 |
94 | bc.Variables.AddRange(newBody.Variables.Select(o =>
95 | {
96 | VariableDefinition? c = new(o.VariableType);
97 | return c;
98 | }));
99 |
100 | Instruction ResolveInstrOff(int off)
101 | {
102 | // Can't check cloned instruction offsets directly, as those can change for some reason
103 | for (int i = 0; i < newBody.Instructions.Count; i++)
104 | {
105 | if (newBody.Instructions[i].Offset == off)
106 | {
107 | return bc.Instructions[i];
108 | }
109 | }
110 |
111 | throw new ArgumentException($"Invalid instruction offset {off}");
112 | }
113 |
114 | m.CustomDebugInformations.AddRange(newBody.Method.CustomDebugInformations.Select(o =>
115 | {
116 | if (o is AsyncMethodBodyDebugInformation ao)
117 | {
118 | AsyncMethodBodyDebugInformation? c = new();
119 | if (ao.CatchHandler.Offset >= 0)
120 | {
121 | c.CatchHandler = ao.CatchHandler.IsEndOfMethod
122 | ? new InstructionOffset()
123 | : new InstructionOffset(ResolveInstrOff(ao.CatchHandler.Offset));
124 | }
125 |
126 | c.Yields.AddRange(ao.Yields.Select(off =>
127 | off.IsEndOfMethod ? new InstructionOffset() : new InstructionOffset(ResolveInstrOff(off.Offset))));
128 | c.Resumes.AddRange(ao.Resumes.Select(off =>
129 | off.IsEndOfMethod ? new InstructionOffset() : new InstructionOffset(ResolveInstrOff(off.Offset))));
130 | c.ResumeMethods.AddRange(ao.ResumeMethods);
131 | return c;
132 | }
133 |
134 | if (o is StateMachineScopeDebugInformation so)
135 | {
136 | StateMachineScopeDebugInformation? c = new();
137 | c.Scopes.AddRange(so.Scopes.Select(s => new StateMachineScope(ResolveInstrOff(s.Start.Offset),
138 | s.End.IsEndOfMethod ? null : ResolveInstrOff(s.End.Offset))));
139 | return c;
140 | }
141 |
142 | return o;
143 | }));
144 |
145 | m.DebugInformation.SequencePoints.AddRange(newBody.Method.DebugInformation.SequencePoints.Select(o =>
146 | {
147 | SequencePoint? c = new(ResolveInstrOff(o.Offset), o.Document);
148 | c.StartLine = o.StartLine;
149 | c.StartColumn = o.StartColumn;
150 | c.EndLine = o.EndLine;
151 | c.EndColumn = o.EndColumn;
152 | return c;
153 | }));
154 |
155 | return bc;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/Cosmos.Patcher/PatchCommand.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using Mono.Cecil;
3 | using Spectre.Console.Cli;
4 |
5 | namespace Cosmos.Patcher;
6 |
7 | public sealed class PatchCommand : Command
8 | {
9 | public class Settings : CommandSettings
10 | {
11 | [CommandOption("--target ")]
12 | [Description("Path to the target assembly.")]
13 | public string TargetAssembly { get; set; } = null!;
14 |
15 | [CommandOption("--plugs ")]
16 | [Description("Paths to plug assemblies.")]
17 | public string[] PlugsReferences { get; set; } = [];
18 |
19 | [CommandOption("--output