Make

Make基础

Make:根据指定的Makefile文件构建新文件

1
2
$ make [-f makefile] [<target>]
# 指定使用某文件中的规则,默认`makefile`/`Makefile`
  • make默认寻找当前目中GNUmakefile/makefile/Makefile 文件作为配置 文件
  • 默认用makefile中首个目标文件作为最终目标文件,否则 使用<target>作为目标文件

Make参数

  • -b/-m:忽略和其他版本make兼容性

  • -B/--always-make:总是更新/重编译所有目标

  • -C <dir>/--directory=<dir>:指定读取makefile的目录, 相当于$ cd <dir> && make

    • 指定多个-C <dir>,make将按次序合并为最终目录
    • -C时,-w选项自动打开
  • --debug[=<options>]:输出make调试信息

    • a:all,输出所有调试信息
    • b:basic,基本信息
    • v:verbose,基本信息之上
    • i:implicit,所有隐含规则
    • j:jobs,执行规则中命令的详细信息,如:PID、返回码
    • m:makefile,make读取、更新、执行makefile的信息
  • -d:等价于--debug=a

  • -e/--environment-overrides:环境变量覆盖makefile中值

  • -f <file>/--file=<file>/--makefile=<file>:指定 makefile

    • 可以多次传递参数-f <filename>,所有makefile合并 传递给make
  • -h/--help:帮助

  • -i/--ignore-errors:忽略执行时所有错误

  • -I <dir>/--include-dir=<dir>:搜索includemakefile 路径

    • 可以多个-I <dir>指定多个目录
  • -j [<jobsum>]/-jobs[=<jobsum>]:指定同时运行命令数

    • 进程数
    • 默认同时运行尽量多命令
    • 多个-j时最后者生效
  • -k/--keep-going:出错不停止运行

    • 若目标生成失败,依赖于其上目标不会继续执行
  • -l <load>/--load-average[=<load>]

  • --max-load[=<load>]:make命令负载

  • -n/--just-print/--dry-run/--recon:仅输出执行 过程中命令序列,不执行

  • -o <file>/--old-file=<file>/--assume-old=<file>: 不重新生成指定的<file>,即使目标依赖其

  • -p/--print-data-base:输出makefile中所有数据:规则、 变量等

    1
    2
    3
    4
    $ make -qp
    # 只想输出信息,不执行makefile
    $ make -p -f /dev/null
    # 查看执行makefile前的预设变量、规则
  • -q/--question:不执行命令、不输出,仅检查指定目标 是否需要更新

    • 0:需要更新
    • 2:有错误发生
  • -r/--no-builtin-rules:禁用make任何隐含规则

  • -R/--no-builtin-variables:禁用make任何作用于变量上 的隐含规则

  • -s/--silent/quiet:禁止显示所有命令输出

  • -S/--no-keep-going/--stop:取消-k作用

  • -t/--touch:修改目标日期为最新,即组织生成目标的命令 执行

  • -v/--version:版本

  • -w/--print-directory:输出运行makefile之前、之后信息

    • 对跟踪嵌套式调用make有用
  • --no-print-directory:禁止-w选项

  • -W <file>/--what-if=<file>/--new-file=<file>/--assume-file=<file>

    • 联合-n选项,输出目标更新时的运行动作
    • 没有-n,修改<file>为当前时间
  • --warn-undefined-variables:有未定义变量是输出警告信息

步骤

  • 读入所有makefile
  • 读入被include其他makefile
  • 初始化(展开)文件中变量、函数,计算条件表达式
  • 展开模式规则%、推导隐式规则、分析所并规则
  • 为所有目标文件创建依赖关系链
  • 根据依赖关系,决定需要重新生成的目标
  • 执行生成命令

相关环境变量

  • MAKEFILES:make会将此环境变量中的文件自动include

    • 不建议使用,会影响所有的make动作
    • 其中文件缺失不会报错
  • MAKEFLAGS:make命令行参数,自动作为make参数

Makefile基本语法

控制符号

  • #:注释

  • @:消除echoing,默认make会打印每条命令

  • -:忽略命令出错

  • 通配符同bash

    • *:任意字符
    • ?:单个字符
    • [...]:候选字符
    • ~:用户目录
      • ~:当前用户目录
      • ~xxx:用户xx目录
  • %:模式匹配

    1
    2
    %.o: %.c
    # 匹配所有c文件,目标为`.o`文件
  • $:引用、展开变量,执行函数

引用其他Makefile

1
include <filename>
  • <filename>可以是默认shell的文件模式,包含通配符、路径
  • include之前可以有空格,但是不能有<tab>(命令提示)
  • make寻找其他makefile,将其内容放当前位置

  • 若文件没有明确指明为绝对路径、相对路径,make会在以下目录 中寻找

    • -I--include-dir参数
    • /usr/local/bin/include/usr/include
  • make会include环境变量MAKEFILES中文件

    • 不建议使用环境变量MAKEFILES,会影响所有make
  • 文件未找到,make会生成一条警告信息,但继续载入其他文件, 完成makefile读取之后,make会重试寻找文件,失败报错

    • 可以使用-include/sinclude代替,忽略include过程 中的任何错误

Makefile显式规则

1
2
<target>: <prerequisite>
[tab]<commands>
  • <target>:目标
  • <prerequisites>:前置条件
  • <commands>:命令,和前置条件至少存在一个
1
2
a.txt: b.txt c.txt
cat b.txt c.txt > a.txt
  • makefile中规则是生成目标的规则

  • make自顶向下寻找可以用于生成目标的规则,生成最终目标类似 调用函数栈

    • 前置条件/依赖类似于被调用函数
    • 命令类似于函数体
    • 目标类似于函数返回值

Target

目标:make的目标

  • 目标通常是文件名,指明需要构建的对象
    • 文件名可以是多个,之间使用空格分隔
  • 不是文件名的目标称为伪目标,视为某种操作

多目标

多目标规则意义是多个目标共享规则依赖、声明命令,并 不是需要同时生成多个目标

  • 需要多目标中的任何一个时,多目标规则就会被应用,其中 命令被执行

  • 每次只生成单独目标的多目标规则,目标之间只是单纯的 可以合并简化规则中的命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    bigoutput littleoutput: text.g
    generate text.g -$(subst output,,$@) > $@

    # 等价于

    bigoutput: text.g
    generate text.g -big > bigoutput
    littleoutput: text.g
    generate text.g -little > littleoutput
  • 同时生成多个目标的多目标规则,多个目标应该满足 需要同时生成、不能单独修改,否则没有必要定义为多目标 ,当然这其实也是合并简化规则中的命令

    1
    2
    %.tab.c %.tab.h: %.y
    bison -d $<

Phony Target

todo

伪目标:目标是某个操作的名字,每次执行都会执行命令

1
2
3
4
.PHONY: clean
# 明确声明是*伪目标*,可省略
clean:
rm *.o
  • 若省略.PYHONY,要求当前目中不存在同名文件,否则make 认为目标已存在,不会执行命令

GNU规范

GNU推荐makefile中包含的伪目标、命名

  • all:编译所有目标
  • clean:删除所有被make创建的文件
  • install:安装已编译好程序,即将目标执行文件拷贝到指定 目标中
  • print:列出改变过的源文件
  • tar:打包源程序备份
  • dist:创建压缩文件
  • tags:更新所有目标,以备完整地编译使用
  • check/test:测试makefile流程

