当可执行文件在执行过程中被替换时如何处理"/proc/self/exe"的readlink()?
How to handle readlink() of "/proc/self/exe" when executable is replaced during execution?
在我的C++应用程序中,我的应用程序在fork()
子进程中执行execv()
,以使用相同的可执行文件来处理具有不同参数的新子进程中的某些工作,这些参数与管道通信到父进程。 为了获得 self 的路径名,我在 Linux 端口上执行以下代码(我在 Macintosh 上有不同的代码):
const size_t bufSize = PATH_MAX + 1;
char dirNameBuffer[bufSize];
// Read the symbolic link '/proc/self/exe'.
const char *linkName = "/proc/self/exe";
const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1));
但是,如果在可执行文件运行时,我将可执行文件替换为磁盘上二进制文件的更新版本,则readlink()
字符串结果为:"/usr/local/bin/myExecutable (deleted)"
我知道我的可执行文件已被更新的更新版本替换,并且/proc/self/exe
的原始版本现在已被替换,但是,当我转到execv()
时,由于结果中的额外尾随" (deleted)"
,它现在因 errno
2 - No such file or directory.
而失败。
我希望execv()
要么自己使用旧的可执行文件,要么使用更新的可执行文件。 我可以检测以 " (deleted)"
结尾的字符串并对其进行修改以省略它并解析为更新的可执行文件,但这对我来说似乎很笨拙。
当原始可执行文件在执行过程中被更新的可执行文件替换时,如何使用一组新的参数execv()
当前可执行文件(或替换,如果更容易的话)?
无需使用 readlink
来发现您自己的可执行文件的路径,您可以直接在 /proc/self/exe
上调用 open
。由于内核已经对当前正在执行的进程有一个开放的 fd,因此无论路径是否已替换为新的可执行文件,这都会为您提供一个 fd。
接下来,您可以使用 fexecve
而不是接受可执行文件的 fd
参数而不是 filename
参数的execv
。
int fd = open("/proc/self/exe", O_RDONLY);
fexecve(fd, argv, envp);
为简洁起见,上面的代码省略了错误处理。
一种解决方案是在可执行启动时(例如在main()
开头附近),读取链接/proc/self/exe
的值一次并将其静态存储以备将来使用:
static string savedBinary;
static bool initialized = false;
// To deal with issue of long running executable having its binary replaced
// with a newer one on disk, we compute the resolved binary once at startup.
if (!initialized) {
const size_t bufSize = PATH_MAX + 1;
char dirNameBuffer[bufSize];
// Read the symbolic link '/proc/self/exe'.
const char *linkName = "/proc/self/exe";
const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1));
savedBinary = dirNameBuffer;
// On at least Linux, if the executable is replaced, readlink() of
// "/proc/self/exe" gives "/usr/local/bin/flume (deleted)".
// Therefore, we just compute the binary location statically once at
// startup, before it can possibly be replaced, but we leave this code
// here as an extra precaution.
const string deleted(" (deleted)");
const size_t deletedSize = deleted.size();
const size_t pathSize = savedBinary.size();
if (pathSize > deletedSize) {
const size_t matchPos = pathSize - deletedSize;
if (0 == savedBinary.compare(matchPos, deletedSize, deleted)) {
// Deleted original binary, Issue warning, throw an exception, or exit.
// Or cludge the original path with: savedBinary.erase(matchPos);
}
}
initialized = true;
}
// Use savedBinary value.
这样,原始可执行文件不太可能在 main() 缓存其二进制文件的路径的微秒内被替换。 因此,长时间运行的应用程序(例如数小时或数天)可能会在磁盘上被替换,但根据原始问题,它可以fork()
并execv()
到可能具有错误修复的更新二进制文件。 这具有跨平台工作的额外好处,因此读取二进制路径的不同Macintosh代码同样可以在启动后免受二进制替换。
警告编辑器注意:readlink
不 null 终止字符串,因此如果在调用 readlink
之前缓冲区未填充零,则上述程序可能会也可能不会意外运行
将(deleted)
部分放入符号链接的原因是,您已将文件替换为正确的程序二进制文本,并且指向可执行文件的符号链接永远不会再次有效。 假设您使用此符号链接来获取该程序的符号表或加载嵌入在其上的一些数据,并且您更改了程序...该表将不正确,您甚至可能导致程序崩溃。 您正在执行的程序的可执行文件不再可用(您已经删除了它),并且您放置的程序与您正在执行的二进制文件不对应。
当你取消链接(2)正在执行的程序时,内核在/proc
中标记该符号链接,所以程序可以
- 检测二进制文件已被删除且无法再访问。
- 允许您仍然收集其姓氏的一些信息(而不是从
/proc
树中删除符号链接)
您无法写入内核正在执行的文件,但没有人阻止您擦除该文件。 只要你执行它,该文件就会继续存在于文件系统中,但没有名称指向它(一旦进程退出,它的空间将被释放(2)) 内核不会擦除其内容,直到内核内存中的索引节点计数为零,当对该文件的所有使用(引用)到期时,就会发生这种情况。
- 如何将不同的可执行文件合并到一个窗口框架中进行编码?像浏览器一样
- 如何使 windows 命令提示符在C++可执行文件上显示返回值?
- CMake:如何将库 A 链接到库 B,然后将可执行文件链接到库 A
- 如何使字符串出现在编译的二进制可执行文件的开头?
- 程序在使用 system() 启动另一个可执行文件时停止
- 如何更改使用 CPack 安装的可执行文件的名称?
- 在Linux上使用Clang / OLLVM交叉编译helloworld Windows可执行文件时的问题
- CMake:我们可以为一组不形成可执行文件或库的特定文件指定包含目录吗?
- 如何将图像传入C++可执行文件并将输出图像存储在新目录中?
- 共享库 (.so) 没有扩展名的 Linux 可执行文件之间的区别?
- 如何在 linux 可执行文件中使用静态库
- 错误:当我从"WinDbg"打开可执行文件时,找不到符号文件。默认导出 ntdll 的符号.dll
- 编译的C可执行文件被Windows defender检测为病毒
- Linux 可执行文件通过 dlopen 在emplace_back崩溃打开共享库
- 是否可以在命令行中将输入参数传递给可执行文件
- 如何在可执行文件中存储常量数组?
- 如果包含映射的静态库与可执行文件和动态库链接,静态映射(变量)是否会被多次释放?
- 可执行文件C++包括.obj,.lib和.dll吗?
- 在 Linux 上没有 /proc/self/exe 的 C/C++ 运行时可执行文件的大小?
- 当可执行文件在执行过程中被替换时如何处理"/proc/self/exe"的readlink()?