User Tools

Site Tools


tech:linux:presdo-op

关于LINUX ASM Pseudo-OP的一点介绍

如果希望读懂一个ASM文件,起码得熟悉两点:其一是目标处理器的指令集;其二是汇编器的Pseudo-OP(Pseudo-OP是不是应该译作伪操作,因为它和伪指令似乎还是有所区别?)。关于LINUX ASM的文章在网上有很多,然而多半仍旧以在I386架构上实现所谓的”Hello World”为例竭力分析,将问题复杂化,而且没有抓到重点。可惜在I386架构上编写ASM文件的机会本不多,何况是庞大的”Hello World”?因此,最终也没有找到介绍LINUX ASM Pseudo-OP的参考资料,只能通过分析实际的编译过程来给出一些简单的解释。

1. SYMBOL

SYMBOL是C/C++以及ASM用于访问变量和函数的一种别名机制,对于LINUX ELF格式,一个SYMBOL具备名称、类型、索引、尺寸、绑定等几种属性。

/******************  test.S  *****************/
l:
        nop
/******************************************/

上面这段代码就可以定义一个SYMBOL l,它的值是由当前Section中的location couter给出的,应该为0x00000000。以下分别为readelf -s及objdump -t给出的信息,注意第4行:

Symbol table '.symtab' contains 7  entries:
Num:      Value    Size  Type        Bind     Vis            Ndx  Name
0:        00000000 0     NOTYPE      LOCAL    DEFAULT        UND  
1:        00000000 0     SECTION     LOCAL    DEFAULT        1  
2:        00000000 0     SECTION     LOCAL    DEFAULT        3  
3:        00000000 0     SECTION     LOCAL    DEFAULT        4
4:        00000000 0     NOTYPE      LOCAL    DEFAULT        1    l
5:        00000000 0     NOTYPE      LOCAL    DEFAULT        1    $a
6:        00000000 0     SECTION     LOCAL    DEFAULT        5  
SYMBOL TABLE:
00000000  l        d    .text                00000000  .text
00000000  l        d    .data                00000000  .data
00000000  l        d    .bss                 00000000  .bss
00000000  l             .text                00000000  l  
00000000  l        d    .note.gnu.arm.ident  00000000  .note.gnu.arm.ident

可以看出,l是定义在.text中的一个LOCAL SYMBOL,它没有类型限定,size为0,值为0x00000000。

2. 使用.type限定SYMBOL的类型。

对代码稍作修改后查看readelf -s及objdump -t给出的信息:

/******************  test.S  *****************/
.type  l,  %function
l:
        nop
/******************************************/
Symbol table '.symtab' contains 7 entries:
Num:        Value    Size  Type        Bind      Vis            Ndx  Name
0:          00000000 0     NOTYPE      LOCAL    DEFAULT         UND
1:          00000000 0     SECTION     LOCAL    DEFAULT         1  
2:          00000000 0     SECTION     LOCAL    DEFAULT         3  
3:          00000000 0     SECTION     LOCAL    DEFAULT         4  
4:          00000000 0     FUNC        LOCAL    DEFAULT         1    l
5:          00000000 0     NOTYPE      LOCAL    DEFAULT         1    $a
6:          00000000 0     SECTION     LOCAL    DEFAULT         5  
SYMBOL TABLE:
00000000  l        d    .text                00000000  .text
00000000  l        d    .data                00000000  .data
00000000  l        d    .bss                 00000000  .bss
00000000  l         F   .text                00000000  l
00000000  l        d    .note.gnu.arm.ident  00000000  .note.gnu.arm.ident

可以注意到,l的类型成为FUNC。

3. 使用.globl限定SYMBOL的绑定特性

LOCAL的可见性局限于当前目标文件,而GLOBAL可以为所有参与链接的目标文件可见。

对代码稍作修改后查看readelf -s及objdump -t给出的信息:

/******************  test.S  *****************/
        .type  l,  %function
        .globl  l
