Two-level namespace and external symbols

Hi everyone,

I am building my dylib with two-level namespace and I am using nm tool to inspect external symbols in the resulting dylib. I see the following list:

nm -m ./foo1/libfoo1.dylib | c++filt

(undefined) external std::terminate() (from libc++)
0000000000008080 (__DATA_CONST,__const) external typeinfo for foo::Base
0000000000008090 (__DATA_CONST,__const) external typeinfo for foo::Inherited
...

I see that std::terminate will only be taken from libc++ at the runtime. But it confuses me that I dont see any "from" statement for typeinfos.

The question is: Is it possible for ld to replace definition of these typeinfos with other definition provided by another library, or will libfoo1 always use its own definitions of typeinfo symbols?

I am asking because in -flat_namespace mode definitions with the same symbol name are merged between multiple dylibs, and I get one instance of typeinfo for each class in my process. I wonder if I can achieve this with two-level namespace.

Accepted Reply

Mach-O has the concept of a ‘weak external symbol’ [1]. At runtime the first loaded instance of that symbol becomes the primary and any other references end up pointing to it. This is necessary to satisfy the One Definition Rule.

You can see this in action in the standard library:

% nm -m libFoo.dylib | c++filt | grep weak
…
00000000000032c0 (__TEXT,__text) weak external std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<<<std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*)

And if you look in the ld man page you’ll see comments like:

Issue a warning if the resulting final linked image contains weak external symbols. Such symbols require dyld to do extra work at launch time to coalesce those symbols.

My C++ Fu is not good enough to know how this interacts with your typeinfos but it is possible to get this sort of coalescing.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Despite the confusingly-similar name, this is different from the weak linking concept used to support deployment targets.

  • @eskimo Hmm, then what is the point of using two-level namespace if my dylib can still accidentally pickup some symbol definition from another plugin in customer process? I thought two-level namespace was supposed to protect from such issues. For example if my dylib uses boost, and some dylib in customer process uses boost of some old version, potentially my dylib can start using vtable symbol of some old version of template polymorphic class from old boost, this can potentially lead to crash (if order of methods for that class in old boost was different).

Add a Comment

Replies

Mach-O has the concept of a ‘weak external symbol’ [1]. At runtime the first loaded instance of that symbol becomes the primary and any other references end up pointing to it. This is necessary to satisfy the One Definition Rule.

You can see this in action in the standard library:

% nm -m libFoo.dylib | c++filt | grep weak
…
00000000000032c0 (__TEXT,__text) weak external std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<<<std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*)

And if you look in the ld man page you’ll see comments like:

Issue a warning if the resulting final linked image contains weak external symbols. Such symbols require dyld to do extra work at launch time to coalesce those symbols.

My C++ Fu is not good enough to know how this interacts with your typeinfos but it is possible to get this sort of coalescing.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Despite the confusingly-similar name, this is different from the weak linking concept used to support deployment targets.

  • @eskimo Hmm, then what is the point of using two-level namespace if my dylib can still accidentally pickup some symbol definition from another plugin in customer process? I thought two-level namespace was supposed to protect from such issues. For example if my dylib uses boost, and some dylib in customer process uses boost of some old version, potentially my dylib can start using vtable symbol of some old version of template polymorphic class from old boost, this can potentially lead to crash (if order of methods for that class in old boost was different).

Add a Comment

then what is the point of using two-level namespace if my dylib can still accidentally pickup some symbol definition from another plugin in customer process?

Because the vast majority of symbols are not weak external and thus always come from the dynamic library recorded in the source image.

I thought two-level namespace was supposed to protect from such issues.

IMO that’s an unrealistic expectation given that C++ is notoriously challenging to deploy in dynamically linked environments. Again, I’m not a C++ expert, but a quick ’net search for C++ odr dynamic linking reveals no clear answer to this problem, on Apple or other platforms.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"