Page 1 of 2

G++ 4 bug?

Posted: Mon Jun 07, 2010 2:25 pm
by arabasso
I'm writing a kernel in C++, and compiling with ELF Cross Compiler g++ 4.5. Seems a bug in g++ or ld, but they simply do not create the "dtors" section. To initialize and finalize the global objects, I'm using the following code:

Code: Select all

class Test
{
    public:
        Test()
        {
            mem = reinterpret_cast<unsigned short *>(0xB8000);

            * mem++ = 0x4041;
        }

        ~Test()
        {
            * mem++ = 0x4042;
        }
    private:
        unsigned short * mem;
};

Test test;

extern "C" unsigned long start_ctors, end_ctors, start_dtors, end_dtors;

extern "C" init(unsigned long magic, unsigned long addr)
{
    for(unsigned long * constructor(& start_ctors); constructor < & end_ctors; ++constructor)
        ((void (*) (void)) (*constructor)) ();

    // Kernel main

    for(unsigned long * destructor(& start_dtors); destructor < & end_dtors; ++destructor)
        ((void (*) (void)) (* destructor)) ();
}
Global constructors are initialized normally, but destructors... nothing happens (the above code should print "AB" on the screen, but strangely only "A" is printed). I seem to be a bug in g++ 4, because the g++ 3.4.6 creates the "dtors" section normally.

Follow my linker script and the output of the readelf:

Code: Select all

ENTRY(start)
SECTIONS
{
	.text 0x100000 :
	{
		code = .; _code = .; __code = .;
		*(.text*)
		*(.gnu.linkonce.t*)

		. = ALIGN(0x1000);
	}

	.rodata :
	{
		*(.rodata*)
		*(.rdata*)
		*(.gnu.linkonce.r*)

		. = ALIGN(0x1000);
	}

	.data :
	{
		data = .; _data = .; __data = .;
		*(.data*)
		*(.gnu.linkonce.d*)

		. = ALIGN(0x1000);
	}

	.ctors :
	{
		start_ctors = .; _start_ctors = .; __start_ctors = .;
		*(.ctor*)
		end_ctors = .; _end_ctors = .; __end_ctors = .;

		. = ALIGN(0x1000);
	}

	.dtors :
	{
		start_dtors = .; _start_dtors = .; __start_dtors = .;
		*(.dtor*)
		end_dtors = .; _end_dtors = .; __end_dtors = .;*/
	}

	.bss :
	{
		bss = .; _bss = .; __bss = .;

		*(.bss*)
		*(.gnu.linkonce.b*)

		. = ALIGN(0x1000);
	}

	end = .; _end = .; __end = .;
}

Code: Select all

There are 6 section headers, starting at offset 0x4024:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

  [ 1] .text             PROGBITS        00100000 001000 001000 00  AX  0   0  4

  [ 2] .data             PROGBITS        00101000 002000 001000 00  WA  0   0  4

  [ 3] .ctors            PROGBITS        00102000 003000 001000 00  WA  0   0  4

  [ 4] .bss              NOBITS          00103000 004000 009000 00  WA  0   0 32

  [ 5] .shstrtab         STRTAB          00000000 004000 000023 00      0   0  1

Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
What happened with the dtors section, "evaporated"?

Re: G++ 4 bug?

Posted: Mon Jun 07, 2010 2:39 pm
by Creature
Might it have something to do with the fact that you've page-aligned all sections, except the .dtors section? (not being sarcastic, just throwing in suggestions). I don't use destructors in my C++ myself (only constructors, because I have no use for destructors whatsoever, if anything would need to be done upon shutdown, I will call shutdown functions instead of relying on possible inner dependencies).

Re: G++ 4 bug?

