静态值在哪里存储在程序集中

时间:2019-02-12 03:34:41

标签: c assembly

这是一个简单的C代码

#include <stdio.h>

int a = 5;

static int b = 20;

int main(){

 int c = 30;

 return 0;
}

编译后没有优化:

    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 13
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    xorl    %eax, %eax
    movl    $0, -4(%rbp)
    movl    $30, -8(%rbp)
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __DATA,__data
    .globl  _a                      ## @a
    .p2align    2
_a:
    .long   5                       ## 0x5



我的问题是,static int b = 20;在上面的程序集中在哪里? 我知道它们应该在内存的全局区域中,但是在编译版本中找不到。

4 个答案:

答案 0 :(得分:7)

您的代码不使用b,并且它是文件作用域的,因此其他文件中没有任何内容可以使用它。 GCC不会为它发出定义。

要回答标题问题:
带有非零初始值设定项的非const静态/全局变量(即静态存储类)变量将进入.section .data,而不是.bss(零初始可变),或者.rdata(Windows)/ .rodata(Linux)处理非零的只读数据。


gcc没有完全可以自动转换为asm的完全死脑模式。请参见Disable all optimization options in GCC-GCC始终必须通过其内部表示进行转换。

即使在-O0,GCC也会进行一次通行证,以排除未使用的东西。 可能有一种禁用该功能的方法,这与gcc甚至在-O0所做的其他转换不同。

gcc和clang -O0将每个语句编译到一个单独的asm块中,该块存储/重新加载所有内容(for consistent debugging),但是gcc仍在其块中应用其标准转换,例如(x+y) < x对于有符号的x和y with gcc8 and newery<0变为x / 10,变成高半部分的乘+移。 (Why does GCC use multiplication by a strange number in implementing integer division?)。

并且if(false)内的代码甚至在-O0处也被gcc删除,因此您无法在GDB中jump对其进行编码。

有些人关心调试版本的运行时性能,尤其是实时软件(如游戏或操作系统)的开发人员,如果运行速度太慢,则无法正确测试。 (游戏中的人机交互,或操作系统中的设备驱动程序。)


其他一些编译器在-O0 上比较死脑筋,因此您经常会看到看起来更像源表达式的asm。我想我已经看到没有优化的MSVC发出的指令将mov立即插入寄存器,然后cmp reg,imm,即在运行时执行仅依赖于立即数的分支,因此可以微不足道地进行计算在该表达式的编译时。

当然,确实有一些非优化的编译器,其整个目标只是使用固定模式进行音译。例如,我认为Tiny C Compiler几乎是一遍遍的,并且在运行过程中会发出asm(或机器代码)。参见Tiny C Compiler's generated code emits extra (unnecessary?) NOPs and JMPs展示了它的简单性:它总是在函数序言中发出sub esp, imm32,并且只有在知道函数需要多少堆栈时才返回以填充函数末尾。 。即使答案为零,也无法删除它并收紧代码。


无论如何,查看优化的asm通常更有趣。编写带有args并返回值的函数,因此您可以看到asm有趣的部分,而不会产生很多样板内容和存储/重载噪声。 How to remove "noise" from GCC/clang assembly output?

答案 1 :(得分:3)

如果编译器未优化static变量,它将进入进程的默认数据部分。

在汇编中,通常可以由程序员在指定用于描述数据部分的文件部分中进行控制。

C标准在第6.2.4节第3段中说:

  

一个其标识符声明为...的对象,其中存储类说明符为static,具有静态存储持续时间。它的生命周期是程序的整个执行过程,并且在程序启动之前,其存储值仅初始化一次。

使用以下代码:

static int a = 100;

int foo()
{
    return (a / 2);
}

查看符号_aMSVC_DATA段中如何出现,GCC的第27-30行,{{3}的第28-30行}。

答案 2 :(得分:0)

整个问题有点不准确...(重读它,您实际上对“在上面的程序集中”很具体。。。哦,那么答案是“无处” ..而我剩下的答案是尚未发布的问题,但希望能解释为什么“无处”是您的问题的答案。

您有C源代码,然后将某些程序集显示为编译器输出(但未指定编译器),然后询问有关程序集的信息。

正在“ C抽象机”上定义C,而您正在查看此类抽象机的特定x86-64 实现

虽然实现有一些通常会导致静态变量终止的规则,但它完全取决于编译器-它希望如何实现它们。

在纯汇编中(如手写体或从CPU角度看),没有“静态值”之类的东西。您只有寄存器,内存和外围设备。

因此,在汇编语言(机器代码)中,您可以将某些寄存器或内存的某些部分用作静态变量。无论哪种方法都更适合您的需求(没有硬性规定会迫使您以任何特定方式进行操作,除非您必须在目标CPU的有效机器代码中表达您的想法,但这通常意味着数十亿种可能性,甚至当将自己限制为仅“合理的”方法,它比仅仅单一方法更倾向于数十种可能的方法。

您甚至可以(在x86-64中)创建一个有点复杂的方案,如何将值保持为代码状态(“内存的一部分”就是机器代码占用的内存),即,它不会被直接写入在内存中作为一个值,但是代码将遵循某些代码路径(从许多可能的路径)以获得正确的最终结果,即在代码本身中对值进行编码。例如,有一个图灵完备的方法,如何仅使用mov指令将C源代码编译为x86-64机器代码,该指令可能不使用内存作为静态变量(不确定,是否添加了.data部分或者也可以通过将其编译成mov代码来避免它,但是从其绝对存在的意义上,应该可以很清楚地知道如何从理论上避免.data。)

所以您要问的是具有特定编译时间选项的特定C编译器如何实现静态值(并且可能有一些变体,具体取决于所使用的源和选项)...

...或者如果您真正询问的是“程序集中的静态值存储在哪里”,那么答案是“只要您的机器代码有效且正确,就可以在任何地方” ,因为整个“静态值”概念的层次要高于CPU的操作水平,所以就好像对特定机器代码用途的解释“静态值”一样,但是CPU中没有特定的指令/支持来处理该问题。

答案 3 :(得分:-3)

静态变量未存储在内存中。它们只会在使用时出现 例如

static int b = 20;  c = c + b;

将编译

添加c,“ 20”