dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.91k stars 4.63k forks source link

ERROR - Failed to start the runtime #9417

Closed benjaml closed 4 years ago

benjaml commented 6 years ago

Hello,

I tried using the source to build coreclr.dll, and I got some issues. First of all I got this error

ERROR - Failed to start the runtime. Error code:80004005

Here is a sample of my code


HRESULT hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&runtimeHost);

hr = runtimeHost->SetStartupFlags(
    static_cast<STARTUP_FLAGS>(
        STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN
        )
);

hr = runtimeHost->Start(); // the error goes from here

As I'm very new to dotnet clr, I don't really know if it is the right way to start the runtimeHost. Feel free to ask for other informations as I don't really know what could be relevant to solve my problem.

Thanks for the help

janvorli commented 6 years ago

Unless you have some very specific reason for using the COM hosting API, I would recommend using the new simple hosting API that works across all platforms. The API is declared in the following header: https://github.com/dotnet/coreclr/blob/5e138a6d69215546e9bf4b38ec9bdd1c436fca5b/src/coreclr/hosts/inc/coreclrhost.h. Also, you need to properly configure various paths that the runtime needs so that e.g. the managed assemblies can be located. You can see an example of how to use the new hosting api e.g. in the corerun tool sources for Unix: https://github.com/dotnet/coreclr/blob/9e3ef8eb56d5027ed96348e89911afacbccee9f0/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp And the implementation of the new API that is actually done using the COM hosting API is here: https://github.com/dotnet/coreclr/blob/5e138a6d69215546e9bf4b38ec9bdd1c436fca5b/src/dlls/mscoree/unixinterface.cpp

benjaml commented 6 years ago

So from what I've understand, coreclr_initialize create clr and appDomain, this means that I can only have one appDomain per clr, or am I wrong ?

janvorli commented 6 years ago

Yes, coreclr doesn't support multiple domains.

janvorli commented 6 years ago

No matter which hosting API you use, to be precise.

benjaml commented 6 years ago

initializeCoreCLR need the path to an exe to be executed, but I don't know which

mattwarren commented 6 years ago

initializeCoreCLR need the path to an exe to be executed, but I don't know which

That's just the path to the exe that you actually want to run, i.e the .NET executable you want the host to execute. I guess you could just write a simple 'Hello World' app to try things out.

BTW there's some docs that might help with this, they mostly cover the COM API, but they do have a section on the new x-plat API, see 'About Hosting .NET Core on Unix'

mattwarren commented 6 years ago

@janvorli the docs on Hosting .NET Core mostly discuss the COM Api's and only have a small section 'About Hosting .NET Core on Unix'.

Should they talk about the new x-plat APIs more prominently, as per your guidance above:

Unless you have some very specific reason for using the COM hosting API, I would recommend using the new simple hosting API that works across all platforms

janvorli commented 6 years ago

@mattwarren, @benjaml the initializeCoreCLR's path to exe is something different. It is a path to the current host executable.

janvorli commented 6 years ago

@mattwarren I agree that the doc should be updated. Moreover, it is missing several relatively recent additions to the API.

benjaml commented 6 years ago

I don't have a .NET executable, I just want to run a .NET dll, can I put the dll path instead of the executable ?

benjaml commented 6 years ago

I got this while trying to use initializeCoreCLR

Exception thrown at 0x00007FFA3DE23FB8 (KernelBase.dll) in ConsoleApplication1.exe: 0x04242420 (parameters: 0x0000000031415927, 0x00007FF9F6AA0000, 0x00000049BAEF2C30).

but it returns a like it is a succes, so I don't really know if it worked or not, I try to create delegates after this and they all failed.

benjaml commented 6 years ago

I'm using windows, is there any example of how to use the new hosting api for windows ?

janvorli commented 6 years ago

The usage should be pretty much the same as in the https://github.com/dotnet/coreclr/blob/9e3ef8eb56d5027ed96348e89911afacbccee9f0/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp I've referenced above. You need to correctly pass it all the parameters and at least these properties: https://github.com/dotnet/coreclr/blob/9e3ef8eb56d5027ed96348e89911afacbccee9f0/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp#L388-L391

benjaml commented 6 years ago

I'm pretty sure they are correct, but it's the exePath of coreclr_initialize that i'm not sure about

as I got no .net executable but only a dll, I need to pass this dll in APP_PATHS (am I right ?)

janvorli commented 6 years ago

As I've mentioned above, the currentExeAbsolutePath is the path to your native executable that calls the API (that's what is call the "host").

