从C中的二进制文件调用函数(main())

时间:2012-09-13 15:36:20

标签: c mmap binary-data

我有简单的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:

为什么在谷歌搜索之后出现了错误,因为我的大小和mmap的偏移量参数。它应该是pagesize的倍数。 [参考:在C中使用mmap读取二进制文件时的Segfault

现在代码如下:

#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 file\n");
        return -1;
   }
   map = mmap(0, 8378 + pageoffset, PROT_READ|PROT_EXEC,
                        MAP_SHARED, fd, offset - pageoffset);
   perror("Err\n"); //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;
}

仍然有趣()或没有它不打印...不知道如何给main()地址。

编辑2 [已解决]:

第一件事:我没有正确阅读定义,我已经给出了我应该读取二进制文件的地址。 第二:mmap:size和offset应该是pagesize的倍数。

3 个答案:

答案 0 :(得分:6)

main()通常不是C程序中的第一个函数。链接器将在此之前放置一些setup / init代码,除其他外,它将设置环境,获取命令行参数,将它们解析为字符串数组,这样的东西。

当新的main()函数开始设置内存分配例程时会出现问题 - 基本上,这会破坏主应用程序的所有重要数据结构。

如果你想执行一个函数(即没有main()),那么将你的C代码编译成一个共享库,并用dlopen()或你的操作系统等效加载它。

如果您确实需要main(),请使用fork()exec()

答案 1 :(得分:1)

通常,通过编译和链接生成的可执行文件不是可以加载到内存并执行的简单二进制映像。 (有一些计算平台目标。)通常,一个称为加载器的特殊程序必须读取可执行文件,使用文件中的内容和指令准备内存,并使用特殊的系统调用来启动程序的执行作为一个新的过程。

例如,可执行文件通常包含一些必须复制到内存然后标记为只读的数据,一些必须复制到内存然后标记为可读写的数据,以及一些数据(称为“文本”或程序)指令)必须复制到内存并标记为可执行文件。可执行文件通常还包含一些关于准备内存的其他信息,例如预留一些初始清除的内存,为堆栈空间留出一定的数量,该地址开始执行(通常不是main)等等。

一个复杂的方面是可执行文件包含有关程序文本的哪些部分以及必须根据使用的内存地址更改数据的信息。数据和文本放在内存中的地址在编译时可能不知道,因此编译器会编写一些原型代码,加载器必须在决定地址后调整代码。

另一个复杂因素是可执行文件可能包含对动态库中符号的引用。加载器必须检查这些引用并加载必要的动态库。

如您所见,加载不是一个简单的过程。您不能简单地将内存映射到可执行文件并跳转到它,即使您可以找出其中main的起始位置。

某些系统提供了加载程序的接口。这些通常与动态库一起使用。您可以构建一个动态库,而不是构建独立的可执行程序。然后,您可以使用对动态库接口的调用来加载库,查找其中的例程地址,并调用这些例程。

通常,编程环境不提供加载可执行文件并调用其main例程的便捷方法。创建动态库可能更容易,而不是创建可执行文件,然后加载动态库并在其中调用例程。或者,有一些方法可以将可执行文件作为一个单独的进程调用,这样它就可以单独运行,就像在命令行输入的命令一样,而不与调用进程共享内存。

答案 2 :(得分:-1)

“正确”解决方案恕我直言,如果链接一个可执行文件中的功能,是不从另一个函数调用main()。相反,创建一个函数来包装main()所做的并从main()s中调用它。

int success()
{   
  printf("Success!\n");
  return 0;
}

int main()
{
  return success();
}

要调用外部系统调用,请使用system