如何在 C 中找到可执行文件的位置? [复制]

Posted

技术标签:

【中文标题】如何在 C 中找到可执行文件的位置? [复制]【英文标题】:How do I find the location of the executable in C? [duplicate] 【发布时间】:2010-10-30 08:24:03 【问题描述】:

在 C/C++ 中有没有办法找到当前执行程序的位置(完整路径)?

argv[0] 的问题在于它没有给出完整的路径。)

【问题讨论】:

哪个操作系统? 我认为没有可移植的方式来做到这一点。如果您使用完整的静态路径调用程序,argv[0] 是否具有完整路径?如果是这样,您可以像 sshd 那样强制用户执行二进制文件。 这里也有很好的答案:***.com/questions/1023306/… 这个问题(“如何在 SETTING X 中找到我的程序的位置?”)可以真的使用标签;使用关键字很难搜索! 重复中的最佳答案比此处的任何或所有答案更全面,尽管这是一个更新的问题。因此,我将其作为该主题的副本关闭,而不是反之亦然,即使这是较旧的问题,并且通常会作为该主题的主要问题而获得点头。 【参考方案1】:

总结一下:

在带有/proc 的 Unix 上,真正直接可行的方法是:

readlink("/proc/self/exe", buf, bufsize) (Linux)

readlink("/proc/curproc/file", buf, bufsize) (FreeBSD)

readlink("/proc/self/path/a.out", buf, bufsize) (Solaris)

在没有 /proc 的 Unix 上(即如果上述失败):

如果 argv[0] 以“/”(绝对路径)开头,这就是路径。

否则,如果 argv[0] 包含“/”(相对路径)将其附加到 cwd (假设尚未更改)。

否则在$PATH 中的目录中搜索可执行文件argv[0]

之后检查可执行文件是否实际上不是符号链接可能是合理的。 如果是相对于符号链接目录解析它。

/proc 方法中不需要此步骤(至少对于 Linux 而言)。 proc 符号链接直接指向可执行文件。

请注意,正确设置argv[0] 取决于调用进程。 大多数情况下这是正确的,但是在某些情况下调用进程不能被信任(例如 setuid 可执行文件)。

在 Windows 上:使用 GetModuleFileName(NULL, buf, bufsize)

【讨论】:

任何依赖于 argv[0] 作为程序名称的东西都是不可靠的。它会在大部分时间工作,但不是每次。这个问题在没有 /proc 的 unix 上很困难 并非所有带有 proc 的 unix 都有 /proc/self/exe。 /proc 的布局完全是特定于操作系统的,它们的做法都有些不同。例如,FreeBSD 提供了 /proc/curproc/file,它的工作方式与 Linux 的 /proc/self/exe 相同。但其他人可能根本不会这样做。 请注意,如果有人需要隐藏他们的踪迹,那么execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0); 会为argv[0] 留下一个与执行的文件名无关的绝对路径名。不过,其他信息很有用;谢谢。 一个不起眼的极端情况:如果 exe 驻留在 clearcase MVFS 视图中,Linux /proc/self/exe 方法就不能正常工作,因为 Linux 最终会返回视图中可执行文件的位置存储目录而不是 /view 限定路径。例如,对于 /vbs/bldsupp/linuxamd64/clang/debug/bin/llvm-config /proc/self/exe 将我指向不友好的路径:/home/peeterj/views/peeterj_clang-7.vws/.s/00024/ 8000023250b8f17fllvm-tblgen 在 Windows 上,您不必致电 GetModuleFileName。相反,只需 #include <windows.h> 并使用 Windows 在 _pgmptr 中自动提供的路径字符串。这比使用GetModuleFileName 函数更容易,因为这有可能会失败。【参考方案2】:

如果您使用的是 Windows,请使用 GetModuleFileName() 函数。

【讨论】:

谢谢,但我使用的是 linux 和 unix。【参考方案3】:

请注意,以下 cmets 仅适用于 unix。

对这个问题的迂腐回答是,没有通用方法可以在所有情况下都正确回答这个问题。正如您所发现的,argv[0] 可以由父进程设置为任何内容,因此与程序的实际名称或其在文件系统中的位置没有任何关系。

但是,以下启发式通常有效:

    如果 argv[0] 是绝对路径,则假定这是可执行文件的完整路径。 如果 argv[0] 是相对路径,即它包含 /,则使用 getcwd() 确定当前工作目录,然后将 argv[0] 附加到它。 如果 argv[0] 是一个普通单词,搜索 $PATH 以查找 argv[0],并将 argv[0] 附加到您找到它的任何目录。

请注意,所有这些都可以通过调用相关程序的进程来规避。最后,您可以使用特定于 linux 的技术,例如 emg-2 中提到的。在其他操作系统上可能有等效的技术。

即使上面的步骤给了你一个有效的路径名,你仍然可能没有你真正想要的路径名(因为我怀疑你真正想要做的是在某个地方找到一个配置文件)。硬链接的存在意味着你可能有以下情况:

