5

I am trying to re-compile an existing C++ application. Unfortunately, I must rely on a proprietary library I only have a pre-compiled static archive of.

I use g++ version 7.3.0 and ld version 2.30.
Whatever GCC version it was compiled with, it is ancient.

The header file defines the method:

class foo {
    int bar(int & i);
}

As nm lib.a shows, the library archive contains the corresponding exported function:

T bar__4fooRi

nm app.o shows my recent compiler employing a different kind of name mangling:

U _ZN4foo9barERi

Hence the linker cannot resolve the symbols provided by the library.

Is there any option to chose the name mangling algorithm?
Can I introduce a map or define the mangled names explicitly?

Hermann
  • 604
  • 7
  • 23
  • Do you have access to the original compiler? Can you discover the version? – Galik Nov 29 '18 at 15:08
  • Comments in the example programs supplied with the library hint to GCC versions 2.91.66, 2.95.2, and 2.95.3. Versions which may or may not relate to the EGCS branch of that era. I suppose the sources of those versions are floating around somewhere, but I do not have the compiler handy, no. – Hermann Nov 29 '18 at 15:21
  • You could generate a [linker script](https://stackoverflow.com/questions/41934005/rename-a-symbol-using-a-linker-script) that provides new-style aliases for the old-style names. – Botje Nov 29 '18 at 15:29
  • @Hermann GCC supplies a name demangling function in `#include ` called `abi::__cxa_demangle()`. If the old compiler comes with the same function you could use it to demangle the names and re-mangle them using the new compiler. Then you could perhaps create a linker script as @Botje suggests? (however there may be other ABI incompatibilities) – Galik Nov 29 '18 at 16:34
  • 1
    Even if you manage to translate all the symbol names, the whole ABI is different isn't it? –  Nov 29 '18 at 22:21
  • @eukaryota The library was compiled years before I even started using Linux. I was not even aware of the issue to this extent. I am used to the toolchain offering backwards compatibility and the ABI being versioned, but apparently the latter is a feature introduced in GCC 3 as stated in https://stackoverflow.com/questions/2801938/gcc-abi-compatibility. I learned something new today. Another reason not to continue this venture into the history of programming. – Hermann Nov 30 '18 at 00:14
  • The sources for gcc 2.95 seem to still be available on http://ftp.gnu.org/gnu/gcc/. I tried to compile it on my ubuntu bionic machine (gcc 7.3) but was not able to. No idea how much work it would be to get it running. –  Nov 30 '18 at 07:16

1 Answers1

2

@Botje's suggestion lead me to writing a linker script like this (the spaces in the PROVIDE stanza are significant):

EXTERN(bar__4fooRi);
PROVIDE(_ZN4foo9barERi = bar__4fooRi);

As far as I understood, this will regard bar__4fooRi as an externally defined symbol (which it is). If _ZN4foo9barERi is searched for, but not defined, bar__4fooRi will take its place.

I am calling the linker from the GNU toolchain like this (mind the order – the script needs to be after the dependant object but before the defining library):

g++ -o application application.o script.ld -lfoo

It looks like this could work.
At least in theory.
The linker now regards other parts of the library, which in turn depends on other unresolvable symbols including (but not limited to) __throw, __cp_pop_exception, and __builtin_delete. I have no idea where these functions are defined nowadays. Joxean Koret shows some locations in this blog post based on guesswork (__builtin_new probably is malloc) – but I am not that confident.

These findings lead me to the conclusion that the library relies on a different style of exception handling and probably memory management, too.

EDIT: The result may be purely academical due to ABI changes as pointed out by @eukaryota, a linker script can indeed be used to "alias" symbols. Here is a complete minimal example:

foo.h:

class Foo {
    public:
    int bar(int);
};

foo.cpp:

#include "foo.h"
int Foo::bar(int i) {
    return i+21;
}

main.cpp:

class Foo {
    public:
    int baa(int); // use in-place "header" to simulate different name mangling algorithm
};

int main(int, char**) {
    Foo f;
    return f.baa(21);
}

script.ld:

EXTERN(_ZN3Foo3barEi);
PROVIDE(_ZN3Foo3baaEi = _ZN3Foo3barEi); /* declare "alias" */

Build process:

g++ -o libfoo.o -c foo.c
ar rvs libfoo.a libfoo.o # simulate building a library
g++ -o app main.o -L. script.ld -lfoo

app is compiled, can be executed and returns expected result.

Hermann
  • 604
  • 7
  • 23
  • Another possibility that occurred to me was getting a decompiler for the old compiler, decompiling and then recompiling the output of that with the new compiler. – Galik Nov 29 '18 at 17:33
  • Another option – but this piece of software is about 20 years old. I reached the point where I can confidently walk up to my boss to announce that we should officially ditch this library and rather buy something new, preferably open source this time. – Hermann Nov 29 '18 at 17:42
  • Hehe. Also you can edit the names directly in a static archive as long as you edit the name to be **shorter** than it was. So you could create shorter versions of the required names but with the new mangling convention and edit those directly into the archive.... – Galik Nov 29 '18 at 17:49