Skip to content

Commit

Permalink
Enforce that only one version of folly is loaded at the same time
Browse files Browse the repository at this point in the history
Test Plan: folly/test, OSS build, check the macro in separate file

Reviewed By: dancol@fb.com

FB internal diff: D1448086
  • Loading branch information
tudor authored and Chip Turner committed Jul 25, 2014
1 parent c52e055 commit 1274a68
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 3 deletions.
1 change: 1 addition & 0 deletions folly/Makefile.am
Expand Up @@ -245,6 +245,7 @@ libfolly_la_SOURCES = \
ThreadCachedArena.cpp \
TimeoutQueue.cpp \
Uri.cpp \
Version.cpp \
wangle/InlineExecutor.cpp \
wangle/ManualExecutor.cpp \
wangle/ThreadGate.cpp \
Expand Down
23 changes: 23 additions & 0 deletions folly/Version.cpp
@@ -0,0 +1,23 @@
/*
* Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <folly/VersionCheck.h>

namespace folly { namespace detail {

FOLLY_VERSION_CHECK(folly, FOLLY_VERSION)

}} // namespaces
88 changes: 88 additions & 0 deletions folly/VersionCheck.h
@@ -0,0 +1,88 @@
/*
* Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef FOLLY_VERSIONCHECK_H_
#define FOLLY_VERSIONCHECK_H_

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <folly/Portability.h>
#include <folly/Preprocessor.h>

/**
* Check if the currently loaded version of a library is what you expect.
*
* It is possible for multiple versions of the same shared library to end up
* being loaded simultaneously in the same address space, usually with
* disastrous results.
*
* For example, let's say you have a shared library (foo) that doesn't keep
* binary compatbility between releases, and so each version is distributed as
* a SO with different SONAME. Let's say you build another shared library, bar
* that depends on version 1 of foo: libbar.so depends on libfoo1.so.
* Your main executable now (baz) depends on version 2 of foo, and also
* depends on bar: baz depends on libfoo2.so and libbar.so.
*
* At load time, baz loads libfoo2.so first, then libbar.so; libbar.so will
* load libfoo1.so, but, as this is normal dynamic loading (and not explicit
* dlopen calls with RTLD_DEEPBIND), any symbols from libfoo1.so that are
* also present in libfoo2.so will be satisfied from the (already loaded)
* libfoo2.so.
*
* But foo does not preserve binary compatibility between versions, so all
* hell breaks loose (the symbols from libfoo2.so are not necessarily direct
* replacements of the identically-named symbols in libfoo1.so).
*
* It is better to crash with a helpful error message instead, which is what
* this macro provides. FOLLY_VERSION_CHECK verifies at load time that
* the compiled-in version is the same as the currently loaded version.
*
* Usage: use this macro at namespace scope in a .cpp file (IMPORTANT: NOT
* in the unnamed namespace):
*
* FOLLY_VERSION_CHECK(mylib, "1")
*
* The first argument identifies your library; the second argument is a
* string literal containing the desired version string.
*
* In order to avoid changing the file for each version, the version string
* could be provided on the compiler command line with -D:
*
* FOLLY_VERSION_CHECK(mylib, MYLIB_VERSION)
*
* ... and then commpile your file with -DMYLIB_VERSION=\"1\"
*/
// Note that this is carefully crafted: PRODUCT##Version must have external
// linkage (so it collides among versions), versionCheck must have internal
// linkage (so it does NOT collide between versions); if we're trying to have
// multiple versions loaded at the same time, they must each run their copy
// of versionCheck, but share the PRODUCT##Version variable.
#define FOLLY_VERSION_CHECK(PRODUCT, VERSION) \
const char* PRODUCT##Version = VERSION; \
namespace { \
__attribute__((constructor(101))) void versionCheck() { \
if (strcmp(PRODUCT##Version, VERSION)) { \
fprintf(stderr, \
"Invalid %s version: desired [%s], currently loaded [%s]\n", \
FB_STRINGIZE(PRODUCT), PRODUCT##Version, VERSION); \
abort(); \
} \
} \
}

#endif /* FOLLY_VERSIONCHECK_H_ */
7 changes: 4 additions & 3 deletions folly/configure.ac
Expand Up @@ -3,12 +3,13 @@
# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.59)
AC_INIT(folly, 0.1, folly@fb.com)

m4_define([folly_libtool_current], [1])
m4_define([folly_version_str], m4_esyscmd_s([cat VERSION]))

AC_INIT([folly], m4_translit(folly_version_str, [:], [.]), [folly@fb.com])

# We assume all revisions are backwards incompatible.
LT_VERSION=folly_libtool_current:0:0
LT_VERSION=folly_version_str:0
AC_SUBST(LT_VERSION)

AC_CONFIG_SRCDIR([Likely.h])
Expand Down

0 comments on commit 1274a68

Please sign in to comment.