1. Segment 是从装载的角度重新划分了 ELF 的各个段,在将目标文件链接成可执行文件的时候,链接器会尽量把相同权限属性的段分配在同一空间。比如可读可执行的段都放一起,如代码段。系统正是按 Segment 而不是 Section 来映射可执行文件的。总的来说,“Segment” 和 “Section” 是从执行视图与链接视图来划分同一个 ELF 文件。

  2. ELF 可执行文件中有一个专门的数据结构叫做程序头表 (Program Header Table)用来保存 “Segment” 的信息,由于 ELF 目标文件不需要被装载,所以没有程序头表,而 ELF 的可执行文件和共享库文件都有。在数据段里面建立一个指向全局变量的指针数组,称为全局偏移表(Global Offset Table, GOT)当代码需要引用该全局变量时,可能通过 GOT 中相对应的项间接引用。

  3. 地址无关代码技术(PIC,Position-independent Code): 把指令中那些需要修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以在每个进程中拥有一个副本。延迟绑定(Lazy Binding): 就是当函数第一次被用到时才进行绑定(符号查找、重定位等),如果没有用到则不进行绑定。所以程序开始执行时,模块间的函数调用都没有进行绑定,而是需要用到时才由动态链接器来负责绑定,这样的做法可以大大加快程序的启动速度,特别有利于一些有大量函数引用和大量模块的程序。

  4. 动态链接比静态链接慢的主要原因是:1.动态链接下对于全局和静态的数据访问都要进行复杂的 GOT 定位,然后间接寻址;对于模块间的调用也要先定位 GOT, 然后再进行间接跳转。2.动态链接的链接工作在运行时完成,即程序开始执行时,动态链接器都要进行一次链接工作,其会寻找并装载所需要的共享对象,然后进行符号查找地址重定位等工作,这些工作势必会减慢程序的启动速度。

  5. ELF 将 GOT 拆分成了两个表叫做 “.got” 和 “.got.plt”。其中 “.got” 用来保存全局变量引用的地址,".got.plt" 用来保存函数引用的地址,也就是所有对于外部函数的引用全部被分离出来放到了 “.got.plt” 中。另外 “.got.plt” 还有一个特殊的地方是它的前三项是有特殊意义的,分别含义是:第一项保存的是".dynamic" 段的地址,这个段描述了本模块动态链接相关的信息;第二项保存的是本模块的 ID; 第三项保存的是 __dl_runtime_resolve() 的地址。其中后面两项由动态链接器在装载共享模块的时候负责将它们初始化。

  6. 动态链接情况下,可执行文件的装载与静态链接情况基本一样。首先操作系统会读取可执行文件的头部,检查文件的合法性,然后从头部的 “Program Header” 中读取每个 “Segment” 的虚拟地址、文件地址和属性,并将它们映射到进程虚拟空间的相应位置,这些步聚与静态链接基本无异,在静态链接情况下,操作系统接着就可以把控制权转给可执行文件的入口地址,然后程序开始执行。但是在动态链接情况下,由于可执行文件里对于很多外部符号的引用还处于无效地址的状态,即还没有跟相应的共享对象中的实际位置链接起来。所以在映射完可执行文件之后,操作系统会先启动一个动态链接器(Dynamic Linker)。

  7. 在 Linux 下,操作系统在加载完动态链接器 ld.so 之后,就将控制权交给动态链接器的入口地址,当动态链接器得到控制权之后,它开始执行一系列自身的初始化操作,然后根据当前的环境参数,开始对可执行文件进行动态链接工作。当所有动态链接工作完成以后,动态链接器会将控制权转交到可执行文件的入口地址,程序开始正执行。

  8. 动态链接 ELF 中的 “.dynamic” 段,这个段里面保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等。动态链接的文件中,也有类似的重定位表分别叫做 “.rel.dyn” 和 “.rel.plt”。其中 “.rel.dyn” 实际上是对数据引用的修正,它修正的位置位于 “.got” 以及数据段;而 “.rel.plt” 是对函数引用的修正,它所修正的位置位于 “.got.plt”。

  9. 动态链接的自举(Bootstrap)代码满足两个条件:(1)其本身不依赖于其他任何共享对象 (2)其本身需要的全局和静态变量的重定位工作由它本身完成。动态库的装载主要是通过: dlopen、dlsym、dlerror、dlclose 这 4 个函数来完成的。

  10. dlopen 第一个参数 filename 如果是相对路径,那么由以下三个顺序去查找该动态库文件:(1)查找由环境变量 LD_LIBRARY_PATH 指定一系列目录 (2)查找由 /etc/ld.so.cache 里面所指定的共享库路径。 (3)/lib、/usr/lib。如果将第一个参数 filename 设置为 0,那么 dlopen 返回的将是全局符号表的句柄,其中这全局符号表包括了程序的可执行文件本身、被动态链接器加载到进程中的所有共享模块以及在运行时通过 dlopen 打开并且使用了 RTLD_GLOBAL 方式的模块中的符号。

  11. dlopen 的第二个参数,表示函数符号的解析方式,常量 RTLD_LAZY 表示使用延迟绑定,当函数第一次被用到时才进行绑定,即 PLT 机制;而 RTLD_GLOBAL 它表示将被加载的模块的全局符号合并到进程的全局符号表中,使得以后加载的模块可以使用这些符号。dlopen 的加载过程基本跟动态链接器一致,在完成装载,映射和重定位以后,就会执行 “.init” 段的代码然后返回。

  12. dlerror 如果返回 NULL , 则表示上一次调用成功;如果不是,则返回相应的错误的信息。dlclose 它的作用就是将一个已经加载的模块卸载,系统会维持一个加载引用计数器,每次使用 dlopen 加载某模块时,相应的计数器加一;每次使用 dlclose 卸载某模块时,相应计数器减一。只有当计算器值减到 0 时,模块才被真正地卸载掉。卸载的过程跟加载刚好相反,先执行 “.finit” 段的代码,然后将相应的符号从符号表中去除,取消进程空间跟模块的映射关系,然后关闭模块文件。

  13. libname.so.x.y.z,其中 x 表示主版本号,y 表示次版本号,z 表示发布版本号。总体来说,/lib 和 /usr/lib 是一些很常用的、成熟的,一般是系统本身所需要的库; 而 /usr/local/lib 是非系统所需的第三方程序的共享库。任务一个动态链接的模块所依赖的模块路径保存在 “.dynamic” 段里面,由 DT_NEED 类型的项表示。动态链接器对于模块的查找有一定的规则:如果 DT_NEED 里面保存的是绝对路径,那么动态链接器就按照这个路径去查找;如果 DT_NEED 里面保存的是相对路径,那么动态链接器会在 /lib、/usr/lib 和由 /etc/ld.so.conf 配置文件指定的目录中查找共享库。为了程序的可移植性和兼容性,共享库的路径往往是相对的。

  14. LD_LIBRARY_PATH 环境变量,可以临时改变这个应用程序的共享库查找路径,而不会影响系统中的其他程序。在 LD_PRELOAD 里面指定的文件会在动态链接器按照固定规则搜索共享库之前装载,它比 LD_LIBRARY_PATH 里面所指定的目录中的共享库还要优先。无论程序是否依赖于它们,LD_PRELOAD 里面指定的共享库或目标文件都会被装载。

  15. 由于全局符号介入这个机制的存在,LD_PRELOAD 里面指定的共享库或目标文件中的全局符号就会覆盖后面加载的同名全局符号,这使得我们可以很方便地做到改写标准 C 库中的某个或某几个函数而不影响其他函数,对于程序的调试或测试非常有用。

  16. strip 工具可以清除掉共享库或可执行文件的所有符号和调试信息。程序的环境由内存、运行库、系统调用三个部分组成,其中系统调用充当了程序与内核交互的中介。Linux 默认情况下将高地址的 1GB 空间分配给内核。栈,用于维护函数调用的上下文,离开了栈函数调用就没有实现;堆,用来容纳应用程序动态分配的内存区域;可执行文件映像,由装载器在装载时将可执行文件的内存读取或映射到这里;保留区,是对内存中受到保护而禁止访问的内存区域的总称。

  17. 在经典的操作系统里,栈向低地址增长,堆向高地址增长。压栈的操作使栈顶的地址减少,弹出的操作使栈顶地址增大。malloc 申请的内存,当进和结束以后就不会存在了。

