1/*
2 * Copyright 2021 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkString.h"
9#include "include/core/SkTypes.h"
10#include "modules/skunicode/src/SkUnicode_icu.h"
11
12#include <dlfcn.h>
13
14#define SK_RUNTIME_ICU_PATHS "libicuuc.so"
15
16std::unique_ptr<SkICULib> SkLoadICULib() {
17    static constexpr char const* gLibPaths[] = { SK_RUNTIME_ICU_PATHS };
18
19    void* dlhnd = nullptr;
20    for (const auto path : gLibPaths) {
21        dlhnd = dlopen(path, RTLD_LAZY);
22        if (dlhnd) {
23            break;
24        }
25    }
26
27    if (!dlhnd) {
28        SkDEBUGF("ICU loader: failed to open libicuuc.\n");
29        return nullptr;
30    }
31
32    int icu_ver = -1;
33
34    bool resolved_required_syms = true;
35
36    auto resolve_sym = [&](void* hnd, const char name[], bool required = false) -> void* {
37        static constexpr int kMinVer =  44,
38                             kMaxVer = 100;
39
40        // First call performs a search to determine the actual lib version.
41        // Subsequent calls are pinned to the version found.
42        const auto search_to = icu_ver > 0 ? icu_ver : kMaxVer;
43        icu_ver              = icu_ver > 0 ? icu_ver : kMinVer;
44
45        for (;;) {
46            const auto sym = SkStringPrintf("%s_%d", name, icu_ver);
47            if (auto* addr = dlsym(dlhnd, sym.c_str())) {
48                return addr;
49            }
50
51            if (icu_ver == search_to) {
52                break;
53            }
54
55            icu_ver++;
56        }
57
58        if (required) {
59            resolved_required_syms = false;
60        }
61        return nullptr;
62    };
63
64    SkICULib lib {};
65
66    // When using dlsym
67    // *(void**)(&procPtr) = dlsym(self, "proc");
68    // is non-standard, but safe for POSIX. Cannot write
69    // *reinterpret_cast<void**>(&procPtr) = dlsym(self, "proc");
70    // because clang has not implemented DR573. See http://clang.llvm.org/cxx_dr_status.html .
71    #define SKICU_FUNC(fname) *(void**)(&lib.f_##fname) = resolve_sym(dlhnd, #fname, true);
72    SKICU_EMIT_FUNCS
73
74    *(void**)(&lib.f_ubrk_clone_)     = resolve_sym(dlhnd, "ubrk_clone");
75    *(void**)(&lib.f_ubrk_safeClone_) = resolve_sym(dlhnd, "ubrk_safeClone");
76
77    if (!resolved_required_syms || (!lib.f_ubrk_clone_ && !lib.f_ubrk_safeClone_)) {
78        SkDEBUGF("ICU loader: failed to resolve required symbols.");
79        dlclose(dlhnd);
80        return nullptr;
81    }
82
83    return std::make_unique<SkICULib>(lib);
84}
85