地址无关代码

Linux宏:__ASSEMBLY__

汇编:assembly

猜测:所以这个宏跟汇编有关?!

引用:某些常量宏会同时被C和asm引用,而C与asm在对立即数符号的处理上是不同的。asm中通过指令来区分其操作数是有符号还是无符号的,而不是通过操作数。而C中是通过变量的属性,而不是通过操作符。C中如果要指明常量有无符号,必须为常量添加后缀,而asm则通过使用不同的指令来指明。如此,当一个常量被C和asm同时包含时,必须做不同的处理。故KBUILD_AFLAGS中将添加一项D__ASSEMBLY__,来告知预处理器此时是asm。

复制代码
/* linux-3.08/include/linux/const.h */

/* const.h: Macros for dealing with constants.  */

#ifndef _LINUX_CONST_H
#define _LINUX_CONST_H

/* Some constant macros are used in both assembler and
 * C code.  Therefore we cannot annotate them always with
 * 'UL' and other type specifiers unilaterally.  We
 * use the following macros to deal with this.
 *
 * Similarly, _AT() will cast an expression with a type in C, but
 * leave it unchanged in asm.
 */

/*
* 上边的注释说:一些常量宏同时在汇编和C中使用,然而,我们不能像注释C的常量宏那样加一个“UL”或其他后缀。所以我们需要使用以下的宏解决这个问题。
* 例如调用:#define DEMO_MACRO _AT(1, UL):在C中会被解释为 #define DEMO_MACRO 1UL; 而在汇编中什么都不做,就是: #define DEMO_MACRO 1
*/
#ifdef __ASSEMBLY__
#define _AC(X,Y) X #define _AT(T,X) X #else #define __AC(X,Y) (X##Y) #define _AC(X,Y) __AC(X,Y) #define _AT(T,X) ((T)(X)) #endif #endif /* !(_LINUX_CONST_H) */

__KERNEL__ 宏作用是什么?


这个宏在内核及应用程序代码中均能看到。它仅起到判断作用,而不是在实际的代码逻辑中被替换,就如之前讲的避免define重复定义的用法一样。但不同的是这个宏的目的并不是避免重复定义,那么这个宏到底是什么意思?

通过相应的代码可以看到如果定义了 __KERNEL__ 则会多编译一段。而多编译的这一段是只有像内核这样的代码会用到的


这样考虑一下:我为某个设备写了一个设备驱动,毫无疑问编译的时候肯定会加上 __KERNEL__,然后这个驱动往往还要提供一个库文件,为应用程序提供访问某些变量或函数的接口。最后我们要写一个应用程序来操纵设备。那么在编写这个应用程序的时候假如库文件提供的东西还不够,比如库文件没有定义上述的#define CLONE_DETACHED 0×00400000,而库文件的API 函数包含返回CLONE_DETACHED 的可能,那么就无法用if(CLONE_DETACHED==fun(arg)) 来做判断了(当然你可以去扒内核代码去比较0×004000000)。那么应用程序代码中肯定要包含这内核头文件了(现在不推荐这样做了),那么就出现了一个问题让应用程序引用内核头文件会暴露很多内核的细节和增加目标文件的大小,因为很多内核结构或变量,应用程序几乎用不着那么就要设置一个边界,设定哪些是只有给内核代码可见的,哪些是开放给所有代码可见的。 这个边界就是__KERNEL__ 宏。其实实际的做法是所有该引用的内核头文件,变量或结构都要库的头文件中包含,这些属于内核的东东往往要重新定义在库的头文件中,然后再提供给应用程序去引用。


 The __KERNEL__ macro is defined because there is programs (like libraries) than include kernel code and there is many things that you don't want them to include. So most modules will want the__KERNEL__ macro to be enabled.


When you compile your kernel, __KERNEL__ is defined on the command line.

User-space programs need access to the kernel headers, but some of the info in kernel headers is intended only for the kernel. Wrapping some statements in an#ifdef __KERNEL__/#endif block ensures that user-space programs don't see those statements.


 example:

in the user space ,if you want to include the header using ‘#ifdef __KERNEL__ XXXX', you should define the __KERNEL__.

/*为了引用kernel中的数据结构,我常用如下样式include那些服务于内核的头文件*/

#ifdef __KERNEL__
#include <linux/list.h>
#include <linux/mm.h>
#undef __KERNEL__


/*将两种类型的头文件隔离开来*/
#include <stdio.h>
#include <errno.h>






出处:http://m.blog.csdn.net/wl772689366/article/details/51603882


在计算机领域中,地址无关代码 (英文position-independent code,缩写为PIC),又称地址无关可执行文件 (英文: position-independent executable,缩写为PIE) ,是指可在主存储器中任意位置正确地运行,而不受其绝对地址影响的一种机器码。PIC广泛使用于共享库,使得同一个库中的代码能够被加载到不同进程的地址空间中。PIC还用于缺少内存管理单元的计算机系统中,[1] 使得操作系统能够在单一的地址空间中将不同的运行程序隔离开来。

