I am compiling a riscv32 architecture baremetal helloworld project, compiled with riscv32-unknown -elf-gcc, and the size of the generated executable file is:
riscv32-unknown-elf-size build/helloworld.elf
text data bss dec hex filename
11348 112 4336 15796 3db4 build/helloworld.elf
However, I compiled with clang, and the result is:
llvm-size build/helloworld.elf
text data bss dec hex filename
55500 2488 4236 62224 f310 build/helloworld.elf
csix
December 14, 2022, 9:39am
2
Did you use any compilation flag? If yes, which?
You may want to add -###
to your compile commands also, to show you what the final command looks like. Since some options may be set to different defaults between the two compilers.
this is the gcc Makefile:
######################################
# target
######################################
TARGET = helloworld
######################################
# building variables
######################################
# optimization
OPT = -O2
#######################################
# paths
#######################################
# Build path
BUILD_DIR = build
######################################
# source
######################################
# C sources
C_SOURCES += $(wildcard SoC/gd32vf103/Board/gd32vf103v_rvstar/Source/*.c)
C_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/Drivers/*.c)
C_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/Stubs/newlib/*.c)
C_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/*.c)
C_SOURCES += $(wildcard application/baremetal/helloworld/*.c)
# ASM sources
ASM_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/GCC/*.S)
#######################################
# binaries
#######################################
# The gcc compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx)
# either it can be added to the PATH environment variable.
CC = riscv32-unknown-elf-gcc
CXX = riscv32-unknown-elf-g++
AS = riscv32-unknown-elf-gcc -x assembler-with-cpp
CP = riscv32-unknown-elf-objcopy
SZ = riscv32-unknown-elf-size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
#######################################
# CFLAGS
#######################################
# mcu
MCU = -march=rv32imac -mabi=ilp32 -mcmodel=medlow
# macros for gcc
# AS defines
AS_DEFS = \
-DSYSTEM_CLOCK=108000000 \
-DSYSCLK_USING_HXTAL \
-DDOWNLOAD_MODE=DOWNLOAD_MODE_FLASHXIP \
-DDOWNLOAD_MODE_STRING=\"FLASHXIP\"
# C defines
C_DEFS = \
-DSYSTEM_CLOCK=108000000 \
-DSYSCLK_USING_HXTAL \
-DDOWNLOAD_MODE=DOWNLOAD_MODE_FLASHXIP \
-DDOWNLOAD_MODE_STRING=\"FLASHXIP\"
# common includes
COM_INCLUDES = \
-I/opt/riscv/riscv32-unknown-elf/include \
-I/opt/riscv/riscv32-unknown-elf/include/c++/12.2.0 \
-I/opt/riscv/riscv32-unknown-elf/include/c++/12.2.0/riscv32-unknown-elf \
-I/opt/riscv/riscv32-unknown-elf/include/c++/12.2.0/backward \
-I/opt/riscv/lib/gcc/riscv32-unknown-elf/12.2.0/include \
-I/opt/riscv/lib/gcc/riscv32-unknown-elf/12.2.0/include-fixed
# AS includes
AS_INCLUDES = \
$(COM_INCLUDES) \
-I application/baremetal/helloworld/ \
-I NMSIS/Core/Include \
-I SoC/gd32vf103/Board/gd32vf103v_rvstar/Include \
-I SoC/gd32vf103/Common/Include \
-I application/baremetal/helloworld/inc
# C includes
C_INCLUDES = \
$(COM_INCLUDES) \
-I application/baremetal/helloworld/ \
-I NMSIS/Core/Include \
-I SoC/gd32vf103/Board/gd32vf103v_rvstar/Include \
-I SoC/gd32vf103/Common/Include \
-I application/baremetal/helloworld/inc
# compile gcc flags
ASFLAGS = $(MCU) $(OPT) $(AS_DEFS) $(AS_INCLUDES) -g -ffunction-sections -fdata-sections -fno-common --specs=nano.specs --specs=nosys.specs
# Generate dependency information
ASFLAGS += -MMD -MT $@ -MF "$(@:%.o=%.o.d)"
CFLAGS += $(MCU) $(OPT) $(C_DEFS) $(C_INCLUDES) -g -ffunction-sections -fdata-sections -fno-common --specs=nano.specs --specs=nosys.specs
# Generate dependency information
CFLAGS += -MMD -MT $@ -MF "$(@:%.o=%.o.d)"
#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = SoC/gd32vf103/Board/gd32vf103v_rvstar/Source/GCC/gcc_gd32vf103_flashxip.ld
# libraries
LD_INCLUDES = \
-I application/baremetal/helloworld/ \
-I NMSIS/Core/Include \
-I SoC/gd32vf103/Board/gd32vf103v_rvstar/Include \
-I SoC/gd32vf103/Common/Include \
-I application/baremetal/helloworld/inc
LDFLAGS += $(MCU) $(COM_FALGS) $(C_DEFS) $(OPT) -g -ffunction-sections -fdata-sections -fno-common --specs=nano.specs --specs=nosys.specs -T $(LDSCRIPT) $(LD_INCLUDES)
LDFLAGS += -nostartfiles
LDFLAGS += -Wl,-Map=helloworld.map
LDFLAGS += -Wl,--gc-sections
LDFLAGS += -Wl,--check-sections
LDFLAGS += -Wl,--start-group -lstdc++ -Wl,--end-group -u _isatty -u _write -u _sbrk -u _read -u _close -u _fstat -u _lseek
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
#######################################
# build the application
#######################################
# list of objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.S=.o)))
vpath %.S $(sort $(dir $(ASM_SOURCES)))
$(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR)
$(AS) -c $(ASFLAGS) $< -o $@
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
$(AS) -c $(ASFLAGS) $< -o $@
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
$(CC) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
$(SZ) $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(BIN) $< $@
$(BUILD_DIR):
mkdir $@
#######################################
# clean up
#######################################
clean:
-rm -fR $(BUILD_DIR)
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
and this is my clang Makefile:
######################################
# target
######################################
TARGET = helloworld
######################################
# building variables
######################################
# optimization
OPT = -O2
#######################################
# paths
#######################################
# Build path
BUILD_DIR = build
######################################
# source
######################################
# C sources
C_SOURCES += $(wildcard SoC/gd32vf103/Board/gd32vf103v_rvstar/Source/*.c)
C_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/Drivers/*.c)
C_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/Stubs/newlib/*.c)
C_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/*.c)
C_SOURCES += $(wildcard application/baremetal/helloworld/*.c)
# ASM sources
ASM_SOURCES += $(wildcard SoC/gd32vf103/Common/Source/GCC/*.S)
#######################################
# binaries
#######################################
# The gcc compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx)
# either it can be added to the PATH environment variable.
CC = clang
CXX = clang++
AS = clang -x assembler-with-cpp
CP = llvm-objcopy
SZ = llvm-size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
#######################################
# CFLAGS
#######################################
# mcu
MCU = --target=riscv32-unknown-elf -march=rv32imac -mabi=ilp32 -mcmodel=medlow
# macros for gcc
# AS defines
AS_DEFS = \
-DSYSTEM_CLOCK=108000000 \
-DSYSCLK_USING_HXTAL \
-DDOWNLOAD_MODE=DOWNLOAD_MODE_FLASHXIP \
-DDOWNLOAD_MODE_STRING=\"FLASHXIP\"
# C defines
C_DEFS = \
-DSYSTEM_CLOCK=108000000 \
-DSYSCLK_USING_HXTAL \
-DDOWNLOAD_MODE=DOWNLOAD_MODE_FLASHXIP \
-DDOWNLOAD_MODE_STRING=\"FLASHXIP\"
# common includes
COM_INCLUDES = \
-I/opt/riscv/riscv32-unknown-elf/include \
-I/opt/riscv/riscv32-unknown-elf/include/c++/12.2.0 \
-I/opt/riscv/riscv32-unknown-elf/include/c++/12.2.0/riscv32-unknown-elf \
-I/opt/riscv/riscv32-unknown-elf/include/c++/12.2.0/backward \
-I/opt/riscv/lib/gcc/riscv32-unknown-elf/12.2.0/include \
-I/opt/riscv/lib/gcc/riscv32-unknown-elf/12.2.0/include-fixed
# AS includes
AS_INCLUDES = \
-I application/baremetal/helloworld/ \
-I NMSIS/Core/Include \
-I SoC/gd32vf103/Board/gd32vf103v_rvstar/Include \
-I SoC/gd32vf103/Common/Include \
-I application/baremetal/helloworld/inc
# C includes
C_INCLUDES = \
-I application/baremetal/helloworld/ \
-I NMSIS/Core/Include \
-I SoC/gd32vf103/Board/gd32vf103v_rvstar/Include \
-I SoC/gd32vf103/Common/Include \
-I application/baremetal/helloworld/inc
# common flags
COM_FALGS = --sysroot=/opt/riscv/riscv32-unknown-elf --gcc-toolchain=/opt/riscv/
# compile gcc flags
ASFLAGS = $(MCU) $(COM_FALGS) $(OPT) $(AS_DEFS) $(AS_INCLUDES) -g -ffunction-sections -fdata-sections -fno-common --specs=nano.specs --specs=nosys.specs
# Generate dependency information
ASFLAGS += -MMD -MT $@ -MF "$(@:%.o=%.o.d)"
CFLAGS += $(MCU) $(COM_FALGS) $(OPT) $(C_DEFS) $(C_INCLUDES) -g -ffunction-sections -fdata-sections -fno-common --specs=nano.specs --specs=nosys.specs
# Generate dependency information
CFLAGS += -MMD -MT $@ -MF "$(@:%.o=%.o.d)"
#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = SoC/gd32vf103/Board/gd32vf103v_rvstar/Source/GCC/gcc_gd32vf103_flashxip.ld
# libraries
LD_INCLUDES = \
-I application/baremetal/helloworld/ \
-I NMSIS/Core/Include \
-I SoC/gd32vf103/Board/gd32vf103v_rvstar/Include \
-I SoC/gd32vf103/Common/Include \
-I application/baremetal/helloworld/inc
LDFLAGS += $(MCU) $(COM_FALGS) $(C_DEFS) $(OPT) -g -ffunction-sections -fdata-sections -fno-common --specs=nano.specs --specs=nosys.specs -T $(LDSCRIPT) $(LD_INCLUDES)
LDFLAGS += -nostartfiles
LDFLAGS += -Wl,-Map=helloworld.map
LDFLAGS += -Wl,--gc-sections
LDFLAGS += -Wl,--check-sections
LDFLAGS += -Wl,--start-group -lstdc++ -Wl,--end-group -u _isatty -u _write -u _sbrk -u _read -u _close -u _fstat -u _lseek
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
#######################################
# build the application
#######################################
# list of objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.S=.o)))
vpath %.S $(sort $(dir $(ASM_SOURCES)))
$(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR)
$(AS) -c $(ASFLAGS) $< -o $@
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
$(AS) -c $(ASFLAGS) $< -o $@
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
$(CC) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
$(SZ) $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(BIN) $< $@
$(BUILD_DIR):
mkdir $@
#######################################
# clean up
#######################################
clean:
-rm -fR $(BUILD_DIR)
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
As a novice, I don’t understand the specific meaning of these compilation flags. I hope the two Makefiles I uploaded can provide some clues.
Do both programs work? You can compare the helloworld.map file for clang and gcc to see the differences in what object files were included in the ELF file. Maybe clang is including stuff that’s not needed. Using -### will help you see if switches like -ffunction-sections and -fdata-sections are actually being seen by clang.
The difference between them is that GCC uses “–specs=nano. specs --specs=nosys. specs”. When GCC deletes these two flags, the difference between the size of the compiled executable file is very small.Does Clang have such a similar flag, which can greatly reduce the size of the executable file?
jrtc27
December 20, 2022, 3:36pm
9
All --specs= does is point GCC at a file with more command-line options to use with some slightly fancy syntax that makes it able to choose flags dynamically. Clang’s closest equivalent is config files. But the important thing is to look at the flags in those two files and see what the important flags are, but I’m going to guess it’s because it makes it pick a different set of libraries to link against that are much smaller in size, i.e. it has nothing to do with code generation.
I can see that these two flags really specify two libraries for GCC. For example, “– specs=nano. specs” specifies the use of “libc_nano. a” for GCC instead of “libc. a”. “libc_nano. a” is a small “libc. a” specially designed for GCC, which reduces the volume of executable files generated. How should Clang specify to use “libc_nano. a” instead of “libc. a”?
Thank you for your help.This problem is resolved now. I added these two flags in the link phase. At first, it reported an error. I carefully checked the GCC map file and changed “-lc_nano” to “-lgcc, -lg_nano”. Now, the file size compiled by Clang is approximately the same as that compiled by GCC using “–specs=nano. specs --specs=nosys. specs” flags.