Skip to content

Instantly share code, notes, and snippets.

@yizhang82
Created January 28, 2017 07:43
Show Gist options
  • Save yizhang82/1c7c8c9c31a345b1841e64a57856f690 to your computer and use it in GitHub Desktop.
Save yizhang82/1c7c8c9c31a345b1841e64a57856f690 to your computer and use it in GitHub Desktop.
Simple coreclr host
#include <iostream>
#include <limits.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <set>
#include <dirent.h>
#include <sys/stat.h>
#include "coreclrhost.h"
using namespace std;
typedef char *(*bootstrap_ptr)();
void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
{
const char * const tpaExtensions[] = {
".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
".dll",
".ni.exe",
".exe",
};
DIR* dir = opendir(directory);
if (dir == nullptr)
{
return;
}
std::set<std::string> addedAssemblies;
// Walk the directory for each extension separately so that we first get files with .ni.dll extension,
// then files with .dll extension, etc.
for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
{
const char* ext = tpaExtensions[extIndex];
int extLength = strlen(ext);
struct dirent* entry;
// For all entries in the directory
while ((entry = readdir(dir)) != nullptr)
{
// We are interested in files only
switch (entry->d_type)
{
case DT_REG:
break;
// Handle symlinks and file systems that do not support d_type
case DT_LNK:
case DT_UNKNOWN:
{
std::string fullFilename;
fullFilename.append(directory);
fullFilename.append("/");
fullFilename.append(entry->d_name);
struct stat sb;
if (stat(fullFilename.c_str(), &sb) == -1)
{
continue;
}
if (!S_ISREG(sb.st_mode))
{
continue;
}
}
break;
default:
continue;
}
std::string filename(entry->d_name);
// Check if the extension matches the one we are looking for
int extPos = filename.length() - extLength;
if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
{
continue;
}
std::string filenameWithoutExt(filename.substr(0, extPos));
// Make sure if we have an assembly with multiple extensions present,
// we insert only one version of it.
if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
{
addedAssemblies.insert(filenameWithoutExt);
tpaList.append(directory);
tpaList.append("/");
tpaList.append(filename);
tpaList.append(":");
}
}
// Rewind the directory stream to be able to iterate over it for the next extension
rewinddir(dir);
}
closedir(dir);
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
cerr << "Usage: host <core_clr_path>" << endl;
return -1;
}
char app_path[PATH_MAX];
if (realpath(argv[0], app_path) == NULL)
{
cerr << "bad path " << argv[0] << endl;
return -1;
}
char *last_slash = strrchr(app_path, '/');
if (last_slash != NULL)
*last_slash = 0;
cout << "app_path:" << app_path << endl;
cout << "Loading CoreCLR..." << endl;
char pkg_path[PATH_MAX];
if (realpath(argv[1], pkg_path) == NULL)
{
cerr << "bad path " << argv[1] << endl;
return -1;
}
//
// Load CoreCLR
//
string coreclr_path(pkg_path);
coreclr_path.append("/libcoreclr.dylib");
cout << "coreclr_path:" << coreclr_path.c_str() << endl;
void *coreclr = dlopen(coreclr_path.c_str(), RTLD_NOW | RTLD_LOCAL);
if (coreclr == NULL)
{
cerr << "failed to open " << coreclr_path << endl;
cerr << "error: " << dlerror() << endl;
return -1;
}
//
// Initialize CoreCLR
//
std::cout << "Initializing CoreCLR..." << endl;
coreclr_initialize_ptr coreclr_init = reinterpret_cast<coreclr_initialize_ptr>(dlsym(coreclr, "coreclr_initialize"));
if (coreclr_init == NULL)
{
cerr << "couldn't find coreclr_initialize in " << coreclr_path << endl;
return -1;
}
string tpa_list;
AddFilesFromDirectoryToTpaList(pkg_path, tpa_list);
const char *property_keys[] = {
"APP_PATHS",
"TRUSTED_PLATFORM_ASSEMBLIES"
};
const char *property_values[] = {
// APP_PATHS
app_path,
// TRUSTED_PLATFORM_ASSEMBLIES
tpa_list.c_str()
};
void *coreclr_handle;
unsigned int domain_id;
int ret = coreclr_init(
app_path, // exePath
"host", // appDomainFriendlyName
sizeof(property_values)/sizeof(char *), // propertyCount
property_keys, // propertyKeys
property_values, // propertyValues
&coreclr_handle, // hostHandle
&domain_id // domainId
);
if (ret < 0)
{
cerr << "failed to initialize coreclr. cerr = " << ret << endl;
return -1;
}
//
// Once CoreCLR is initialized, bind to the delegate
//
std::cout << "Creating delegate..." << endl;
coreclr_create_delegate_ptr coreclr_create_dele = reinterpret_cast<coreclr_create_delegate_ptr>(dlsym(coreclr, "coreclr_create_delegate"));
if (coreclr_create_dele == NULL)
{
cerr << "couldn't find coreclr_create_delegate in " << coreclr_path << endl;
return -1;
}
bootstrap_ptr dele;
ret = coreclr_create_dele(
coreclr_handle,
domain_id,
"manlib",
"ManLib",
"Bootstrap",
reinterpret_cast<void **>(&dele)
);
if (ret < 0)
{
cerr << "couldn't create delegate. err = " << ret << endl;
return -1;
}
//
// Call the delegate
//
cout << "Calling ManLib::Bootstrap() through delegate..." << endl;
char *msg = dele();
cout << "ManLib::Bootstrap() returned " << msg << endl;
free(msg); // returned string need to be free-ed
dlclose(coreclr);
}
@polyclash
Copy link

polyclash commented Sep 4, 2020

@blakemcbride

Need the rest of the code. e.g. where is the C# code we're calling? What would be good is if you included a simple C# program and shows the C++ code calling it, passing arguments to it, and getting a result from it.

see here
https://yizhang82.dev/hosting-coreclr

@DeafMan1983
Copy link

If I use wrapper for NativeAOT like I want to use dotnet 8.0/9.0 and it loads assembly as app_path and compile with gcc into object *.o and ar into *.a then move to DotnetApp

<ItemGroup>
  <DirectPInvoke Include="__Internal" />
  <NativeLibrary Include="-L./ -ldotnetapp_interop -lcoreclr -lhostfxr" />
</ItemGroup>

libdotnetapp_interop.a is for wrapper like __Internal like it works close to Unity3D or Godot.

But I got still failed to initialize when I already libcoreclr.so and libhostfxr.so into DotnetApp's directory but it shows "Failed to initialize CoreClr!".

I think I missed to add delegate for loading assembly like static methods, correctly?

Example:
MyAssembly is classlib:

namespace MyAssembly;

using static DotnetLibrary.Library;

public class HelloTest
{
    public static void Hello()
    {
        //SayPrint();
        Console.WriteLine("Hello from MyAssembly!");
    }
}

Please update newest version without trying dlopen, dlsym or dlclose because new version of Dotnet has already implemented with nethost.h, hostfxr.h and coreclr.h

Do you require loading runtimeconfig.json or not? If you use custom runtime app without installing dotnet like Godot 4.x or Unity3D.

I am very disappointed because I have tried to resolve initializing coreclr. But loading assembly was correct but I don't know how do I initialize with dotnet 8/9 with NativeAOT development.

Thanks! I hope you help my resolve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment