《程序员的自我修养—链接、装载与库》

2023-11-02

程序员的自我修养链接、装载与库

——读书笔记(本文为记录笔记,大部分内容为书中的摘抄


作者微博:@MTK_蛙蛙鱼

写作时间:2013年11月18日

更新时间:2014年0218


编译和链接

2.1 被隐藏了的过程

预编译(cpp or gcc -E)-> 编译(cc1包含了预编译 or gcc -S)-> 汇编(as or gcc -c)-> 链接(ld)

2.2 编译器做了什么

扫描(词法分析)-> 语法分析-> 语义分析-> 源代码优化-> 代码生成-> 目标代码优化

2013年11月18日

lex 词法扫描程序,它会按照用户之前描述好的词法规则将输入的字符串分割成一个个记号

yacc 语法分析程序,它会根据用户给定的语法规则对输入的记号序列进行解析,从而构建出一颗语法树,以表达式为节点的树,符号和数字就是树的叶子

静态语义,声明和类型的匹配,类型的转换,为语法树节点添加类型

源代码优化,比如2+6可以优化掉变成8,属于编译器前端,产生机器无关的中间代码

代码生成器,将中间代码转换成目标机器代码

目标代码优化器,对目标代码进行优化,比如选择合适的寻址方式、使用位移来替代乘法、删除多余的指令等

2.4 模块拼装——静态链接

地址和空间分配-> 符号决议-> 重定位

对于模块A引用到模块B的变量或函数地址时,编译器默认将其地址置为0等待连接器在将目标文件A和B链接起来的时候再将其修正,这个地址修正的过程被叫做重定位

目标文件里有什么

3.1 目标文件格式

目标文件就是源代码编译后但未进行链接的那些中间文件,它跟可执行文件的内容与结构很相似,从广义上看,目标文件与可执行文件的格式其实几乎是一样的

PE格式(Windows Portable Executable)

ELF格式(Linux Executable Linkable Format),可重定位文件(.o)、可执行文件、共享目标文件(.so)、核心转储文件(Core Dump File)。file命令可查看文件类型

COFF格式(Common file format),从a.out格式发展而来,为了解决共享库问题,起源于Unix System V Release 3

3.2 目标文件是什么样的

.bss段只是为未初始化的全局变量局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间

为什么把程序的指令和数据的存放分开?

  • 指令只读,数据可读写,对指令起到保护作用
  • CPU的缓存命中率,现代CPU的缓存一般都被设计成数据缓存和指令缓存分离
  • 指令部分可以共享内存

2013年11月19日

3.3 挖掘SimpleSection.o

binutils工具:objdump、size、objcopy、nm、strip

readelf

CPU的字节序,就是所谓的大端和小端

__attribute__((section("FOO"))) int var = 32;,可以将变量var放入自定义的‘FOO’段中,而不会放到.data段里

3.4 ELF文件结构描述

ELF格式的魔数:0x7F 0x45 0x4c 0x46,分别代表DEL控制字符和'ELF'字符的ACSII码

a.out格式的魔数:0x01 0x07,有一段小历史

e_type文件类型:ET_REL(可重定位文件.o)、ET_EXEC(可执行文件)、ET_DYN(共享目标文件.so)

ELF Header -> Section Table -> 

  • sh_type段的类型:SHT_NULL(无效段)、SHT_PROGBITS(代码段和数据段类型)、SHT_SYMTAB(符号表)、SHT_STRTAB(字符串表)、SHT_RELA(重定位表)、SHT_HASH(符号表的哈希表)、SHT_DYNAMIC(动态链接信息)、SHT_NOTE(提示信息)、SHT_NOBITS(该段在文件中没有内容,如.bss段)、SHT_REL(重定位信息)、SHT_SHLIB(保留)和SHT_DYNSYM(动态链接的符号表)
  • sh_flag段的标志:SHF_WRITE(该段在进程空间中可写)、SHF_ALLOC(该段在进程空间需要分配空间,如代码段、数据段和.bss段)和SHF_EXECINSTR(该段在进程空间中可以被执行,一般指代码段)
ELF Header -> .shstrtab(段表的字符串表在段表中的下标)

3.5 链接的接口——符号

st_value符号的值:

  • 如果在目标文件中定义的符号不是COMMON块,则它的值表示该符号在段中的偏移
  • 如果在目标文件中定义的符号是COMMON块,则它的值表示该符号的对齐属性
  • 如果在可执行文件中定义了符号,则它的值表示该符号的虚拟地址
特殊符号,被定义在ld连接器的链接脚本中,最终生成可执行文件的时候这些符号才会存在,比如__executable_start(程序起始地址)、_etext(代码段结束地址)、_edata(数据段结束地址)、_end(程序结束地址)

早期,C语言源代码文件中的所有全局变量和函数经过编译(.s)后,相应符号名前加上下划线“_”来避免与其他的语言(如Fortran编译后的符号名前后都会加上“_”)的符号冲突,但是无法避免同一种语言产生的符号冲突,-fleading-underscore/-fno-leading-underscore来打开或关闭在C语言符号前加上下划线

C++,命令空间(namespace)

C++符号修饰c++filt工具可以解析修饰过的名称(c++filt _ZN1N1C4funcEi => N::C::func(int)),不同编译器厂商的名称修饰方法可能不同

extern "C",这是C++编译器支持C语言(符号修饰)的编译处理手段,C++宏__cplusplus支持

强符号与弱符号(针对的是符号的定义而不是引用),编译器默认函数和初始化了的全局变量为强符号,为初始化的全局变量为弱符号。通过“__attribute__ ((weak))”可以定义任何一个强符号为弱符号

  • 不允许强符号多次定义,否则报错
  • 有强符号和弱符号,有限选择强符号
  • 都是弱符号,选择占用空间最大的一个

强引用和弱引用(针对的是符号的引用而不是定义),对于一个外部引用没有定义,链接器会报符号未定义错误,这种被称为强引用,与之相对应的叫弱引用。一般对于未定义的弱引用,链接器默认其为0或一个特殊值。通过“__attribute__ ((weakref))”可以声明对一个外部函数的引用为弱引用(测试失败,还是报错了@||,另:那个“一个程序被设计可以支持单线程或多线程的模式”代码有误@||)

2013年11月20日

3.6 调试信息

ELF格式:DWARF(Debug With Arbitrary Record Format)

Microsoft:CodeView

strip

静态链接

4.1 空间与地址分配

两步链接:

  • 空间与地址分配:扫描所有输入目标文件-> 获取各个段的长度、属性和位置信息空间分配)-> 计算出各个段的虚拟地址->计算出目标文件中符号表中所有符号的地址地址分配)-> 收集目标文件的符号表中的所有全局符号建立全局符号表
  • 符号解析与重定位:通过目标文件中的符号表、重定位表和全局符号表来调整代码中的地址