l:
        nop
/*******************************************/
Symbol table '.symtab' contains 7 entries:
Num:        Value    Size  Type        Bind      Vis            Ndx  Name
0:          00000000 0     NOTYPE      LOCAL     DEFAULT        UND  
1:          00000000 0     SECTION     LOCAL     DEFAULT        1  
2:          00000000 0     SECTION     LOCAL     DEFAULT        2  
3:          00000000 0     SECTION     LOCAL     DEFAULT        3  
4:          00000000 0     NOTYPE      LOCAL     DEFAULT        1    $a
5:          00000000 0     SECTION     LOCAL     DEFAULT        4  
6:          00000000 0     FUNC        GLOBAL    DEFAULT        1    l
    
SYMBOL TABLE:
00000000  l        d    .text                00000000  .text
00000000  l        d    .data                00000000  .data
00000000  l        d    .bss                 00000000  .bss
00000000  l        d    .note.gnu.arm.ident  00000000  .note.gnu.arm.ident
00000000  g         F   .text                00000000  l

4. 使用.section显式设定当前的section

对代码稍作修改后查看readelf -s及objdump -t给出的信息:

/******************  test.S  *****************/
        .type  l,  %function
        .globl  l
        .section  my_section
l:
        nop
/*******************************************/       
Symbol table '.symtab' contains 8 entries:
Num:        Value    Size  Type        Bind      Vis           Ndx  Name
0:          00000000 0     NOTYPE      LOCAL    DEFAULT        UND  
1:          00000000 0     SECTION     LOCAL    DEFAULT        1  
2:          00000000 0     SECTION     LOCAL    DEFAULT        2  
3:          00000000 0     SECTION     LOCAL    DEFAULT        3  
4:          00000000 0     SECTION     LOCAL    DEFAULT        5  
5:          00000000 0     NOTYPE      LOCAL    DEFAULT        5    $a
6:          00000000 0     SECTION     LOCAL    DEFAULT        4    l             
SYMBOL TABLE:
00000000  l        d    .text                00000000  .text
00000000  l        d    .data                00000000  .data
00000000  l        d    .bss                 00000000  .bss
00000000  l        d    my_section           00000000  my_section
00000000  l        d    .note.gnu.arm.ident  00000000  .note.gnu.arm.ident
00000000  g         F   my_section           00000000  l

能发现两个变化,其一,多出了一个索引为4的符号,代表my_section;其二,l从.text跑到了my_section中。

5. 使用.equ令一个SYMBOL无关于任何section,而其值成为一个绝对的值。

对代码稍作修改后查看readelf -s及objdump -t给出的信息,注意黑体部分:

/******************  test.S  *****************/          
        .type  l,  %function
        .globl  l
        .section  my_section
        .type  m,  %object
        .globl  m
        .equ  m,  0x10
l:
        nop          
/*******************************************/    

Symbol table '.symtab' contains 9 entries:

Num:        Value    Size  Type        Bind      Vis           Ndx  Name
0:          00000000 0     NOTYPE      LOCAL    DEFAULT        UND  
1:          00000000 0     SECTION     LOCAL    DEFAULT        1  
2:          00000000 0     SECTION     LOCAL    DEFAULT        2  
3:          00000000 0     SECTION     LOCAL    DEFAULT        3  
4:          00000000 0     SECTION     LOCAL    DEFAULT        5  
5:          00000000 0     NOTYPE      LOCAL    DEFAULT        5    $a
6:          00000000 0     SECTION     LOCAL    DEFAULT        4  
7:          00000000 0     FUNC        GLOBAL   DEFAULT        5    l
8:          00000010 0     OBJECT      GLOBAL   DEFAULT        ABS  m
SYMBOL TABLE:
00000000  l        d    .text                00000000  .text
00000000  l        d    .data                00000000  .data
00000000  l        d    .bss                 00000000  .bss
00000000  l        d    my_section           00000000  my_section
00000000  l        d    .note.gnu.arm.ident  00000000  .note.gnu.arm.ident
00000000  g         F   my_section           00000000  l
00000010  g         O   *ABS*                00000000  m