benjaml commented 6 years ago

ok so this should be ok, then I don't know why I got Exception thrown at 0x00007FFA3DE23FB8 (KernelBase.dll) in ConsoleApplication1.exe: 0x04242420 (parameters: 0x0000000031415927, 0x00007FF9F6AA0000, 0x00000049BAEF2C30). when I call initializeCoreCLR, even if it throws an exception it also give the return of a success, so I'm not sure of how I should interpret it

janvorli commented 6 years ago

@benjaml could you share your current piece of code that is trying to initialize CoreCLR? I may be able to spot something in it.

benjaml commented 6 years ago
const char *currentExeAbsolutePath= "D:\\Workspace\\KEA\\InteropSample\\CppExecutable\\x64\\Debug\\ConsoleApplication1.exe";
const char* clrFilesAbsolutePath= "D:\\Workspace\\clr\\coreclr\\bin\\Product\\Windows_NT.x64.Debug";
const char* managedAssemblyAbsolutePath= "D:\\Workspace\\KEA\\InteropSample\\CSharpDll\\HostedApplication\\bin\\Debug\\netcoreapp2.0\\HostedApplication.dll"; // this is my c# dll that I want to use in c++

int tpaSize = 100 * MAX_PATH; // Starting size for our TPA (Trusted Platform Assemblies) list
char* trustedPlatformAssemblies = new char[tpaSize];
trustedPlatformAssemblies[0] = L'\0';

// Extensions to probe for when finding TPA list files
char *tpaExtensions[] = {
    "*.dll",
    "*.exe",
    "*.winmd"
};

// Probe next to CoreCLR.dll for any files matching the extensions from tpaExtensions and
// add them to the TPA list. In a real host, this would likely be extracted into a separate function
// and perhaps also run on other directories of interest.
for (int i = 0; i < _countof(tpaExtensions); i++)
{
    // Construct the file name search pattern
    char searchPath[MAX_PATH];
    strcpy_s(searchPath, MAX_PATH, clrFilesAbsolutePath);
    strcat_s(searchPath, MAX_PATH, "\\");
    strcat_s(searchPath, MAX_PATH, tpaExtensions[i]);

    // Find files matching the search pattern
    WIN32_FIND_DATAA findData;
    HANDLE fileHandle = FindFirstFileA(searchPath, &findData);

    if (fileHandle != INVALID_HANDLE_VALUE)
    {
        do
        {
            // Construct the full path of the trusted assembly
            char pathToAdd[MAX_PATH];
            strcpy_s(pathToAdd, MAX_PATH, clrFilesAbsolutePath);
            strcat_s(pathToAdd, MAX_PATH, "\\");
            strcat_s(pathToAdd, MAX_PATH, findData.cFileName);

            // Check to see if TPA list needs expanded
            if (strlen(pathToAdd) + (3) + strlen(trustedPlatformAssemblies) >= tpaSize)
            {
                // Expand, if needed
                tpaSize *= 2;
                char* newTPAList = new char[tpaSize];
                strcpy_s(newTPAList, tpaSize, trustedPlatformAssemblies);
                trustedPlatformAssemblies = newTPAList;
            }

            // Add the assembly to the list and delimited with a semi-colon
            strcat_s(trustedPlatformAssemblies, tpaSize, pathToAdd);
            strcat_s(trustedPlatformAssemblies, tpaSize, ";");

            // Note that the CLR does not guarantee which assembly will be loaded if an                      assembly
            // is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll
            // extensions. Therefore, a real host should probably add items to the list in priority order and only
            // add a file if it's not already present on the list.
            //
            // For this simple sample, though, and because we're only loading TPA assemblies from a single path,
            // we can ignore that complication.
        } while (FindNextFileA(fileHandle, &findData));
        FindClose(fileHandle);
    }
}

// APP_PATHS
// App paths are directories to probe in for assemblies which are not one of the well-known Framework assemblies
// included in the TPA list.
//
// For this simple sample, we just include the directory the target application is in.
// More complex hosts may want to also check the current working directory or other
// locations known to contain application assets.
char appPaths[MAX_PATH * 50];

// Just use the targetApp provided by the user and remove the file name
strcpy_s(appPaths, managedAssemblyAbsolutePath);

//APP_NI_PATHS
// App (NI) paths are the paths that will be probed for native images not found on the TPA list. 
// It will typically be similar to the app paths.
// For this sample, we probe next to the app and in a hypothetical directory of the same name with 'NI' suffixed to the end.
char appNiPaths[MAX_PATH * 50];
strcpy_s(appNiPaths, clrFilesAbsolutePath);

