调用约定

留下评论

__cdecl
    对于C C++程序而言,这是在函数调用时默认的参数传递约定。因为函数调用者负责清理堆栈,所以它很方便实现
    可变参数的函数。但是这种方式有一个缺点就是产生的可执行文件比__stdcall更大,因为它在函数调用结束时需要
    更多的指令去做清理工作。
__stdcall
    用于Win32 API函数的调用。被调用的函数负责清理堆栈。语法规则如下:
    return-type __stdcall function-name[(argument-list)]
__fastcall
    它要求在函数调用时参数尽量被存储到寄存器中传递。

__fastcall

__stdcall

__cdecl

Argument-passing order

The first two DWORD or smaller arguments are passed in ECX and EDX registers; all other arguments are passed right to left.

Right to left.

Right to left

Stack-maintenance responsibilit

Called function pops the arguments from the stack.

Called function pops its own arguments from the stack.

Calling function pops the arguments from the stack

Name-decoration convention

At sign (@) is prefixed to names; an at sign followed by the number of bytes (in decimal) in the parameter list is suffixed to names

An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list. Therefore, the function declared as int func( int a, double b ) is decorated as follows: _func@12

Underscore character (_) is prefixed to names

Case-translation convention

No case translation performed.

None

No case translation performed

栈指针ESP操作

留下评论

PUSH操作
     虽然80X86支持16位的栈压入操作,这主要是为了程序能够在16位环境下执行,例如DOS。但是为了获取最佳性能,栈指针最好是
4的偶数倍。事实上,如果ESP存储非4的偶数倍地址时,运行在Windows或者Linux下的程序可能会表现得很诡异。出现这种情况的唯
一实际原因就是你通过两次连续的单字压入来创建一个双字。
POP操作
    POP的传入参数不能是一个常量。
    POP操作后,弹出的数据不会从内存中删除,它只是调整栈指针的位置。但是我们也不能依赖这一点,因为这些数据可能会被其他
    PUSH操作所破坏。
PUSH和POP操作常用于保存寄存器的值当计算需要临时值时。

移出栈上的数据但是不使用POP操作
push(eax);
push(ebx);
<<Some code that winds up computing some values we want to keep into
     EAX and EBX>>
if (Calculation_was_performed) then
    // Whoops, we don’t want to pop EAX and EBX!
    // What to do here?
    add(8, ESP);
else
    // No calculation, so restore EAX, EBX.
    pop(ebx);
    pop(eax);
endif;

访问栈上数据但是不使用POP操作
[ESP+offset]
push(eax);
push(ebx);
<<Some code that winds up computing some values we want to keep into
     EAX and EBX>>
if (Calculation_was_performed) then
    // Overwrite saved values on stack with new EAX/EBX values.
    // So the pops that follow won’t change the values in EAX/EBX.
    mov(eax, [ESP + 4]);
    mov(ebx, [ESP]);
else
    // No calculation, so restore EAX, EBX.
    pop(ebx);
    pop(eax);
endif;

INC DEC与ADD SUB之间的区别是前者不会影响EFLAG寄存器中的进位标识位。

寄存器简介

留下评论

80X86CPU的寄存器可以分为四类:
通用寄存器-- EAX EBX ECX EDX ESI EDI EBP ESP
    E代表extend,该前缀将32位寄存器于16位寄存器区分开来:AX BX CX DX SI DI BP SP
    还有8个8位寄存器:AL AH BL BH CL CH DL DH
    实际上并没有24个寄存器,只是将8位寄存器重叠于16位之上,16位又重叠于32位之上。
特殊目的寄存器--
段寄存器--
特殊目的核心模式寄存器--

IA-32指令码由四个主要部分构成:可选的指令前缀(0-4字节),操作码(1-3字节),可选的修饰符(
ModR/M(0-1字节) SIB(0-1字节) 移位(0-4字节)),可选的数据元素(0-4字节)。
例如2字节的操作码OF A2定义CPUID指令。当CPU执行这个指令码时,它返回不同寄存器中关于微处理器的特定信息。

指令前缀按照功能可以划分为4个组:
锁定前缀和重复前缀 — 指令将独占使用共享内存区域。重复前缀通常在处理字符串时使用。
段覆盖前缀和分支提示前缀 — 段覆盖前缀定义可以覆盖定义了的段寄存器值的指令。
    分支提示前缀尝试向处理器提供程序在条件跳转语句中最可能的路径的线索。
操作数长度覆盖前缀 — 操作数长度覆盖前缀通知处理器,程序将在这个操作码之内切换16位和32位的操作数长度。
    这使程序可以在使用大长度的操作数时警告处理器,帮助加快对寄存器的数据赋值。
地址长度覆盖前缀 — 地址长度覆盖前缀通知处理器,程序将切换16位和32位的内存地址。这两种长度都可以被声明为
    程序的默认长度,这个前缀通知处理器程序将切换到另一种长度。

