美文网首页代码世界
Solaris SPARC内存访问地址未对齐导致的崩溃分析

Solaris SPARC内存访问地址未对齐导致的崩溃分析

作者: CodingCode | 来源:发表于2017-09-28 16:30 被阅读1次

从一个程序coredump说起

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char * argv[]) {
    int len1    = 1;
    int len2    = 2;
    const char * msg = "AAAAAA";
    unsigned char * buffer = malloc(20);
    int out1;
    int out2;

    int i = 0;
    memset(buffer, 0, 20);
    for (i = 0; i < 20; i++) { printf("%02X ", buffer[i]); } printf("\n");
    memcpy(buffer, &len1, sizeof(int));
    memcpy(buffer+sizeof(int), msg, strlen(msg));
    memcpy(buffer+sizeof(int)+strlen(msg), &len2, sizeof(int));
    for (i = 0; i < 20; i++) { printf("%02X ", buffer[i]); } printf("\n");

    
    printf("p1=%p,p2=%p\n", buffer, buffer+sizeof(int)+strlen(msg));
    out1 = *(int *)buffer;
    printf("out1=%d\n", out1);
    out2 = *(int *)(buffer+sizeof(int)+strlen(msg));
    printf("out1=%d\n", out2);
    return 0;
}

这个例子的目的就是创造错误场景,有一段buffer先把一个整数len1存入,然后再存入6个字符msg,接着再存入一个整数len2;后面从buffer里面读出存入的两个整数。
在Solaris SPARC上编译运行:

bash-3.2$ uname -a
SunOS <hostname> 5.10 Generic_148888-02 sun4v sparc sun4v
bash-3.2$ gcc -g t.c 
bash-3.2$ ./a.out 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 01 41 41 41 41 41 41 00 00 00 02 00 00 00 00 00 00 
p1=20dc8,p2=20dd2
out1=1
Bus Error (core dumped)
bash-3.2$ echo $?
138
bash-3.2$ 

用GDB查看出错的位置:

bash-3.2$ gdb a.out core 
...
#0  0x10aa4 in main (argc=1, argv=0xffbffaac) at t.c:25
25          out2 = *(int *)(buffer+sizeof(int)+strlen(msg));
(gdb) 

很明显在第25行的时候出现了Bus Error问题;这个程序的退出代码是138,换算成最大127的返回值就是10,对应的signal就是10,即SIGBUS

而同样的程序在Linux 86平台是可以编译运行的

$ uname -a
Linux <hostname> 4.1.12-94.3.8.el7uek.x86_64 #2 SMP Fri Jun 30 10:40:13 PDT 2017 x86_64 x86_64 x86_64 GNU/Linux
$ gcc -g t.c 
$ ./a.out 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
01 00 00 00 41 41 41 41 41 41 02 00 00 00 00 00 00 00 00 00 
p1=0xcb6010,p2=0xcb601a
out1=1
out2=2

正常的打印出了out1和 out2。

究其原因因为SPARC是RISC类型处理器,当需要访问内存地址的时候需要满足对齐条件,具体来说,

  1. 如果要从一个内存地址读写2字节,例如short,那么这个地址必须是2字节对齐的,也就是说地址能被2整除。
  2. 如果要从一个内存地址读写4字节,例如int,那么这个地址必须是4字节对齐的,也就是说地址能被4整除,通俗的说地址末尾必须是0x0, 0x4, 0x8, 0xC。
  3. 如果要从一个内存地址读写8字节,例如long long,那么这个地址必须是8字节对齐的,也就是说地址能被8整除,通俗的说地址末尾必须是0x0, 0x8。

我们在例子中看到p1的值的末尾是0x0,这是一个4字节对齐的地址,所以可以读出来,赋给一个int类型out1,而p2的值的末尾是0xa,它不是一个4字节对齐的地址,所以它不能读出来付给int类型,但其实这是一个2字节对齐的地址,那么可以赋值给一个short。

$ vim t.c
    //int out2;
    short out2;
    ...
    out2 = *(int *)(buffer+sizeof(int)+strlen(msg));
    printf("out=%d\n", out2);

$ gcc t.c && ./a.out
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
01 00 00 00 41 41 41 41 41 41 02 00 00 00 00 00 00 00 00 00 
p1=0x15431010,p2=0x1543101a
out=1
out=2

此时我们看到在Solaris SPARC环境下整个程序编译运行正常。

那为什么在Linux x86环境下面没有问题,这主要是依赖于CISC处理器的特点,CISC和RISC处理器的特征差异,具体得看他们的文档了,我也解释不了,RISC为什么要这样限制。

参考文档

Runtime Checking Errors: https://docs.oracle.com/cd/E18659_01/html/821-1380/blaiu.html

相关文章

  • Solaris SPARC内存访问地址未对齐导致的崩溃分析

    从一个程序coredump说起 这个例子的目的就是创造错误场景,有一段buffer先把一个整数len1存入,然后再...

  • 内存对齐

    为什么需求内存对齐 为了访问未对齐的内存,处理器需要做两次内存访问; 对齐的内存访问仅需要一次访问 什么数据需要内...

  • 话说内存对齐

    文章原文在我的个人博客 为什么会有内存对齐? 为了访问未对齐的内存,处理器需要作两次内存访问;而,对齐访问仅需要一...

  • 内存对齐

    内存对齐 一种提高内存访问速度的策略,cpu在访问未对其的内存需要经过两次内存访问,而经过内存对齐一次就可以了。 ...

  • 004-iOS 内存对齐

    一、概念 内存对齐是编译器为每个数据元单位安排在适当的内存位置上。 二、内存对齐原因 为了访问未对齐的内存,处理器...

  • Simics & Solaris Sparc

    用 simics 模拟运行 Solaris SPARC 版。 环境 机器: Macbook Pro 11,1 OS...

  • Golang内存对齐

    如何得到一个对象所占内存大小? 内存对齐: 为何会有内存对齐?1.并不是所有硬件平台都能访问任意地址上的任意数据。...

  • iOS底层之内存对齐

    一、什么是内存对齐? 内存对齐是一种在计算机内存中排列数据(表现为变量的地址)、访问数据(表现为CPU读取数据)的...

  • iOS中的内存对齐

    一、什么是内存对齐 内存对齐是一种在计算机内存中排列数据(表现为变量的地址)、访问数据(表现为CPU读取数据)的一...

  • 二、iOS-内存对齐

    一、什么是内存对齐 内存对齐是一种在计算机内存中排列数据(表现为变量的地址)、访问数据(表现为CPU读取数据)的一...

网友评论

    本文标题:Solaris SPARC内存访问地址未对齐导致的崩溃分析

    本文链接:https://www.haomeiwen.com/subject/oifvextx.html