When I first started compiling C programs in Linux, I ran into one of the most confusing errors I’d ever seen:
undefined reference to `readFile'
collect2: error: ld returned 1 exit status
I had three source filesmain.c
, process_info.c
, and process_info.h
and I was confident I was compiling them correctly. But no matter how many times I ran gcc
, I hit the same wall. If you’ve ever been in this situation, let me walk you through exactly what went wrong, how I fixed it, and how I expanded the project to practice Linux system programming.
Pinpointing the Error
At first, I thought the compiler was broken or my commands were wrong. But this was not a compiler error—it was a linker error. The compiler had accepted my code, but during linking it couldn’t find the actual definition of the function readFile
.
Looking back at my files, I noticed:
- In
process_info.h
I declared:void readFile(char *buffer);
- In
process_info.c
I accidentally defined:void readFIle(char *buffer) { ... }
Notice the subtle difference: readFile
vs readFIle
. That one misplaced capital letter was enough for the linker to fail. In C, function names are case-sensitive, so readFile
and readFIle
are two completely different functions.
Fix the Code
Once I corrected the spelling so the declaration and definition matched, the program compiled. Along the way, I also cleaned up some smaller issues:
- Replaced
write(WRITE_INDEX, ...)
withwrite(STDOUT_FILENO, ...)
to make the output clearer. - Added proper
close()
calls for file descriptors. - Made sure the message sizes were consistent.
Here’s the minimal corrected version that finally compiled and worked.
Process_info.h
#ifndef PROCESS_INFO_H
#define PROCESS_INFO_H
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define MAX_BUFF_SIZE 1024
#define FILE_DESC_SIZE 2
#define IS_CHILD 0
#define MESSAGE_SIZE 12
#define READ_INDEX 0
#define WRITE_INDEX 1
#define FILE_DIR_1 "/workspace/CSCI_474/Project1/filedat1"
#define FD_ERROR -1
// Function prototype
void readFile(char *buffer);
#endif
Process_info.c
#include "process_info.h"
void readFile(char *buffer) {
int fileDesc;
const char *filename = FILE_DIR_1;
fileDesc = open(filename, O_RDONLY);
if (fileDesc == FD_ERROR) {
perror("Cannot open input file");
exit(1);
}
ssize_t n = read(fileDesc, buffer, MAX_BUFF_SIZE);
if (n < 0) {
perror("read failed");
close(fileDesc);
exit(1);
}
if (n > 0) {
write(STDOUT_FILENO, buffer, n);
}
close(fileDesc);
}
main.c
#include "process_info.h"
int main(void) {
const char *msg = "hello world\n";
char msg_buffer[MAX_BUFF_SIZE] = {0};
char file_buffer[MAX_BUFF_SIZE] = {0};
int fileDes[FILE_DESC_SIZE];
readFile(file_buffer);
if (pipe(fileDes) == -1) {
perror("pipe");
return 1;
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 1;
}
if (pid == IS_CHILD) {
close(fileDes[READ_INDEX]);
write(fileDes[WRITE_INDEX], msg, MESSAGE_SIZE);
close(fileDes[WRITE_INDEX]);
_exit(0);
}
close(fileDes[WRITE_INDEX]);
ssize_t n = read(fileDes[READ_INDEX], msg_buffer, MESSAGE_SIZE);
if (n > 0) {
write(STDOUT_FILENO, msg_buffer, n);
}
close(fileDes[READ_INDEX]);
return 0;
}
Compile with:
gcc -Wall -Wextra -O2 main.c process_info.c -o newmain
And finally, it worked!
Expanding the Project
Once I had the basic version compiling, I wanted to practice more. I added extra functionality:
- Command-line argument to pick which file to read.
- Helper functions:
readFileIntoBuffer
andwriteAll
for safer I/O. - Better parent/child roles: child sends file contents, parent prints them.
This way, I could practice pipes, process management, and file handling more realistically. I even tried running:
./pipe_demo /etc/hosts
And watched the parent print the file contents sent through the pipe by the child.
Final Thought
The biggest lesson I learned is this: an undefined reference error usually means a mismatch between declaration and definition. It’s not that the compiler hates me it just can’t find the function body. A single letter out of place (readFIle
vs readFile
) wasted me hours of head scratching. But fixing that small mistake turned into a valuable learning moment. I not only solved the error but also built a stronger understanding of how compilation, linking, and process communication work in Linux. Now, instead of fearing these errors, I see them as stepping stones for growth.