1/*
2 * librdkafka - The Apache Kafka C/C++ library
3 *
4 * Copyright (c) 2017 Magnus Edenhill
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "rd.h"
30#include "rddl.h"
31
32#if WITH_LIBDL
33#include <dlfcn.h>
34
35#elif defined( _MSC_VER)
36
37#else
38#error "Dynamic library loading not supported on this platform"
39#endif
40
41
42
43/**
44 * @brief Latest thread-local dl error, normalized to suit our logging.
45 * @returns a newly allocated string that must be freed
46 */
47static char *rd_dl_error (void) {
48#if WITH_LIBDL
49 char *errstr;
50 char *s;
51 errstr = dlerror();
52 if (!errstr)
53 return rd_strdup("No error returned from dlerror()");
54
55 errstr = rd_strdup(errstr);
56 /* Change newlines to separators. */
57 while ((s = strchr(errstr, '\n')))
58 *s = '.';
59
60 return errstr;
61
62#elif defined(_MSC_VER)
63 char buf[1024];
64 rd_strerror_w32(GetLastError(), buf, sizeof(buf));
65 return rd_strdup(buf);
66#endif
67}
68
69/**
70 * @brief Attempt to load library \p path.
71 * @returns the library handle (platform dependent, thus opaque) on success,
72 * else NULL.
73 */
74static rd_dl_hnd_t *
75rd_dl_open0 (const char *path, char *errstr, size_t errstr_size) {
76 void *handle;
77 const char *loadfunc;
78#if WITH_LIBDL
79 loadfunc = "dlopen()";
80 handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
81#elif defined(_MSC_VER)
82 loadfunc = "LoadLibrary()";
83 handle = (void *)LoadLibraryA(path);
84#endif
85 if (!handle) {
86 char *dlerrstr = rd_dl_error();
87 rd_snprintf(errstr, errstr_size, "%s failed: %s",
88 loadfunc, dlerrstr);
89 rd_free(dlerrstr);
90 }
91 return (rd_dl_hnd_t *)handle;
92}
93
94
95/**
96 * @brief Attempt to load library \p path, possibly with a filename extension
97 * which will be automatically resolved depending on platform.
98 * @returns the library handle (platform dependent, thus opaque) on success,
99 * else NULL.
100 */
101rd_dl_hnd_t *rd_dl_open (const char *path, char *errstr, size_t errstr_size) {
102 rd_dl_hnd_t *handle;
103 char *extpath;
104 size_t pathlen;
105 const char *td, *fname;
106 const char *solib_ext = SOLIB_EXT;
107
108 /* Try original path first. */
109 handle = rd_dl_open0(path, errstr, errstr_size);
110 if (handle)
111 return handle;
112
113 /* Original path not found, see if we can append the solib_ext
114 * filename extension. */
115
116 /* Get filename and filename extension.
117 * We can't rely on basename(3) since it is not portable */
118 fname = strrchr(path, '/');
119#ifdef _MSC_VER
120 td = strrchr(path, '\\');
121 if (td > fname)
122 fname = td;
123#endif
124 if (!fname)
125 fname = path;
126
127 td = strrchr(fname, '.');
128
129 /* If there is a filename extension ('.' within the last characters)
130 * then bail out, we will not append an extension in this case. */
131 if (td && td >= fname + strlen(fname) - strlen(SOLIB_EXT))
132 return NULL;
133
134 /* Append platform-specific library extension. */
135 pathlen = strlen(path);
136 extpath = rd_alloca(pathlen + strlen(solib_ext) + 1);
137 memcpy(extpath, path, pathlen);
138 memcpy(extpath+pathlen, solib_ext, strlen(solib_ext) + 1);
139
140 /* Try again with extension */
141 return rd_dl_open0(extpath, errstr, errstr_size);
142}
143
144
145/**
146 * @brief Close handle previously returned by rd_dl_open()
147 * @remark errors are ignored (what can we do anyway?)
148 */
149void rd_dl_close (rd_dl_hnd_t *handle) {
150#if WITH_LIBDL
151 dlclose((void *)handle);
152#elif defined(_MSC_VER)
153 FreeLibrary((HMODULE)handle);
154#endif
155}
156
157/**
158 * @brief look up address of \p symbol in library handle \p handle
159 * @returns the function pointer on success or NULL on error.
160 */
161void *
162rd_dl_sym (rd_dl_hnd_t *handle, const char *symbol,
163 char *errstr, size_t errstr_size) {
164 void *func;
165#if WITH_LIBDL
166 func = dlsym((void *)handle, symbol);
167#elif defined(_MSC_VER)
168 func = GetProcAddress((HMODULE)handle, symbol);
169#endif
170 if (!func) {
171 char *dlerrstr = rd_dl_error();
172 rd_snprintf(errstr, errstr_size,
173 "Failed to load symbol \"%s\": %s",
174 symbol, dlerrstr);
175 rd_free(dlerrstr);
176 }
177 return func;
178}
179
180