ModR/M字节由3个字段的信息构成:r/m(0-2),reg/opcode(3-5),Mod(6-7)。
mod字段和r/m字段一起使用,用于定义指令中使用的寄存器或者寻址模式。在指令中,可能的寻址模式有24个,加上8个
可以使用的通用寄存器,所以有32个可能值。
reg/opcode可以进一步定义操作码功能,或者是定义寄存器值。
r/m用于定义用作该功能的操作数的另一个寄存器, 或者可以和modeR/M字段组合在一起定义指令的寻址模式。

SIB字节由3个字段的信息构成:比例(7-6位),索引(5-3位),基址(2-0位)。
比例字段指定操作的比例因子。索引字段指定内存访问中用作索引寄存器的寄存器。基址字段指定用作内存访问的基址
寄存器的寄存器。

地址移位字节:指定对于ModR/M和SIB字节中定义的内存位置的偏移量。可以使用它作为基本内存位置的索引,用于
存储或者访问内存之内的数据。

书籍列表

留下评论

Intel 汇编语言程序设计(第五版)
Intel微处理器(英文版,第七版)(经典原版书库)
IBM PC汇编语言程序设计(第5版)/大学计算机教育国外著名教材系列(影印版)
计算机组成及汇编语言原理(英文版)
微处理器与外设大学教程(第2版)(国外计算机科学经典教材)
80×86 IBM PC及兼容计算机(卷I和II)汇编语言、设计与接口技术(第4版)
计算理论导引(第2版)——计算机科学丛书
面向计算机科学的数理逻辑(第二版)(中国科学院研究生教学丛书)
计算机程序的构造和解释:原书第2版
算法概论(国外经典教材·计算机科学与技术)
小小世界:有序与无序之间的网络动力学
现代编译原理C语言描述
现代通信原理
具体数学计算机科学基础(英文版·第2版)

BFD 文件格式

留下评论

编译器的链接程序会调用BFD库来访问OBJ和压缩文件。BFD库提供了一套通用的前端接口函数来操作OBJ文件,使编译器不用考虑其所链接的OBJ文件具体采用什么格式。如果有一种新的OBJ文件,那么只要把其对应的操作函数添加到BFD库中就行了。尽管如此,为了节省运行时内存,链接器一般会被配置成只支持当前所有OBJ文件格式的一个子集。我们可以使用命令objdump -i命令来查看当前系统中配置支持的格式。

由于要考虑相互制约的需求,大多数BFD库的实现会选择一种折衷方案。影响BFD库设计的主要因素是效率:any time used converting
between formats is time which would not have been spent had BFD not been involved. This is partly offset by abstraction
playback; since BFD simplifies applications and back ends, more time and care may be spent optimizing algorithms for a
greater speed.

BFD库实现的一个小的瑕疵就是可能会导致信息丢失。有两个地方可能会发生这种事:转换和输出时。例如,a.out文件中位对齐信息。
另外一个例子是COFF中各节对应的名字。COFF文件对节的总数没有限制,并且每一节会附带一个名字。如果链接的目标类型不支持
这样的结构,那么这些节的名字可能会丢失,也可能这些节会被压缩成几节。
You can circumvent this problem by describing the desired input-to-output section mapping with the linker command
language.
当然,这种情况并不严重如果链接的输入和输出目标文件格式都一样的话。

当一个OBJ文件被打开时,BFD例程库会自动检测输入文件的格式,然后就会生成一个访问此种格式的函数指针,并采用对应的数据结
构来遍历文件中个各块数据。不同的数据块会表针不同种类的信息。例如,链接器通常会需要加载符号表。那么,把符号表从OBJ文件
中提取出来就是BFD的责任了。当程序链接完成需要把符号表写进输出文件时,函数指针会引用另一个BFD后端例程把生成的符号表转
换成相应的OBJ格式。

BFD header file version (GNU Binutils for Ubuntu) 2.18.93.20081009
elf64-x86-64
 (header little endian, data little endian)
  i386
elf32-i386
 (header little endian, data little endian)
  i386
a.out-i386-linux
 (header little endian, data little endian)
  i386
efi-app-ia32
 (header little endian, data little endian)
  i386
efi-bsdrv-ia32
 (header little endian, data little endian)
  i386
efi-rtdrv-ia32
 (header little endian, data little endian)
  i386
efi-app-x86_64
 (header little endian, data little endian)
  i386
efi-bsdrv-x86_64
 (header little endian, data little endian)
  i386
efi-rtdrv-x86_64
 (header little endian, data little endian)
  i386
elf64-little
 (header little endian, data little endian)
  i386
elf64-big
 (header big endian, data big endian)
  i386
elf32-little
 (header little endian, data little endian)
  i386
elf32-big
 (header big endian, data big endian)
  i386
srec
 (header endianness unknown, data endianness unknown)
  i386
symbolsrec
 (header endianness unknown, data endianness unknown)
  i386
tekhex
 (header endianness unknown, data endianness unknown)
  i386
binary
 (header endianness unknown, data endianness unknown)
  i386
