chroot "no such file or directory" 打印错误丢失的文件

chroot "no such file or directory" prints wrong missing file

我知道类似的问题在这里被问了十亿次,但与我让我的系统正常工作的问题不同。当我破坏它时,我只是收到了错误的错误消息(我想使用这些调试另一个问题,所以它们的工作至关重要)。

从工作系统开始:

$ tree
.
├── bin
│   └── bash
├── lib
│   ├── libc.so.6
│   ├── libdl.so.2
│   └── libtinfo.so.6
└── lib64
    └── ld-linux-x86-64.so.2
$ sudo chroot . /bin/bash
bash-5.0#

正如我们所期望的那样 运行 bash 和 bash 运行s.

现在,当我删除 lib 文件夹中的任何内容时,我收到一条错误消息,提示我缺少该库:

$ rm -f lib/libdl.so.2
$ sudo chroot . /bin/bash
/bin/bash: error while loading shared libraries: libdl.so.2: cannot open shared object file: No such file or directory

也符合预期。但是,当我删除 lib64 文件夹中的 ld-linux-x86-64.so.2 时:

$ rm -f lib64/ld-linux-x86-64.so.2
$ sudo chroot . /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory

它告诉我 /bin/bash 不见了。该消息与我实际删除时完全相同。

$ rm -f bin/bash
$ sudo chroot . /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory

所以出于某种原因,它认为 bash 丢失了,而实际上,我认为是动态链接器丢失了。我认为这是因为它首先使用此链接器加载小精灵,但这并不能使消息更正确。

我什至在我实际阻止 bash 它通过 运行 在 qemu 中找到 ld-linux-x86-64.so.2 时进行了检查 在不同的系统上 我得到了正确的错误消息:

<some arm system>$ qemu-x86_64 -L /tmp/nowhere bin/bash
/lib64/ld-linux-x86-64.so.2: No such file or directory

这是一个错误吗?是否有一些选项可以告诉 chroot 不要这样做并打印实际丢失的文件?这个文件有什么魔力吗?这是怎么回事?

TLDR:为什么 chroot 告诉我缺少可执行文件,而实际上 lib64/ld-linux-x86-64.so.2 是?

Why does chroot tell me the executable is missing when actually lib64/ld-linux-x86-64.so.2 is?

假设您正在使用来自 GNU coreutils 的 chroot 程序,我们可以查看代码以了解发生了什么(希望“魔法”会消失)。这里是 github mirror of chroot.c.

如果我们在错误消息 failed to run command 中搜索字符串,我们会立即找到打印它的(唯一)代码行:

  /* Execute the given command.  */
  execvp (argv[0], argv);

  int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
  error (0, errno, _("failed to run command %s"), quote (argv[0]));
  return exit_status;
}

如您所见,它是在 execvp() 系统调用之后打印的。 execvp() 是允许执行程序的系统调用(的一种变体)(在您的情况下为 /bin/bash )。如果程序执行成功,execvp() 不会 return 因为:

The exec() family of functions replaces the current process image with a new process image.

it returns only 以防出现错误并适当设置 errno

代码然后检查 errno 以确定退出状态:

int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;

最后打印错误:

error (0, errno, _("failed to run command %s"), quote (argv[0]));

如您所见,argv[0] 总是 failed to run command 之后打印(/bin/bash 在您的情况下,即它试图 exec在 chroot 环境中使用。

errno 是由 execvp() 错误编号“returned”并确定之后打印的内容(error() 在我的系统上定义如下):

/* Print a message with `fprintf (stderr, FORMAT, ...)';
   if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM).
   If STATUS is nonzero, terminate the program with `exit (STATUS)'.  */

extern void error (int __status, int __errnum, const char *__format, ...)

No such file or directory 是错误编号 ENOENT 并且在以下情况下被 execvp() 和朋友“returned”:

ENOENT The file pathname or a script or ELF interpreter does not exist.

(“ELF解释器”是动态链接器的同义词I guess)

chroot 实际上不知道 真正 出了什么问题,它只能报告 execvp()errno 中输入的内容,我认为这就是错误含糊不清且有点误导的原因。

Why does chroot tell me the executable is missing when actually lib64/ld-linux-x86-64.so.2 is?

可执行文件本身不是 运行。

它类似于 shell 脚本 - 你有 #!/bin/sh 并且文件有可执行权限,所以你 ./file 它真的 运行s /bin/sh ./file.

当您执行 ./file 并且文件是带有 PT_INTERP 的 ELF 文件时,来自 PT_INTERP header 的文件被获取并且是 运行。所以真正发生的是,你真的 运行ning /lib64/ld-linux-x86-64.so.2 ./file 为(几乎)你系统上的每一个可能的可执行文件。您可以在您的系统上执行此操作 - /lib64/ld-linux-x86-64.so.2 /bin/bash 将 运行 Bash.

所以内核读取 /bin/bash,看到它是一个 ELF 文件,读取 PT_INTERP,然后 运行s /lib64/ld-linux-x86-64.so.2。因为 /lib64/ld-linux-x86-64.so.2 不存在,内核将 errno 设置为 ENOENT.

然后 chroot 程序检查 errno,并打印消息 printf("Failed to run command <that command>: <error description>chroot 不知道内核找不到程序解释器或可执行文件本身,它不知道。 chroot 只打印错误描述。