Page 1 of 2

Linking fails with templates? [SOLVED]

Posted: Thu May 03, 2007 2:46 am
by pcmattman
I'm testing out my knowledge of templates (and making sure I know how GCC likes them, I learnt on MSVC++ 6). I've written the following, really basic, class:

Code: Select all

template< typename T >
class Accumulator
{
	public:
		T Value();
		T operator = ( T val );
	private:
		T m_val;
};
This compiles fine (the code is implemented, I'm just not going to show it because it's simple enough, Value() just returns m_val, the '=' operator sets m_val and then returns it.

However, once it hits the linker, all hell breaks loose:

Code: Select all

c:/djgpp/bin/ld-elf.exe: kernel.o: Unrecognized storage class 127 for /4 symbol `__ZN11AccumulatorIj
EaSEj'
c:/djgpp/bin/ld-elf.exe: kernel.o: Unrecognized storage class 127 for /44 symbol `__ZN11AccumulatorI
jE5ValueEv'
The code which causes this:

Code: Select all

Accumulator<int> acc;
acc = 0;
int val = acc.Value();
This is the way I learnt to use templates. GCC doesn't like it. Any ideas?

Edit: solution is on next page...

Posted: Thu May 03, 2007 3:01 am
by os64dev
with the implementation i tried

Code: Select all

template< typename T > 
T Accumulator<T>::Value(){
    return(m_val);
}

template< typename T > 
T Accumulator<T>::operator = (T val){
    return(m_val = val);
}
everything works fine under gcc 4.1.2 cross-compiler.

Posted: Thu May 03, 2007 3:05 am
by pcmattman
I can't get it...

GCC version:

Code: Select all

Using built-in specs.
Target: djgpp
Configured with: /gnu/gcc-4.10/configure djgpp --prefix=/dev/env/DJDIR --disable-nls --disable-werro
r --enable-languages=c,c++,fortran,objc,obj-c++,ada
Thread model: single
gcc version 4.1.0
Edit: It seems to be in the class instance initialization, not in the actual implementation. Accumulator<int> is a cause of the error (presumably because of the constructor?)... As is the = operator and Value().

Edit 2: No member functions work at all, but initialization does:

Code: Select all

Accumulator<int> acc;
acc.test(); // commment this out, it works perfectly, leave uncommented, doesn't work

Posted: Thu May 03, 2007 5:23 am
by Solar
What is the exact command line(s) you're invocating the compiler / linker with?

What does your linker script look like?

Posted: Thu May 03, 2007 5:31 am
by pcmattman
Command lines:

Code: Select all

gcc -I "C:\osdev\cppkern\include" -c *.cc */*.cc -nostdlib -fno-builtin -fno-rtti -fno-exceptions

ld-elf -T linker.ld -o kernel.bin loader.obj *.o
Linker script:

Code: Select all

ENTRY (_loader) 

SECTIONS 
{ 
   . = 0x00100000; 

   .text : 
   { 
      *(.text) 
   } 

   .rodata ALIGN (0x1000) : 
   { 
      *(.rodata) 
   } 

   .data ALIGN (0x1000) : 
   { 
      start_ctors = .; 
      *(.ctor*) 
      end_ctors = .; 
      start_dtors = .; 
      *(.dtor*) 
      end_dtors = .; 
      *(.data) 
   } 

   .bss : 
   { 
      _sbss = .; 
      *(COMMON) 
      *(.bss) 
      _ebss = .; 
   } 
}

Posted: Thu May 03, 2007 5:59 am
by frank
Try adding this behind *(.text)
*(.text.*)

Virtual classes and templates seem to go into sections like .text._ZN6BufferI11FILE_INFO_SLj16EE6CreateEjjj

Posted: Thu May 03, 2007 6:17 am
by mystran
What's the version of binutils? The error message suggests to me that GCC is requesting something not supported by your LD, so make sure you have a compatible (=reasonably new) version of binutils as well.

Posted: Thu May 03, 2007 6:33 am
by os64dev
Does gcc immediatly recognises C++ or do you need to use gcc -x c++ or g++

Posted: Thu May 03, 2007 6:36 am
by Solar
Indeed, you have to use g++ to invoke the C++ compiler, not gcc. You're trying to compile C++ code with a C toolchain.

Posted: Thu May 03, 2007 3:19 pm
by pcmattman
It recognizes *.cc as a c++ file extension. I'm using classes already (and they work as long as they aren't templates)...

Posted: Thu May 03, 2007 3:27 pm
by mystran
Gcc should recognize C++ from .cc or .cpp or .cxx or (on case-sensitive filesystems) .C extension. The error message sounds like your linker doesn't support common symbols, or some similar issue, so please, check that you have reasonably up-to-date binutils.

Gcc emits code that results from template-expansion as common, such that when you link several object files, if they contain the same expansions, you'll only get one copy in the resulting binary. This requires support from linker.

Posted: Thu May 03, 2007 3:28 pm
by Candy
pcmattman wrote:It recognizes *.cc as a c++ file extension. I'm using classes already (and they work as long as they aren't templates)...
Could you try linking with g++ instead of ld? g++ does a number of things slightly different in invoking ld than gcc does, it might be one of those things. Another thing could be that your binutils chain doesn't match your gcc chain, but that's fairly unlikely.

Posted: Thu May 03, 2007 3:31 pm
by pcmattman
Ah... I let G++ run the linker for me, and I have no problems. Apart from the fact that my 'loader.o' object isn't linked in - this is really important because it handles startup (setting up the stack, GDT stuff etc...). How can I tell G++ to tell the linker to link in an object first?

Posted: Thu May 03, 2007 3:44 pm
by Candy
pcmattman wrote:Ah... I let G++ run the linker for me, and I have no problems. Apart from the fact that my 'loader.o' object isn't linked in - this is really important because it handles startup (setting up the stack, GDT stuff etc...). How can I tell G++ to tell the linker to link in an object first?
To be quite honest, you don't. You tell it which objects it can use and which function should be the first, and for all function groups how to place them etc., but you can't force it to locate stuff somewhere. The concept is that you tell it which function should go first (--entry) and that it orders stuff so that the executable format understands that your function is the entry point (as first byte of the file, 257th, entry point pointer there etc.) and that all other required functions are included. It should not skip any files unless they're really not used.

Posted: Thu May 03, 2007 3:57 pm
by pcmattman
Actually there's another reason why that loader has to go first - it holds the multiboot header...