VMA,Virtual Memory Address,即虚拟地址。 LMA,Load Memory Address,即加载地址。我们只需关注VMA

在Linux下,ELF可执行文件默认从地址0x08048000开始分配

4.2 符号解析与重定位

重定位表,如.rel.text(保存了代码段的重定位表)、.rel.data(保存了数据段的重定位表)

r_offset:重定位入口的偏移,对于可重定位文件来说,这个值是该重定位入口所要修正的位置的第一个字节相对于段起始的偏移;对于可执行文件或共享对象文件来说,这个值是...的第一个字节的虚拟地址

r_info:重定位入口的类型和符号,低8位表示重定位入口的类型,高24位表示重定位入口的符号在符号表中的下标

x86基本重定位类型:

  • R_386_32:绝对寻址修正 S + A,A=保存在修正位置的值,S=符号的实际地址(这里其实就是虚拟地址)
  • R_386_PC32:相对寻址修正 S + A - P,P=被修正的位置(这里其实就是该位置的虚拟地址,可通过r_offset加上段虚拟地址计算得到)

4.3 COMMON块

弱符号编译器将未初始化的全局变量定义作为弱符号

编译选项加入“-fno-common”或者代码中使用“__attribute__((nocommon))”来取消COMMON块

4.4 C++的相关问题

模板重复代码消除,编译选项“-ffunction-sections”和"-fdata-sections"可以将每个函数或变量保持到独立的段中

ELF文件格式定义了两种特殊的段:

  • .init:该段里面保存的是可执行指令,它构成了进程的初始化代码
  • .fini:该段保存进程终止代码

ABI,Application Binary Interface,可执行代码的二进制兼容,两个编译器编译出来的目标文件能够互相链接

4.5 静态库连接

ar,打包工具(ar rcs libx.a a.o b.o c.o...),其实最终libx.a安排形式为:一个头 + a.o (一个头大小为0x3c +.o 整体)+ b.o + c.o +

链接器最终需要去寻找到 crt1.o、crti.o、crtbeginT.o、libgcc.a、libgcc_eh.a、libc.a、crtend.o和crtn.o这些文件来做静态链接

4.6 链接过程控制

ld -verbose,打印了连接器默认的控制脚本,已经放在了ld程序的.rodata段中

ld链接脚本,如果把整个链接过程比作一台计算机,那么ld连接器就是CPU,所以目标文件、库文件就是输入,链接输出的可执行文件就是输出,而链接控制脚本正是这台计算机的“程序”,它控制CPU的运行,以“程序”要求的方式将输入加工成所需要的输出结果

链接脚本语句分两种,一种是命令语句,另外一种是赋值语句

可以进入ld参考手册进行学习

2013年11月22日

Windows PE/COFF

5.1 Windows的二进制文件格式PE/COFF

PE(Protable Executable),与ELF同根同源,都是由COFF(Common Equipment File Format)格式发展而来

目标文件默认为COFF格式,可执行文件为PE格式

