Strange 'undefined reference to ...' linker error

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Strange 'undefined reference to ...' linker error

Post by Virtlink »

I can't wrap my head around it. My kernel.c file is throwing linker errors: undefined reference to `Console_Clear'.

Code: Select all

#include <types.h>
#include <console.h>

void Kernel_Main()
{
	Console_Initialize();
	Console_Clear();
    
    for(;;);
}
The strangest thing is that when I remove Console_Initialize(), it suddenly links correctly. And when I leave it there but I copy Console_Clear() before Console_Initialize() then it also links. However, when I replace Console_Clear() by Console_Write() for example, then I get an 'undefined reference to Console_Write' error instead. What is happening here?

I am using a cross-compiler (i586-elf-*) on Cygwin, made a few days ago from fresly downloaded gcc-core-4.5.1 and binutils-2.20 sources, compiled with Cygwin's gcc-core 3.4.4-999 (the most recent stable version I could select). It feels very similar to this question, but that one was never answered.

Now follows the hopefully relevant information. I've stripped as much useless code as possible, and tested that the problem still persists. Thanks in advance to anyone pointing me in the right direction!

Code: Select all

$ make all
nasm  -I./src/ -o src/boot.obj -f elf src/boot.asm
i586-elf-gcc -Wall -O -fno-stack-protector -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o src/kernel.o src/kernel.c
i586-elf-gcc -Wall -O -fno-stack-protector -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o src/system.o src/system.c
i586-elf-gcc -Wall -O -fno-stack-protector -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o src/memory.o src/memory.c
i586-elf-gcc -Wall -O -fno-stack-protector -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o src/string.o src/string.c
i586-elf-gcc -Wall -O -fno-stack-protector -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o src/console.o src/console.c
i586-elf-ld  -o ../myos_kernel.bin -T link.ld src/boot.obj  src/kernel.o src/system.o src/memory.o src/string.o src/console.o
src/kernel.o: In function `Kernel_Main':
kernel.c:(.text+0x9): undefined reference to `Console_Clear'
make: *** [../myos_kernel.bin] Error 1
The linker script is as follows:

Code: Select all

OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata)
    . = ALIGN(4096);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}
The console.h and types.h files are as follows:

Code: Select all

#ifndef __CONSOLE_H
#define __CONSOLE_H
#include <types.h>

extern void Console_SetCursorPosition(nint left, nint top);
extern void Console_Clear();
extern void Console_Write(const achar* text);
extern void Console_WriteLine(const achar* text);
extern void Console_WriteFromEnd(const achar* text);
extern byte Console_GetBackgroundColor();
extern void Console_SetBackgroundColor(byte value);
extern byte Console_GetForegroundColor();
extern void Console_SetForegroundColor(byte value);
extern void Console_ResetColor();
extern void Console_Initialize();

#endif  // __CONSOLE_H



#ifndef __TYPES_H
#define __TYPES_H
 
#ifndef NULL
#define NULL ((void*) 0)
#endif
 
typedef unsigned char         byte;
typedef   signed char        sbyte;
typedef   signed short       int16;
typedef unsigned short      uint16;
typedef   signed long        int32;
typedef unsigned long       uint32;
 
typedef   signed int          nint;
typedef unsigned int         unint;
 
typedef 		 char        achar;

typedef enum { false, true } bool;
 
#endif    // __TYPES_H
Symbols from console.o and kernel.o:

Code: Select all

$ objdump -t src/console.o

