Eva Red
Eva Red

Reputation: 609

Fail to create an executable file linking a static library

In folder test, I create hello.h,hello.c,main.c. My goal is to create a static lib from hello.h, hello.c and an executable file from the library and main.c. The following is what I have done.

hello.h:

#ifndef HELLO_H  
#define HELLO_H  
void hello(const char* name);  
#endif

hello.c:

#include <stdio.h>  
void hello(const char* name){  
    printf("hello %s! \n",name);  
}  

main.c:

#include "hello.h"  
int main(){  
    hello("everyone");  
    return 0;  
}  

In the terminal (in the test folder): I run

gcc -c hello.c
ar crv libmyhello.a hello.o // to create a staticlib 
gcc -c main.c
ld -o Cuteee hello.o -lmyhello
>>> ld: cannot find -lmyhello

I wonder if anything is wrong?

Upvotes: 1

Views: 2267

Answers (3)

Mike Kinghan
Mike Kinghan

Reputation: 61137

This takes account of your comments:

Then I tried ld -o Cuteee main.o -L. -lmyhello but still fails with ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0 ./libmyhello.a(hello.o): In function 'hello': hello.c:(.text+0x1e): undefined reference to 'printf' I am puzzled again.

gcc is the GCC tooldriver for compiling and linking C programs.

When you invoke it with options and inputs that signify you want to a compile a C source file, say hello.c, it first invokes the GNU C compiler, cc1, to compile hello.c file to a temporary assembly file, say /tmp/cc8bfSqS.s. It quietly adds to the compiler commandline various boilerplate options that are invariant for compiling C on your system, to spare you the trouble.

Then it invokes the GNU assembler, as, to assemble /tmp/cc8bfSqS.s to the object file hello.o.

You can pick out all of this from the compilation output if you ask gcc to be verbose, e.g.

gcc -v -c hello.c

When you invoke gcc with options and inputs that signify you want to link object files and possibly libraries into a program or shared library, it invokes the GCC internal tool collect2 to do it - which in turn invokes the system linker ld - and gcc quietly adds to the commandline many boilerplate options, libraries and object files that are always required for linking a C language program or shared library, once again to spare you trouble.

You have used gcc to compile hello.c and main.c and allowed it to Do The Right Thing behind the scenes. You haven't attempted to invoke cc1 and as yourself.

But in contrast, when you come to link your program, you haven't used gcc; you've invoked ld yourself, without any of the boilerplate additions to the the commandline that gcc would make. That's why the linkage fails.

If you link your program with gcc in verbose mode:

gcc -v -o Cuteee main.o -L. -lhello

you can pick the collect2 commandline out of the output, something like:

/usr/lib/gcc/x86_64-linux-gnu/7/collect2 \
-plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so \
-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper \
-plugin-opt=-fresolution=/tmp/ccgWPdno.res \
-plugin-opt=-pass-through=-lgcc \
-plugin-opt=-pass-through=-lgcc_s \
-plugin-opt=-pass-through=-lc \
-plugin-opt=-pass-through=-lgcc \
-plugin-opt=-pass-through=-lgcc_s \
--sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu \
--as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
-pie -z now -z relro -o Cuteee \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o \
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L. \
-L/usr/lib/gcc/x86_64-linux-gnu/7 \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu \
-L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib \
-L/lib/x86_64-linux-gnu -L/lib/../lib \
-L/usr/lib/x86_64-linux-gnu \
-L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. \
main.o -lhello -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc \
--as-needed -lgcc_s --no-as-needed \
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o \
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o

All of those options that are passed to collect2 are passed through to ld. So if you replace /usr/lib/gcc/x86_64-linux-gnu/7/collect2 with ld in that monster commandline (or rather the commandline you get on your own system), you will find that it links your program ./Cuteee.

That is what linking the program with gcc does, over an above:

ld -o Cuteee hello.o -lmyhello

One of the errors that your linkage attempt fails on:

cannot find entry symbol _start

is due to the fact that you haven't linked Scrt1.o (/usr/lib/x86_64-linux-gnu/Scrt1.o, in the commandline above) which contains the C runtime initialization code for a dynamically linked C program: it defines the symbol _start, whose address is the entry point of the program, to which the loader passes initial control at runtime, and after program initialization is complete it calls main.

The other linkage error:

undefined reference to 'printf

is due to the fact that you haven't linked the standard C library, -lc (/lib/x86_64-linux-gnu/libc.so.6).

Programmers don't link with ld directly if they don't have to - e.g. unless they're targeting an application to a bare-metal environment, and you can see why.

Upvotes: 1

user3629249
user3629249

Reputation: 16540

the following proposed code:

  1. corrects several problems in the posted code and in the command line statements.
  2. performs the desired operation
  3. cleanly compiles/links

And now the proposed changes to the posted code and command line statements:

hello.h

#ifndef HELLO_H  
#define HELLO_H  
void hello( const char* );  
#endif
=======================

hello.c:

#include <stdio.h>
#include "hello.h"

void hello(const char* name)
{  
    printf("hello %s! \n",name);  
}  
========================

main.c:

#include "hello.h"  
int main( void )
{  
    hello("everyone");  
    return 0;  
}  
=========================

In terminal (in test folder):

gcc -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c hello.c -o hello.o -I.
ar crv libmyhello.a hello.o 
=========================

gcc -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c main.c  -o main.o -I.
ld -static main.o -o Cuteee -L. -lmyhello
=========================

./Cuteee
=========================

this should eliminate the error message:
>>> ld: cannot find -lmyhello

Upvotes: 1

SHG
SHG

Reputation: 2616

You need to provide -L to let gcc know where to look for your -l libraries:

gcc -c hello.c
ar crv libmyhello.a hello.o
gcc -c main.c
gcc main.o -L. -lmyhello -o Cuteee

To create the final executable it's enough to use gcc, ld is not needed.

See this question in order to understand why you probably don't need to use ld specifically.

Upvotes: 2

Related Questions