#pragma与“__attribute__((section("name")))”用法一致,将函数或变量放到自定义段中

5.2 PE的前身——COFF

Microsoft Visual C++ 的编译器为cl,链接器为link,类似objdump的工具dumpbin

__STDC__这个宏的定义表示了编译器禁用了Microsoft C/C++的语法扩展(它们不符合ANSI C/C++标准)

COFF文件由文件头若干段组成,文件头包括两部分,一个是描述文件总体结构和属性的映像头,另一个是描述该文件包含段属性的段表

5.3 Windos 下的ELF——PE

PE文件是基于COFF的扩展

PE文件头包含了:DOS MZ可执行文件格式的文件头和桩代码(DOS MZ File Header and Stub)、IMAGE_NT_HEADERS(包括了原来的“Image Header”和新增的PE扩展头部结构“PE Optional Header”)、描述该文件包含段属性的段表(Section Table)

2013年12月16日

可执行文件的装载与进程

6.1 进程的虚拟地址空间

4GB的Linux虚拟地址空间安排:(0x00000000~ 0xBFFFFFFF)3G为用户进程地址空间、(0xC0000000 ~0xFFFFFFFF)1G为操作系统的地址空间

PAE(Physical Address Extension),32位地址扩展到36位

6.2 装载的方式

覆盖载入,页映射

覆盖载入,覆盖管理器(Overlay Manager),程序员需要手工将模块按照它们的之间的调用依赖关系组织成树状结构

  • 这个树状结构中从任何一个模块到树的根(也就是main)模块都叫调用路径,当模块被调用时,整个调用路径上的模块必须都在内存中
  • 禁止跨树间调用,任何一个模块不允许跨过树状结构进行调用
页映射,装载管理器(操作系统),至于选择放弃哪个页

  • FIFO,先进先出算法
  • LUR,最少使用算法

6.3 从操作系统角度看可执行文件的装载

装载一个可执行文件:

  • 创建一个独立的虚拟地址空间
  • 读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系
  • 将CPU的指令寄存器设置成可执行文件的入口地址,启动运行
创建虚拟地址空间分配一个页目录(Page Directory)且并不设置页映射关系,等到程序发生页错误的时候在进行设置

  1. 可执行文件的真正指令和数据并没有装入到内存中
  2. 页错误产生
  3. 从进程的数据结构中找到空页所在的VMA,计算出相应的页面在可执行文件中的偏移
  4. 在物理内存中分配一个物理页面,将进程中该虚拟页与分配的物理页之间建立映射关系
  5. 将控制器再还回给进程,进程从刚才页错误的位置重新开始执行
读取可执行文件头,建立虚拟空间与可执行文件的映射关系:(进程中相应的数据结构中设置有一个.text段的VMA,它在虚拟空间中的地址为0x08048000~ 0x08049000,对应ELF文件中偏移为0,它的属性为只读)

CPU指令寄存器设置成可执行文件,启动运行:操作系统将控制器转交给进程

6.4 进程虚拟空间分布

空间浪费问题解决(对于相同权限的段,把他们合并到一起):

  • 以代码段为代表的权限为可读可执行的段
  • 以数据段和BSS段为代表的权限为可读可写的段
  • 以只读数据段为代表的权限为只读的段