// NATIVE_DLL_SEARCH_DIRECTORIES
// Native dll search directories are paths that the runtime will probe for native DLLs called via PInvoke
char nativeDllSearchDirectories[MAX_PATH * 50];
strcpy_s(nativeDllSearchDirectories, appPaths);
strcat_s(nativeDllSearchDirectories, MAX_PATH * 50, ";");
strcat_s(nativeDllSearchDirectories, MAX_PATH * 50, clrFilesAbsolutePath);

HMODULE coreclrLib = LoadLibraryA(coreClrDllPath.c_str());
if (coreclrLib == nullptr)
{
    fprintf(stderr, "dlopen failed to open the libcoreclr.so with error\n");
    return -1;
}
coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)GetProcAddress(coreclrLib, "coreclr_initialize");
coreclr_create_delegate_ptr createDelegateCoreCLR = (coreclr_create_delegate_ptr)GetProcAddress(coreclrLib, "coreclr_create_delegate");
coreclr_shutdown_2_ptr shutdownCoreCLR = (coreclr_shutdown_2_ptr)GetProcAddress(coreclrLib, "coreclr_shutdown_2");

if (initializeCoreCLR == nullptr)
{
    fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n");
    return -1;
}
else if (createDelegateCoreCLR == nullptr)
{
    fprintf(stderr, "Function coreclr_create_delegate not found in the libcoreclr.so\n");
    return -1;
}
else if (shutdownCoreCLR == nullptr)
{
    fprintf(stderr, "Function coreclr_shutdown_2 not found in the libcoreclr.so\n");
    return -1;
}
// Check whether we are enabling server GC (off by default)
const char* useServerGc = GetEnvValueBoolean(serverGcVar);

// Check Globalization Invariant mode (false by default)
const char* globalizationInvariant = GetEnvValueBoolean(globalizationInvariantVar);

// Allowed property names:
// APPBASE
// - The base path of the application from which the exe and other assemblies will be loaded
//
// TRUSTED_PLATFORM_ASSEMBLIES
// - The list of complete paths to each of the fully trusted assemblies
//
// APP_PATHS
// - The list of paths which will be probed by the assembly loader
//
// APP_NI_PATHS
// - The list of additional paths that the assembly loader will probe for ngen images
//
// NATIVE_DLL_SEARCH_DIRECTORIES
// - The list of paths that will be probed for native DLLs called by PInvoke
//
const char *propertyKeys[] = {
    "TRUSTED_PLATFORM_ASSEMBLIES",
    "APP_PATHS",
    "APP_NI_PATHS",
    "NATIVE_DLL_SEARCH_DIRECTORIES",
    "System.GC.Server",
    "System.Globalization.Invariant",
};
const char *propertyValues[] = {
    // TRUSTED_PLATFORM_ASSEMBLIES
    trustedPlatformAssemblies,
    // APP_PATHS
    appPaths,
    // APP_NI_PATHS
    appNiPaths,
    // NATIVE_DLL_SEARCH_DIRECTORIES
    nativeDllSearchDirectories,
    // System.GC.Server
    useServerGc,
    // System.Globalization.Invariant
    globalizationInvariant,
};

void* hostHandle;
unsigned int domainId;

std::string entryPointExecutablePath;
if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
{
    printf("Could not get full path to current executable");
    return E_FAIL;
}
int st = -1;

st = initializeCoreCLR(
    entryPointExecutablePath.c_str(),
    "unixcorerun",
    sizeof(propertyKeys) / sizeof(propertyKeys[0]),
    propertyKeys,
    propertyValues,
    &hostHandle,
    &domainId);
janvorli commented 6 years ago

@benjaml I have few questions. As for the code sample above. I assume the GetEntrypointExecutableAbsolutePath returns the currentExeAbsolutePath, right?

when I call initializeCoreCLR, even if it throws an exception it also give the return of a success, so I'm not sure of how I should interpret it

I am not sure what you mean by that. If it returns success, it cannot throw at the same time. So is it possible that the exception is thrown after the initializeCoreCLR returns? In other words, if you run it under a debugger and step over the initializeCoreCLR, does the exception happen?

benjaml commented 6 years ago

ok so maybe I'll need to be more precise. I'm using a native exe (c++) to load a native dll (c++), that will run the clr to link with the c# assembly.

I've also change the path to the coreclr.dll as it was not the right one so now the initializeCoreCLR fail and I got : coreclr_initialize failed - status: 0x80004005

