`bash: ./a.out: Такого файла или каталога нет «при запуске исполняемого файла, созданного» ld`

#assembly #compilation #linker #riscv #crt

Вопрос:

Вот код Hello World на языке Си:

 // a.c
#include <stdio.h>

int main() {
    printf("Hello worldn");
    return 0;
}
 

Я компилирую его как gcc a.c , который производит a.out , как ожидалось, и ./a.out печатает Hello world … как ожидалось.

Теперь , если я выполню компиляцию и свяжусь отдельно: gcc -c a.c; ld -lc a.o , он запустит a.out созданное, как ./a.out только я получу сообщение:

 bash: ./a.out: No such file or directory
 

Я погуглил эту ошибку, и, похоже, это происходит, когда исполняемый файл создается 32-разрядным ELF, а архитектура машины 64-разрядная.

Я использую 64-разрядную машину и запускаю file a.out ее:

 a.out: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
 

Почему это происходит?

Редактировать:

Выход из uname -m

 $ uname -m
x86_64
 

Выход из ldd a.out

 $ ldd a.out
    linux-vdso.so.1 =>  (0x00007ffeeedfb000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa13a7b8000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fa13abab000)
 

gcc a.c производит a.out то, что работает правильно.

Комментарии:

1. 64-разрядная машина не обязательно означает 64-разрядную ОС. Каков результат uname -m ?

2. Что ldd a.out производит? Что происходит, когда вы это делаете gcc a.c ?

3. Добавлены выходные данные в качестве правок к вопросу

4. вы можете выполнить: gcc-v a.c и проверить этап связи (нужно ли вам связываться с объектом запуска, обычно crt0.o)

5. Почему вы не хотите, чтобы gcc занимался связыванием?

Ответ №1:

ld -lc a.o

В этой командной строке есть несколько ошибок:

  1. В общем случае код пользовательского уровня никогда не следует использовать ld напрямую и всегда использовать соответствующий интерфейс компилятора ( gcc здесь) для выполнения ссылки.

    Как вы обнаружили, командная строка ссылки, которая gcc создает, довольно сложна, и командная строка, которую вы приняли в ответе Джоан Эстебан, неверна.

    Если вы хотите увидеть фактическую команду link, просмотрите вывод из gcc -v a.o .

    Также обратите внимание, что команда link значительно меняется, когда вы меняете gcc команду незначительно (например, некоторые операционные системы требуют другого crt1.o в зависимости от того, связываете ли вы многопоточный исполняемый файл или нет), а командная строка всегда зависит от операционной системы (что является еще одной причиной никогда не использовать ld напрямую).

  2. Библиотеки должны следовать за объектными файлами в командной строке. Так ld -lc a.o никогда не бывает правильно, и всегда должно быть (вариант) ld a.o -lc . Объяснение.

Комментарии:

1. пояснение заархивированная ссылка web.archive.org/web/20180627210132/http://webpages.charter.net/…

Ответ №2:

Свяжите динамические исполняемые файлы с gcc foo.o (чтобы использовать правильные пути для CRT и libc, а также интерпретатор динамического компоновщика / ELF ld-linux-x86-64.so.2 ).
Или gcc -nostartfiles foo.o для libc , но не для CRT _start , если у вас есть рукописный _start

(Для статических исполняемых файлов без libc или CRT вы можете использовать ld напрямую или gcc -nostdlib -static .)

gcc -v foo.o покажет вам фактические пути GCC, используемые в вашей системе.


Другие ответы касаются только того, как избежать этого 1, а не фактического вопроса о том, что произошло.

gcc -c a.c; ld -lc a.o Команды, которые вы дали, дают довольно очевидное предупреждение:

 ld: warning: cannot find entry symbol _start; defaulting to 0000000000400260
 

Поэтому, даже если этот файл можно было бы выполнить, он, вероятно, сразу же рухнет. Смотрите ответ @EmployedRussian для объяснения того, что вы должны были сделать.


Вопрос о том, почему он даже не может быть выполнен, по-прежнему интересен:

 $ strace ./a.out 
execve("./a.out", ["./a.out"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
 

execve(2) возвращает ENOENT, потому что он не может найти интерпретатор (из которого я понял file и так далее, см. Ниже). Вы получите ту же ошибку при попытке запустить файл, который начинался с

 #!/usr/non-existant-path/bin/bash
 

Как вы обнаружили, обычная причина этого сообщения об ошибке заключается в запуске двоичного файла ELF в системе без установленного правильного динамического компоновщика и динамических библиотек (например, 64-разрядная система без установленной поддержки 32 бит). В вашем случае это связано с тем, что вы использовали неверную команду link и создали динамический исполняемый файл с неверным путем интерпретатора.


Я на Ubuntu 15.10, где GNU file версии 5.22 сообщает:

 a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld64.so.1, not stripped
 

/lib/ld64.so.1 В моей системе его нет. ldd вывод сбивает с толку, потому ldd что использует интерпретатор ELF по умолчанию, а не тот, который указан в двоичном файле.

 $ ldd a.out
        linux-vdso.so.1 =>  (0x00007ffc18d2b000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e0a79f000)
        /lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x0000559dbc9d2000)
 

Таким образом, предполагается, что интерпретатор времени выполнения в двоичном файле разрешен к тому ldd , который сам использовался, я полагаю.

Ваш ldd вывод, вероятно, тоже из старой версии, так как он просто отображается /lib64/ld-linux-x86-64.so.2 для этой строки. Не делать неверных предположений, вероятно, лучше для такого странного случая, как этот, но это не поможет вам увидеть, что ваш двоичный файл имеет странный путь интерпретатора.

 readelf -l a.out
 

будет декодировать заголовки ELF для вас, включая путь интерпретатора. (Спасибо комментарию @EmployedRussian за указание на это.)

Комментарии:

1. Стандартный способ узнать, что такое интерпретатор для данного двоичного файла, — это readelf -l a.out | grep interpreter

Ответ №3:

Используйте это:

   ld -o a.out -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc c.o /usr/lib/crtn.o
 

Комментарии:

1. Во-первых, crt1.o , crti.o , и crtn.o присутствуют /usr/lib/x86_64-linux-gnu/ на моей машине. После изменения этого и запуска ld , а затем a.out , я получаю ошибку: bash: ./a.out: Accessing a corrupted shared library

2. @pratyaksh Вам также необходимо адаптировать компоновщик /lib64/ld-linux-x86-64.so.2 .

3. @piggs_boson: Это хорошая причина для того, чтобы связывать динамические исполняемые файлы с gcc c.o или gcc -nostartfiles c.o вместо ld вручную — ваш дистрибутив настроит его так, чтобы он использовал правильные пути для вашей системы . Используйте gcc -v , чтобы узнать, какие эти пути находятся в вашей системе, а не у кого-то другого в Интернете.