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(_WIN32)
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  */
rd_dl_error(void)47 static 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(_WIN32)
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  */
74 static rd_dl_hnd_t *
rd_dl_open0(const char * path,char * errstr,size_t errstr_size)75 rd_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(_WIN32)
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  */
rd_dl_open(const char * path,char * errstr,size_t errstr_size)101 rd_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 _WIN32
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  */
rd_dl_close(rd_dl_hnd_t * handle)149 void rd_dl_close (rd_dl_hnd_t *handle) {
150 #if WITH_LIBDL
151         dlclose((void *)handle);
152 #elif defined(_WIN32)
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  */
161 void *
rd_dl_sym(rd_dl_hnd_t * handle,const char * symbol,char * errstr,size_t errstr_size)162 rd_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(_WIN32)
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