地址无关代码能够在不做修改的情况下被复制到内存中的任意位置。这一点不同于重定位代码,因为重定位代码需要经过链接器加载器的特殊处理才能确定合适的运行时内存地址。 地址无关代码需要在源代码级别遵循一套特定的语义,并且需要编译器的支持。那些引用了绝对内存地址的指令(比如绝对跳转指令)必须被替换为PC相对寻址指令。这些间接处理过程可能导致PIC的运行效率下降,但是目前大多数处理器对PIC都有很好的支持,使得这效率上的这一点点下降基本可以忽略。



出处:http://m.blog.csdn.net/lizuobin2/article/details/52049892

在移植 uboot 时,接触到一个概念叫做 位置无关码,那么与它对应的就是位置有关码。提到这两个概念就还得提一提链接地址、加载地址。

    链接地址,链接脚本里指定的,理论上程序运行时所处的地址。在编译时,编译器会根据链接地址来翻译位置有关码。

    加载地址,程序运行时,实际所处的地址。

    位置无关码,位置有关码,是相对于一条指令的正常目的来说的。比如 ldr r0 ,=标号,它的正常目的是取得标号处的地址,对于这个目的,它是位置有关码,运行的地址不对就获取不到正确的标号地址,其实它无论在哪都是获取的程序加载地址等于链接地址时,标号的地址,如果你就是想要这个值,那么用这条指令是非常正确的,就不用理会什么位置无关码,位置有关码的概念了,这一点非常重要。

    因此,当加载地址不等于链接地址时,并不是不可以用位置无关码,而是要看你用位置无关码是否达到了你想要的目的。

    位置无关码,依赖于程序当前运行的PC值,进行相对的跳转,导致的结果就是,无论代码在哪,总能达到指令的正常目的,因此是位置无关的。

    位置有关码,不依赖当前PC值,是绝对跳转,只有程序运行在链接地址处时,才能达到指令的正常目的,因此是位置有关系的。

    下面,我们来看常用的汇编指令以及C语言中哪些操作是位置有关码,哪些是位置无关码。

SECTIONS {
    . = 0x33f80000;
    .text : { *(.text) }
    
    . = ALIGN(4);
    .rodata : {*(.rodata*)} 
    
    . = ALIGN(4);
    .data : { *(.data) }
    
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss)  *(COMMON) }
    __bss_end = .;
}
.text
.global _start
_start:

	bl close_watch_dog		/* 相对跳转,位置无关 */
	bl _start
	adr r0, close_watch_dog	/* 获取标号地址,位置无关 */
	
	ldr r0, SMRDATA			/* 获取标号处的值,位置无关 */

	ldr r0, =0x12345678
	ldr r0, =SMRDATA        /* 获取标号地址,位置有关 */
	ldr r0, =main			/* 获取函数名的地址,位置有关 */
	ldr r0 ,=__bss_start	/* 获取链接脚本里标号的地址,位置有关 */

	
close_watch_dog:
	mov r1, #0
	str r1, [r0]
	mov pc, lr
	
SMRDATA:
    .word  0x22111120 
