соединение выполнено успешно с помощью arm-none-eabi-g ++, но не arm-none-eabi-gcc

Я использую инструменты компилятора Launchpad Arm. Конкретно,

arm-none-eabi-g ++ и arm-none-eabi-gcc из:

(Инструменты GNU для встроенных процессоров ARM) 5.2.1 20151202 (выпуск) [ARM / embedded-5-branch, ревизия 231848]

У меня есть простая программа, предназначенная для процессора STM32F103, у которой нет никакой цели, кроме как доказать, что я могу написать оборудование и вызвать функцию из математической библиотеки. Это все, что есть:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "stm32f10x.h"

void hardwareTest(void){
   // Turn on the clock for PortB
   RCC->APB2ENR = RCC_APB2ENR_IOPBEN; // Turn on IO Port B
   // Put PB0 into push pull 50 MHz mode
   GPIOB->CRL = 0x03;
   // Turn PB0 on
   GPIOB->ODR = 1;
}

volatile int x; // force call to sqrt() later

int main(void) {
   x = sqrt(100.0f);
   x = sqrt(x);
   hardwareTest();
   return (x);
}

Когда я попытался построить это, у меня возникла ошибка компоновщика, говорящая о том, что существует неопределенная ссылка на sqrt. Сборка производилась с помощью arm-none-eabi-gcc. Случайно я обнаружил, что если сборка выполняется с помощью arm-none-eabi-g ++ с использованием тех же аргументов командной строки, связывание выполняется успешно.

Я написал Makefile, чтобы продемонстрировать разницу:

PROJECT = minimal
SOURCES = src/startup_stm32f10x_hd.s \
          src/system_stm32f10x.c \
          src/main.c
OUTPUT = ./out
print-%:
    @echo '$*=$($*)'

TOOLCHAIN = arm-none-eabi-

CXX = $(TOOLCHAIN)g++
CC = $(TOOLCHAIN)gcc
AR = $(TOOLCHAIN)ar
AS = $(TOOLCHAIN)gcc -c -x assembler-with-cpp
LD =  $(TOOLCHAIN)gcc
OBJCOPY = $(TOOLCHAIN)objcopy
OBJDUMP = $(TOOLCHAIN)objdump
SIZE = $(TOOLCHAIN)size
RM = rm -f

CFLAGS  = -O
CFLAGS += -nostartfiles

CXXFLAGS  = -O
CXXFLAGS += -nostartfiles

ARCH = -mcpu=cortex-m3 -mthumb -DSTM32F10X_HD 
LDFLAGS = 

all: clean $(PROJECT).elf $(PROJECT).gcc $(PROJECT).bin

$(PROJECT).bin: $(PROJECT).elf 
    @echo ' ======== '
    @echo ' Generating binaries'
    $(OBJCOPY) -O binary $(OUTPUT)/$< $(OUTPUT)/$(PROJECT).bin
    $(OBJCOPY) -O ihex   $(OUTPUT)/$< $(OUTPUT)/$(PROJECT).hex
    @echo ' ======== '

$(PROJECT).elf: $(SOURCES)
    @echo ' ======== '
    @echo ' Successful build uses g++'
    @echo ' CXXFLAGS = $(CXXFLAGS)'
    @echo ' LDFLAGS = $(LDFLAGS)'
    @echo ' ARCH = $(ARCH)'
    $(CXX) -o $(OUTPUT)/$@ $(ARCH)  $(CXXFLAGS) $(LDFLAGS) -Wl,-Tld_script/stm32.ld,-lm  $^
    @echo ' ======== '

$(PROJECT).gcc: $(SOURCES)
    @echo ' ======== '
    @echo ' Broken build uses gcc'
    @echo ' CFLAGS = $(CFLAGS)'
    @echo ' LDFLAGS = $(LDFLAGS)'
    @echo ' ARCH = $(ARCH)'
    $(CC) -o $(OUTPUT)/$@ $(ARCH)   $(CFLAGS) $(LDFLAGS) -Wl,-Tld_script/stm32.ld,-lm  $^
    @echo ' ======== '

$(PROJECT).gxx: $(SOURCES)
    @echo ' ======== '
    @echo ' build with g++'
    $(CXX) -o $(OUTPUT)/$@ $(ARCH)  $(CXXFLAGS) $(LDFLAGS) -Wl,-Tld_script/stm32.ld  $^
    @echo ' ======== '

# Program the binary to the board using the builtin serial bootloader
program:
    stm32loader.py -p /dev/ttyUSB0 -ewv $(OUTPUT)/$(PROJECT).bin

