目录
一:C语言功能模块规范
二:如何生成.a文件
三:注册真正功能函数
四:makefile编写
五:编译运行结果
钩子函数,从表面意思上看就不是一个名门正派,拿同事的话讲这个就是一个下三滥的手段(哈哈哈),不过对于初学者碰到钩子函数可能会有点蒙圈。正好最近又遇到了这个钩子函数,所以通过例子来详细讲解一下钩子函数,顺便也科普C语言一个完整的功能模块创建规范是什么样的。
钩子函数本质上一个函数指针。这时候讲一些钩子函数什么作用,为什么要用钩子函数,可能大部分都听不懂,所以还是通过例子来解释。我们例子要实现一个什么样的功能,我们要引用一个静态库,里面有个函数初始化了三个学生的姓名,存储在结构体里,如何通过钩子函数来将三个学生的姓名给它钩出来。
正常情况下,一个大型的C语言工程,都会分成很多的功能模块,这样做一是将功能单一的模块放在一起,利于后续维护,二是各个模块分开每个人并行开发,文件层次更加清晰。
每个C语言模块,一般都会有头文件,源文件,有的也会有静态库或者动态库文件,生成的目标文件。另外如果引用了静态库,也会有静态库的头文件。所以完整的C语言模块文件夹一般如下
[root@promote c_LEARNING]# tree HOOKMAIN/
HOOKMAIN/
├── include
│ └── hookfunc_main.h
├── library
│ ├── include
│ └── lib
│ └── libstudentsInfo.a
├── makefile
├── source
│ └── hookfunc_main.c
└── target
└── hookfuncMain
[root@promote HOOKMAIN]# ll
total 4
drwxrwxrwx 2 root root 29 Jan 23 19:52 include
drwxrwxrwx 4 root root 32 Jan 23 19:52 library
-rwxrwxrwx 1 root root 1279 Jan 23 20:53 makefile
drwxrwxrwx 2 root root 29 Jan 23 19:51 source
drwxr-xr-x 2 root root 26 Jan 23 20:53 target
可以看到library文件夹里面又有include和lib文件夹,这就是上面提到的引用了静态库,会有.a文件和伴随静态库的.h文件
什么是.a文件?.a文件就是静态库,静态库是一些.o目标文件的集合,一般以.a形式结尾。静态库在程序链接阶段使用,链接器将程序要用到的函数从库中提取出来,并整合到程序中,程序运行不再使用静态库了。由于每个程序要用到函数都从库提取并整合在一起,所以可执行文件夹会比较大。
生成.a文件有两个步骤
1,用gcc命令生成.o文件
gcc -o 可执行文件 调用者的C源文件.c -Ldir -l库文件名
2,用ar命令组织.o文件生成.a文件
ar rcs lib库文件名.a 目标文件1 目标文件2 ... 目标文件n
r
:表示将.o目标文件加入到静态库中;c
:表示创建静态库;s
:表示生产索引;首先看一下我们生成.a文件的源文件students_info.c
#include
#include
#include
#define FAIL 0
#define SUCCESS 1
typedef enum{
STUDENTS_111 = 1,
STUDENTS_222,
STUDENTS_333
}STUDENTS_INFO_NUM;
typedef void (*GET_SUTDENTS_NAME_INFO_Fucptr)(char *pBuf, STUDENTS_INFO_NUM sutdentsNum);
GET_SUTDENTS_NAME_INFO_Fucptr GET_SUTDENTS_NAME_INFO_HOOK=NULL;
int egRegGetStudentsInfoFunc(GET_SUTDENTS_NAME_INFO_Fucptr pGetStudentsInfoFuc)
{
if(pGetStudentsInfoFuc == NULL)
{
return FAIL;
}
GET_SUTDENTS_NAME_INFO_HOOK = pGetStudentsInfoFuc;
return SUCCESS;
}
void getStudentsInfoEntry()
{
char *students_111_name = "KaiYing Z";
char *students_222_name = "TianZe F";
char *students_333_name = "WuMing R";
char *pBuf;
pBuf = students_111_name;
GET_SUTDENTS_NAME_INFO_HOOK(pBuf, STUDENTS_111);
pBuf = students_222_name;
GET_SUTDENTS_NAME_INFO_HOOK(pBuf, STUDENTS_222);
pBuf = students_333_name;
GET_SUTDENTS_NAME_INFO_HOOK(pBuf, STUDENTS_333);
}
提供了两个函数,一是注册函数egRegGetStudentsInfoFunc,功能是将干实事的函数注册进来,这时候只是留一个壳子。getStudentsInfoEntry函数就是后面要调用的函数,要将该函数中的三个学生名字给送出去,此时GET_SUTDENTS_NAME_INFO_HOOK(pBuf, STUDENTS_111);该函数具体实现的什么功能,这时候根本就不知道,要看后面注册进来的函数是实现什么功能才能确定,这时候只知道pBuf指向了三个学生名字的那块内存,至于名字怎么送?后面会讲
介绍完了上面的功能,接着讲怎么将students_info.c打包成.a文件,命令如下
gcc -c students_info.c
ar cr libstudentsInfo.a students_info.o
生成了libstudentsInfo.a文件,将生成的.a文件放到上述的library/lib文件夹下
-rwxr-xr-x 1 root root 2154 Jan 23 20:00 libstudentsInfo.a
-rwxrwxrwx 1 root root 1020 Jan 23 19:57 students_info.c
-rw-r--r-- 1 root root 1936 Jan 23 19:59 students_info.o
上述壳子留好了,接下来就是要写真正的钩子了。首先来看一下源代码hookfunc_main.c
#include
#include
#include
#include "hookfunc_main.h"
extern int egRegGetStudentsInfoFunc(GET_SUTDENTS_NAME_INFO_Fucptr pGetStudentsInfoFuc);
extern void getStudentsInfoEntry();
T_STUDENTS_INFO g_StudentsInfo;
void getStudentsName(char *pbuf,STUDENTS_INFO_NUM sutdentsNum)
{
if(sutdentsNum == STUDENTS_111)
{
memcpy(g_StudentsInfo.sutdents_111_name,pbuf,strlen(pbuf));
}
if(sutdentsNum == STUDENTS_222)
{
memcpy(g_StudentsInfo.sutdents_222_name,pbuf,strlen(pbuf));
}
if(sutdentsNum == STUDENTS_333)
{
memcpy(g_StudentsInfo.sutdents_333_name,pbuf,strlen(pbuf));
}
}
int callback_function_register(void)
{
int retVaule = egRegGetStudentsInfoFunc(getStudentsName);
return retVaule;
}
int main()
{
memset(&g_StudentsInfo,'\0',sizeof(T_STUDENTS_INFO));
callback_function_register();
getStudentsInfoEntry();
printf("sutdents_111_name = %s\n",g_StudentsInfo.sutdents_111_name);
printf("sutdents_222_name = %s\n",g_StudentsInfo.sutdents_222_name);
printf("sutdents_333_name = %s\n",g_StudentsInfo.sutdents_333_name);
return 0;
}
头文件hookfunc_main.h
#ifndef _HOOKFUNC_MAIN_H_
#define _HOOKFUNC_MAIN_H_
#define STUDENTS_NAME_MAX_LEN 128
typedef enum{
STUDENTS_111 = 1,
STUDENTS_222,
STUDENTS_333
}STUDENTS_INFO_NUM;
typedef void (*GET_SUTDENTS_NAME_INFO_Fucptr)(char *pBuf,STUDENTS_INFO_NUM sutdentsNum);
typedef struct
{
char sutdents_111_name[STUDENTS_NAME_MAX_LEN];
char sutdents_222_name[STUDENTS_NAME_MAX_LEN];
char sutdents_333_name[STUDENTS_NAME_MAX_LEN];
} T_STUDENTS_INFO;
#endif
getStudentsName函数就是上面提到的,具体要实现什么功能的函数。memcpy(g_StudentsInfo.sutdents_111_name,pbuf,strlen(pbuf));可以看到要实现的功能就是将pbug指向内存的数据拷贝到结构体里去。而students_info.c中的函数getStudentsInfoEntry里面pBuf正好指向了学生名字的内存。这样就通过钩子将学生的名字给勾出来了
上述提到了该模块虽然简单但是一个完整的功能模块,makefile肯定少不了,下面就贴出makefile
_BIN_NAME = hookfuncMain
_BIN_SOURCE_PATH = ./source/
_INCLUDE_PATH = ./include/ \
$(addprefix -I,../library/include/)
_TARGET_PATH = ./target/
_LIB_PATH = ./library
_BIN_OBJECT_PATH = $(_TARGET_PATH)obj/
CC = gcc -march=native
AR = ar
ECHO = @echo
MKDIR = mkdir -p
RM = rm -rf
CFLAGS = -fPIC -Wall -O2
_LIB_DEF += $(_LIB_PATH)/lib/libstudentsInfo.a
_BIN_SRC_C = $(wildcard $(_BIN_SOURCE_PATH)*.c)
_BIN_OBJ = $(addprefix $(_BIN_OBJECT_PATH),$(subst .c,.o,$(notdir $(_BIN_SRC_C))))
all:$(_TARGET_PATH)$(_BIN_NAME)
$(_TARGET_PATH)$(_BIN_NAME):$(_BIN_OBJ)
@$(RM) -f $@
$(CC) $(CFLAGS) -o $@ $^ $(_MACRO_DEF) $(_LIB_DEF)
#@objdump -x $(_TARGET_PATH)$(_BIN_NAME) > $(_TARGET_PATH)symbol.txt
@echo GO "------------------>" $(_TARGET_PATH)$(_BIN_NAME)
$(RM) $(_BIN_OBJECT_PATH)
@echo GO "------------------ !BEAUTIFUL! ------------------"
$(_BIN_OBJECT_PATH)%.o:$(_BIN_SOURCE_PATH)%.c
$(ECHO) CC $<
@if [ ! -d $(_BIN_OBJECT_PATH) ]; then $(MKDIR) $(_BIN_OBJECT_PATH); fi
$(CC) $(CFLAGS) -c $< -o $@ -I $(_INCLUDE_PATH)
.PHONY : clean
clean:
$(RM) $(_BIN_OBJECT_PATH)
执行编译命令生成可执行文件
[root@promote HOOKMAIN]# make clean
rm -rf ./target/obj/
[root@promote HOOKMAIN]# make
CC source/hookfunc_main.c
gcc -march=native -fPIC -Wall -O2 -c source/hookfunc_main.c -o target/obj/hookfunc_main.o -I ./include/ -I../library/include/
gcc -march=native -fPIC -Wall -O2 -o target/hookfuncMain target/obj/hookfunc_main.o ./library/lib/libstudentsInfo.a
#@objdump -x ./target/hookfuncMain > ./target/symbol.txt
GO ------------------> ./target/hookfuncMain
rm -rf ./target/obj/
GO ------------------ !BEAUTIFUL! ------------------
运行可执行文件
[root@promote HOOKMAIN]# cd target/
[root@promote target]# ll
total 12
-rwxr-xr-x 1 root root 8784 Jan 23 20:53 hookfuncMain
[root@promote target]#
[root@promote target]# ./hookfuncMain
sutdents_111_name = KaiYing Z
sutdents_222_name = TianZe F
sutdents_333_name = WuMing R
!!!!大功告成!!!!!