C is said to be a programming language that can get you anytime. There is the character arrays, uninitialised variables containing mysterious numbers and ofcourse, the dreaded pointers. Granted, it was not C itself that got me today but it happened because I was compiling C code so half the blame is on C, sue me. Anyways, this is a story about how a small typo can lead you down a giant rabbit hole and eventually become a wonderful learning experience.

The error

Let’s create a super simple (not too simple) C program with pointers. This code is inspired by the super cool and funny book called Beej’s Guide to C Programming, I highly recommend reading it.


#include <stdio.h>

void increment(int *p) 
{ 
    *p = *p + 1; 
}

int main() 
{
    int i = 10; 
    int *j = &i; 
    printf("i is : %d\n", i); 
    printf("i is also j: %d\n", *j); 
    
    increment(j); 
    
    printf("now i is: %d\n", *j);

}

The increment function takes a pointer p, dereferences it, and increments it by 1. In the main function, we have a variable i with the value 10. We also have a pointer j which points to i.

Now, we increment whatever is at j (in our case it’s 10) with the increment function. Finally, we print a statement to see if j has been incremented or not.

Ideally, the code should print the following statements when compiled:

i is: 10
i is also j: 10
now i is: 11

But, before it can print out anything, we need to compile it. Spoiler alert: Here’s where I made a stupid typo and my computer started levitating.

To compile the C code with gcc I ran this command.

gcc -Wall pointers pointers.c

And, instead of executing quietly it gave me this error:

/usr/bin/ld: cannot use executable file 'pointers' as input to a link
collect2: error: ld returned 1 exit status

What the hell is this I was just trying to code some pointers.

The typo

My first instinct was to google the error itself but wait, the error stems not from gcc but from /usr/bin/ld. ld is the GNU linker. From the man page:

ld combines a number of object and archive files, relocates their data and ties up symbol references. Usually the last step in compiling a program is to run ld.

Now, look at what gcc does, from the man page:

When you invoke GCC, it normally does preprocessing, compilation, assembly and linking.

Ideally, gcc should take care of the linking process when we run it as an ‘overall’ command. I have not specified any linker or linker related command so ld should not be throwing an error. This is where my typo comes in.

Let’s look at the command I ran again:

gcc -Wall pointers pointers.c

What I was trying to do here was to first print all the warnings the compiler might throw indicating problems in the code. Then, I was trying to compile the pointers.c code in the out file pointers. The typo was right after the -Wall flag. I had not specified that pointers should be the out file. The actual command should be:

gcc -Wall -o pointers pointers.c

Here, the filename after -o specifies the out file, in my case, pointers. Now, it compiles and runs without errors:

$ ./pointers
i is : 10
i is also j: 10
now i is: 11

But, why did the typo-ed command made ld throw an error?

The rabbit hole

To understand the error, I spent almost an hour looking through several stackoverflow questions about linking shared libraries with gcc. This question does have some great answers but all of these require us to pass an option to gcc. I wasn’t passing anything instead of -Wall.

The gcc --help does give me this:

-Wl,<options> Pass comma-separated <options> on to the linker.

But, then again, my option was -Wall, not -Wl. Why was gcc treating the file pointers as a shared library?

My final destination was the GNU Compiler documentation. Remember kids, always READ THE DOCS so you don’t fall in a similar rabbit hole like me. Anyways, the very first line in the GNU Compiler’s Options for Linking documentation gives me the answer.

object-file-name

     A file name that does not end in a special recognized suffix is considered to name an object file or library. (Object files are distinguished from libraries by the linker according to the file contents.) If linking is done, these object files are used as input to the linker.

That’s it! Since the pointers outfile did not end in a special recognised suffix, gcc was treating it as an object file. However, when it tried to use this file as an input to the linker, ld threw an error. The error is pretty self-explanatory now.

/usr/bin/ld: cannot use executable file 'pointers' as input to a link
collect2: error: ld returned 1 exit status

Since pointers is an ELF file, ld cannot use it as an input to a link.

Side note: pointers existed already because I had compiled it earlier (without a typo). If it was a fresh compile attempt, there would have been a File Not Found error.

This rabbit hole once again taught me the importance of READING THE DOCS, which I never do and regret later. Don’t be like me, READ THE DOCS NOW okay I will stop screaming and see you in the next blog!