程序员自我修养-总结 (2)相关推荐

  1. 《程序员自我修养》第七章读书笔记

    书还是接上回,本篇主要对第七章的相关内容进行总结.第七章主要对动态链接的相关内容进行分析. 7.1 为什么要动态链接 既然要对动态链接进行分析,首先应对动态链接出现的原因进行一个简单的分析.动态链接从 ...

  2. 程序员自我修养之链接

    我最近在看PE文件,稍后可能需要dll这些所以顺带看看链接.太久不看这些书,你问我链接是干什么的,我可能会说就是分模块时候用啊,因为一个项目有很多模块,不能写在同一个文件下,所以要把它们链接起来,链接 ...

  3. 程序员自我修养》系统调用与API

    什么是系统调用 在现代的操作系统里,程序运行的时候,本身是没有权利访问多少系统资源的.由于系统有限的资源有可能被多个不同的应用程序同时访问,因此,如果不加以保护,那么各个应用程序难免产生冲突.所以现代 ...

  4. 一个Java工程师的自我修养_程序员自我修养

    毕业N年,每个人在能力跑道上,有了或大或小的差距.有些人一直在重复的劳动,有些人却能从中总结和解决问题.通过成长日活动,我们或许可以探讨下,怎样共同成长.共同前行,跟"勤奋战术掩盖下的战略懒 ...

  5. 程序员自我修养-总结 (1)

    你可以不自己造轮子,但应该了解轮子的构造,而且越详尽越好,这就是程序员的自我修养吧.虽然我在这个系统上花费了很多时间和精力,却没有获得什么直接的收益,也没有让我跟上最新的技术潮流,但是它带给我的间接收 ...

  6. 程序员自我修养之长篇连载

    本文是本人读<程序员的自我修养>一书,夹杂本人理解与感受写成的长篇连载,主要以本书节奏为主线,不保证一定与该书章节前后顺序一致.不定期更新... 开篇 开篇打算从一个C 语言版本的hell ...

  7. 程序员自我修养的那些事儿

    本篇博客主要是:程序员的自我修养-链接.装载与库的一些学习笔记与心得. 1.首先得注意最大的坑:这本书基于32位系统讲解,因此跟着书本的例子操作一遍的时候,记得要在32位的系统上进行,否则可能出现以下 ...

  8. 最牛逼程序员自我修养反观认识运动路-中国职场江湖的人情世故--喝酒应酬

    在中国做程序员,以为光靠技术,人情世故也还是有的呢! 喝酒,整的休息不好,喝茶叶解酒,都还感觉到有点昏沉.实在影响做事啊! 然后,员工内心真是想法是什么? 老板一个月给您一个月发个三五千多块钱,并没有 ...

  9. 程序员自我修养阅读笔记——运行库

    主要关注程序的启动过程. 1 入口函数和程序初始化 1.1 程序真正的入口   通常写代码时,我们认为程序的入口是main函数,但是实际上有一些现象值得我们怀疑该结论是不是正确的.比如全局变量的初始化 ...

  10. 菜鸟程序员自我修养心得

    题记 一个菜鸟码农,初入职场,涉世未深,但感触颇多,或许这是菜鸟们的通病把,毕竟现实和理想是有差距的. 不放弃 或许是被繁琐的工作折腾的已精力殆尽,或许是寒冬已来,人自然就懒了,每每回家都很累,念想最 ...