janvorli commented 6 years ago

Ok, that's a really general error code which can be caused by many different things. But one thing I wonder about - from the code fragment above, it seems that you are using only files built in the coreclr repo. That's not enough, you need output of both coreclr and corefx repos. But as a first step, I would recommend downloading a zip file with the latest dotnet core from https://dotnetcli.blob.core.windows.net/dotnet/Runtime/master/dotnet-runtime-latest-win-x64.zip and using the binaries from the shared\Microsoft.NETCore.App\2.1.0-preview1-26012-06 folder in the zip. That would help you to make sure that your hosting code is ok. Once that works, you can move to using the coreclr / corefx files you build.

benjaml commented 6 years ago

Ok so coreclr_initialize succeed but I got this during coreclr_initialize : Exception thrown at 0x00007FFA3DE23FB8 (KernelBase.dll) in ConsoleApplication1.exe: 0x04242420 (parameters: 0x0000000031415927, 0x00007FF9FA2C0000, 0x000000154E593620).

currentExeAbsolutePath = D:\Workspace\KEA\InteropSample\CppExecutable\x64\Debug\ConsoleApplication1.exe
clrFilesAbsolutePath = C:\Users\Stage\Downloads\dotnet-runtime-latest-win-x64\shared\Microsoft.NETCore.App\2.1.0-preview1-26012-06
managedAssemblyAbsolutePath = D:\Workspace\KEA\InteropSample\CSharpDll\HostedApplication\bin\Debug\netcoreapp2.0\HostedApplication.dll
janvorli commented 6 years ago

I've already asked about it, but could you please clarify this? If you step over the call to coreclr_initialize in the debugger, do you get the exception?

benjaml commented 6 years ago

yes

janvorli commented 6 years ago

Ah, this looks like a benign thing. The error code 0x04242420 means CLRDBG_NOTIFICATION_EXCEPTION_CODE and the first parameter 0x0000000031415927 means CLRDBG_EXCEPTION_DATA_CHECKSUM. So it is not a failure, but a debugger notification. You can ignore it. As you've said, the coreclr_initialize has returned success.

janvorli commented 6 years ago

See here: https://github.com/dotnet/coreclr/blob/master/src/debug/inc/dbgipcevents.h#L34-L42

benjaml commented 6 years ago

Ok so then I try to create delegates but I got : ERROR - Failed to create delegate. Error code:80070002 I've put my c# assembly in appPaths and nativeDllSearchDirectories, is it enough because it seams like it failed to open files as I got this several times : Exception thrown at 0x00007FFA3DE23FB8 in ConsoleApplication1.exe: Microsoft C++ exception: EEFileLoadException at memory location 0x000000E6CF1F0B28. Exception thrown at 0x00007FFA3DE23FB8 in ConsoleApplication1.exe: Microsoft C++ exception: [rethrow] at memory location 0x0000000000000000.

benjaml commented 6 years ago

Here is the code of AddDelegates :

int AddDelegates(int domainId, void* hostHandle, coreclr_create_delegate_ptr createDelegateCoreCLR)
{
    DelegateMap = std::map<std::string, void*>();
    return AddDelegate("Character.CreateCharacter", domainId, hostHandle, createDelegateCoreCLR);
}

int AddDelegate(std::string route, int domainId, void* hostHandle, coreclr_create_delegate_ptr createDelegateCo)
{
    std::string delimiter = ".";
    std::string className = route.substr(0, route.find(delimiter));
    std::string methodName = route.substr(route.find(delimiter) + 1, route.length() - 1);
    void *delegateFunc = NULL;
    int st = createDelegateCo(
        hostHandle,
        domainId,
        "HostedApplication",    // entryPointAssemblyName
        ("HostedApplication."+className).c_str(),      // entryPointTypeName
        methodName.c_str(),     // entryPointMethodName
        &delegateFunc);
    if (FAILED(st))
    {
        printf("ERROR - Failed to create delegate.\nError code:%x\n", st);
        return -1;
    }
    DelegateMap.insert(std::pair<std::string, void*>(route, delegateFunc));
    return 0;
}
janvorli commented 6 years ago

Looking at the coreruncommon.cpp sources, we also add the absolute path to the C# assembly to the TRUSTED_PLATFORM_ASSEMBLIES property.

benjaml commented 6 years ago

It looks like it created well the delegates but I can't call them. I got : System.Runtime.InteropServices.SEHException Parameters looks correct I don't understand

C++ function :

