符号表

Linux的ELF文件

社会主义新天地 提交于 2019-11-27 22:35:20
一、文件头: sizeof(Elf32_Ehdr)=52 e_type: 类型---可重定位文件.o、可执行文件、共享目标文件.so e_ehsize: 文件头大小---52 e_machine: CPU平台属性,如Intel_x86; e_entry: 入口虚拟地址,但是可重定位文件没有; e_shoff: 段表偏移量, 即在ELF文件的位置;下面两个11*40=440表达段表长度 e_shnum: 段表描述符数量,即段表中段的个数:11 e_shentsize: 段表描述符大小,sizeof(Elf32_Shdr)=40 e_shstrndx: 段表字符串表所在的段在段表中的下标; 二、段表: sizeof(Elf32_Shdr)=40 sh_name: 段名,如.test/.data/.bss/.rodata/.comment, .rel.text/.rel.data, .symtab, .strtab/.shstrtab, sh_type: 类型---程序段、重定位表、符号表、字符串表 sh_flags: 标志位---可写、可执行、分配空间 sh_addr: 加载后在地址空间中的虚拟地址 sh_offset: 段偏移量,即该段该文件中的地址!!!!!!!!!!!!!!!!! sh_size: 段大小 sh_link: 符号表的下标 sh_info: 作用于哪个段? 三

从零写一个编译器(八):语义分析之构造符号表

