3

I have a mixed Free Pascal/C++ project. Debian 5.0 ("Lenny") on i386, FPC 2.4.4. When I run the program, it crashes on the first cout<< call. Funnily, it used to work for some time; some OS update probably broke it. Here's the issue isolated:

p.pas:

{$L c.o}    
program p;
uses initc;
procedure Hello; cdecl; external name 'Hello';

begin
     Hello;
end.     

c.cpp:

#include <iostream>
//void * __dso_handle; //You might need to uncomment that
extern "C" void Hello()
{
    std::cout << "Hello world";
}

Makefile:

p : c.o p.pas Makefile
    fpc p.pas -k-lstdc++

c.o : c.cpp
    g++ -c c.cpp

Make, run, segfault. Tried on a brand new Debian VM - same result.

The crash takes place within basic_fstream::sentry::sentry(). They claim this crash location is consistent with the global cout object not being initialized. That's strange - I thought using initc from the Pascal side makes sure global C++ variables are initialized.

Any ideas, please? Could it be somehow the version of libstdc++ I'm linking against (it's libstdc++.so.6.0.10)?

EDIT: it gets weirder and weirder. I run the same binary (p) on a CentOS 5.3 box - it works as advertised. So probably it's about shared lib versions... I'll go gather some more stats on different Linuces.

EDIT2: one thing I noticed: when I do ldd p on my Debian box, here's what I get:

linux-gate.so.1 =>  (0xb77a6000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb76a6000)
libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb754b000)
libm.so.6 => /lib/i686/cmov/libm.so.6 (0xb7524000)
/lib/ld-linux.so.2 (0xb77a7000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7517000)

And when I do the same on the CentOS box where it works:

libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7ec2000)
libc.so.6 => /lib/libc.so.6 (0xb7d69000)
libm.so.6 => /lib/libm.so.6 (0xb7d40000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7d34000)
/lib/ld-linux.so.2 (0xb7fb7000)

So all C libraries (just not the C++ one) are coming from i686/cmov directory. The Debian machine also has /lib/libc.so.6, and it's different from the one in cmov. What's the deal with that cmov directory? And why two different copies of libc with the same name?

EDIT: even on CentOS, the global constructors are not called - just tested with an ad-hoc global object. It just does not crash in sentry() for some reason. Looks like this is a FPC issue after all. There's a bug report in FPC regarding this behavior.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
  • Might this have to do with calling conventions? What if you add `__stdcall`? – Kerrek SB Nov 17 '11 at 22:11
  • Was this working ever before? It smells like C++ runtime is not initialized (global ctors/dtors not called for example). Are you using GNU linker? This is more a question of linker rather than compiler, and you haven't provided a link command you are using... –  Nov 17 '11 at 22:12
  • @KerrekSB `cdecl` looks right here and anyway, how could it matter on a void function with no parameters? – David Heffernan Nov 17 '11 at 22:30
  • I can't see anything in any docs that would indicate that `initc` initialises a C++ runtime. Do you have a link to such a doc? – David Heffernan Nov 17 '11 at 22:32
  • Generally `iostream` uses some tricks to ensure that the streams are properly initialized even for output from static object constructors (normally this would not be guaranteed due to undefined static initialization order between translation units). Maybe those tricks are not compatible with the initialization by FreePascal. – celtschk Nov 17 '11 at 22:41
  • @DavidHeffernan: perhaps, though there's still the matter of who sets up and cleans up the stack frame... well, I don't know. – Kerrek SB Nov 17 '11 at 22:54
  • @Vlad: I swear it did! An old build of the project (not of the test case) still runs happily on another box. – Seva Alekseyev Nov 17 '11 at 23:18
  • @DavidHeffernan: idea taken from [here](http://bugs.freepascal.org/view.php?id=0015174). Also, the sources of `initc`. – Seva Alekseyev Nov 17 '11 at 23:20
  • I think `initc` is related to C code. But you are wanting to run C++. I'd just link dynamically like sehe suggests. – David Heffernan Nov 17 '11 at 23:21
  • initc was not meant as an documented unit. It is used as a bridge between libc errno, and the internal errno, if there is a difference. It abstracts the name of the library containing libc, and the ways to access errno in a threadsafe manner. (which differ across *nixes). The closest there is as documentation is the original unix rtl design document http://www.stack.nl/~marcov/unixrtl.pdf paragraph 4.2.1 – Marco van de Voort Nov 18 '11 at 19:13
  • Is there a documented way to link FPC to C++? In a way that calls the global constructors? – Seva Alekseyev Nov 18 '11 at 20:18
  • No, since there is no "C++". There are various implementations and compilers. First find out what causes the problem. It could also be something with the linker script. And test 2.6.0rc1 or trunk, to make sure you are not investing a lot of work into problems already solved. – Marco van de Voort Nov 19 '11 at 11:02

3 Answers3

3

Indeed, I tried explicitely linking to a static c.o (with all the various versions of libstdc++.so I could find on my box) and I get the same kind of failure:

Runtime error 216 at $00007F3B9C9EFAD1
  $00007F3B9C9EFAD1

I will try on an older installation shortly. Update Can't make linking c.o work on Maverick either (gcc 4.4.5 and fpc 2.4.0-2ubuntu1.10.10).

I made it work on my Natty box only after changing to dynamic linking:

In p.pas:

{$L c.so}

Makefile

p : c.so p.pas Makefile
    fpc p.pas

c.so : c.cpp
    g++ -shared -fPIC c.cpp -o $@

Run

$ LD_LIBRARY_PATH=$PWD
$ ./p
Hello world
sehe
  • 374,641
  • 47
  • 450
  • 633
0

Initc should cause FPC to switch startup code to a form that calls glibc initializers that then should init C++ via the usual ctor/dtor mechanisms.

Note that initc does NOT switch Pascal memory management to use libmalloc. The pascal code will use its own suballocator, which bases directly on mmap(2)

Since your problems also seem to be memory allocation related, try to use unit "cmem" instead of initc to force the pascal runtime to use libmalloc part of glibc for memory management.

Marco van de Voort
  • 25,628
  • 5
  • 56
  • 89
  • Same difference. :( And I don't see how memory allocation has to do with contructors not being called. – Seva Alekseyev Nov 18 '11 at 13:38
  • The constructor could fail in silent ways due to operations on the heap, halting the chain of constructors, and the FPC startup code might not detect that (and abort) properly. The next step would be debugging the startup code, and/or testing with trunk – Marco van de Voort Nov 18 '11 at 13:48
  • See question edit. It's not the compiler per se, it's a subtle interplay between the compiler and system and/or shared libraries. Also, [this](http://bugs.freepascal.org/view.php?id=15175). – Seva Alekseyev Nov 18 '11 at 14:02
  • Also, if I add an arbitrary global object to c.cpp, its constructor is not called either. – Seva Alekseyev Nov 18 '11 at 21:02
0

Specifically for cout (as well as other global streams), adding the following line to the C++ entry point helps:

std::ios_base::Init();

The larger problem still exists - arbitrary global C++ objects are not constructed.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281