在编译时将扩展的宏打印到文件中

jli*_*u83 3 c macros gcc compilation

我想在 gcc 编译器编译时将宏的结果打印到文件中。我想知道这是否可能。下面的例子:

#define MD_BTLDB_APP_VERSION_OFFSET(x) 
Run Code Online (Sandbox Code Playgroud)

扩展到

(0x00000000u + (0x00040000u - ((uint32)(x) * 0x00000100u) - (64u)))
Run Code Online (Sandbox Code Playgroud)

计算得出

0x0003ffd6
Run Code Online (Sandbox Code Playgroud)

为了

MD_BTLDB_APP_VERSION_OFFSET(0)
Run Code Online (Sandbox Code Playgroud)

这是引导加载程序的一些元数据的地址,我希望能够在运行时之外使用该地址。

我查看了 #pragma 消息,但这仅将值输出到 gcc 输出,而不是输出到文件。它还存在混入许多其他编译器消息的不便。欢迎任何提示或创造性的解决方案。

Nik*_*vin 5

我不知道你是怎么得到的0x0003ffd6,因为我得到了0x3ffc0。任何。

在这里,我将描述获得扩展宏观评估结果的三种不同方法。

1. 一种#pragma message方法。

首先,我们来学习一下如何扩展宏。这是通过双重扫描完成的,您可以在C 预处理器技巧、提示和习惯用法中阅读更多相关信息。

假设我们有文件expand.c:

#include <stdio.h>
#include <stdint.h>
typedef uint32_t uint32;

#define MD_BTLDB_APP_VERSION_OFFSET(x)\
    (0x00000000u + (0x00040000u - ((uint32)(x) * 0x00000100u) - (64u)))

#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) # __VA_ARGS__

#pragma message "Value of MD_BTLDB_APP_VERSION_OFFSET(0) is " \
    STR(MD_BTLDB_APP_VERSION_OFFSET(0))

int main() {
    printf("%d\n", MD_BTLDB_APP_VERSION_OFFSET(0));
}
Run Code Online (Sandbox Code Playgroud)

编译后,您将获得宏的完整扩展。您可以使用 tee 将其复制到文件中:

$ gcc expand.c 2>&1 | tee output
expand.c:11:9: note: #pragma message: Value of MD_BTLDB_APP_VERSION_OFFSET(0) is (0x00000000u + (0x00040000u - ((uint32)(0) * 0x00000100u) - (64u)))
   11 | #pragma message "Value of MD_BTLDB_APP_VERSION_OFFSET(0) is " \
      |         ^~~~~~~
Run Code Online (Sandbox Code Playgroud)

其次,我们需要抓住表达方式。我将使用 sed 来完成:

$ pattern='.*note: \#pragma message: Value of MD_BTLDB_APP_VERSION_OFFSET\(0\) is'
$ sed -E -ne "s/$pattern (.*)/\1/p" output
(0 + 0x00000000u + (0x00040000u - ((uint32)(0) * 0x00000100u) - (64u)))
Run Code Online (Sandbox Code Playgroud)

-n抑制默认输出并p在表达式末尾仅打印出匹配的字符串。

最后,评估表达式。我会使用 python 或直接使用 bash 来完成此操作,因为它们都支持十六进制数字。但无论如何,首先我们需要删除特定于 C 和 C++ 的(uint32)转换和suffices:u

$ expr=$(sed -E -e 's/\(uint32\)//g' \
                -e 's/([0-9a-f]+)u/\1/g' \
                -ne "s/$pattern (.*)/\1/p" output)
$ printf "0x%016x\n" $(($expr))
0x000000000003ffc0
Run Code Online (Sandbox Code Playgroud)

就是这样!

2. 使用主机编译器进行评估。

另一种方法是首先获得交叉编译器的输出gcc -E,例如

$ CC_CROSS=arm-noeabi-gcc
$ expr=$($CC_CROSS -E -xc - <<EOF
#include "your_header.h"
MD_BTLDB_APP_VERSION_OFFSET(0)
EOF | tail -n 1)
Run Code Online (Sandbox Code Playgroud)

然后使用主机编译器评估表达式:

$ gcc -o get_md_btldb -xc - <<EOF
#include <stdio.h>
int main(){ printf("%p", $expr); }
EOF
$ ./get_md_btldb
Run Code Online (Sandbox Code Playgroud)

应该也可以,但没有尝试。

3.直接从二进制中取出值!

让我们在翻译单元中添加一个常量value.c

typedef uint32_t uint32;

#define MD_BTLDB_APP_VERSION_OFFSET(x) \
    (0x00000000u + (0x00040000u - ((uint32)(x) * 0x00000100u) - (64u)))

uintptr_t MD_BTLDB_APP_VERSION_OFFSET_VALUE = MD_BTLDB_APP_VERSION_OFFSET(0);
Run Code Online (Sandbox Code Playgroud)

现在,如果将其编译为目标文件,其中将有一个符号:

$ gcc -c value.c
$ objdump -t -s -j.data value.o 

value.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 g     O .data  0000000000000008 MD_BTLDB_APP_VERSION_OFFSET_VALUE


Contents of section .data:
 0000 c0ff0300 00000000                    ........    
Run Code Online (Sandbox Code Playgroud)

就是这样,c0ff0300 00000000以小端格式编写。还有那条线

0000000000000000 g     O .data  0000000000000008 MD_BTLDB_APP_VERSION_OFFSET_VALUE
Run Code Online (Sandbox Code Playgroud)

意味着符号MD_BTLDB_APP_VERSION_OFFSET_VALUE的值位于该部分中的偏移量 0 处.data并且大小为 8。您现在可以将其带到那里。

不过看反汇编就更容易了:

$ gcc -S value.s
$ cat value.s
    .file   "value.c"
    .text
    .globl  MD_BTLDB_APP_VERSION_OFFSET_VALUE
    .data
    .align 8
    .type   MD_BTLDB_APP_VERSION_OFFSET_VALUE, @object
    .size   MD_BTLDB_APP_VERSION_OFFSET_VALUE, 8
MD_BTLDB_APP_VERSION_OFFSET_VALUE:
    .quad   262080
    .ident  "GCC: (GNU) 9.1.0"
    .section    .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

该值就在MD_BTLDB_APP_VERSION_OFFSET_VALUE标签后面:

MD_BTLDB_APP_VERSION_OFFSET_VALUE:
    .quad   262080
Run Code Online (Sandbox Code Playgroud)

拿去:

grep -A 1 -e 'MD_BTLDB_APP_VERSION_OFFSET_VALUE:' value.s\
 | tail -n 1 | awk '{print $2;}'
 | xargs printf "%016x"

000000000003ffc0
Run Code Online (Sandbox Code Playgroud)