【问题标题】:i386-elf-gcc out put strange assembler command about "static a = 0"i386-elf-gcc 输出关于“static a = 0”的奇怪汇编命令
【发布时间】:2016-10-11 10:24:38
【问题描述】:

我正在写一个迷你操作系统。当我写这段代码来显示时钟时,它出错了

  7 void timer_callback(pt_regs *regs)
  8 {
  9     static uint32_t tick = 0;
 10     printf("Tick: %dtimes\n", tick);
 11     tick++;
 12 }

tick 不是用 0 初始化,而是 1818389861。但是如果用 0x01 或其他任何零来初始化,就可以了!!!

所以我写了一个简单的 c 文件然后 objdump:

staic.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
extern void printf(char *, int);

int main(){
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  -0x4(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   51                      push   %ecx
   e:   83 ec 04                sub    $0x4,%esp
    static int a = 1;
    printf("%d\n", a);
  11:   a1 00 00 00 00          mov    0x0,%eax
  16:   83 ec 08                sub    $0x8,%esp
  19:   50                      push   %eax
  1a:   68 00 00 00 00          push   $0x0
  1f:   e8 fc ff ff ff          call   20 <main+0x20>
  24:   83 c4 10                add    $0x10,%esp
    return 0;
  27:   b8 00 00 00 00          mov    $0x0,%eax
}
  2c:   8b 4d fc                mov    -0x4(%ebp),%ecx
  2f:   c9                      leave
  30:   8d 61 fc                lea    -0x4(%ecx),%esp
  33:   c3                      ret

好奇怪,没用过内存!!!

更新:让我说清楚

  1. 第二个 static.c 是一个实验,它被认为显示没有使用内存,但我错了,mov 0x0 %eab 是。我混淆了 0x0 和 $0x0 /..\

  2. 我的起源问题是为什么 tick 不能成功初始化为 0。(但可以初始化为 1 或任何其他数字)。

  3. 我用gdb再次查找,好吧,它确实像mov
    eax,ds:0x106010
    一样使用内存,但真正强大的是内存x 0x106010不是0,但应该是,正如我所说,如果我让tick = 1 or anythingelse,内存按照我的意愿进行初始化,那就太奇怪了!

  4. 工具:gdb ,objdump 返回不同的asm(不同的手段,不是formate),因为,刚学os,不擅长c,所以就放了,忽略....

【问题讨论】:

  • 这里的问题是什么?您还显示了一些 C 代码,但反汇编是针对不同的东西 - 连接是什么?你指的那个奇怪的程序集在哪里?
  • 如果第一次调用 timer_callback 时滴答声没有打印为零,那么您的代码中的其他地方一定有未定义的行为。在您的第二个示例中,a 没有使用内存,因为编译器已经对其进行了优化,它不需要是静态的,因为您只打印一次然后退出。
  • 11: 处的指令是从地址 0x0 到 %eax 的 mov。地址为 0x0 的原因是因为这是一个目标文件,并且链接器所做的重定位尚未完成。链接后,这将是来自存储a 的正确内存位置的正确地址。
  • @Secto Kia 不管我给它起什么名字,仍然不是零。你的意思是什么“未定义的行为”?
  • 大部分代码。您必须有一些其他错误的代码导致未定义的行为。一个程序只会让你的 timer_callback 函数工作并打印“0times”。您可能不使用 timer_callback。如果您在其他地方有错误,您可能会在不知不觉中写到内存已用刻度的顶部。

标签: c gcc i386


【解决方案1】:

使用内存,请确保;但是,您不会在 .text 部分找到该内存。静态变量的内存分配在 .bss(零初始化时;或者,在 C++ 的情况下,动态初始化)或 .data(非零初始化时)部分。

当使用-d(反汇编)选项转储带有objdump 的目标文件时,同时使用-r(重定位)选项也很重要。没有它,你得到的反汇编是欺骗性的,没有任何意义。

在您的情况下,地址111f 处的指令必须重定位,地址为11,到变量a 和地址1f,到函数printf。地址11 处的指令从变量a 加载值,如果没有适当的重定位,它看起来好像从地址0 加载值。

至于您最初的问题,您得到的值 1818389861 或 0x6C626D65 非常了不起。我敢打赌,在您的程序的某个地方,您的缓冲区溢出涉及包含子序列embl 的字符串。

作为旁注,我想提醒您注意在printf 调用中使用正确的类型规范。类型规范%d对应类型int;在所有现代主流架构上,intint32_t 大小相同。但是,不能保证总是如此。对于显式大小的类型,有一些特殊的类型规范,例如,对于您使用 "PRId32"int32_t

uint32_t x;
printf("%"PRId32, x);

【讨论】:

  • 优秀,知识渊博的答案。那个 ASCII 序列是一个很好的收获。但是你错过了一个地方。 1a 也有搬迁。那里推送的常量是格式字符串的地址。
  • @Art,真的;我花了太多时间思考地址27 的指令。为什么编译器会在这里发出这么长的指令?
  • 我得到mov $0, %eax,gcc低于-O2,在-O2我得到更多预期的xorl %eax,%eax。我猜 gcc 甚至不会在没有更高优化级别的情况下为简单的恒定负载生成好的代码。很奇怪,但并不完全令人惊讶。
  • 当然,在 -O2 测试编译它是没有意义的,因为 gcc 看到 a 不能被修改,只是将一个常量推入堆栈
  • @petelin,正如我之前所说,零初始化数据放在.bss 部分,而非零初始化数据放在.data 部分。因此,通过将变量初始化为 0 以外的值,可以将其放置在不受缓冲区溢出影响的不同位置。
猜你喜欢
  • 2014-01-16
  • 2017-04-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
  • 1970-01-01
  • 2014-05-28
  • 1970-01-01
  • 2015-11-09
相关资源
最近更新 更多