0

Okay, I'm rather new to gcc and ld and I'm clearly missing something, but I can't seem to find a specific answer here, in "Using ld", or elsewhere on the net.

I'm using a simple source code file I pulled off the net (I've saved it as dummy.c):

 #include <stdio.h>

 int main () {
    int ch;

    for( ch = 75 ; ch <= 100; ch++ ) {
       printf("ASCII value = %d, Character = %c\n", ch , ch );
    }

    return(0);
 }

 gcc dummy.c 

works, and then

 ./a.out 

also works.

And, the more specific:

 gcc -o dummy dummy.c 

works, and then

 ./dummy 

also works.

But then I delete a.out and dummy and try:

 gcc -c dummy.c 

which produces dummy.o

And then I do:

 ld -o dummy dummy.o -lc

But this results in:

 ld: warning: cannot find entry symbol _start; defaulting to 0000000008049020

and, although it produces dummy, trying ./dummy results in:

 -bash: ./dummy: No such file or directory

even though dummy is clearly in the directory (with permissions 755).

What am I doing wrong?

=====

As recommended by ssbssa below, I tried gcc -v dummy.c --> The result was:

 Using built-in specs.
 COLLECT_GCC=gcc
 COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/8/lto-wrapper
 Target: i686-linux-gnu
 Configured with: ../src/configure -v --with-pkgversion='Debian 8.3.0-6' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --program-prefix=i686-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-targets=all --enable-multiarch --disable-werror --with-arch-32=i686 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
 Thread model: posix
 gcc version 8.3.0 (Debian 8.3.0-6)
 COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
  /usr/lib/gcc/i686-linux-gnu/8/cc1 -quiet -v -imultiarch i386-linux-gnu dummy.c -quiet -dumpbase dummy.c -mtune=generic -march=i686 -auxbase dummy -version -o /tmp/ccbFKhxi.s
 GNU C17 (Debian 8.3.0-6) version 8.3.0 (i686-linux-gnu)
         compiled by GNU C version 8.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.20-GMP

 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 ignoring nonexistent directory "/usr/local/include/i386-linux-gnu"
 ignoring nonexistent directory "/usr/lib/gcc/i686-linux-gnu/8/../../../../i686-linux-gnu/include"
 #include "..." search starts here:
 #include <...> search starts here:
  /usr/lib/gcc/i686-linux-gnu/8/include
  /usr/local/include
  /usr/lib/gcc/i686-linux-gnu/8/include-fixed
  /usr/include/i386-linux-gnu
  /usr/include
 End of search list.
 GNU C17 (Debian 8.3.0-6) version 8.3.0 (i686-linux-gnu)
         compiled by GNU C version 8.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.20-GMP

 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 Compiler executable checksum: c52019c44a1e362eeb6021f108314d7e
 COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
  as -v --32 -o /tmp/ccORYUuk.o /tmp/ccbFKhxi.s
 GNU assembler version 2.31.1 (i686-linux-gnu) using BFD version (GNU Binutils for Debian) 2.31.1
 COMPILER_PATH=/usr/lib/gcc/i686-linux-gnu/8/:/usr/lib/gcc/i686-linux-gnu/8/:/usr/lib/gcc/i686-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/8/:/usr/lib/gcc/i686-linux-gnu/
 LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/8/:/usr/lib/gcc/i686-linux-gnu/8/../../../i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/8/../../../../lib/:/lib/i386-linux-gnu/:/lib/../lib/:/usr/lib/i386-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/i686-linux-gnu/8/../../../:/lib/:/usr/lib/
 COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
  /usr/lib/gcc/i686-linux-gnu/8/collect2 -plugin /usr/lib/gcc/i686-linux-gnu/8/liblto_plugin.so -plugin-opt=/usr/lib/gcc/i686-linux-gnu/8/lto-wrapper -plugin-opt=-fresolution=/tmp/cci9u5um.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 --build-id --eh-frame-hdr -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 -pie /usr/lib/gcc/i686-linux-gnu/8/../../../i386-linux-gnu/Scrt1.o /usr/lib/gcc/i686-linux-gnu/8/../../../i386-linux-gnu/crti.o /usr/lib/gcc/i686-linux-gnu/8/crtbeginS.o -L/usr/lib/gcc/i686-linux-gnu/8 -L/usr/lib/gcc/i686-linux-gnu/8/../../../i386-linux-gnu -L/usr/lib/gcc/i686-linux-gnu/8/../../../../lib -L/lib/i386-linux-gnu -L/lib/../lib -L/usr/lib/i386-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/i686-linux-gnu/8/../../.. /tmp/ccORYUuk.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/i686-linux-gnu/8/crtendS.o /usr/lib/gcc/i686-linux-gnu/8/../../../i386-linux-gnu/crtn.o
 COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686' 