能够发现多出了一个名为m的符号,它的值为0x10,类型为OBJECT,是一个absolute的值。 SYMBOL m的索引为ABS(0xfff1),与任何section都无关。

6. 用于数据定义的Pseudo-OP

用于数据定义的Pseudo-OP如.long、.short、.byte等可以在当前section根据位置计数器指定的位置设置一个特定值。

/******************  test.S  *****************/          
        .type  l,  %function
        .globl  l
        .section  my_section
        .type  m,  %object
        .globl  m
        .equ  m,  0x10
l:
        nop
 
        .section  my_data
        .long  0xff
        .short  0xff
        .byte  0xff          
/******************************************* / 

以下为objdump -s给出的信息:

Contents  of  section  my_data:
  0000  ff000000  ff00ff                                            .......      

在section my_data中,offset 0处存放了一个双字0x000000ff,而在offset 4处存放了一个字0x00ff,最后在offset 6出存放了一个字节0xff。

7. 使用.align限定位置计数器

/******************  test.S  *****************  /  
        .type  l,  %function
        .globl  l
        .section  my_section
        .type  m,  %object
        .globl  m
        .equ  m,  0x10
l:
        nop
 
        .section  my_data
        .long  0xff
        .short  0xff
        .align  2
        .byte  0xff
/*******************************************/  
Contents  of  section  my_data:
  0000  ff000000  ff000000  ff000000                      ............    

从上面objdump给出的信息可以看出,由于使用.align 2(2的2次方)使位置计数器双字对齐,字节0xff被设置到了section my_data的offset 8,不再是6。

8. 在回到SYMBOL来。

SYMBOL和C语言的变量是什么关系,他们可以相互引用吗?答案是肯定的。

下面的C文件将引用test.S中的m:

/*****************  main.c  *************************/
 
#include  <stdio.h>
 
extern  int  m;
 
int  main()
{
	printf("&m=0x%x\n",  &m);
	return  0;
}
 
/**************************************************/

输出结果为:&m=0x10,是不是有些令人意外?

9. 关于ENTRY

ENTRY是一个在LINUX ASM中常见的“关键字”,然而实际上它并不是一个Pseudo-OP,而是一个宏,定义在linux/linkage.h中:

#define  ENTRY(name)  \
    .globl  name;  \
    ALIGN;  \
    name:

这个宏可以让ASM文件中定义的函数为C文件所调用。

10. 关于size

使用.size关键字可以设定SYMBOL的size,比如定义一个函数,可以用如下方法:

    .long  0xffffffff
    .globl main
    .align 4
main:
    mov pc, lr
    .type main, %function
    .size main, .-main

查看readelf -s及objdump -t给出的信息 Symbol table '.symtab' contains 7 entries:

Num:    Value  Size Type    Bind   Vis      Ndx Name
  0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
  1: 00000000     0 SECTION LOCAL  DEFAULT    1 
  2: 00000000     0 SECTION LOCAL  DEFAULT    2 
  3: 00000000     0 SECTION LOCAL  DEFAULT    3 
  4: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 $a
  5: 00000000     0 SECTION LOCAL  DEFAULT    4 
  6: 00000000     4 FUNC    GLOBAL DEFAULT    1 main
SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .ARM.attributes        00000000 .ARM.attributes
00000000 g     F .text  00000004 main

可以看到,main是一个位于.text段的全局函数,size为4,值为0。通常,Linux中会定义一个对应于ENTRY的宏ENDPROC,用于定义可以为C语言调用的函数:

#define ENDPROC(name) \
  .type name, %function; \
  END(name)
 
#define END(name) \
  .size name, .-name
tech/linux/presdo-op.txt · Last modified: 2014/11/10 08:22 (external edit)