Последний шаг в проблеме компиляции GCC (ld)

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

Программа состоит из нескольких файлов, начиная с main.c:

#include "file1.h"
#include "file2.h"

int main() {
  printout1("Print one");
  printout2(5);
  return 0;
}

file1.h

#include <stdio.h>

void printout1(char *str);

file1.c

#include "file1.h"

void printout1(char *str) {
  printf("%s\n", str);
}

file2.h

#include <stdio.h>
#include "file1.h"

void printout2(int);

file2.c

#include "file2.h"

void printout2(int val) {
  printf("%d\n", val);
  printout1("Print two");
}

Я пытаюсь скомпилировать эту программу со следующим make-файлом:

all:
    cpp main.c main.i 
    gcc -S main.i
    as -o main.o main.s
    ld -o main.o

Возникает следующая ошибка:

ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
main.o: in function 'main':
main.c:(.text+0xa): undefined reference to 'printout1'
main.c:(.text+0x14): undefined reference to 'printout2'
makefile:2 recipe for target 'all' failed
make:***[all] Error 1

Я новичок в таких вещах, и я знаю, что проблема в файлах заголовков, я просто не знаю, как их включить в это.

1.) не используйте ld напрямую, используйте gcc как интерфейс, 2.) то же самое для as, собственно, зачем вам вообще создавать исходный код сборки? 3.) вы когда-либо компилируете и связываете только свой код main, так как вы ожидаете, что функции из file1 и file2 будут доступны? 4.) make не предназначен для написания скриптов, используйте отдельные правила, где целью является файл, созданный этим правилом

user2371524 05.05.2018 16:13

где вы компилируете file2.c и file1.c (или связываете их, если на то пошло)?

PeterT 05.05.2018 16:13
main.o вызывает printout1, но не связан с объектным файлом, который его предоставляет
myaut 05.05.2018 16:13

Почему ты собираешься собирать? Обычный способ - сгенерировать объектные файлы непосредственно для исходных файлов все, а затем связать все объектные файлы.

Some programmer dude 05.05.2018 16:15

Кроме того, при сборке с использованием GCC вам понадобится еще несколько объектных файлов и библиотек, которые программа внешнего интерфейса gcc предоставляет для ld, но вы этого не делаете.

Some programmer dude 05.05.2018 16:16

Если невозможно построить его напрямую, определенно невозможно построить его шаг за шагом.

honzakuzel1989 05.05.2018 16:20

Наконец, если вы хотите пройти подобную сборку и напрямую использовать компоновщик, используйте опцию --verbose для gcc, чтобы увидеть, какие команды, флаги и библиотеки он использует.

Some programmer dude 05.05.2018 16:20
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Две проблемы с основной:

  1. gcc - это интерфейс для всех необходимых шагов - не пытайтесь напрямую вызывать другие инструменты вашей инструментальной цепочки, всегда используйте gcc. Это гарантирует, что он всегда добавляет необходимые параметры для этих других инструментов.

  2. У вас есть функции в разных единицах компиляции (file1 и file2), но никогда не компилируйте и не связывайте их.


Разумный Makefile для GNU make с вашей структурой будет выглядеть так:

CC ?= gcc

all: main

main: main.o file1.o file2.o
    $(CC) -o$@ $^

%.o: %.c
    $(CC) -c -o$@ $<

.PHONY: all

Это компилирует все файлы .c непосредственно в объектные файлы и, наконец, связывает их (используя $(CC), например, gcc) с вашим окончательным исполняемым файлом. Правило all помечено как .PHONY, потому что оно не создает файл с именем all - вы должны сообщить об этом make.

Вы, конечно, можете явно добавить дополнительные шаги, но это не имеет особого смысла.

Другие вопросы по теме