web-dev-qa-db-ja.com

`b`:./a.out:` ld`によって生成された実行可能ファイルの実行時にそのようなファイルまたはディレクトリはありません

これがCのHelloWorldコードです。

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

int main() {
    printf("Hello world\n");
    return 0;
}

私はそれをgcc a.cとしてコンパイルします。これにより、予想どおりa.outが生成され、予想どおり./a.outHello 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を生成します。

9
pratyaksh

他の回答は、これを回避する方法のみを扱っており、何が起こったのかについての実際の質問ではありません。

指定した_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
_

ご存知のとおり、このエラーメッセージの通常の理由は、適切なダイナミックリンカーとダイナミックライブラリがインストールされていないシステム(32ビットサポートがインストールされていない64ビットシステムなど)でELFバイナリを実行している場合です。あなたの場合、それはあなたが悪いリンクコマンドを使用し、悪いインタプリタパスで動的実行可能ファイルを作成したためです。


私はUbuntu15.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は、バイナリで指定されたものではなく、デフォルトのELFインタープリターを使用するため、lddの出力は混乱を招きます。

_$ 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のコメントに感謝します。)

4
Peter Cordes

ld -lc a.o

このコマンドラインにはいくつかの問題があります。

  1. 一般に、ユーザーレベルのコードは決してldを直接使用するべきではなく、常に適切なコンパイラフロントエンド(ここではgcc)を使用してリンクを実行します。

    ご存知のように、gccが構築するリンクコマンドラインは非常に複雑であり、JoanEstebanの回答で受け入れたコマンドラインは間違っています。

    actualリンクコマンドを確認したい場合は、gcc -v a.oからの出力を調べてください。

    また、gccコマンドをわずかに変更するとlinkコマンドが大幅に変更されます(たとえば、一部のOSでは、マルチスレッド実行可能ファイルをリンクするかどうかによって、異なるcrt1.oが必要になります)。コマンドラインは常にOS固有(これは、決してldを直接使用しないもう1つの理由です)。

  2. ライブラリは、コマンドラインでオブジェクトファイルに従う必要があります。したがって、ld -lc a.o決して正しくなく、常にである必要があります(の変形)ld a.o -lc説明

6

それを使用してください:

  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
4
Joan Esteban