我有一个简单的c程序,比如my_bin.c:
#include <stdio.h>
int main()
{
printf("Success!n");
return 0;
}
我用gcc编译它,得到了可执行文件:my_bin。
现在我想使用另一个C程序调用main(或运行这个my_bin)。我用mmap和函数指针这样做:
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
void (*fun)();
int fd;
int *map;
fd = open("./my_bin", O_RDONLY);
map = mmap(0, 8378, PROT_READ, MAP_SHARED, fd, 0);
fun = map;
fun();
return 0;
}
编辑1:已添加PROT_EXEC从回应中更清楚地表明。。。我想在第二个程序中调用一个外部二进制程序。
我不知道如何用main(其他程序的)地址初始化函数指针。知道吗?
编辑2:
谷歌搜索后发现,为什么seg会出错,是因为我对mmap的大小和偏移量的争论。它应该是页面大小的倍数。[参考:Segfault同时使用C中的mmap读取二进制文件
现在代码看起来像:
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
void (*fun)();
int fd;
int *map;
int offset = 8378;
int pageoffset = offset % getpagesize();
fd = open("./my_bin", O_RDONLY);
if(fd == -1) {
printf("Err opening filen");
return -1;
}
map = mmap(0, 8378 + pageoffset, PROT_READ|PROT_EXEC,
MAP_SHARED, fd, offset - pageoffset);
perror("Errn"); //This is printing err and Success!
//fun = map; // If I uncomment this and
//fun(); // this line then, still it
// print err and Success! from perror
// but later it says Illegal instruction.
return 0;
}
仍然使用fun()或不使用它都不会打印。。。不知道如何给main()地址。
编辑2[已解决]:
第一件事:我并没有正确地阅读定义,我已经给出了地址,我应该从中读取二进制文件。第二个:mmap:size和偏移量应该是页面大小的倍数。
main()
通常不是C程序中的第一个函数。链接器会在此之前放入一些setup/init代码,其中包括设置环境、获取命令行参数、将它们解析为字符串数组等。
当新的main()
函数开始设置内存分配例程时,就会出现问题——基本上,这会破坏主应用程序的所有重要数据结构。
如果你想执行一个函数(即没有main()
),那么把你的C代码编译到一个共享库中,然后用dlopen()
或你的操作系统加载它。
如果您确实需要main()
,请使用fork()
和exec()
。
通常,编译和链接生成的可执行文件不是一个简单的二进制映像,可以加载到内存中并执行。(有特定的计算平台目标。)通常,一个称为加载器的特殊程序必须读取可执行文件,使用文件中的内容和指令准备内存,并使用特殊的系统调用将程序作为新进程启动执行。
例如,可执行文件通常包含一些必须复制到内存中然后标记为只读的数据,一些必须拷贝到内存中,然后标记为可读和可写的数据,以及一些必须被复制到内存并标记为可执行的数据(称为"文本"或程序指令)。可执行文件通常还包含一些关于准备内存的其他信息,例如留出一些初始清除的内存,留出一定的堆栈空间,从哪个地址开始执行(通常不是main
),等等
一个复杂的方面是,可执行文件包含关于程序文本和数据的哪些部分必须根据使用的存储器地址而改变的信息。在编译时,数据和文本放在内存中的地址可能是未知的,因此编译器编写一些原型代码,加载程序在决定地址后必须调整代码。
另一个复杂的问题是,可执行文件可能包含对动态库中符号的引用。加载程序必须检查这些引用并加载必要的动态库。
正如您所看到的,加载并不是一个简单的过程。您不能简单地对一个可执行文件进行内存映射并跳转到它,即使您可以找出main
在其中的起始位置。
一些系统为加载器提供接口。这些通常与动态库一起使用。与其构建一个独立的可执行程序,不如构建一个动态库。然后,您可以使用对动态库接口的调用来加载库,找到其中例程的地址,并调用这些例程。
通常,编程环境不提供加载可执行文件并调用其main
例程的方便方式。创建一个动态库可能更容易,而不是创建一个可执行文件,然后加载动态库并调用其中的例程。或者,也有一些方法可以将可执行文件作为一个单独的进程调用,这样它基本上可以单独运行,就像在命令行键入的命令一样,而无需与调用进程共享内存。
如果链接一个可执行文件中的功能,"正确"的解决方案IMHO是不从另一个函数调用main()。相反,创建一个函数来封装main()的作用,并从两个main()调用它。
int success()
{
printf("Success!n");
return 0;
}
int main()
{
return success();
}
要调用外部系统调用,请使用系统