Posted: Mon Jun 07, 2010 4:48 pm
by arabasso
I must have deleted the page alignment in time to post the message but nevertheless the g++ 4 does not create the "dtors" section. The problem is not only that, local static objects are also problems with g++ 4 (I'm using the ABI implementation in the osdev wiki). The constructors are executed, but not their destructors. If I run the function __cxa_finalize(0), the processor sends triple fault and restarts (implementation __cxa_atexit does not work).

I did a test here with the mingw gcc 3.4.5, and it usually creates a global dtor section, and for the local static objects, only the atexit function it is necessary (no dso, guard, etc.). Here's the code works perfectly in the g++ 3.4.5:

Code: Select all

#define ATEXIT_MAX_DESTRUCTORS			128

struct AtExit
{
	void (* func)(void *);
	void * obj;
};

static int atExitIndex = 0;
static AtExit atExitDestructors[ATEXIT_MAX_DESTRUCTORS];

extern "C" unsigned long start_ctors, end_ctors, start_dtors, end_dtors;

extern "C" void init(unsigned long magic, unsigned long addr)
{
	for(unsigned long * constructor(& start_ctors); constructor < & end_ctors; ++constructor)
		((void (*) (void)) (*constructor)) ();

	// Executes the kernel


	for (int i = 0; i < atExitIndex; i++)
		atExitDestructors[i].func(atExitDestructors[i].obj);

	for(unsigned long * destructor(& start_dtors); destructor < & end_dtors; ++destructor)
		((void (*) (void)) (* destructor)) ();
}

extern "C" void atexit(void (* f)(void *), void * obj)
{
	if (atExitIndex < ATEXIT_MAX_DESTRUCTORS)
	{
		atExitDestructors[atExitIndex].func = f;
		atExitDestructors[atExitIndex].obj  = obj;

		atExitIndex++;
	}
}
Also, sewed one has nothing to do with each other. If I use the destructors or not, this is MY CHOICE, not the compiler. After making several tests I conclude: there are numerous problems in the GCC 4. Not only that, another strange thing is with respect to code optimization. If I turn on the optimization flags of the G++ 4 (-O,-O2,-Os, no flag), the kernel collapses, and the processor sends an ISR 1, 6, 13 (each time a different error!?).

Using gcc 3.4.5, I can sets -O3 that the kernel runs normally (I can sets any flag,-Os,-O2, and everything works correctly). Another extremely annoying thing is that GCC 4 increases the size of the binaries (I have a SDL game where if I use gcc 3.4 to compile, the game is only 180KB. If I use GCC 4, the game jumps to more of 460kb, even using strip symbols and -O2 optimization flag).

I know this will generate discussion, but to comment on possible errors. I believe it would help to improve this great opensource software...

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 4:37 am
by Creature
arabasso wrote:Also, sewed one has nothing to do with each other. If I use the destructors or not, this is MY CHOICE, not the compiler.
You are absolutely right and you should be able to use what you want.
arabasso wrote:Using gcc 3.4.5, I can sets -O3 that the kernel runs normally (I can sets any flag,-Os,-O2, and everything works correctly). Another extremely annoying thing is that GCC 4 increases the size of the binaries (I have a SDL game where if I use gcc 3.4 to compile, the game is only 180KB. If I use GCC 4, the game jumps to more of 460kb, even using strip symbols and -O2 optimization flag).
The answer that many will give you here is: GCC 4's optimization is probably different from GCC 3, and thus optimization messing your kernel up will be because of your code being "broken" (since usually the definition for "proper C/C++ code" is "code that works under any circumstance, regardless of the optimization level". Annoying or not, I know how optimizations tend to mess everything up (still have quite some problems myself which I still need to fix) and I've had some C++ problems as well. Can't help you with the destructor problem however, as I've never really used them myself. If I weren't in a busy exam period right now, I'd give it a try myself.

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 5:30 am
by fronty

Code: Select all

crayon% uname -a
FreeBSD dev.localdomain 8.0-RELEASE FreeBSD 8.0-RELEASE #0: Sat Nov 21 15:02:08 UTC 2009     [email protected]:/usr/obj/usr/src/sys/GENERIC  amd64
g++45 -v
Using built-in specs.
Target: x86_64-portbld-freebsd8.0
Configured with: ./../gcc-4.5-20090924/configure --disable-nls --with-system-zlib --with-libiconv-prefix=/usr/local --with-gmp=/usr/local --program-suffix=45 --libdir=/usr/local/lib/gcc45 --libexecdir=/usr/local/libexec/gcc45 --with-gxx-include-dir=/usr/local/lib/gcc45/include/c++/ --disable-libgcj --prefix=/usr/local --mandir=/usr/local/man --infodir=/usr/local/info/gcc45 --build=x86_64-portbld-freebsd8.0
Thread model: posix
gcc version 4.5.0 20090924 (experimental) (GCC)
crayon% cat main.cc
#include <iostream>

class foo {
public:
        foo(int val) : i(val) { }
        ~foo() {
            std::cout << "destroyed: i = " << i << std::endl;
        }

private:
        int i;
};

int
main(void)
{
        foo bar(10);
        return 0;
}
crayon% g++45 main.cc
crayon% ./a.out
destroyed: i = 10
objdump -h a.out | grep dtors
 19 .dtors        00000010  0000000000500dc0  0000000000500dc0  00000dc0  2**3
At least my g++ 4.5.0 creates .dtors section.

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 5:34 am
by Solar
arabasso wrote:After making several tests I conclude: there are numerous problems in the GCC 4.
When compiling your code.

I'm willing to bet 100 Euro that the problem is not in GCC 4.5 source.

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 6:16 am
by gerryg400
I'm not so sure Solar, 4.5.0 is quite new and I've had some small problems with it that I don't understand. The native versions get a lot of user testing, cross-compiling bugs might take longer to appear. I find that increase in the size of the binaries a bit strange too.

Are you using the latest (or recommended binutils) ?

Why not try 4.3.5 ?

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 7:43 am
by pcmattman
I've seen the same behaviour in my own kernel on GCC 4.4.1. You get a .ctors section but no .dtors. I don't use the .dtors section at all so I ignored it :)

