Hello there, ☺️
This issue is related to the following previous issues (and there are more closed):
Environment.ProcessPath and Environment.GetCommandLineArgs()[0] are behaving quite differently (See the follow parts for more details):
- between them
- between platforms
- between regular dotnet built app and NativeAOT
- When we access the exe through a symbolic link
This is quite problematic when trying to build apps that expect to recover the C/C++ equivalent of argv[0] which should give exactly what was typed on the command line (e.g if it is a symbolic link, we should keep its name), and not something else.
Reproducible steps
Windows - .NET 8.0
Powershell with admin rights (for the symbolic link):
dotnet new console -n HelloWorld
cd HelloWorld
Write-Output "Console.WriteLine($""Environment.ProcessPath: {Environment.ProcessPath}"");" > Program.cs
Write-Output "Console.WriteLine($""Environment.GetCommandLineArgs()[0]: {Environment.GetCommandLineArgs()[0]}"");" >> Program.cs
dotnet build -c Release
cd .\bin\Release\net8.0
Then we create a symbolic link from special_hello.exe to HelloWorld.exe:
New-Item -ItemType SymbolicLink -Path special_hello.exe -Value HelloWorld.exe
.\special_hello.exe
Outputs:
Environment.ProcessPath: C:\code\HelloWorld\bin\Release\net8.0\special_hello.exe
Environment.GetCommandLineArgs()[0]: C:\code\HelloWorld\bin\Release\net8.0\HelloWorld.dll
Windows - NativeAOT .NET 8.0
Similar steps then before, but with dotnet publish -c Release -p:PublishAot=true
cd .\bin\Release\net8.0\win-x64\publish
New-Item -ItemType SymbolicLink -Path special_hello.exe -Value HelloWorld.exe
.\special_hello.exe
Outputs:
Environment.ProcessPath: C:\code\HelloWorld\bin\Release\net8.0\win-x64\publish\special_hello.exe
Environment.GetCommandLineArgs()[0]: C:\code\HelloWorld\bin\Release\net8.0\win-x64\publish\special_hello.exe
MacOS/Ubuntu - .NET 8.0
cd ./bin/Release/net8.0
ln -s HelloWorld special_hello
./special_hello
Outputs:
Environment.ProcessPath: /home/xoofx/code/HelloWorld/bin/Release/net8.0/HelloWorld
Environment.GetCommandLineArgs()[0]: /home/xoofx/code/HelloWorld/bin/Release/net8.0/HelloWorld.dll
MacOS/Ubuntu - NativeAOT .NET 8.0
cd ./bin/Release/net8.0/linux-x64/publish
ln -s HelloWorld special_hello
./special_hello
Outputs:
Environment.ProcessPath: /home/xoofx/code/HelloWorld/bin/Release/net8.0/linux-x64/publish/HelloWorld
Environment.GetCommandLineArgs()[0]: ./special_hello
Expected behavior
Environment.GetCommandLineArgs()[0]
This value should always be the strict equivalent of argv[0] in a C/C++ program (and MacOS/Ubuntu - NativeAOT .NET 8.0 is the only one to respect it from the above)
In particular:
- It should not transform to an absolute path or resolve symbolic link
- It should not give a dll whether we are running through corehost from a regular
dotnet build
Environment.ProcessPath
From the above it is unclear if:
- It should give an absolute path to the exe name but not following symbolic link? (as on
Windows - NativeAOT .NET 8.0) e.g C:\code\HelloWorld\bin\Release\net8.0\win-x64\publish\special_hello.exe
- It should give an absolute path to the fully resolve exe name following symbolic link? (as on MacOS/Ubuntu) e.g
/home/xoofx/code/HelloWorld/bin/Release/net8.0/HelloWorld
Implementation problems
I tried to fix it but the problem is that we are not able to recover argv[0] from coreclr_execute_assembly, and I end up hacking argv to actually have argv[0] in it and putting 1 on bit 0 on the pointer to understand behind that it is a special argv, which I conceded is super ugly 😅 (Thinking about it I could hack it more easily by using argc as a negative number to convery that it includes argv[0], but it still ugly 😁 )
int coreclr_execute_assembly(
void* hostHandle,
unsigned int domainId,
int argc,
const char** argv,
const char* managedAssemblyPath,
unsigned int* exitCode)
and while coreclr_initialize receive an exePath (which is not argv[0]), it is processing it to create a commandline string and things get more convoluted here (and it's not used by Environment.GetCommandLineArgs()[0])
int coreclr_initialize(
const char* exePath,
const char* appDomainFriendlyName,
int propertyCount,
const char** propertyKeys,
const char** propertyValues,
void** hostHandle,
unsigned int* domainId)
I assume that it is not possible to change the signature of these functions?
I would be happy to make a PR, but some guidance regarding coreclr_execute_assembly/coreclr_initialize on how to propagate argv[0] would be appreciated. (If I can add a new arg, that should be relatively easy to fix - but I would have change also e.g CorHost2::ExecuteAssembly)
Thanks! ☺️
cc: @jkotas @MichalStrehovsky @agocke
Hello there,☺️
This issue is related to the following previous issues (and there are more closed):
Environment.ProcessPathandEnvironment.GetCommandLineArgs()[0]are behaving quite differently (See the follow parts for more details):This is quite problematic when trying to build apps that expect to recover the C/C++ equivalent of
argv[0]which should give exactly what was typed on the command line (e.g if it is a symbolic link, we should keep its name), and not something else.Reproducible steps
Windows - .NET 8.0
Powershell with admin rights (for the symbolic link):
Then we create a symbolic link from
special_hello.exetoHelloWorld.exe:Outputs:
Windows - NativeAOT .NET 8.0
Similar steps then before, but with
dotnet publish -c Release -p:PublishAot=trueOutputs:
MacOS/Ubuntu - .NET 8.0
cd ./bin/Release/net8.0 ln -s HelloWorld special_hello ./special_helloOutputs:
MacOS/Ubuntu - NativeAOT .NET 8.0
cd ./bin/Release/net8.0/linux-x64/publish ln -s HelloWorld special_hello ./special_helloOutputs:
Expected behavior
Environment.GetCommandLineArgs()[0]This value should always be the strict equivalent of
argv[0]in a C/C++ program (andMacOS/Ubuntu - NativeAOT .NET 8.0is the only one to respect it from the above)In particular:
dotnet buildEnvironment.ProcessPathFrom the above it is unclear if:
Windows - NativeAOT .NET 8.0) e.gC:\code\HelloWorld\bin\Release\net8.0\win-x64\publish\special_hello.exe/home/xoofx/code/HelloWorld/bin/Release/net8.0/HelloWorldImplementation problems
I tried to fix it but the problem is that we are not able to recover
argv[0]fromcoreclr_execute_assembly, and I end up hackingargvto actually haveargv[0]in it and putting 1 on bit 0 on the pointer to understand behind that it is a specialargv, which I conceded is super ugly 😅 (Thinking about it I could hack it more easily by using argc as a negative number to convery that it includes argv[0], but it still ugly 😁 )and while
coreclr_initializereceive anexePath(which is notargv[0]), it is processing it to create a commandline string and things get more convoluted here (and it's not used byEnvironment.GetCommandLineArgs()[0])I assume that it is not possible to change the signature of these functions?
I would be happy to make a PR, but some guidance regarding
coreclr_execute_assembly/coreclr_initializeon how to propagateargv[0]would be appreciated. (If I can add a new arg, that should be relatively easy to fix - but I would have change also e.gCorHost2::ExecuteAssembly)Thanks!☺️
cc: @jkotas @MichalStrehovsky @agocke