学了这么多年C语言,你真的知道全局变量,局部变量,静态变量,本地函数,外部函数是如何区分标识的吗?
动态库内容分析
文章目录
1. 动态库编译
基本思路为:
- 先编写两个C文件,其中各自实现几个函数,变量,然后将其分别编译为动态库;
- 再编写一个实现main函数的C文件,分别调用上述第一步动态库中的函数;
- 分析最后的可执行文件和动态库文件的符号表;
1.1 第一个C文件:basic.c
这个C文件只定义并实现了四个不同形参的函数、五个静态变量、一个全局变量。由于只关心符号表或者其他二进制内容,因此不具体实现特定功能。
/*************************************************************************
> File Name: basic.c
> Author: Toney Sun
> Mail: vip_13031075266@163.com
> Created Time: 2020年04月20日 星期一 09时50分51秒
************************************************************************/
#include<stdio.h>
int basic_func=4;
static char *Author="Toney Sun";
void func1()
{
int tmp_var;
static char *Mail="vip_13031075266@163.com";
}
void func2(int x)
{
static char *Mail="vip_13031075266@163.com";
}
int func3(char *a)
{
static char *Mail="vip_13031075266@163.com";
}
char * func4(int x, int y)
{
static char *Mail="vip_13031075266@163.com";
}
1.2第二个C文件:demo.c
在demo.c中我定义一个结构体udphdr。然后分别定义了两个全局变量,实现了三个函数:func5, func6, fun7。
/*************************************************************************
> File Name: demo.c
> Author: Toney Sun
> Mail: vip_13031075266@163.com
> Created Time: 2020年04月19日 星期日 22时33分39秒
************************************************************************/
#include<stdio.h>
struct udphdr{
short dstport;
short srcport;
short checksum;
short length;
};
enum Date{
Monday,
Tuesday,
Wensday,
Thursday,
Friday,
Saturday,
Sunday,
};
struct udphdr udp1;
enum Date today = Monday;
int iphdr1=10;
extern void func1();
extern void func2(int x);
extern int func3(char *);
extern char * func4(int x, int y);
int fun5(int a)
{
struct udphdr udp2;
func1();
printf("aaaaaaaaaaa\n");
}
int fun6(char *a)
{
static struct udphdr udp1;
func2(10);
printf("aaaaaaaaaaa\n");
}
int fun7(int a, char *b)
{
func3("test");
printf("aaaaaaaaaaa\n");
}
1.3第三个C文件:main.c
main.c主要用来实现main函数,并调用其他C文件中实现的函数和全局变量。从而观察对比不同的函数、变量在符号表中的异同。
/*************************************************************************
> File Name: main.c
> Author: Toney Sun
> Mail: vip_13031075266@163.com
> Created Time: 2020年04月20日 星期一 09时44分38秒
************************************************************************/
#include<stdio.h>
extern void func1();
extern int fun5(int a);
extern int fun6(char *a);
extern int fun7(int a, char *b);
extern struct udphdr udp;
extern int iphdr;
int myAge=25;
char *mail="vip_13031075266@163.com";
int show()
{
printf("Author: Toney Sun\n");
}
int main(int argc, char **argv)
{
int a=10;
int b=11;
fun5(a);
fun6("aaaaa");
fun7(a, "Toney Sun");
show();
udp.srcport=4500;
iphdr=10;
return 0;
}
2.动态库编译
使用gcc工具将basic.c编译为libbasic.so;
使用gcc工具将demo.c编译为libdemo.so;
使用gcc工具将main.c链接上述两个动态库后编译为a.out
**注意:**我们不是为了执行该a.out, 而是想查看上述三个二进制文件的内容(符号表)。
toney@ubantu$ gcc -shared -fpic -o libdemo.so demo.c
toney@ubantu$ gcc -shared -fpic -o libbasic.so basic.c
toney@ubantu$ gcc main.c -L./ -ldemo -lbasic
toney@ubantu$ ls -l
total 35
-rwxrwxrwx 1 root root 8552 4月 20 10:18 a.out
-rwxrwxrwx 1 root root 454 4月 20 09:52 basic.c
-rwxrwxrwx 1 root root 763 4月 20 09:49 demo.c
-rwxrwxrwx 1 root root 8016 4月 20 09:43 demo.so
-rwxrwxrwx 1 root root 7528 4月 20 10:17 libbasic.so
-rwxrwxrwx 1 root root 8128 4月 20 10:17 libdemo.so
-rwxrwxrwx 1 root root 846 4月 20 10:18 main.c
链接怎么还有个顺序问题:(
toney@ubantu$ gcc main.c -L./ -lbasic -ldemo
.//libdemo.so: undefined reference to `func3'
.//libdemo.so: undefined reference to `func1'
.//libdemo.so: undefined reference to `func2'
collect2: error: ld returned 1 exit status
toney@ubantu$ gcc main.c -L./ -ldemo -lbasic
toney@ubantu$
3.二进制内容分析
3.1 libbasic.so分析
3.1.1 basic.c内容汇总
| 序号 | 函数或变量 | 性质 |
|---|---|---|
| 1 | void func1() | 自定义函数 |
| 2 | void func2(int x) | 自定义函数 |
| 3 | int func3(char *a) | 自定义函数 |
| 4 | char * func4(int x, int y) | 自定义函数 |
| 5 | int basic_func; | 自定义全局变量 |
| 6 | static char *Author; | 自定义全局静态变量 |
| 7 | static char *Mail; | func1内定义局部静态变量 |
| 8 | static char *Mail; | func2内定义局部静态变量 |
| 9 | static char *Mail; | func3内定义局部静态变量 |
| 10 | static char *Mail; | func4内定义局部静态变量 |
3.1.2 libbasic.so符号表
- 使用nm工具查看符号表内容(当然也可以使用其他工具查看,例如objdump, readelf, ldd等):
toney@ubantu$ nm libbasic.so
0000000000201028 d Author ==================全局静态变量=================
0000000000201020 D basic_func ====================全局变量==================
0000000000201050 B __bss_start
0000000000201050 b completed.7698
w __cxa_finalize
00000000000005a0 t deregister_tm_clones
0000000000000630 t __do_global_dtors_aux
0000000000200e88 t __do_global_dtors_aux_fini_array_entry
0000000000201018 d __dso_handle
0000000000200e90 d _DYNAMIC
0000000000201050 D _edata
0000000000201058 B _end
00000000000006a4 T _fini
0000000000000670 t frame_dummy
0000000000200e80 t __frame_dummy_init_array_entry
00000000000007e8 r __FRAME_END__
000000000000067a T func1 ===================实现函数===================
0000000000000681 T func2 ===================实现函数===================
000000000000068b T func3 ===================实现函数===================
0000000000000696 T func4 ===================实现函数===================
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
00000000000006d0 r __GNU_EH_FRAME_HDR
0000000000000568 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000201030 d Mail.2252 ================局部静态变量===================
0000000000201038 d Mail.2256 ================局部静态变量===================
0000000000201040 d Mail.2260 ================局部静态变量===================
0000000000201048 d Mail.2265 ================局部静态变量===================
00000000000005e0 t register_tm_clones
0000000000201050 d __TMC_END__
- 使用nm -Da 查看本动态库定义的内容信息
toney@ubantu$ nm -Da libbasic.so
0000000000201020 D basic_func ----------------1----------------
0000000000201050 B __bss_start
w __cxa_finalize
0000000000201050 D _edata
0000000000201058 B _end
00000000000006a4 T _fini
000000000000067a T func1 ----------------2----------------
0000000000000681 T func2 ----------------3----------------
000000000000068b T func3 ----------------4----------------
0000000000000696 T func4 ----------------5----------------
w __gmon_start__
0000000000000568 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
3.1.3 小结
- 本文件内定义的全局变量用‘D’来表示;
- 本文件内定义的全局静态变量用‘d’来表示;
- 本文件内实现的函数用‘T’来表示;
- 不同函数内定义的静态变量在符号表中的符号有所差别,因此不会出现混淆的问题。
- 从上述内容可以猜测:使用‘d’表示的变量不能被其他文件引用(上述‘d’标识的都是静态变量,这也比较合理)
3.2 libdemo.so分析
3.2.1 demo.c内容汇总
| 序号 | 函数或变量 | 性质 |
|---|---|---|
| 1 | struct udphdr udp1; | 自定义的全局结构体变量 |
| 2 | static struct udphdr udp2; | 自定义全局静态变量 |
| 3 | struct udphdr udp2; | 自定义局部变量 |
| 4 | enum Date today | 自定义全局枚举变量 |
| 5 | int iphdr; | 自定义全局变量 |
| 6 | extern void func1(); | 外部函数 |
| 7 | extern void func2(int x); | 外部函数 |
| 8 | extern int func3(char *); | 外部函数 |
| 9 | extern char * func4(int x, int y); | 外部函数 |
| 10 | int fun5(int a) | 自定义函数 |
| 11 | int fun6(char *a) | 自定义函数 |
| 12 | int fun7(int a, char *b) | 自定义函数 |
3.2.2 demo.so符号表
同样的,我们使用nm工具来查看动态库符号表信息:
toney@ubantu$ nm libdemo.so
0000000000201044 B __bss_start
0000000000201048 b completed.7698
w __cxa_finalize@@GLIBC_2.2.5
00000000000006b0 t deregister_tm_clones
0000000000000740 t __do_global_dtors_aux
0000000000200e18 t __do_global_dtors_aux_fini_array_entry
0000000000201038 d __dso_handle
0000000000200e20 d _DYNAMIC
0000000000201044 D _edata
0000000000201068 B _end
0000000000000800 T _fini
0000000000000780 t frame_dummy
0000000000200e10 t __frame_dummy_init_array_entry
0000000000000908 r __FRAME_END__
000000000000078a T fun5 =============================
00000000000007ae T fun6 =============================
00000000000007d3 T fun7 =============================
U func1 =============================
U func2 =============================
U func3 =============================
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
000000000000081c r __GNU_EH_FRAME_HDR
0000000000000638 T _init
0000000000201040 D iphdr1 =============================
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U puts@@GLIBC_2.2.5
00000000000006f0 t register_tm_clones
0000000000201048 d __TMC_END__
0000000000201050 B today =============================
0000000000201060 B udp1 =============================
0000000000201058 b udp1.2278 =============================
3.2.3 小结
- 结构体变量(非基本型变量)使用‘B’或‘b’来标识。
- 全局结构体变量使用‘B’标识
- 局部静态结构体变量使用‘b’标识
- 局部变量不会显示在符号表中
- 本文件内使用的函数使用‘T’标识
- 引用其他文件的函数使用‘U’标识
3.3 可执行文件a.out分析
3.3.1 main.c内容汇总
| 序号 | 函数或变量 | 性质 |
|---|---|---|
| 1 | extern void func1(); | 引用外部函数 |
| 2 | extern int fun5(int a); | 引用外部函数 |
| 3 | extern int fun6(char *a); | 引用外部函数 |
| 4 | extern int fun7(int a, char *b); | 引用外部函数 |
| 5 | extern struct udphdr udp1; | 引用外部结构体变量 |
| 6 | extern int iphdr1; | 引用外部基本类型变量 |
| 7 | int myAge=25; | 本地全局变量 |
| 8 | char *mail=“vip_13031075266@163.com”; | 本地全局静态变量 |
3.3.2 a.out符号表
同样使用nm工具进行查看:
toney@ubantu$ nm a.out
0000000000201020 B __bss_start
0000000000201030 b completed.7698
w __cxa_finalize@@GLIBC_2.2.5
0000000000201000 D __data_start
0000000000201000 W data_start
00000000000007a0 t deregister_tm_clones
0000000000000830 t __do_global_dtors_aux
0000000000200d88 t __do_global_dtors_aux_fini_array_entry
0000000000201008 D __dso_handle
0000000000200d90 d _DYNAMIC
0000000000201020 D _edata
0000000000201038 B _end
0000000000000974 T _fini
0000000000000870 t frame_dummy
0000000000200d80 t __frame_dummy_init_array_entry
0000000000000b2c r __FRAME_END__
U fun5 ============1==============
U fun6 ============2==============
U fun7 ============3==============
0000000000200fa0 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
00000000000009c0 r __GNU_EH_FRAME_HDR
00000000000006f8 T _init
0000000000200d88 t __init_array_end
0000000000200d80 t __init_array_start
0000000000000980 R _IO_stdin_used
0000000000201020 B iphdr1 ============4=============
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000000970 T __libc_csu_fini
0000000000000900 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
0000000000201018 d mail ============5==============
000000000000088d T main ============6==============
0000000000201010 D myAge ============7==============
U puts@@GLIBC_2.2.5
00000000000007e0 t register_tm_clones
000000000000087a T show ============8==============
0000000000000770 T _start
0000000000201020 D __TMC_END__
0000000000201028 B udp1 ============9==============
3.3.3 小结
- 引用的外部函数使用‘U’来标识
- 全局变量使用‘D’来标识
- 全局静态变量使用‘d’来标识
- 引用的外部全局变量(简单类型和复杂类型)使用‘B’来标识
4.总结
对符号表中常见的变量、函数标识总结如下:
| 序号 | 标识 | 说明 |
|---|---|---|
| 1 | T | 自定义函数(本文件内) |
| 2 | t | 尚未分析 |
| 3 | D | 自定义标准类型全局变量(如int, char, float等) |
| 4 | d | 自定义标准类型静态变量(包括全局静态变量、局部静态变量) |
| 5 | B | 自定义扩展类型全局变量(如结构体类型,枚举型等)、引用的外部全局变量 |
| 6 | b | 自定义静态扩展类型变量(包括全局静态、局部静态类型变量) |
| 7 | U | 引用的外部函数 |
| 8 | 局部变量在符号表中是不存在的。 | |
| … | … | … |
学了这么多年C语言,你真的知道全局变量,局部变量,静态变量,本地函数,外部函数是如何区分标识的吗?的更多相关文章
- 浅析c语言中的变量(局部变量,外部变量,静态变量,寄存器变量)[转]
c语言中变量分为四类,分别是 1.auto 自动变量 2.static 静态存贮分配变量(又分为内部静态和外部静态) 3.extern 全程变量(用于外部变量说明) 4.register ...
- C++学了这么多年,你也许不知道为什么类定义要放在.h文件,类实现放在cpp文件。它们如何关联?
原文 http://blog.csdn.net/ithzhang/article/details/8119286 主题 C++ C++学了这么多年你知道为什么定义类时,类的定义放在.h文件中,而类 ...
- C++学了这么多年,你仍不知道的事
C++学了这么多年你知道为什么定义类时,类的定义放在.h文件中,而类的实现放在cpp文件中.它们为什么能够关联到一起呢?你知道什么东西可以放在.h文件中,什么不能.什么东西又可以放在cpp文件中.如果 ...
- 1164: 零起点学算法71——C语言合法标识符(存在问题)
1164: 零起点学算法71——C语言合法标识符 Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lldSubmitted: 10 ...
- 零基础学Python--------第2章 Python语言基础
第2章 Python语言基础 2.1 Python语法特点 2.11注释 在Python中,通常包括3种类型的注释,分别是单行注释.多行注释和中文编码声明注释. 1.单行注释 在Python中,使用 ...
- c语言指针函数与函数指针
例一:指针函数 指针函数是指带指针的函数,即本质是一个函数.函数返回类型是某一类型的指针 类型标识符 *函数名(参数表) int *f(x,y); 首先它是一个函数,只不过这个函数的返回值是一个 ...
- 网络采集软件核心技术剖析系列(3)---如何使用C#语言下载博文中的全部图片到本地并可以离线浏览
一 本系列随笔概览及产生的背景 本系列开篇受到大家的热烈欢迎,这对博主是莫大的鼓励,此为本系列第三篇,希望大家继续支持,为我继续写作提供动力. 自己开发的豆约翰博客备份专家软件工具问世3年多以来,深受 ...
- 《Linux就该这么学》培训笔记_ch03_管道符、重定向与环境变量
<Linux就该这么学>培训笔记_ch03_管道符.重定向与环境变量 文章最后会post上书本的笔记照片. 文章主要内容: 输入输出重定向 管道命令符 命令行的通配符 常用的转义字符 重要 ...
- C语言的本质(34)——静态库
库是一种软件组件技术,库里面封装了数据和函数. 库的使用可以使程序模块化. Windows系统包括静态链接库(.lib文件)和动态链接库(.dll文件). Linux通常把库文件存放在/usr/lib ...
随机推荐
- 几张图搞懂 NodeJS 的流
假设我们现在要盖一座房子,我们买了一些砖块,厂家正在送货.现在我们有两个选择,一是等所有砖块都到了以后再开始动工:二是到一批砖块就开始动工,砖块到多少我们就用多少. 这两种方式哪种效率更高呢?显然是第 ...
- Java书单-比较全的一篇
本文已收录至码云:https://gitee.com/jalon2015/java-book 前言 之前写过一篇,Java核心书单,里面涵盖了几本主要的Java书籍: 后来有朋友反馈说,这几本太少了, ...
- UI_UE在线就业-笔记
UI设计 一.字体设计的应用范围 Logo设计.平面海报.包装设计.banner设计.APP引导页.UI图标.UI界面.影视设计 二.字体的分类 衬线字体和非衬线字体之分 区别在于笔画开始与结尾地方有 ...
- promise详解 : 实现promise(附实现代码)
promise then 的特点 : then 函数的返回值是一个 promise, 可以继续调用 then 函数 回调函数 resolve 和 reject 的参数 value /reason, 可 ...
- pwnable.kr之simple Login
pwnable.kr之simple Login 懒了几天,一边看malloc.c的源码,一边看华庭的PDF.今天佛系做题,到pwnable.kr上打开了simple Login这道题,但是这道题个人觉 ...
- RHCSA_DAY03
cd 切换工作目录命令 cd(英文全拼:change directory)切换目录 命令格式:cd [-选项] [目录名] 提示:目录名称可以是绝对路径或相对路径,如果不指定目录名称,则切换到当前用户 ...
- openssl常用命令行汇总
openssl常用命令行汇总 随机数 openssl rand -out rand.dat -base64 32 摘要 直接做摘要 openssl dgst -sha1 -out dgst.dat p ...
- SpringBoot开发二
需求介绍-Spring入门 主要是理解IOC,理解容器和Bean 代码 在Test里面利用getBean方法帮助我们看一下容器的创建: 那我首先要写一个Bean对象,假设是写一个访问数据库类. Alp ...
- OSI网络参考模型学习
文章目录 一.计算机与网络的发展 1.1 批处理系统 1.2 分时系统 1.3 计算机之间的通信 1.4 基于分组交换技术的计算机网络 1.5 互联网时代的计算机网络 1.6 计算机网络中协议的规定 ...
- Slope Trick:解决一类凸代价函数DP优化
[前言] 在补Codeforce的DP时遇到一个比较新颖的题,然后在知乎上刚好 hycc 桑也写了这道题的相关题解,这里是作为学习并引用博客的部分内容 这道题追根溯源发现2016年这个算法已经在API ...