精华内容
下载资源
问答
  • linux中,查看符号表的方法
    2021-05-13 17:06:28

    //读取ELF header

    $readelf -Wh a.out

    Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

    Class: ELF32

    Data: 2's complement, little endian

    Version: 1 (current)

    OS/ABI: UNIX - System V

    ABI Version: 0

    Type: EXEC (Executable file)

    Machine: Intel 80386

    Version: 0x1

    Entry point address: 0x8048320

    Start of program headers: 52 (bytes into file)

    Start of section headers: 4436 (bytes into file)

    Flags: 0x0

    Size of this header: 52 (bytes)

    Size of program headers: 32 (bytes)

    Number of program headers: 9

    Size of section headers: 40 (bytes)

    Number of section headers: 30

    Section header string table index: 27

    //读取section header

    $readelf -Ws a.out

    //读取program header

    $readelf -Wl a.out

    更多相关内容
  • strip经常用来去除目标文件中的一些符号、调试符号表信息...strip -h用法:strip 输入文件从文件中删除符号和节选项为:-I --input-target= Assume input file is in format -O --output-target= Create an outpu...

    strip经常用来去除目标文件中的一些符号表、调试符号表信息,以减小程序的大小,在rpmbuild包的最后就用到。

    其支持的选项如下:

    >strip -h

    用法:strip 输入文件

    从文件中删除符号和节

    选项为:

    -I --input-target=      Assume input file is in format

    -O --output-target=     Create an output file in format

    -F --target=            Set both input and output format to

    -p --preserve-dates              Copy modified/access timestamps to the output

    -R --remove-section=       Remove section from the output

    -s --strip-all                   Remove all symbol and relocation information

    -g -S -d --strip-debug           Remove all debugging symbols & sections

    --strip-unneeded              Remove all symbols not needed by relocations

    --only-keep-debug             Strip everything but the debug information

    -N --strip-symbol=         Do not copy symbol

    -K --keep-symbol=          Do not strip symbol

    --keep-file-symbols           Do not strip file symbol(s)

    -w --wildcard                    Permit wildcard in symbol comparison

    -x --discard-all                 Remove all non-global symbols

    -X --discard-locals              Remove any compiler-generated symbols

    -v --verbose                     List all object files modified

    -V --version                     Display this program's version number

    -h --help                        Display this output

    --info                        List object formats & architectures supported

    -o                         Place stripped output into

    strip:支持的目标: elf32-i386 a.out-i386-linux efi-app-ia32 elf32-little elf32-big elf64-alpha ecoff-littlealpha elf64-little elf64-big elf32-littlearm elf32-bigarm elf32-hppa-linux elf32-hppa elf64-ia64-little elf64-ia64-big efi-app-ia64 elf32-m68k a.out-m68k-linux elf32-powerpc aixcoff-rs6000 elf32-powerpcle ppcboot elf64-powerpc elf64-powerpcle aixcoff64-rs6000 elf32-s390 elf64-s390 elf32-sparc a.out-sparc-linux elf64-sparc a.out-sunos-big elf64-x86-64 pe-i386 pei-i386 srec symbolsrec tekhex binary ihex trad-core

    目标文件分为:可重定位文件、可执行文件、共享文件

    strip的默认选项会去除.symbol节的内容以及.debug节的内容,因此尽量只对可执行文件执行strip而不要对静态库或动态库等目标文件strip。

    测试代码如下:

    /* max.c */int max(int val1, int val2){  int iVal = (val1 > val2) ? val1 : val2;  return iVal;}

    /* min.c */int min(int val1, int val2){  int iVal = (val1 < val2) ? val1 : val2;  return iVal;}

    /* main.c */#include extern int max(int val1, int val2);extern int min(int val1, int val2);int main(){   int val1, val2;   scanf("%d  %d", &val1, &val2);   printf("%d\n", max(val1, val2));   printf("%d\n", min(val1, val2));}

    >gcc -c max.c min.c

    >ar rcs libcmp.a max.o min.o

    >gcc -o test main.c libcmp.a

    >gcc -share -fPIC -o libcmp.so max.c min.c

    >cp libcmp.a libcmp.a.bak

    >cp libcmp.so libcmp.so.bak

    >cp test test.orig

    >strip libcmp.a libcmp.so

    >strip test

    >ll -h

    总计 92K

    -rwxr-xr-x 1 6.9K a.out

    -rw-r--r-- 1 1.1K libcmp.a

    -rw-r--r-- 1 1.6K libcmp.a.bak

    -rwxr-xr-x 1 2.9K libcmp.so

    -rwxr-xr-x 1 5.3K libcmp.so.bak

    -rw-r--r-- 1  237 main.c

    -rw-r--r-- 1   89 max.c

    -rw-r--r-- 1  695 max.o

    -rw-r--r-- 1   89 min.c

    -rw-r--r-- 1  695 min.o

    -rwxr-xr-x 1 3.2K test

    -rwxr-xr-x 1 6.8K test.orig

    选项简释:

    The -fPIC flag directs the compiler to generate position independent code section).

    The -shared flag directs the linker to create a shared object file.

    可见无论是静态库(libcmp.a)还是动态库(libcmp.so)还是可执行文件(test),去掉一些符号信息后都减小了很多,

    但如果这时再链接这两个库的话是编不过的,因此,如果不是指定特殊的strip选项的话,还是尽量不要对库文件strip,

    只对链接后的可执行文件strip就可以了(如果也不调试)。

    展开全文
  • C/C++编译和链接过程详解 (重定向,导出符号表,未解决符号表) 详解link 有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ......

    /*********************************************************************

    * Author  : Samson

    * Date    : 01/30/2015

    * Test platform:

    *              3.13.0-24-generic

    *              GNU bash, 4.3.11(1)-release

    * *******************************************************************/

    很多时候,发行版的程序在编译的时候都是没有加上-g这个选项的,那么若是想调试一个程序,应该怎么办呢?

    在加了-g选项时,是可以通过行号、函数名等进行断点的设置的,但是没有符号表的情况下,那么怎么来进行程序的断点的设置并进行调试呢?

    这就要用到反汇编然后再对地址进行断点的设置来进行调试,具体情况可参看以下例子的过程:

    test.c代码如下:

    #include

    #include

    int main()

    {

    int m = 0, n =9;

    int k = m+n;

    printf("k is %d\n", k);

    m = k + n;

    printf("m is %d\n", m);

    n = m-n;

    printf("n is %d\n", n);

    return 0;

    }

    使用不带-g参数的编译命令行进行编译:

    linuxidc@linuxidc:~$ gcc test.c

    使用gdb进行程序的调试:

    linuxidc@linuxidc:~$ gdb a.out

    GNU gdb (GDB) 7.5.91.20130417-cvs-Ubuntu

    Copyright (C) 2013 Free Software Foundation, Inc.

    License GPLv3+: GNU GPL version 3 or later

    This is free software: you are free to change and redistribute it.

    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

    and "show warranty" for details.

    This GDB was configured as "i686-linux-gnu".

    For bug reporting instructions, please see:

    ...

    Reading symbols from a.out...(no debugging symbols found)...done.

    (gdb) l

    没有符号表被读取。请使用 "file" 命令。

    (gdb) disassemble main

    Dump of assembler code for function main:

    0x0804841c :    push  %ebp

    0x0804841d :    mov    %esp,%ebp

    0x0804841f :    and    $0xfffffff0,%esp

    0x08048422 :    sub    $0x20,%esp

    0x08048425 :    movl  $0x0,0x14(%esp)

    0x0804842d :    movl  $0x9,0x18(%esp)

    0x08048435 :    mov    0x18(%esp),%eax

    0x08048439 :    mov    0x14(%esp),%edx

    0x0804843d :    add    %edx,%eax

    0x0804843f :    mov    %eax,0x1c(%esp)

    0x08048443 :    mov    0x1c(%esp),%eax

    0x08048447 :    mov    %eax,0x4(%esp)

    0x0804844b :    movl  $0x8048540,(%esp)

    0x08048452 :    call  0x80482f0

    0x08048457 :    mov    0x18(%esp),%eax

    0x0804845b :    mov    0x1c(%esp),%edx

    0x0804845f :    add    %edx,%eax

    0x08048461 :    mov    %eax,0x14(%esp)

    0x08048465 :    mov    0x14(%esp),%eax

    0x08048469 :    mov    %eax,0x4(%esp)

    0x0804846d :    movl  $0x8048549,(%esp)

    0x08048474 :    call  0x80482f0

    0x08048479 :    mov    0x18(%esp),%eax

    0x0804847d :    mov    0x14(%esp),%edx

    0x08048481 :    mov    %edx,%ecx

    0x08048483 :    sub    %eax,%ecx

    0x08048485 :    mov    %ecx,%eax

    0x08048487 :    mov    %eax,0x18(%esp)

    0x0804848b :    mov    0x18(%esp),%eax

    0x0804848f :    mov    %eax,0x4(%esp)

    0x08048493 :    movl  $0x8048552,(%esp)

    0x0804849a :    call  0x80482f0

    0x0804849f :    mov    $0x0,%eax

    0x080484a4 :    leave

    0x080484a5 :    ret

    End of assembler dump.

    (gdb) b *0x08048452

    Breakpoint 1 at 0x8048452

    (gdb) b *0x08048474

    Breakpoint 2 at 0x8048474

    (gdb) b *0x0804849a

    Breakpoint 3 at 0x804849a

    (gdb) info b

    Num    Type          Disp Enb Address    What

    1      breakpoint    keep y  0x08048452

    2      breakpoint    keep y  0x08048474

    3      breakpoint    keep y  0x0804849a

    (gdb) r

    Starting program: a.out

    Breakpoint 1, 0x08048452 in main ()

    (gdb) c

    Continuing.

    k is 9

    Breakpoint 2, 0x08048474 in main ()

    (gdb) c

    Continuing.

    m is 18

    Breakpoint 3, 0x0804849a in main ()

    (gdb) c

    Continuing.

    n is 9

    [Inferior 1 (process 19933) exited normally]

    由以上的步骤可以看出,使用了disassemble main 进行主函数的反汇编,然后使用了b *address进行三处printf的地址的断点的设置。

    GDB 的详细介绍:请点这里

    GDB的下载地址:请点这里

    使用GDB调试将符号表与程序分离后的可执行文件

    环境: Centos7.3.GCC4.8.5 适用场景: 由于调试信息比较大,通常将程序分离为可执行程序和符号信息文件,只对外发布可执行程序,需要调试时再将符号信息文件附加. 一.创建可执行程序: u ...

    ELF格式文件符号表全解析及readelf命令使用方法

    http://blog.csdn.net/edonlii/article/details/8779075 1. 读取ELF文件头: $ readelf -h signELF Header:  Magi ...

    C&sol;C&plus;&plus;编译和链接过程详解 &lpar;重定向表,导出符号表,未解决符号表&rpar;

    详解link  有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...

    (转载) C&sol;C&plus;&plus;编译和链接过程详解 &lpar;重定向表,导出符号表,未解决符号表&rpar;

    转载http://blog.csdn.net/neo_ustc/article/details/9024839 有 些人写C/C++(以下假定为C++)程序,对unresolved external ...

    【转】gdb的调试与使用

    转载自:https://www.jianshu.com/p/7a06b0bda2d8 gdb的调试与使用 这篇应该是我见过的总结最详细的gdb调试指南了,这位博主是个很强的人,他的博客对萌新比较友好, ...

    程序减肥,strip,eu-strip 及其符号表

    程序减肥,strip,eu-strip 及其符号表 我们要给我们生成的可执行文件和DSO瘦身,因为这样可以节省更多的磁盘空间,所以我们移除了debug信息,移除了符号表信息,同时我们还希望万一出事了, ...

    VxWorks 符号表

    符号表初始化           符号表用于建立符号名称.类型和值之间的关系.其中,名称为null结尾的任意字符串:类型为标识各种符号的整数:值为一个字符指针.符号表主要用来作为目标模块加载的基础,但 ...

    转: iOS崩溃堆栈符号表使用与用途

    转:http://bugly.qq.com/blog/?p=119 iOS崩溃堆栈符号化,定位问题分分钟搞定! 2015.3.16 腾讯Bugly 微信分享   最近一段时间,在跟开发者沟通过程中,萝 ...

    嵌入式arm linux环境中gdb&plus;gdbserver调试

    一.前言嵌入式Linux系统中,应用开发过程中,很多情况下,用户需要对一个应用程序进行反复调试,特别是复杂的程序.采用GDB方法调试,由于嵌入式系统资源有限性,一般不能直接在目标系统上进行调试,通常采 ...

    随机推荐

    NodeJs 开发微信公众号(四)微信网页授权

    微信的网页授权指的是在微信公众号中访问第三方网页时获取用户地理.个人等信息的权限.对于开发了自己的网页app应用时,获取个人的信息非常重要.上篇博客讲到了注册时可以获取用户的信息,很多人会问为什么还需 ...

    Asp&period;Net 自定义控件实现图片的上传,浏览,删除功能

    4月的时候公司比较闲,就想着自己做点东西,其实主要是为了更加熟悉.Net,毕竟接触的时间不长,趁着有时间想提高提高.不过当我做到图片上传这个功能的时候,就有些停滞不前了,连续写了两天也达不到自己想要的 ...

    ApiDemo&sol;FragmentRetainInstance 解析

    /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Versi ...

    &lbrack;转载&rsqb;AOP面向方面编程

    1.引言 软件开发的目标是要对世界的部分元素或者信息流建立模型,实现软件系统的工程需要将系统分解成可以创建和管理的模块.于是出现了以系统模块化特性的面向对象程序设计技术.模块化的面向对象编程极度极地提 ...

    最受欢迎的5个Android ORM框架

    在开发Android应用时,保存数据有这么几个方式, 一个是本地保存,一个是放在后台(提供API接口),还有一个是放在开放云服务上(如 SyncAdapter 会是一个不错的选择). 对于第一种方式, ...

    PAT乙1003

    这次终于觉得智商不够用了,特么的. 总结给你的经验,对于这样字符串的题目,经常会出现一种叫做递归定义的东西. 还有一种叫做,相同的字母表示相同的字符串. 这道题目一共有三个条件. 1. 字符串中必须仅 ...

    解决Windows对JDK默认版本切换问题

    注意修改path路径,或者修改控制面板下的java控制面板并不有效,原因是由于在WINDOWS\System32环境变量中的优先级高于JAVA_HOME设置的环境变量优先级,故如果只修改环境变量JAV ...

    第十七节&colon; EF的CodeFirst模式的四种初始化策略和通过Migration进行数据的迁移

    一. 四种初始化策略 EF的CodeFirst模式下数据库的初始化有四种策略: 1. CreateDatabaseIfNotExists:EF的默认策略,数据库不存在,生成数据库:一旦model发生变 ...

    XCode各种问题

    2018.07.10 1.clang: warning: libstdc++ is deprecated; move to libc++ [-Wdeprecated] 2.2018.07.29  海康 ...

    五、CLR加载程序集代码时&comma;JIT编译器对性能的产生的影响

    1.CLR首次加载代码造成的性能损失 四.CLR执行程序集中代码介绍了CLR在首次执行一个类的时,会初始化一个内部结构,然后当目标方法被首次调用时,JITComplier函数(JIT编译器)会验证IL ...

    展开全文
  • 编译过程和符号表重定位问题:转载至:点击打开链接 对于代码的编译问题千头万绪从何说起呢,首先来说一下计算机是如何处理应用程序的,实质上应用程序是通过操作系统来应用机器指令操控硬件设施完成各种任务的,...

    编译过程和符号表重定位问题:转载至:点击打开链接

    对于代码的编译问题千头万绪从何说起呢,首先来说一下计算机是如何处理应用程序的,实质上应用程序是通过操作系统来应用机器指令操控硬件设施完成各种任务的,就从编译的环节开始谈起吧,众所周知,程序开发人员所写的代码实际上计算机是没有办法去认识的,那么就必须通过编译将其转换为计算机可以认识的机器指令,在有操作系统根据具体指令从硬件上分配内存处理程序段。以下从预编译,编译,汇编,链接,来简单的说一下程序的编译过程。

    2.1编译预处理

    在这个阶段主要是宏定义的展开,以及头文件的递归处理,即展开所有的以#开头的编译命令,删除所有注释,添加行号和文件名标识,保留所有的#program

    2.2编译阶段

    将程序代码段按字符流格式进行切割,处理,主要是词法分析,语法分析,语义分析以及优化等阶段,编译完成后生成中间代码。

    2.3汇编

    将编译后的中间代码通过汇编器模块生成计算机能够识别的机器指令用以操控硬件设施生成目标代码(可重定位目标代码)。

    2.4链接

    通过链接器模块将各种目标代码以及库文件(*.lib文件),资源文件(*,rec)进行链接处理最终生成可以执行的*.exe文件。

    2.5重定位问题

    通过一个例子来看:假如我们有两个头文件和两个源文件分别叫做function1.h和function2.h以及function1.cpp和function2.cpp文件其中function1.h内容如下

     

    Function1.h

    #ifndef   _FUNCTION1_H

    #define   _FUNCTION1_H

    Int g_val;

    Int Add(int m, int n);

    #endif

     

    Function1.cpp

    g_val=10;

    Int Add(int m, int n)

    {

    Return m+n;

    }

    Function2.cpp其中包含了main函数内容如下

    #include “function1.h”

    Int main()

    {

    Int  l_valfri=3;

    Int  l_valsec=4;

    g_val=14;

    Int result=Add(l_valfri,l_valsec);

    Return 0;

    }

    对于这样的代码编译器在编译function2.cpp时对于外部符号g_val 和外部函数Add该如何决议呢,这里又会涉及到可重定位文件中的符号表问题。

    其实在可重定位目标文件之中会存在一个用来放置变量和其入口地址的符号表,当编译过程中能够找到该符号的定义时就将该符号入口地址更新到符号表中否则就对该符号的地址不做任何决议一直保留到链接阶段处理。通过两个例子来看符号表的结构。

    在编译过程中function1.cpp文件的可重定位目标文件中的符号表如下

    变量名

    内存地址

    g_val

    0x100

    Add

    0x200

     

     

    为什么可以做到对于符号g_val和Add分配内存地址呢,因为在编译阶段就能够在function1.cpp文件中找到他们的定义,所以能够进行明确的内存地址分配。

    再来看看function2.cpp所生成的可重定位目标文件的结构:

    变量名

    内存地址

    g_val

    0x00

    Add

    0x00

    为什么会出现这样的状况。因为在编译阶段虽然可以看到这些符号变量的声明,但却找不到他们的定义所以编译器陷入了一个决而未决的境地。

    将包含文件展开时,function2.cpp大概会是这个样子很明显只有符号变量的声明但是没有定义。

    #ifndef   _FUNCTION1_H

    #define   _FUNCTION1_H

    Int g_val;

    Int Add(int m, int n);

    #endif

     

     

    Int main()

    {

    Int  l_valfri=3;

    Int  l_valsec=4;

    g_val=14;

    Int result=Add(l_valfri,l_valsec);

    Return 0;

    }

     

    先将他们存放在符号表中但却不去为他们进行内存关联一直等到链接阶段在进行处理。

    重定位发生于目标代码链接阶段,在链接阶段链接器就会查找符号表,当他发现了function2.cpp的符号表之中任然有没有决议的内存地址时,链接器就会查找所有的目标代码文件,一直到他找到了function1.cpp所生成的目标代码文件符号表时发现了这些没有决议的符号变量的真正内存地址,这是function2.cpp所生成的目标代码文件就会更新它的符号表,将这些尚未决议的符号变量的内存地址写进其符号表中。

    更新之后的function2.obj文件符号表

    变量名

    内存地址

    g_val

    0x100

    Add

    0x200

     

     

     

    当所有的符号变量都能够找到合法的内存地址时,链接阶段重定位完成。


    静态和动态链接:转载至点击打开链接

    即使是最简单的HelloWorld的程序,它也要依赖于别人已经写好的成熟的软件库,这就是引出了一个问题,我们写的代码怎么和别人写的库集成在一起,也就是链接所要解决的问题。

    首先看HelloWorld这个例子:
    [cpp]  view plain  copy
    1. // main.c  
    2.   1 #include <stdio.h>  
    3.   2  
    4.   3 int main(int argc, char** argv)  
    5.   4 {  
    6.   5         printf("Hello World! argc=%d\n", argc);  
    7.   6         return 0;  
    8.   7 }  

    HelloWorld的main函数中引用了标准库提供的printf函数。链接所要解决的问题就是要让我们的程序能正确地找到printf这个函数。
    解决这个问题有两个办法:一种方式是在生成可执行文件的时候,把printf函数相关的二进制指令和数据包含在最终的可执行文件中,这就是静态链接;另外一种方式是在程序运行的时候,再去加载printf函数相关的二进制指令和数据,这就是动态链接。
    每个源文件都会首先被编译成目标文件,每个目标文件都提供一些别的目标文件需要的函数或者数据,同时又从别的目标文件中获得一些函数或者数据。因此,链接的过程就是目标文件间互通有无的过程。 本文根据《程序员的自我修养》一书中关于静态和动态链接总结而成,欢迎指正并推荐阅读原书。

    静态链接

    静态链接就是在生成可执行文件的时候,把所有需要的函数的二进制代码都包含到可执行文件中去。因此, 链接器需要知道参与链接的目标文件需要哪些函数,同时也要知道每个目标文件都能提供什么函数,这样链接器才能知道是不是每个目标文件所需要的函数都能正确地链接。如果某个目标文件需要的函数在参与链接的目标文件中都找不到的话,链接器就报错了。
    目标文件中有两个重要的接口来提供这些信息:一个是符号表,另外一个是重定位表。 利用Linux中的readelf工具就可以查看这些信息。
    首先我们用命令gcc -c -o main.o main.c 来编译上面main.c文件来生成目标文件main.o。然后我们用命令readelf -s main.o来查看main.o中的符号表:
    [plain]  view plain  copy
    1. Symbol table '.symtab' contains 11 entries:  
    2.    Num:    Value  Size Type    Bind   Vis      Ndx Name  
    3.      0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND  
    4.      1: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c  
    5.      2: 00000000     0 SECTION LOCAL  DEFAULT    1  
    6.      3: 00000000     0 SECTION LOCAL  DEFAULT    3  
    7.      4: 00000000     0 SECTION LOCAL  DEFAULT    4  
    8.      5: 00000000     0 SECTION LOCAL  DEFAULT    5  
    9.      6: 00000000     0 SECTION LOCAL  DEFAULT    7  
    10.      7: 00000000     0 SECTION LOCAL  DEFAULT    8  
    11.      8: 00000000     0 SECTION LOCAL  DEFAULT    6  
    12. <strong>     9: 00000000    36 FUNC    GLOBAL DEFAULT    1 main  
    13.     10: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf</strong>  

    我们重点关注最后两行,从中可以看到main.o中提供main函数(Type列为FUNC,Ndx为1表示它是在本目标文件中第1个Section中),同时依赖于printf函数(Ndx列为UND)。

    因为在编译main.c的时候,编译器还不知道printf函数的地址,所以在编译阶段只是将一个“临时地址”放到目标文件中,在链接阶段,这个“临时地址”将被修正为正确的地址,这个过程叫重定位。所以链接器还要知道该目标文件中哪些符号需要重定位,这些信息是放在了重定位表中。很明显,在main.o这个目标文件中,printf的地址需要重定位,我们还是用命令readelf -r main.o来验证一下,这些信息是保存在.rel.textSection中:
    [plain]  view plain  copy
    1. Relocation section '.rel.text' at offset 0x400 contains 2 entries:  
    2.  Offset     Info    Type            Sym.Value  Sym. Name  
    3. 0000000a  00000501 R_386_32          00000000   .rodata  
    4. 00000019  00000a02 R_386_PC32        00000000   printf  
    那么既然main.o依赖于printf函数,你可能会问,printf是在哪个目标文件里面?printf函数是标准库的一部分,在Linux下静态的标准库libc.a位于/usr/lib/i386-linux-gnu/中。你可以认为标准库就是把一些常用的函数的目标文件打包在一起,用命令ar -t libc.a可以查看libc.a中的内容,其中你就可以发现printf.o这个目标文件。在链接的时候,我们需要告诉链接器需要链接的目标文件和库文件(默认gcc会把标准库作为链接器输入的一部分)。链接器会根据输入的目标文件从库文件中提取需要目标文件。比如,链接器发现main.o会需要printf这个函数,在处理标准库文件的时候,链接器就会把printf.o从库文件中提取处理。当然printf.o依赖的目标文件也很被一起提取出来。库中其他目标文件就被舍弃掉,从而减小了最终生成的可执行文件的大小。

    知道了这些信息后,链接器就可以开始工作了,分为两个步骤:1)合并相似段,把所有需要链接的目标文件的相似段放在可执行文件的对应段中。2)重定位符号使得目标文件能正确调用到其他目标文件提供的函数。
    用命令gcc -static -o helloworld.static main.c来编译并做静态链接,生成可执行文件helloworld.static。因为可执行文件helloworld.static已经是链接好了的,所以里面就不会有重定位表了。命令 readelf -S helloworld.static | grep .rel.text将不会有任何输出(注:-S是打印出ELF文件中的Sections)。经过静态链接生成的可执行文件,只要装载到了内存中,就可以开始运行了。

    动态链接

    静态链接看起来很简单,但是有些不足。其中之一就对磁盘空间和内存空间的浪费。标准库中那些函数会被放到每个静态链接的可执行文件中,在运行的时候,这些重复的内容也会被不同的可执行文件加载到内存中去。同时,如果静态库有更新的话,所有可执行文件都得重新链接才能用上新的静态库。动态链接就是为了解决这个问题而出现的。所谓动态链接就是在运行的时候再去链接。 理解动态链接需要从两个角度来看,一是从动态库的角度,而是从使用动态库的可执行文件的角度。

    从动态库的角度来看,动态库像普通的可执行文件一样,有其代码段和数据段。为了使得动态库在内存中只有一份,需要做到不管动态库装载到什么位置,都不需要修改动态库中代码段的内容,从而实现动态库中代码段的共享。而数据段中的内容需要做到进程间的隔离,因此必须是私有的,也就是每个进程都有一份。因此,动态库的做法是把代码段中变化的部分放到数据段中去,这样代码段中剩下的就是不变的内容,就可以装载到虚拟内存的任何位置。那代码段中变化的内容是什么,主要包括了对外部函数和变量的引用。
    我们来看一个简单的例子吧,假设我们要把下面的代码做成一个动态库:
    [plain]  view plain  copy
    1.  1 #include <stdio.h>  
    2.  2 extern int shared;  
    3.  3 extern void bar();  
    4.  4 void foo(int i)  
    5.  5 {  
    6.  6   printf("Printing from Lib.so %d\n", i);  
    7.  7   printf("Printing from Lib.so, shared %d\n", shared);  
    8.  8  
    9.  9   bar();  
    10. 10   sleep(-1);  
    11. 11 }  
    用命令gcc -shared -fPIC -o Lib.so Lib.c将生成一个动态库Lib.so(-shared是生成共享对象,-fPIC是生成地址无关的代码)。该动态库提供(导出)一个函数foo,依赖(导入)一个函数bar,和一个变量shared。
    这里我们需要解决的问题是如何让foo这个函数能正确地引用到外部的函数bar和shared变量?程序装载有个特性,代码段和数据段的相对位置是固定的,因此我们把这些外部函数和外部变量的地址放到数据段的某个位置,这样代码就能根据其当前的地址从数据段中找到对应外部函数的地址(前提是谁能帮忙在数据段中填上这个外部函数的正确地址,下面会讲)。动态库中外部变量的地址是放在.got(global offset table)中,外部函数的地址是放在了.got.plt段中。
    如果你用命令readelf -S Lib.so | grep got将会看到Lib.so中有这样两个Section。他们就是分别存放外部变量和函数地址的地方。
    [plain]  view plain  copy
    1. [20] .got              PROGBITS        00001fe4 000fe4 000010 04  WA  0   0  4  
    2. [21] .got.plt          PROGBITS        00001ff4 000ff4 000020 04  WA  0   0  4  
    到此为止,我们知道了动态库是把地址相关的内容放到了数据段中来实现地址无关的代码,从而使得动态库能被多个进程共享。那么接着的问题就谁来帮助动态库来修正.got和.got.plt中的地址。

    那么我们就从动态链接器的角度来看看吧!

    静态链接的可执行文件在装载进入内存后就可以开始运行了,因为所有的外部函数都已经包含在可执行文件中。而动态链接 的可执行文件中对外部函数的引用地址在生成可执行文件的时候是未知的,所以在这些地址被修正前是动态链接生成的可执行文件是不能运行的。因此,动态链接生成的可执行文件运行前,系统会首先将动态链接库加载到内存中,动态链接器所在的路径在可执行文件可以查到的。

    还是以前面的helloworld为例,用命令gcc -o helloworld.dyn main.c来以动态链接的方式生成可执行文件。然后用命令readelf -l helloworld.dyn | grep interpreter可以看到动态链接器在系统中的路径。
    [plain]  view plain  copy
    1. [Requesting program interpreter: /lib/ld-linux.so.2]  
    当动态链接器被加载进来后,它首先做的事情就是先找到该可执行文件依赖的动态库,这部分信息也是在可执行文件中可以查到的。用命令readelf -d helloworld.dyn,可以看到如下输出:
    [plain]  view plain  copy
    1. Dynamic section at offset 0xf28 contains 20 entries:  
    2.   Tag        Type                         Name/Value  
    3.  0x00000001 (NEEDED)                     Shared library: [libc.so.6]  
    或者用命令ldd  helloworld.dyn ,可以看到如下输出:
    [plain]  view plain  copy
    1. linux-gate.so.1 =>  (0x008cd000)  
    2. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00a7a000)  
    3. /lib/ld-linux.so.2 (0x0035d000)  
    都表明该可执行文件依赖于libc.so.6这个动态库,也就是C语言标准库的动态链接版本。 如果某个库依赖于别的动态库,它们也会被加载进来直到所有依赖的库都被加载进来。

    当所有的库都被加载进来以后,类似于静态链接,动态链接器从各个动态库中可以知道每个库都提供什么函数(符号表)和哪些函数引用需要重定位(重定位表),然后修正.got和.got.plt中的符号到正确的地址,完成之后就可以将控制权交给可执行文件的入口地址,从而开始执行我们编写的代码了。
    可见,动态链接器在程序运行前需要做大量的工作(修正符号地址),为了提高效率,一般采用的是延迟绑定,也就是只有用到某个函数才去修正.got.plt中地址,具体是如何做到延迟绑定的,推荐看《程序员的自我修养》一书。

    小结

    链接解决我们写的程序是如何和别的库组合在一起这个问题。每个参与链接的目标文件中都提供了这样的信息:我有什么符号(变量或者函数),我需要什么符号,这样链接器才能确定参与链接的目标文件和库是否能组合在一起。静态链接是在生成可执行文件的时候把需要的所有内容都包含在了可执行文件中,这导致的问题是可执行文件大,浪费磁盘和内存空间以及静态库升级的问题。动态链接是在程序运行的时候完成链接的,首先是动态链接器被加载到内存中,然后动态链接器再完成类似于静态链接器的所做的事情。

    展开全文
  • 介绍了符号表的组织形式有线性组织(重插入简洁性,轻索引性能),排序组织(插入操作耗时,重索引性能),哈希映射(以空间换取时间)。那么如下便介绍了符号表的链表实现方式和哈希实现方式,分别对应线性组织...
  • strip符号和节移除工具,符号表删除

    千次阅读 2018-09-02 01:26:56
    .symtab因为是在调试和链接时有用的,所以,可以...移除.symtab 符号表 以及 .strtab 符号字符串 直接运行 strip --remove-section=.symtab file_in strip --remove-section=.strtab file_in 想要让可执行文...
  • 一、符号表管理 1.1 语义分析部分三个核心知识点 abcd选择和当前位置最近的声明性出现 映射关系建立之后,变量的名字即无用,知道语义信息即可 1.2 处理原则 在程序运行到某一个位置的时候是有效的。 1.3 Pascal...
  • 符号表

    2020-12-21 22:08:00
    目录符号表概述符号表的组织符号表的操作名字的作用范围栈式符号表 符号表概述 符号表的作用: 保存各类标识符的属性 检查语义的正确性:上下文敏感成分的分析实质上是语法分析的内容。但我们的语法分析是以上下文...
  • 树的每个内部节点和它的孩子对应一个三元式或四元式 符号表 符号表的作用:连接声明与引用的桥梁,记住每个符号的相关信息,如作用域和绑定等,帮助编译的各个阶段正确有效地工作 符号表的空间存储应该是可以动态...
  • gcc 去除符号表

    万次阅读 2015-07-03 17:41:02
    读取符号表 readelf -p .dynstr mylib.so 通过gcc控制可见性 __attribute__((visibility("hidden"))) int foo(int a, int b); __attribute__((visibility("default"))) int foo(int a, int b); ndk中控制 NDK_...
  • 1.符号表信息和调试信息 符号表信息(symbols)和调试信息(debug info)是由不同段区分的。 使用 readelf -S binfile 可以查看ELF文件的所有段。 调试信息相关的段: # readelf -S a.out | grep debug [27] .debug...
  • Android NDK SO库隐藏内部符号表

    千次阅读 2020-03-18 21:55:40
    本文描述编译SO库时最大限度地隐藏内部符号表,那样IDA pro反编译的时候看到的大部分函数都是一些无意义的名称,从而加大逆向分析难度。 具体做法如下: 在build.gradle中添加 cppFlags "-fvisibility=hidden" 参见...
  • 编译时默认包含另一组符号.这些是链接符号,并且存在于ELF(可执行链接格式)符号表中.这包含比调试符号少得多的信息,但包含最重要的东西,例如可执行文件(或库或对象文件)中的东西的地址.没有这个信息,gdb甚至不知道...
  • C++编译器符号表有哪些内容?

    万次阅读 2015-09-10 11:06:35
    其一是因为符号表在编译器的设计中占有举足轻重的地位【我们在学习编译原理的时候更多的是注重principles,而没有关心一个编译器的实现,所以符号表讲解的也比较少】,编译阶段的每“遍”都会和符号表打交道,本人只...
  • 这些可以通过配置xcode的编译选项来达到效果。具体操作请看这:Xcode中和symbols有关的几个设置。 Xcode显示调用堆栈中符号时,只会显示符号表中有的符号。为了我们调试过程的顺利,我们有必要把可执行文件中的符号...
  • Mach-0符号表

    千次阅读 2022-01-19 17:14:29
    Mach-O文件格式保存了在编译过程和链接过程中产生机器代码和数据。从而为静态链接和动态链接的代码提供了单一文件格式。 段之前始终是4096字节或者4KB的倍数,其中4096字节是最小大小。 现在短是16
  • 符号表包含用来定位、重定位程序中符号定义和引用的信息,简单的理解就是符号表记录了该文件中的所有符号,所谓的符号就是经过修饰了的函数名或者变量名,不同的编译器有不同的修饰规则。例如符号_ZL15global_static...
  • C语言的符号表和类型系统1

    万次阅读 2016-09-06 17:37:26
    当解析读取到右括号时,便把variable在符号表中的信息删除,因为出了variable的作用范围只在括号之内。 符号表还可以用来存储类型定义(typedef)和常量声明,在词法解析的过程,词法解析还需要和符号表交互...
  • Linux内核符号表

    万次阅读 2015-07-03 18:18:10
    一,什么是符号(Symbols)?  什么是Symbol? 其实就是kernel中的变量(Variable Name)或函数名称(Function Name),  这样可以方便程序员在写程序时可以直接参照这一份Symbol的索引文件,...二,内核符号表(Kernel
  • ADD_DEFINITIONS(-g) 添加了之后,就相当于在编译的时候加上了-g选项 ADD_DEFINITIONS(-Os) 添加了之后,就相当于在编译的时候加上了 -Os选项 ADD_DEFINITIONS(-D DEBUG_WARN) 将在gcc命令行添加DEBUG_WARN宏定义 ...
  • !!! ...C++编译器符号表有哪些内容?...很早就想写一篇关于符号表的学习小结,可是迟迟不能...其一是因为符号表在编译器的设计中占有举足轻重的地位【我们在学习编译原理的时候更多的是注重principles,而没有关心一
  • GCC安全编译选项简介

    千次阅读 2020-03-11 20:37:06
    GCC编译选项 安全编译选项 描述 级别 BIND_NOW 立即绑定 high NX 堆栈不可执行 high PIC 地址无关 high PIE 随机化 high RELRO GOT保护 high SP 栈保护 ...
  • GCC 编译选项

    2021-04-10 22:32:57
    1. 一般选项 Overall ... -v 会打印编译过程的详细信息,如完全的编译选项(包括gcc内部添加的编译选项),如头文件目录的搜索顺序等。 2. 语言选项 Language Option 选项 作用 -std= 编译时遵循的语言
  • 该电子书 有关于 编译 原理 空格 删除 分隔符 扫描
  • linux,可执行文件,符号信息
  • 小编典典这是一个很晚的答案,只是为了好玩。在这种情况下,我建议您以提高速度的可读性为目标。当然,您可以超级可读,但速度太慢,例如在此超级...}这很慢,因为每次调用此方法时,都会编译正则表达式。因此,您可...
  • ELF 符号表使用

    2019-04-09 11:37:28
    ELF 符号表 2018-01-06 Saturday linux , program -rdynamic VS. -g 如果通过 readelf -s 查看,可以发现,使用 -rdynamic 选项后,在 .dynsym 段中增加了很多符号,包括本程序内定义的符号。 -g 会添加调试信息(....
  • 编写一个词法分析,从输入的源程序(编写的语言为C语言的一个子集)中,识别出各个具有独立意义的单词,即基本保留字、标识符、...扩充功能:删除注释,增加识别单词的类型,将标识符和常量分别插入到相应的符号表
  • linux静态库 动态库 去符号 符号恢复

    千次阅读 2018-04-17 00:45:08
    最近遇到了一题去除符号的题目,需要进行符号修复。为此学习了一波,顺便补了补其它的一下知识。 准备知识 命名方式: 动态库libxxx.so.major.minor .so .dll 静态库:libxxx.a .a .lib 生成动态库 gcc -...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 132,569
精华内容 53,027
关键字:

删除符号表信息 编译选项

友情链接: RSI_Convolve.rar