Esempio n. 1
0
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;
}
Esempio n. 2
0
/**
 * 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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
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);
    }
}
Esempio n. 5
0
/**
 *  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;
}
Esempio n. 6
0
/**
 * 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;
}
Esempio n. 7
0
/**
 *  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;
}