代码改变世界

简单的main方法调用一个加减法函数背后的细节

2019-10-13 21:30  chen.simon  阅读(663)  评论(0编辑  收藏  举报

测试程序

/*
 * AddTest.c
 *
 *  Created on: 2019年10月13日
 *      Author: appweb
 */
#include <stdio.h>

int add(int a, int b) {
	int c = addAgain(a, b);
	return c;
}

int addAgain(int a, int b) {
	int c = a + b;
	return c;
}

int sub(int a, int b) {
	int c = a - b;
	return c;
}

int main() {
	int s = add(5, 3);
	int d = sub(5, 3);
	return 0;
}

makefile

PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

OBJS = AddTest.o

# 如果在命令行直接执行make 需要export BUILD_MODE=debug 或者run
ifeq ($(BUILD_MODE),debug)
	CFLAGS += -g
else ifeq ($(BUILD_MODE),run)
	CFLAGS += -O2
else
	$(error Build mode $(BUILD_MODE) not supported by this Makefile)
endif

all:	InvokeFunction

# $@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
# $^ 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
# $< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
# $? 所有比目标新的依赖目标的集合。以空格分隔。

# 输出变量可以使用如下办法
$(info $$OBJS is [${OBJS}])
$(info $$(CXX) is [$(CXX)])
$(info $$(PROJECT_ROOT) is [$(PROJECT_ROOT)])

AddTest:	$(OBJS)
	$(CXX) -o $@ $^

%.o:	$(PROJECT_ROOT)src/%.c
	$(CXX) -c $(CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -o $@ $<


clean:
	rm -fr AddTest $(OBJS)

一如既往的使用CDT,寄存器监视(我需要观察的几个)、内存监视 、反编译的汇编指令窗口弄在一起,调试起来真是方便
打包好的工程

先编译下然后开始调试并观察


开始调试



添加寄存器监视


添加内存监视


启用指令单步调试


rsp栈顶rbp栈底,大小及栈分配的生长方向如下:

                     栈分配生长方向
     <-------------------------------------+
                      rsp 栈顶           rbp 栈底
     +------------------+------------------+
     |                  |                  |
     |                  |                  |
     +------------------+------------------+
内存低位地址                                 内存高位地址

现在rsp是0xfffffffd750,执行完下图的

00000000000004195598:   mov     %rsp,%rbp
00000000000004195601:   sub     $0x10,%rsp 

这两条汇编指令后,rbp是0xfffffffd750,rsp是0xfffffffd740,这就是 函数栈帧空间分配





在执行完callq与push指令之后,栈顶再往前的内存(就是紧靠着栈顶比栈顶还小的内存)会发生变化,看图中内存监视器红色部分,按有些书上说法此处是保存了rip和rbp,但是我没能太理解,看数值不怎么能对上

注意看 进入addAagin方法后,并不是像某些书上说的会分配函数栈帧控空间,我猜测是编译器做了优化吧,因为addAagin方法不再调用其他方法了。
看了R大的文章,这种函数应该是 叶函数,叶函数是不调用别的函数的函数。


00000000000004195547:   mov     %edi,-0x14(%rbp)
00000000000004195550:   mov     %esi,-0x18(%rbp)

看上图,从edi和esi集群器向 相对于rbp(栈底)偏移(负偏移)的内存传数据,这个称之为压栈
不过有些书上讲的是 向 相对rsp(栈顶)偏移(正偏移)的内存传数据,我理解成一个意思,不过是内存定位的方式不同罢了