readelf -l获取段(Segment)信息,这里的段其实就是程序头( Program Header

一个进程基本上可以分为如下几种VMA区域

  • 代码VMA,权限只读、可执行,有映像文件
  • 数据VMA,权限可读写、可执行,有映像文件
  • 堆VMA,权限可读写、可执行,无映像文件,匿名,可向上扩展
  • 栈VMA,权限可读写、不可执行,无映像文件,匿名,可向下扩展
段地址对齐在保证页面属性相同的情况下又节约了物理内存的好方法,一个物理页面被多次映射到虚拟空间中

6.5 Linux内核装载ELF过程简介

bash进程-> fork()创建新进程 -> 调用execve()系统调用执行指定的ELF文件 => sys_execve()进行一些参数的检查复制 =>do_execve()解析文件的前128字节=> search_binary_handle()搜索和匹配合适的可执行文件装载处理过程(ELF文件用load_elf_binary()、a.out文件用load_aout_binary()、脚本文件用load_script())=> 返回do_execve() => 返回sys_execve()=> 跳转到EIP寄存器指定的程序入口

load_elf_binary():

  1. 检查ELF可执行文件格式的有效性,比如魔数、程序头表中段数量
  2. 寻找动态链接的“.interp”段,设置动态链接器路径
  3. 根据ELF可执行文件的程序头表的描述,对ELF文件进行映射,比如代码、数据、只读数据
  4. 初始化ELF进程环境,比如进程启动时EDX寄存器的地址应该是DT_FINI的地址
  5. 将系统调用的返回地址修改成ELF可执行文件的入口点

6.6 Windows PE的装载

可执行文件比较简单,一般仅有代码段、数据段、只读数据段和BSS段

RVA(Relative Virtual Address),相对虚拟地址。目标地址(就是所谓的基地址 Base Address)

装载一个PE可执行文件:

  • 先读取文件的第一个页,这个页包含了DOS头、PE文件头和段表
  • 检查进程地址空间中目标地址是否可用,如果不可用,则另外选一个装载地址(Rebasing)
  • 使用段表中提供的信息,将PE文件中所有的段一一映射到地址空间中相应的位置
  • 如果装载地址不是目标地址,则进行Rebasing
  • 装载所有PE文件所需要的DLL文件
  • 对PE文件中的所有导入符号进行解析
  • 根据PE头中指定的参数建立初始化栈和堆
  • 建立主线程并且启动进程

2013年12月19日

动态链接

7.1 为什么要动态链接

内存和磁盘空间,Program1和Program2依赖的Lib.o库只需提供一份副本,不管在内存和磁盘中

程序开发和发布,只需将新开发的模块覆盖掉旧的即可

程序的可扩展性,被人们用来制作程序的插件(运行时可以动态地选择加载各种程序模块)

程序的兼容性,不同操作系统只需提供对应版本的库接口,而程序本身不用改变

动态链接器,延迟绑定

7.2 简单的动态链接例子

gcc -fPIC -shared -o Lib.so Lib.c

gcc -o Program1 Program1.c ./Lib.so

gcc -o Program2 Program2.c ./Lib.so

Program1除了使用Lib.so以外,还用到了动态链接形式的C语言运行库libc-2.6.1.so,另外还有一个共享对象是ld-2.6.so(动态链接器)

共享对象的最终装载地址在编译时是无法确定的

7.3 地址无关代码

共享对象在被装载时,如何确定它在进程虚拟地址空间中的位置?

  • 手工指定各个模块的地址,比如把0x1000~0x2000分配给A模块,0x2000~0x3000分配给B模块。早期系统采用了这样的做法,叫做静态共享库。缺点:1)共享库中的全局函数和变量的地址不能改变,2)空间受到限制,无法增加太多的全局函数和变量
  • 共享对象在编译时不能假设自己在进程虚拟地址空间中的位置
装载时重定位:在链接时,对所有绝对地址的引用不做重定位,而把这一步推迟到装载时在完成,-shared:

  • 链接时重定位(Link Time Relocation)
  • 装载时重定位(Load Time Relocation)
  • 在Windows中,基址重置(Rebasing)
指令部分在多个进程中共享,目的很简单,希望程序模块中共享的指令部分在装载时不需要因为装载地址的改变而改变,基本思想是把指令中那些需要被修改的部分分离出来,跟数据部分放在一起。 地址无关代码(PIC,Position-independent Code)技术:

  • 类型一、模块内部调用和跳转。相对地址调用指令
  • 类型二、模块内部数据访问。利用<__i686.get_pc_thunk.cx>来获取当前PC地址指针
  • 类型三、模块间数据访问。全局偏移表(GOT,Global Offset Table)
  • 类型四、模块间调用、跳转。同类型三
  • 类型五、模块内部全局调用或跳转。(自己添加的)
  • 类型六、模块内部全局数据访问。(自己添加的)
pic,PIC,地址无关可执行文件(PIE,Position-independent Executable)

类型五、模块内部全局数据访问。当一个模块(module.c)引用了一个共享对象的全局变量的时候:

extern int global;
int foo()
{
    global =1;
}

当编译器编译module.c时,它无法根据这个上下文判断global是定义在同一个模块的其他目标文件还是定义在另一个共享对象之中,这句话有问题。我的理解:首先,如果仅仅编译而不链接module.c,那么它会被编译成目标文件,而其中引用的global在符号表中仅是UND符号未定义类型,其次,在链接的时候,引用global的定义无非是在某个目标文件中或在某个共享对象中,这是可以判断的。因此作者的文笔是有误的。

  • 假设module.c是可执行程序的一部分,链接器会在创建可执行文件时,在它的“.bss”段创建一个global变量的副本,模块内部的global全局变量访问引用可执行文件中global变量的副本的地址,存储在GOT全局偏移表中,也即模块内部全局数据访问采用模块间数据访问方式
  • 假设module.c是共享对象的一部分,编译器在PIC情况下,按照模块间数据访问方式
数据段地址无关性:

static int a;
static int *p = &a;

R_386_RELATIVE,链接时重定位即装载时重定位

7.4 延迟绑定(PLT)

动态链接比静态链接慢大约1%~5%:

  1. 对于全局或模块间的函数调用和数据访问需要进行复杂的GOT定位
  2. 程序开始执行时,动态链接器需要进行一次链接工作
延迟绑定的实现,(Lazy Binding),基本的思想就是当函数第一次被用到时才进行绑定,如果没有用到则不进行绑定。实现方法PLT(Procedure Linkage Table)

