17

GCC is great with ABI-compatibility as long as you use the same C++ standard [1].

But it strikes me that if a shared library compiled by GCC 4.3 in C++03 mode exposes, say, a std::string, this is going to be a different std::string than that understood by an executable compiled by GCC 4.8 in C++11 mode.

The reason I ask is that I am planning to deploy a program compiled by GCC 4.8 in C++11 mode on CentOS 6, whose maximum packaged GCC is 4.3... and some of the shared libraries (be they third-party C++ libraries or more system-level stuff) will presumably therefore all be C++03. But if that were the case, we'd never be able to deploy any C++11 programs on older Linux distributions, which seems unlikely.

Am I naive to think there might be a problem here? And, if there is, how can I resolve it?

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
  • 2
    inb4 "just try it" Well, it appearing to work doesn't mean it's supposed to – Lightness Races in Orbit Aug 04 '16 at 14:15
  • 1
    I couldn't imagine it could work safely. As you say, if you pass around std::strings between different parts they will likely have different layout or implementation. Even if you don't expose them in interfaces you may get symbol clashes I presume. – Johan Lundberg Aug 04 '16 at 14:17
  • @JohanLundberg: Mm indeed. So there must be some general guidance for this kind of thing. Or does nobody deploy C++11 with third-party CentOS-packaged dependencies on CentOS 6?! – Lightness Races in Orbit Aug 04 '16 at 14:19
  • 1
    [_"If you can't build your code with the standard CentOS 6 g++/glibc/libstdc++, then no, it's not going to run on standard CentOS 6 installs._"](http://stackoverflow.com/a/22111180/560648) Crap... – Lightness Races in Orbit Aug 04 '16 at 14:20
  • [Here](http://stackoverflow.com/questions/19729441/c11-backwards-compatibility)'s some related information. To 'resolve' it, you might want to ship two versions (03 & 11)? – Patrik H Aug 04 '16 at 14:30
  • @PatrikH: Meh. There was a reason I started writing C++11 though :( If I'm going to ship a C++03 version I might as well just go back to C++03 altogether and lose all the benefits of the new language. In fact, since `-std=c++11` is not default even in GCC 4.8, doesn't this mean that a C++11 app won't work out of the box on CentOS 7 either?? – Lightness Races in Orbit Aug 04 '16 at 14:31
  • Have you considered building your own dependencies? If you're building an enterprise application, you'll probably want to do this anyway. If you're building a personal application, then I can't imagine that you're using so many third-party libraries that it becomes completely unruly. – Andrew Aug 04 '16 at 14:33
  • @Andrew: I don't follow why an enterprise application would rebuild third-party dependencies rather than using the default package repository from an _enterprise_ Linux distro ;) – Lightness Races in Orbit Aug 04 '16 at 14:38
  • To prevent the exact scenario that you're describing. You typically need to upgrade hardware, switch/upgrade compilers, use different versions software installed with a distribution, etc. Building from source allows you the flexibility change at your pace. If you look at dependency management system such as conan, this will greatly help out with this problem. – Andrew Aug 04 '16 at 14:42
  • @Andrew: I have no control over the deployment platform. – Lightness Races in Orbit Aug 04 '16 at 14:43
  • But you control which software gets deployed. Right? – Andrew Aug 04 '16 at 14:43
  • @Andrew: My software yes but I'm not going to be able to rely on being able to install a dependency manager on the target platform. Basically an RPM (and _maybe_ a couple of RPMs to go with it) is it. – Lightness Races in Orbit Aug 04 '16 at 14:53
  • @Lightness Build for the target system on a system that you can control (cross compile if necessary) and then deploy your final product. I've only encountered a target system where I was limited what I could deploy and that was due to licensing constraints. We overcame that problem just using a build environment and deployment/target environment. – Andrew Aug 04 '16 at 14:58
  • @Andrew: Well, of course, how else would I build it? That does not seem to address any of the issues described here. – Lightness Races in Orbit Aug 04 '16 at 15:16
  • @Lightness I don't see why it wouldn't. Your problem says that you're using a complier that isn't a system default (ie. You obtained it elsewhere and deployed it on your target system). Why can't you apply the same approach for third party libraries? Otherwise, you're limited to GCC 4.3 since that's what's comes on your target system. – Andrew Aug 04 '16 at 15:19
  • @Andrew: Because it's more complicated than that. The issue is binary compatibility with libraries already present on the target system. Fortunately there are some good suggestions below :) – Lightness Races in Orbit Aug 04 '16 at 15:20
  • @Lightness You've already shown that you can deploy software that doesn't come default to the system. Do the same thing with your libraries. Otherwise you're going to be forced to C++03. – Andrew Aug 04 '16 at 15:22
  • can you recompile the libraries that you use on the target machine? chroot might help. – dau_sama Aug 05 '16 at 00:15

4 Answers4

10

There is a wonderful page on this matter: https://gcc.gnu.org/wiki/Cxx11AbiCompatibility

In short, C++11 in gcc is mostly ABI compatible with c++98, but there are a couple of mismatches. Page above lists all of those.

To alleviate the issue I can suggest following approach:

  • Clearly identify all your dependencies which are C++ libraries. You usually do not have too many of them - boost comes to mind first, do you have anything else?
  • Than you check if the symbols your app needs are in the list of broken ABI (see above). If they are not, you are in the clear.
  • If they are, you recompile the lib, and either distribute it as shared lib together with your app (playing with Rpath flags to make sure your app loads your version) or link statically against it.

Just in case, you might as well link statically against libstdc++.

SergeyA
  • 56,524
  • 5
  • 61
  • 116
  • 3
    Okay, so as I suggested the ABI is not the same across language standards. But how do I resolve the problem? – Lightness Races in Orbit Aug 04 '16 at 14:21
  • The page outlines the algo. If you find a symbol which is non-compatible, the only option is to recompile all the C++ libs your app needs and distribute it with it. That's what we did. – SergeyA Aug 04 '16 at 14:25
  • My question requests a little more detail on what I can do to rectify the situation. Do I need to statically link new builds of all my native third-party dependencies? – Lightness Races in Orbit Aug 04 '16 at 14:27
  • 2
    @LightnessRacesinOrbit, the first thing is to clearly identify all your dependencies which are C++ libraries. You usually do not have too many of them - boost comes to mind first, do you have anything else? Than you check if the symbols your app needs are in the list of broken ABI. If they are, you recompile the lib, and either distribute it as shared lib together with your app (playing with Rpath flags to make sure your app loads your version) or link statically against it. You might as well link statically against libstdc++. – SergeyA Aug 04 '16 at 14:33
  • Hmm okay now we're talking - would you mind adding that into the answer? – Lightness Races in Orbit Aug 04 '16 at 14:38
7

Actually, you can distribute a program compiled with a newer g++ compiler on a vanilla CentOS 6 platform. There are several ways to do this: The easiest is to use the DevToolset 3, which will give you g++ 4.9.2 (the dev toolset 2 will give you gcc 4.8.2). Then, just compile your application with this g++. When distributing your software, you need to make sure to also ship the libstdc++.so that is being shipped with g++ 4.9. Either set the LD_LIBRARY_PATH so it gets picked up on startup, or set the RPATH to tell your executable where to look first for libraries.

Essentially, you can do this also with newer compilers, but then you first need to compile the compiler itself. If you don't want to compile a compiler first, go with a respective dev toolset and you should be fine.

Yes, you can also try to statically link libstdc++.a. Search for the option -static-libstdc++:

When the g++ program is used to link a C++ program, it normally automatically links against libstdc++. If libstdc++ is available as a shared library, and the -static option is not used, then this links against the shared version of libstdc++. That is normally fine. However, it is sometimes useful to freeze the version of libstdc++ used by the program without going all the way to a fully static link. The -static-libstdc++ option directs the g++ driver to link libstdc++ statically, without necessarily linking other libraries statically.

But if you statically link, you will not get any security updates etc. Granted, you will not get the updates, if you ship libstdc++.so on your own as well, but incremental updates maybe easier.

And with respect to running your application: The rule of thumb is: Compile on the oldest platform you need to support, then your binaries (with self-shipped libstdc++ and other required libs) will likely work also on newer versions. That is, if you compile on CentoOS 6, and it works, then you can expect it to also work on CentOS 7. On a related subject, this is exactly the reason why for instance AppImage and related solutions recommend to build on an old system.

Community
  • 1
  • 1
dhaumann
  • 1,375
  • 11
  • 24
  • Or statically link libstdc++ yeah? – Lightness Races in Orbit Aug 04 '16 at 14:40
  • As a follow-up, what about for deployment on CentOS 7? Its GCC is 4.8 but the default in GCC 4.8 is C++03... Or CentOS 6 with GCC 4.3 in `-std=c++0x` mode? – Lightness Races in Orbit Aug 04 '16 at 14:42
  • The rule of thumb is: Compile on the oldest platform, then your binaries (with shipped libstdc++) will likely work also on newer versions. That is, if you compile on CentoOS 6, and it works, then you can expect it to also work on CentOS 7. Better test this, though. – dhaumann Aug 04 '16 at 14:47
  • 1
    As long as you ship libstdc++, there is no reason to not use the C++11 features etc. Just make sure you compile all your required libs with the same compiler (you can even vary the C++ standard version). – dhaumann Aug 04 '16 at 15:02
  • Aha, okay. Presumably then libstdc++ does the necessary namespacing to make it all work regardless of compilation flags. It's just using the same libstdc++ that matters. Thanks! – Lightness Races in Orbit Aug 04 '16 at 15:09
  • So my solution is basically this. No matter what compiler was used, I'm deploying my C++ shared libraries (inc. libstdc++) along with the product and using rpath. I do need to build on the target platform itself because I'm creating an RPM and there are all sorts of dependencies that just wouldn't work out if I tried to build e.g. CentOS 7 package on CentOS 6, or CentOS 6 64-bit package on CentOS 6 32-bit. So I'll use devtoolset on CentOS 6 64-bit and, um, do something else on 32-bit (would prefer not to have to build GCC from source every time I instantiate a virtual build machine though :/). – Lightness Races in Orbit Aug 17 '16 at 20:33
3

In my company we use gcc 5.1.0, compiled and used on CentOS 5.5 (with old gcc on-board).

When we deploy our application we also redistribute libstdc++.so and libgcc_s.so, compiled from gcc 5.1.0 sources.

For example:

/opt/ourapp/lib/libstdc++.so
/opt/ourapp/lib/libgcc_s.so
/opt/ourapp/bin/myapp

And for starting the binary correctly we execute:

LD_LIBRARY_PATH=/opt/ourapp/lib/ myapp.

Hope it helps.

Drawbacks: At least you can't use native gdb on such an environment because DWARF format incompatibilities.

jarlh
  • 35,821
  • 8
  • 33
  • 49
user1503944
  • 349
  • 2
  • 12
  • I'm finding conflicting information about whether redistributing `libgcc_s.so` is necessary. Can you explain why you chose to redistribute it along with `libstdc++.so`? – Lightness Races in Orbit Aug 17 '16 at 20:35
2

If you build your C++11 program with the define _GLIBCXX_USE_CXX11_ABI=0 (see this) and the option --abi-version=2 (see this) you should be compatible with any library build with GCC 4.3, including libstdc++.

The default ABI version was 2 through 4.9, it seems like a safe assumption that CentOS uses the default ABI.

The _GLIBCXX_USE_CXX11_ABI macro will affect the standard library's types, to use the same layout as the pre C++11 version. This will introduce some C++11 conformance issues (the reason why they were changed in the first place), things like the complexity of std::list<>::size().

The --abi-version= command line option affects the compiler's ABI, calling conventions, name mangling etc. The default ABI was 2 from 3.4 through 4.9.

Arvid
  • 10,352
  • 1
  • 27
  • 35
  • (Did you mean ABI version 2?) – Lightness Races in Orbit Aug 04 '16 at 15:15
  • Ooh this is interesting. It seems that besides those changes, I can still use the newer types like `std::thread`, as long as I statically link newer libstdc++ for my own purposes? Because you can't have a conflict with something that didn't exist at all before... That would then be by far the easiest approach for me. – Lightness Races in Orbit Aug 04 '16 at 15:39
  • oh yeah, it does say abi version 2 was default through 4.9. It's a bit unclear I suppose, but you're probably right. 2 seems like a better assumption. – Arvid Aug 04 '16 at 18:15
  • This was promising - ultimately I actually need the features from more recent library builds rather than simply ABI compatibility, so I have ended up redistributing the lot. But good information, thank you! – Lightness Races in Orbit Aug 17 '16 at 20:39
  • ... and I dare say I haven't even come close to finishing testing it yet, so... :) – Lightness Races in Orbit Aug 17 '16 at 20:39