你说的曾经没有我的故事 提交于 2019-11-27 20:43:18
项目的完整代码在 C2j-Compiler 前言 在之前完成了描述符号表的数据结构,现在就可以正式构造符号表了。符号表的创建自然是要根据语法分析过程中走的,所以符号表的创建就在LRStateTableParser里的takeActionForReduce方法 不过在此之前,当然还需要一个方便对这个符号表操作的类了 这一篇主要的两个文件是 TypeSystem.java LRStateTableParser.java 操作符号表 操作符号表的方法都在TypeSystem类里。 TypeSystem里主要有这几个方法: 类型说明符 逻辑都很简单 public TypeLink newType(String typeText) { Specifier sp; int type = Specifier.NONE; boolean isLong = false, isSigned = true; switch (typeText.charAt(0)) { case 'c': if (typeText.charAt(1) == 'h') { type = Specifier.CHAR; } break; case 'd': case 'f': System.err.println("Floating point Numbers are not supported"); System.exit

JIT晚期(运行期)

不羁的心 提交于 2019-11-27 07:35:43
1. Java的编译和执行 编译 包括两种情况: 1,源码编译成字节码 2,字节码编译成本地机器码(符合本地系统专属的指令) 解释执行 也包括两种情况: 1,源码解释执行 2,字节码解释执行 解释和编译执行的区别是: 是否产生中间本地机器码。 即时编译生成机器相关的中间码,可重复执行缓存效率高。解释执行直接执行字节码,重复执行需要重复解释。 2. 编译原理 在执行前先对程序源码进行词法解析和语法解析处理,把源码转化为抽象语法树。 其中绿色的模块可以选择性实现。 上图中间的那条分支是解释执行的过程(即一条字节码一条字节码地解释执行,如JavaScript), 而下面的那条分支就是传统编译原理中从源代码到目标机器代码的生成过程。 对于一门具体语言的实现来说: 词法和语法分析乃至后面的优化器和目标代码生成器都可以选择独立于执行引擎,形成一个完整意义的编译器去实现,这类代表是C/C++语言。 也可以把抽象语法树或指令流之前的步骤实现一个半独立的编译器,这类代表是Java语言。 又或者可以把这些步骤和执行引擎全部集中在一起实现,如大多数的JavaScript执行器。 3. 三个编译器 JVM的编译器可以分为三个编译器: ( 1) 前端编译器 : 把.java转变为.class的过程 。如Sun的 Javac 、Eclipse JDT中的增量式编译器( ECJ )。 ( 2) 后端编译器

链接原理

房东的猫 提交于 2019-11-27 04:16:36
本文简单介绍了程序的链接原理。学习链接原理有助于程序员理解程序的本质,同时也可以为日后的大型软件的代码开发打下坚实的基础。由此可知链接原理的重要性,尤其是一些程序员被一些莫名其妙的错误困扰的时候,更加能够体会到这一点。 1 连接器的任务 连接器将多个目标文件链接成一个完整的、可加载、可执行的目标文件。其输入是一组可重定位的目标文件。链接的两个主要任务如下: (1) 符号解析,将目标文件内的引用符号和该符号的定义联系起来。 (2) 将符号定义与存储器的位置联系起来,修改对这些符号的引用。 2 目标文件 典型的目标文件分为以下3种形式: (1) 可重定位目标文件 这种文件包含二进制代码和数据,这些代码和数据已经转换成了机器指令代码和数据,但是还不可以直接执行。因为这些指令和数据中往往引用其他模块(目标文件)中的符号,这些其他模块的符号对于本模块来说是未知的,这些符号的解析需要链接器将所有模块进行链接。这种操作称为“重定位”,因此,这种目标文件被称为“可重定位的目标文件”,后缀名通常为*.o (2) 可执行目标文件 这种文件同样包含了二进制代码和数据。所不同的是,这种文件已经经过了链接操作,和所有的模块(目标文件)都产生了联系。链接器将所有需要的可重定位目标文件连接成一个可执行目标文件。这时,每个目标文件中引用其他目标文件中的符号都已经得到了解析和重定位。因此,每个符号都是已知的了

linux-coredump

百般思念 提交于 2019-11-26 05:29:39
Core,又称之为Core Dump文件,是Unix/Linux操作系统的一种机制,对于线上服务而言,Core令人闻之色变,因为出Core的过程意味着服务暂时不能正常响应,需要恢复,并且随着吐Core进程的内存空间越大,此过程可能持续很长一段时间(例如当进程占用60G+以上内存时,完整Core文件需要15分钟才能完全写到磁盘上),这期间产生的流量损失,不可估量。 凡事皆有两面性,OS在出Core的同时,虽然会终止掉当前进程,但是也会保留下第一手的现场数据,OS仿佛是一架被按下快门的相机,而照片就是产出的Core文件。里面含有当进程被终止时内存、CPU寄存器等信息,可以供后续开发人员进行调试。 关于Core产生的原因很多,比如过去一些Unix的版本不支持现代Linux上这种GDB直接附着到进程上进行调试的机制,需要先向进程发送终止信号,然后用工具阅读core文件。在Linux上,我们就可以使用kill向一个指定的进程发送信号或者使用gcore命令来使其主动出Core并退出。如果从浅层次的原因上来讲,出Core意味着当前进程存在BUG,需要程序员修复。从深层次的原因上讲,是当前进程触犯了某些OS层级的保护机制,逼迫OS向当前进程发送诸如SIGSEGV(即signal 11)之类的信号, 例如访问空指针或数组越界出Core,实际上是触犯了OS的内存管理,访问了非当前进程的内存空间

Linux下显示运行时链接(运行时加载)

那年仲夏 提交于 2019-11-25 19:25:53
目录 介绍 如何加载动态库 dlopen() 第一个参数: 被加载动态库的路径 第二个参数: flag表示函数符号的解析方式 dlopen 返回值 dlsym() 参数: 返回值 符号优先级 dlerror() dlclose() show code 内容学习自《 程序员的自我修养 链接装载与库》 如果只是想知道如何使用如何加载动态库和那4个函数的使用,可以直接从如何加载动态库开始看。 介绍 支持动态链接的系统往往都支持一种更加灵活的模块加载方式,叫做显式运行时链接(Explicit Run-time Linking),有时候也叫做运行时加载。也就是让程序自己在运行时控制加载指定的模块,并且可以在不需要该模块时将其卸载。从前面我们了解到的来看,如果动态链接器可以在运行时将共享模块装载进内存并且可以进行重定位等操作,那么这种运行时加载在理论上也是很容易实现的。而且一般的共享对象不需要进行任何修改就可以进行运行时装载,这种共享对象往往被叫做动态装载库(Dynamic Loading Library),其实本质上它跟一般的共享对象没什么区别,只是程序开发者使用它的角度不同。 这种运行时加载使得程序的模块组织变得很灵活,可以用来实现一些诸如插件、驱动等功能。当程序需要用到某个插件或者驱动的时候,才将相应的模块装载进来,而不需要从一开始就将他们全部装载进来,从而减少了程序启动时间和内存使用