-- assume /app/bin/foo is the actual program
$ mkdir /some/where/else
$ ln /app/bin/foo /some/where/else/foo     # create a hard link to foo
$ /some/where/else/foo

现在,上面的方法(我怀疑包括 /proc/$pid/exe)将把 /some/where/else/foo 作为程序的真实路径。而且,事实上,它是通向该程序的真正路径,而不是您想要的路径。请注意,在实践中比硬链接更常见的符号链接不会出现此问题。

尽管这种方法在原则上是不可靠的,但它在实践中对于大多数用途来说都足够好。

【讨论】:

【参考方案4】:

实际上不是答案,只是要记住的一个注释。

正如我们所见,在 Linux 和 Unix 中查找运行可执行文件位置的问题非常棘手,并且是特定于平台的。这样做之前应该三思而后行。

如果你需要你的可执行位置来发现一些配置或资源文件,也许你应该遵循Unix在系统中放置文件的方式:把配置放到/etc/usr/local/etc或当前用户主目录中,然后@ 987654323@ 是放置资源文件的好地方。

【讨论】:

【参考方案5】:

在许多 POSIX 系统中,您可以检查位于 /proc/PID/exe 下的 simlink。几个例子:

# file /proc/*/exe
/proc/1001/exe: symbolic link to /usr/bin/distccd
/proc/1023/exe: symbolic link to /usr/sbin/sendmail.sendmail
/proc/1043/exe: symbolic link to /usr/sbin/crond

【讨论】:

/proc 不是 POSIX,也不是很标准化。许多现代 Unices 都有,有些没有。 学习新事物总是很好。谢谢。有没有更多的“程序化”方式来做到这一点? @Dietrich:你说得对,它不是 posix。根据***的类 unix 系统有:Linux、AIX、BSD、Solaris、QNX。然而,并没有说明所有这些系统是否都有 /proc/*/cmd simlink。 Solaris 有 /proc,但没有 /proc/*/cmd。 在 Linux 中 /proc 文件系统是一个内核级别的选项——因此不能保证它在任何给定的 Linux 系统上都可用或启用。此外,如果 /etc/fstab 没有挂载点,它可以在内核中启用但不可用。此外,您可能会遇到安全问题。【参考方案6】:

请记住,在 Unix 系统上,二进制文件可能在启动后就被删除了。它在 Unix 上是完全合法和安全的。最后我检查了 Windows 将不允许您删除正在运行的二进制文件。

/proc/self/exe 仍然是可读的,但它不会是一个真正有效的符号链接。会很……奇怪。

【讨论】:

Unix在运行前是否会将所有可执行程序加载到内存中?在那种情况下,它如何有足够的内存来运行非常大的程序,例如一些自解压档案 一般不会。可执行文件被锁定(试图打开写入给出“文本文件忙”)和“内存映射”,这意味着它看起来就像它都在内存中,但它会在第一次内存时延迟加载页面被访问。如果它是只读页面(代码往往如此),那么内核可以在需要内存时“忘记”数据,并在再次访问时重新加载它。有点像交换,但由于代码没有被修改,它永远不会被写回磁盘。 我想在这种情况下,如果在文件被删除后将卸载的内存页面加载到内存中,将会发生严重的事情 什么都不会发生。 IIRC,您不会“删除”文件,而只是删除其在目录中的条目。一旦所有对它的引用消失,实际文件将被自动删除。引用可以是文件系统条目(硬链接)或打开的文件描述符(如进程的图像)。因此,在您“删除”文件后,您将看不到该文件,但实际删除将推迟到进程终止。【参考方案7】:

在 Mac OS X 上,使用 _NSGetExecutablePath

请参阅man 3 dyld 和this answer 来回答类似问题。

【讨论】:

【参考方案8】:

对于 Linux,您可以找到捆绑在一个名为 binreloc 的漂亮库中的 /proc/self/exe 处理方式,您可以在以下位置找到该库:

http://autopackage.org/docs/binreloc/

【讨论】:

【参考方案9】:

我愿意

1) 使用 basename() 函数:http://linux.die.net/man/3/basename 2) chdir() 到那个目录 3) 使用 getpwd() 获取当前目录

这样您将获得整洁、完整的目录,而不是 ./ 或 ../bin/。

如果这对您的程序很重要,您可能会想要保存和恢复当前目录。

【讨论】:

保存当前目录,open("."),稍后使用fchdir()返回。 realpath() 可以将相对路径名转换为绝对路径名并解析符号链接。

以上是关于如何在 C 中找到可执行文件的位置? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

分别构建第二个可执

Linux可执二进制行文件和库依赖查看方法

如何告诉 clang 将调试符号放入可执行二进制文件中? [复制]

不同 C 编译器生成的可执行文件的差异

内核作为动态链接器范例?

什么叫静态库和动态库