src/console.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 console.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000004 l     O .bss   00000004 cursor_x
00000008 l     O .bss   00000004 cursor_y
00000000 l     O .data  00000002 textcolor
00000000 l     O .bss   00000004 consolememory
00000000 l    d  .rodata.str1.1 00000000 .rodata.str1.1
00000000 l    d  .comment       00000000 .comment
00000000 g     F .text  00000095 Console_SetCursorPosition
00000000         *UND*  00000000 System_WritePortByte
00000095 g     F .text  00000054 Console_Clear
00000000         *UND*  00000000 Memory_SetW
000000e9 g     F .text  00000157 Console_Write
00000000         *UND*  00000000 Memory_Move
00000000         *UND*  00000000 String_Length
00000240 g     F .text  0000001c Console_WriteLine
0000025c g     F .text  00000028 Console_WriteFromEnd
00000000         *UND*  00000000 String_VisibleLength
00000284 g     F .text  0000000b Console_GetBackgroundColor
0000028f g     F .text  00000010 Console_SetBackgroundColor
0000029f g     F .text  0000000a Console_GetForegroundColor
000002a9 g     F .text  00000011 Console_SetForegroundColor
000002ba g     F .text  0000000a Console_ResetColor
000002c4 g     F .text  00000016 Console_Initialize

$ objdump -t src/kernel.o

src/kernel.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 kernel.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .comment       00000000 .comment
00000000 g     F .text  0000000f Kernel_Main
00000000         *UND*  00000000 Console_Initialize
00000000         *UND*  00000000 Console_Clear
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Strange 'undefined reference to ...' linker error

Post by Virtlink »

Yes, Console_Initialize() also calls Console_Clear() (and further only sets the pointer to text memory). However, removing the Console_Clear() call from Console_Initialize() doesn't make any difference.

And for your second suggestion: it increases the number of undefined references. And apparently the order of the object files on the linker's command line also makes a difference. It shouldn't, right?

Code: Select all

$ i586-elf-ld -o ../../myos_kernel.bin -T ../link.ld console.o boot.obj memory.o string.o console.o kernel.o system.o
console.o: In function `Console_Clear':
console.c:(.text+0xc2): undefined reference to `Memory_SetW'
console.c:(.text+0x1d7): undefined reference to `Memory_Move'
console.c:(.text+0x1fb): undefined reference to `Memory_SetW'
console.c:(.text+0x229): undefined reference to `String_Length'
console.c:(.text+0x266): undefined reference to `String_VisibleLength'
console.o: In function `Console_Clear':
console.c:(.text+0xc2): undefined reference to `Memory_SetW'
console.o: In function `Console_Write':
console.c:(.text+0x1d7): undefined reference to `Memory_Move'
console.c:(.text+0x1fb): undefined reference to `Memory_SetW'
console.c:(.text+0x229): undefined reference to `String_Length'
console.o: In function `Console_WriteFromEnd':
console.c:(.text+0x266): undefined reference to `String_VisibleLength'
kernel.o: In function `Kernel_Main':
kernel.c:(.text+0x9): undefined reference to `Console_Clear'

$ i586-elf-ld -o ../../myos_kernel.bin -T ../link.ld boot.obj console.o kernel.o system.o string.o memory.o
console.o: In function `Console_Write':
console.c:(.text+0x229): undefined reference to `String_Length'
kernel.o: In function `Kernel_Main':
kernel.c:(.text+0x9): undefined reference to `Console_Clear'

$ i586-elf-ld -o ../../myos_kernel.bin -T ../link.ld boot.obj console.o kernel.o system.o memory.o string.o
kernel.o: In function `Kernel_Main':
kernel.c:(.text+0x9): undefined reference to `Console_Clear'

$ i586-elf-ld -o ../../myos_kernel.bin -T ../link.ld console.o boot.obj kernel.o system.o memory.o string.o
kernel.o: In function `Kernel_Main':
kernel.c:(.text+0x9): undefined reference to `Console_Clear'

$ i586-elf-ld -o ../../myos_kernel.bin -T ../link.ld console.o boot.obj system.o memory.o string.o kernel.o
console.o: In function `Console_Clear':
console.c:(.text+0xc2): undefined reference to `Memory_SetW'
console.o: In function `Console_Write':
console.c:(.text+0x1fb): undefined reference to `Memory_SetW'
kernel.o: In function `Kernel_Main':
kernel.c:(.text+0x9): undefined reference to `Console_Clear'

