1

我在我的 Raspberry Pi 上使用 GCC 为我正在学习的课程编译一些汇编代码。根据GNU Assembler Reference中的信息,我了解到我可以在 GNU ARM 汇编中重现以下 C 代码:

int num = 0;

通过这样写:

        .data
num:    .word 0

伟大的!现在我该怎么写?

int num;

我的理解是,像这样保持未初始化的变量意味着我应该将它视为包含之前内存位置中的任何垃圾值。因此,在我以某种方式赋予它一个价值之前,我不应该使用它。

但是假设出于某种原因,我打算在内存中存储大量数据,并且需要为其预留大量空间。在我看来,如果我无论如何都要用一些数据填充它,那么将整个内存区域初始化为某个值将是一种巨大的资源浪费。然而,据我所知,如果不将其初始化为某个值,似乎无法在 GCC ARM 程序集中制作标签。根据我的汇编教科书,该.word指令后面可以有零个表达式,但如果以这种方式使用“则地址计数器不高级并且不保留任何字节”。我的第一个想法是改用“.space”或“.skip”指令,但对于这个指令,甚至官方文档表示“如果省略逗号和填充,则假定填充为零。”

我有没有办法在 GCC ARM 程序集中不初始化它来保留一块内存?

4

2 回答 2

4

一般来说,不需要初始化的数据应该放在.bsssection中。

    .bss
foobar:
    .skip 99999999

这将在节中分配 99999999 字节.bss,标签foobar将是其地址。它不会使您的目标文件或可执行文件变大 99999999 字节;可执行文件头只是指示需要多少字节.bss,并且在加载时,系统分配适当的数量并将其初始化为零。

您不能跳过加载时间零初始化。系统需要对其进行初始化,因为它可能包含来自内核或其他进程的敏感数据。但是将内存归零非常快,并且内核将使用一种高效的算法,所以我不会担心性能影响。它甚至可能在空闲时间将页面归零,因此当您的程序加载时,已经有归零的内存可用。无论如何,您的程序实际使用内存所花费的时间将会淹没它。

这意味着您也可以安全地.bss用于您确实希望初始化为零的数据(尽管不是任何非零值;如果您愿意int foo = 3;,您必须.data像原始示例一样将其放入。)。

于 2019-04-04T03:05:53.227 回答
2

当你尝试它时发生了什么?

当我尝试它时:

int num = 0;
int mun;

有了gnu,我得到了

    .cpu arm7tdmi
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .file   "so.c"
    .text
    .comm   mun,4,4
    .global num
    .bss
    .align  2
    .type   num, %object
    .size   num, 4
num:
    .space  4
    .ident  "GCC: (GNU) 8.3.0"

.comm 符号,长度

.comm 声明了一个名为 symbol 的通用符号。链接时,一个对象文件中的公共符号可能与另一个对象文件中已定义或同名的公共符号合并。如果 ld 没有看到符号的定义——只有一个或多个常见符号——那么它将分配长度字节的未初始化内存。长度必须是绝对表达式。如果 ld 看到多个具有相同名称的通用符号,并且它们的大小不同,它将使用最大的大小分配空间。

使用 ELF 时,.comm 指令采用可选的第三个参数。这是符号的所需对齐方式,指定为字节边界(例如,16 对齐意味着地址的最低有效 4 位应为零)。对齐必须是绝对表达式,并且必须是 2 的幂。如果 ld 为公共符号分配未初始化的内存,它将在放置符号时使用对齐方式。如果未指定对齐方式,as 会将对齐方式设置为小于或等于符号大小的 2 的最大幂,最大为 16。

.comm 的语法在 HPPA 上略有不同。语法是`symbol .comm, length';符号是可选的。

汇编语言是由汇编器而不是目标定义的。所以答案将是汇编器(读取和汇编汇编语言程序的工具)特定的,没有理由假设一个汇编器的答案与另一个相同。以上是gnu汇编器,gas。

您本可以查看您引用的文档或阅读其他 gnu 文档,但回答“在已编译程序中执行此操作时会发生什么”的最简单方法是编译它并查看编译器输出。

但不一定假设它没有被初始化:

unsigned int num;
unsigned int fun ( void )
{
    return(num);
}

足以链接它:

Disassembly of section .text:

00001000 <fun>:
    1000:   e59f3004    ldr r3, [pc, #4]    ; 100c <fun+0xc>
    1004:   e5930000    ldr r0, [r3]
    1008:   e12fff1e    bx  lr
    100c:   00002000    andeq   r2, r0, r0

Disassembly of section .bss:

00002000 <__bss_start>:
    2000:   00000000

它最终以 bss 初始化。

您真的想要对某些东西进行未初始化的访问,然后只需选择一个地址(您知道未初始化(sram))并访问它:

ldr r0,=0x1234
ldr r0,[r0]
于 2019-04-04T05:21:39.313 回答