c程序的编译和链接

使用gcc汇编器编译c语言程序时通常会经历四个阶段,即预处理阶段、编译阶段、汇编阶段、链接阶段,如下图。

例如:

gcc -o hello hello.c 生成可执行文件hello

gcc -S -o hello.s hello.c

gcc -c -o hello.o hello.c

嵌入式汇编(内联汇编)

1、格式

asm("汇编语句"

:输出寄存器

:输入寄存器

:会被修改的寄存器)

asm是内联汇编语句的关键词;输出寄存器与输入寄存器都分别对应着一个C变量或常数值,会被修改的寄存器表示你已经对于列出的寄存器中的值进行了修改,gcc编译器不能再依赖于它原先对这些寄存器加载的值。如果必要的话,gcc需要重新加载这些寄存器,因此需要把那些没有在输入输出寄存器部分列出,但是在汇编语句中明确用到或者隐含用到的寄存器名列在这个部分中。

如以下代码的seg代表一指定的内存段值,而addr表示一内存偏移地址量;且该宏函数的作用就是从指定的段和段偏移的内存地址处取出一个字节

#define get_seg_byte(seg,  addr) \
({ \
register char __res; \
__asm__("push %%fs; //保存fs寄存器的原值
mov %%ax, %%fs; //将seg设置到fs
movb %%fs:%, %%a1; //将seg:addr处的一个字节放置到a1寄存器中
pop %%fs "\
: "=a" (__res) \ //输出寄存器列表
:"" (seg), "m"(*(addr)));\ //输入寄存器列表 __res;})

嵌入式汇编程序规定把输出和输入寄存器统一按顺序编号,顺序从输出寄存器序列从左到右从上到下,以%0开始,分别记为%0, %1, ... %9。

输入语句中“0”加载符表示与输出行相同位置相同寄存器

代码 说明 代码 说明
a 使用寄存器eax m 使用内存地址
b 使用寄存器ebx o 使用内存地址并可以加偏移值
c 使用寄存器ecx I 使用常数0-31
d 使用寄存器edx J 使用常数0-63
S 使用寄存器esi K 使用常数0-255
D 使用寄存器edi L 使用常数0-65535
q

使用动态分配字节可寻址寄存器

(eax、ebx、ecx、edx)

M 使用常数0-3
r 使用任意动态分配寄存器 N 使用1字节常数(0-255)
g

使用通用的有效地址即可

(eax、ebx、ecx、edx或内存变量)

O 使用常数0-31
A 使用eax与edx联合(64位) = 输出操作数,输出值将替换当前值
+ 表示操作数可读可写    & 早期会变操作数,表示在使用完操作数之前,内容会被修改

指令leal用于计算有效地址,leal (r1, r2, 4), r3语句表示r1+r2*4 -> r3

在代码执行时,若不希望汇编语句被gcc优化而修改,就需要在asm符号后面添加关键字volatile。其格式 asm valatile (......);  __asm__ __valatile__(......)

关键字valatile 也可以放在函数名前来修饰函数,用来通知gcc该函数不会返回,从而避免gcc对于不返回函数的告警

volatile  void do_exit(long code);

static inline volatile void oom(void)
{
printf("out of memory\n");
do_exit(SIGSEGV);
}

圆括号中的组合语句 ({.......}),且最后一个语句代表表达式的值,若最后一条语句不是表达式,则表示该组合语句具有void类型;实际使用中通常都是用宏来实现

寄存器变量分为全局全局寄存器变量和局部寄存器变量

register int res __asm__("ax")

c与汇编程序相互调用

1、c函数调用机制

Linux内核程序boot/head.s执行完基本初始化后,就会跳转到去执行init/main.c程序,那么head.s程序是如何把执行控制权交给main.c的?

c函数的调用机制和控制权转移

函数调用包括从一块代码到另一块代码之间的双向数据传递和执行控制权转移;数据传递通过函数参数和返回值来进行,另外还包括局部变量的申请与释放。数据传递与局部变量 存储空间的回收和分配都是通过栈操作来实现的。

2、栈帧结构和控制权转移方式

栈被用来传递函数参数、存储返回信息、临时保存寄存器原有值以备恢复、存储局部变量。单个函数调用操作所使用的栈部分被称为栈帧(stack frame)结构。帧结构的两端由两个指针来指定,寄存器ebp通常用作帧指针,而esp则用作栈指针,在函数执行的过程中,由于栈指针会随着数据的入栈与出栈而移动,故函数中对大部分数据的访问都是基于帧指针而进行。

指令CALL和RET用于处理函数调用和返回操作,调用指令CALL的作用是把返回地址压入栈中,并且跳转到被调用函数开始处执行。返回地址是程序中紧随调用指令CALL后面一条指令的地址,因此当调用函数返回时,就会从该位置继续执行。

返回指定RET用于弹出栈顶处的地址并跳转到该地址处,使用该指令前,应该先正确处理栈中内容,使得当前栈指针所指位置内容正是先前CALL指令保存的返回地址

3、Intel CPU采用了所有函数必须遵循的寄存器用法统一惯例:

寄存器eax,ecx,edx的内容必须由调用者自己负责保存,也就是被调用函数B可以不管调用函数A是否使用而随意使用

寄存器ebx,esi,edi,ebp,esp的内容必须由被调用者来保护,当被调用者B需要使用此中某寄存器时,需要先将寄存器中的当前值保存到栈中,并在退出时恢复其内容

汇编调用c程序

linux0.11目标文件格式

System.map文件

1、当运行GNU链接器gld(ld)时,若使用了“-M”,或者使用nm命令,则会在标准输出设备(通常是屏幕)上打印出链接映像(link map)信息,即是指由链接程序产生的目标程序内存地址映像信息,其中列出了程序装入到内存中的位置信息。具体来讲有如下信息:

  • 目标文件及符号映射到内存中的位置
  • 公共符号如何放置
  • 链接中包含的所有文件成员以及引用的符号

2、通常我们会把送到标准输出设备的链接映像信息重定向到一个文件中(例如system.map)。符号表是所有内核的符号及其对应地址的一个列表(包括上文提到的end,etext, edata等符号的地址信息)。

3、每次内核编译都会产生一个新的对应system.map文件,当内核运行出错时,通过system.map文件中的符号表解析,就可以查到一个地址值对应的变量名,或反之。

4、符号表示例

第一列表示符号值(地址);第二栏是符号类型,指明符号位于目标文件的哪个区或其属性;第三栏是对应的符号名称。

符号类型    名称                       说明
A Absolute 符号值是绝对值,并且在进一步链接过程中不会改变
B BSS 符号在bss区中,即bss段
C Common

符号是公共的,公共符号是未初始化数据,在链接时,多个公共符号可能具有同一名称。

如果该符号定义在其他地方,则公共符号被看作是未定义的引用

D Data 符号在已初始化的数据中
G Global

符号是在小对象已初始化数据区中的符号。某些目标文件的格式允许对小数据对象(例如一个

全局整型变量)可进行更有效的访问

I Indirect 符号是对另一个符号的间接引用
N Debugging 符号是一个调试符号
R readonly 符号在一个只读数据区中
 S  Small  符号是小对象未初始化数据区中的符号
 T  Text  符号是代码区中的符号
 U  Undefined  符号是外部符号,并且值为0(未定义)
 -  stabs  符号是a.out目标文件中的一个stab符号,用于保存调试信息
 ?  unknown  符号的类型未知,或者是与具体文件格式有关

Linux内核完全注释之编程语言和环境(二)的更多相关文章

  1. Linux内核完全注释之编程语言和环境(一)

    as86汇编器 1.来源与对于linux的用途 as86来源minix-386开发的intel 8086.80386汇编编译程序和链接程序,他主要为linux创建16位的启动引导扇区程序boot/bo ...

  2. 赵炯博士《Linux内核完全注释》

    赵炯:男,1963年10月5日出生,江苏苏州人,汉族. 同济大学机械工程学院机械电子教研室副教授,从事教学和科研工作. 现在主要为硕士和博士研究生开设<计算机通信技术>.<计算机控制 ...

  3. linux内核参数注释与优化

    目录 1.linux内核参数注释 2.两种修改内核参数方法 3.内核优化参数生产配置 参数解释由网络上收集整理,常用优化参数对比了网上多个实际应用进行表格化整理,使查看更直观. 学习linux也有不少 ...

  4. (转)linux内核参数注释与优化

    linux内核参数注释与优化 原文:http://blog.51cto.com/yangrong/1321594 http://oldboy.blog.51.cto.com/2561410/13364 ...

  5. 读《linux内核完全注释》的FAQ

    以下只是个人看了<linux内核完全注释>的一点理解,如果有错误,欢迎指正! 1 eip中保存的地址是逻辑地址.线性地址还是物理地址? 这个应该要分情况.eip保存的是下一条要执行的指令地 ...

  6. LINUX内核完全注释

    学习教材:LINUX内核完全注释,内核版本0.11,修正版V3.0 赵炯编著 参考教材:UNIX操作系统设计--M. J. Bach, programming the 80x86  --John H. ...

  7. linux内核代码注释 赵炯 第三章引导启动程序

    linux内核代码注释 第三章引导启动程序 boot目录中的三个汇编代码文件   bootsect.s和setup.s采用近似intel的汇编语法,需要8086汇编器连接器as86和ld86 head ...

  8. 2017-2018-1 《Linux内核原理与设计》第十二周作业

    <linux内核原理与设计>第十二周作业 Sql注入基础原理介绍 分组: 和20179215袁琳完成实验 一.实验说明   SQL注入攻击通过构建特殊的输入作为参数传入Web应用程序,而这 ...

  9. 2019-2020-1 20199329《Linux内核原理与分析》第十二周作业

    <Linux内核原理与分析>第十二周作业 一.本周内容概述: 通过编程理解 Set-UID 的运行机制与安全问题 完成实验楼上的<SET-UID程序漏洞实验> 二.本周学习内容 ...

随机推荐

  1. IOS中微博正文开发步骤总结

    微博正文开发步骤总结 1.新建正文控制器,在点击首页的某一条微博时跳转过去 2.在MainController中设置导航控制器的代理,监听所有导航控制器的跳转 1> 如果即将显示的不是根控制器 ...

  2. 07day2

    居然是动规专场.这样不好吧?   采药 [问题描述] 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出了一个难题.医师把他带到 ...

  3. 正则表达式 java版

    众所周知,在程序开发中,难免会遇到需要匹配.查找.替换.判断字符串的情况发生,而这些情况有时又比较复杂,如果用纯编码方式解决,往往会浪费程序员的时间及精力.因此,学习及使用正则表达式,便成了解决这一矛 ...

  4. aspose.word使用简单方法

    概念介绍 使用aspose生成word报表步骤: 加载word模板 提供数据源 填充 加载模板 提供了4种重载方法 public Document(); public Document(Stream ...

  5. javamail模拟邮箱功能发送电子邮件-中级实战篇【新增附件发送方法】(javamail API电子邮件实例)

    引言: JavaMail jar包下载地址:http://java.sun.com/products/javamail/downloads/index.html 此篇是紧随上篇文章而封装出来的,阅读本 ...

  6. Hbase常用命令

    转:http://lib.csdn.net/article/hadoop/33499

  7. Eclipse无法识别小米2S手机

    某日,发现小米2S手机调试程序,发现Eclipse识别不出该硬件设备. 最后,确认小米2S系统升级后,会把开发者选项-USB调试选项默认关闭,打开即可. ----------------------补 ...

  8. html:唤起手机qq开始对话 & 自动拨号

    <a href="mqqwpa://im/chat?chat_type=wpa&uin=[qq号]&version=1">XXX</a> 另 ...

  9. codeforces 340B Maximal Area Quadrilateral(叉积)

    事实再一次证明:本小菜在计算几何上就是个渣= = 题意:平面上n个点(n<=300),问任意四个点组成的四边形(保证四条边不相交)的最大面积是多少. 分析: 1.第一思路是枚举四个点,以O(n4 ...

  10. 点击图片名,让图片在pictureBox中显示 z

    public string filepath; public Form1() { InitializeComponent(); } private void button1_Click(object ...