$ i586-elf-ld -o ../../myos_kernel.bin -T ../link.ld console.o boot.obj memory.o string.o kernel.o system.o
console.o: In function `Console_Clear':
console.c:(.text+0xc2): undefined reference to `Memory_SetW'
console.o: In function `Console_Write':
console.c:(.text+0x1fb): undefined reference to `Memory_SetW'
kernel.o: In function `Kernel_Main':
kernel.c:(.text+0x9): undefined reference to `Console_Clear'
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Re: Strange 'undefined reference to ...' linker error

Post by JamesM »

Can you please try it using GCC instead of LD? You'll need to add a Wl, option before the -T option to pass the link script to LD:

Code: Select all

i586-elf-gcc -o ../../myos_kernel.bin -Wl,-T,../link.ld console.o boot.obj memory.o string.o console.o kernel.o system.o
Cheers,

James
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Strange 'undefined reference to ...' linker error

Post by Virtlink »

Thanks for thinking with me.

Gcc calls ld, which in turn can't find crt0.o:

Code: Select all

$ i586-elf-gcc -o ../../myos_kernel.bin -Wl,-T,../link.ld console.o boot.obj memory.o string.o console.o kernel.o system.o
/usr/local/cross/lib/gcc/i586-elf/4.5.1/../../../../i586-elf/bin/ld: crt0.o: No
such file: No such file or directory
collect2: ld returned 1 exit status
I searched the internet a bit, but couldn't find a clear cause for this error.

However, I just found one way to completely solve the problem: remove the OUTPUT_FORMAT("binary") line from the link-file. It then creates an ELF file instead, and no strange errors anymore. While I personally don't mind having my kernel in an ELF file instead, a plain binary file has less overhead. But maybe someone gets an idea from this hint as to what may be the underlying problem?
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Re: Strange 'undefined reference to ...' linker error

Post by JamesM »

Hi,

You can remove the GCC error by adding "-nostartfiles -nostdlib" to your command line.

Try that.

Cheers,

James
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Strange 'undefined reference to ...' linker error

Post by Virtlink »

JamesM wrote:You can remove the GCC error by adding "-nostartfiles -nostdlib" to your command line.
Indeed, that solved the ctr0.o error, but gcc spits out many undefined reference errors just like ld, and ends with the message "collect2: ld returned 1 exit status". However, again just like ld, gcc gives no errors when I remove the OUTPUT_FORMAT("binary") line from the link script.
berkus wrote:Do you then have undefined symbols in your ELF file or not?
Invoking "objdump -t myos_kernel.bin" results in a list of symbols where none have *UND* before the name. So: no undefined symbols in the resulting ELF file. However, it still isn't a binary file.
Virtlink
Member
Member
Posts: 34
Joined: Thu Jun 05, 2008 3:53 pm
Location: The Netherlands
Contact:

Re: Strange 'undefined reference to ...' linker error

Post by Virtlink »

Well, while reducing the kernel, the problem starts to show almost immediately. I have reduced the kernel to the startup .asm file (with the multiboot header), a console.c file and the kernel.c file which calls some functions from the console.c file. It includes the link script and makefiles, so anyone who wants to give it a try just has to copy the contents of the attached file into a folder and run:

Code: Select all

make all
./update.sh
As noted before, when you change the order of the files in the makefile, the problem *seems* to disappear, but is doesn't. Removing the OUTPUT_FORMAT("binary") statement in link.ld resolves the problem, but creates a fully functional ELF file instead of a binary file. After you then call ./update.sh, you can mount the resulting cd.iso to see the kernel printing something on the screen.