静态库

目标archive(member):指定静态库文件、及其组成

  • 这种定义目标的方法就是方便ar命令
1
2
3
4
5
6
7
8
9
10
11
12
13
foolib(hack.o kludge.o): hack.o kludge.o
ar cr foolib hack.o kludge.o

foolib(hack.o): hack.o
ar cr foolib hack.l kudge.o
foolib(kludge.o): kludge.o
ar cr foolib kludge.o

# 确实等价,但是这个看起来有点不对劲,只要传了任何一个
# 静态库的构成,就执行命令???

foolib(*.o): hack.o kludge.o
# 这样更好???

Prerequisites

前置条件/依赖:生成目标的依赖

  • 通常是一组空格分隔的文件名,为空则说明目标的生成和其他 文件无关

  • 指定目标是否重新构建的判断标准,只要有一个前置文件不存在 、有过更新(时间戳比较),目标就需要更新

  • 若前置文件不存在,则需要建立以其作为目标的规则用于生成, make target时会自动调用

1
2
source: file1 file2 file3
# 利用伪目标+前置条件,同时构建多个文件

Commands

命令:更新、构建文件的方法

  • 在linux下默认使用环境变量SHELL/bin/sh)执行命令,
  • 在MS-DOS下没有SHELL环境变量,会在PATH环境变量中寻找 ,并自动加上.exe.bat.sh等后缀

<tab>

每行命令前必须有一个<tab>,否则需要使用提前声明

1
2
3
.RECIPEPREFIX=>
all:
> echo Hello, world

Shell进程

每行命令在单独的shell进程中执行,其间没有继承关系 (即使是同一个规则中)

  • 多条命令可以使用;分隔

    1
    2
    var-kept:
    export foo=bar; echo "foo=[$$foo]"
  • 可类似python\换行

    1
    2
    3
    var-kept:
    export foo=bar; \
    echo "foo=[$$foo]"
  • 使用.ONESHELL命令

    1
    2
    3
    4
    .ONESHELL
    var-kept:
    export foo=bar
    echo "foo=[$$foo]"

嵌套执行Make

大工程中不同模块、功能源文件一般存放在不同目录,可以为每个 目录单独建立makefile

  • 利于维护makefile,使得其更简洁
  • 利于分块/分段编译
  • 最顶层、调用make执行其他makefile的makefile称为总控
1
2
3
4
5
6
7
8
9
10
subsystem:
cd subdir && $(MAKE)
# 等价
subsystem:
$(MAKE) -C subdir

subsystem:
cd subdir && $(MAKE) -w MAKEFLAGS=
# 将命令行参数`MAKEFLAGS`置空,实现其不向下级传递
# 指定`-w`参数输出make开始前、后信息

搜索路径

VPATH

VPATH:makefile中定义的特殊环境变量,指明寻找依赖文件、 目标文件的路径

1
VPATH = src:../src
  • :分隔路径
  • 当前目录优先级依旧最高

vpath

vpath:make关键字,指定不同模式文件不同搜索目录

1
2
3
4
5
6
7
vpath <pattern> <directories>
vpath %.h ../headers
# 指明`<pattern>`模式文件搜索目录
vpath <pattern>
# 清除`<pattern>`模式文件搜索目录设置
vpath
# 清除所有`vapth`设置
  • <pattern>中使用%匹配0或若干字符
  • vpath可以重复为某个模式指定不同搜索策略,按照出现顺序 先后执行搜索

隐含规则

  • 隐含规则是一种惯例,在makefile中没有书写相关规则时自动 照其运行

    • 隐含规则中优先级越高的约经常被使用
    • 甚至有些时候,显式指明的目标依赖都会被make忽略
      1
      2
      3
      4
      foo.o: foo.p
      # Pascal规则出现在C规则之后
      # 若当前目录下存在foo.c文件,C隐含规则生效,生成
      # foo.o,显式依赖被忽略
    • 很多规则使用后缀规则定义,即使使用-r参数,其 仍会生效
  • 隐含规则会使用系统变量

    • CPPFLAGS/CFLAGS:C++/C编译时参数
  • 可以通过模式规则自定义隐含规则,更智能、清晰

    • 后缀规则有更好的兼容性,但限制更多

常用隐含规则

编译C

  • 目标:<n>.o

  • 依赖包含:<n>.c

  • 生成命令

    1
    $(CC) -c $(CPPFLAGS) $(CFLAGS)

编译CPP

  • 目标:<n>.o

  • 依赖包含<n>.cc/<n>.c

  • 生成命令

    1
    $(CXX) -c $(CPPFLAGS) $(CFLAGS)

编译Pascal

  • 目标:<n>.p

  • 依赖包含:<n>.p

  • 生成命令

    1
    $(PC) -c $(PFLAGS)

编译Fortran/Ratfor

  • 目标:<n>.o

  • 依赖包含:<n>.f/<n>.r

  • 生成命令

    1
    2
    3
    4
    5
    6
    $(FC) -c $(FFLAGS)
    # `.f`
    $(FC) -c $(FFLAGS) $(CPPFLAGS)
    # `.F`
    $(FC) -c $(FFLAGS) $(RFLAGS)
    # `.r`

预处理Fortran/Ratfor

  • 目标:<n>.f

  • 依赖包含:<r>.r/<n>.F

  • 生成命令

    1
    2
    3
    4
    $(FC) -F $(CPPFLAGS) $(FFLAGS)
    # `.F`
    $(FC) -F $(FFLAGS) $(RFLAGS)
    # `.r`
  • 转换Ratfor、有预处理的Fortran至标准Fortran

编译Modula-2

  • 目标:<n>.sym/<n>.o

  • 依赖包含:<n>.def/<n>.mod

  • 生成命令
    1
    2
    3
    4
    $(M2C) $(M2FLAGS) $(DEFFLAGS)
    # `.def`
    $(M2C) $(M2FLAGS) $(MODFLAGS)
    # `.mod`

汇编汇编

  • 目标:<n>.o

  • 依赖包含:<n>.s

  • 生成命令:默认使用编译器as

    1
    2
    $(AS) $(ASFLAGS)
    # `.s`

预处理

  • 目标:<n>.s

  • 依赖包含:<n>.S

  • 生成命令:默认使用预处理器cpp

    1
    2
    $(CPP) $(ASFLAGS)
    # `.S`

链接object

  • 目标:<n>

  • 依赖包含:<n>.o

  • 生成命令:默认使用C工具链中链接程序ld

    1
    $(CC) <n>.o $(LOADLIBS) $(LDLIBS)

Yacc C

  • 目标:<n>.c

  • 依赖包含:<n>.y

  • 生成命令

    1
    $(YACC) $(YFALGS)

Lex C

  • 目标:<n>.c

  • 依赖包含:<n>.c

  • 生成命令

    1
    $(LEX) $(LFLAGS)

Lex Ratfor

  • 目标:<n>.r

  • 依赖包含:<n>.l

  • 生成命令

    1
    $(LEX) $(LFLAGS)

创建Lint库

  • 目标:<n>.ln

  • 依赖包含:<n>.c/<n>.y/<n>.l

  • 生成命令

    1
    $(LINT) $(LINTFLAGS) $(CPPFLAGS) -i

创建静态链接库

  • 目标:<archive>(member.o)

  • 依赖包含:member

  • 生成命令

    1
    ar cr <archive> member.o
  • 即使目标传递多个memeber.o,隐含规则也只会解析出把首个 .o文件添加进静态链接库中的命令
