C memcpy()函数

时间:2009-11-11 17:11:34

标签: c memory linux-kernel function-pointers memcpy

有没有计算函数大小的方法?我有一个指向函数的指针,我必须使用memcpy复制整个函数。我必须malloc一些空间并知道memcpy的第三个参数 - 大小。我知道sizeof(function)不起作用。你有什么建议吗?

15 个答案:

答案 0 :(得分:23)

C中的函数不是first class objects。这意味着它们不能传递给另一个函数,它们不能从函数返回,也不能复制到内存的另一部分。 / p>

虽然函数指针可以满足所有这些要求,但它是第一类对象。函数指针只是一个内存地址,它通常与机器上任何其他指针的大小相同。

答案 1 :(得分:8)

它没有直接回答你的问题,但你不应该实现从内核代码到用户空间的回调

将代码注入内核空间 解决方案

最好像进程间屏障一样表示用户/内核屏障。通过char设备在明确定义的协议之间来回传递数据,而不是代码。如果确实需要传递代码,只需将其包装在内核模块中即可。然后,您可以动态加载/卸载它,就像.so-based plugin system

一样

旁注,起初我误读了您确实想要将memcpy()传递给内核。你必须提醒它是一个非常特殊的功能。它在C标准中定义,非常简单,范围很广,因此它是一个完美目标,可以作为built-in by the compiler提供。

就像strlen()strcmp()others in GCC

那就是说,内置的事实并不妨碍你指挥它的能力。

答案 2 :(得分:5)

即使有办法让sizeof()成为一个函数,当你试图调用已经复制到内存中另一个区域的版本时,它仍然可能会失败。如果编译器具有到特定内存位置的本地或长跳转,该怎么办?你不能只在内存中移动一个函数并期望它运行。操作系统可以做到这一点,但它拥有完成它所需的所有信息。


我打算问操作系统是如何做到这一点的,但是现在我想到了,当操作系统移动它时,它通常移动整个页面并处理内存,使地址转换为页面/偏移量。我甚至不确定OS是否会在内存中移动单个函数。


即使在OS在内存中移动函数的情况下,也必须声明或以其他方式编译/汇编函数本身以允许此类操作,通常通过指示代码可重定位的pragma。所有内存引用都需要相对于其自己的堆栈帧(也称为局部变量)或包含某种段+偏移结构,以便CPU直接或在OS的命令下可以选择适当的段值。如果有一个链接器参与创建应用程序,应用程序可能必须 重新链接到新功能地址的帐户。

有些操作系统可以为每个应用程序提供自己的32位地址空间,但它适用于整个进程和任何子线程,而不是单个函数。

正如其他地方所提到的,你真的需要一种语言,其中函数是一流的对象,否则你运气不好。

答案 3 :(得分:3)

你想要复制一个功能吗?我不认为这在C中是可能的。 假设您有一个Harvard-Architecture微控制器,其中代码(换句话说“函数”)位于ROM中。在这种情况下,你根本不能这样做。 我也知道几个编译器和链接器,它们在文件上进行优化(不仅仅是功能级别)。这导致操作码,其中C函数的部分彼此混合。

我认为唯一可行的方法可能是:

  • 生成函数的操作码(例如,通过自己编译/汇编)。

  • 将该操作码复制到C阵列中。

  • 使用指向该数组的正确函数指针来调用此函数。

  • 现在,您可以对该阵列上的典型“数据”执行所有操作。

但除此之外:您是否考虑重新设计软件,以便不需要复制功能内容?

答案 4 :(得分:3)

我不太明白你要完成什么,但假设你用-fPIC编译并且没有你的函数做任何花哨的事情,没有其他函数调用,没有从外部函数访问数据,你甚至可能得到做一次。我认为最安全的可能性是将支持函数的最大大小限制为1千字节,然后将其转移,并忽略尾随垃圾。

如果你真的需要知道函数的确切大小,请找出编译器的结尾和序言。这应该在x86上看起来像这样:

:your_func_epilogue
mov esp, ebp
pop ebp
ret
:end_of_func

;expect a varying length run of NOPs here

:next_func_prologue
push ebp
mov ebp, esp

反汇编编译器的输出以进行检查,并获取相应的汇编序列进行搜索。单独的结语可能已经足够了,但是如果搜索序列过早弹出,所有这些都可能会爆炸,例如,在函数嵌入的数据中。我认为,搜索下一个序幕可能也会让你遇到麻烦。

现在请忽略我写的所有内容,因为你显然是试图以错误的,本质上不安全的方式解决问题。请给我们画一幅更大的图片,为什么你要这样做,看看我们是否能找到一种完全不同的方法。

答案 5 :(得分:2)

在这里进行了类似的讨论:

http://www.motherboardpoint.com/getting-code-size-function-c-t95049.html

