94be9e56be5cf398b7ec77ba1132f5836e796fa5
[folly.git] / folly / VersionCheck.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef FOLLY_VERSIONCHECK_H_
18 #define FOLLY_VERSIONCHECK_H_
19
20 #include <cstdio>
21 #include <cstdlib>
22 #include <cstring>
23
24 #include <folly/Portability.h>
25 #include <folly/Preprocessor.h>
26
27 /**
28  * Check if the currently loaded version of a library is what you expect.
29  *
30  * It is possible for multiple versions of the same shared library to end up
31  * being loaded simultaneously in the same address space, usually with
32  * disastrous results.
33  *
34  * For example, let's say you have a shared library (foo) that doesn't keep
35  * binary compatbility between releases, and so each version is distributed as
36  * a SO with different SONAME. Let's say you build another shared library, bar
37  * that depends on version 1 of foo: libbar.so depends on libfoo1.so.
38  * Your main executable now (baz) depends on version 2 of foo, and also
39  * depends on bar: baz depends on libfoo2.so and libbar.so.
40  *
41  * At load time, baz loads libfoo2.so first, then libbar.so; libbar.so will
42  * load libfoo1.so, but, as this is normal dynamic loading (and not explicit
43  * dlopen calls with RTLD_DEEPBIND), any symbols from libfoo1.so that are
44  * also present in libfoo2.so will be satisfied from the (already loaded)
45  * libfoo2.so.
46  *
47  * But foo does not preserve binary compatibility between versions, so all
48  * hell breaks loose (the symbols from libfoo2.so are not necessarily direct
49  * replacements of the identically-named symbols in libfoo1.so).
50  *
51  * It is better to crash with a helpful error message instead, which is what
52  * this macro provides. FOLLY_VERSION_CHECK verifies at load time that
53  * the compiled-in version is the same as the currently loaded version.
54  *
55  * Usage: use this macro at namespace scope in a .cpp file (IMPORTANT: NOT
56  * in the unnamed namespace):
57  *
58  * FOLLY_VERSION_CHECK(mylib, "1")
59  *
60  * The first argument identifies your library; the second argument is a
61  * string literal containing the desired version string.
62  *
63  * In order to avoid changing the file for each version, the version string
64  * could be provided on the compiler command line with -D:
65  *
66  * FOLLY_VERSION_CHECK(mylib, MYLIB_VERSION)
67  *
68  * ... and then commpile your file with -DMYLIB_VERSION=\"1\"
69  */
70
71 #ifdef __APPLE__
72 // OS X doesn't support constructor priorities. Just pray it works, I guess.
73 #define FOLLY_VERSION_CHECK_PRIORITY __attribute__((__constructor__))
74 #else
75 #define FOLLY_VERSION_CHECK_PRIORITY __attribute__((__constructor__(101)))
76 #endif
77
78 // Note that this is carefully crafted: PRODUCT##Version must have external
79 // linkage (so it collides among versions), versionCheck must have internal
80 // linkage (so it does NOT collide between versions); if we're trying to have
81 // multiple versions loaded at the same time, they must each run their copy
82 // of versionCheck, but share the PRODUCT##Version variable.
83 #define FOLLY_VERSION_CHECK(PRODUCT, VERSION) \
84   const char* PRODUCT##Version = VERSION; \
85   namespace { \
86   FOLLY_VERSION_CHECK_PRIORITY void versionCheck() { \
87     if (strcmp(PRODUCT##Version, VERSION)) { \
88       fprintf(stderr, \
89               "Invalid %s version: desired [%s], currently loaded [%s]\n", \
90               FB_STRINGIZE(PRODUCT), PRODUCT##Version, VERSION); \
91       abort(); \
92     } \
93   } \
94   }
95
96 #endif /* FOLLY_VERSIONCHECK_H_ */