1
2
3
4
(%.o): %.o
$(AR) rv $@ $*.o
# 此命令可以得到添加所有`member.o`的命令
# 但是此时`$*=member.o member`

隐含规则使用变量

隐含规则使用的变量基本都是预先设置的变量

  • makefile中改变
  • make命令环境变量传入
  • 设置环境变量
  • -R/--no-builtin-variable参数取消变量对隐含规则作用

命令

  • AR:函数打包程序,默认ar
  • AS:汇编语言编译程序,默认as
  • CC:C语言编译程序,默认cc
  • CXX:C++语言编译程序,默认c++/g++
  • CPP:C程序预处理器,默认$(CC) -E/cpp
  • FC:Fortran、Ratfor编译器、预处理程序,默认f77
  • PC:Pascal语言编译程序,默认pc
  • LEX:Lex文法分析器程序(针对C、Ratfor),默认lex
  • YACC:Yacc文法分析器程序(针对C、Ratfor),默认 yacc -r
  • GET:从SCCS文件中扩展文件程序,默认get
  • CO:从RCS文件中扩展文件程序,默认co
  • MAKEINFO:转换Texinfo .texi到Info程序,默认 makeinfo
  • TEX:转换TeX至Tex DVI程序,默认tex
  • TEXI2DVI:转换Texinfo至Tex DVI程序,默认texi2dvi
  • WEAVE:转换Web至TeX程序,默认weave
  • TANGLE:转换Web至Pascal程序,默认tangle
  • CTANGEL:转换C Web至C,默认ctangle
  • RM:删除文件命令,默认rm -f

命令参数

未指明默认值则为空

  • ARFLAGS:静态链接库打包程序AR参数,默认rv
  • ASFLAGS:汇编语言汇编器参数
  • CFLAGS:C编译器参数
  • CXXFLAGS:C++编译器参数
  • CPPFLAGS:C预处理参数
  • LDFLAGS:链接器参数
  • FFLAGS:Fortran编译器参数
  • RFLAGS:Fatfor的Fortran编译器参数
  • LFLAGS:Lex文法分析器参数
  • YFLAGS:Yacc文法分析器参数
  • COFLAGS:RCS命令参数
  • GFLAGS:SCCS get参数

隐含规则链

make会努力自动推导生成目标的一切方法,无论中间目标 数量,都会将显式规则、隐含规则结合分析以生成目标

  • 中间目标不存在才会引发中间规则

  • 目标成功产生后,中间目标文件被删除

    • 可以使用.SECONDARY强制声明阻止make删除该中间目标
    • 指定某模式为伪目标.PRECIOUS的依赖目标,以保存被 隐含规则生成的符合该模式中间文件
  • 通常makefile中指定成目标、依赖目标的文件不被当作中间目标 ,可以用.INTERMEDIATE强制声明目标(文件)是中间目标

  • make会优化特殊的隐含规则从而不生成中间文件,如从文件 foo.c直接生成可执行文件foo

模式规则

模式规则:隐式规则可以看作内置模式规则

  • 目标定义包含%,表示任意长度非空字符串
  • 依赖中同样可以使用%,但是其取值取决于目标
  • 命令中不使用模式%,使用自动化变量
  • 模式规则没有确定目标,不能作为最终make目标

    • 但是符合模式规则的某个具体文件可以作为最终目标
    • 不需要作为显式规则的目标,如:archive(member)作为 静态库隐含规则目标
  • 模式的启用取决于其目标%解析同样取决于目标 (因为根据目标查找、应用模式规则)

  • 模式规则类似于隐含规则,给出符合某个模式的某类目标 的依赖、生成命令

  • %展开发生在变量、函数展开后,发生在运行时

静态模式

静态模式:给定目标候选范围的模式,限制规则只能应用在以 给定范围文件作为目标的情况