他们建议在你要复制的函数之后创建一个虚函数,然后获取两者的内存指针。但是你需要关闭编译器优化才能工作。

如果你有GCC> = 4.4,你可以尝试使用#pragma来关闭你的功能的优化:

http://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-Pragmas.html#Function-Specific-Option-Pragmas

另一个建议的解决方案是根本不复制该功能,而是在要将其复制到的地方定义该功能。

祝你好运!

答案 6 :(得分:1)

如果您的链接器没有进行全局优化,那么只需计算函数指针和下一个函数地址之间的差异。

请注意,如果您的代码未编译为可重定位的,则复制函数将产生无法调用的内容(即代码中的所有地址必须是相对的,例如分支;全局变量可以工作,但因为它们不移动)。

答案 7 :(得分:1)

听起来您希望从内核驱动程序到用户空间进行回调,以便在某些异步作业完成时它可以通知用户空间。

这可能听起来很合理,因为它是常规用户空间库可能会做的事情 - 但对于内核/用户空间界面,这是非常错误的。即使你设法将你的功能代码复制到内核中,即使你使其适当地与位置无关,它仍然是错误的,因为内核和用户空间代码在根本上不同的上下文中执行。对于可能导致问题的差异的一个示例,如果由于换出的页面而在内核上下文中发生页面错误,那将导致内核跳过而不是交换页面。

正确的方法是让内核在异步作业完成时使某些文件描述符可读(在您的情况下,此文件描述符几乎肯定是您的驱动程序提供的字符设备)。然后,用户空间进程可以使用select / pollread等待此事件 - 如果需要,它可以将文件描述符设置为非阻塞,并且基本上只使用所有标准UNIX处理这种情况的工具。毕竟,这是如何处理网络套接字的异步性质(以及几乎所有其他异步情况)。

如果您需要提供有关发生的事件的其他信息,那么当用户空间进程在可读文件描述符上调用read时,可以使用该信息。

答案 8 :(得分:0)

功能不仅仅是您可以复制的对象。那么交叉引用/符号等等呢?当然你可以采取标准的linux“binutils”包之类的东西并折磨你的二进制文件,但这是你想要的吗?

顺便说一句,如果您只是想替换memcpy()实现,请查看LD_PRELOAD机制。

答案 9 :(得分:0)

我能想出一种方法来实现你想要的东西,但我不会告诉你,因为这是一种可怕的语言滥用。

答案 10 :(得分:0)

一种比禁用优化并依赖编译器来维护函数顺序的更简洁方法是将该函数(或一组需要复制的函数)安排在自己的部分中。这是依赖于编译器和链接器的,如果在复制的函数之间调用,则还需要使用相对寻址。对于那些问你为什么要这样做的人来说,它是嵌入式系统中需要更新运行代码的常见要求。

答案 11 :(得分:0)

我的建议是:不要。

将代码注入内核空间是一个巨大的安全漏洞most modern OSes forbid self-modifying code altogether

答案 12 :(得分:0)

尽管我可以说,原始海报想要做一些特定于实现的事情,因此不可移植;这是关于C ++标准关于构建指针到函数的主题而不是C标准的说法,但这应该足够好了。

在某些环境中,对于某些编译器,可能会做海报似乎想做的事情(也就是说,将指向函数指向的内存块复制到其他位置,也许用malloc分配,将该块转换为指向函数的指针,并直接调用它。但它不可移植,这可能不是问题。查找该内存块所需的大小本身取决于环境和编译器,并且可能非常需要一些非常神秘的东西(例如,扫描内存以获取返回操作码,或通过反汇编程序运行内存)。同样,特定于实现,并且非常不便携。再说一遍,原来的海报可能无关紧要。

潜在解决方案的链接似乎都使用了特定于实现的行为,我甚至不确定它们是否符合要求,但它们可能适用于OP。

将这匹马打死,我很想知道OP为什么要这样做。即使它在目标环境中工作也会非常脆弱(例如,可能会破坏编译器选项,编译器版本,代码重构等的变化)。我很高兴我不会在需要这种魔法的地方工作(假设它是)......

答案 13 :(得分:0)

我在Nintendo GBA上完成了这项工作,我将一些低级渲染功能从闪存(16位访问慢速内存)复制到高速工作区ram(32位访问,至少快两倍)。这是通过在我想要复制的函数后立即获取函数的地址来完成的,size =(int)(NextFuncPtr - SourceFuncPtr)。这确实运作良好,但显然不能在所有平台上进行操作(当然不能在Windows上运行)。

答案 14 :(得分:0)

我认为一个解决方案可以如下所示。

例如:如果你想知道程序a.c中的func()大小,并且在函数之前和之后都有指标。

尝试编写一个perl脚本,将该文件编译为对象格式(cc -o),确保不删除预处理器语句。稍后您需要它们来计算目标文件的大小。

现在搜索您的两个指标,并找出它们之间的代码大小。