Undefined reference to printf when using GCC cross compiler
Asked Answered
R

5

17

I'm trying to get the following simple 'Hello World' program to compile using a cross compiler (GCC 4.9.2) targeting mips:

#include <stdio.h>

int main()
{
  int x = 5;
  printf("x = %d\n", x);
}

The x variable is there to stop GCC changing printf to puts, which it seems to do automatically for a simple newline-terminated string.

I've built a cross compiler under ${HOME}/xc and am executing it using the following command:

${HOME}/xc/bin/mips-gcc -v hello.c

However, I'm getting the following error:

/tmp/ccW5mHJu.o: In function `main':
(.text+0x24): undefined reference to `printf'
collect2: error: ld returned 1 exit status

I'm assuming this is a problem with the linker, as I'd expect the process to fail earlier if for example stdio.h couldn't be found on the search path. I can compile a simpler program which simply returns zero, so it's not the case that the entire toolchain is broken, presumably just the standard library linking (I'm using newlib 2.2.0-1).

I get the same error regardless of whether I run the cross compiler under Linux (Ubuntu 14.10) or Cygwin (Windows 8).

The full output from GCC is:

Using built-in specs.
COLLECT_GCC=/home/paul/xc/bin/mips-gcc
COLLECT_LTO_WRAPPER=/home/paul/xc/libexec/gcc/mips/4.9.2/lto-wrapper
Target: mips
Configured with: /home/paul/xc/mips/tmp/gcc-4.9.2/configure --prefix=/home/paul/xc --target=mips --enable-languages=c --with-newlib --without-isl --without-cloogs --disable-threads --disable-libssp --disable-libgomp --disable-libmudflap
Thread model: single
gcc version 4.9.2 (GCC) 
COLLECT_GCC_OPTIONS='-v'
 /home/paul/xc/libexec/gcc/mips/4.9.2/cc1 -quiet -v hello.c -quiet -dumpbase hello.c -auxbase hello -version -o /tmp/ccCpAajQ.s
GNU C (GCC) version 4.9.2 (mips)
    compiled by GNU C version 4.9.1, GMP version 6.0.0, MPFR version 3.1.2, MPC version 1.0.3
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/sys-include"
#include "..." search starts here:
#include <...> search starts here:
 /home/paul/xc/lib/gcc/mips/4.9.2/include
 /home/paul/xc/lib/gcc/mips/4.9.2/include-fixed
 /home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/include
End of search list.
GNU C (GCC) version 4.9.2 (mips)
    compiled by GNU C version 4.9.1, GMP version 6.0.0, MPFR version 3.1.2, MPC version 1.0.3
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: cffaaedf0b24662e67a5d97387fc5b17
COLLECT_GCC_OPTIONS='-v'
 /home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/bin/as -EB -O1 -no-mdebug -mabi=32 -o /tmp/ccW5mHJu.o /tmp/ccCpAajQ.s
COMPILER_PATH=/home/paul/xc/libexec/gcc/mips/4.9.2/:/home/paul/xc/libexec/gcc/mips/4.9.2/:/home/paul/xc/libexec/gcc/mips/:/home/paul/xc/lib/gcc/mips/4.9.2/:/home/paul/xc/lib/gcc/mips/:/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/bin/
LIBRARY_PATH=/home/paul/xc/lib/gcc/mips/4.9.2/:/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/lib/
COLLECT_GCC_OPTIONS='-v'
 /home/paul/xc/libexec/gcc/mips/4.9.2/collect2 -plugin /home/paul/xc/libexec/gcc/mips/4.9.2/liblto_plugin.so -plugin-opt=/home/paul/xc/libexec/gcc/mips/4.9.2/lto-wrapper -plugin-opt=-fresolution=/tmp/cc8TAJb9.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc -EB /home/paul/xc/lib/gcc/mips/4.9.2/crti.o /home/paul/xc/lib/gcc/mips/4.9.2/crtbegin.o -L/home/paul/xc/lib/gcc/mips/4.9.2 -L/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/lib /tmp/ccW5mHJu.o -lgcc -lgcc /home/paul/xc/lib/gcc/mips/4.9.2/crtend.o /home/paul/xc/lib/gcc/mips/4.9.2/crtn.o
/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400050
/tmp/ccW5mHJu.o: In function `main':
(.text+0x24): undefined reference to `printf'
collect2: error: ld returned 1 exit status

The build script I'm using is here (I wrote it based on half a dozen tutorials which all suggested slightly different things):

https://github.com/UoMCS/mips-cross-compile

Basically it does the following steps:

  1. Build binutils.
  2. Build GCC (stage 1).
  3. Build newlib.
  4. Build GCC (stage 2).

I'm aware that there are other tools such as crosstool-ng and builtroot, however the person I'm building this toolchain for wants to edit parts of binutils before setting off the build process, and the toolchain also has to work under Cygwin (crosstool-ng won't for various reasons, including case-sensitive file paths).

I think this is probably going to be something obvious, but I've been messing around with this for a week and can't see what it could be. Any help would be greatly appreciated!

Riser answered 3/5, 2015 at 9:38 Comment(7)
Try dumping the list of symbols defined by newlibColossus
How would I do that?Riser
Just out of curiosity: Does int main(void) {}; compile?Averir
Yes, I still get the cannot find entry symbol _start warning (which I think can be ignored based on my experience with ARM) but it does compile successfully.Riser
If i run gcc -v test.c on my Linux, in its output is a -lc for the c-library which is not present in your output hence the undefined reference to printf.Solomonsolon
The spec string "lib" in your mips-gcc seem to be missing to have an entry for the newlib-library. It is responsible for which libraries to include on the command line to the linker. The spec strings can be printed with gcc -dumpspecs. Documentation is here. I could not figure it out where to configure these builtin spec strings.Solomonsolon
I succeded in linking your example, see my updated answer, but I think you can't ignore cannot find entry symbol _start. The entry point is now the start of the .text section, whatever function resides there.Solomonsolon
L
7

It is necessary to build libraries to go with your cross compiler. In particular, you need to have a cross-compiled version of glibc or some other implementation of the standard library, to get a version of printf().

Have a look at this link for an example of the type of things you need to consider to get all the things you need - the cross-compiler, the headers, and libraries.

Ludivinaludlew answered 3/5, 2015 at 10:50 Comment(6)
I have used the instructions in the link you mentioned, but I could never get glibc to compile, which is why I've switched to newlib (which has compiled successfully).Riser
This is probably the closest answer to the eventual solution - I had to use glibc and play around with the various config options until I found ones which worked (--disable-werror was important).Riser
@Riser You're almost done. ${HOME}/xc/bin/mips-gcc hello.c -lc -lcfe -lc should yield an a.out. newlib should work. Just _start is still missing for the argv and calling the main function.Solomonsolon
@Solomonsolon newlib doesn't work at all, GCC fails in the second stage with an error about not being able to find crti.o etc.Riser
@Riser strange, using your build script crti.o was created: see directory contents in the update of my answer.Solomonsolon
That's the glibc version of the build script. crti.o isn't created using the newlib version (there are two branches other than master).Riser
S
0

A custom specs file could work:

cd /home/paul/xc/lib/gcc/mips/4.9.2/
${HOME}/xc/bin/mips-gcc -dumpspecs > specs

Add to the specs file:

*lib:
-lc

Note that there must be empty lines before *lib: and after -lc. Perhaps you have to change the library name to the name of your newlib-c-library. Perhaps more must be added than only -lc, e.g. the *lib:-section on my Linux looks more complex.


UPDATE: The builtin specs lib for the default libraries are configured here:

In file gcc-4.9.2/gcc/gcc.c lines 527-530:

/* config.h can define LIB_SPEC to override the default libraries.  */
#ifndef LIB_SPEC
#define LIB_SPEC "%{!shared:%{g*:-lg} %{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}}"
#endif

In file gcc-4.9.2/gcc/config/mips/elf.h lines 40-42:

/* Leave the linker script to choose the appropriate libraries.  */
#undef  LIB_SPEC
#define LIB_SPEC ""

Perhaps the default LIB_SPEC in gcc.c works for you by commenting out lines 40-42 in elf.h. Perhaps you need to edit elf.h and replace the empty LIB_SPEC with "-lc" or something similar.


UPDATE: When you configured gcc you gave --target=mips. In gcc-4.9.2\gcc\config.gcc there are other mips-targets which are more specific, e.g.mips*-*-linux*, perhaps selecting the appropriate one gives the right LIB_SPEC and linking will be successful.


UPDATE: big endian Linux target: mips-unknown-linux-gnu little endian Linux target: mipsel-unknown-linux-gnu source


UPDATE: Using your build script I was able to link your sample program with following modifications:

In your config.sh:

export ISL_VERSION="0.12.2"

In file gcc-4.9.2/gcc/config/mips/elf.h lines 40-42:

/* Leave the linker script to choose the appropriate libraries.  */
#undef  LIB_SPEC
#define LIB_SPEC "-lc -lcfe -lc"

If you don't want the modification in elf.h the libraries must be given when invoking mips-gcc.


UPDATE:

newlib doesn't work at all, GCC fails in the second stage with an error about not being able to find crti.o etc.

Strange, using your build script crti.o was created:

[osboxes@osboxes 4.9.2]$ pwd
/home/osboxes/xc/lib/gcc/mips/4.9.2
[osboxes@osboxes 4.9.2]$ ll
total 6240
-rw-r--r--. 1 osboxes osboxes    3248 May 16 19:49 crtbegin.o
-rw-r--r--. 1 osboxes osboxes    1924 May 16 19:49 crtend.o
-rw-r--r--. 1 osboxes osboxes    1040 May 16 19:49 crti.o
-rw-r--r--. 1 osboxes osboxes    1056 May 16 19:49 crtn.o
drwxrwxr-x. 3 osboxes osboxes    4096 May 16 19:49 include
drwxrwxr-x. 2 osboxes osboxes    4096 May 16 19:45 include-fixed
drwxrwxr-x. 3 osboxes osboxes    4096 May 16 19:49 install-tools
-rw-r--r--. 1 osboxes osboxes 6289352 May 16 19:49 libgcc.a
-rw-r--r--. 1 osboxes osboxes   56844 May 16 19:49 libgcov.a
drwxrwxr-x. 3 osboxes osboxes    4096 May 16 19:49 plugin
-rw-rw-r--. 1 osboxes osboxes    6215 May 18 18:45 specs
Solomonsolon answered 14/5, 2015 at 16:32 Comment(0)
B
0

Try linking the library on the command line:

${HOME}/xc/bin/mips-gcc -v hello.c -lib

Including the std libraries (lib and io)header links the implementations by default (libc.so or .a). However, you are using a 'user-defined' implementation and may not be linking the proper one.

I suggest explicit linkage on the command line. I'm not certain of the syntax.

EDIT: Or better Still, use a makefile to compile with the following lines, and specifying other include directories in the INCLUDES place holder:

CC = gcc
CXX = g++
INCLUDES =
CFLAGS = -g -Wall $(INCLUDES)
CXXFLAGS = -g -Wall $(INCLUDES)
LDFLAGS = -g
hello: hello.o newlib.o
hello.o: hello.c newlib.h
newlib.o: newlib.c newlib.h

newlib.h is the header file you'll include in newlib.c (implementation/definition) source file (that declares the functions) and hello.c. It may be named differently from stdio.h.

Check this out, it may help:

Why do you have to link the math library in C?

and this too:

http://www.tldp.org/HOWTO/Glibc2-HOWTO-6.html

Burnt answered 15/5, 2015 at 15:24 Comment(0)
A
-1

printf() was implemented in libc,

Please check your c lib, such as glibc, oh, yours is newlib.


  1. try @4566976 's way
  2. use readelf -s check is there a printf section exists in libc.so libc.a

( i'm not sure the lib filename in newlib, mine is glibc )

Arbitral answered 15/5, 2015 at 13:11 Comment(1)
I'm aware that printf is defined in libc, what I want to know is how to fix the linker error. 'Check your C lib' isn't terribly helpful.Riser
P
-1

The most convenient way to achieve this is to use putchar in place of printf. May be you have to change some of your code, or may be you have to add macros/functions that may run like printf.

Peristome answered 17/5, 2015 at 15:9 Comment(1)
That won't work, putchar is part of the standard library so if there's a linker problem it will break on putchar just as it will for printf.Riser

© 2022 - 2024 — McMap. All rights reserved.