Команда gcc
в основном является диспетчерским движком.Для каждого входного файла он определяет тип файла из расширения имени файла , а затем передает файл соответствующему процессору.Таким образом, .c
файлы компилируются компилятором C, .h
файлы собираются в предварительно скомпилированные заголовки, .go
файлы отправляются компилятору cgo и так далее.
Если имя файла не имеет расширения или расширение не распознано, gcc
предполагает, что это какой-то объектный файл, который должен участвовать в последнем шаге ссылки.Эти файлы передаются в утилиту collect2
, которая затем вызывает ld
, возможно, дважды.Это будет иметь место в случае подстановки процесса, при которой создаются имена файлов, такие как /dev/fd/63
, которые не включают расширения.
ld
не использует имя файла для идентификации формата объектного файла.Как правило, он состоит из нескольких различных распознавателей объектных файлов, каждый из которых зависит от своего рода «магического числа» (то есть специального шаблона в начале файла или рядом с ним).Он вызывает этих распознавателей по одному, пока не найдет тот, который с радостью интерпретирует файл.Если файл не распознается как двоичный формат, ld
предполагает, что это сценарий компоновщика (который представляет собой простой текстовый файл), и пытается проанализировать его как таковой.
Естественно, между попытками ld
необходимо перемотать файл, и поскольку подстановка процесса организует передачу канала вместо файла, поиск завершится неудачно.(То же самое произойдет, если вы попытаетесь передать файл через перенаправление stdin в канал, что вы можете сделать: gcc обработает stdin
как файл, если вы укажете -
в качестве имени файла. Но он настаивает на том, чтобы выскажите ему, что это за файл. См. ниже.)
Поскольку ld
не может перемотать файл, произойдет сбой после того, как файл не соответствует его первому предположению.Отсюда и сообщение об ошибке от ld
, которое немного вводит в заблуждение, так как вы можете подумать, что файл уже скомпилирован и последующий сбой произошел на этапе ссылки.Это не тот случай;поскольку имя файла не имеет расширения, gcc
пропускается непосредственно к фазе соединения и почти сразу завершается неудачей.
В случае замены процесса, каналов, стандартного ввода и файлов с плохим именем, вы все равно можете указать * вручную1029 * что это за файл.Это делается с помощью опции -x
, которая задокументирована в разделе руководства GCC о параметрах, управляющих типом вывода (хотя в этом случае опция фактически контролирует тип ввода).
Существует множество ответов на подобные вопросы, распространяющиеся по Интернету, в том числе различные ответы здесь, в StackOverflow, в которых утверждается, что GCC пытается определить язык входных файлов.Он этого не делает и никогда не имеет.(И я сомневаюсь, что это когда-либо произойдет, поскольку некоторые языки, которые он компилирует, достаточно похожи друг на друга, поэтому точное обнаружение будет невозможно.) Единственный компонент, который выполняет автоматическое обнаружение, - это ld
, и он делает это только после того, как GCCокончательно решил обработать входной файл как объектный файл или скрипт компоновщика.