The problem appears when using gcc 4.5.1 and binutils 2.20, cross-compiled on Cygwin 1.7.7. I have tested this on two different systems, and both show the same results. Apart from the packages installed by Cygwin by default, I installed the following packages (and automatically any packages required for those):
All > Devel > gcc-core (3.4.4-999)
All > Devel > make (3.81-2)
All > Devel > flex (2.5.35-1)
All > Devel > bison (2.4.2-1)
All > Devel > gcc+g++ (3.4.4-999)
All > Libs > libgmp-devel (4.3.1-3)
All > Libs > libmpfr-devel (2.4.1-4)
All > Libs > libmpc-devel (0.8-1)
All > Utils > genisoimage (1.1.7.1-1)
I used the guide from GCC Cross-Compiler to make the cross-compiler.
Attachments
Reproduction.rar
Problem reproduction
(59.96 KiB) Downloaded 138 times
kaitsu
Posts: 3
Joined: Mon Oct 25, 2010 5:31 pm

Re: Strange 'undefined reference to ...' linker error

Post by kaitsu »

berkus wrote:
Virtlink wrote: gcc 4.5.1
All > Devel > gcc-core (3.4.4-999)
All > Devel > gcc+g++ (3.4.4-999)
I don't think gcc 3.4 and 4.5 are related, but you might be mixing up toolchains.

I compiled your example file as downloaded without modifications and it compiles, links and runs properly, therefore I conclude a toolchain problem.
I've ran into this very same problem completely independently and religiously following the GCC Cross Compiler tutorial.

I believe he means to say he's used gcc 4.5.1 source to create his toolset, compiled with gcc 3.4. So it's just compiler compiling some source code. Or am I missing something here?

Any chance you could let us know what your configuration looks like, since everything is running smooth on your system?

My case is perhaps even stranger than what the OP was experiencing. Consider the following as my main.cpp

Code: Select all

extern "C" void _kmain( void )
{
	Console::Clear( );
	Console::PutChar( 'x', 0, 0 );
}
By itself it will not build. The linker gives me an error about Console::Clear( ) being undeclared. If I comment out the line with PutChar, everything links and executes fine so obviously Clear is there somewhere. Clear and PutChar are defined in the very same .h and .cpp files. Neither one of these functions call any other functions. Here comes the strange part: I discovered that if I insert

Code: Select all

void Foo( ) { }
between the function bodies in the .cpp file (and nothing in the .h), again everything links and executes fine! So definetely something is amiss with my i586-elf-ld.exe, but what and how to fix it?

As what comes to the solution OP figured, I'm quite keen on having the output file in binary format, not ELF.

Just for fun I also made a x86_64 toolset (in addition to trying everything what was suggested in this thread), which compiles and links fine (but does not run since CPU is still in 32bit mode). So I started to wonder if it has to do with my system being 64 bit.

Or some strange alignment problem in my object files? I'm lost.

Any help is greatly appreciated! Source code can be made available, but as already tried it probably won't bring so much.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Strange 'undefined reference to ...' linker error

Post by gerryg400 »

As what comes to the solution OP figured, I'm quite keen on having the output file in binary format, not ELF.
It seems to be a bug in ld w.r.t. binary output files. Why not build an ELF file and convert it to binary format afterwards ? ELF files are very good during debugging anyway because you can disassemble them and get some symbols.
If a trainstation is where trains stop, what is a workstation ?
kaitsu
Posts: 3
Joined: Mon Oct 25, 2010 5:31 pm

Re: Strange 'undefined reference to ...' linker error

Post by kaitsu »

gerryg400 wrote:Why not build an ELF file and convert it to binary format afterwards ?
I'm willing to give that a go, if only I had a tool that could convert ELF to binary. Cygwin does not have elf2bin. Where could one obtain a copy?
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Strange 'undefined reference to ...' linker error

Post by gerryg400 »

I think objcopy does it.
If a trainstation is where trains stop, what is a workstation ?
kaitsu
Posts: 3
Joined: Mon Oct 25, 2010 5:31 pm

Re: Strange 'undefined reference to ...' linker error

Post by kaitsu »

gerryg400 wrote:I think objcopy does it.
And indeed it does!

Everything compiles and runs now. You just saved my week, thanks mate!
Post Reply