]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | #include <dlfcn.h> |
2 | #include <opentracing/dynamic_load.h> | |
3 | #include <opentracing/version.h> | |
4 | ||
5 | namespace opentracing { | |
6 | BEGIN_OPENTRACING_ABI_NAMESPACE | |
7 | namespace { | |
8 | class DynamicLibraryHandleUnix : public DynamicLibraryHandle { | |
9 | public: | |
10 | explicit DynamicLibraryHandleUnix(void* handle) : handle_{handle} {} | |
11 | ||
12 | ~DynamicLibraryHandleUnix() override { dlclose(handle_); } | |
13 | ||
14 | private: | |
15 | void* handle_; | |
16 | }; | |
17 | } // namespace | |
18 | ||
19 | // Undefined behavior sanitizer has a bug where it will produce a false positive | |
20 | // when casting the result of dlsym to a function pointer. | |
21 | // | |
22 | // See https://github.com/envoyproxy/envoy/pull/2252#issuecomment-362668221 | |
23 | // https://github.com/google/sanitizers/issues/911 | |
24 | // | |
25 | // Note: undefined behavior sanitizer is supported in clang and gcc > 4.9 | |
26 | #if defined(__clang__) | |
27 | __attribute__((no_sanitize("function"))) | |
28 | // Copied from https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html | |
29 | #elif defined(__GNUC__) && \ | |
30 | ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40900) | |
31 | __attribute__((no_sanitize_undefined)) | |
32 | #endif | |
33 | expected<DynamicTracingLibraryHandle> | |
34 | DynamicallyLoadTracingLibrary(const char* shared_library, | |
35 | std::string& error_message) noexcept try { | |
36 | dlerror(); // Clear any existing error. | |
37 | ||
38 | const auto handle = dlopen(shared_library, RTLD_NOW | RTLD_LOCAL); | |
39 | if (handle == nullptr) { | |
40 | error_message = dlerror(); | |
41 | return make_unexpected(dynamic_load_failure_error); | |
42 | } | |
43 | ||
44 | std::unique_ptr<DynamicLibraryHandle> dynamic_library_handle{ | |
45 | new DynamicLibraryHandleUnix{handle}}; | |
46 | ||
47 | const auto make_tracer_factory = | |
48 | reinterpret_cast<OpenTracingMakeTracerFactoryType**>( | |
49 | dlsym(handle, "OpenTracingMakeTracerFactory")); | |
50 | if (make_tracer_factory == nullptr) { | |
51 | error_message = dlerror(); | |
52 | return make_unexpected(dynamic_load_failure_error); | |
53 | } | |
54 | ||
55 | if (*make_tracer_factory == nullptr) { | |
56 | error_message = | |
57 | "An error occurred while looking up for OpenTracingMakeTracerFactory. " | |
58 | "It seems that it was set to nullptr."; | |
59 | return make_unexpected(dynamic_load_failure_error); | |
60 | } | |
61 | ||
62 | const void* error_category = nullptr; | |
63 | void* tracer_factory = nullptr; | |
64 | const auto rcode = (*make_tracer_factory)( | |
65 | OPENTRACING_VERSION, OPENTRACING_ABI_VERSION, &error_category, | |
66 | static_cast<void*>(&error_message), &tracer_factory); | |
67 | if (rcode != 0) { | |
68 | if (error_category == nullptr) { | |
69 | error_message = "failed to construct a TracerFactory: unknown error code"; | |
70 | return make_unexpected(dynamic_load_failure_error); | |
71 | } | |
72 | const auto error_code = std::error_code{ | |
73 | rcode, *static_cast<const std::error_category*>(error_category)}; | |
74 | if (error_message.empty()) { | |
75 | error_message = error_code.message(); | |
76 | } | |
77 | return make_unexpected(dynamic_load_failure_error); | |
78 | } | |
79 | ||
80 | if (tracer_factory == nullptr) { | |
81 | error_message = | |
82 | "failed to construct a TracerFactory: `tracer_factory` is null"; | |
83 | return make_unexpected(dynamic_load_failure_error); | |
84 | } | |
85 | ||
86 | return DynamicTracingLibraryHandle{ | |
87 | std::unique_ptr<const TracerFactory>{ | |
88 | static_cast<TracerFactory*>(tracer_factory)}, | |
89 | std::move(dynamic_library_handle)}; | |
90 | } catch (const std::bad_alloc&) { | |
91 | return make_unexpected(std::make_error_code(std::errc::not_enough_memory)); | |
92 | } | |
93 | END_OPENTRACING_ABI_NAMESPACE | |
94 | } // namespace opentracing |