int load_host_library_common( const pal::string_t& lib_dir, pal::string_t& host_path, pal::dll_t* h_host, corehost_load_fn* load_fn, corehost_unload_fn* unload_fn) { if (!library_exists_in_dir(lib_dir, LIBHOSTPOLICY_NAME, &host_path)) { return StatusCode::CoreHostLibMissingFailure; } // Load library if (!pal::load_library(&host_path, h_host)) { trace::info(_X("Load library of %s failed"), host_path.c_str()); return StatusCode::CoreHostLibLoadFailure; } // Obtain entrypoint symbols *load_fn = (corehost_load_fn)pal::get_symbol(*h_host, "corehost_load"); *unload_fn = (corehost_unload_fn)pal::get_symbol(*h_host, "corehost_unload"); return (*load_fn != nullptr) && (*unload_fn != nullptr) ? StatusCode::Success : StatusCode::CoreHostEntryPointFailure; }
/** * Given a directory and a version, find if the package relative * dir under the given directory contains hostpolicy.dll */ bool to_hostpolicy_package_dir(const pal::string_t& dir, const pal::string_t& version, pal::string_t* candidate) { assert(!version.empty()); candidate->clear(); // Ensure the relative dir contains platform directory separators. pal::string_t rel_dir = _STRINGIFY(HOST_POLICY_PKG_REL_DIR); if (DIR_SEPARATOR != '/') { replace_char(&rel_dir, '/', DIR_SEPARATOR); } // Construct the path to directory containing hostpolicy. pal::string_t path = dir; append_path(&path, _STRINGIFY(HOST_POLICY_PKG_NAME)); // package name append_path(&path, version.c_str()); // package version append_path(&path, rel_dir.c_str()); // relative dir containing hostpolicy library // Check if "path" contains the required library. if (!library_exists_in_dir(path, LIBHOSTPOLICY_NAME, nullptr)) { trace::verbose(_X("Did not find %s in directory %s"), LIBHOSTPOLICY_NAME, path.c_str()); return false; } // "path" contains the directory containing hostpolicy library. *candidate = path; trace::verbose(_X("Found %s in directory %s"), LIBHOSTPOLICY_NAME, path.c_str()); return true; }
bool hostpolicy_exists_in_svc(pal::string_t* resolved_dir) { pal::string_t svc_dir; if (!pal::getenv(_X("DOTNET_SERVICING"), &svc_dir)) { trace::verbose(_X("Servicing root doesn't exist")); return false; } pal::string_t rel_dir = _STRINGIFY(HOST_POLICY_PKG_REL_DIR); if (DIR_SEPARATOR != '/') { replace_char(&rel_dir, '/', DIR_SEPARATOR); } pal::string_t path = svc_dir; append_path(&path, _STRINGIFY(HOST_POLICY_PKG_NAME)); append_path(&path, _STRINGIFY(HOST_POLICY_PKG_VER)); append_path(&path, rel_dir.c_str()); append_path(&path, LIBHOSTPOLICY_NAME); if (!pal::realpath(&path)) { trace::verbose(_X("Servicing root ::realpath(%s) doesn't exist"), path.c_str()); return false; } if (library_exists_in_dir(path, LIBHOSTPOLICY_NAME, nullptr)) { resolved_dir->assign(path); trace::verbose(_X("[%s] exists in servicing [%s]"), LIBHOSTPOLICY_NAME, path.c_str()); return true; } trace::verbose(_X("[%s] doesn't exist in servicing [%s]"), LIBHOSTPOLICY_NAME, path.c_str()); return false; }
int fx_muxer_t::parse_args_and_execute(const pal::string_t& own_dir, int argoff, int argc, const pal::char_t* argv[], bool exec_mode, bool* is_an_app) { *is_an_app = true; std::vector<pal::string_t> known_opts = { _X("--additionalprobingpath") }; if (exec_mode) { known_opts.push_back(_X("--depsfile")); } // Parse the known muxer arguments if any. int num_parsed = 0; std::unordered_map<pal::string_t, std::vector<pal::string_t>> opts; if (!parse_known_args(argc - argoff, &argv[argoff], known_opts, &opts, &num_parsed)) { trace::error(_X("Failed to parse supported arguments.")); return InvalidArgFailure; } int cur_i = argoff + num_parsed; if (cur_i >= argc) { return muxer_usage(); } pal::string_t app_candidate = argv[cur_i]; bool is_app_runnable = ends_with(app_candidate, _X(".dll"), false) || ends_with(app_candidate, _X(".exe"), false); // If exec mode is on, then check we have a dll at this point if (exec_mode) { if (!is_app_runnable) { trace::error(_X("dotnet exec needs a dll to execute. Try dotnet [--help]")); return InvalidArgFailure; } } // For non-exec, there is CLI invocation or app.dll execution after known args. else { // Test if we have a real dll at this point. if (!is_app_runnable) { // No we don't have a dll, this must be routed to the CLI. *is_an_app = false; return Success; } } // Transform dotnet [exec] [--additionalprobingpath path] [--depsfile file] dll [args] -> dotnet dll [args] std::vector<const pal::char_t*> vec_argv; const pal::char_t** new_argv = argv; int new_argc = argc; if (cur_i != 1) { vec_argv.resize(argc - cur_i + 1, 0); // +1 for dotnet memcpy(vec_argv.data() + 1, argv + cur_i, (argc - cur_i) * sizeof(pal::char_t*)); vec_argv[0] = argv[0]; new_argv = vec_argv.data(); new_argc = vec_argv.size(); } pal::string_t opts_deps_file = _X("--depsfile"); pal::string_t opts_probe_path = _X("--additionalprobingpath"); pal::string_t deps_file = get_last_known_arg(opts, opts_deps_file, _X("")); std::vector<pal::string_t> probe_paths = opts.count(opts_probe_path) ? opts[opts_probe_path] : std::vector<pal::string_t>(); trace::verbose(_X("Current argv is %s"), app_candidate.c_str()); pal::string_t app_or_deps = deps_file.empty() ? app_candidate : deps_file; pal::string_t no_json = app_candidate; pal::string_t dev_config_file; auto config_file = get_runtime_config_from_file(no_json, &dev_config_file); runtime_config_t config(config_file, dev_config_file); for (const auto& path : config.get_probe_paths()) { probe_paths.push_back(path); } if (!config.is_valid()) { trace::error(_X("Invalid runtimeconfig.json [%s] [%s]"), config.get_path().c_str(), config.get_dev_path().c_str()); return StatusCode::InvalidConfigFile; } if (!deps_file.empty() && !pal::file_exists(deps_file)) { trace::error(_X("Deps file [%s] specified but doesn't exist"), deps_file.c_str()); return StatusCode::InvalidArgFailure; } if (config.get_portable()) { trace::verbose(_X("Executing as a portable app as per config file [%s]"), config_file.c_str()); pal::string_t fx_dir = resolve_fx_dir(own_dir, &config); corehost_init_t init(deps_file, probe_paths, fx_dir, host_mode_t::muxer, &config); return execute_app(fx_dir, &init, new_argc, new_argv); } else { trace::verbose(_X("Executing as a standalone app as per config file [%s]"), config_file.c_str()); pal::string_t impl_dir = get_directory(app_or_deps); if (!library_exists_in_dir(impl_dir, LIBHOSTPOLICY_NAME, nullptr) && !probe_paths.empty() && !deps_file.empty()) { bool found = false; pal::string_t candidate = impl_dir; deps_json_t deps_json(false, deps_file); for (const auto& probe_path : probe_paths) { trace::verbose(_X("Considering %s for hostpolicy library"), probe_path.c_str()); if (deps_json.is_valid() && deps_json.has_hostpolicy_entry() && deps_json.get_hostpolicy_entry().to_full_path(probe_path, &candidate)) { found = true; // candidate contains the right path. break; } } if (!found) { trace::error(_X("Policy library either not found in deps [%s] or not found in %d probe paths."), deps_file.c_str(), probe_paths.size()); return StatusCode::CoreHostLibMissingFailure; } impl_dir = get_directory(candidate); } corehost_init_t init(deps_file, probe_paths, _X(""), host_mode_t::muxer, &config); return execute_app(impl_dir, &init, new_argc, new_argv); } }
/** * Resolve native and culture assembly directories based on "asset_type" parameter. */ bool deps_resolver_t::resolve_probe_dirs( deps_entry_t::asset_types asset_type, pal::string_t* output, std::unordered_set<pal::string_t>* breadcrumb) { bool is_resources = asset_type == deps_entry_t::asset_types::resources; assert(is_resources || asset_type == deps_entry_t::asset_types::native); // For resources assemblies, we need to provide the base directory of the resources path. // For example: .../Foo/en-US/Bar.dll, then, the resolved path is .../Foo std::function<pal::string_t(const pal::string_t&)> resources = [] (const pal::string_t& str) { return get_directory(get_directory(str)); }; // For native assemblies, obtain the directory path from the file path std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) { return get_directory(str); }; // Action for post processing the resolved path std::function<pal::string_t(const pal::string_t&)>& action = is_resources ? resources : native; // Set for de-duplication std::unordered_set<pal::string_t> items; pal::string_t core_servicing = m_core_servicing; pal::realpath(&core_servicing); // Filter out non-serviced assets so the paths can be added after servicing paths. pal::string_t non_serviced; std::vector<deps_entry_t> empty(0); const auto& entries = m_deps->get_entries(asset_type); const auto& fx_entries = m_portable ? m_fx_deps->get_entries(asset_type) : empty; pal::string_t candidate; auto add_package_cache_entry = [&](const deps_entry_t& entry, const pal::string_t& deps_dir) -> bool { if (entry.is_serviceable) { breadcrumb->insert(entry.library_name + _X(",") + entry.library_version); breadcrumb->insert(entry.library_name); } if (items.count(entry.asset_name)) { return true; } // Ignore placeholders if (ends_with(entry.relative_path, _X("/_._"), false)) { return true; } trace::verbose(_X("Processing native/culture for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); if (probe_deps_entry(entry, deps_dir, &candidate)) { init_known_entry_path(entry, candidate); add_unique_path(asset_type, action(candidate), &items, output, &non_serviced, core_servicing); } else { // For standalone apps, apphost.exe will be renamed. Do not use the full package name // because of rid-fallback could happen (ex: CentOS falling back to RHEL) if ((ends_with(entry.library_name, _X(".Microsoft.NETCore.DotNetHost"), false) && entry.asset_name == _X("dotnet")) || (ends_with(entry.library_name, _X(".Microsoft.NETCore.DotNetAppHost"), false) && entry.asset_name == _X("apphost"))) { trace::warning(_X("Warning: assembly specified in the dependencies manifest was not found -- package: '%s', version: '%s', path: '%s'"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); return true; } trace::error(_X("Error: assembly specified in the dependencies manifest was not found -- package: '%s', version: '%s', path: '%s'"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); return false; } if (m_api_set_paths.empty() && pal::need_api_sets() && ends_with(entry.library_name, _X("Microsoft.NETCore.Windows.ApiSets"), false)) { m_api_set_paths.insert(action(candidate)); } return true; }; for (const auto& entry : entries) { if (!add_package_cache_entry(entry, m_app_dir)) { return false; } } // If the deps file is missing add known locations. if (!m_deps->exists()) { // App local path add_unique_path(asset_type, m_app_dir, &items, output, &non_serviced, core_servicing); (void) library_exists_in_dir(m_app_dir, LIBCORECLR_NAME, &m_coreclr_path); (void) library_exists_in_dir(m_app_dir, LIBCLRJIT_NAME, &m_clrjit_path); } for (const auto& entry : fx_entries) { if (!add_package_cache_entry(entry, m_fx_dir)) { return false; } } output->append(non_serviced); return true; }
/** * Given own location, FX location, app binary and specified --depsfile and probe paths * return location that is expected to contain hostpolicy */ bool fx_muxer_t::resolve_hostpolicy_dir(host_mode_t mode, const pal::string_t& own_dir, const pal::string_t& fx_dir, const pal::string_t& app_candidate, const pal::string_t& specified_deps_file, const pal::string_t& specified_fx_version, const std::vector<pal::string_t>& probe_realpaths, const runtime_config_t& config, pal::string_t* impl_dir) { // Obtain deps file for the given configuration. pal::string_t resolved_deps = get_deps_file(fx_dir, app_candidate, specified_deps_file, config); // Resolve hostpolicy version out of the deps file. pal::string_t version = resolve_hostpolicy_version_from_deps(resolved_deps); if (trace::is_enabled() && version.empty() && pal::file_exists(resolved_deps)) { trace::warning(_X("Dependency manifest %s does not contain an entry for %s"), resolved_deps.c_str(), _STRINGIFY(HOST_POLICY_PKG_NAME)); } // Check if the given version of the hostpolicy exists in servicing. if (hostpolicy_exists_in_svc(version, impl_dir)) { return true; } // Get the expected directory that would contain hostpolicy. pal::string_t expected; if (config.get_portable()) { if (!pal::directory_exists(fx_dir)) { pal::string_t fx_version = specified_fx_version.empty() ? config.get_fx_version() : specified_fx_version; handle_missing_framework_error(config.get_fx_name(), fx_version, fx_dir); return false; } expected = fx_dir; } else { // Standalone apps can be activated by muxer or by standalone host or "corehost" // 1. When activated with dotnet.exe or corehost.exe, check for hostpolicy in the deps dir or // app dir. // 2. When activated with app.exe, the standalone host, check own directory. assert(mode == host_mode_t::muxer || mode == host_mode_t::standalone || mode == host_mode_t::split_fx); expected = (mode == host_mode_t::standalone) ? own_dir : get_directory(specified_deps_file.empty() ? app_candidate : specified_deps_file); } // Check if hostpolicy exists in "expected" directory. trace::verbose(_X("The expected %s directory is [%s]"), LIBHOSTPOLICY_NAME, expected.c_str()); if (library_exists_in_dir(expected, LIBHOSTPOLICY_NAME, nullptr)) { impl_dir->assign(expected); return true; } trace::verbose(_X("The %s was not found in [%s]"), LIBHOSTPOLICY_NAME, expected.c_str()); // Start probing for hostpolicy in the specified probe paths. pal::string_t candidate; if (resolve_hostpolicy_dir_from_probe_paths(version, probe_realpaths, &candidate)) { impl_dir->assign(candidate); return true; } // If it still couldn't be found, somebody upstack messed up. Flag an error for the "expected" location. trace::error(_X("A fatal error was encountered. The library '%s' required to execute the application was not found in '%s'."), LIBHOSTPOLICY_NAME, expected.c_str()); return false; }
/** * Resolve native and culture assembly directories based on "asset_type" parameter. */ bool deps_resolver_t::resolve_probe_dirs( deps_entry_t::asset_types asset_type, pal::string_t* output, std::unordered_set<pal::string_t>* breadcrumb) { bool is_resources = asset_type == deps_entry_t::asset_types::resources; assert(is_resources || asset_type == deps_entry_t::asset_types::native); // For resources assemblies, we need to provide the base directory of the resources path. // For example: .../Foo/en-US/Bar.dll, then, the resolved path is .../Foo std::function<pal::string_t(const pal::string_t&)> resources = [] (const pal::string_t& str) { return get_directory(get_directory(str)); }; // For native assemblies, obtain the directory path from the file path std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) { return get_directory(str); }; // Action for post processing the resolved path std::function<pal::string_t(const pal::string_t&)>& action = is_resources ? resources : native; // Set for de-duplication std::unordered_set<pal::string_t> items; pal::string_t core_servicing = m_core_servicing; pal::realpath(&core_servicing, true); // Filter out non-serviced assets so the paths can be added after servicing paths. pal::string_t non_serviced; std::vector<deps_entry_t> empty(0); pal::string_t candidate; auto add_package_cache_entry = [&](const deps_entry_t& entry, const pal::string_t& deps_dir, int fx_level) -> bool { if (breadcrumb != nullptr && entry.is_serviceable) { breadcrumb->insert(entry.library_name + _X(",") + entry.library_version); breadcrumb->insert(entry.library_name); } if (items.count(entry.asset.name)) { return true; } // Ignore placeholders if (ends_with(entry.asset.relative_path, _X("/_._"), false)) { return true; } trace::verbose(_X("Processing native/culture for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str()); if (probe_deps_entry(entry, deps_dir, fx_level, &candidate)) { init_known_entry_path(entry, candidate); add_unique_path(asset_type, action(candidate), &items, output, &non_serviced, core_servicing); } else { // For self-contained apps do not use the full package name // because of rid-fallback could happen (ex: CentOS falling back to RHEL) if ((entry.asset.name == _X("apphost")) && ends_with(entry.library_name, _X(".Microsoft.NETCore.DotNetAppHost"), false)) { return report_missing_assembly_in_manifest(entry, true); } return report_missing_assembly_in_manifest(entry); } return true; }; // Add app entries const auto& entries = get_deps().get_entries(asset_type); for (const auto& entry : entries) { if (!add_package_cache_entry(entry, m_app_dir, 0)) { return false; } } // If the deps file is missing add known locations. if (!get_deps().exists()) { // App local path add_unique_path(asset_type, m_app_dir, &items, output, &non_serviced, core_servicing); (void) library_exists_in_dir(m_app_dir, LIBCORECLR_NAME, &m_coreclr_path); (void) library_exists_in_dir(m_app_dir, LIBCLRJIT_NAME, &m_clrjit_path); } // Handle any additional deps.json that were specified. for (const auto& additional_deps : m_additional_deps) { const auto additional_deps_entries = additional_deps->get_entries(asset_type); for (const auto entry : additional_deps_entries) { if (!add_package_cache_entry(entry, m_app_dir, 0)) { return false; } } } // Add fx package locations to fx_dir for (int i = 1; i < m_fx_definitions.size(); ++i) { const auto& fx_entries = m_fx_definitions[i]->get_deps().get_entries(asset_type); for (const auto& entry : fx_entries) { if (!add_package_cache_entry(entry, m_fx_definitions[i]->get_dir(), i)) { return false; } } } output->append(non_serviced); return true; }