00000340 <bar@plt-0x10>:
 340:	ff b3 04 00 00 00    	pushl  0x4(%ebx)
 346:	ff a3 08 00 00 00    	jmp    *0x8(%ebx)
 34c:	00 00                	add    %al,(%eax)
	...

00000350 <bar@plt>:
 350:	ff a3 0c 00 00 00    	jmp    *0xc(%ebx)
 356:	68 00 00 00 00       	push   $0x0
 35b:	e9 e0 ff ff ff       	jmp    340 <_init+0x30>

PC 0x00000350处,跳转到bar函数,初始时跳转到下一条指令

PC 0x00000356处,压入的值是bar函数在.rel.plt重定位表中的下标

PC 0x0000036b处,相对地址跳转

PC 0x00000340处,压入该模块的ID,存储在.got.plt段的第二项中

PC 0x00000346处,跳转到_dl_runtime_resolve函数来完成符号解析和重定位工作

.got段用来保存全局变量引用的地址,.got.plt段用来保存函数引用的地址

.got.plt段的前三项是有特殊意义的:

  1. 第一项是“.dynamic”段的地址
  2. 第二项是本模块的ID
  3. 第三项是_dl_runtime_resolve()的地址

7.5 动态链接相关结构

映射可执行文件——>加载动态链接器——>动态链接器入口地址——>执行自身的初始化操作——>对可执行文件进行动态链接——>可执行文件入口地址——>程序开始执行

.interp”段,保存动态链接器路径“/lib/ld-linux.so.2”,readelf -l

“.dynamic”段,保存动态链接器所需要的基本信息,动态链接下ELF文件的”文件头“,readelf -d

”.dynsym“段,保存动态符号

”.dynstr“段,保存动态符号的字符串

”.rel.dyn“段,保存数据段”.got“的重定位信息,readelf -r

”.rel.plt“段,保存函数引用段”.got.plt“的重定位信息,readelf -r

  • R_386_32,符号的实际地址 + 保存在被修正位置的值
  • R_386_PC32,符号的实际地址 + 保存在被修正位置的值 - 被修正的位置
  • R_386_GLOB_DAT、R_386_JUMP_SLOT,符号的实际地址
  • R_386_RELATIVE,装载地址 + 保存在被修正位置的值
动态链接时进程堆栈初始化信息(当操作系统把控制器交给动态链接器的时候,它需要知道关于可执行文件和本进程的一些信息),它位于进程空间的环境变量指针后面:

  • AT_NULL,辅助信息数组结束标志
  • AT_EXEFD,可执行文件句柄
  • AT_PHDR,可执行文件中的程序头地址
  • AT_PHENT,可执行文件中每个程序头的大小
  • AT_PHNUM,可执行文件中程序头的个数
  • AT_BASE,动态链接器本身的装载地址
  • AT_ENTRY,可执行文件入口地址

7.6 动态链接器的步骤和实现

启动动态链接器本身——>装载所有需要的共享对象——>重定位和初始化

首先:动态链接器本身不可以依赖其他任何共享对象,其次:动态链接器本身所需要的全局和静态变量的重定位工作由它本身完成(这里有个笔误,对于静态变量而言,是不需要重定位的)

自举代码——>找到”.dynamic“段地址——>获取动态链接器本身的重定位表和符号表——>重定位——>开始可以使用变量和调用函数

装载共享对象,通过读取”.dynamic“段中的DT_NEEDED字段广度优先或深入优先

当一个新的共享对象被装载进来时,它的符号表会被合并到全局符号表中

符号的优先级,它定义了一个规则:当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。全局符号介入(类型六、模块内部全局调用或跳转)

重定位(其实是被延迟绑定)——>初始化共享对象”.init“段(比如C++全局/静态对象的构造)——>将控制器转交给可执行程序入口——>程序开始执行

Linux的ELF动态链接器是Glibc的一部分,源代码位于elf目录下面

7.7 显示运行时链接

动态装载库(/lib/libdl.so.2)(dlopen、dlsym、dlerror、dlclose)

void * dlopen(const char *filename, int flag);

打开一个动态库,将其加载到进程的虚拟地址空间,完成初始化

filename为0,将返回全局符号表的句柄

flag,RTLD_LAZY表示使用延迟绑定,RTLD_NOW表示立即绑定,RTLD_GLOBAL表示将模块的符号表合并到进程的全局符号表中

void * dlsym(void *handle, char *symbol);

查找符号的值

void * dlerror();

返回上一次调用是否成功,NULL表示成功,否则是错误消息

void dlclose(void *handle);

卸载模块,相应的引用计数减一,当引用计数为0时,才被真正地卸载掉。先执行”.fini“段的代码,然后将相应的符号从符号表中去除,取消进程空间跟模块的映射关系,然后关闭模块文件

演示程序书上的程序是有问题的,可以用下面的代码替换尝试)