最新文章

  1. python图表交互控件_用djang中的交互式控件制作bokeh图表
  2. 人脸检测--SSH: Single Stage Headless Face Detector
  3. IVs提取合并工具ivstools
  4. Android踩坑日记:使用Fesco图片加载库在GridView上的卡顿优化
  5. 杀入共享汽车市场的PonyCar,是下一个牺牲者还是引领者?
  6. 增强优化JavaScript性能的方法 - 技巧大全
  7. hibernate系列之四
  8. 阶乘之和计算_浅谈积分计算的技巧
  9. 高精度练习(hdoj1042)
  10. Tomcat漏洞修复方法【补丁下载及安装详细流程】
  11. 稀土储量由80%变成了35%?这是何等的。。。
  12. C# 读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC...
  13. Ubuntu阿里源镜像
  14. 数据库查询条件优化方案
  15. 成都盛铭轩:做好主图要从这些方面做
  16. 极智装修知识|飘窗五大改造妙招,让你只想窝在这
  17. MySQL中怎么对varchar类型排序问题(数字字符串和汉字拼音的顺序)
  18. linux 子接口 非vlan,VLAN之间通过子接口通信配置示例
  19. SVN异常处理——禁止访问
  20. 骚操作,VSCode上发布知乎

热门文章

  1. 服务器机房监控系统研究,机房环境及服务器运行状态的嵌入式监控系统设计
  2. 微信小程序应用”腾讯位置服务路线规划“插件
  3. springboot初学解决Result Maps collection does not contain value for ...问题
  4. 强化学习系列文章(二十七):VPG+Beta分布在CartPoleContinuous环境中的应用
  5. Springboot中的bug①
  6. matlab/simulink自适应控制轮毂电机驱动的电动汽车主动悬架构型与控制
  7. springboot中spring.jpa.hibernate.ddl-auto四个属性的含义
  8. 值得一看!Shopee营销节日,把握爆单时机
  9. Java之jsp标签
  10. pandas使用方法说明