# Remove the temporary files
clean:
    @echo ' '
    @echo ' Cleaning up: '
    $(RM) $(OUTPUT)/* *.o *.elf *.bin *.hex *.gcc *.gxx *.g++
    @echo ' ======== '

Это дает следующие результаты:

Cleaning up: 
rm -f ./out/* *.o *.elf *.bin *.hex *.gcc *.gxx *.g++
======== 
======== 
 Successful build uses g++
 CXXFLAGS = -O -nostartfiles
 LDFLAGS = 
 ARCH = -mcpu=cortex-m3 -mthumb -DSTM32F10X_HD 
arm-none-eabi-g++ -o ./out/minimal.elf -mcpu=cortex-m3 -mthumb -DSTM32F10X_HD   -O -nostartfiles  -Wl,-Tld_script/stm32.ld,-lm  src/startup_stm32f10x_hd.s src/system_stm32f10x.c src/main.c
 ======== 
 ======== 
 Broken build uses gcc
 CFLAGS = -O -nostartfiles
 LDFLAGS = 
 ARCH = -mcpu=cortex-m3 -mthumb -DSTM32F10X_HD 
arm-none-eabi-gcc -o ./out/minimal.gcc -mcpu=cortex-m3 -mthumb -DSTM32F10X_HD    -O -nostartfiles  -Wl,-Tld_script/stm32.ld,-lm  src/startup_stm32f10x_hd.s src/system_stm32f10x.c src/main.c
/var/folders/t4/dv7b46055cjgknp4nndn1_zr0000gn/T//ccbl4swG.o: In function `main':
main.c:(.text+0x28): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
make: *** [minimal.gcc] Error 1
 ======== 
 Generating binaries
arm-none-eabi-objcopy -O binary ./out/minimal.elf ./out/minimal.bin
arm-none-eabi-objcopy -O ihex   ./out/minimal.elf ./out/minimal.hex
make: Target `all' not remade because of errors.

Так может ли кто-нибудь сказать мне, почему два компилятора ведут себя по-разному? Какую простую вещь я упустил из виду? Как мне обеспечить правильную связь с libm и другими, если я хочу использовать arm-none-eabi-gcc?

Я просмотрел make-файлы Фредди Шопена, но они слишком сложны, чтобы я мог их разобрать.


person Peter Harrison    schedule 25.01.2016    source источник
comment
C и C ++ - разные языки. Ожидаете ли вы, что компилятор Java будет вести себя идентично? Идентичный синтаксис и грамматика не подразумевают идентичную семантику.   -  person too honest for this site    schedule 25.01.2016
comment
В чем проблема с указанием -lm в качестве флагов компоновщика для gcc? Поведение такое, как и ожидалось, afaics.   -  person Ctx    schedule 25.01.2016
comment
использование флага -lm не влияет на результат здесь   -  person Peter Harrison    schedule 25.01.2016


Ответы (1)


C ++ требует, чтобы математические функции были частью базовой среды выполнения, в то время как C позволяет им находиться в библиотеке. Реализация GCC достигла этого путем автоматического связывания libm в сборке C ++.

Есть много других отличий в фазе связи; Связывание C ++ будет постоянно терпеть неудачу, если используется компоновщик C.

Для ссылки C используйте компоновщик C и укажите -lm, чтобы libm стал доступным.

person rici    schedule 25.01.2016
comment
использование -lm не влияет на результат - person Peter Harrison; 25.01.2016
comment
Порядок важен. -lm должен появляться в аргументах gcc после всех объектных файлов, для которых требуется библиотека. Кстати, gcc принимает -lm в качестве опции; вам не нужно скрывать это в -Wl - person rici; 25.01.2016
comment
Превосходно! Я уверен, что раньше что-то читал о порядке объектов компоновщика, но он не дошел до него. В этом примере я просто переместил список объектов до записи LDFLAGS, затем добавил -lm в LDFLAGS, и все сработало. - person Peter Harrison; 25.01.2016
comment
@peter: Не всегда правильно помещать LDFLAGS в конце, поэтому многие make-файлы (включая встроенные правила gmake) имеют как LDFLAGS, так и LDLIBS. - person rici; 25.01.2016
comment
OK. Хорошая точка зрения. Спасибо. Я проигнорировал порядок в предположении, что, если порядок был важен, взаимозависимые объектные файлы не смогли бы связываться. Однако теперь я предполагаю, что они будут в порядке, но статические библиотеки - еще одна проблема. - person Peter Harrison; 25.01.2016