"undefined reference to 'main'" : main.o created but main function not compiled

67 Views Asked by At

Please note that as this question is related to my work, I would like to give as few as necessary information about my program.

I am trying to compile a program for a RISC-V microcontroller. I am using the GCC RISC-V compilation tools.

The project contains several files together with a 'main.c' file, which contains a basic 'main' function (empty for now):

int main(void)
{
    return 0;
}

During compilation, I am getting the following message from the linker:

riscv-none-embed/bin/ld.exe : in startup.S : undefined reference to 'main'

I've checked, and the compiler has produced an object file for the main.c file: main.o . But the 'main' function does not appear inside it; when running the nm tool on main.o, I'm getting this output :

0000018e t .L0
00000196 t .L0
0000019c t .L0
000001a4 t .L0
000001aa t .L0
000001c2 t .L0
000001c4 t .L0
000001c8 t .L0
000001ce t .L0
000001ce t .L0
00000076 t .L4
00000104 t .L5
000001e7 N .LASF0
000001ac N .LASF1
00000224 N .LASF10
000000e1 N .LASF11
.
.
.
00000469 N .LASF57
000000e9 N .LASF58
00000151 N .LASF59
0000008a N .LASF6
000004a2 N .LASF60
0000026c N .LASF61
00000000 d some_global_variable
00000000 T some_function0
0000017c T some_function1
00000110 T some_function2
00000050 T some_function3
         U some_function4
         U some_function5

Please note that all of the 'some_functionX' and the global variable were not defined in main.c but in others C-files.

Here is my Makefile (to be correct: the main part of it, I've removed the confidential parts):

STARTUP_FILE := $(ROOT_DIR)/../startup.S
STARTUP_OBJ := $(ROOT_DIR)/../startup.o

TOOL_DIR := $(ROOT_DIR)/../../riscv_gcc/riscv-none-embed-gcc/bin
GCC         := $(TOOL_DIR)/riscv-none-embed-gcc-10.2.0
LD          := $(TOOL_DIR)/riscv-none-embed-ld
GDB         := $(TOOL_DIR)/riscv-none-embed-gdb
OBJCOPY     := $(TOOL_DIR)/riscv-none-embed-objcopy
OBJDUMP     := $(TOOL_DIR)/riscv-none-embed-objdump
READELF     := $(TOOL_DIR)/riscv-none-embed-readelf
CODESIZE    := $(TOOL_DIR)/riscv-none-embed-size 
NMINFO      := $(TOOL_DIR)/riscv-none-embed-nm


LIB := $(LIB_DIR)/lib$(TARGET).a
ELF := $(BIN_DIR)/$(TARGET).elf
BIN := $(BIN_DIR)/$(TARGET).bin
HEX := $(BIN_DIR)/$(TARGET).hex
DASM := $(BIN_DIR)/$(TARGET).dasm

EN_OPTIMIZE = -O0
OPTFLAGS    := -march=rv32imc -mabi=ilp32 -mno-strict-align --specs=nano.specs $(EN_OPTIMIZE)
AFLAGS      := -D__STACK_SIZE=$(STACK_SIZE) -nostartfiles --entry Reset_Handler
LDFLAGS     := $(LINKER_FILE) -Xlinker -Map=$(BIN_DIR)/$(TARGET).map -L$(LIB_DIR)
LDFLAGS     += -falign-functions=4 -falign-jumps -falign-loops -falign-labels
# The -MMD flags additionaly creates a .d file as header dependency with the same name as the .o file.
CFLAGS      := -g -Wall -MMD -Wtype-limits -Woverflow -Wabsolute-value -Wno-unused-label $(INC_DIR) $(EN_OPTIMIZE)
CFLAGS      += -falign-functions=4 -falign-jumps -falign-loops -falign-labels -mno-relax




.PHONY: all
all: $(ELF) $(BIN) $(HEX) $(DASM)


# build object files
$(OBJ): $(SRC)
ifeq ($(wildcard $(OBJ_DIR)),)
    mkdir -p $(OBJ_DIR)
endif
    $(GCC) $(CFLAGS) -c $< -o $@



# Build the library .a 
$(LIB): $(OBJ)
ifeq ($(wildcard $(LIB_DIR)),)
    mkdir -p $(LIB_DIR)
endif
    $(TOOL_DIR)/riscv-none-embed-ar -crs $@ $(OBJ)


# Build .elf
$(ELF): $(LIB) $(OBJ)
ifeq ($(wildcard $(BIN_DIR)),)
    mkdir -p $(BIN_DIR)
endif
    $(GCC) $(CFLAGS) $(STARTUP_FILE) $(LDFLAGS) $(AFLAGS) -l$(TARGET) -o $@


# Build .bin
$(BIN): $(ELF)
ifeq ($(wildcard $(BIN_DIR)),)
    mkdir -p $(BIN_DIR)
endif
    $(OBJCOPY) -O binary $< $@


# Build .hex
$(HEX): $(ELF)
ifeq ($(wildcard $(BIN_DIR)),)
    mkdir -p $(BIN_DIR)
endif
    $(OBJCOPY) -O ihex $< $@


#Build disassembly file .dasm
$(DASM): $(ELF)
ifeq ($(wildcard $(BIN_DIR)),)
    mkdir -p $(BIN_DIR)
endif
    $(OBJDUMP) -d -S $< > $@



.PHONY: clean
clean:
    rm -rf $(OUTPUT_DIR)

Please let me know if more information is required. Thanks for your help.

1

There are 1 best solutions below

1
Chris Dodd On BEST ANSWER

The rule

$(OBJ): $(SRC)
        $(GCC) $(CFLAGS) -c $< -o $@

will compile (only) the first source file in $(SRC), and do that repeatedly for each object file in $(OBJ). Assuming you have mulitple source and object files, all the .o files will end up being the same -- the result of compiling that first source file. This seems consistent with your nm output of main.o as some other source file is actually being compiled to get that, not main.c

What you actually want is a rule more like

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

this is a pattern rule that makes each file matching the pattern on the left depend on the pattern on the right, and will compile each source file to produce the corresponding object file.