Question - what are the flags you're passing the compiler?

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 7:50 am
by arabasso
Nothing special:

g++ -c init.cpp -o init.o
ld -Tlink.ld -o kernel.ko init.o

Tested on g++ 4.3, 4.4 and 4.5 - binutils 2.20

Code: Select all

There are 7 section headers, starting at offset 0x2034:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

  [ 1] .text             PROGBITS        00100000 001000 001000 00  AX  0   0  4

  [ 2] .ctors            PROGBITS        00101000 002000 000004 00  WA  0   0  4

  [ 3] .bss              NOBITS          00101004 002004 000ffc 00  WA  0   0  4

  [ 4] .shstrtab         STRTAB          00000000 002004 00002d 00      0   0  1

  [ 5] .symtab           SYMTAB          00000000 00214c 000320 10      6  13  4

  [ 6] .strtab           STRTAB          00000000 00246c 000285 00      0   0  1

Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 8:26 am
by Solar
That readelf output of yours... is that kernel.ko, or init.o?

If it's kernel.ko, check if there's a .dtors section in init.o...

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 8:32 am
by Owen
gerryg400 wrote:I'm not so sure Solar, 4.5.0 is quite new and I've had some small problems with it that I don't understand. The native versions get a lot of user testing, cross-compiling bugs might take longer to appear. I find that increase in the size of the binaries a bit strange too.

Are you using the latest (or recommended binutils) ?

Why not try 4.3.5 ?
I have had no issues with GCC 4.5 - even when testing some of the newest features like ASM goto. As for the binary size increase - I'm betting that GCC is just using some space-intensive optimizations (e.g. I don't know if GCC 3.4 knew how align loops, which will surely increase your code size as your binary gets padded with long nops). If you want to compare code size, compare -Os against -Os

As for your code not working at O3 - 99% chance thats a bug of your creation.

As for GCC not creating a destructors section - said section is not part of the IA64 C++ ABI and so compiler and compiler version dependent. G++ 3 and 4 use a different ABI.

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 8:59 am
by arabasso
I managed to solve what is happening with G++ 4, is not a problem, it's just a different way. Actually, the G++ 4 does not create the dtors section, nor puts data about destructors in some section. It Apparently only makes use of the routine "atexit" for everything, for both global objects and local static objects. That's what I noted here at least, correct me if I'm wrong.

About optimization flags, I can not say with certainty that there are no errors in my code, but I believe that is not the case. Depending on the optimization, the compiler attempts to improve the code and ends up ruining it.

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 9:15 am
by JamesM
arabasso wrote:Depending on the optimization, the compiler attempts to improve the code and ends up ruining it.
This usually occurs when your code breaks some of the assumptions the compiler makes about it. If the compiler reasons incorrectly, it will produce incorrect code.

The usual example of this is forgetting "volatile", but there are others.

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 9:33 am
by gerryg400
Or not getting your inline asm constraints correct.

Re: G++ 4 bug?

Posted: Tue Jun 08, 2010 9:43 am
by arabasso
This really happens, when gcc tries to optimize inline assembly or files .S, "it more spoils" that optimizes the code.