1
2
<target>: <target-pattern>: <prereq-patterns>
<commands>
  • <target>:目标候选范围,可含有通配符
  • <target-pattern>所有目标文件满足的模式
  • <prereq-pattern>:目标相应依赖
  • 简单例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    objects = foo.o bar.o
    all: $(objects)
    $(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@

    # 等价于

    foo.o: foo.c
    $(CC) -c $(CFLAGS) foo.c -o foo.o
    bar.o: bar.c
    $(CC) -c $(CFLAGS) bar.c -o bar.o
  • 静态模式+filter函数筛选范围

    1
    2
    3
    4
    5
    files = foo.elc bar.o lose.o
    $(filter %.o,$(files)): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@
    $(filter %.elc,$(files)): %.elc: %.el
    emacs -f batch-byte-compile $<

重载内建隐含规则

1
2
3
4
5
%.o: %c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -D $(date)
# 重载内建隐含规则
%o: %c
# 命令置空,取消内建隐含规则

后缀规则

  • 双后缀规则:定义一对目标文件后缀、依赖后缀
  • 单后缀规则:定义一个依赖后缀
1
2
3
4
5
6
.c.o:
# 等价于`%.o: %c`
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
.c:
# 等价于`%: %.c`
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
  • 后缀规则中不能有任何依赖文件,否则不是后缀规则,后缀被 认为是目标文件

  • 后缀规则中必须有命令,否则没有任何意义,这不会移去内建 的隐含规则

  • 后缀规则定义中的后缀需要是make所认识的,可以使用伪目标 .SUFFIXES修改make认识的后缀

    1
    2
    3
    4
    .SUFFIXES:
    # 删除默认后缀
    .SUFFIXES: .c .o .h
    # 添加自定义后缀
    • 变量$SUFFIXE用于定义默认后缀列表,不应该修改

    • -r/--no-builtin-rules同样会清空默认后缀列表

  • 后缀规则是老式定义隐含规则的方法,会被逐步取代,事实上 后缀规则在makefile载入后会被转换为模式规则

模式规则搜索算法

设目标为src/foo.o

  • 将目标目录部分、文件名部分分离,得到src/foo.o

  • 搜索所有模式规则列表,创建目标和src/foo.o匹配的模式 规则列表

    • 若模式规则列表中有目标匹配所有文件的模式(如%), 则从列表中移除其他模式
    • 移除列表中没有命令的规则
  • 对列表中首个模式规则

    • src/foo.ofoo.o匹配目标,推断%匹配非空部分 茎S
    • 把依赖中%替换为茎S,如果依赖项中没有包含目录, 尝试将src添加在其前
    • 检查所有依赖文件存在、理当存在(文件被定义为其他规则 的目标文件、显式规则的依赖文件)
    • 若有依赖文件存在、理当存在或没有依赖文件,此规则被 采用,退出算法
  • 若没有找到合适模式规则,则检查列表中下个规则是否可行

  • 若没有模式规则可以使用,检查.DEFAULT规则存在性,存在 则应用

变量、赋值

Makefile中定义的变量类似C++/C中的宏

  • 代表一个字符串,在makefile中执行的时候展开在所在位置

    • ""会被作为字符串一部分
    • 默认空格、逗号分隔列表

      1
      2
      3
      4
      5
      empty:=
      space:=$(empty) # 后面有空格,就得到了空格
      comma:=,
      foo:=a,b,c
      bar:=$(substr $(comma), $(space), $(foo)) # 得到字符串`a b c`
  • 变量可以改变值

  • 在shell中需要$$处应使用两个$$$,一个$被escape,则 shell解释时仍然保留一个$,如:变量、函数等都需要

赋值

Makefile内自定义变量

1
2
3
4
5
6
7
txt = Hello World
# 自定义变量
test:
@echo $(txt)
echo ${txt}
# 调用变量,`()`、`{}`含义相同
# 若变量名为单个字符,可以省略括号,但不建议省略
  • =lazy set,在执行时扩展

    • 可以使用任意位置定义(可能还未定义)的变量赋值
    • 允许递归扩展,make报错
  • :=immediate set,在定义/赋值时扩展完毕

    • 只允许使用之前已定义变量赋值(否则为空)
  • ?=set if absent,只有变量为空时才设置值

  • +=append,将值追加到变量的尾部

    • 若前面变量有定义,+=会继承前一次操作符:=/=
    • 对于=定义变量,make自动处理“递归”

define

define可以换行定义变量

  • 变量类似宏的行为、可换行定义变量,方便定义命令包
1
2
3
4
5
6
7
8
9
10
define run-yacc
# `define`后跟变量名作为命令包名称
yacc $(firstword $^); \
mv y.tab.c $@
endef
# 结束定义

foo.c: foo.y
$(run-yacc)
# 使用命令包

override

  • 若变量由make命令行参数-e设置,makefile中默认忽略对其 赋值
  • 需要显式使用override关键字设置
1
2
3
override <variable> = <value>
override <variable> := <value>
override define <variable>

export

上级makefile中变量可以显式export传递到下层makefile中, 但是不会覆盖下层中定义的变量(除指定-e参数)

1
2
3
4
5
6
7
8
9
export <variable>[=value]
# 传递变量至下级makefile中
unexport <variable>
# 禁止变量传递至下级makefile中

export variable = value
# 等价
variable = value
export variable
  • export后面不指定具体传递变量,表示传递所有变量
  • MAKEFLAGSSHELL两个变量总是会传递到下级makefile中

系统环境变量

make运行时系统环境变量、命令行环境变量可以被载入makefile

  • 默认makefile中定义变量覆盖系统环境变量
  • -e参数则表示makefile中变量被覆盖
1
2
3
test:
@echo $$HOME
# 传递给shell的变量,需要`$$` escape

Target-Specific Variable

目标/局部变量:作用范围局限于规则、连带规则中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<target ...>: [override] <variable-assignment>

prog: CFLAGS = -g
prog: prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o

prog.o: prog.c
$(CC) $(CFLAGS) prog.c

foo.o: foo.c
$(CC) $(CFLAGS) foo.c

bar.o: bar.c
$(CC) $(CFLAGS) bar.c

Pattern-Specific Variable

模式变量:给定模式,变量定义在符合模式的所有目标

1
2
3
<pattern ...>: [override]<variable-assignment>

%.o: CFLAGS = -o

Implicit Variables

内置变量:主要是为了跨平台的兼容性

  • $(CC):当前使用的编译器

    1
    2
    output:
    $(CC) -o output input.c
  • $(MAKE):当前使用的Make工具

  • $(MAKECMDGOLA):make目标列表

Automatic Variables

自动化变量:应用规则时被自动赋予相应值(一般是文件)的变量

  • $@当前需要生成的目标文件
    • 多目标规则中,$@也只表示被需要目标
  • $*:匹配符%匹配部分
    • 若目标中没有%模式符,$*不能被推导出,为空
    • GNU make中,目标中没有%$*被推导为除后缀部分, 但是很可能不兼容其他版本,谨慎使用
  • $<:首个前置条件
  • $%:仅当目标是函数库文件,表示目标成员名,否则为空

    • 目标为foo.a(bar.o)$%bar.o$@foo.a
  • $?:比目标更新的前置条件,空格分隔

  • $^:所有前置条件,会取出其中重复项
  • $+:类似于$^,但是剔除重复依赖项
  • 自动化变量只应出现在规则的命令
  • 自动化变量值与当前规则有关
  • 其中$@$*$<$%扩展后只会为单个文件,$?$^$+扩展后可能是多个文件
1
2
3
dest/%.txt: src/%.txt
@[ -d test ] || mkdir dest
cp $< $@

D、F

  • 7个自动化变量可以搭配DF取得相应路径中目录名、 文件名
  • 新版本GNU make可以使用函数dirnotdir代替D/F
  • D/dir:目录带有最后/,若为当前目录则为./
  • F/nodir:文件名
  • 对可能会扩展为多文件的$?$^$+D/F处理后 返回同样是多个目录/文件
1
2
3
4
5
6
7
8
9
10
11
12
13
$(@D)
$(dir $@)
# `$@`的目录名
$(@F)
$(nodir $@)
# `$@`的文件名

$(?D)
$(dir $?)
# `$?`中所有目录,空格分隔
$(?F)
$(nodir $?)
# `$?`中所有文件,空格分隔

控制语句

if

1
2
3
4
5
6
7
8

<conditional-directive>
<text-if-true>
[
else
<text-if-false>
]
endif
  • ifeq:比较参数是否相等

  • ifneq:比较参数是否不等

    1
    2
    3
    4
    5
    6
    ifeq ($(CC), gcc)
    # 也可以用单/双引号括起,省略括号
    libs=$(libs_for_gcc)
    else
    libs=$(normal_libs)
    endif
  • ifdef

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    bar =
    foo = $(bar)
    # `foo`有定义
    ifdef foo
    frobozz = yes
    # 此分支
    else
    frobozz = no
    endif

    foo =
    # `foo`未定义
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    # 此分支
    endif
  • ifndef

  • <conditional-directive>, else, endif行可以有多余空格, 但是不能以<tab>开头,否则被认为是命令
  • make在读取makefile时就计算表达式值、选择语句,所以最好 别把自动化变量放入条件表达式中
  • make不允许把条件语句分拆放入两个文件中

for

1
2
3
4
5
6
7
8
9
10
11
LIST = one two three

all:
for i in $(LIST); do \
echo $$i;
// 这里传递给shell的变量,需要`$$` escape
done
all:
for i in one two three; do
echo $$i;
done

内建函数

1
2
$(function parameters)
${function paremeters}

Make控制函数

提供一些函数控制make执行

  • 检测运行makefile的运行时信息,根据信息决定make继续执行 、停止

error

产生错误并退出make,错误信息<text>

1
2
3
4
5
6
7
8
9
10
$(error <text...>)

ifdef ERROR_001
$(error error is $(ERROR_001))
endif

ERR = $(error found an error)
.PHONY: err
err:
err: ; $(ERR)

warning

类似error函数,输出警告信息,继续make

其他函数

shell

执行shell命令的输出作为函数返回值

1
srcfiles := $(shell echo src/{00..99}.txt)
  • 函数会创建新shell执行命令,大量使用函数可能造成性能下降 ,尤其makefile的隐晦规则可能让shell函数执行次数过多

wildcard

在变量中展开通配符*

1
2
3
srcfiles := $(wildcard src/*.txt)
# 若不展开,则`srcfiles`就只是字符串
# 展开后则表示所有`src/*.txt`文件集合

字符串处理函数

subst

文本替换

1
2
3
4
5
$(subst <from>,<to>,<text>)
# `subst`函数头

$(subst ee,EE,feet on the street)
# 替换成*fEEt on the strEET*

patsubst

模式匹配的替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(patsubst <pattern>,<replacement>,<text>)
# 函数头文件
$(patsubst %.c,%o,x.c.c bar.c)
# 替换为`x.c.o bar.o`

foo := a.o b.o c.o
$(variable: <pattern>=<replacement>)
# `patsubst`函数的简写形式
bar := $(foo:%.o=%.c)
# `$(bar)`变为`a.c b.c c.c`

$(variable: <suffix>=<replacement>)
# 没有模式匹配符`%`则替换结尾
bar := $(foo:.o=.c)
# `$(bar)`变为`a.c b.c c.c`

strip

去字符串头、尾空格

1
2
$(strip <string>)
$(strip a b c)

findstring

<in>中查找<find>,找到返回<find>,否则返回空串

1
2
$(findstring <find>,<in>)
$(findstring a,a b c)

filter

<pattern>模式过滤<text>字符串中单词,返回符合模式的 单词

1
2
3
4
5
6
$(filter <pattern..>,<text>)

sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s, $(sources)) -o foo
# 返回`foo.c bar.c baz.s`

filter-out

<pattern>模式过滤<text>字符串中单词,返回不符合模式的 单词

1
2
3
4
objects := main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains), $(objects))
# 返回`foo.o bar.o`

sort

<list>中单词升序排序

1
2
3
4
$(sort <list>)

$(sort foo bar lose)
# 返回`bar foo lose`

word

取字符串<text>中第<n>个单词

1
2
3
4
$(word <n>,<text>)

$(word 2, foo bar baz)
# 返回`bar`

wordlist

<text>中取<s>-<e>单词(闭区间)

1
2
3
4
$(wordlist <s>,<e>,<text>)

$(wordlist 2, 3, foo bar baz)
# 返回`bar baz`

words

统计<text>中单词个数

1
2
3
4
$(word <text>)

$(word, foo bar baz)
# 返回3

firstword

<text>中首个单词

1
2
3
4
$(firstword <text>)

$(firstword foo bar)
# 返回`foo`

文件名操作函数

dir

从文件名序列中取出目录部分

  • 最后/之前部分
  • 若没有/则返回./
1
2
3
4
$(dir <names...>)

$(dir src/foo.c hacks)
# 返回`src/ ./`

notdir

从文件名序列中取出非目录部分(最后/之后部分)

1
2
3
4
$(notdir <names...>)

$(notdir src/foo.c hacks)
# 返回`foo.c hacks`

suffix

从文件名序列中取出各文件名后缀

1
2
3
4
$(suffix <names...>)

$(suffix src/foo.c src-1.0/bar.c hacks)
# 返回`.c .c`

basename

从文件名序列中取出各文件名“前缀”(除后缀外部分)

1
2
3
4
$(basename <names...>)

$(basename src/foo.c src-1.0/bar.c hacks)
# 返回`src/foo src-1.o/bar hacks`

addsuffix

把后缀<suffix>添加到文件名序列中每个单词后

1
2
3
4
$(addsuffix <suffix>,<names...>)

$(addsuffix .c, foo bar)
# 返回`foo.c bar.c`

addprefix

把后缀<prefix>添加到文件名序列中每个单词后

1
2
3
4
$(addprefix <prefix>,<names...>)

$(addprefix src/, foo bar)
# 返回`src/foo src/bar`

join

<list2>中单词对应添加到<list1>中单词后

  • 较多者剩余单词保留
1
2
3
4
$(join <list1>,<list2>)

$(join aaa bbb, 111 222 333)
# 返回`aaa111 bbb222 333`

控制函数

foreach

循环函数,类似于Bash中的for语句

  • <list>中单词逐一取出放到参数<var>所指定的变量中
  • 再执行<text>所包含的表达式,每次返回一个字符串
  • 循环结束时,返回空格分隔的整个字符串
1
2
3
4
5
$(foreach <var>,<list>,<text>)

names := a b c d
files := $(foreach n,$(names),$(n).o)
# 返回`a.o b.o c.o d.o`
  • <var>是临时局部变,函数执行完后将不再作用

if

类似于make中的ifeq

  • <condition>为真(非空字符串),计算<then-part>返回值
  • <condition>为假(空字符串),计算<else-part>、返回空 字符串
1
$(if <condition>,<then-part>,[<else-part>])

call

创建新的参数化函数的函数

  • 创建表达式<expression>,其中可以定义很多参数
  • call函数向其中传递参数,<expression>返回值即call 返回值
1
2
3
4
5
6
7
8
$(call <expression>,<param1>,<param2>,...>

reverse = $(1) $(2)
foo = $(call reverse,a,b)
# 返回`a b`
reverse = $(2) $(1)
foo = $(call reverse,a,b)
# 返回`b a`
  • <expression>要先创建为变量,然后不带$传递

origin

返回变量的来源

  • undefined<variable>未定义
  • default:make默认定义变量
  • environment:环境变量,且-e选项未开
  • file:定义在makefile中
  • command line:命令行定义环境变量
  • overrideoverride关键字定义
  • atomatic:命令运行中自动化变量
1
2
3
4
5
6
7
$(origin <variable>)

ifdef bletch
ifeq "$(origin bletch)" "environment"
bletch = barf, gag, etc
endif
endif
  • <variable>不操作变量值,不带$传递

Makefile技巧

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
edit: main.o kdd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o dispaly.o\
insert.o search.o files.o utils.o

main: main.c defs.h
cc -c main.c
kbd.o: kbd.c defs.h
cc -c kbd.c
command.o: command.c defs.h command.h
cc -c command.c
display.o: display.o defs.h buffer.h
cc -c display.c
insert.o: insert.c defs.h buffer.h
cc -c insert.c
search.o: search.c defs.h buffer.h
cc -c search.c
files.o: files.c defs.h buffer.h command.h
cc -c files.c
utils.o utils.c defs.h
cc -c utils.c

clean:
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

.PHONY: edit clean
# 设置`edit`、`clean`为伪目标

利用变量简化目标

1
2
3
4
5
6
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

edit: $(objects)
cc -o edit $(objects)
# 以下同上

隐式模式自动推导

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit: $(objects)
cc -o edit $(objects)
main.o: defs.h
kbd.o: defs.h command.h
command.o: defs.h command.h
display: defs.h buffer.h
insert.o: defs.h buffer.h
search.o: defs.h buffer.h
files.o: defs.h buffer.h command.h
utils.o: defs.h

clean:
rm edit $(objects)

.PHONY: clean
  • 利用隐式模式自动推导文件、文件依赖关系

利用变量提取依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit: $(objects)
cc -o edit $(objects)

$(objects): defs.h
kbd.o command.o files.o: command.h
display.o insert.o search.o files.o: buffer.h

clean:
rm edit $(objects)

.PHONY: clean
  • 文件变得简单,但是依赖关系不再清晰

自动生成依赖

  • 大部分C++/C编译器支持-M选项,自动寻找源文件中包含的 头文件,生成依赖关系

  • GNU建议为每个源文件自动生成依赖关系,存放在一个文件中, 可以让make自动更新依赖关系文件.d,并包含在makefile中

1
2
3
4
5
6
7
8
9
10
11
12
13
%.d: %.c
@set -e; rm -f $@; \
$(cc) -M $(CPPFLAGS) $< > $@.$$$$; \
# 生成中间文件
# `$$$$`表示4位随机数
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; \
# 用`sed`替换中间文件target
# `xxx.o` -> `xxx.o xxx.d`
rm -f $@.$$$$
# 删除中间文件

source = foo.c bar.c
include $(sources: .c=.d)

GCC

G++

g++:是gcc的特殊版本,链接时其将自动使用C++标准库而不是 C标准库

1
2
$ gcc src.cpp -l stdc++ -o a.out
// 用`gcc`编译cpp是可行的

GDB

概述

GDB是GNU发布的UNIX程序调试工具,可以帮助完成

  • 自定义运行程序
  • 让程序在指定断点处停止
  • 检查停止程序的内部状态
  • 动态改变程序执行环境

调试准备

  • gdb会根据文件名后缀确认调试程序的语言,设置gdb自身语言 环境,并随之改变语言环境

    • 如果程序由多种语言编译而成,gdb能根据不同语言自动 切换语言环境
  • 可以使用infoshowset命令查看设置当前语言环境

  • 可能会缺少glibc-debuginfo,无法显示全部调试信息

    • OpenSuse安装可能需要添加源

编译选项

  • 调试C++/C程序时,要求在编译时就把调试信息添加到可执行 文件中

    • 使用gcc/g++的-g参数添加源码信息
    • 否则调试时将看不见函数名、变量名,而是全部以运行时 内存地址代替
  • 关闭优化选项

    • 否则优化器会删改程序,使得某些变量不能访问、取值错误

启动GDB调试

  • gdb <exe>:直接gdb启动可执行文件<exe>
  • gdb <exe> core:用gdb同时调试可执行文件、core文件
  • gdb <exe> <PID>:给定进程PID,gdb自动attach该进程, 调试已经运行的程序
    • 也可不指定PID,直接关联源码,在gdb中使用attach命令 手动挂接,调试已运行程序

配置调试环境

源文件

  • gdb启动程序后,gdb会在PATH、当前目录中搜索程序的源文件 -directory/-d添加源文件搜索路径
  • 在gdb交互中使用dir[rectory] <dirname>指定源文件搜索 路径
  • gdb中使用list/l检查gdb是否能列出源码

程序运行环境

在gdb中run程序之前,可能需要设置、查看程序运行环境

  • 程序运行参数

    • set <args>:设置程序运行时参数
    • show <args>:查看已设置参数
  • 环境变量

    • path [<dir>]:查看、添加<dir>PATH
    • show paths:查看PATH
    • set environment var [=value]:设置环境变量
    • show environment var:查看环境变量
  • 工作目录

    • cd <dir>:等价于cd
    • pwd:等价于pwd
  • 输入输出

    • info terminal:显示程序所有终端模式
    • run > outfile:重定向程序输出
    • tty /dev/:指定输入、输出设备

参数

  • -s <file>/-symbols <file>:从指定文件读取符号表
  • -se <file>:从指定文件读取符号表信息,并用于可执行文件
  • -c <file>/-core <file>:调试core dump产生的core文件
  • -d <dir>/-directory <dir>:添加源文件搜索路径
    • 默认搜索路径是PATH

表达式

  • 表达式语法应该是当前所调试语言的语法

  • gdb另外还支持一些通用操作符

    • @:指定存储在堆上、动态分配内存大小的长度

      1
      2
      3
      4
      int *array = (int*)malloc(len * sizeof(int));
      # 源码
      > p *array@len
      # 打印数组`*array`
    • :::指定某个具体文件、函数中的变量

      • 常用于取出被隐藏的全局变量
    • [(type)]<addr>:内存地址addr处为一个type类型 变量

停止点

设置Breakpoint

break

break:在某位置设置断点

1
2
b[reak] [<probe_modifier>] [<location>] [thread <threadno>]
[if <condition>]
  • probo_modifier:命令处于probe point时需要
    • -probe:通用、自动推测探测类型
    • -probe-stapSystemTap探测
    • -probe-dtraceDTrace探测
  • location:设置断点位置
    • 缺省:当前栈帧中的执行位置
    • linespecs冒号分隔绝对位置参数(逐步精确)
      • 其中可以包括:文件名、函数名、标签名、行号
    • +-[n]标识相对当前行位置
    • *addr:在程序运行的内存地址addr
    • explicit:类似于linespecs,通过多个参数给出
  • threadno:设置断点的线程号
    • 缺省断点设置在所有线程上
    • gdb分配的线程编号,通过info threads查询
    • 多线程被gdb停止时,所有运行线程都被停止,方便 查看运行程序总体情况
  • if <condition>:满足条件condition时在断点处停止, 缺省立即停止程序
1
2
3
4
5
6
7
8
9
> break factorial.c:fact:the_top
# factorial.c文件、fact函数、the_top标签
> break +2
# 当前行之后两行
> break *main + 4
# `main`函数4B之后
> break -source factorial.c -function fact
-label the_top
# explicit方式,同linespecs
  • 标签:C++/C中配合goto使用(label_1:
  • <tab>:补全函数名称、函数原型(重载函数)
  • 若指定函数名称不唯一,gdb会列出函数原型供选择

tbreak

tbreak:设置一次性断点,生效后立刻被删除

rbreak

rbreak:对匹配正则表达式的行均设置断点

condition

condition:修改、设置断点n生效条件

1
2
> condition <bpnum> <condition>
# 设置断点`bpnum`生效条件为`condition`

ignore

ignore:设置忽略断点n次数

1
2
> ignore <bpnum> <count>`
# 忽略断点`bpnum` `count`次

设置Watchpoint

watch

watch:为某个表达式设置观察点

  • 观察某个表达式,其值发生变化时停止程序
1
2
> watch [-l|-location] <expr> [thread <threadno>]
[mask <maskvalue>]
  • -l:表示将expression视为地址,观察表达式指向的地址 中的值
  • expr:表达式、*开头表示的内地地址
  • threadno:同break
  • maskvalue:观察值mask,只观察部分bit值
1
2
3
4
> watch foo mask 0xffff00ff
# 观察`foo`
> watch *0xdeadbeef 0xffffff00
# 观察内存地址`0xdeadbeef`处的值
  • 取决于系统,观察点有可能以硬件、软件方式实现,大部分 PowerPC、X86支持硬件观察点
    • 软件观察点通过逐步测试变量实现,程序执行速度慢得多
    • 硬件观察点不会降低程序执行速度
  • mask参数需要架构支持

rwatch

rwatch:为某表达式设置读观察点

  • 当表达式值被读取时停止程序
1
2
> rwatch [-l|location] <expression> [thread <threadno>]
[mask <maskvalue>]

awatch

awatch:为某表达式设置写观察点

  • 当表达式值被修改时停止程序
1
2
> awatch [-l|-location] <expression> [thread <threadno>]
[mask <maskvalue>]

设置Catchpoint

catch

catch:设置捕捉点捕捉事件

1
> cat[ch] <event>
  • 通用事件
    • catch:捕获异常
    • exec:调用系统调用exec(仅HP-UX下有用)
    • fork:调用系统调用fork(仅HP-UX下有用)
    • load [<libname>]:载入共享库(仅HP-UX下有用)
    • unload [<libname>]:卸载共享库(仅HP-UX下有用)
    • throw:抛出异常
    • rethrow:再次抛出异常
    • vfork:调用系统调用vfork时(仅HP-UX下有用)
    • syscall:被系统调用
  • Ada
    • assert:捕获Ada断言失败
    • exception [arg]:捕获Ada和参数匹配的异常
    • handlers:捕获Ada异常
  • 捕捉点:捕捉程序运行时的一些事件,如:载入动态链接库、 异常事件<event>发生时停止程序

tcatch

tcatch:设置一次性捕捉点,程序停止后自动删除

信号处理

gdb可以在调试程序时处理任何信号,可以要求gdb在收到指定信号后 停止正在执行的程序以供调试

1
> handle <signal> <keywords>
  • signal:需要处理的信号
    • 信号可选使用SIG开头
    • 可以定义需要处理的信号范围SIGIO-SIGKILL
    • 可以使用all表示处理所有信号
  • keywords:被调试程序接收到信号后gdb的动作
    • nostop:不停止程序运行,打印消息告知收到信号
    • stop:停止程序运行
    • print:显示信息

      todo

    • noprint:不显示信息
    • noigore:gdb处理信号,交给被调试程序处理
    • nopass/ignore:gdb截留信号,不交给被调试程序处理

维护停止点

clear

clear:清除指定位置断点

1
> clear [<location>]
  • location:指定清除断点位置
    • 缺省:清除当前栈帧中正在执行行断点
    • 其余设置同break

commands

commands:设置断点生效时自动执行命令

  • 利于自动化调试
1
2
3
> commands [bpnum]
> ...command-list...
> end
  • bpnum:断点序号
    • 缺省:最近设置的断点
    • 5-7:指定断点区间

执行

文件

list

list:打印源代码

1
> l[ist] [<location>]
  • location:输入的源代码
    • 缺省/+:显示当前行后源代码
    • -:显示当前行前源代码
    • [[start], [end]]:显示范围内源代码(缺省表当前行)
    • 指定单行类似breaklocation参数
  • 一般默认显示10行,可以通过set listsize <count>设置

search/forward-search:从打印出最后行开始正则搜索源码

1
> search/forward-search <regexp>

reverse-search:从打印出的最后行反向正则搜索源码

1
> reverse-search <regexp>

directory

directory:指定源文件搜索路径

1
> dir[rectory] <dir>
  • dir:源文件路径
    • 可以指定多个搜索路径,linux下:分隔,windows下;
    • 缺省:清除自定义源文件搜索路径信息
  • show查看当前

继续执行

run

run:启动程序开始调试

1
> r[un] [<args>]
  • args
    • 缺省:上次runset args指定的参数
    • 参数中包含的*...将被用于执行的shell扩展 (需清除参数,使用set args置空)
  • 允许输入、输出重定向

continue

continue:继续执行直到之后断点、程序结束

1
> c[ontinue]/fg [<ignore-count>]
  • ignore-count:执行直到之后第ingore-count个断点 (忽略ignore-count-1个断点)
    • 缺省:1

step/next

step/next:单步/单行跟踪

1
> s[tep]/[n]ext [<count>]
  • count:执行代码行数
    • 缺省:1
  • 有函数调用,step进入函数(需要函数被编译有debug信息)

    next不进入函数调用,视为一代代码

stepi/nexti

stepi/nexti:单条intruction(机器指令、汇编语句)跟踪

1
> s[tep]i/n[ext]i [<count>]
  • count:执行指令条数
    • 缺省:1

finish

finish:运行直到当前栈帧/函数返回,并打印函数返回值、存入 值历史

return

return:强制函数忽未执行语句,并返回

1
> return [expr]
  • expr:返回的表达式值
    • 缺省:不返回值

util

until/u:运行程序直到退出循环体

jump

jump:修改程序执行顺序,跳转至程序其他执行处

1
> jump [<location>]
  • location:同break
  • jump不改变当前程序栈中内容,所以在函数间跳转时,函数 执行完毕返回时进行弹栈操作式必然发生错误、结果错误、 core dump,所以最好在同一个函数中跳转
  • 事实上,jump就是改变了寄存器中保存当前代码所在的内存 地址,所以可以通过set $pc更改跳转执行地址

call

call:强制调用函数,并打印函数返回值(void不显示)

1
> call <expr>
  • print也可以调用函数,但是如果函数返回voidprint 显示并存储如历史数据中

查看信息

1
> p[rint] [/<f>]<expr>[=value]
  • f:输出格式
    • x:16进制格式
    • d:10进制格式
    • u:16进制格式显示无符号整形
    • o:8进制格式
    • t:2进制
    • a:16进制
    • c:字符格式
    • f:浮点数格式
    • i:机制指令码
    • s
  • expr:输出表达式、gdb环境变量、寄存器值、函数
    • 输出表达式:gdb中可以随时查看以下3种变量值
      • 全局变量:所有文件可见
      • 静态全局变量:当前文件可见
      • 局部变量:当前scope可见
      • 局部变量会隐藏全局变量,查找被隐藏变量可以使用 ::指定
    • 编译程序时若开启优化选项,会删改程序,使得某些变量 不能访问
    • 输出环境变量、寄存器变量时,需要使用$前缀
    • 函数名称:强制调用函数,类似call
  • value:修改被调试程序运行时变量值
    • 缺省:打印变量值
    • =是C++/C语法,可以根据被调试程序改为相应程序赋值 语法
    • 可以通过set var实现(当变量名为gdb参数时,必须 使用set var
  • 每个print输出的表达式都会被gdb记录,gdb会以$1$2 等方式记录下来,可以使用此编号访问以前的表达式

examine

examine/x:查看内存地址中的值

1
> examine/x /[<n/f/u>] <addr>
  • 输出参数:可以三者同时使用
    • n:查看内存的长度(单元数目
    • f:展示格式,同print
      • u:内存单元长度
      • b:单字节
      • h:双字节
      • w:四字节,默认
      • g:八字节
  • addr:内存地址
1
2
> x/3uh 0x54320
# 从内存地址`0x54320`开始,16进制展示3个双字节单位

display

display:设置自动显示变量,程序停止时变量会自动显示

1
> display/[<fmt>] [<expr>] [<addr>]
  • fmt:显示格式
    • print
  • exprt:表达式
  • addr:内存地址
1
2
3
> display/i $pc
# `$pc`:gdb环境变量,表示指令地址
# 单步跟踪会在打印程序代码时,同时打印出机器指令

undisplay

undisplay:删除自动显示

1
> undisplay [<num>]
  • num:自动显示编号
    • info查看
    • 可以使用a-b表示范围

查看、设置GDB环境

info

停止点

  • locals:打印当前函数中所有局部变量名、值
  • args:打印当前函数参数名、值
  • b[reak][points] [n]:查看断点
  • watchpoints [n]:列出所有观察点
  • catch:打印当前函数中异常处理信息
  • line [<location>]:查看源代码在内存中地址
  • f[rame]:可以打印更详细当前栈帧信息
    • 大部分为运行时内存地址
  • display:查看display设置的自动显示信息

线程

  • threads查看在正在运行程序中的线程信息

信号

  • info signals/handle:查看被gdb检测的信号
  • frame:查看当前函数语言
  • source:查看当前文件程序语言

其他

  • terminal:显示程序所有终端模式
  • registers [reg]:查看寄存器情况
    • 缺省:除浮点寄存器外所有寄存器
    • 还可以通过print实现
  • all-registers:查看所有寄存器情况(包括浮点寄存器)

set

停止点

  • step-mode [on] [off]:开启/关闭step-mode模式
    • 程序不会因为没有debug信息而不停止,方便查看机器码

环境

  • language [lang]:设置当前语言环境
  • args [<args>]:设置被调试程序启动参数
  • environment var [=value]:设置环境变量
  • listsize <count>:设置最大打印源码行数
  • var <var=value>:修改被调试程序运行时变量值
    • 还可以通过print变量修改

print

  • address [on/off]:打开地址输出

    • 即程序显示函数信息时,显示函数地址
    • 默认打开
  • array [on/off]:打开数组显示

    • 打开数组显示后,每个函数占一行,否则以逗号分隔
    • 默认关闭
  • elements <num-of-elements>:设置数组显示最大长度

    • 0:不限制数组显示
  • null-stop [on/off]:打开选项后,显示字符串时遇到结束符 则停止显示

    • 默认关闭
  • pretty [on/off]:打开选项后,美化结构体输出

    • 打开选项后,结构体成员单行显示,否则逗号分隔
  • sevenbit-strings [on/off]:字符是否按照/nnn格式显示

    • 打开后字符串/字符按照/nnn显示

      todo

  • union [on/off]:显示结构体时是否显示其内联合体数据

    • 打开时联合体显示结构体各种值,否则显示...
  • object [on/off]:打开选项时,若指针对象指向其派生类, gdb自动按照虚方法调用的规则显示输出,否则gdb忽略虚函数表

    • 默认关闭
  • static-members [on/off]:是否对象中静态数据成员

    • 默认打开
  • vtbl [on/off]:选项打开,gdb将用比较规则的格式输出 虚函数表

    • 默认关闭

show

执行

  • args:查看被调试程序启动参数
  • paths:查看gdb中PATH
  • environtment [var]:查看环境变量
  • directories:显示源文件搜索路径
  • convenience:查看当前设置的所有环境变量
  • address:查看是否打开地址输出
  • array:查看是否打开数组显示
  • element:查看再打数组显示最大长度
  • pretty:查看是否美化结构体输出
  • sevenbit-strings [on/off]:查看字符显示是否打开
  • union:查看联合体数据输出方式
  • object:查看对象选项设置
  • static-members:查看静态数据成员选项设置
  • vtbl:查看虚函数显示格式选项设置

shell

shell:执行shell命令

1
> shell <shell-cmd>
  • cd:等同> shell cd
  • pwd
  • make <make-args>
  • Linux:使用环境变量SHELL/bin/sh执行命令
  • Windows:使用cmd.exe执行命令

path

path:添加路径至gdb中PATH(不修改外部PAHT

1
> path <dir>

GDB环境

环境变量

可以在gdb调试环境中自定义环境变量保存调试程序中需要的数据

1
2
3
4
5
6
7
8
> set $foo = *object_ptr
# 设置环境变量
> show convenience
# 查看当前设置的所有环境变量
> set $i=0
> print bar[$i++] -> contents
# 环境变量、程序变量交互使用
# 只需要回车重复上条命令,环境变量自动累加,逐个输出变量
  • 环境变量使用$开头(定义时也需要)
  • gdb会在首次使用时创建该变量,在以后使用直接对其赋值
  • 环境变量没有类型,可以定义任何类型,包括结构体、数组

寄存器

寄存器:存放了程序运行时数据

  • ip:程序当前运行指令地址
  • sp:程序当前堆栈地址
1
2
3
4
5
6
> info registers [<reg-name>]
# 输出寄存器值,缺省除浮点外所有
> info all-registers
# 输出所有寄存器值
> print $eip
# 输出寄存器`eip`值

其他

  • disassemble:查看程序当前执行的机器码
    • 此命令会dump当前内存中指令
  • si[gnal] <signal>:产生信号量发给被调试程序
    • signal:取值1-15,即Unix信号量
    • 此命令直接发送信号给被调试程序,而系统信号则是发送给 被调试程序,但由gdb截获

调试设置

delete

delete:删除断点(缺省)、自动输出表达式等

1
2
> delete [breakpoints] [bookmark] [checkpoints]
[display] [mem] [tracepoints] [tvariable] [num]
  • breakpoints:删除断点
  • bookmark:从书签中删除书签
  • checkpoints:删除检查点
  • display:取消程序停止时某些输出信息
  • mem:删除存储区
  • tracepoint:删除指定追踪点
  • tvariable:删除追踪变量

  • num

    • 缺省:删除所有断点/自动输出/书签等
    • 指定的序号
      • info查看
      • 可以使用a-b表示范围

disable

disable:禁用断点(缺省)、输出表达式等

1
2
3
> disable [breakpoints] [display] [frame-filter]
[mem] [pretty-printer] [probes] [type-printer]
[unwinder] [xmethod] [num]

breakpoints

禁用断点

1
> disable [breakpoints] [num]
  • 缺省:禁用所有断点
  • 仅指定的序号(info查看)

display

禁用程序停止时某些输出信息

frame-filter

禁用某些帧过滤器

mem

禁用存储区

pretty-printer

禁用某些打印美化

probes

禁用探测

type-printer

禁用某些类型打印

unwinder

禁用某些unwinder

xmethod

禁用某些xmethod

enable

enable:启用断点(缺省)、输出表达式等

1
2
3
> enable [breakpoints] [display] [frame-filter]
[mem] [pretty-printer] [probes] [type-printer]
[unwinder] [xmethod] [num]

breakpoints

启用断点

1
2
> enable [breakpoints] [num] [once] [delete]`
- `[delete]`:启用断点,生效后自动被删除
  • num:断点序号
    • 缺省:启用所有断点
  • once:启用断点一次
  • delete:启用生效后自动删除
  • count:启用断点count

display

启用程序停止时某些输出信息

frame-filter

启用某些帧过滤器

mem

启用存储区

pretty-printer

启用某些打印美化

probes

启用探测

type-printer

启用某些类型打印

unwinder

启用某些unwinder

xmethod

启用某些xmethod

backtrace

backtrace:打印函数栈

1
> backtrace/bt [-][<n>]
  • -:打印栈底信息
    • 缺省:打印栈顶信息
  • n:打印栈数量
    • 缺省:打印当前函数调用栈所有信息
  • 一般而言,程序停止时,最顶层栈就是当前函数栈

frame

frame:切换当前栈

1
> f[rame] [<n>]
  • n:切换到第n个栈帧
    • 缺省打印当前栈编号、断点信息(函数参数、行号等)

up

up:上移当前栈帧

1
> up [<n>]
  • n:上移n层栈帧
    • 缺省:上移1层

down

down:下移当前栈帧

1
> down [<n>]
  • n:下移n层栈帧
    • 缺省:下移1层

GDB命令大全

aliases

breakpoints

data

files

internals

obscure

running

stack

status

support

tracepoints

user-defined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ gdb tst
# `gdb`启动可执行文件tst
(gdb) l
# `l`:list,列出源码
(gdb)
# 直接回车:重复上次命令
(gdb) break 16
# `break [n]`:在第`n`行设置断点
(gdb) break func
# `break func`:在函数`func`入口处设置断点
(gdb) info break
# `info break`:查看断点信息
(gdb) r
# `r`:run,执行程序,会自动在断点处停止
(gdb) n
# `n`:next,单条语句执行
(gdb) c
# `c`:continue,继续执行(下个断点、程序结束为止)
(gdb) p i
# `p i`:print,打印变量i的值
(gdb) bt
# `bt`:查看函数栈
(gdb) finish
# `finish`:退出**函数**
(gdb) 1
# `q`:quit,退出gdb