4

我有简单的 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 的大小和偏移量参数。它应该是页面大小的倍数。[参考:在 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 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;
}

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

编辑 2 [已解决]:

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

4

3 回答 3

6

main()通常不是 C 程序中的第一个函数。链接器将在其前面放置一些设置/初始化代码,其中包括设置环境、获取命令行参数、将它们解析为字符串数组等。

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

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

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

于 2012-09-13T15:40:15.110 回答
1

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

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

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

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

如您所见,加载不是一个简单的过程。你不能简单地对一个可执行文件进行内存映射并跳转到它,即使你能弄清楚它从哪里main开始。

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

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

于 2012-09-13T17:24:18.167 回答
-1

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

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

int main()
{
  return success();
}

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

于 2012-09-13T15:53:48.223 回答