01【QT】QT程序官网下载与安装与编译
Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。Qt是一个跨平台的C++图形用户界面应用程序框架,面向所有具有形同代码库的嵌入式、做面和移动平台。文中介绍了一项目示例,可以编译运行。02Lua循环与迭代器函数
Lua 语言提供了以下几种循环处理方式:
while 循环:在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true
for 循环:重复执行指定语句,重复次数可在 for 语句中控制。
repeat...until :重复执行循环,直到 指定的条件为真时为止
循环嵌套:可以在循环内嵌套一个或多个循环语句(while do ... end;for ... do ... end;repeat ... until;)
03Lua的rawset和rawget浅析
raw:原始的,未加工的。
rawset/rawget:对“原始的”表进行直接的赋值/取值操作。
所以,raw方法就是忽略table对应的metatable,绕过metatable的行为约束,强制对原始表进行一次原始的操作,也就是一次不考虑元表的简单更新。另外,一次原始的操作其实并不会加速代码执行的速度,效率一样。

04gcc 编译参数 -fPIC 的详解和一些问题
-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
05C语言函数调用栈(一)
程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call
stack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。
不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。
06C语言函数调用栈(二)
程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call
stack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。
不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。

07X86-64寄存器和栈帧
说到x86-64,总不免要说说AMD的牛逼,x86-64是x86系列中集大成者,继承了向后兼容的优良传统,最早由AMD公司提出,代号AMD64;正是由于能向后兼容,AMD公司打了一场漂亮翻身战。导致Intel不得不转而生产兼容AMD64的CPU。这是IT行业以弱胜强的经典战役。不过,大家为了名称延续性,更习惯称这种系统结构为x86-64。
X86-64在向后兼容的同时,更主要的是注入了全新的特性,特别的:x86-64有两种工作模式,32位OS既可以跑在传统模式中,把CPU当成i386来用;又可以跑在64位的兼容模式中,更加神奇的是,可以在32位的OS上跑64位的应用程序。有这种好事,用户肯定买账啦。
值得一提的是,X86-64开创了编译器的新纪元,在之前的时代里,Intel CPU的晶体管数量一直以摩尔定律在指数发展,各种新奇功能层出不穷,比如:条件数据传送指令cmovg,SSE指令等。但是GCC只能保守地假设目标机器的CPU是1985年的i386,额。。。这样编译出来的代码效率可想而知,虽然GCC额外提供了大量优化选项,但是这对应用程序开发者提出了很高的要求,会者寥寥。X86-64的出现,给GCC提供了一个绝好的机会,在新的x86-64机器上,放弃保守的假设,进而充分利用x86-64的各种特性,比如:在过程调用中,通过寄存器来传递参数,而不是传统的堆栈。又如:尽量使用条件传送指令,而不是控制跳转指令。
08Linux系统下x86和ARM的区别有哪些?
由于定位的不同,ARM处理器基于精简指令集(RISC)架构。指令集数量少就可以简化硬件逻辑的设计,减少晶体管数量,也就意味着低功耗。而且由于移动平台应用通常简单,程序的控制流不复杂,执行效率没有必要很高,所以流水线、分支预测等硬件逻辑都比较简单。这些都降低了晶体管总量。同时因为移动设备有电池的能源限制,ARM的电源管理是作为重要部分特别设计了的。比如移动设备的处理器在待机时通常只以极低的主频在运行,甚至可以暂时关闭闲置的核心、协处理器来降低功耗。
x86就截然不同。x86是复杂指令集(CISC)架构,存在很多机器指令,只为了高效地完成一项专门任务(比如MMX,
SSE中的指令)。这就使得硬件的逻辑很复杂,晶体管数量庞大。为了高效地进行运算,x86架构有较长的流水线以达到指令级并行(ILP)。长流水线带来的一个弊端,就是当遇到分支时,如果预载入分支指令不是未来真实的分支,那么要清空整个流水,代价较高。所以x86为此还必须有复杂的分支预测机构,确保流水线的效率。再加上多级cache,支持超线程、虚拟化等等,x86的复杂度其实相当高。
09gcc -D选项 编译时添加宏定义
使用 gcc -D选项 可以在编译时添加宏定义选择性的编译代码
例:gcc debugtest.c -o debugtest.exe -D DEBUG
即在编译时加入 DEBUG 宏

10gcc的编译参数-fPIC
non-PIC 与 PIC 在代码的区别主要在于 access global data, jump label 的不同。
比如一条 access global data 的指令,
non-PIC 的形式是:ld r3,var1
PIC 的形式则是:ld r3,var1-offset@GOT
意思是从 GOT 表的 index 为 var1-offset的地方处指示的地址处装载一个值,即var1-offset@GOT处的4个 byte 其实就是 var1的地址。这个地址只有在运行的时候才知道,是由 dynamic-loader(ld-linux.so)填进去的。
再比如 jump label 指令
non-PIC 的形式是:jump printf ,意思是调用 printf。
PIC 的形式则是:jump printf-offset@GOT,
意思是跳到 GOT 表的 index 为 printf-offset 的地方处指示的地址去执行,
这个地址处的代码摆放在 .plt section,
每个外部函数对应一段这样的代码,其功能是呼叫dynamic-loader(ld-linux.so)来查找函数的地址(本例中是
printf),然后将其地址写到 GOT 表的 index 为 printf-offset的地方,同时执行这个函数。这样,第2次呼叫 printf 的时候,就会直接跳到 printf 的地址,而不必再查找了。
11AT&T与Intel汇编语言的比较
在Intel的语法中,寄存器和和立即数都没有前缀。但是在AT&T中,寄存器前冠以“%”,而立即数前冠以“$”。在Intel的语法中,十六进制和二进制立即数后缀分别冠以“h”和“b”,而在AT&T中,十六进制立即数前冠以“0x”。
1、直接跳转:跳转目标是作为指令封一部分编码。 如:jmp 0x2358, 直接跳转是跳转到这个地址执行指令,是根据下一条指令位置作的加减法。
objdump -d xxxx 里面看到的汇编指令(如下图),虽然显示是目标指令的地址,但看机器码会发现,这个值实际上是一个偏移值。
405678-405657=30 (1、这个是16进制的计算,不是10进制的。2、需要用405657即下一条指令的地址作为基准作偏移,实际上是加上了本身这条指令占用的字节数)
jmp的机器码有三个:e9、eb、ff (ff 是用于绝对跳转的,就是跳转到这个地址执行;e9和eb是相对跳转的,就是拿下一条指令的地址加上后面的偏移量)
2、间接跳转:目标是从寄存器或存储器位置读出来的。如:jmp *%eax间接跳转字节码基本都是ff,也是绝对跳转。就是跳转到这个地址。这个区别于e9和eb是相对地址。
3、e9和ff 是扩展到64位的
12函数调用-汇编分析
汇编函数调用过程
1、压栈: 函数参数压栈,返回地址压栈
2、跳转: 跳转到函数所在代码处执行
3、执行: 执行函数代码
4、返回: 平衡堆栈,找出之前的返回地址,跳转回之前的调用点之后,完成函数调用
call:把call指令的下一条指令地址作为本次函数调用的返回地址压栈,然后使用jmp指令修改指令指针寄存器EIP,使cpu执行被调用函数的指令代码。
ret:它表示取出当前栈顶值,作为返回地址,并将指令指针寄存器EIP修改为该值,实现函数返回。
ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

点击排行

站长推荐

猜你喜欢
站点信息
- 建站时间:2016-04-01
- 文章统计:728条
- 文章评论:82条
- QQ群二维码:扫描二维码,互相交流