ihex
 (header endianness unknown, data endianness unknown)
  i386

               elf64-x86-64 elf32-i386 a.out-i386-linux efi-app-ia32
          i386 elf64-x86-64 elf32-i386 a.out-i386-linux efi-app-ia32

               efi-bsdrv-ia32 efi-rtdrv-ia32 efi-app-x86_64 efi-bsdrv-x86_64
          i386 efi-bsdrv-ia32 efi-rtdrv-ia32 efi-app-x86_64 efi-bsdrv-x86_64

               efi-rtdrv-x86_64 elf64-little elf64-big elf32-little elf32-big
          i386 efi-rtdrv-x86_64 elf64-little elf64-big elf32-little elf32-big

               srec symbolsrec tekhex binary ihex
          i386 srec symbolsrec tekhex binary ihex

mpatrol LOG文件分析

留下评论

ERROR: [MISMAT]: free: 0x017CF2B1 does not match allocation of 0x017CF2B0
    0x017CF2B0 (16 bytes)
{malloc:29:0} [int main()|main.cpp|9]
malloc: 内存分配函数。其他可供调用的函数例如realloc, reallocf,recalloc,expand和xrealloc,
            那么此处会是对应函数的名字。
29:Allocation index. 意思是此处在程序运行时是第29次分配内存。
0:Reallocation index. 意思是此内存起始地址是调用0次realloc调整内存大小后的结果。
                                                        

mpatrol 菜鸟之路 -- 让Hello World腾飞!!!!

留下评论

整了半天,终于让使用mpatrol库的hello world运行起来了。

#include <stdio.h>
#include <stdlib.h>
#include "mpatrol.h"

int main()
{
    char *p = (char*)malloc(16);
        if (p)
    {
        free(p + 1);
    }
    return 0;
}

首先直接使用新立得安装mpatrol。关键是后面把如下代码粘贴上去用Code Block跑不起来。
以为是Code Block环境的问题,直接用gcc命令行编译,也通不过。查了一下,虽然没有解决问题,
但是,有了一点意外的收获:
g++跟gcc命令默认的行为原来还有差异:g++默认加载C++运行时库,而gcc默认加载C运行时库。
gcc可以通过参数设置也加载C++运行库。

其实,我也知道这个问题的实质应该是lib没有找到,所以导致
undefined reference __mp_alloc
undefined reference __mp_free
但是却不知道怎样添加lib引用。说也奇怪,mpatrol已经被添加到usr/include和usr/lib目录下了,为啥
编译器就不会自动在这些目录下找呢?再说,代码中引用的"mpatrol.h"文件还没报错呢!!

添加环境变量MPATROL_OPTIONS:
export MPATROL_OPTIONS=LOGALL
确认是否添加成功:
env|grep MPATROL_OPTIONS
如果有回显说明添加成功
要删除环境变量:
unset MPATROL_OPTIONS
很奇怪,这个地方,命令的名字不是一组反义词,有set命令,但不是添加环境变量的作用。

成功编译的命令如下:
gcc main.cpp -lbfd -lmpatrol
在Code Block的Project->Build Options->Link settings->other linker options中添加:
-lbfd
-lmpatrol
也可以成功生成mpatrol.log文件。

operating system:       UNIX
system variant:         Linux
processor architecture: Unknown
processor word size:    32-bit
object file format:     BFD
dynamic linker type:    SVR4

Log file generated on Sat Apr  4 22:22:46 2009

read 10 symbols from /home/tristan/WorkSpace/P1/a.out

FREE: free (0x0179A2B1) [int main()|main.cpp|12]

ERROR: [MISMAT]: free: 0x0179A2B1 does not match allocation of 0x0179A2B0
    0x0179A2B0 (16 bytes) {malloc:29:0} [int main()|main.cpp|9]

下面翻译一段mpatrol附带文档里的关于入门的一段:
如果mpatrol库在编译时选择不支持任何OBJ文件格式或者选择支持.out目标文件格式,
使用如下命令编译:
cc -I/usr/include <file> -L/usr/lib -lmpatrol
如果mpatrol库在编译是选择支持COFF或者XCOFF目标文件格式(非LynxOS系统):
cc -I/usr/include <file> -L/usr/lib -lmpatrol -lld
如果支持ELF32或者ELF64目标文件格式:
cc -I/usr/include <file> -L/usr/lib -lmpatrol -lelf
如果支持GNU BFD目标文件格式:
cc -I/usr/include <file> -L/usr/lib -lmpatrol -lbfd
如果支持GNU BFD目标文件格式和libunwind栈遍历库:
cc -I/usr/include <file> -L/usr/lib -lmpatrol -lbfd -liberty -lintl -lunwind
如果库在HP/UX机器上编译并且支持GNU BFD目标文件格式:
cc -I/usr/include <file> -L/usr/lib -lmpatrol -lbfd -liberty -lintl -lcl
这次经历的最主要的心得就是ubuntu要使用规则“如果支持GNU BFD目标文件格式”。