Wednesday, February 7, 2024

[SOLVED] How to link to libraries correctly in C for Windows

Issue

I'm currently trying my hand at C programming for Windows. I have Windows 11, I code on VS Code, and I have installed MinGW and correctly added it to the PATH. The problem is that I can't link my program to the libiphlapi and ws2_32 libraries.

This is my working environment :

tree /F
D:.
│   Makefile
│
├───.vscode
│       settings.json
│
├───build
├───include
│       windows_backup_scheduler.h
│
├───obj
│       hwid.o
│       main.o
│
└───src
        hwid.c
        main.c

Makefile:

BUILD_DIR := build
INCLUDE_DIR := include
OBJ_DIR := obj
SRC_DIR := src
NAME := windows_backup_scheduler.exe

CC := gcc
CFLAGS := -m64 -fPIE -Wall -Wextra -Werror -pedantic -g3
LFLAGS := -I$(INCLUDE_DIR) -I"C:\MinGW\include" -L"C:\MinGW\x86_64-w64-mingw32\lib" -liphlpapi -lws2_32 -static-libgcc

SRC_FILES := $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC_FILES))
TARGET := $(BUILD_DIR)/$(NAME)

all: $(TARGET)

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
    $(CC) $(CFLAGS) $(LFLAGS) -c $< -o $@

$(TARGET): $(OBJ_FILES)
    $(CC) $(CFLAGS) $(LFLAGS) -o $@ $^

clean:
    @del /Q $(OBJ_DIR)\*.o

fclean: clean
    @del /Q $(BUILD_DIR)\$(NAME)

re: fclean all

windows_backup_scheduler.h

#ifndef WINDOWS_BACKUP_SCHEDULER_H
    #define WINDOWS_BACKUP_SCHEDULER_H

    #ifndef NULL
        #define NULL (void *)0;
    #endif

    #include <stdio.h>
    #include <tchar.h>
    #include <winsock2.h>
    #include <iphlpapi.h>
    #include <windows.h>

    char *GetMACAddress(void);

#endif

main.c

#include <windows_backup_scheduler.h>

int main(void)
{
    printf("MAC %s\n", GetMACAddress());
    return (0);
}

hwid.c

#include <windows_backup_scheduler.h>

char *GetMACAddress(void)
{
    IP_ADAPTER_INFO* AdapterInfo = NULL;
    ULONG ulBufLen = 0;
    DWORD dwStatus = 0;
    char* macAddr = NULL;

    if (GetAdaptersInfo(AdapterInfo, &ulBufLen) == ERROR_BUFFER_OVERFLOW) {
        if ((AdapterInfo = malloc(ulBufLen)) == NULL) {
            return (NULL);
        }
    }
    if ((dwStatus = GetAdaptersInfo(AdapterInfo, &ulBufLen)) != ERROR_SUCCESS) {
        free(AdapterInfo);
        return (NULL);
    }
    if ((macAddr = (char*)malloc(18)) == NULL) {
        free(AdapterInfo);
        return (NULL);
    }
    snprintf(macAddr, 18, "%02X-%02X-%02X-%02X-%02X-%02X",
            AdapterInfo[0].Address[0], AdapterInfo[0].Address[1],
            AdapterInfo[0].Address[2], AdapterInfo[0].Address[3],
            AdapterInfo[0].Address[4], AdapterInfo[0].Address[5]);
    free(AdapterInfo);
    return (macAddr);
}

When I try to compile I get this:

D:\WMP\Training\Windows_Backup_Scheduler>make re
Unable to find D:\WMP\Training\Windows_Backup_Scheduler\build\windows_backup_scheduler.exe
gcc -m64 -fPIE -Wall -Wextra -Werror -pedantic -g3 -Iinclude -I"C:\MinGW\include" -L"C:\MinGW\x86_64-w64-mingw32\lib" -liphlpapi -lws2_32 -static-libgcc -c src/hwid.c -o obj/hwid.o
gcc -m64 -fPIE -Wall -Wextra -Werror -pedantic -g3 -Iinclude -I"C:\MinGW\include" -L"C:\MinGW\x86_64-w64-mingw32\lib" -liphlpapi -lws2_32 -static-libgcc -c src/main.c -o obj/main.o
gcc -m64 -fPIE -Wall -Wextra -Werror -pedantic -g3 -Iinclude -I"C:\MinGW\include" -L"C:\MinGW\x86_64-w64-mingw32\lib" -liphlpapi -lws2_32 -static-libgcc -o build/windows_backup_scheduler.exe obj/hwid.o obj/main.o
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/11.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: obj/hwid.o: in function `GetMACAddress':
D:\WMP\Training\Windows_Backup_Scheduler/src/hwid.c:10: undefined reference to `GetAdaptersInfo'
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/11.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: D:\WMP\Training\Windows_Backup_Scheduler/src/hwid.c:15: undefined reference to `GetAdaptersInfo'
collect2.exe: error: ld returned 1 exit status
make: *** [Makefile:21: build/windows_backup_scheduler.exe] Error 1

VSCode found the header files, and libiphlapi.a and ws2_32.a are in C:\MinGW\x86_64-w64-mingw32\lib and the headers in C:\MinGW\include

Can you provide me with a solution?


Solution

Your variables are not separated out properly.

You need to put flags for the compiler in one set of variables and flags for the linker in a different set of variables. Here for example:

LFLAGS := -I$(INCLUDE_DIR) -I"C:\MinGW\include" -L"C:\MinGW\x86_64-w64-mingw32\lib" -liphlpapi -lws2_32 -static-libgcc

This is wrong because -I is a compiler option, while -L and -l are linker options. You should not have them in the same variable because you don't need to pass compiler flags to the linker and you definitely don't want to pass linker flags to the compiler.

But the main direct problem you are seeing with your link error is that you must put the libraries (-l....) after the object files in the link line. Most linkers are "single-pass" linkers which means they only link symbols that they need at the time they process the library on the command line, and the linker won't know what symbols are needed by the libraries until after it handles the object files.

If you wanted to use the standard make variables (which of course you don't have to do if you don't want to) you should use:

CC := gcc
CFLAGS := -m64 -fPIE -Wall -Wextra -Werror -pedantic -g3
CPPFLAGS := -I$(INCLUDE_DIR) -I"C:\MinGW\include"
LDFLAGS := -L"C:\MinGW\x86_64-w64-mingw32\lib"
LDLIBS := -liphlpapi -lws2_32 -static-libgcc

Then you would write:

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
        $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@

$(TARGET): $(OBJ_FILES)
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)


Answered By - MadScientist
Answer Checked By - David Goodson (WPSolving Volunteer)