void Test()
{
    if (DotNetRuntime::GetInstance() == nullptr)
    {
        printf("CLR environment is not running try to call 'Init' before 'Test'\n");
        return;
    }
    void* func = DotNetRuntime::GetInstance()->DelegateMap.at("Character.Test");
    if (func == nullptr)
    {
        printf("Delegate for %s not found\n", "Character.Test");
        return;
    }
    ((NoArgsNoReturnMethod*)func)();
}

C# function :

static void Test()
{
      Console.WriteLine("TEST");
}

I just store all the delegates in DelegateMap with an id

janvorli commented 6 years ago

@benjaml I cannot see anything obviously incorrect. If the function had parameters or it was a member, I would think that the calling convention might be wrong, but since it is a static parameterless and function with void return, that cannot be the case. Are you familiar with amd64 assembly enough so that you'd be able to step through the code of the delegate or at least get me the disassembly of the delegate function code? The way it works is that when you call the delegate, it first calls a generated helper code that does various things that coreclr needs before entering a managed method and then executes the method itself.

benjaml commented 6 years ago

Here is the disassambly of the delegate function code

00007FF99738316C  mov         r10,7FF997383140h  
00007FF997383176  mov         rax,7FF9F6FF3FF0h  
00007FF997383180  jmp         rax 
janvorli commented 6 years ago

@benjaml That looks correct. Could you please wrap the body of your test method in a try / catch, catching just "Exception" and swallowing it ? I wonder if an exception is thrown from the Console.WriteLine - that would result in the System.Runtime.InteropServices.SEHException, I think.

benjaml commented 6 years ago

yes this is it, I got a System.Runtime.InteropServices.SEHException

benjaml commented 6 years ago

I tried an executeAssembly that just do : Console.WriteLine("HelloWorld");

And it worked well

benjaml commented 6 years ago

I've tried to make the delegate function return an int, it gives me the same result --> System.Runtime.InteropServices.SEHException I've also tried to make the delegate function call a dll import function of the native dll to print something. This gives me the same result --> System.Runtime.InteropServices.SEHException

benjaml commented 6 years ago

Even if the delegate function is empty and does nothing, I still got System.Runtime.InteropServices.SEHException

janvorli commented 6 years ago

@benjaml ok, thank you for testing all of these things. Let me try to debug it locally. Would you be able to get me a complete simple repro source that I can use? I can create my own based on the fragments of code you've pasted into this issue, but getting it from you would help me to do it faster.

benjaml commented 6 years ago

Here is my code :

https://github.com/benjaml/CppCSharpInteropSample/

janvorli commented 6 years ago

@benjaml thank you, I'll try to find some time today or tomorrow to debug it.

benjaml commented 6 years ago

ok thanks a lot for your time

janvorli commented 6 years ago

@benjaml I've found the problem. Your code called coreclr_shutdown before calling the delegate. You rely on ExecuteManagedAssembly to initialize the coreclr. but it also calls coreclr_shutdown. So when you call the delegate later, the runtime is shut down. In such state, calling any managed code is prohibited for obvious reasons. I have commented out that call to shutdownCoreCLR and the delegate was successfully called and printed the "TEST" message.

benjaml commented 6 years ago

I feel so dumb ... Thanks a lot

janvorli commented 6 years ago

No problem :-), I was happy to help.

benjaml commented 6 years ago

I've one last problem (I hope).

I want to reload the appdomain when I modify my c# dll. I think this is not possible to only reload the appdomain here since it's not detach from the clr. So I need to reload the clr, So I've tried to shutdown then recreate the clr but it is not working.

here is my code :

static void ReloadAppDomain(char* dotNetDllPath)
{
    instance.reset(nullptr); //  --> this call shutdown and success
    instance = std::make_unique<DotNetRuntime>(dotNetDllPath); // --> this call Initilize and fails
}

I got : coreclr_initialize failed - status: 0x80131022

Is there something else to do if I want to reload it ?

benjaml commented 6 years ago

I've tried to change the appDomainFriendlyName, but it does not change the problem

PS : I've updated the code on the github repo, I gave you yesterday, if you need

janvorli commented 6 years ago

@benjaml Neither reloading the app domain nor reinitializing the runtime is possible in coreclr. There was some work done to support assembly unloading, but it is very complex and was not completed yet. See the discussion in the issue dotnet/runtime#8815 for more details.

janvorli commented 6 years ago

If you have more questions, please ask @jkotas. I am starting my paternity leave on Monday.

RussKeldorph commented 6 years ago

Assuming this discussion has reached a stopping point.