.previous

摘自百度文库:

 

linux源代码中汇编语言部分总是有.previous.section.long,这是一个ELF段堆栈操作命令。其他的段堆栈操作命令还有.section.subsection.pushsection.popsection,本命令交换当前段(及其子段)和最近访问过的段(及其子段)。多个连续的.previous命令将使当前位置两个段(及其子段)之间反复切换。用段堆栈的术语来说,本命令使当前段和堆顶段交换位置。

previous表示恢复到当前.section定义之前的那个段作为当前段。也就是说这个previous与它前面的那个section一般成对出现.

只不过是一段汇编,主要还是对目标地址进行读写,不过在代码中添加了修正代码(.fixup),其目的就是当发生异常时执行该修正代码,防止内核被BUG掉。

1. __copy_user

__copy_userinclude/asm-i386/uaccess.h中定义,是作为从用户空间和内核空间进行内存复制的关键。这个宏扩展为汇编后如下:

000 #define __copy_user(to,from,size)

001 do {

002 int __d0, __d1;

003 __asm__ __volatile__(

004 "0: rep; movsl\n"

005 " movl %3,%0\n"

006 "1: rep; movsb\n"

007 "2:\n"

008 ".section .fixup,\"ax\"\n"

009 "3: lea 0(%3,%0,4),%0\n"

010 " jmp 2b\n"

011 ".previous\n"

012 ".section __ex_table,\"a\"\n"

013 " .align 4\n"

014 " .long 0b,3b\n"

015 " .long 1b,2b\n"

016 ".previous"

017 : "=&c"(size), "=&D" (__d0), "=&S" (__d1)

018 : "r"(size & 3), "0"(size / 4), "1"(to), "2"(from)

019 : "memory");

020 } while (0)

这段代码的主要操作就是004-007行,它的主要功能是将from处长度为size的数据复制到to处。

看这段代码之前,先看看它的约束条件:

017 : "=&c"(size), "=&D" (__d0), "=&S" (__d1)

018 : "r"(size & 3), "0"(size / 4), "1"(to), "2"(from)

019 : "memory");

017是输出部,根据描述可知size保存在ecx中,__d0保存在DI中,__d1保存在SI中。

018是输入部,根据描述可知size/4(size除以4后的整数部分)保存在ecx中,size&3(size除以4的余数部分)随便保存在某一个寄存器中,to保存在DI中,from保存在SI中。

然后再反过头来看004-007行,就明白了:

004行:将size/44字节从from复制到to。为了提高速度,这里使用的是movsl,所以对size也要处理一下。

005行:将size&3,即size/4后余下的余数,复制到ecx中。

006行:根据ecx中的数量,从from复制数据到to,这里使用的是movsb

007行:代码结束。

到这里,复制就结束了。

但是实际上没有这么简单,因为还可能发生复制不成功的现象,所以008-016行的代码都是进行此类处理的。

内核提供了一个意外表,它的每一项的结构是(x,y),即如果在地址x上发生了错误,那么就跳转到地址y处,这里行012-015就是利用了这个机制在编译时声明了两个表项。将这几行代码说明如下:

012行:声明以下内容属于段__ex_table

013行:声明此处内容4字节对齐。

014行:声明第一个意外表项,即如果在标志0处出错,就跳转到标志3(.section .fixup段中)

015行:声明第二个意外表项,即如果在标志1处出错,就跳转到标志2(.section .text段中)

上面之所以要在标志后面加上b,是因为引用之前的代码,如果要引用之后的代码就加f

这里对size的操作约定是:如果复制失败,则size中保留的是还没有复制完的数据字节数。

由于复制数据的代码只有4行,其中可能出现问题的就是004006行。从上面的异常表可以看出,内核的处理策略是:

(1) 如果在0处出错,那么这时没有复制完的字节数就是ecx中剩余的数字乘以4加上先前size除以4以后的那个余数。009行代码即完成此任务,lea 0(%3,%0,4),%0即计算%ecx = (size % 4) + %ecx * 4,并将这个数值赋值给返回C代码的size中。

(2)如果在1处出现错误,那么由于之前ecx中的size/4个字节都已经复制成功了,所以只需要将保存在任意一个寄存器中的size/4的余数赋值给size返回。

从汇编代码中可以看到,009行的异常处理代码被编译到一个叫做fixup的段中。

可见这段代码的本质就是从from复制数据到to,并对两处可能出现错误的地方进行简单的异常处理——返回未复制的字节数。

注意:

(1).section .fixup,"ax";.section __ex_table,"a";

将这两个.section.previous中间的代码汇编到各自定义的段中,然后跳回去,将这之后的的代码汇编到.text段中,也就是自定义段之前的段。.section.previous必须配套使用。

(2)例子中__ex_table异常表的安排在用户空间是不会得到执行的,它只在内核中有效。

 

自己写了个测试下:

文件sramboot.S

   1 #include"regdef.h"                                                                                          
      2 .set noreorder
      3 .text
      4 .globl start
      5 start:
      6     .set noat
      7     la at,value
      8     lw a1, 4(at)
      9     nop
     10     add a2, a0, a1
     11     sw a2, 8(at)
     12
     13 .data
     14 .section soochow ,"a"
     15 .align 2
     16 value:
     17     .long 0x0001
     18 .section seu,"a"
     19     .long 0x0004
     20 .previous
     21     .long 0x0002
     22     .long 0x0003
     23 .section seu,"a"
     24     .long 0x0005

Makefile文件

   1CROSS_COMPILE=mipsellinux-
      2 CC = $(CROSS_COMPILE)gcc
      3 AS = $(CROSS_COMPILE)as
      4 LD = $(CROSS_COMPILE)ld
      5 OBJDUMP = $(CROSS_COMPILE)objdump
      6 OBJCOPY = $(CROSS_COMPILE)objcopy
      7 TEXTBASE =# -Ttext
      8 DATABASE =
      9 LDFLAGS = $(TEXTBASE) $(DATABASE) -T my.ld
     10
     11 test.bin:test.exe
     12     $(OBJCOPY) -O binary test.exe test.bin
     13     
     14 test.exe: sramboot.o
     15     $(LD)  sramboot.o $(LDFLAGS) -o test.exe
     16     $(OBJDUMP) -D test.exe > test.dump
     17
     18 sramboot.o:sramboot.S
     19     $(CC) -E sramboot.S -o sramboot.s
     20     $(AS) sramboot.s -o sramboot.o
     21     @rm sramboot.s
     22
     23 clean:
     24     @rm *.o *.exe *.bin *.dump

链接脚本my.ld

   1 OUTPUT_FORMAT("elf32-tradlittlemips","elf32-tradbigmips","elf32-tradlittlemips")
      2 OUTPUT_ARCH(mips)
      3 ENTRY(start)
      4 SECTIONS
      5 {
      6     . =0;
      7     .text :
      8     {
      9         *(.text)
     10     }
     11     .data :
     12     {
     13         *(.data)
     14     }
     15     .bss :
     16     {
     17         *(.bss)
     18     }
     19 }

bin文件反汇编脚本test.sh

   1#!/bin/sh
      2 A=0
      3 if [ ! -e test.bin ]
      4 then
      5         make
      6         A=1
      7 fi
      8
      9 mipsel-linux-objdump -D -m mips -b binary -EL -M no-aliases -z test.bin >my.dump
     10
     11 if [ $A = 1 ]
     12     then
     13         rm sramboot.o *bin *exe
     14 fi

结果:

 

 

 

 

posted on 2012-05-30 17:15  阿加  阅读(2850)  评论(0编辑  收藏  举报

导航