【问题标题】:Is there any way to get offset of structure member to assembly code?有没有办法让结构成员偏移到汇编代码?
【发布时间】:2021-12-13 08:27:17
【问题描述】:

比如我有如下结构,

struct thread {
    char *pName;
    ...        // number of members between may be different for different configurations
    int member;
    ...
};

对于不同的配置,pNamemember之间可能存在不同的成员,所以member的偏移量对于不同的配置是不一样的。 而且我想在汇编代码中使用member的偏移量,但又不想根据不同的配置硬编码。我曾尝试定义以下 C 风格的宏,但 GCC 汇编器无法识别,

#define OFFSET (&(((struct thread*)0)->member))

还有其他方法吗?

【问题讨论】:

  • 你需要已经知道ABI才能手动编写asm;汇编程序无法从 C 源文件中即时获取它。您可以编写一个 C 程序来打印结构偏移量的.equ 定义,您的.S 文件可以是.include#include
  • 如果有不同的配置,只需要传递成员本身的地址,来自C代码。那么汇编代码就不需要知道包含struct了。
  • 在 C 中,您可以使用 stddef.h 中定义的 offsetof 宏找到成员的偏移量。没有办法直接在汇编中做同样的事情,因为汇编器没有“结构”的概念。
  • @WeatherVane 这是一种方式。但在我的情况下,我实际上想将结构的基地址传递给程序集,并根据它们的偏移量访问多个成员。正如您所建议的,需要传入太多参数。这不是一个好主意。

标签: assembly gcc structure


【解决方案1】:

您可以编写一个脚本,作为构建步骤调用以创建文件 offsets.h。

这样做的最大困难是从头文件中提取结构名称和成员名称。下面的示例使用一个非常简单的头文件和一个非常简单的 awk 脚本来获取名称,只是为了展示这个想法。真正的实现将取决于头文件的复杂程度以及解析器需要的健壮程度。如果你的头文件中的结构定义有一个非常一致的布局,那么它可能不会比这个例子复杂多少。

这是一个完整且经过充分测试的示例。

生成文件

a.out: main.o asm.o
        cc main.o asm.o

demo_offsets.h: demo.h
        sh make_offsets.sh $<

main.o : demo.h
asm.o: demo_offsets.h

make_offsets.sh

set -e
tmp=make_tmp$$

cat << EOF > $tmp.c
#include <stdio.h>
#include <stddef.h>
#include "$1"
int main()
{
`awk -f awkfile $1`
}
EOF
cc -o $tmp $tmp.c
$tmp > $(basename ${1} .h)_offsets.h
rm $tmp.c $tmp

awk 文件

/struct/ { s=$2 }
/[A-Za-z_];/ { m=gensub(".*([A-Za-z_]*);", "\\1", 1); printf "printf(\"#define %s_%s_offset %%lu\\n\", offsetof(struct %s, %s));\n", s, m, s, m; }

demo.h

struct demo {
    int x;
    int y;
};

main.c

#include "demo.h"

extern int f(const struct demo *);

int main()
{
    struct demo s = { 1, 2 };
    return f(&s);
}

asm.S

#include "demo_offsets.h"

.global f
f:
    mov demo_x_offset(%rdi), %eax
    add demo_y_offset(%rdi), %eax
    ret

【讨论】:

    【解决方案2】:

    您的宏是实现您需要的标准方法。 offsetof(在 cmets 中建议)也有同样的作用。不幸的是,它不会转化为编译时常量。可惜编译器没有为此提供所需的方法。

    所以,您可以尝试以下方法:

    (1) 硬编码偏移量(是的,对于不同的配置,这将是不同的数字),然后编写一种单元测试来验证其正确性,最好在构建时进行。

    (2) 你可以用以下方式重写你的结构体定义:

       struct thread_base {
          char *pName;
          // ... more members added here ...
       };
    
       struct thread :public thread_base {
          int member;
       };
    

    那么sizeof(thread_base) 实际上就是您所需要的,然而它可能会受到填充的影响。如果sizeof(thread_base) 出现多个假定的结构成员对齐,那么你很好。否则你可以做相应的调整,就可以实现编译时常量表达式。

    【讨论】:

    • 请注意,提供的宏 OP 会受到未定义行为的影响(尝试对不指向对象的指针执行算术运算)。出于这个原因,offsetof 宏通常使用内置函数而不是现代编译器上的指针算法来实现。
    • 这是一个新颖的想法。但在我的例子中,结构是由 RTOS 定义的,如果定义发生变化,源代码会有很多工作要做。
    猜你喜欢
    • 2014-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-18
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多