int a;
void abc(){
	a = 2;
}
int main(){
	int b;
	a =1 ;
	b =1 ;
	abc();
	return 0;
}
如果加载地址为 0 ,那么代码将按照下面的顺序排放
00000000 <_start>:
00000000:	eb000006 	bl	33f80020 <close_watch_dog>
00000004:	ebfffffd 	bl	33f80000 <_start>
00000008:	e28f0010 	add	r0, pc, #16
0000000c:	e59f0018 	ldr	r0, [pc, #24]	; 
00000010:	e59f0018 	ldr	r0, [pc, #24]	; 
00000014:	e59f0018 	ldr	r0, [pc, #24]	; 
00000018:	e59f0018 	ldr	r0, [pc, #24]	; 
0000001c:	e59f0018 	ldr	r0, [pc, #24]	; 

00000020 <close_watch_dog>:
00000020:	e3a01000 	mov	r1, #0
00000024:	e5801000 	str	r1, [r0]
00000028:	e1a0f00e 	mov	pc, lr

0000002c <SMRDATA>:
0000002c:	22111120 	andscs	r1, r1, #8
00000030:	12345678 	eorsne	r5, r4, #125829120	; 0x7800000
00000034:	33f8002c 	mvnscc	r0, #44	; 0x2c
00000038:	33f80064 	mvnscc	r0, #100	; 0x64
0000003c:	33f800a0 	mvnscc	r0, #160	; 0xa0

00000040 <abc>:
00000040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
00000044:	e28db000 	add	fp, sp, #0
00000048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
0000004c:	e3a02002 	mov	r2, #2
00000050:	e5832000 	str	r2, [r3]
00000054:	e28bd000 	add	sp, fp, #0
00000058:	e8bd0800 	pop	{fp}
0000005c:	e12fff1e 	bx	lr
00000060:	33f800a0 	mvnscc	r0, #160	; 0xa0

00000064 <main>:
00000064:	e92d4800 	push	{fp, lr}
00000068:	e28db004 	add	fp, sp, #4
0000006c:	e24dd008 	sub	sp, sp, #8
00000070:	e59f3024 	ldr	r3, [pc, #36]	; 33f8009c <main+0x38>
00000074:	e3a02001 	mov	r2, #1
00000078:	e5832000 	str	r2, [r3]
0000007c:	e3a03001 	mov	r3, #1
00000080:	e50b3008 	str	r3, [fp, #-8]
00000084:	ebffffed 	bl	33f80040 <abc>
00000088:	e3a03000 	mov	r3, #0
0000008c:	e1a00003 	mov	r0, r3
00000090:	e24bd004 	sub	sp, fp, #4
00000094:	e8bd4800 	pop	{fp, lr}
00000098:	e12fff1e 	bx	lr
0000009c:	33f800a0 	mvnscc	r0, #160	; 0xa0
如果加载地址为0x33f80000 则按照下边的顺序排放
33f80000 <_start>:
33f80000:	eb000006 	bl	33f80020 <close_watch_dog>
33f80004:	ebfffffd 	bl	33f80000 <_start>
33f80008:	e28f0010 	add	r0, pc, #16
33f8000c:	e59f0018 	ldr	r0, [pc, #24]	; 33f8002c <SMRDATA>
33f80010:	e59f0018 	ldr	r0, [pc, #24]	; 33f80030 <SMRDATA+0x4>
33f80014:	e59f0018 	ldr	r0, [pc, #24]	; 33f80034 <SMRDATA+0x8>
33f80018:	e59f0018 	ldr	r0, [pc, #24]	; 33f80038 <SMRDATA+0xc>
33f8001c:	e59f0018 	ldr	r0, [pc, #24]	; 33f8003c <SMRDATA+0x10>

33f80020 <close_watch_dog>:
33f80020:	e3a01000 	mov	r1, #0
33f80024:	e5801000 	str	r1, [r0]
33f80028:	e1a0f00e 	mov	pc, lr

33f8002c <SMRDATA>:
33f8002c:	22111120 	andscs	r1, r1, #8
33f80030:	12345678 	eorsne	r5, r4, #125829120	; 0x7800000
33f80034:	33f8002c 	mvnscc	r0, #44	; 0x2c
33f80038:	33f80064 	mvnscc	r0, #100	; 0x64
33f8003c:	33f800a0 	mvnscc	r0, #160	; 0xa0

33f80040 <abc>:
33f80040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
33f80044:	e28db000 	add	fp, sp, #0
33f80048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
33f8004c:	e3a02002 	mov	r2, #2
33f80050:	e5832000 	str	r2, [r3]
33f80054:	e28bd000 	add	sp, fp, #0
33f80058:	e8bd0800 	pop	{fp}
33f8005c:	e12fff1e 	bx	lr
33f80060:	33f800a0 	mvnscc	r0, #160	; 0xa0

33f80064 <main>:
33f80064:	e92d4800 	push	{fp, lr}
33f80068:	e28db004 	add	fp, sp, #4
33f8006c:	e24dd008 	sub	sp, sp, #8
33f80070:	e59f3024 	ldr	r3, [pc, #36]	; 33f8009c <main+0x38>
33f80074:	e3a02001 	mov	r2, #1
33f80078:	e5832000 	str	r2, [r3]
33f8007c:	e3a03001 	mov	r3, #1
33f80080:	e50b3008 	str	r3, [fp, #-8]
33f80084:	ebffffed 	bl	33f80040 <abc>
33f80088:	e3a03000 	mov	r3, #0
33f8008c:	e1a00003 	mov	r0, r3
33f80090:	e24bd004 	sub	sp, fp, #4
33f80094:	e8bd4800 	pop	{fp, lr}
33f80098:	e12fff1e 	bx	lr
33f8009c:	33f800a0 	mvnscc	r0, #160	; 0xa0

Disassembly of section .bss:

33f800a0 <a>:
33f800a0:	00000000 	andeq	r0, r0, r0

一、B BL指令

    bl close_watch_dog

    33f80000:eb000006bl33f80020

    b 是相对跳转:PC + 偏移值 (PC值等于当前地址+8)

    偏移值:机器码 0xeb000006 低 24位 0x000006 按符号为扩展为 32 位 0x00000006 正数,向后跳转 0x6 个 4字节 也就是 0x1c

    1、加载地址0:0 + 8 + 0x1c = 0x20 正确跳转

    2、加载地址0x3ff80000:0x3ff80000 + 8 + 0x1c = 0x33f80020 正确跳转

    bl _start

    33f80004:ebfffffdbl33f80000 <_start>

    偏移值:机器码 0xebfffffd 低 24位 fffffd 按符号位扩展为 32 位 0xfffffffd 负数(-3),向前跳转 0x3 个 4字节 也就是 0xc 

    1、加载地址0:4 + 8 - 0xc = 0 正确跳转

    2、加载地址0x3ff80000:0x3ff80004 + 8 + 0xc = 0x33f80000 正确跳转

    通过以上分析,我们知道B是相对跳转,位置无关码,也可以知道为什么32为arm指令集,B的范围为正负32M了,24位去掉1位符号位,恰好等于32M。


二、ADR

    adr r0, close_watch_dog     /* 获取标号处的地址,位置无关 */

    33f80008: e28f0010addr0,  pc,  #16  

    1、加载地址0:0 + 8 + 16 = 0x20 正确

    2、加载地址0x3ff80000:0x3ff80008 + 8 + 16 = 0x33f80020 正确

    adr 获取的是标号处的“实际”地址,标号在哪就是哪个地址,跟位置无关,总能获得想要的值。


三、LDR

    ldr r0, SMRDATA       /* 获取标号处的值,位置无关 */

    33f8000c:e59f0018ldrr0, [pc, #24]; 33f8002c <SMRDATA>

    1、加载地址0:r0 = c + 8 + 24 = 0x2c 处的值 0x22111120 正确

    2、加载地址0x3ff80000:r0 = 0x3ff8000c + 8 + 24 = 0x33f8002c处的值 0x22111120 正确

    

    ldr r0, =0x12345678   /* 常数赋值,位置无关 */

    33f80010: e59f0018ldrr0, [pc, #24];  33f80030 <SMRDATA+0x4>

    1、加载地址0:r0 = 0x10 + 8 + 24 = 0x30 处的值 0x12345678 正确

    2、加载地址0x3ff80000:r0 = 0x3ff80010 + 8 + 24 = 0x33f80030处的值 0x12345678 正确


    ldr r0, =SMRDATA            /* 获取标号地址,位置有关 */

    33f80014: e59f0018ldrr0,  [pc, #24];  33f80034 <SMRDATA+0x8>

    1、加载地址0:r0 = 0x14 + 8 + 24 = 0x34 处的值 33f8002c 与标号实际地址(2c)不符合,不正确

    2、加载地址0x3ff80000:r0 = 0x3ff80014 + 8 + 24 = 0x33f80034 处的值 33f8002c 正确


    ldr r0, =main/* 获取函数名的地址,位置有关 */
    ldr r0 ,=__bss_start/* 获取链接脚本里标号的地址,位置有关 */

    这俩和 ldr r0, =SMRDATA 一致,位置有关,在0地址处运行不正确。


四、C函数

    1、全局变量

00000040 <abc>:
00000040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
00000044:	e28db000 	add	fp, sp, #0
00000048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
0000004c:	e3a02002 	mov	r2, #2
00000050:	e5832000 	str	r2, [r3]
00000054:	e28bd000 	add	sp, fp, #0
00000058:	e8bd0800 	pop	{fp}
0000005c:	e12fff1e 	bx	lr
00000060:	33f800a0 	mvnscc	r0, #160	; 0xa0
000000a0 <a>:
000000a0:	00000000 	andeq	r0, r0, r0

33f80040 <abc>:
33f80040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
33f80044:	e28db000 	add	fp, sp, #0
33f80048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
33f8004c:	e3a02002 	mov	r2, #2
33f80050:	e5832000 	str	r2, [r3]
33f80054:	e28bd000 	add	sp, fp, #0
33f80058:	e8bd0800 	pop	{fp}
33f8005c:	e12fff1e 	bx	lr
33f80060:	33f800a0 	mvnscc	r0, #160	; 0xa0

33f800a0 <a>:
33f800a0:	00000000 	andeq	r0, r0, r0

    r3 为全局变量 a 的地址,a 是存放在 0起始的地址还是0x33f80000起始的地址,它都认为 a 的地址是 0x33f800a0 。因此,C函数中调用全局变量是位置有关码。


    2、函数调用

    33f80084:ebffffedbl33f80040 <abc>

    由于 main 函数和 abc 函数挨得比较近,在32M范围之内,因此被翻译成了一条 bl 指令,那么与位置无关。

    如果,调用的函数比较远,大于32M的话,我认为是与位置有关系的,这个不再验证了。


    3、局部变量

    局部变量在函数刚开始的地方被压入栈,赋值语句被翻译成:

    33f8007c: e3a03001movr3, #1
    33f80080: e50b3008 strr3, [fp, #-8]

    位置无关。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值