#define SETUP_STACK \
i=2; \
while (++i < argc-1) { \
	switch (argv[i][0]) { \
		case 'i': \
			asm volatile ("push %%edx\n\t" \
				"sub $4,%%esp\n\t" \
				"mov %%ebx,(%%esp)\n\t" \
				"call atoi\n\t" \
				"add $4,%%esp\n\t" \
				"pop %%edx\n\t" \
				"mov %%eax,(%%esp,%%edx)":: \
				"b"(&argv[i][1]),"d"(esp)); \
			esp +=4; break;\
		case 'd': \
			asm volatile ("push %%edx\n\t" \
				"sub $4,%%esp\n\t" \
				"mov %%ebx,(%%esp)\n\t" \
				"call atof\n\t" \
				"add $4,%%esp\n\t" \
				"pop %%edx\n\t" \
				"fstpl (%%esp,%%edx)":: \
				"b"(&argv[i][1]),"d"(esp)); \
			esp +=8; break; \
		case 's': \
			asm volatile ("mov %%ebx,(%%esp,%%edx)":: \
				"b"(&argv[i][1]),"d"(esp)); \
			esp +=4; break; \
		default: \
			printf("Error argument type\n"); \
			goto exit_runso; \
	} \
}

#define RESTORE_STACK

2014年02月18日

