1

I have recently encountered what appears to be a bug in building shared libraries (.dylib files) from compiled C++ code (.o files) on Mavericks (OS X 10.9.4, Xcode 5.1). Key symbols from one particular implementation class seem to be exported in the .o file, but not from the .dylib file incorporating the .o.

Our codebase is broken up into modules. We make heavy use of the Curiously Recurring Template Pattern (CRTP). A sketch of the code is shown here:

#include <string>
class ExprVec;

class Operator
{
 public:
  virtual bool operator()(bool &result, ExprVec const &args) const = 0;
  virtual bool operator()(int32_t &result, ExprVec const &args) const = 0;
  virtual bool operator()(double &result, ExprVec const &args) const = 0;
  virtual bool operator()(std::string &result, ExprVec const &args) const = 0;
};

template <class IMPL>
class OperatorShim : public Operator
{
 public:
  bool operator()(bool &result, ExprVec const &args) const
  {
    return static_cast<IMPL const *>(this)->calc(result, args);
  }
  bool operator()(int32_t &result, ExprVec const &args) const
  {
    return static_cast<IMPL const *>(this)->calc(result, args);
  }
  bool operator()(double &result, ExprVec const &args) const
  {
    return static_cast<IMPL const *>(this)->calc(result, args);
  }
  bool operator()(std::string &result, ExprVec const &args) const
  {
    return static_cast<IMPL const *>(this)->calc(result, args);
  }
};

template <typename T>
class OperatorImpl : public OperatorShim<OperatorImpl<T> >
{
 public:
  // implemented by derived classes
  virtual bool calc(T &result, ExprVec const &args) const = 0;

  // default methods
  template <typename U>
  bool calc <U &result, ExprVec const &args) const
  {
    // throw wrong-type exception
    return false;
  }
};

template class OperatorImpl<bool>;
template class OperatorImpl<int32_t>;
template class OperatorImpl<double>;
template class OperatorImpl<std::string>;

In our actual codebase, the declarations are in a .hh file, and the definitions in a .cc file. The .o file from the .cc exports all the templatized calc() default methods - but the .dylib doesn't. These symbols are in the .dylib, but only as debugging symbols. I've verified this by using nm and c++filt to inspect the files.

This results in an error linking any application using this library, as the OperatorShim methods call the unexported OperatorImpl methods.

This only happens when building on Mac OS X 10.9.x with Xcode 5.1.x command line tools. 10.8.x and Xcode 5.0.x have no problem linking this library, nor do the GNU tools on Linux.

As a workaround, I've moved the method implementations back into the .hh file. Because they require several other include files, I would prefer to keep the implementations separate from the declarations.

You can reproduce the actual problem on OS X 10.9.x as follows:

export PLEXIL_HOME=$PWD
svn checkout -r3785 svn+ssh://svn.code.sf.net/p/plexil/code/branches/plexil-3-new-exp/src
cd src
./configure --disable-static --enable-module-tests --prefix=$PLEXIL_HOME
make

The files in question are .../src/expr/OperatorImpl.{hh,cc,o}, and .../src/expr/.libs/libPlexilExpr.0.dylib . The command that built the dylib file is:

libtool: link: g++ -dynamiclib -Wl,-undefined -Wl,dynamic_lookup -o .libs/libPlexilExpr.0.dylib  .libs/libPlexilExpr_la-Alias.o .libs/libPlexilExpr_la-Array.o .libs/libPlexilExpr_la-ArrayImpl.o .libs/libPlexilExpr_la-ArithmeticFunctionFactory.o .libs/libPlexilExpr_la-ArithmeticOperators.o .libs/libPlexilExpr_la-ArrayOperators.o .libs/libPlexilExpr_la-ArrayReference.o .libs/libPlexilExpr_la-ArrayVariable.o .libs/libPlexilExpr_la-Assignable.o .libs/libPlexilExpr_la-AssignableImpl.o .libs/libPlexilExpr_la-BooleanOperators.o .libs/libPlexilExpr_la-CommandHandle.o .libs/libPlexilExpr_la-Comparisons.o .libs/libPlexilExpr_la-ConcreteExpressionFactory.o .libs/libPlexilExpr_la-Constant.o .libs/libPlexilExpr_la-Expression.o .libs/libPlexilExpr_la-ExpressionConstants.o .libs/libPlexilExpr_la-ExpressionFactories.o .libs/libPlexilExpr_la-ExpressionFactory.o .libs/libPlexilExpr_la-ExpressionImpl.o .libs/libPlexilExpr_la-ExpressionListener.o .libs/libPlexilExpr_la-ExprVec.o .libs/libPlexilExpr_la-Function.o .libs/libPlexilExpr_la-FunctionFactory.o .libs/libPlexilExpr_la-NodeConstants.o .libs/libPlexilExpr_la-NodeConstantExpressions.o .libs/libPlexilExpr_la-NotifierImpl.o .libs/libPlexilExpr_la-OperatorImpl.o .libs/libPlexilExpr_la-PlexilExpr.o .libs/libPlexilExpr_la-StringOperators.o .libs/libPlexilExpr_la-UserVariable.o .libs/libPlexilExpr_la-Value.o .libs/libPlexilExpr_la-ValueType.o   -lm -lpthread -ldl  -O2   -install_name  /Users/chucko/src/plexil-3-new-exp/lib/libPlexilExpr.0.dylib -compatibility_version 1 -current_version 1.0 -Wl,-single_module

Is this the result of a coding error on my part? I tried a number of different approaches but couldn't get the problem to go away.

Chuck Fry
  • 387
  • 1
  • 2
  • 10
  • 4
    1: What is the command line for building dylib? 2: what does "nm" reports ? – Icarus3 Jul 28 '14 at 19:04
  • Please see the edited problem description for how to reproduce. I am typing this from a different computer than the one on which the problem occurs, so I don't have the 'nm' output handy. 'nm' reports that all the calc() methods corresponding to the types in OperatorShim are present in the .o file, but the templated default methods are not exported from the .dylib. – Chuck Fry Jul 28 '14 at 19:28
  • just tried to build that code from svn. Its working for me. Seems like you fixed the problem... – Icarus3 Jul 29 '14 at 10:04
  • Icarus3 is correct. I reverted to an earlier approach where the templatized calc() method implementations are in the header file, rather than a separate .cc file. I will revise the instructions above to get the previous revision. I would still like to solve the linker problem if possible. – Chuck Fry Jul 29 '14 at 14:55

0 Answers0