From 1274a688d51ee0b82c2eeb0e1f373e9e992722e6 Mon Sep 17 00:00:00 2001 From: Tudor Bosman Date: Mon, 21 Jul 2014 15:18:02 -0700 Subject: [PATCH] Enforce that only one version of folly is loaded at the same time Test Plan: folly/test, OSS build, check the macro in separate file Reviewed By: dancol@fb.com FB internal diff: D1448086 --- folly/Makefile.am | 1 + folly/Version.cpp | 23 ++++++++++++ folly/VersionCheck.h | 88 ++++++++++++++++++++++++++++++++++++++++++++ folly/configure.ac | 7 ++-- 4 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 folly/Version.cpp create mode 100644 folly/VersionCheck.h diff --git a/folly/Makefile.am b/folly/Makefile.am index a69b0abb939..401b849a697 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -245,6 +245,7 @@ libfolly_la_SOURCES = \ ThreadCachedArena.cpp \ TimeoutQueue.cpp \ Uri.cpp \ + Version.cpp \ wangle/InlineExecutor.cpp \ wangle/ManualExecutor.cpp \ wangle/ThreadGate.cpp \ diff --git a/folly/Version.cpp b/folly/Version.cpp new file mode 100644 index 00000000000..78786163edb --- /dev/null +++ b/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 + +namespace folly { namespace detail { + +FOLLY_VERSION_CHECK(folly, FOLLY_VERSION) + +}} // namespaces diff --git a/folly/VersionCheck.h b/folly/VersionCheck.h new file mode 100644 index 00000000000..a15675d7def --- /dev/null +++ b/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 +#include +#include + +#include +#include + +/** + * 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_ */ diff --git a/folly/configure.ac b/folly/configure.ac index 99b4fbbe638..93bb5d03949 100644 --- a/folly/configure.ac +++ b/folly/configure.ac @@ -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])