Linux共享库的组织




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《程序员的自我修养—链接、装载与库》 的相关文章

  • 202318读书笔记|《芭蕉·芜村·一茶:俳句三圣新译300》——樱花——让一整个春夜亮起来!

    202318读书笔记 芭蕉 芜村 一茶 俳句三圣新译300 樱花 让一整个春夜亮起来 芭蕉 芜村 一茶 俳句三圣新译300 诗歌 词 短歌 俳句我都喜欢 读起来轻松明快 松尾芭蕉 与谢芜村 小林一茶并称三圣 芭蕉为俳句之圣 芜村 一茶居 至
  • 《国富论》笔记——货币

    上一个读书笔记 我简单发散思维到了货币 从以物易物到以贝壳作为货币举了一段例子 国富论 第四章就马上讲到了货币 并且补充了我很多未分析到的地方 货币的起源 当以物易物的时候 也许是因为我不再需要你的物品了 所以不再需要和你交换 你的东西只有
  • CDN架构

    CDN公司在整个互联网中部署了数以百计的节点 Cache服务器集群 这些Cache服务器都分布在各个网络运营商的IDC机房中 位置尽量靠近用户网络 CDN系统将内容从源站复制到各个节点 在内容提供者更新内容时 CDN系统将更新后的内容重新分
  • 【Java书笔记】:《Redis 深度历险:核心原理和应用实践》分布式锁,延时队列,位图,HyperLogLog,布隆过滤器,漏斗限流,GeoHash,Scan,管道,事务,主从,Redis源码

    Redis 深度历险 核心原理和应用实践 目 录 开篇 授人以鱼不若授人以渔 Redis 可以用来做什么 7 由 Redis 面试想到的 7 小册的内容范围 8 Redis 可以做什么 8 基础 万丈高楼平地起 Redis 基础数据结构 1
  • 指针基础(2)【数组与指针】

    文章目录 写在前面 1 数组概述 1 1 一维数组 1 2 二维数组 1 3 多维数组 1 4 小结 2 C 中 vector 容器 2 1 定义和初始化 vector 对象 2 2 向 vector 对象中增加元素 2 3 vector
  • Python深度学习-u4.1:分类和回归术语表

    分类和回归都包含很多专业术语 这些术语在机器学习领域都有确切的定义 本文对常见术语进行整理 样本 sample 或输入 input 进入模型的数据点 预测 prediction 或输出 output 从模型出来的结果 目标 target 真
  • 软件测试的艺术(2)代码走查,检查与评审

    人工测试 代码检查 走查以及可用性测试是三种主要的人工测试方法 这种人工测试方法有点像是若干个人员坐在一起开 头脑风暴会 也就是说 目的是为了找出错误 而不是调试 优点 1 一旦发现错误 就能在代码中对其进行精准的定位 降低了调试的成本 2
  • oracle表空间迁移transport_tablespace的使用

    问题描述 如果某个表空间有多个用户 那么我们在迁移的时候如果按用户导出导入的话 那将是一个麻烦费事的活 这个时候我们可以通过transport tablespace参数进行表空间迁移 达到一次性迁移整个表空间的目的 下面通过一个实验来讲解整
  • 读书笔记_《Linux高性能服务器编程》_第 5 章:网络编程基础API

    第 5 章 Linux网络编程基础API 知识要点 socket 地址 API socket 基础 API 网络信息 API 1 socket 地址API 主机字节序和网络字节序 CPU 32位 的累加器一次至少可以装载 4 字节 即一个整
  • LeetCode 101Pro

    LeetCode 101 本文档是对LeetCode101中题目的详细解释和扩展 文章目录 LeetCode 101 第一章 题目分类 第二章 最易懂的贪心算法 2 1 算法解释 2 2 分配问题 455 分发饼干 135 分发糖果 2 3
  • 读书笔记:《人工智能》

    读书笔记 摘自 人工智能 作者 李开复 王咏刚 第一章 人工智能来了 人工智能已经来了 而且它就在我们身边 几乎无处不在 人类 你好 不管我们是碳基人类还是硅基机器人 都没有本质的区别 我们中的每一员都应获得应有的尊重 每当前沿科技取得重大
  • 《曾国藩家书》读书手记(修身篇一)

    曾国藩被章太炎评价为 誉之则圣相 谳之则元凶 为什么有这样的评价呢 我们可以看出曾国藩这个人褒贬不一 不过毛和蒋对于曾国藩都是推崇备至 毛说过 吾近于人 独服于曾国藩 看来曾国藩还是有可取之处的 尤其是他的家书 很多人评价甚高 一 修身篇
  • wireshark数据包分析实战 读书笔记

    由头 永久链接 之前读了很多书籍 但是现在回顾的时候 很多内容仅仅是熟悉 而不是真正掌握 所以尝试一种新的方式 将读书时觉得比较重要的 或者是自己还不理解的东西记录下来 达到这本书我已经不需要再去翻 只要看笔记即可的效果 第一章 数据包分析
  • 202326读书笔记

    202326读书笔记 读给孩子的时令古词 冰肌绰约月朦胧 仿佛暗香浮动 竹杖芒鞋轻胜马 谁怕 一蓑烟雨任平生 料峭春风吹酒醒 微冷 山头斜照却相迎 春 雨水 惊蛰 春分 清明 谷雨 夏 小满 芒种 小暑 大暑 秋 处暑 白露 寒露 霜降 冬
  • 突破人生的瓶颈(心灵之灯)

    人生 四度 平时除了看新闻外我很少看电视 但是那天除外 那天晚上写完稿子 随意打开了一个频道 是央视三套的艺术人生 为何庆魁加油 我不喜欢主持人以煽情的方式 不停发掘主人公内心伤痛泪水的惯用风格 本想换台 考虑到剧作家何庆魁今年来家庭遇到的
  • 概念学习—机器学习

    概念学习 介绍 概念学习 假设的一般到特殊序 Find S 寻找极大特殊假设 变型空间和候选消除算法 表示 更简明的表示 关于变型空间和候选消除的说明 候选消除算法是否会收敛到正确的假设 归纳偏置 介绍 定义 概念学习是指从有关某个布尔函数
  • 《Android 开发艺术探索》笔记2--IPC机制

    Android 开发艺术探索 笔记2 IPC机制 思维导图 Android IPC简介 Android中的多进程的模式 IPC基础概念 Serializable接口 Parcelable接口 Android的几种跨进程的方式 使用Bundl
  • 《Java并发编程的艺术》知识点

    目录 一 并发编程挑战 1 上下文切换 2 死锁 二 并发机制底层实现原理 1 volatile原理 2 synchronized原理 3 原子类实现原理 CAS存在的三大问题 三 内存模型 1 指令重排 四 并发编程基础 1 概念 2 优
  • 工业数据的特殊性和安全防护体系探索思考

    随着工业互联网的发展 工业企业在生产运营管理过程中会产生各式各样数据 主要有研发设计数据 用户数据 生产运营数据 物流供应链数据等等 这样就形成了工业大数据 这些数据需要依赖企业的网络环境和应用系统进行内外部流通才能实现价值挖掘 如何高效安
  • 【华为数据之道学习笔记】5-9图模型设计

    图模型作为当前流行的信息处理加工技术 自提出以来 迅速在 学术界和工业界得到了普及 在智能推荐 决策分析等方面有着广泛的应用 图模型由节点和边组成 节点表示实体或概念 边则由属性或关 系构成 实体指的是具有可区别性且独立存在的某种事物 如某

