【问题标题】:How can I influence the address of a function?如何影响函数的地址?
【发布时间】:2016-12-16 13:51:48
【问题描述】:

有没有一种方法可以影响、设置或定义函数的(相对)地址?也许链接描述文件有任何可能性,以确保函数 abc() 始终位于 OFFSET+0x0034C0 (只是一个示例)。 我想以某种方式“控制”内存中函数的位置,以使这些位置具有某种参数化。 目前我正在寻找一种使用 gcc 在我的 x86 上的方法。但是,真正的应用程序应该在嵌入式设备上运行。

问候

【问题讨论】:

  • IMO,如果应用程序在嵌入式设备上运行,我假设,某种自定义操作系统,不要试图让它在 x86 上工作,然后发现它不能在设备。 (好奇,为什么?)
  • 您是否考虑过在数据结构中存储指向函数的指针?
  • 这听起来很像一个 XY 问题,你正在尝试解决问题 X,所以你认为涉及到 Y 的解决方案,所以你问如何做 Y。你能再解释一下吗你实际上是想做什么?我认为使用函数指针或类似的东西是一个很可能的候选者,但如果没有真正理解“我需要我的函数在一个特殊地址上”背后的真正想法,很难知道建议什么解决方案。
  • @MatsPetersson:你确实是对的。我的目标是创建一个硬件-软件-绑定方案。更多详细信息可以在我的评论中找到,以放松的回答。
  • 因此,这听起来更像是“我希望只有在某些身份验证成功时才能访问代码块”,我可以通过以下方式解决:1.将整个块放在一个部分中 [参见例如,如何在 Linux 中定义 __init],然后仅在身份验证有效的情况下将该区域从 MMU 映射到虚拟空间。 2. 如前所述使用函数指针,或者使用一组不同的指针,或者将指针设置为在身份验证失败时停止[或执行其他一些“这不起作用”操作]的函数。

标签: c assembly


【解决方案1】:

你可能可以使用linker script magic 和 gcc 来做到这一点,是的。看看如何定义section placement,然后在你的源代码中放置指令以将函数放置在你选择的部分中。

完全不确定这是否适用于 x86 机器,因为操作系统可能有……反对意见。这更适合直接嵌入式使用。

在完整的操作系统上控制代码位置的要点是什么?

【讨论】:

  • 其实我想创建一些Software-Hardware-Binding-Scheme。某些功能的位置应在软件开发过程中定义。在执行软件时,它从硬件中读取认证信息,用于指导软件的控制流程。只有从硬件中读取了正确的凭证,后续的函数调用才会指向正确的内存地址。否则,它们会指向内存中的错误位置,并且软件的执行会停止/软件崩溃。
  • @user1192748,我通过存储特定硬件地址(例如中断向量)的内容的校验和看到了类似的结果。
  • @user1192748 CRC 检查的优点是您可以简单地检查该值,如果不匹配则不运行。调用未定义的行为只是灾难的根源(即,如果软件“停止”,您无法控制软件“停止”的时间/地点/方式)
  • 是的,使用 gnu 链接器,您可以控制对象的位置,并且通过在源文件中安排函数,您可以控制哪个函数是第一个和/或每个对象只有一个函数。 (并不是那么神奇)
  • @JoshPetitt 您能否提供有关校验和方法的其他参考资料?
【解决方案2】:

一个典型的通用实现是向量表(我也听说这称为补丁表)。

首先,在你的 C 文件中,编写你的函数:

void my_first_function(int){ /* do something */ }
void my_second_function(int){ /* do something */ }

然后,在你的 C 文件中创建一个定义表格布局的结构:

struct MyVectorTable
{
  void (*first_function)(int);
  int (*second_function)(float, char);

  // all the rest
};

接下来,在你的 C 文件中创建一个静态表:

static struct MyVectorTable my_vector_table = {
  my_first_function,
  my_second_function,
};

最后将地址暴露为 void*

void* get_table_base_address(void) { return &my_vector_table; }

现在您应该能够将所有函数作为基地址的偏移量。

如果您的所有函数都具有相同的调用签名,您可以通过使用函数指针数组而不是结构来简化这一点。但是,数组和结构都会保存指针,所以指针数学基本相同。

这还允许您使用链接器将补丁表定位在特定地址。

【讨论】:

    【解决方案3】:

    IMO,最好的方法是将函数放入用户定义的部分,就像上面 unwind 提到的那样。您可以在下面找到一个简单的示例,该示例将函数 myFunc 放置在 4kByte 边界处:

    通过修改链接器脚本在你的记忆中创建一个新部分:

    /* .my_section will be the name of the section in the final executable */
    .my_section : ALIGN (8)
    {
        . = ALIGN (0x1000);
    
        KEEP(*(.mysection))    /* The section will be called ".mysection" in the
                                  compiled translation unit (.obj) */
    
        . = ALIGN (8);
    } >rom
    

    现在,使用 gcc 的 attribute 功能将函数放入我们刚刚创建的部分:

    void myFunc(void) __attribute__ ((section(".mysection"))); // The section name to use 
                                                               // here is ".mysection", 
                                                               // not ".my_section"
    
    // Looks like you need to use __attribute__ along with function declaration, rather 
    // than with the function definition, though I'm not sure why
    
    void myFunc(void)
    {
        // ...
    }
    

    如果您现在执行objdump,您将看到名称为.my_section 的部分在地址0x2000 而非0x12e8 处包含myFunc 的代码

    Disassembly of section .my_section:
    
    000012e8 <myFunc-0xd18>:
            ...
    
    00002000 <myFunc>:
        2000:       b508            push    {r3, lr}
        ...
    

    此代码适用于 ARM-Cortex 的 Codesourcey gcc 套件。我不太确定 x86...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-27
      • 2019-10-29
      • 2019-11-06
      • 2017-03-28
      • 2023-01-17
      • 1970-01-01
      • 2023-02-05
      • 2011-05-30
      相关资源
      最近更新 更多