The only thing that even looks a little bit like "ld" seems to be in the next -to-the-last line, a little less than halfway across the scrolling screen at:

 /lib/ld-linux.so.2 -pie 

Thanks ssbssa; I'll play around with that a bit and see what happens. I'll also go through the specific documentation page you pointed to.


After looking into ssbssa's recommended references and a few others, I began to suspect that ld really shouldn't be as complicated as those references make it seem. So, I tried a simple straight assembly source program:

 # From GASProfessional Book - location 108 ff
 # cpuid.s Sample program to extract the processor Vendor ID

 .section .data

 output:
   .ascii "The processor Vendor ID is 'xxxxxxxxxxxx'\n"

 .section .text

 .globl _start

 _start:

   movl $0, %eax           # Get the CPU's Vendor ID
   cpuid

   movl $output, %edi      # Setup the output string
   movl %ebx, 28(%edi)
   movl %edx, 32(%edi)
   movl %ecx, 36(%edi)

   movl $4, %eax           # Display the output string
   movl $1, %ebx
   movl $output, %ecx
   movl $42, %edx
   int $0x80

   movl $1, %eax           # Exit 0
   movl $0, %ebx
   int $0x80

Then I did:

 as -o cpuid.o cpuid.s

and:

 ld -o cpuid cpuid.s

and the command:

 ./cpuid

then produced:

 The processor Vendor ID is 'GenuineIntel'

as expected.

So, in the c program, perhaps I need to play around with main vs. _start ?? Onward, into the breach !!

mdavidjohnson
  • 119
  • 12
  • Try `gcc -v dummy.c` to see how gcc invokes ld, it's not that easy as you think (also, see [ld documentation](https://sourceware.org/binutils/docs/ld/Options.html)). – ssbssa Oct 18 '19 at 17:44
  • Well, you certainly seem to be right about it not being easy. Please see above. – mdavidjohnson Oct 18 '19 at 22:00
  • You edit looks incomplete. – ssbssa Oct 18 '19 at 22:07
  • Yes, it took awhile to get it all in and add the spaces at the left to make it appear as code. – mdavidjohnson Oct 18 '19 at 22:15
  • AFAIK, the `collect2` process of the penultimate line calls then `ld` with more or less the same arguments. – ssbssa Oct 18 '19 at 22:35
  • But is there actually a reason why you need to call `ld` directly instead of letting `gcc` do the hard work for you? Or are you just curious how this works behind the scenes? – ssbssa Oct 18 '19 at 22:40
  • Once I figure this all out, I want to access the cpuid opcode and the nanotimer. I understand these are both significantly easier to accomplish in the assembler than in c. Thus, I will be linking object files, some of which were compiled by c, and others which were assembled by as. I suspect that ld is the way to do that, although I've seen a couple of blurbs in my reading which seem to indicate I can do even that with gcc proper. Still learning. – mdavidjohnson Oct 19 '19 at 23:48
  • I don't know about nanotimer, but there is a [cpuid header interface](https://stackoverflow.com/a/17769071/1983398), or you could use [inline assembly](https://wiki.osdev.org/CPUID#Using_CPUID_from_GCC). – ssbssa Oct 20 '19 at 10:24
  • I want to avoid inline assembly. After I get something built and running on x86-64, I'll probably want to port it to ARM and perhaps a few other things. Keeping the assembly completely separate from c should greatly facilitate that. I'll go take a look at the cpuid header interface though. Thanks. – mdavidjohnson Oct 21 '19 at 14:25
  • I tried a straight assembly program for reference - see above. – mdavidjohnson Oct 21 '19 at 22:25
  • I'm still not sure why you want to use ld directly tho, you can add the object files by assembler to your gcc link command as well. – ssbssa Oct 22 '19 at 15:42
  • Thanks ssbssa, I'll experiment a bit with that too. – mdavidjohnson Oct 25 '19 at 04:27

1 Answers1

1

ld -o dummy dummy.o -lc

In general, you don't want to call ld directly. Instead, use gcc to link. gcc is actually just a driver program that calls the needed sup-processes (compiler proper (cc1 for C, cc1plus for C++), assembler as, linker ld) and also passes quite some options to these sub-processes like paths, libraries, multilib information, startup-code, etc. In short: link with

gcc -o dummy dummy.o

As you only have 1 compilation unit, you can compile, assemble and link with

gcc -o dummy dummy.c

In order to see the commands that gcc is invoking, add option -v.

Similar applies for running the assembler. Having gcc run the assembler can simplify the commands, for example when the C preprocessor should run on the asm source (extension .S or .sx, or use gcc -x assembler-with-cpp module.asm for other extensions).

emacs drives me nuts
  • 2,785
  • 13
  • 23