随机推荐

  • 『学Vue2+Vue3』声明式导航

    day06 一 声明式导航 导航链接 1 需求 实现导航高亮效果 如果使用a标签进行跳转的话 需要给当前跳转的导航加样式 同时要移除上一个a标签的样式 太麻烦 2 解决方案 vue router 提供了一个全局组件 router link
  • ES6使用递归实现深拷贝

    使用递归实现深拷贝 let deepCopy function obj 判断拷贝的要进行深拷贝的是数组还是对象 是数组的话进行数组拷贝 对象的话进行对象拷贝 let newObj obj instanceof Array if obj ty
  • 安卓小游戏:俄罗斯方块

    安卓小游戏 俄罗斯方块 前言 最近用安卓自定义view写了下飞机大战 贪吃蛇 小板弹球三个游戏 还是比较简单的 这几天又把俄罗斯方块还原了一下 写了一天 又摸鱼调试了两天 逻辑不是很难 但是要理清 处理对还是有点东西的 需求 这里的需求玩过
  • python导入包失败ModuleNotFoundError: No module named 'matplotlib.pyplot'; 'matplotlib' is not a package

    最近在看 python 在使用matplotlib进行绘图时 提示 ModuleNotFoundError No module named matplotlib pyplot matplotlib is not a package 怎么回事
  • pytorch9-微调VGG16网络(基于kaggle数据集识别10种猴)

    import hiddenlayer as hl import torch import torch nn as nn from torch optim import SGD Adam import torch utils data as
  • 使用auth_request做权限控制

    网上很多如标题的文章 为何我还要写呢 网上一搜一大堆对我来说完全没用的文章 或者直接nginx官网复制过来 看得我贼无语 我的需求是校验参数是拼接到链接后面 而不是请求头的 网上大部分都是通过请求头来获取校验参数 开启auth reques
  • postman测试websocket接口(带鉴权)

    postman测试websocket接口 带鉴权 文章目录 postman测试websocket接口 带鉴权 1 前言 2 无用户鉴权情况下 3 有用户鉴权的情况下 4 最后 1 前言 目前开发实时日志推送 确认了两个方案 一个是通过tra
  • windows和linux下的c语言/网络/网络编程面试题收集[更新:2006.6.8]

    以下试题和答案均由 明天去要饭 从网上收集 仅供参考 如果发现错误或有更好的答案请回复 本人将对答案进行更新 基础部份 1 下列程序在32位linux或unix中的结果是什么 func char str printf d sizeof st
  • IDEA的DEBUG

    先认识一下DEBUG面板 先讲讲右侧的几个功能 12 Show Execution Point Alt F10 跳转到程序正在执行的地方 当浏览其他地方的代码后忘记程序执行到哪了或找起来麻烦时相当实用 13 Step Over F8 下一步
  • 多个组件的生命周期执行顺序

    多个组件的生命周期顺序 1 单个组件生命周期执行顺序 从官方文档上我们可以看出单个组件的生命周期顺序 beforeCreate created beforeMount mounted 2 多个组件的生命周期顺序 1 父组件与子组件 验证步骤
  • Linux中在Eclipse中远行tomcat失败

    Could not load the Tomcat server configuration 现象 启动tomcat时提示 Could not load the Tomcat server configuration at Servers
  • React使用antd实现可编辑单元格

    import React useContext useState useEffect useRef from react import Input Form from antd import Table from com Table imp
  • Flutter中的方法回调备忘

    类似于Android中的Callback iOS中的block 大致思路是一样的 需要自定义一个函数或者使用官方自己的也行 直接上代码 先写一个按钮点击事件 然后监听点击事件 备忘 onPress 和 onPress 特别需要注意 在wid
  • mysqld: Can‘t read dir of ‘/etc/mysql/conf.d/‘ (Errcode: 13 - Permission denied)

    今天用docker去运行mysql的时候 一直existing 输入 docker logs 镜像ID的时候发现报了mysqld Can t read dir of etc mysql conf d Errcode 13 Permissio
  • X-CSRF-Token

    Odata服务HTTP测试总是出现烦人的 CSRF token validation failed for all modifying requests 忽略下图中的报文错误 怀疑是服务器参数的设置问题 临时应急的话可以先针对这个服务把CS
  • Tars- zipkin环境本地搭建

    该图片引用于它处 https blog csdn net u012394095 article details 94389644 1 下载opentracing cpp 客户端调用的代码 网址为 opentraceing cpp 注意要下稳
  • 离散数学期末复习

    第一章 命题逻辑 联结词 蕴涵的注意事项 公式的层次 单个命题公式为0公式 等值演算的公式 范式 1 简单合取式 简单析取式 2 极小项 由简单合取式构成 m0 极大项 由简单析取式 构成 M0 奎因 莫可拉斯基方法求最简展开式 1 找极小
  • C语言笔记(二)

    基础 1 进制问题 1 1 二进制 1 2 ASCII 1 3 k进制转换为十进制 1 4 十进制转换为k进制 2 输入输出 3 逻辑运算符 4 运算符优先级 5 switch分支语句 6 字符串查找strchr函数 1 进制问题 1 1
  • 工作和生活中,如何用项目管理思维解决复杂的事情?

    在工作和生活中 许多事情都可以采用项目思维方式来解决 当我们逐渐将工作和生活中的各种事务以项目的方式来处理和推进时 我们可能并没有意识到 实际上我们正在运用项目管理思维 项目管理思维能帮助我们在面对繁杂事务时 理清思路 考虑周全 明确行动
  • 《程序员的自我修养—链接、装载与库》

    程序员的自我修养 链接 装载与库 读书笔记 本文为记录笔记 大部分内容为书中的摘抄 作者微博 MTK 蛙蛙鱼 写作时间 2013年11月18日 更新时间 2014年02月18日 编译和链接 2 1 被隐藏了的过程 预编译 cpp or gc